/* * UAE - The Un*x Amiga Emulator * * Input record/playback * * Copyright 2010 Toni Wilen * */ #define INPUTRECORD_DEBUG 1 #define ENABLE_DEBUGGER 0 #define HEADERSIZE 12 #include "sysconfig.h" #include "sysdeps.h" #include "options.h" #include "inputrecord.h" #include "zfile.h" #include "custom.h" #include "savestate.h" #include "cia.h" #include "events.h" #include "uae.h" #include "disk.h" #include "fsdb.h" #include "xwin.h" #if INPUTRECORD_DEBUG > 0 #include "memory.h" #include "newcpu.h" #endif int inputrecord_debug = 3; extern int inputdevice_logging; #define INPREC_BUFFER_SIZE 10000 static uae_u8 *inprec_buffer, *inprec_p; static struct zfile *inprec_zf; static int inprec_size; int input_record = 0; int input_play = 0; static uae_u8 *inprec_plast, *inprec_plastptr; static int header_end, header_end2; static int replaypos; static int lasthsync, endhsync; static TCHAR inprec_path[MAX_DPATH]; static uae_u32 seed; static uae_u32 lastcycle; static uae_u32 cycleoffset; static uae_u32 pcs[16]; static uae_u32 pcs2[16]; extern void activate_debugger (void); static int warned; static void setlasthsync (void) { if (lasthsync / current_maxvpos () != hsync_counter / current_maxvpos ()) { lasthsync = hsync_counter; refreshtitle (); } } static void flush (void) { if (inprec_p > inprec_buffer) { zfile_fwrite (inprec_buffer, inprec_p - inprec_buffer, 1, inprec_zf); inprec_p = inprec_buffer; } } static void inprec_ru8 (uae_u8 v) { if (!input_record || !inprec_zf) return; *inprec_p++= v; } static void inprec_ru16 (uae_u16 v) { if (!input_record || !inprec_zf) return; inprec_ru8 ((uae_u8)(v >> 8)); inprec_ru8 ((uae_u8)v); } void inprec_ru32 (uae_u32 v) { if (!input_record || !inprec_zf) return; inprec_ru16 ((uae_u16)(v >> 16)); inprec_ru16 ((uae_u16)v); } static void inprec_rstr (const TCHAR *src) { if (!input_record || !inprec_zf) return; char *s = uutf8 (src); char *ss = s; while (*s) { inprec_ru8 (*s); s++; } inprec_ru8 (0); xfree (ss); } static bool inprec_rstart (uae_u8 type) { if (!input_record || !inprec_zf || input_record == INPREC_RECORD_PLAYING) return false; lastcycle = get_cycles (); int mvp = current_maxvpos (); if ((type != INPREC_DEBUG && type != INPREC_DEBUG2 && type != INPREC_CIADEBUG) || (0 && vsync_counter >= 49 && vsync_counter <= 51)) write_log (_T("INPREC: %010d/%03d: %d (%d/%d) %08x\n"), hsync_counter, current_hpos (), type, hsync_counter % mvp, mvp, lastcycle); inprec_plast = inprec_p; inprec_ru8 (type); inprec_ru16 (0xffff); inprec_ru32 (hsync_counter); inprec_ru8 (current_hpos ()); inprec_ru32 (lastcycle); return true; } static void inprec_rend (void) { if (!input_record || !inprec_zf) return; int size = inprec_p - inprec_plast; inprec_plast[1] = size >> 8; inprec_plast[2] = size >> 0; flush (); endhsync = hsync_counter; setlasthsync (); } static bool inprec_realtime (bool stopstart) { if (input_record == INPREC_RECORD_RERECORD) gui_message (_T("INPREC error")); write_log (_T("INPREC: play -> record\n")); input_record = INPREC_RECORD_RERECORD; input_play = 0; int offset = inprec_p - inprec_buffer; zfile_fseek (inprec_zf, offset, SEEK_SET); zfile_truncate (inprec_zf, offset); xfree (inprec_buffer); inprec_size = INPREC_BUFFER_SIZE; inprec_buffer = inprec_p = xmalloc (uae_u8, inprec_size); clear_inputstate (); return true; } static int inprec_pstart (uae_u8 type) { uae_u8 *p = inprec_p; uae_u32 hc = hsync_counter; uae_u8 hpos = current_hpos (); uae_u32 cycles = get_cycles (); static uae_u8 *lastp; uae_u32 hc_orig, hc2_orig; int mvp = current_maxvpos (); if (!input_play || !inprec_zf) return 0; if (savestate_state || hsync_counter > 0xffff0000) return 0; if (p == inprec_buffer + inprec_size) { write_log (_T("INPREC: STOP\n")); if (input_play == INPREC_PLAY_RERECORD) { input_play = 0; inprec_realtime (true); } else { inprec_close (true); } return 0; } else if (p > inprec_buffer + inprec_size) { write_log (_T("INPREC: buffer error\n")); gui_message (_T("INPREC error")); } if (p[0] == INPREC_END) { inprec_close (true); return 0; } else if (p[0] == INPREC_QUIT) { inprec_close (true); uae_quit (); return 0; } hc_orig = hc; for (;;) { uae_u32 type2 = p[0]; uae_u32 hc2 = (p[3] << 24) | (p[4] << 16) | (p[5] << 8) | p[6]; uae_u32 hpos2 = p[7]; uae_u32 cycles2 = (p[8] << 24) | (p[9] << 16) | (p[10] << 8) | p[11]; if (p >= inprec_buffer + inprec_size) break; #if 0 if (p > lastp) { write_log (_T("INPREC: Next %010d/%03d, %010d/%03d (%d/%d): %d (%d)\n"), hc2, hpos2, hc, hpos, hc2 - hc, hpos2 - hpos, p[5 + 1], p[5]); lastp = p; } #endif hc2_orig = hc2; if (type2 == type && hc > hc2) { write_log (_T("INPREC: %010d/%03d > %010d/%03d: %d missed!\n"), hc, hpos, hc2, hpos2, p[0]); #if ENABLE_DEBUGGER == 0 gui_message (_T("INPREC missed error")); #else activate_debugger (); #endif lastcycle = cycles; inprec_plast = p; inprec_plastptr = p + 12; setlasthsync (); return 1; } if (hc2 != hc) { lastp = p; break; } if (type2 == type) { if (type != INPREC_DEBUG && type != INPREC_DEBUG2 && type != INPREC_CIADEBUG && cycles != cycles2) write_log (_T("INPREC: %010d/%03d: %d (%d/%d) (%d/%d) %08X/%08X\n"), hc, hpos, type, hc % mvp, mvp, hc_orig - hc2_orig, hpos - hpos2, cycles, cycles2); if (cycles != cycles2 + cycleoffset) { if (warned > 0) { warned--; for (int i = 0; i < 7; i++) write_log (_T("%08x (%08x) "), pcs[i], pcs2[i]); write_log (_T("\n")); } cycleoffset = cycles - cycles2; #if ENABLE_DEBUGGER == 0 gui_message (_T("INPREC OFFSET=%d\n"), (int)cycleoffset / CYCLE_UNIT); #else activate_debugger (); #endif } lastcycle = cycles; inprec_plast = p; inprec_plastptr = p + 12; setlasthsync (); return 1; } if (type2 == INPREC_END || type2 == INPREC_QUIT) break; p += (p[1] << 8) | (p[2] << 0); } inprec_plast = NULL; return 0; } static void inprec_pend (void) { uae_u8 *p = inprec_p; uae_u32 hc = hsync_counter; uae_u32 hpos = current_hpos (); if (!input_play || !inprec_zf) return; if (!inprec_plast) return; inprec_plast[0] |= 0x80; inprec_plast = NULL; inprec_plastptr = NULL; for (;;) { uae_u32 hc2 = (p[3] << 24) | (p[4] << 16) | (p[5] << 8) | p[6]; uae_u32 hpos2 = p[7]; if (hc2 != hc) break; if ((p[0] & 0x80) == 0) return; p += (p[1] << 8) | (p[2] << 0); inprec_p = p; } } static uae_u8 inprec_pu8 (void) { return *inprec_plastptr++; } static uae_u16 inprec_pu16 (void) { uae_u16 v = inprec_pu8 () << 8; v |= inprec_pu8 (); return v; } static uae_s16 inprec_ps16 (void) { uae_u16 v = inprec_pu8 () << 8; v |= inprec_pu8 (); return (uae_s16)v; } static uae_u32 inprec_pu32 (void) { uae_u32 v = inprec_pu16 () << 16; v |= inprec_pu16 (); return v; } static int inprec_pstr (TCHAR *dst) { char tmp[MAX_DPATH]; char *s; int len = 0; *dst = 0; s = tmp; for(;;) { char v = inprec_pu8 (); *s++ = v; if (!v) break; len++; } if (tmp[0]) { TCHAR *d = utf8u (tmp); _tcscpy (dst, d); xfree (d); } return len; } static void findlast (void) { uae_u32 hsync = 0; uae_u8 *p = inprec_p; while (p < inprec_buffer + inprec_size) { hsync = (p[3] << 24) | (p[4] << 16) | (p[5] << 8) | p[6]; uae_u16 len = (p[1] << 8) | (p[2] << 0); p += len; } endhsync = hsync; } int inprec_open (const TCHAR *fname, const TCHAR *statefilename) { int i; inprec_close (false); if (fname == NULL) inprec_zf = zfile_fopen_empty (NULL, _T("inp")); else inprec_zf = zfile_fopen (fname, input_record ? _T("wb") : _T("rb"), ZFD_NORMAL); if (inprec_zf == NULL) return 0; currprefs.cs_rtc = changed_prefs.cs_rtc = 0; inprec_path[0] = 0; if (fname) getpathpart (inprec_path, sizeof inprec_path / sizeof (TCHAR), fname); seed = (uae_u32)time(0); inprec_size = INPREC_BUFFER_SIZE; lasthsync = 0; endhsync = 0; warned = 10; cycleoffset = 0; header_end2 = 0; if (input_play) { uae_u32 id; zfile_fseek (inprec_zf, 0, SEEK_END); inprec_size = zfile_ftell (inprec_zf); zfile_fseek (inprec_zf, 0, SEEK_SET); inprec_buffer = inprec_p = xmalloc (uae_u8, inprec_size); zfile_fread (inprec_buffer, inprec_size, 1, inprec_zf); inprec_plastptr = inprec_buffer; id = inprec_pu32(); if (id != 'UAE\0') { inprec_close (true); return 0; } int v = inprec_pu8 (); if (v != 2) { inprec_close (true); return 0; } inprec_pu8 (); inprec_pu8 (); inprec_pu8 (); seed = inprec_pu32(); seed = uaesrand (seed); vsync_counter = inprec_pu32 (); hsync_counter = inprec_pu32 (); i = inprec_pu32 (); while (i-- > 0) inprec_pu8 (); header_end = inprec_plastptr - inprec_buffer; inprec_pstr (savestate_fname); if (savestate_fname[0]) { savestate_state = STATE_RESTORE; for (;;) { TCHAR tmp[MAX_DPATH]; _tcscpy (tmp, fname); _tcscat (tmp, _T(".uss")); if (zfile_exists (tmp)) { _tcscpy (savestate_fname, tmp); break; } if (zfile_exists (savestate_fname)) break; TCHAR *p = _tcsrchr (savestate_fname, '\\'); if (!p) p = _tcsrchr (savestate_fname, '/'); if (!p) p = savestate_fname; else p++; if (zfile_exists (p)) { _tcscpy (savestate_fname, p); break; } fetch_statefilepath (tmp, sizeof tmp / sizeof (TCHAR)); _tcscat (tmp, p); if (zfile_exists (tmp)) { _tcscpy (savestate_fname, tmp); break; } fetch_inputfilepath (tmp, sizeof tmp / sizeof (TCHAR)); _tcscat (tmp, p); if (zfile_exists (tmp)) { _tcscpy (savestate_fname, tmp); break; } write_log (_T("Failed to open linked statefile '%s'\n"), savestate_fname); savestate_fname[0] = 0; savestate_state = 0; break; } } inprec_p = inprec_plastptr; header_end2 = inprec_plastptr - inprec_buffer; findlast (); } else if (input_record) { seed = uaesrand (seed); inprec_buffer = inprec_p = xmalloc (uae_u8, inprec_size); inprec_ru32 ('UAE\0'); inprec_ru8 (2); inprec_ru8 (UAEMAJOR); inprec_ru8 (UAEMINOR); inprec_ru8 (UAESUBREV); inprec_ru32 (seed); inprec_ru32 (vsync_counter); inprec_ru32 (hsync_counter); inprec_ru32 (0); // extra header size flush (); header_end2 = header_end = zfile_ftell (inprec_zf); } else { input_record = input_play = 0; return 0; } if (inputrecord_debug) { if (disk_debug_logging < 1) disk_debug_logging = 1 | 2; } write_log (_T("inprec initialized '%s', play=%d rec=%d\n"), fname ? fname : _T(""), input_play, input_record); refreshtitle (); return 1; } void inprec_startup (void) { uaesrand (seed); } bool inprec_prepare_record (const TCHAR *statefilename) { TCHAR state[MAX_DPATH]; int mode = statefilename ? 2 : 1; state[0] = 0; if (statefilename) _tcscpy (state, statefilename); if (hsync_counter > 0 && savestate_state == 0) { TCHAR *s = _tcsrchr (changed_prefs.inprecfile, '\\'); if (!s) s = _tcsrchr (changed_prefs.inprecfile, '/'); if (s) { fetch_statefilepath (state, sizeof state / sizeof (TCHAR)); _tcscat (state, s + 1); } else { _tcscpy (state, changed_prefs.inprecfile); } _tcscat (state, _T(".uss")); savestate_initsave (state, 1, 1, true); save_state (state, _T("input recording test")); mode = 2; } input_record = INPREC_RECORD_NORMAL; inprec_open (changed_prefs.inprecfile, state); changed_prefs.inprecfile[0] = currprefs.inprecfile[0] = 0; return true; } void inprec_close (bool clear) { if (clear) input_play = input_record = 0; if (!inprec_zf) return; if (inprec_buffer && input_record) { if (inprec_rstart (INPREC_END)) inprec_rend (); } zfile_fclose (inprec_zf); inprec_zf = NULL; xfree (inprec_buffer); inprec_buffer = NULL; input_play = input_record = 0; write_log (_T("inprec finished\n")); refreshtitle (); } static void setwriteprotect (const TCHAR *fname, bool readonly) { struct mystat st; int mode, oldmode; if (!my_stat (fname, &st)) return; oldmode = mode = st.mode; mode &= ~FILEFLAG_WRITE; if (!readonly) mode |= FILEFLAG_WRITE; if (mode != oldmode) my_chmod (fname, mode); } void inprec_playdiskchange (void) { if (!input_play) return; while (inprec_pstart (INPREC_DISKREMOVE)) { int drv = inprec_pu8 (); inprec_pend (); write_log (_T("INPREC: disk eject drive %d\n"), drv); disk_eject (drv); } while (inprec_pstart (INPREC_DISKINSERT)) { int drv = inprec_pu8 (); bool wp = inprec_pu8 () != 0; TCHAR tmp[MAX_DPATH], tmp2[MAX_DPATH]; inprec_pstr (tmp); _tcscpy (tmp2, tmp); if (!zfile_exists (tmp)) { TCHAR tmp3[MAX_DPATH]; _tcscpy (tmp3, inprec_path); _tcscat (tmp3, tmp); _tcscpy (tmp, tmp3); } if (!zfile_exists (tmp)) { gui_message (_T("INPREC: Disk image\n'%s'\nnot found!\n"), tmp2); } _tcscpy (currprefs.floppyslots[drv].df, tmp); _tcscpy (changed_prefs.floppyslots[drv].df, tmp); setwriteprotect (tmp, wp); disk_insert_force (drv, tmp, wp); write_log (_T("INPREC: disk insert drive %d '%s'\n"), drv, tmp); inprec_pend (); } } bool inprec_playevent (int *nr, int *state, int *max, int *autofire) { if (inprec_pstart (INPREC_EVENT)) { *nr = inprec_ps16 (); *state = inprec_ps16 (); *max = inprec_pu16 (); *autofire = inprec_ps16 () & 1; inprec_pend (); return true; } return false; } void inprec_recorddebug_cia (uae_u32 v1, uae_u32 v2, uae_u32 v3) { #if INPUTRECORD_DEBUG > 0 if (inprec_rstart (INPREC_CIADEBUG)) { inprec_ru32 (v1); inprec_ru32 (v2); inprec_ru32 (v3); inprec_rend (); } #endif } void inprec_playdebug_cia (uae_u32 v1, uae_u32 v2, uae_u32 v3) { #if INPUTRECORD_DEBUG > 0 int err = 0; if (inprec_pstart (INPREC_CIADEBUG)) { uae_u32 vv1 = inprec_pu32 (); uae_u32 vv2 = inprec_pu32 (); uae_u32 vv3 = inprec_pu32 (); if (vv1 != v1 || vv2 != v2 || vv3 != v3) write_log (_T("CIA SYNC ERROR %08x,%08x %08x,%08x %08x,%08x\n"), vv1, v1, vv2, v2, vv3, v3); inprec_pend (); } #endif } void inprec_recorddebug_cpu (int mode) { #if INPUTRECORD_DEBUG > 0 if (inprec_rstart (INPREC_DEBUG2)) { inprec_ru32 (m68k_getpc ()); inprec_ru32 (get_cycles () | mode); inprec_rend (); } #endif } void inprec_playdebug_cpu (int mode) { #if INPUTRECORD_DEBUG > 0 int err = 0; if (inprec_pstart (INPREC_DEBUG2)) { uae_u32 pc1 = m68k_getpc (); uae_u32 pc2 = inprec_pu32 (); uae_u32 v1 = get_cycles () | mode; uae_u32 v2 = inprec_pu32 (); if (pc1 != pc2) { if (warned > 0) { warned--; write_log (_T("SYNC ERROR2 PC %08x != %08x\n"), pc1, pc2); for (int i = 0; i < 15; i++) write_log (_T("%08x "), pcs[i]); write_log (_T("\n")); } err = 1; } else { memmove (pcs + 1, pcs, 15 * 4); pcs[0] = pc1; memmove (pcs2 + 1, pcs2, 15 * 4); pcs2[0] = get_cycles (); } if (v1 != v2) { if (warned > 0) { warned--; write_log (_T("SYNC ERROR2 %08x != %08x\n"), v1, v2); for (int i = 0; i < 15; i++) write_log (_T("%08x "), pcs[i]); write_log (_T("\n")); } err = 1; } inprec_pend (); } else if (input_play > 0) { if (warned > 0) { warned--; write_log (_T("SYNC ERROR2 debug event missing!?\n")); } } #endif } void inprec_recorddebug (uae_u32 val) { #if INPUTRECORD_DEBUG > 0 if (inprec_rstart (INPREC_DEBUG)) { inprec_ru32 (uaerandgetseed ()); inprec_ru32 (val); inprec_rend (); } #endif } void inprec_playdebug (uae_u32 val) { #if INPUTRECORD_DEBUG > 0 extern void activate_debugger (void); static uae_u32 pcs[16]; int err = 0; if (inprec_pstart (INPREC_DEBUG)) { uae_u32 seed1 = uaerandgetseed (); uae_u32 seed2 = inprec_pu32 (); if (seed1 != seed2) { write_log (_T("SYNC ERROR seed %08x != %08x\n"), seed1, seed2); err = 1; } uae_u32 val2 = inprec_pu32 (); if (val != val2) { write_log (_T("SYNC ERROR val %08x != %08x\n"), val, val2); err = 1; } inprec_pend (); } else if (input_play > 0) { gui_message (_T("SYNC ERROR debug event missing!?\n")); } #endif } void inprec_recordevent (int nr, int state, int max, int autofire) { if (savestate_state) return; if (input_record < INPREC_RECORD_NORMAL) return; if (inprec_rstart (INPREC_EVENT)) { inprec_ru16 (nr); inprec_ru16 (state); inprec_ru16 (max); inprec_ru16 (autofire ? 1 : 0); inprec_rend (); if (input_record == INPREC_RECORD_NORMAL) input_record = INPREC_RECORD_RERECORD; } } void inprec_recorddiskchange (int nr, const TCHAR *fname, bool writeprotected) { if (savestate_state) return; if (input_record < INPREC_RECORD_NORMAL) return; if (fname && fname[0]) { if (inprec_rstart (INPREC_DISKINSERT)) { inprec_ru8 (nr); inprec_ru8 (writeprotected ? 1 : 0); inprec_rstr (fname); write_log (_T("INPREC: disk insert %d '%s'\n"), nr, fname); inprec_rend (); } } else { if (inprec_rstart (INPREC_DISKREMOVE)) { inprec_ru8 (nr); write_log (_T("INPREC: disk eject %d\n"), nr); inprec_rend (); } } } int inprec_getposition (void) { int pos = -1; if (input_play == INPREC_PLAY_RERECORD) { pos = inprec_p - inprec_buffer; } else if (input_record) { pos = zfile_ftell (inprec_zf); } write_log (_T("INPREC: getpos=%d cycles=%08X\n"), pos, lastcycle); if (pos < 0) { write_log (_T("INPREC: getpos failure\n")); gui_message (_T("INPREC error")); } return pos; } // normal play to re-record void inprec_playtorecord (void) { write_log (_T("INPREC: PLAY to RE-RECORD\n")); replaypos = 0; findlast (); input_play = INPREC_PLAY_RERECORD; input_record = INPREC_RECORD_PLAYING; zfile_fclose (inprec_zf); inprec_zf = zfile_fopen_empty (NULL, _T("inp")); zfile_fwrite (inprec_buffer, header_end2, 1, inprec_zf); uae_u8 *p = inprec_buffer + header_end2; uae_u8 *end = inprec_buffer + inprec_size; while (p < end) { int len = (p[1] << 8) | (p[2] << 0); p[0] &= ~0x80; p += len; } zfile_fwrite (inprec_buffer + header_end2, inprec_size - header_end2, 1, inprec_zf); inprec_realtime (false); savestate_capture_request (); } void inprec_setposition (int offset, int replaycounter) { if (!inprec_buffer) return; replaypos = replaycounter; write_log (_T("INPREC: setpos=%d\n"), offset); if (offset < header_end || offset > zfile_size (inprec_zf)) { write_log (_T("INPREC: buffer corruption. offset=%d, size=%d\n"), offset, zfile_size (inprec_zf)); gui_message (_T("INPREC error")); } zfile_fseek (inprec_zf, 0, SEEK_SET); xfree (inprec_buffer); inprec_size = zfile_size (inprec_zf); inprec_buffer = xmalloc (uae_u8, inprec_size); zfile_fread (inprec_buffer, inprec_size, 1, inprec_zf); inprec_p = inprec_plastptr = inprec_buffer + offset; findlast (); input_play = INPREC_PLAY_RERECORD; input_record = INPREC_RECORD_PLAYING; if (currprefs.inprec_autoplay == false) inprec_realtime (false); } static void savelog (const TCHAR *path, const TCHAR *file) { TCHAR tmp[MAX_DPATH]; _tcscpy (tmp, path); _tcscat (tmp, file); _tcscat (tmp, _T(".log.txt")); struct zfile *zfd = zfile_fopen (tmp, _T("wb")); if (zfd) { int loglen; uae_u8 *log; loglen = 0; log = save_log (TRUE, &loglen); if (log) zfile_fwrite (log, loglen, 1, zfd); xfree (log); loglen = 0; log = save_log (FALSE, &loglen); if (log) zfile_fwrite (log, loglen, 1, zfd); xfree (log); zfile_fclose (zfd); write_log (_T("log '%s' saved\n"), tmp); } } static int savedisk (const TCHAR *path, const TCHAR *file, uae_u8 *data, uae_u8 *outdata) { int len = 0; TCHAR *fname = utf8u ((const char*)data + 2); if (fname[0]) { TCHAR tmp[MAX_DPATH]; TCHAR filename[MAX_DPATH]; filename[0] = 0; struct zfile *zf = zfile_fopen (fname, _T("rb"), ZFD_NORMAL); if (!zf) { _tcscpy (tmp, path); _tcscat (tmp, fname); zf = zfile_fopen (tmp, _T("rb"), ZFD_NORMAL); if (!zf) write_log (_T("failed to open '%s'\n"), tmp); } if (zf) { _tcscpy (tmp, path); _tcscpy (filename, file); _tcscat (filename, _T(".")); getfilepart (filename + _tcslen (filename), MAX_DPATH, zfile_getname (zf)); _tcscat (tmp, filename); struct zfile *zfd = zfile_fopen (tmp, _T("wb")); if (zfd) { int size = zfile_size (zf); uae_u8 *data = zfile_getdata (zf, 0, size); zfile_fwrite (data, size, 1, zfd); zfile_fclose (zfd); xfree (data); } zfile_fclose (zf); setwriteprotect (fname, data[1] != 0); } if (filename[0]) { outdata[0] = data[0]; char *fn = uutf8 (filename); strcpy ((char*)outdata + 2, fn); xfree (fn); len = 2 + strlen ((char*)outdata + 2) + 1; } } xfree (fname); return len; } void inprec_save (const TCHAR *filename, const TCHAR *statefilename) { TCHAR path[MAX_DPATH], file[MAX_DPATH]; if (!inprec_buffer) return; getpathpart (path, sizeof path / sizeof (TCHAR), filename); getfilepart (file, sizeof file / sizeof (TCHAR), filename); struct zfile *zf = zfile_fopen (filename, _T("wb"), 0); if (zf) { TCHAR fn[MAX_DPATH]; uae_u8 *data; data = zfile_getdata (inprec_zf, 0, header_end); zfile_fwrite (data, header_end, 1, zf); xfree (data); getfilepart (fn, MAX_DPATH, statefilename); char *s = uutf8 (fn); zfile_fwrite (s, strlen (s) + 1, 1, zf); int len = zfile_size (inprec_zf) - header_end2; data = zfile_getdata (inprec_zf, header_end2, len); uae_u8 *p = data; uae_u8 *end = data + len; while (p < end) { uae_u8 tmp[MAX_DPATH]; int plen = (p[1] << 8) | (p[2] << 0); int wlen = plen - HEADERSIZE; memcpy (tmp, p + HEADERSIZE, wlen); if (p[0] == INPREC_DISKINSERT) { wlen = savedisk (path, file, p + HEADERSIZE, tmp); } if (wlen) { wlen += HEADERSIZE; p[1] = wlen >> 8; p[2] = wlen; zfile_fwrite (p, HEADERSIZE, 1, zf); zfile_fwrite (tmp, wlen - HEADERSIZE, 1, zf); } else { zfile_fwrite (p, plen, 1, zf); } p += plen; } xfree (data); zfile_fclose (zf); savelog (path, file); write_log (_T("inputfile '%s' saved\n"), filename); } else { write_log (_T("failed to open '%s'\n"), filename); } } bool inprec_realtime (void) { if (input_record != INPREC_RECORD_PLAYING || input_play != INPREC_PLAY_RERECORD) return false; //clear_inputstate (); return inprec_realtime (false); } void inprec_getstatus (TCHAR *title) { TCHAR *p; if (!input_record && !input_play) return; _tcscat (title, _T("[")); if (input_record) { if (input_record != INPREC_RECORD_PLAYING) _tcscat (title, _T("-REC-")); else _tcscat (title, _T("REPLAY")); } else if (input_play) { _tcscat (title, _T("PLAY-")); } _tcscat (title, _T(" ")); p = title + _tcslen (title); int mvp = current_maxvpos (); _stprintf (p, _T("%03d %02d:%02d:%02d/%02d:%02d:%02d"), replaypos, lasthsync / (vblank_hz * mvp * 60), ((int)(lasthsync / (vblank_hz * mvp)) % 60), (lasthsync / mvp) % (int)vblank_hz, endhsync / (vblank_hz * mvp * 60), ((int)(endhsync / (vblank_hz * mvp)) % 60), (endhsync / mvp) % (int)vblank_hz); p += _tcslen (p); _tcscat (p, _T("] ")); }