1 /*
2 * UAE - The Un*x Amiga Emulator
3 *
4 * Input record/playback
5 *
6 * Copyright 2010 Toni Wilen
7 *
8 */
9
10 #define INPUTRECORD_DEBUG 1
11 #define ENABLE_DEBUGGER 0
12
13 #define HEADERSIZE 12
14
15 #include "sysconfig.h"
16 #include "sysdeps.h"
17
18 #include "options.h"
19 #include "inputrecord.h"
20 #include "zfile.h"
21 #include "custom.h"
22 #include "savestate.h"
23 #include "cia.h"
24 #include "events.h"
25 #include "uae.h"
26 #include "disk.h"
27 #include "misc.h"
28 #include "fsdb.h"
29
30 #if INPUTRECORD_DEBUG > 0
31 #include "memory_uae.h"
32 #include "newcpu.h"
33 #endif
34
35 int inputrecord_debug = 3;
36
37 extern int inputdevice_logging;
38
39 #define INPREC_BUFFER_SIZE 10000
40
41 static uae_u8 *inprec_buffer, *inprec_p;
42 static struct zfile *inprec_zf;
43 static int inprec_size;
44 int input_record = 0;
45 int input_play = 0;
46 static uae_u8 *inprec_plast, *inprec_plastptr;
47 static int header_end, header_end2;
48 static int replaypos;
49 static int lasthsync, endhsync;
50 static TCHAR inprec_path[MAX_DPATH];
51 static uae_u32 seed;
52 static uae_u32 lastcycle;
53 static uae_u32 cycleoffset;
54
55 static uae_u32 pcs[16];
56 static uae_u32 pcs2[16];
57 extern void activate_debugger (void);
58 static int warned;
59
60 /* external prototypes */
61 extern void refreshtitle (void);
62 extern uae_u32 uaesrand (uae_u32 seed);
63 extern uae_u32 uaerandgetseed (void);
64
65
66
setlasthsync(void)67 static void setlasthsync (void)
68 {
69 if (lasthsync / current_maxvpos () != (int)(hsync_counter / current_maxvpos ()) ) {
70 lasthsync = hsync_counter;
71 refreshtitle ();
72 }
73 }
74
flush(void)75 static void flush (void)
76 {
77 if (inprec_p > inprec_buffer) {
78 zfile_fwrite (inprec_buffer, inprec_p - inprec_buffer, 1, inprec_zf);
79 inprec_p = inprec_buffer;
80 }
81 }
82
inprec_ru8(uae_u8 v)83 void inprec_ru8 (uae_u8 v)
84 {
85 if (!input_record || !inprec_zf)
86 return;
87 *inprec_p++= v;
88 }
89
inprec_ru16(uae_u16 v)90 void inprec_ru16 (uae_u16 v)
91 {
92 if (!input_record || !inprec_zf)
93 return;
94 inprec_ru8 ((uae_u8)(v >> 8));
95 inprec_ru8 ((uae_u8)v);
96 }
97
inprec_ru32(uae_u32 v)98 void inprec_ru32 (uae_u32 v)
99 {
100 if (!input_record || !inprec_zf)
101 return;
102 inprec_ru16 ((uae_u16)(v >> 16));
103 inprec_ru16 ((uae_u16)v);
104 }
105
inprec_rstr(const TCHAR * src)106 static void inprec_rstr (const TCHAR *src)
107 {
108 if (!input_record || !inprec_zf)
109 return;
110 char *s = uutf8 (src);
111 char *ss = s;
112 while (*s) {
113 inprec_ru8 (*s);
114 s++;
115 }
116 inprec_ru8 (0);
117 xfree (ss);
118 }
119
inprec_rstart(uae_u8 type)120 static bool inprec_rstart (uae_u8 type)
121 {
122 if (!input_record || !inprec_zf || input_record == INPREC_RECORD_PLAYING)
123 return false;
124 lastcycle = get_cycles ();
125 int mvp = current_maxvpos ();
126 if ((type != INPREC_DEBUG && type != INPREC_DEBUG2 && type != INPREC_CIADEBUG) || (0 && vsync_counter >= 49 && vsync_counter <= 51))
127 write_log (_T("INPREC: %010d/%03d: %d (%d/%d) %08x\n"), hsync_counter, current_hpos (), type, hsync_counter % mvp, mvp, lastcycle);
128 inprec_plast = inprec_p;
129 inprec_ru8 (type);
130 inprec_ru16 (0xffff);
131 inprec_ru32 (hsync_counter);
132 inprec_ru8 (current_hpos ());
133 inprec_ru32 (lastcycle);
134 return true;
135 }
136
inprec_rend(void)137 static void inprec_rend (void)
138 {
139 if (!input_record || !inprec_zf)
140 return;
141 int size = inprec_p - inprec_plast;
142 inprec_plast[1] = size >> 8;
143 inprec_plast[2] = size >> 0;
144 flush ();
145 endhsync = hsync_counter;
146 setlasthsync ();
147 }
148
inprec_realtime(bool stopstart)149 static bool inprec_realtime (bool stopstart)
150 {
151 if (input_record == INPREC_RECORD_RERECORD)
152 gui_message (_T("INPREC error"));
153 write_log (_T("INPREC: play -> record\n"));
154 input_record = INPREC_RECORD_RERECORD;
155 input_play = 0;
156 int offset = inprec_p - inprec_buffer;
157 zfile_fseek (inprec_zf, offset, SEEK_SET);
158 zfile_truncate (inprec_zf, offset);
159 xfree (inprec_buffer);
160 inprec_size = INPREC_BUFFER_SIZE;
161 inprec_buffer = inprec_p = xmalloc (uae_u8, inprec_size);
162 clear_inputstate ();
163 return true;
164 }
165
inprec_pstart(uae_u8 type)166 static int inprec_pstart (uae_u8 type)
167 {
168 uae_u8 *p = inprec_p;
169 uae_u32 hc = hsync_counter;
170 uae_u8 hpos = current_hpos ();
171 uae_u32 cycles = get_cycles ();
172 static uae_u8 *lastp;
173 uae_u32 hc_orig, hc2_orig;
174 int mvp = current_maxvpos ();
175
176 if (!input_play || !inprec_zf)
177 return 0;
178 if (savestate_state || hsync_counter > 0xffff0000)
179 return 0;
180 if (p == inprec_buffer + inprec_size) {
181 write_log (_T("INPREC: STOP\n"));
182 if (input_play == INPREC_PLAY_RERECORD) {
183 input_play = 0;
184 inprec_realtime (true);
185 } else {
186 inprec_close (true);
187 }
188 return 0;
189 } else if (p > inprec_buffer + inprec_size) {
190 write_log (_T("INPREC: buffer error\n"));
191 gui_message (_T("INPREC error"));
192 }
193 if (p[0] == INPREC_END) {
194 inprec_close (true);
195 return 0;
196 } else if (p[0] == INPREC_QUIT) {
197 inprec_close (true);
198 uae_quit ();
199 return 0;
200 }
201 hc_orig = hc;
202 for (;;) {
203 uae_u32 type2 = p[0];
204 uae_u32 hc2 = (p[3] << 24) | (p[4] << 16) | (p[5] << 8) | p[6];
205 uae_u32 hpos2 = p[7];
206 uae_u32 cycles2 = (p[8] << 24) | (p[9] << 16) | (p[10] << 8) | p[11];
207
208 if (p >= inprec_buffer + inprec_size)
209 break;
210 #if 0
211 if (p > lastp) {
212 write_log (_T("INPREC: Next %010d/%03d, %010d/%03d (%d/%d): %d (%d)\n"),
213 hc2, hpos2, hc, hpos, hc2 - hc, hpos2 - hpos, p[5 + 1], p[5]);
214 lastp = p;
215 }
216 #endif
217 hc2_orig = hc2;
218 if (type2 == type && hc > hc2) {
219 write_log (_T("INPREC: %010d/%03d > %010d/%03d: %d missed!\n"), hc, hpos, hc2, hpos2, p[0]);
220 #if ENABLE_DEBUGGER == 0
221 gui_message (_T("INPREC missed error"));
222 #else
223 activate_debugger ();
224 #endif
225 lastcycle = cycles;
226 inprec_plast = p;
227 inprec_plastptr = p + 12;
228 setlasthsync ();
229 return 1;
230 }
231 if (hc2 != hc) {
232 lastp = p;
233 break;
234 }
235 if (type2 == type) {
236 if (type != INPREC_DEBUG && type != INPREC_DEBUG2 && type != INPREC_CIADEBUG && cycles != cycles2)
237 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);
238 if (cycles != cycles2 + cycleoffset) {
239 if (warned > 0) {
240 warned--;
241 for (int i = 0; i < 7; i++)
242 write_log (_T("%08x (%08x) "), pcs[i], pcs2[i]);
243 write_log (_T("\n"));
244 }
245 cycleoffset = cycles - cycles2;
246 #if ENABLE_DEBUGGER == 0
247 gui_message (_T("INPREC OFFSET=%d\n"), (int)cycleoffset / CYCLE_UNIT);
248 #else
249 activate_debugger ();
250 #endif
251 }
252 lastcycle = cycles;
253 inprec_plast = p;
254 inprec_plastptr = p + 12;
255 setlasthsync ();
256 return 1;
257 }
258 if (type2 == INPREC_END || type2 == INPREC_QUIT)
259 break;
260 p += (p[1] << 8) | (p[2] << 0);
261 }
262 inprec_plast = NULL;
263 return 0;
264 }
265
inprec_pend(void)266 static void inprec_pend (void)
267 {
268 uae_u8 *p = inprec_p;
269 uae_u32 hc = hsync_counter;
270 uae_u32 hpos = current_hpos ();
271
272 if (!input_play || !inprec_zf)
273 return;
274 if (!inprec_plast)
275 return;
276 inprec_plast[0] |= 0x80;
277 inprec_plast = NULL;
278 inprec_plastptr = NULL;
279 for (;;) {
280 uae_u32 hc2 = (p[3] << 24) | (p[4] << 16) | (p[5] << 8) | p[6];
281 uae_u32 hpos2 = p[7];
282 if (hc2 != hc)
283 break;
284 if ((p[0] & 0x80) == 0)
285 return;
286 p += (p[1] << 8) | (p[2] << 0);
287 inprec_p = p;
288 }
289 }
290
inprec_pu8(void)291 static uae_u8 inprec_pu8 (void)
292 {
293 return *inprec_plastptr++;
294 }
inprec_pu16(void)295 static uae_u16 inprec_pu16 (void)
296 {
297 uae_u16 v = inprec_pu8 () << 8;
298 v |= inprec_pu8 ();
299 return v;
300 }
inprec_ps16(void)301 static uae_s16 inprec_ps16 (void)
302 {
303 uae_u16 v = inprec_pu8 () << 8;
304 v |= inprec_pu8 ();
305 return (uae_s16)v;
306 }
inprec_pu32(void)307 static uae_u32 inprec_pu32 (void)
308 {
309 uae_u32 v = inprec_pu16 () << 16;
310 v |= inprec_pu16 ();
311 return v;
312 }
inprec_pstr(TCHAR * dst)313 static int inprec_pstr (TCHAR *dst)
314 {
315 char tmp[MAX_DPATH];
316 char *s;
317 int len = 0;
318
319 *dst = 0;
320 s = tmp;
321 for(;;) {
322 char v = inprec_pu8 ();
323 *s++ = v;
324 if (!v)
325 break;
326 len++;
327 }
328 if (tmp[0]) {
329 TCHAR *d = utf8u (tmp);
330 _tcscpy (dst, d);
331 xfree (d);
332 }
333 return len;
334 }
335
findlast(void)336 static void findlast (void)
337 {
338 uae_u32 hsync = 0;
339 uae_u8 *p = inprec_p;
340 while (p < inprec_buffer + inprec_size) {
341 hsync = (p[3] << 24) | (p[4] << 16) | (p[5] << 8) | p[6];
342 uae_u16 len = (p[1] << 8) | (p[2] << 0);
343 p += len;
344 }
345 endhsync = hsync;
346 }
347
348
inprec_open(const TCHAR * fname,const TCHAR * statefilename)349 int inprec_open (const TCHAR *fname, const TCHAR *statefilename)
350 {
351 int i;
352
353 inprec_close (false);
354 if (fname == NULL)
355 inprec_zf = zfile_fopen_empty (NULL, _T("inp"), 0);
356 else
357 inprec_zf = zfile_fopen (fname, input_record ? _T("wb") : _T("rb"), ZFD_NORMAL);
358 if (inprec_zf == NULL)
359 return 0;
360
361 currprefs.cs_rtc = changed_prefs.cs_rtc = 0;
362
363 inprec_path[0] = 0;
364 if (fname)
365 getpathpart (inprec_path, sizeof inprec_path / sizeof (TCHAR), fname);
366 seed = (uae_u32)time(0);
367 inprec_size = INPREC_BUFFER_SIZE;
368 lasthsync = 0;
369 endhsync = 0;
370 warned = 10;
371 cycleoffset = 0;
372 header_end2 = 0;
373 if (input_play) {
374 uae_u32 id;
375 zfile_fseek (inprec_zf, 0, SEEK_END);
376 inprec_size = zfile_ftell (inprec_zf);
377 zfile_fseek (inprec_zf, 0, SEEK_SET);
378 inprec_buffer = inprec_p = xmalloc (uae_u8, inprec_size);
379 zfile_fread (inprec_buffer, inprec_size, 1, inprec_zf);
380 inprec_plastptr = inprec_buffer;
381 id = inprec_pu32();
382 if (id != 0x55414500 /* 'UAE\0' */ ) {
383 inprec_close (true);
384 return 0;
385 }
386 int v = inprec_pu8 ();
387 if (v != 2) {
388 inprec_close (true);
389 return 0;
390 }
391 inprec_pu8 ();
392 inprec_pu8 ();
393 inprec_pu8 ();
394 seed = inprec_pu32();
395 seed = uaesrand (seed);
396 vsync_counter = inprec_pu32 ();
397 hsync_counter = inprec_pu32 ();
398 i = inprec_pu32 ();
399 while (i-- > 0)
400 inprec_pu8 ();
401 header_end = inprec_plastptr - inprec_buffer;
402 inprec_pstr (savestate_fname);
403 if (savestate_fname[0]) {
404 savestate_state = STATE_RESTORE;
405 for (;;) {
406 TCHAR tmp[MAX_DPATH];
407 _tcscpy (tmp, fname);
408 _tcscat (tmp, _T(".uss"));
409 if (zfile_exists (tmp)) {
410 _tcscpy (savestate_fname, tmp);
411 break;
412 }
413 if (zfile_exists (savestate_fname))
414 break;
415 TCHAR *p = _tcsrchr (savestate_fname, '\\');
416 if (!p)
417 p = _tcsrchr (savestate_fname, '/');
418 if (!p)
419 p = savestate_fname;
420 else
421 p++;
422 if (zfile_exists (p)) {
423 _tcscpy (savestate_fname, p);
424 break;
425 }
426 fetch_statefilepath (tmp, sizeof tmp / sizeof (TCHAR));
427 _tcscat (tmp, p);
428 if (zfile_exists (tmp)) {
429 _tcscpy (savestate_fname, tmp);
430 break;
431 }
432 fetch_inputfilepath (tmp, sizeof tmp / sizeof (TCHAR));
433 _tcscat (tmp, p);
434 if (zfile_exists (tmp)) {
435 _tcscpy (savestate_fname, tmp);
436 break;
437 }
438 write_log (_T("Failed to open linked statefile '%s'\n"), savestate_fname);
439 savestate_fname[0] = 0;
440 savestate_state = 0;
441 break;
442 }
443 }
444 inprec_p = inprec_plastptr;
445 header_end2 = inprec_plastptr - inprec_buffer;
446 findlast ();
447 } else if (input_record) {
448 seed = uaesrand (seed);
449 inprec_buffer = inprec_p = xmalloc (uae_u8, inprec_size);
450 inprec_ru32 (0x55414500 /* 'UAE\0' */);
451 inprec_ru8 (2);
452 inprec_ru8 (UAEMAJOR);
453 inprec_ru8 (UAEMINOR);
454 inprec_ru8 (UAESUBREV);
455 inprec_ru32 (seed);
456 inprec_ru32 (vsync_counter);
457 inprec_ru32 (hsync_counter);
458 inprec_ru32 (0); // extra header size
459 flush ();
460 header_end2 = header_end = zfile_ftell (inprec_zf);
461 } else {
462 input_record = input_play = 0;
463 return 0;
464 }
465 if (inputrecord_debug) {
466 if (disk_debug_logging < 1)
467 disk_debug_logging = 1 | 2;
468 }
469 write_log (_T("inprec initialized '%s', play=%d rec=%d\n"), fname ? fname : _T("<internal>"), input_play, input_record);
470 refreshtitle ();
471 return 1;
472 }
473
inprec_startup(void)474 void inprec_startup (void)
475 {
476 uaesrand (seed);
477 }
478
inprec_prepare_record(const TCHAR * statefilename)479 bool inprec_prepare_record (const TCHAR *statefilename)
480 {
481 TCHAR state[MAX_DPATH];
482 int mode = statefilename ? 2 : 1;
483 state[0] = 0;
484 if (statefilename)
485 _tcscpy (state, statefilename);
486 if (hsync_counter > 0 && savestate_state == 0) {
487 TCHAR *s = _tcsrchr (changed_prefs.inprecfile, '\\');
488 if (!s)
489 s = _tcsrchr (changed_prefs.inprecfile, '/');
490 if (s) {
491 fetch_statefilepath (state, sizeof state / sizeof (TCHAR));
492 _tcscat (state, s + 1);
493 } else {
494 _tcscpy (state, changed_prefs.inprecfile);
495 }
496 _tcscat (state, _T(".uss"));
497 savestate_initsave (state, 1, 1, true);
498 save_state (state, _T("input recording test"));
499 mode = 2;
500 }
501 input_record = INPREC_RECORD_NORMAL;
502 inprec_open (changed_prefs.inprecfile, state);
503 changed_prefs.inprecfile[0] = currprefs.inprecfile[0] = 0;
504 return true;
505 }
506
507
inprec_close(bool clear)508 void inprec_close (bool clear)
509 {
510 if (clear)
511 input_play = input_record = 0;
512 if (!inprec_zf)
513 return;
514 if (inprec_buffer && input_record) {
515 if (inprec_rstart (INPREC_END))
516 inprec_rend ();
517 }
518 zfile_fclose (inprec_zf);
519 inprec_zf = NULL;
520 xfree (inprec_buffer);
521 inprec_buffer = NULL;
522 input_play = input_record = 0;
523 write_log (_T("inprec finished\n"));
524 refreshtitle ();
525 }
526
setwriteprotect(const TCHAR * fname,bool readonly)527 static void setwriteprotect (const TCHAR *fname, bool readonly)
528 {
529 struct mystat st;
530 int mode, oldmode;
531 if (!my_stat (fname, &st))
532 return;
533 oldmode = mode = st.mode;
534 mode &= ~FILEFLAG_WRITE;
535 if (!readonly)
536 mode |= FILEFLAG_WRITE;
537 if (mode != oldmode)
538 chmod (fname, mode);
539 }
540
inprec_playdiskchange(void)541 void inprec_playdiskchange (void)
542 {
543 if (!input_play)
544 return;
545 while (inprec_pstart (INPREC_DISKREMOVE)) {
546 int drv = inprec_pu8 ();
547 inprec_pend ();
548 write_log (_T("INPREC: disk eject drive %d\n"), drv);
549 disk_eject (drv);
550 }
551 while (inprec_pstart (INPREC_DISKINSERT)) {
552 int drv = inprec_pu8 ();
553 bool wp = inprec_pu8 () != 0;
554 TCHAR tmp[MAX_DPATH], tmp2[MAX_DPATH];
555 inprec_pstr (tmp);
556 _tcscpy (tmp2, tmp);
557 if (!zfile_exists (tmp)) {
558 TCHAR tmp3[MAX_DPATH];
559 _tcscpy (tmp3, inprec_path);
560 _tcscat (tmp3, tmp);
561 _tcscpy (tmp, tmp3);
562 }
563 if (!zfile_exists (tmp)) {
564 gui_message (_T("INPREC: Disk image\n'%s'\nnot found!\n"), tmp2);
565 }
566 _tcscpy (currprefs.floppyslots[drv].df, tmp);
567 _tcscpy (changed_prefs.floppyslots[drv].df, tmp);
568 setwriteprotect (tmp, wp);
569 disk_insert_force (drv, tmp, wp);
570 write_log (_T("INPREC: disk insert drive %d '%s'\n"), drv, tmp);
571 inprec_pend ();
572 }
573 }
574
inprec_playevent(int * nr,int * state,int * max,int * autofire)575 bool inprec_playevent (int *nr, int *state, int *max, int *autofire)
576 {
577 if (inprec_pstart (INPREC_EVENT)) {
578 *nr = inprec_ps16 ();
579 *state = inprec_ps16 ();
580 *max = inprec_pu16 ();
581 *autofire = inprec_ps16 () & 1;
582 inprec_pend ();
583 return true;
584 }
585 return false;
586 }
587
inprec_recorddebug_cia(uae_u32 v1,uae_u32 v2,uae_u32 v3)588 void inprec_recorddebug_cia (uae_u32 v1, uae_u32 v2, uae_u32 v3)
589 {
590 #if INPUTRECORD_DEBUG > 0
591 if (inprec_rstart (INPREC_CIADEBUG)) {
592 inprec_ru32 (v1);
593 inprec_ru32 (v2);
594 inprec_ru32 (v3);
595 inprec_rend ();
596 }
597 #endif
598 }
inprec_playdebug_cia(uae_u32 v1,uae_u32 v2,uae_u32 v3)599 void inprec_playdebug_cia (uae_u32 v1, uae_u32 v2, uae_u32 v3)
600 {
601 #if INPUTRECORD_DEBUG > 0
602 int err = 0;
603 if (inprec_pstart (INPREC_CIADEBUG)) {
604 uae_u32 vv1 = inprec_pu32 ();
605 uae_u32 vv2 = inprec_pu32 ();
606 uae_u32 vv3 = inprec_pu32 ();
607 if (vv1 != v1 || vv2 != v2 || vv3 != v3)
608 write_log (_T("CIA SYNC ERROR %08x,%08x %08x,%08x %08x,%08x\n"), vv1, v1, vv2, v2, vv3, v3);
609 inprec_pend ();
610 }
611 #endif
612 }
613
inprec_recorddebug_cpu(int mode)614 void inprec_recorddebug_cpu (int mode)
615 {
616 #if INPUTRECORD_DEBUG > 0
617 if (inprec_rstart (INPREC_DEBUG2)) {
618 inprec_ru32 (m68k_getpc ());
619 inprec_ru32 (get_cycles () | mode);
620 inprec_rend ();
621 }
622 #endif
623 }
inprec_playdebug_cpu(int mode)624 void inprec_playdebug_cpu (int mode)
625 {
626 #if INPUTRECORD_DEBUG > 0
627 int err = 0;
628 if (inprec_pstart (INPREC_DEBUG2)) {
629 uae_u32 pc1 = m68k_getpc ();
630 uae_u32 pc2 = inprec_pu32 ();
631 uae_u32 v1 = get_cycles () | mode;
632 uae_u32 v2 = inprec_pu32 ();
633 if (pc1 != pc2) {
634 if (warned > 0) {
635 warned--;
636 write_log (_T("SYNC ERROR2 PC %08x != %08x\n"), pc1, pc2);
637 for (int i = 0; i < 15; i++)
638 write_log (_T("%08x "), pcs[i]);
639 write_log (_T("\n"));
640
641 }
642 err = 1;
643 } else {
644 memmove (pcs + 1, pcs, 15 * 4);
645 pcs[0] = pc1;
646 memmove (pcs2 + 1, pcs2, 15 * 4);
647 pcs2[0] = get_cycles ();
648 }
649 if (v1 != v2) {
650 if (warned > 0) {
651 warned--;
652 write_log (_T("SYNC ERROR2 %08x != %08x\n"), v1, v2);
653 for (int i = 0; i < 15; i++)
654 write_log (_T("%08x "), pcs[i]);
655 write_log (_T("\n"));
656 }
657 err = 1;
658 }
659 inprec_pend ();
660 } else if (input_play > 0) {
661 if (warned > 0) {
662 warned--;
663 write_log (_T("SYNC ERROR2 debug event missing!?\n"));
664 }
665 }
666 #endif
667 }
668
inprec_recorddebug(uae_u32 val)669 void inprec_recorddebug (uae_u32 val)
670 {
671 #if INPUTRECORD_DEBUG > 0
672 if (inprec_rstart (INPREC_DEBUG)) {
673 inprec_ru32 (uaerandgetseed ());
674 inprec_ru32 (val);
675 inprec_rend ();
676 }
677 #endif
678 }
inprec_playdebug(uae_u32 val)679 void inprec_playdebug (uae_u32 val)
680 {
681 #if INPUTRECORD_DEBUG > 0
682 extern void activate_debugger (void);
683 static uae_u32 pcs[16];
684 int err = 0;
685 if (inprec_pstart (INPREC_DEBUG)) {
686 uae_u32 seed1 = uaerandgetseed ();
687 uae_u32 seed2 = inprec_pu32 ();
688 if (seed1 != seed2) {
689 write_log (_T("SYNC ERROR seed %08x != %08x\n"), seed1, seed2);
690 err = 1;
691 }
692 uae_u32 val2 = inprec_pu32 ();
693 if (val != val2) {
694 write_log (_T("SYNC ERROR val %08x != %08x\n"), val, val2);
695 err = 1;
696 }
697 inprec_pend ();
698 } else if (input_play > 0) {
699 gui_message (_T("SYNC ERROR debug event missing!?\n"));
700 }
701 #endif
702 }
703
704
inprec_recordevent(int nr,int state,int max,int autofire)705 void inprec_recordevent (int nr, int state, int max, int autofire)
706 {
707 if (savestate_state)
708 return;
709 if (input_record < INPREC_RECORD_NORMAL)
710 return;
711 if (inprec_rstart (INPREC_EVENT)) {
712 inprec_ru16 (nr);
713 inprec_ru16 (state);
714 inprec_ru16 (max);
715 inprec_ru16 (autofire ? 1 : 0);
716 inprec_rend ();
717 if (input_record == INPREC_RECORD_NORMAL)
718 input_record = INPREC_RECORD_RERECORD;
719 }
720 }
721
inprec_recorddiskchange(int nr,const TCHAR * fname,bool writeprotected)722 void inprec_recorddiskchange (int nr, const TCHAR *fname, bool writeprotected)
723 {
724 if (savestate_state)
725 return;
726 if (input_record < INPREC_RECORD_NORMAL)
727 return;
728 if (fname && fname[0]) {
729 if (inprec_rstart (INPREC_DISKINSERT)) {
730 inprec_ru8 (nr);
731 inprec_ru8 (writeprotected ? 1 : 0);
732 inprec_rstr (fname);
733 write_log (_T("INPREC: disk insert %d '%s'\n"), nr, fname);
734 inprec_rend ();
735 }
736 } else {
737 if (inprec_rstart (INPREC_DISKREMOVE)) {
738 inprec_ru8 (nr);
739 write_log (_T("INPREC: disk eject %d\n"), nr);
740 inprec_rend ();
741 }
742 }
743 }
744
inprec_getposition(void)745 int inprec_getposition (void)
746 {
747 int pos = -1;
748 if (input_play == INPREC_PLAY_RERECORD) {
749 pos = inprec_p - inprec_buffer;
750 } else if (input_record) {
751 pos = zfile_ftell (inprec_zf);
752 }
753 write_log (_T("INPREC: getpos=%d cycles=%08X\n"), pos, lastcycle);
754 if (pos < 0) {
755 write_log (_T("INPREC: getpos failure\n"));
756 gui_message (_T("INPREC error"));
757 }
758 return pos;
759 }
760
761 // normal play to re-record
inprec_playtorecord(void)762 void inprec_playtorecord (void)
763 {
764 write_log (_T("INPREC: PLAY to RE-RECORD\n"));
765 replaypos = 0;
766 findlast ();
767 input_play = INPREC_PLAY_RERECORD;
768 input_record = INPREC_RECORD_PLAYING;
769 zfile_fclose (inprec_zf);
770 inprec_zf = zfile_fopen_empty (NULL, _T("inp"), 0);
771 zfile_fwrite (inprec_buffer, header_end2, 1, inprec_zf);
772 uae_u8 *p = inprec_buffer + header_end2;
773 uae_u8 *end = inprec_buffer + inprec_size;
774 while (p < end) {
775 int len = (p[1] << 8) | (p[2] << 0);
776 p[0] &= ~0x80;
777 p += len;
778 }
779 zfile_fwrite (inprec_buffer + header_end2, inprec_size - header_end2, 1, inprec_zf);
780 inprec_realtime (false);
781 savestate_capture_request ();
782 }
783
inprec_setposition(int offset,int replaycounter)784 void inprec_setposition (int offset, int replaycounter)
785 {
786 if (!inprec_buffer)
787 return;
788 replaypos = replaycounter;
789 write_log (_T("INPREC: setpos=%d\n"), offset);
790 if (offset < header_end || offset > zfile_size (inprec_zf)) {
791 write_log (_T("INPREC: buffer corruption. offset=%d, size=%d\n"), offset, zfile_size (inprec_zf));
792 gui_message (_T("INPREC error"));
793 }
794 zfile_fseek (inprec_zf, 0, SEEK_SET);
795 xfree (inprec_buffer);
796 inprec_size = zfile_size (inprec_zf);
797 inprec_buffer = xmalloc (uae_u8, inprec_size);
798 zfile_fread (inprec_buffer, inprec_size, 1, inprec_zf);
799 inprec_p = inprec_plastptr = inprec_buffer + offset;
800 findlast ();
801 input_play = INPREC_PLAY_RERECORD;
802 input_record = INPREC_RECORD_PLAYING;
803 if (currprefs.inprec_autoplay == false)
804 inprec_realtime (false);
805 }
806
savelog(const TCHAR * path,const TCHAR * file)807 static void savelog (const TCHAR *path, const TCHAR *file)
808 {
809 TCHAR tmp[MAX_DPATH];
810
811 _tcscpy (tmp, path);
812 _tcscat (tmp, file);
813 _tcscat (tmp, _T(".log.txt"));
814 struct zfile *zfd = zfile_fopen (tmp, _T("wb"), 0);
815 if (zfd) {
816 int loglen;
817 uae_u8 *log;
818 loglen = 0;
819
820 zfile_fclose (zfd);
821 write_log (_T("log '%s' saved\n"), tmp);
822 }
823 }
824
savedisk(const TCHAR * path,const TCHAR * file,uae_u8 * data,uae_u8 * outdata)825 static int savedisk (const TCHAR *path, const TCHAR *file, uae_u8 *data, uae_u8 *outdata)
826 {
827 int len = 0;
828 TCHAR *fname = utf8u ((const char*)data + 2);
829 if (fname[0]) {
830 TCHAR tmp[MAX_DPATH];
831 TCHAR filename[MAX_DPATH];
832 filename[0] = 0;
833 struct zfile *zf = zfile_fopen (fname, _T("rb"), ZFD_NORMAL);
834 if (!zf) {
835 _tcscpy (tmp, path);
836 _tcscat (tmp, fname);
837 zf = zfile_fopen (tmp, _T("rb"), ZFD_NORMAL);
838 if (!zf)
839 write_log (_T("failed to open '%s'\n"), tmp);
840 }
841 if (zf) {
842 _tcscpy (tmp, path);
843 _tcscpy (filename, file);
844 _tcscat (filename, _T("."));
845 getfilepart (filename + _tcslen (filename), MAX_DPATH, zfile_getname (zf));
846 _tcscat (tmp, filename);
847 struct zfile *zfd = zfile_fopen (tmp, _T("wb"), 0);
848 if (zfd) {
849 int size = zfile_size (zf);
850 uae_u8 *data = zfile_getdata (zf, 0, size);
851 zfile_fwrite (data, size, 1, zfd);
852 zfile_fclose (zfd);
853 xfree (data);
854 }
855 zfile_fclose (zf);
856 setwriteprotect (fname, data[1] != 0);
857 }
858 if (filename[0]) {
859 outdata[0] = data[0];
860 char *fn = uutf8 (filename);
861 strcpy ((char*)outdata + 2, fn);
862 xfree (fn);
863 len = 2 + strlen ((char*)outdata + 2) + 1;
864 }
865 }
866 xfree (fname);
867 return len;
868 }
869
inprec_save(const TCHAR * filename,const TCHAR * statefilename)870 void inprec_save (const TCHAR *filename, const TCHAR *statefilename)
871 {
872 TCHAR path[MAX_DPATH], file[MAX_DPATH];
873 if (!inprec_buffer)
874 return;
875 getpathpart (path, sizeof path / sizeof (TCHAR), filename);
876 getfilepart (file, sizeof file / sizeof (TCHAR), filename);
877 struct zfile *zf = zfile_fopen (filename, _T("wb"), 0);
878 if (zf) {
879 TCHAR fn[MAX_DPATH];
880 uae_u8 *data;
881 data = zfile_getdata (inprec_zf, 0, header_end);
882 zfile_fwrite (data, header_end, 1, zf);
883 xfree (data);
884 getfilepart (fn, MAX_DPATH, statefilename);
885 char *s = uutf8 (fn);
886 zfile_fwrite (s, strlen (s) + 1, 1, zf);
887 int len = zfile_size (inprec_zf) - header_end2;
888 data = zfile_getdata (inprec_zf, header_end2, len);
889 uae_u8 *p = data;
890 uae_u8 *end = data + len;
891 while (p < end) {
892 uae_u8 tmp[MAX_DPATH];
893 int plen = (p[1] << 8) | (p[2] << 0);
894 int wlen = plen - HEADERSIZE;
895 memcpy (tmp, p + HEADERSIZE, wlen);
896 if (p[0] == INPREC_DISKINSERT) {
897 wlen = savedisk (path, file, p + HEADERSIZE, tmp);
898 }
899 if (wlen) {
900 wlen += HEADERSIZE;
901 p[1] = wlen >> 8;
902 p[2] = wlen;
903 zfile_fwrite (p, HEADERSIZE, 1, zf);
904 zfile_fwrite (tmp, wlen - HEADERSIZE, 1, zf);
905 } else {
906 zfile_fwrite (p, plen, 1, zf);
907 }
908 p += plen;
909 }
910 xfree (data);
911 zfile_fclose (zf);
912 savelog (path, file);
913 write_log (_T("inputfile '%s' saved\n"), filename);
914 } else {
915 write_log (_T("failed to open '%s'\n"), filename);
916 }
917 }
918
inprec_realtimev(void)919 bool inprec_realtimev (void)
920 {
921 if (input_record != INPREC_RECORD_PLAYING || input_play != INPREC_PLAY_RERECORD)
922 return false;
923 //clear_inputstate ();
924 return inprec_realtime (false);
925 }
926
inprec_getstatus(TCHAR * title)927 void inprec_getstatus (TCHAR *title)
928 {
929 TCHAR *p;
930 if (!input_record && !input_play)
931 return;
932 _tcscat (title, _T("["));
933 if (input_record) {
934 if (input_record != INPREC_RECORD_PLAYING)
935 _tcscat (title, _T("-REC-"));
936 else
937 _tcscat (title, _T("REPLAY"));
938 } else if (input_play) {
939 _tcscat (title, _T("PLAY-"));
940 }
941 _tcscat (title, _T(" "));
942 p = title + _tcslen (title);
943 int mvp = current_maxvpos ();
944 _stprintf (p, _T("%03d %02d:%02d:%02d/%02d:%02d:%02d"), replaypos,
945 lasthsync / (vblank_hz * mvp * 60), ((int)(lasthsync / (vblank_hz * mvp)) % 60), (lasthsync / mvp) % (int)vblank_hz,
946 endhsync / (vblank_hz * mvp * 60), ((int)(endhsync / (vblank_hz * mvp)) % 60), (endhsync / mvp) % (int)vblank_hz);
947 p += _tcslen (p);
948 _tcscat (p, _T("] "));
949
950 }
951