#include "angband.h" #include "ui-event.h" #include "player/types.h" #include "cmds.h" #include "externs.h" #include "z-form.h" static bool debug = 1; /* * Some common cases where logging can be abbreviated * Code assumes that everything not in these sets must be logged. * (Don't bother abbreviating uncommon cases.) */ #define HAS_NO_KEY ( EVT_MOUSE | EVT_SELECT | EVT_MOVE ) #define HAS_NO_INDEX ( EVT_KBRD | EVT_ESCAPE ) #define HAS_NO_MOUSE ( EVT_KBRD | EVT_SELECT | EVT_MOVE | EVT_ESCAPE ) /* Timekeeping */ static u64b max_wait_ms = 30000; /* cannot exceed MAX_SHORT */ /* minimum keystroke wait (macro expansions are assumed to have time 0 */ static u16b min_wait_ms = 0; /* Replay speed. > 1 = fast-forward; < 1 = slow mo */ static double rate = 1.0; /* Log file */ static ang_file *logfile; extern void macro_disable(void); extern void macro_enable(void); #ifndef WINDOWS #include u64b milliseconds(void) { struct timeval t; gettimeofday(&t, 0); return ((u64b)1000)*t.tv_sec + t.tv_usec/1000; } #else extern DWORD WINAPI GetTickCount(void); u64b milliseconds(void) { return GetTickCount(); } #endif bool dungeon_stateless(ui_event_data ke) { if (p_ptr->command_cmd == 0 && ke.key == '&') return true; else return false; } /* Maintaining the log */ static u64b prev_time; void log_nowait(void) { prev_time = 0; } void writelog(ui_event_data ke) { u64b delta; u64b cur_time; byte l2type; int s = 0; u32b key = ke.type; if (logfile == NULL || (p_ptr->noscore & NOSCORE_REPLAY)) return; /* HACK: don't log SCAN_INSTANT failures */ if (ke.type == EVT_NONE || (ke.type == EVT_KBRD && ke.key == 0)) return; cur_time = milliseconds(); if (prev_time == 0) { delta = 0; } else { delta = cur_time - prev_time; if (delta > max_wait_ms) delta = max_wait_ms ; } prev_time = cur_time; fwrite_s((u16b)delta, logfile); s+=2; for (l2type = 0; key > 1; key >>= 1) l2type ++; fwrite_b(l2type, logfile); if (!(ke.type & HAS_NO_MOUSE)) { fwrite_b(ke.mousex, logfile); fwrite_b(ke.mousey, logfile); s+=2; } if (!(ke.type & HAS_NO_KEY)) { fwrite_b((byte) ke.key, logfile); s += 1; } if (!(ke.type & HAS_NO_INDEX)) { fwrite_s((u16b) ke.index, logfile); s+=2; } if (debug) printf("%d/%d:%c:%o:%o:%d(%d)\n", ke.type, l2type, ke.key, ke.mousex, ke.mousey, ke.index, s); fflush(stdout); } /* Movie replay */ /* Macros must be re-enabled during user input */ void log_pause() { macro_enable(); } void log_resume() { macro_disable(); } void log_exit(void) { if (p_ptr->total_winner) plog("And they all lived happily ever after."); else if (p_ptr->is_dead == FALSE) plog_fmt("At this point, %s returned to RL", op_ptr->full_name); else if (!strcmp(p_ptr->died_from, "Quitting")) plog_fmt("Ah, %s, we hardly knew ye.", op_ptr->full_name); else plog(" THE END "); } ui_event_data readlog(void) { u16b delta; bool success; byte l2type = 1; int s=0; ui_event_data ke = { EVT_NONE, 0, 0, '\xff', 0 }; success = fread_s(logfile, &delta); if (delta > 0 && delta < min_wait_ms) delta = min_wait_ms; else if (delta > max_wait_ms) delta = max_wait_ms; success &= fread_b(logfile, &l2type); s+= 2; printf("%d/", l2type); /* l2type cannot be 0 -- EVT_NONE not logged */ for (ke.type = 1; l2type > 0 ; --l2type) ke.type *= 2; if (!(ke.type & HAS_NO_MOUSE)) { success &= fread_b(logfile, &ke.mousex); success &= fread_b(logfile, &ke.mousey); s+=2; } if (!(ke.type & HAS_NO_KEY)) { success &= fread_b(logfile, (byte*) &ke.key); s+=1; } if (!(ke.type & HAS_NO_INDEX)) { success &= fread_s(logfile, (u16b*) &ke.index); s+=2; } if (debug) printf("%x:%c:%d:%d:%d(%d)\n", ke.type, ke.key, ke.mousex, ke.mousey, ke.index, s); Term_flush(); if (!success) { plog("Thththat's all folks!"); quit(0); } delta /= rate; while (delta > 0) { ui_event_data dummy; if (0 == Term_inkey(&dummy, FALSE, FALSE)) break; else if (delta > 300) { Term_xtra(TERM_XTRA_DELAY, 300); Term_xtra(TERM_XTRA_EVENT, FALSE); delta -= 300; } else { Term_xtra(TERM_XTRA_DELAY, delta); delta = 0; } } return ke; } static void inven_loop(bool inven) { do { if (inven) show_inven(); else show_equip(); inven = !inven; } while (inkey() == '/'); } static void movie_command_loop(void) { bool target = target_okay(); u16b tmonster = target_get_monster(); s16b tx, ty; target_get(&tx, &ty); bool done = FALSE; while (!done) { ui_event_data ch = inkey_ex(); if (ch.type & EVT_KBRD ) { switch (ch.key) { case 'i': inven_loop(true); break; case 'E': inven_loop(false); break; case 'I': do_cmd_observe(); break; case 'M': do_cmd_view_map(); break; case 'x': do_cmd_look(); break; case '&': done = TRUE; break; case '+': rate *= 9/8; break; case '-': rate *= 8; rate /= 9; break; default: break; } } } if (target && tmonster) target_set_monster(tmonster); else if (target) target_set_location(tx, ty); else target_set_monster(0); } /* Logging control methods */ static void record_movie(void) { char name[84]; char dirname[1024]; char fname[1024]; size_t i; ang_file *fprf; ui_event_data ke = { EVT_RESIZE, Term->wid, Term->hgt, '\xff', 0 }; const dump_func prefs[] = { autoinsc_dump, squelch_dump, option_dump, keymap_dump }; strnfmt(name, sizeof name, "%s.bmv", op_ptr->full_name); get_file(name, dirname, sizeof dirname); mkdir(dirname, 0733); p_ptr->noscore |= NOSCORE_MOVIE; save_game(); path_build(fname, sizeof fname, dirname, "save"); file_move(savefile, fname); p_ptr->noscore &= ~NOSCORE_MOVIE; save_game(); path_build(fname, sizeof fname, dirname, "pref"); fprf = file_open(fname, MODE_WRITE, FTYPE_TEXT); for (i = 0; i < N_ELEMENTS(prefs); i++) { prefs[i](fprf); } file_close(fprf); path_build(fname, sizeof fname, dirname, "log"); logfile = file_open(fname, MODE_WRITE, FTYPE_RAW); writelog(ke); } static void stop_logging(void) { file_close(logfile); logfile = NULL; msg_print("It's a wrap."); } void do_cmd_toggle_movie(void) { if (!logfile && !(p_ptr->noscore & NOSCORE_MOVIE)) { record_movie(); } else if (!(p_ptr->noscore & NOSCORE_MOVIE)) { stop_logging(); } else { movie_command_loop(); } } void start_reading() { char dirname[1024]; char filename[1024]; p_ptr->noscore |= NOSCORE_REPLAY; my_strcpy(dirname, savefile, strrchr(savefile, *PATH_SEP)-savefile+1); path_build(filename, sizeof filename, dirname, "pref"); process_pref_file(filename); path_build(filename, sizeof filename, dirname, "log"); log_resume(); logfile = file_open(filename, MODE_READ, FTYPE_RAW); }