~random-stuff/random-stuff/snes9x-OLD

1 by OV2
Initial 1.52 import
1
/***********************************************************************************
2
  Snes9x - Portable Super Nintendo Entertainment System (TM) emulator.
3
4
  (c) Copyright 1996 - 2002  Gary Henderson (gary.henderson@ntlworld.com),
5
                             Jerremy Koot (jkoot@snes9x.com)
6
7
  (c) Copyright 2002 - 2004  Matthew Kendora
8
9
  (c) Copyright 2002 - 2005  Peter Bortas (peter@bortas.org)
10
11
  (c) Copyright 2004 - 2005  Joel Yliluoma (http://iki.fi/bisqwit/)
12
13
  (c) Copyright 2001 - 2006  John Weidman (jweidman@slip.net)
14
15
  (c) Copyright 2002 - 2006  funkyass (funkyass@spam.shaw.ca),
16
                             Kris Bleakley (codeviolation@hotmail.com)
17
18
  (c) Copyright 2002 - 2010  Brad Jorsch (anomie@users.sourceforge.net),
19
                             Nach (n-a-c-h@users.sourceforge.net),
87 by OV2
Update copyright in all files
20
21
  (c) Copyright 2002 - 2011  zones (kasumitokoduck@yahoo.com)
1 by OV2
Initial 1.52 import
22
23
  (c) Copyright 2006 - 2007  nitsuja
24
290 by Brandon Wright
Update some copyrights, bump version number.
25
  (c) Copyright 2009 - 2016  BearOso,
1 by OV2
Initial 1.52 import
26
                             OV2
27
28
29
  BS-X C emulator code
30
  (c) Copyright 2005 - 2006  Dreamer Nom,
31
                             zones
32
33
  C4 x86 assembler and some C emulation code
34
  (c) Copyright 2000 - 2003  _Demo_ (_demo_@zsnes.com),
35
                             Nach,
36
                             zsKnight (zsknight@zsnes.com)
37
38
  C4 C++ code
39
  (c) Copyright 2003 - 2006  Brad Jorsch,
40
                             Nach
41
42
  DSP-1 emulator code
43
  (c) Copyright 1998 - 2006  _Demo_,
44
                             Andreas Naive (andreasnaive@gmail.com),
45
                             Gary Henderson,
46
                             Ivar (ivar@snes9x.com),
47
                             John Weidman,
48
                             Kris Bleakley,
49
                             Matthew Kendora,
50
                             Nach,
51
                             neviksti (neviksti@hotmail.com)
52
53
  DSP-2 emulator code
54
  (c) Copyright 2003         John Weidman,
55
                             Kris Bleakley,
56
                             Lord Nightmare (lord_nightmare@users.sourceforge.net),
57
                             Matthew Kendora,
58
                             neviksti
59
60
  DSP-3 emulator code
61
  (c) Copyright 2003 - 2006  John Weidman,
62
                             Kris Bleakley,
63
                             Lancer,
64
                             z80 gaiden
65
66
  DSP-4 emulator code
67
  (c) Copyright 2004 - 2006  Dreamer Nom,
68
                             John Weidman,
69
                             Kris Bleakley,
70
                             Nach,
71
                             z80 gaiden
72
73
  OBC1 emulator code
74
  (c) Copyright 2001 - 2004  zsKnight,
75
                             pagefault (pagefault@zsnes.com),
76
                             Kris Bleakley
77
                             Ported from x86 assembler to C by sanmaiwashi
78
79
  SPC7110 and RTC C++ emulator code used in 1.39-1.51
80
  (c) Copyright 2002         Matthew Kendora with research by
81
                             zsKnight,
82
                             John Weidman,
83
                             Dark Force
84
85
  SPC7110 and RTC C++ emulator code used in 1.52+
86
  (c) Copyright 2009         byuu,
87
                             neviksti
88
89
  S-DD1 C emulator code
90
  (c) Copyright 2003         Brad Jorsch with research by
91
                             Andreas Naive,
92
                             John Weidman
93
94
  S-RTC C emulator code
95
  (c) Copyright 2001 - 2006  byuu,
96
                             John Weidman
97
98
  ST010 C++ emulator code
99
  (c) Copyright 2003         Feather,
100
                             John Weidman,
101
                             Kris Bleakley,
102
                             Matthew Kendora
103
104
  Super FX x86 assembler emulator code
105
  (c) Copyright 1998 - 2003  _Demo_,
106
                             pagefault,
107
                             zsKnight
108
109
  Super FX C emulator code
110
  (c) Copyright 1997 - 1999  Ivar,
111
                             Gary Henderson,
112
                             John Weidman
113
114
  Sound emulator code used in 1.5-1.51
115
  (c) Copyright 1998 - 2003  Brad Martin
116
  (c) Copyright 1998 - 2006  Charles Bilyue'
117
118
  Sound emulator code used in 1.52+
119
  (c) Copyright 2004 - 2007  Shay Green (gblargg@gmail.com)
120
295 by Brandon Wright
Add byuu copyright for his S-SMP
121
  S-SMP emulator code used in 1.54+
122
  (c) Copyright 2016         byuu
123
1 by OV2
Initial 1.52 import
124
  SH assembler code partly based on x86 assembler code
125
  (c) Copyright 2002 - 2004  Marcus Comstedt (marcus@mc.pp.se)
126
127
  2xSaI filter
128
  (c) Copyright 1999 - 2001  Derek Liauw Kie Fa
129
130
  HQ2x, HQ3x, HQ4x filters
131
  (c) Copyright 2003         Maxim Stepin (maxim@hiend3d.com)
132
133
  NTSC filter
134
  (c) Copyright 2006 - 2007  Shay Green
135
136
  GTK+ GUI code
290 by Brandon Wright
Update some copyrights, bump version number.
137
  (c) Copyright 2004 - 2016  BearOso
1 by OV2
Initial 1.52 import
138
139
  Win32 GUI code
140
  (c) Copyright 2003 - 2006  blip,
141
                             funkyass,
142
                             Matthew Kendora,
143
                             Nach,
144
                             nitsuja
298 by Brandon Wright
Bump OV2 Windows port copyright.
145
  (c) Copyright 2009 - 2016  OV2
1 by OV2
Initial 1.52 import
146
147
  Mac OS GUI code
148
  (c) Copyright 1998 - 2001  John Stiles
87 by OV2
Update copyright in all files
149
  (c) Copyright 2001 - 2011  zones
1 by OV2
Initial 1.52 import
150
151
152
  Specific ports contains the works of other authors. See headers in
153
  individual files.
154
155
156
  Snes9x homepage: http://www.snes9x.com/
157
158
  Permission to use, copy, modify and/or distribute Snes9x in both binary
159
  and source form, for non-commercial purposes, is hereby granted without
160
  fee, providing that this license information and copyright notice appear
161
  with all copies and any derived work.
162
163
  This software is provided 'as-is', without any express or implied
164
  warranty. In no event shall the authors be held liable for any damages
165
  arising from the use of this software or it's derivatives.
166
167
  Snes9x is freeware for PERSONAL USE only. Commercial users should
168
  seek permission of the copyright holders first. Commercial use includes,
169
  but is not limited to, charging money for Snes9x or software derived from
170
  Snes9x, including Snes9x or derivatives in commercial game bundles, and/or
171
  using Snes9x as a promotion for your commercial product.
172
173
  The copyright holders request that bug fixes and improvements to the code
174
  should be forwarded to them so everyone can benefit from the modifications
175
  in future versions.
176
177
  Super NES and Super Nintendo Entertainment System are trademarks of
178
  Nintendo Co., Limited and its subsidiary companies.
179
 ***********************************************************************************/
180
181
//  Input recording/playback code
182
//  (c) Copyright 2004 blip
183
184
#ifndef __WIN32__
185
#include <unistd.h>
186
#endif
187
#include "snes9x.h"
188
#include "memmap.h"
189
#include "controls.h"
190
#include "snapshot.h"
191
#include "movie.h"
192
#include "language.h"
193
#ifdef NETPLAY_SUPPORT
194
#include "netplay.h"
195
#endif
196
197
#ifdef __WIN32__
198
#include <io.h>
199
#ifndef W_OK
200
#define W_OK 2
201
#endif
202
#define ftruncate chsize
203
#endif
204
205
#define SMV_MAGIC				0x1a564d53 // SMV0x1a
206
#define SMV_VERSION				5
207
#define SMV_HEADER_SIZE			64
208
#define SMV_EXTRAROMINFO_SIZE	30
209
#define BUFFER_GROWTH_SIZE		4096
210
211
enum MovieState
212
{
213
	MOVIE_STATE_NONE = 0,
214
	MOVIE_STATE_PLAY,
215
	MOVIE_STATE_RECORD
216
};
217
218
struct SMovie
219
{
220
	enum MovieState	State;
221
222
	FILE	*File;
223
	char	Filename[PATH_MAX + 1];
224
	char	ROMName[23];
225
	uint32	ROMCRC32;
226
	uint32	MovieId;
227
	uint32	Version;
228
229
	uint32	SaveStateOffset;
230
	uint32	ControllerDataOffset;
231
232
	uint8	ControllersMask;
233
	uint8	Opts;
234
	uint8	SyncFlags;
235
	uint32	MaxFrame;
236
	uint32	MaxSample;
237
	uint32	CurrentFrame;
238
	uint32	CurrentSample;
239
	uint32	BytesPerSample;
240
	uint32	RerecordCount;
241
	bool8	ReadOnly;
242
	uint8	PortType[2];
243
	int8	PortIDs[2][4];
244
245
	uint8	*InputBuffer;
246
	uint8	*InputBufferPtr;
247
	uint32	InputBufferSize;
248
};
249
250
static struct SMovie	Movie;
251
252
static uint8	prevPortType[2];
253
static int8		prevPortIDs[2][4];
254
static bool8	prevMouseMaster, prevSuperScopeMaster, prevJustifierMaster, prevMultiPlayer5Master;
255
256
static uint8	Read8 (uint8 *&);
257
static uint16	Read16 (uint8 *&);
258
static uint32	Read32 (uint8 *&);
259
static void		Write8 (uint8, uint8 *&);
260
static void		Write16 (uint16, uint8 *&);
261
static void		Write32 (uint32, uint8 *&);
262
static void		store_previous_settings (void);
263
static void		restore_previous_settings (void);
264
static void		store_movie_settings (void);
265
static void		restore_movie_settings (void);
266
static int		bytes_per_sample (void);
267
static void		reserve_buffer_space (uint32);
268
static void		reset_controllers (void);
269
static void		read_frame_controller_data (bool);
270
static void		write_frame_controller_data (void);
271
static void		flush_movie (void);
272
static void		truncate_movie (void);
273
static int		read_movie_header (FILE *, SMovie *);
274
static int		read_movie_extrarominfo (FILE *, SMovie *);
275
static void		write_movie_header (FILE *, SMovie *);
276
static void		write_movie_extrarominfo (FILE *, SMovie *);
277
static void		change_state (MovieState);
278
279
// HACK: reduce movie size by not storing changes that can only affect polled input in the movie for these types,
280
//       because currently no port sets these types to polling
281
#define SKIPPED_POLLING_PORT_TYPE(x)	(((x) == CTL_NONE) || ((x) == CTL_JOYPAD) || ((x) == CTL_MP5))
282
283
#ifndef max
284
#define max(a, b)	(((a) > (b)) ? (a) : (b))
285
#endif
286
287
288
static uint8 Read8 (uint8 *&ptr)
289
{
290
	uint8	v = *ptr++;
291
	return (v);
292
}
293
294
static uint16 Read16 (uint8 *&ptr)
295
{
296
	uint16	v = READ_WORD(ptr);
297
	ptr += 2;
298
	return (v);
299
}
300
301
static uint32 Read32 (uint8 *&ptr)
302
{
303
	uint32	v = READ_DWORD(ptr);
304
	ptr += 4;
305
	return (v);
306
}
307
308
static void Write8 (uint8 v, uint8 *&ptr)
309
{
310
	*ptr++ = v;
311
}
312
313
static void Write16 (uint16 v, uint8 *&ptr)
314
{
315
	WRITE_WORD(ptr, v);
316
	ptr += 2;
317
}
318
319
static void Write32 (uint32 v, uint8 *&ptr)
320
{
321
	WRITE_DWORD(ptr, v);
322
	ptr += 4;
323
}
324
325
static void store_previous_settings (void)
326
{
327
	for (int i = 0; i < 2; i++)
328
	{
329
		enum controllers pt;
330
		S9xGetController(i, &pt, &prevPortIDs[i][0], &prevPortIDs[i][1], &prevPortIDs[i][2], &prevPortIDs[i][3]);
331
		prevPortType[i] = (uint8) pt;
332
	}
333
334
	prevMouseMaster        = Settings.MouseMaster;
335
	prevSuperScopeMaster   = Settings.SuperScopeMaster;
336
	prevJustifierMaster    = Settings.JustifierMaster;
337
	prevMultiPlayer5Master = Settings.MultiPlayer5Master;
338
}
339
340
static void restore_previous_settings (void)
341
{
342
	Settings.MouseMaster        = prevMouseMaster;
343
	Settings.SuperScopeMaster   = prevSuperScopeMaster;
344
	Settings.JustifierMaster    = prevJustifierMaster;
345
	Settings.MultiPlayer5Master = prevMultiPlayer5Master;
346
347
	S9xSetController(0, (enum controllers) prevPortType[0], prevPortIDs[0][0], prevPortIDs[0][1], prevPortIDs[0][2], prevPortIDs[0][3]);
348
	S9xSetController(1, (enum controllers) prevPortType[1], prevPortIDs[1][0], prevPortIDs[1][1], prevPortIDs[1][2], prevPortIDs[1][3]);
349
}
350
351
static void store_movie_settings (void)
352
{
353
	for (int i = 0; i < 2; i++)
354
	{
355
		enum controllers pt;
356
		S9xGetController(i, &pt, &Movie.PortIDs[i][0], &Movie.PortIDs[i][1], &Movie.PortIDs[i][2], &Movie.PortIDs[i][3]);
357
		Movie.PortType[i] = (uint8) pt;
358
	}
359
}
360
361
static void restore_movie_settings (void)
362
{
363
	Settings.MouseMaster        = (Movie.PortType[0] == CTL_MOUSE      || Movie.PortType[1] == CTL_MOUSE);
364
	Settings.SuperScopeMaster   = (Movie.PortType[0] == CTL_SUPERSCOPE || Movie.PortType[1] == CTL_SUPERSCOPE);
365
	Settings.JustifierMaster    = (Movie.PortType[0] == CTL_JUSTIFIER  || Movie.PortType[1] == CTL_JUSTIFIER);
366
	Settings.MultiPlayer5Master = (Movie.PortType[0] == CTL_MP5        || Movie.PortType[1] == CTL_MP5);
367
368
	S9xSetController(0, (enum controllers) Movie.PortType[0], Movie.PortIDs[0][0], Movie.PortIDs[0][1], Movie.PortIDs[0][2], Movie.PortIDs[0][3]);
369
	S9xSetController(1, (enum controllers) Movie.PortType[1], Movie.PortIDs[1][0], Movie.PortIDs[1][1], Movie.PortIDs[1][2], Movie.PortIDs[1][3]);
370
}
371
372
static int bytes_per_sample (void)
373
{
374
	int	num_controllers = 0;
375
376
	for (int i = 0; i < 8; i++)
377
	{
378
		if (Movie.ControllersMask & (1 << i))
379
			num_controllers++;
380
	}
381
382
	int	bytes = CONTROLLER_DATA_SIZE * num_controllers;
383
384
	for (int p = 0; p < 2; p++)
385
	{
386
		if (Movie.PortType[p] == CTL_MOUSE)
387
			bytes += MOUSE_DATA_SIZE;
388
		else
389
		if (Movie.PortType[p] == CTL_SUPERSCOPE)
390
			bytes += SCOPE_DATA_SIZE;
391
		else
392
		if (Movie.PortType[p] == CTL_JUSTIFIER)
393
			bytes += JUSTIFIER_DATA_SIZE;
394
	}
395
396
	return (bytes);
397
}
398
399
static void reserve_buffer_space (uint32 space_needed)
400
{
401
	if (space_needed > Movie.InputBufferSize)
402
	{
403
		uint32 ptr_offset   = Movie.InputBufferPtr - Movie.InputBuffer;
404
		uint32 alloc_chunks = space_needed / BUFFER_GROWTH_SIZE;
405
406
		Movie.InputBufferSize = BUFFER_GROWTH_SIZE * (alloc_chunks + 1);
407
		Movie.InputBuffer     = (uint8 *) realloc(Movie.InputBuffer, Movie.InputBufferSize);
408
		Movie.InputBufferPtr  = Movie.InputBuffer + ptr_offset;
409
	}
410
}
411
412
static void reset_controllers (void)
413
{
414
	for (int i = 0; i < 8; i++)
415
		MovieSetJoypad(i, 0);
416
417
	uint8 clearedMouse[MOUSE_DATA_SIZE];
164 by OV2
Change ZeroMemory to memset
418
	memset(clearedMouse, 0, MOUSE_DATA_SIZE);
1 by OV2
Initial 1.52 import
419
	clearedMouse[4] = 1;
420
421
	uint8 clearedScope[SCOPE_DATA_SIZE];
164 by OV2
Change ZeroMemory to memset
422
	memset(clearedScope, 0, SCOPE_DATA_SIZE);
1 by OV2
Initial 1.52 import
423
424
	uint8 clearedJustifier[JUSTIFIER_DATA_SIZE];
164 by OV2
Change ZeroMemory to memset
425
	memset(clearedJustifier, 0, JUSTIFIER_DATA_SIZE);
1 by OV2
Initial 1.52 import
426
427
	for (int p = 0; p < 2; p++)
428
	{
429
		MovieSetMouse(p, clearedMouse, true);
430
		MovieSetScope(p, clearedScope);
431
		MovieSetJustifier(p, clearedJustifier);
432
	}
433
}
434
435
static void read_frame_controller_data (bool addFrame)
436
{
437
	// reset code check
438
	if (Movie.InputBufferPtr[0] == 0xff)
439
	{
440
		bool reset = true;
441
		for (int i = 1; i < (int) Movie.BytesPerSample; i++)
442
		{
443
			if (Movie.InputBufferPtr[i] != 0xff)
444
			{
445
				reset = false;
446
				break;
447
			}
448
		}
449
450
		if (reset)
451
		{
452
			Movie.InputBufferPtr += Movie.BytesPerSample;
453
			S9xSoftReset();
454
			return;
455
		}
456
	}
457
458
	for (int i = 0; i < 8; i++)
459
	{
460
		if (Movie.ControllersMask & (1 << i))
461
			MovieSetJoypad(i, Read16(Movie.InputBufferPtr));
462
		else
463
			MovieSetJoypad(i, 0); // pretend the controller is disconnected
464
	}
465
466
	for (int p = 0; p < 2; p++)
467
	{
468
		if (Movie.PortType[p] == CTL_MOUSE)
469
		{
470
			uint8 buf[MOUSE_DATA_SIZE];
471
			memcpy(buf, Movie.InputBufferPtr, MOUSE_DATA_SIZE);
472
			Movie.InputBufferPtr += MOUSE_DATA_SIZE;
473
			MovieSetMouse(p, buf, !addFrame);
474
		}
475
		else
476
		if (Movie.PortType[p] == CTL_SUPERSCOPE)
477
		{
478
			uint8 buf[SCOPE_DATA_SIZE];
479
			memcpy(buf, Movie.InputBufferPtr, SCOPE_DATA_SIZE);
480
			Movie.InputBufferPtr += SCOPE_DATA_SIZE;
481
			MovieSetScope(p, buf);
482
		}
483
		else
484
		if (Movie.PortType[p] == CTL_JUSTIFIER)
485
		{
486
			uint8 buf[JUSTIFIER_DATA_SIZE];
487
			memcpy(buf, Movie.InputBufferPtr, JUSTIFIER_DATA_SIZE);
488
			Movie.InputBufferPtr += JUSTIFIER_DATA_SIZE;
489
			MovieSetJustifier(p, buf);
490
		}
491
	}
492
}
493
494
static void write_frame_controller_data (void)
495
{
496
	reserve_buffer_space((uint32) (Movie.InputBufferPtr + Movie.BytesPerSample - Movie.InputBuffer));
497
498
	for (int i = 0; i < 8; i++)
499
	{
500
		if (Movie.ControllersMask & (1 << i))
501
			Write16(MovieGetJoypad(i), Movie.InputBufferPtr);
502
		else
503
			MovieSetJoypad(i, 0); // pretend the controller is disconnected
504
	}
505
506
	for (int p = 0; p < 2; p++)
507
	{
508
		if (Movie.PortType[p] == CTL_MOUSE)
509
		{
510
			uint8 buf[MOUSE_DATA_SIZE];
511
			MovieGetMouse(p, buf);
512
			memcpy(Movie.InputBufferPtr, buf, MOUSE_DATA_SIZE);
513
			Movie.InputBufferPtr += MOUSE_DATA_SIZE;
514
		}
515
		else
516
		if (Movie.PortType[p] == CTL_SUPERSCOPE)
517
		{
518
			uint8 buf[SCOPE_DATA_SIZE];
519
			MovieGetScope(p, buf);
520
			memcpy(Movie.InputBufferPtr, buf, SCOPE_DATA_SIZE);
521
			Movie.InputBufferPtr += SCOPE_DATA_SIZE;
522
		}
523
		else
524
		if (Movie.PortType[p] == CTL_JUSTIFIER)
525
		{
526
			uint8 buf[JUSTIFIER_DATA_SIZE];
527
			MovieGetJustifier(p, buf);
528
			memcpy(Movie.InputBufferPtr, buf, JUSTIFIER_DATA_SIZE);
529
			Movie.InputBufferPtr += JUSTIFIER_DATA_SIZE;
530
		}
531
	}
532
}
533
534
static void flush_movie (void)
535
{
536
	if (!Movie.File)
537
		return;
538
539
	fseek(Movie.File, 0, SEEK_SET);
540
	write_movie_header(Movie.File, &Movie);
541
	fseek(Movie.File, Movie.ControllerDataOffset, SEEK_SET);
542
543
	size_t	ignore;
544
	ignore = fwrite(Movie.InputBuffer, 1, Movie.BytesPerSample * (Movie.MaxSample + 1), Movie.File);
545
}
546
547
static void truncate_movie (void)
548
{
549
	if (!Movie.File || !Settings.MovieTruncate)
550
		return;
551
552
	if (Movie.SaveStateOffset > Movie.ControllerDataOffset)
553
		return;
554
555
	int	ignore;
556
	ignore = ftruncate(fileno(Movie.File), Movie.ControllerDataOffset + Movie.BytesPerSample * (Movie.MaxSample + 1));
557
}
558
559
static int read_movie_header (FILE *fd, SMovie *movie)
560
{
561
	uint32	value;
562
	uint8	buf[SMV_HEADER_SIZE], *ptr = buf;
563
564
	if (fread(buf, 1, SMV_HEADER_SIZE, fd) != SMV_HEADER_SIZE)
565
		return (WRONG_FORMAT);
566
567
	value = Read32(ptr);
568
	if (value != SMV_MAGIC)
569
		return (WRONG_FORMAT);
570
571
	value = Read32(ptr);
572
	if(value > SMV_VERSION || value < 4)
573
		return (WRONG_VERSION);
574
575
	movie->Version              = value;
576
	movie->MovieId              = Read32(ptr);
577
	movie->RerecordCount        = Read32(ptr);
578
	movie->MaxFrame             = Read32(ptr);
579
	movie->ControllersMask      = Read8(ptr);
580
	movie->Opts                 = Read8(ptr);
581
	ptr++;
582
	movie->SyncFlags            = Read8(ptr);
583
	movie->SaveStateOffset      = Read32(ptr);
584
	movie->ControllerDataOffset = Read32(ptr);
585
	movie->MaxSample            = Read32(ptr);
586
	movie->PortType[0]          = Read8(ptr);
587
	movie->PortType[1]          = Read8(ptr);
588
	for (int p = 0; p < 2; p++)
589
	{
590
		for (int i = 0; i < 4; i++)
591
			movie->PortIDs[p][i] = Read8(ptr);
592
	}
593
594
	if (movie->MaxSample < movie->MaxFrame)
595
		movie->MaxSample = movie->MaxFrame;
596
597
	return (SUCCESS);
598
}
599
600
static int read_movie_extrarominfo (FILE *fd, SMovie *movie)
601
{
602
	uint8	buf[SMV_EXTRAROMINFO_SIZE], *ptr = buf;
603
604
	fseek(fd, movie->SaveStateOffset - SMV_EXTRAROMINFO_SIZE, SEEK_SET);
605
606
	if (fread(buf, 1, SMV_EXTRAROMINFO_SIZE, fd) != SMV_EXTRAROMINFO_SIZE)
607
		return (WRONG_FORMAT);
608
609
	ptr += 3; // zero bytes
610
	movie->ROMCRC32 = Read32(ptr);
611
	strncpy(movie->ROMName, (char *) ptr, 23);
612
613
	return (SUCCESS);
614
}
615
616
static void write_movie_header (FILE *fd, SMovie *movie)
617
{
618
	uint8	buf[SMV_HEADER_SIZE], *ptr = buf;
619
164 by OV2
Change ZeroMemory to memset
620
	memset(buf, 0, sizeof(buf));
1 by OV2
Initial 1.52 import
621
622
	Write32(SMV_MAGIC, ptr);
623
	Write32(SMV_VERSION, ptr);
624
	Write32(movie->MovieId, ptr);
625
	Write32(movie->RerecordCount, ptr);
626
	Write32(movie->MaxFrame, ptr);
627
	Write8(movie->ControllersMask, ptr);
628
	Write8(movie->Opts, ptr);
629
	ptr++;
630
	Write8(movie->SyncFlags, ptr);
631
	Write32(movie->SaveStateOffset, ptr);
632
	Write32(movie->ControllerDataOffset, ptr);
633
	Write32(movie->MaxSample, ptr);
634
	Write8(movie->PortType[0], ptr);
635
	Write8(movie->PortType[1], ptr);
636
	for (int p = 0; p < 2; p++)
637
	{
638
		for (int i = 0; i < 4; i++)
639
			Write8(movie->PortIDs[p][i], ptr);
640
	}
641
642
	size_t	ignore;
643
	ignore = fwrite(buf, 1, SMV_HEADER_SIZE, fd);
644
}
645
646
static void write_movie_extrarominfo (FILE *fd, SMovie *movie)
647
{
648
	uint8	buf[SMV_EXTRAROMINFO_SIZE], *ptr = buf;
649
650
	Write8(0, ptr);
651
	Write8(0, ptr);
652
	Write8(0, ptr);
653
	Write32(movie->ROMCRC32, ptr);
654
	strncpy((char *) ptr, movie->ROMName, 23);
655
656
	size_t	ignore;
657
	ignore = fwrite(buf, 1, SMV_EXTRAROMINFO_SIZE, fd);
658
}
659
660
static void change_state (MovieState new_state)
661
{
662
	if (new_state == Movie.State)
663
		return;
664
665
	if (Movie.State == MOVIE_STATE_RECORD)
666
		flush_movie();
667
668
	if (new_state == MOVIE_STATE_NONE)
669
	{
670
		truncate_movie();
671
		fclose(Movie.File);
672
		Movie.File = NULL;
673
674
		if (S9xMoviePlaying() || S9xMovieRecording())
675
			restore_previous_settings();
676
	}
677
678
	Movie.State = new_state;
679
}
680
681
void S9xMovieFreeze (uint8 **buf, uint32 *size)
682
{
683
	if (!S9xMovieActive())
684
		return;
685
686
	uint32	size_needed;
687
	uint8	*ptr;
688
689
	size_needed = sizeof(Movie.MovieId) + sizeof(Movie.CurrentFrame) + sizeof(Movie.MaxFrame) + sizeof(Movie.CurrentSample) + sizeof(Movie.MaxSample);
690
	size_needed += (uint32) (Movie.BytesPerSample * (Movie.MaxSample + 1));
691
	*size = size_needed;
692
693
	*buf = new uint8[size_needed];
694
	ptr = *buf;
695
	if (!ptr)
696
		return;
697
698
	Write32(Movie.MovieId, ptr);
699
	Write32(Movie.CurrentFrame, ptr);
700
	Write32(Movie.MaxFrame, ptr);
701
	Write32(Movie.CurrentSample, ptr);
702
	Write32(Movie.MaxSample, ptr);
703
704
	memcpy(ptr, Movie.InputBuffer, Movie.BytesPerSample * (Movie.MaxSample + 1));
705
}
706
707
int S9xMovieUnfreeze (uint8 *buf, uint32 size)
708
{
709
	if (!S9xMovieActive())
710
		return (FILE_NOT_FOUND);
711
712
	if (size < sizeof(Movie.MovieId) + sizeof(Movie.CurrentFrame) + sizeof(Movie.MaxFrame) + sizeof(Movie.CurrentSample) + sizeof(Movie.MaxSample))
713
		return (WRONG_FORMAT);
714
715
	uint8	*ptr = buf;
716
717
	uint32	movie_id       = Read32(ptr);
718
	uint32	current_frame  = Read32(ptr);
719
	uint32	max_frame      = Read32(ptr);
720
	uint32	current_sample = Read32(ptr);
721
	uint32	max_sample     = Read32(ptr);
722
	uint32	space_needed   = (Movie.BytesPerSample * (max_sample + 1));
723
724
	if (current_frame > max_frame || current_sample > max_sample || space_needed > size)
725
		return (WRONG_MOVIE_SNAPSHOT);
726
727
	if (Settings.WrongMovieStateProtection)
728
		if (movie_id != Movie.MovieId)
729
			if (max_frame < Movie.MaxFrame || max_sample < Movie.MaxSample || memcmp(Movie.InputBuffer, ptr, space_needed))
730
				return (WRONG_MOVIE_SNAPSHOT);
731
732
	if (!Movie.ReadOnly)
733
	{
734
		change_state(MOVIE_STATE_RECORD);
735
736
		Movie.CurrentFrame  = current_frame;
737
		Movie.MaxFrame      = max_frame;
738
		Movie.CurrentSample = current_sample;
739
		Movie.MaxSample     = max_sample;
740
		Movie.RerecordCount++;
741
742
		store_movie_settings();
743
744
		reserve_buffer_space(space_needed);
745
		memcpy(Movie.InputBuffer, ptr, space_needed);
746
747
		flush_movie();
748
		fseek(Movie.File, Movie.ControllerDataOffset + (Movie.BytesPerSample * (Movie.CurrentSample + 1)), SEEK_SET);
749
	}
750
	else
751
	{
40 by Brandon Wright
(gocha) "Fix wrong logic of snapshot inconsistent with movie"
752
      uint32   space_processed = (Movie.BytesPerSample * (current_sample + 1));
753
      if (current_frame > Movie.MaxFrame || current_sample > Movie.MaxSample || memcmp(Movie.InputBuffer, ptr, space_processed))
1 by OV2
Initial 1.52 import
754
			return (SNAPSHOT_INCONSISTENT);
755
756
		change_state(MOVIE_STATE_PLAY);
757
758
		Movie.CurrentFrame  = current_frame;
759
		Movie.CurrentSample = current_sample;
760
	}
761
762
	Movie.InputBufferPtr = Movie.InputBuffer + (Movie.BytesPerSample * Movie.CurrentSample);
763
	read_frame_controller_data(true);
764
765
	return (SUCCESS);
766
}
767
768
int S9xMovieOpen (const char *filename, bool8 read_only)
769
{
770
	FILE	*fd;
771
	STREAM	stream;
772
	int		result;
773
	int		fn;
774
775
	if (!(fd = fopen(filename, "rb+")))
776
	{
777
		if (!(fd = fopen(filename, "rb")))
778
			return (FILE_NOT_FOUND);
779
		else
780
			read_only = TRUE;
781
	}
782
783
	change_state(MOVIE_STATE_NONE);
784
785
	result = read_movie_header(fd, &Movie);
786
	if (result != SUCCESS)
787
	{
788
		fclose(fd);
789
		return (result);
790
	}
791
792
	read_movie_extrarominfo(fd, &Movie);
793
32 by Brandon Wright
Remove POSIX dup and access calls, and rename qword to fix compilation
794
	fflush(fd);
795
	fn = fileno(fd);
1 by OV2
Initial 1.52 import
796
797
	store_previous_settings();
798
	restore_movie_settings();
799
800
	lseek(fn, Movie.SaveStateOffset, SEEK_SET);
801
	stream = REOPEN_STREAM(fn, "rb");
802
	if (!stream)
803
		return (FILE_NOT_FOUND);
804
805
	if (Movie.Opts & MOVIE_OPT_FROM_RESET)
806
	{
807
		S9xReset();
808
		reset_controllers();
809
		result = (READ_STREAM(Memory.SRAM, 0x20000, stream) == 0x20000) ? SUCCESS : WRONG_FORMAT;
810
	}
811
	else
812
		result = S9xUnfreezeFromStream(stream);
813
814
	CLOSE_STREAM(stream);
815
816
	if (result != SUCCESS)
817
		return (result);
818
819
	if (!(fd = fopen(filename, "rb+")))
820
	{
821
		if (!(fd = fopen(filename, "rb")))
822
			return (FILE_NOT_FOUND);
823
		else
824
			read_only = TRUE;
825
	}
826
827
	if (fseek(fd, Movie.ControllerDataOffset, SEEK_SET))
288 by Brandon Wright
Fix some possible problems from cppcheck. Everything else looks fine.
828
	{
829
		fclose(fd);
1 by OV2
Initial 1.52 import
830
		return (WRONG_FORMAT);
288 by Brandon Wright
Fix some possible problems from cppcheck. Everything else looks fine.
831
	}
1 by OV2
Initial 1.52 import
832
833
	Movie.File           = fd;
834
	Movie.BytesPerSample = bytes_per_sample();
835
	Movie.InputBufferPtr = Movie.InputBuffer;
836
	reserve_buffer_space(Movie.BytesPerSample * (Movie.MaxSample + 1));
837
838
	size_t	ignore;
839
	ignore = fread(Movie.InputBufferPtr, 1, Movie.BytesPerSample * (Movie.MaxSample + 1), fd);
840
841
	// read "baseline" controller data
842
	if (Movie.MaxSample && Movie.MaxFrame)
843
		read_frame_controller_data(true);
844
845
	Movie.CurrentFrame  = 0;
846
	Movie.CurrentSample = 0;
847
	Movie.ReadOnly      = read_only;
848
	strncpy(Movie.Filename, filename, PATH_MAX + 1);
849
	Movie.Filename[PATH_MAX] = 0;
850
851
	change_state(MOVIE_STATE_PLAY);
852
853
	S9xUpdateFrameCounter(-1);
854
855
	S9xMessage(S9X_INFO, S9X_MOVIE_INFO, MOVIE_INFO_REPLAY);
856
857
	return (SUCCESS);
858
}
859
860
int S9xMovieCreate (const char *filename, uint8 controllers_mask, uint8 opts, const wchar_t *metadata, int metadata_length)
861
{
862
	FILE	*fd;
863
	STREAM	stream;
864
865
	if (controllers_mask == 0)
866
		return (WRONG_FORMAT);
867
868
	if (!(fd = fopen(filename, "wb")))
869
		return (FILE_NOT_FOUND);
870
871
	if (metadata_length > MOVIE_MAX_METADATA)
872
		metadata_length = MOVIE_MAX_METADATA;
873
874
	change_state(MOVIE_STATE_NONE);
875
876
	store_previous_settings();
877
	store_movie_settings();
878
879
	Movie.MovieId              = (uint32) time(NULL);
880
	Movie.RerecordCount        = 0;
881
	Movie.MaxFrame             = 0;
882
	Movie.MaxSample            = 0;
883
	Movie.SaveStateOffset      = SMV_HEADER_SIZE + (sizeof(uint16) * metadata_length) + SMV_EXTRAROMINFO_SIZE;
884
	Movie.ControllerDataOffset = 0;
885
	Movie.ControllersMask      = controllers_mask;
886
	Movie.Opts                 = opts;
887
	Movie.SyncFlags            = MOVIE_SYNC_DATA_EXISTS | MOVIE_SYNC_HASROMINFO;
888
889
	write_movie_header(fd, &Movie);
890
891
	// convert wchar_t metadata string/array to a uint16 array
892
	// XXX: UTF-8 is much better...
893
	if (metadata_length > 0)
894
	{
895
		uint8 meta_buf[sizeof(uint16) * MOVIE_MAX_METADATA];
896
		for (int i = 0; i < metadata_length; i++)
897
		{
898
			uint16 c = (uint16) metadata[i];
899
			meta_buf[i * 2]     = (uint8) (c & 0xff);
900
			meta_buf[i * 2 + 1] = (uint8) ((c >> 8) & 0xff);
901
		}
902
903
		size_t	ignore;
904
		ignore = fwrite(meta_buf, sizeof(uint16), metadata_length, fd);
905
	}
906
907
	Movie.ROMCRC32 = Memory.ROMCRC32;
908
	strncpy(Movie.ROMName, Memory.RawROMName, 23);
909
910
	write_movie_extrarominfo(fd, &Movie);
911
912
	fclose(fd);
913
32 by Brandon Wright
Remove POSIX dup and access calls, and rename qword to fix compilation
914
	stream = OPEN_STREAM(filename, "ab");
1 by OV2
Initial 1.52 import
915
	if (!stream)
916
		return (FILE_NOT_FOUND);
917
918
	if (opts & MOVIE_OPT_FROM_RESET)
919
	{
920
		S9xReset();
921
		reset_controllers();
922
		WRITE_STREAM(Memory.SRAM, 0x20000, stream);
923
	}
924
	else
925
		S9xFreezeToStream(stream);
926
927
	CLOSE_STREAM(stream);
928
929
	if (!(fd = fopen(filename, "rb+")))
930
		return (FILE_NOT_FOUND);
931
932
	fseek(fd, 0, SEEK_END);
933
	Movie.ControllerDataOffset = (uint32) ftell(fd);
934
935
	// 16-byte align the controller input, for hex-editing friendliness if nothing else
936
	while (Movie.ControllerDataOffset % 16)
937
	{
938
		fputc(0xcc, fd); // arbitrary
939
		Movie.ControllerDataOffset++;
940
	}
941
942
	// write "baseline" controller data
943
	Movie.File           = fd;
944
	Movie.BytesPerSample = bytes_per_sample();
945
	Movie.InputBufferPtr = Movie.InputBuffer;
946
	write_frame_controller_data();
947
948
	Movie.CurrentFrame  = 0;
949
	Movie.CurrentSample = 0;
950
	Movie.ReadOnly      = false;
951
	strncpy(Movie.Filename, filename, PATH_MAX + 1);
952
	Movie.Filename[PATH_MAX] = 0;
953
954
	change_state(MOVIE_STATE_RECORD);
955
956
	S9xUpdateFrameCounter(-1);
957
958
	S9xMessage(S9X_INFO, S9X_MOVIE_INFO, MOVIE_INFO_RECORD);
959
960
	return (SUCCESS);
961
}
962
963
int S9xMovieGetInfo (const char *filename, struct MovieInfo *info)
964
{
965
	FILE	*fd;
966
	SMovie	local_movie;
967
	int		metadata_length;
968
	int		result, i;
969
970
	flush_movie();
971
164 by OV2
Change ZeroMemory to memset
972
	memset(info, 0, sizeof(*info));
1 by OV2
Initial 1.52 import
973
974
	if (!(fd = fopen(filename, "rb")))
975
		return (FILE_NOT_FOUND);
976
977
	result = read_movie_header(fd, &local_movie);
978
	if (result != SUCCESS)
288 by Brandon Wright
Fix some possible problems from cppcheck. Everything else looks fine.
979
	{
980
		fclose(fd);
1 by OV2
Initial 1.52 import
981
		return (result);
288 by Brandon Wright
Fix some possible problems from cppcheck. Everything else looks fine.
982
	}
1 by OV2
Initial 1.52 import
983
984
	info->TimeCreated     = (time_t) local_movie.MovieId;
985
	info->Version         = local_movie.Version;
986
	info->Opts            = local_movie.Opts;
987
	info->SyncFlags       = local_movie.SyncFlags;
988
	info->ControllersMask = local_movie.ControllersMask;
989
	info->RerecordCount   = local_movie.RerecordCount;
990
	info->LengthFrames    = local_movie.MaxFrame;
991
	info->LengthSamples   = local_movie.MaxSample;
992
	info->PortType[0]     = local_movie.PortType[0];
993
	info->PortType[1]     = local_movie.PortType[1];
994
995
	if (local_movie.SaveStateOffset > SMV_HEADER_SIZE)
996
	{
997
		uint8	meta_buf[sizeof(uint16) * MOVIE_MAX_METADATA];
998
		int		curRomInfoSize = (local_movie.SyncFlags & MOVIE_SYNC_HASROMINFO) ? SMV_EXTRAROMINFO_SIZE : 0;
999
1000
		metadata_length = ((int) local_movie.SaveStateOffset - SMV_HEADER_SIZE - curRomInfoSize) / sizeof(uint16);
1001
		metadata_length = (metadata_length >= MOVIE_MAX_METADATA) ? MOVIE_MAX_METADATA - 1 : metadata_length;
1002
		metadata_length = (int) fread(meta_buf, sizeof(uint16), metadata_length, fd);
1003
1004
		for (i = 0; i < metadata_length; i++)
1005
		{
1006
			uint16 c = meta_buf[i * 2] | (meta_buf[i * 2 + 1] << 8);
1007
			info->Metadata[i] = (wchar_t) c;
1008
		}
1009
1010
		info->Metadata[i] = '\0';
1011
	}
1012
	else
1013
		info->Metadata[0] = '\0';
1014
1015
	read_movie_extrarominfo(fd, &local_movie);
1016
1017
	info->ROMCRC32 = local_movie.ROMCRC32;
1018
	strncpy(info->ROMName, local_movie.ROMName, 23);
1019
1020
	fclose(fd);
32 by Brandon Wright
Remove POSIX dup and access calls, and rename qword to fix compilation
1021
	if ((fd = fopen(filename, "r+")) == NULL)
1 by OV2
Initial 1.52 import
1022
		info->ReadOnly = true;
32 by Brandon Wright
Remove POSIX dup and access calls, and rename qword to fix compilation
1023
	else
1024
		fclose(fd);
1 by OV2
Initial 1.52 import
1025
1026
	return (SUCCESS);
1027
}
1028
1029
void S9xMovieUpdate (bool addFrame)
1030
{
1031
	switch (Movie.State)
1032
	{
1033
		case MOVIE_STATE_PLAY:
1034
		{
1035
			if (Movie.CurrentFrame >= Movie.MaxFrame || Movie.CurrentSample >= Movie.MaxSample)
1036
			{
1037
				change_state(MOVIE_STATE_NONE);
1038
				S9xMessage(S9X_INFO, S9X_MOVIE_INFO, MOVIE_INFO_END);
1039
				return;
1040
			}
1041
			else
1042
			{
1043
				if (addFrame)
1044
					S9xUpdateFrameCounter();
1045
				else
1046
				if (SKIPPED_POLLING_PORT_TYPE(Movie.PortType[0]) && SKIPPED_POLLING_PORT_TYPE(Movie.PortType[1]))
1047
					return;
1048
1049
				read_frame_controller_data(addFrame);
1050
				Movie.CurrentSample++;
1051
				if (addFrame)
1052
					Movie.CurrentFrame++;
1053
			}
1054
1055
			break;
1056
		}
1057
1058
		case MOVIE_STATE_RECORD:
1059
		{
1060
			if (addFrame)
1061
				S9xUpdateFrameCounter();
1062
			else
1063
			if (SKIPPED_POLLING_PORT_TYPE(Movie.PortType[0]) && SKIPPED_POLLING_PORT_TYPE(Movie.PortType[1]))
1064
				return;
1065
1066
			write_frame_controller_data();
1067
			Movie.MaxSample = ++Movie.CurrentSample;
1068
			if (addFrame)
1069
				Movie.MaxFrame = ++Movie.CurrentFrame;
1070
1071
			size_t	ignore;
1072
			ignore = fwrite((Movie.InputBufferPtr - Movie.BytesPerSample), 1, Movie.BytesPerSample, Movie.File);
1073
1074
			break;
1075
		}
1076
1077
		default:
1078
		{
1079
			if (addFrame)
1080
				S9xUpdateFrameCounter();
1081
1082
			break;
1083
		}
1084
	}
1085
}
1086
1087
void S9xMovieUpdateOnReset (void)
1088
{
1089
	if (Movie.State == MOVIE_STATE_RECORD)
1090
	{
1091
		reserve_buffer_space((uint32) (Movie.InputBufferPtr + Movie.BytesPerSample - Movie.InputBuffer));
1092
		memset(Movie.InputBufferPtr, 0xFF, Movie.BytesPerSample);
1093
		Movie.InputBufferPtr += Movie.BytesPerSample;
1094
		Movie.MaxSample = ++Movie.CurrentSample;
1095
		Movie.MaxFrame = ++Movie.CurrentFrame;
1096
1097
		size_t	ignore;
1098
		ignore = fwrite((Movie.InputBufferPtr - Movie.BytesPerSample), 1, Movie.BytesPerSample, Movie.File);
1099
	}
1100
}
1101
1102
void S9xMovieInit (void)
1103
{
164 by OV2
Change ZeroMemory to memset
1104
	memset(&Movie, 0, sizeof(Movie));
1 by OV2
Initial 1.52 import
1105
	Movie.State = MOVIE_STATE_NONE;
1106
}
1107
1108
void S9xMovieStop (bool8 suppress_message)
1109
{
1110
	if (Movie.State != MOVIE_STATE_NONE)
1111
	{
1112
		change_state(MOVIE_STATE_NONE);
1113
1114
		if (!suppress_message)
1115
			S9xMessage(S9X_INFO, S9X_MOVIE_INFO, MOVIE_INFO_STOP);
1116
	}
1117
}
1118
1119
void S9xMovieShutdown (void)
1120
{
1121
	if (S9xMovieActive())
1122
		S9xMovieStop(TRUE);
1123
}
1124
1125
bool8 S9xMovieActive (void)
1126
{
1127
	return (Movie.State != MOVIE_STATE_NONE);
1128
}
1129
1130
bool8 S9xMoviePlaying (void)
1131
{
1132
	return (Movie.State == MOVIE_STATE_PLAY);
1133
}
1134
1135
bool8 S9xMovieRecording (void)
1136
{
1137
	return (Movie.State == MOVIE_STATE_RECORD);
1138
}
1139
1140
uint8 S9xMovieControllers (void)
1141
{
1142
	return (Movie.ControllersMask);
1143
}
1144
1145
bool8 S9xMovieReadOnly (void)
1146
{
1147
	if (!S9xMovieActive())
1148
		return (FALSE);
1149
	return (Movie.ReadOnly);
1150
}
1151
1152
uint32 S9xMovieGetId (void)
1153
{
1154
	if (!S9xMovieActive())
1155
		return (0);
1156
	return (Movie.MovieId);
1157
}
1158
1159
uint32 S9xMovieGetLength (void)
1160
{
1161
	if (!S9xMovieActive())
1162
		return (0);
1163
	return (Movie.MaxFrame);
1164
}
1165
1166
uint32 S9xMovieGetFrameCounter (void)
1167
{
1168
	if (!S9xMovieActive())
1169
		return (0);
1170
	return (Movie.CurrentFrame);
1171
}
1172
1173
void S9xMovieToggleRecState (void)
1174
{
1175
	Movie.ReadOnly = !Movie.ReadOnly;
1176
1177
	if (Movie.ReadOnly)
1178
	   S9xMessage(S9X_INFO, S9X_MOVIE_INFO, "Movie is now read-only.");
1179
	else
1180
	   S9xMessage(S9X_INFO, S9X_MOVIE_INFO, "Movie is now read+write.");
1181
}
1182
1183
void S9xMovieToggleFrameDisplay (void)
1184
{
1185
	Settings.DisplayMovieFrame = !Settings.DisplayMovieFrame;
1186
	S9xReRefresh();
1187
}
1188
1189
void S9xUpdateFrameCounter (int offset)
1190
{
1191
	extern bool8	pad_read;
1192
1193
	offset++;
1194
1195
	if (!Settings.DisplayMovieFrame)
1196
		*GFX.FrameDisplayString = 0;
1197
	else
1198
	if (Movie.State == MOVIE_STATE_RECORD)
1199
		sprintf(GFX.FrameDisplayString, "Recording frame: %d%s",
1200
			max(0, (int) (Movie.CurrentFrame + offset)), pad_read || !Settings.MovieNotifyIgnored ? "" : " (ignored)");
1201
	else
1202
	if (Movie.State == MOVIE_STATE_PLAY)
1203
		sprintf(GFX.FrameDisplayString, "Playing frame: %d / %d",
1204
			max(0, (int) (Movie.CurrentFrame + offset)), Movie.MaxFrame);
1205
#ifdef NETPLAY_SUPPORT
1206
	else
1207
	if (Settings.NetPlay)
1208
		sprintf(GFX.FrameDisplayString, "%s frame: %d", Settings.NetPlayServer ? "Server" : "Client",
1209
			max(0, (int) (NetPlay.FrameCount + offset)));
1210
#endif
1211
}