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