1/*****************************************************************************\
2 Snes9x - Portable Super Nintendo Entertainment System (TM) emulator.
3 This file is licensed under the Snes9x License.
4 For further information, consult the LICENSE file in the root directory.
5\*****************************************************************************/
6
7#include <stdlib.h>
8#include <unistd.h>
9#include <ctype.h>
10#include <fcntl.h>
11#include <dirent.h>
12#include <signal.h>
13#include <errno.h>
14#include <string.h>
15#ifdef HAVE_STRINGS_H
16#include <strings.h>
17#endif
18#ifdef USE_THREADS
19#include <sched.h>
20#include <pthread.h>
21#endif
22#include <sys/stat.h>
23#include <sys/time.h>
24#include <sys/types.h>
25#ifdef HAVE_SYS_IOCTL_H
26#include <sys/ioctl.h>
27#endif
28#ifndef NOSOUND
29#include <sys/soundcard.h>
30#include <sys/mman.h>
31#endif
32#ifdef JOYSTICK_SUPPORT
33#include <linux/joystick.h>
34#endif
35
36#include "snes9x.h"
37#include "memmap.h"
38#include "apu/apu.h"
39#include "gfx.h"
40#include "snapshot.h"
41#include "controls.h"
42#include "cheats.h"
43#include "movie.h"
44#include "logger.h"
45#include "display.h"
46#include "conffile.h"
47#ifdef NETPLAY_SUPPORT
48#include "netplay.h"
49#endif
50#ifdef DEBUGGER
51#include "debug.h"
52#endif
53#include "statemanager.h"
54
55#ifdef NETPLAY_SUPPORT
56#ifdef _DEBUG
57#define NP_DEBUG 2
58#endif
59#endif
60
61typedef std::pair<std::string, std::string> strpair_t;
62
63ConfigFile::secvec_t keymaps;
64
65StateManager stateMan;
66
67#define FIXED_POINT 0x10000
68#define FIXED_POINT_SHIFT 16
69#define FIXED_POINT_REMAINDER 0xffff
70#define SOUND_BUFFER_SIZE (1024 * 16)
71#define SOUND_BUFFER_SIZE_MASK (SOUND_BUFFER_SIZE - 1)
72
73static volatile bool8 block_signal = FALSE;
74static volatile bool8 block_generate_sound = FALSE;
75
76static const char *sound_device = NULL;
77
78static const char *s9x_base_dir = NULL,
79 *rom_filename = NULL,
80 *snapshot_filename = NULL,
81 *play_smv_filename = NULL,
82 *record_smv_filename = NULL;
83
84static char default_dir[PATH_MAX + 1];
85
86static const char dirNames[13][32] =
87{
88 "", // DEFAULT_DIR
89 "", // HOME_DIR
90 "", // ROMFILENAME_DIR
91 "rom", // ROM_DIR
92 "sram", // SRAM_DIR
93 "savestate", // SNAPSHOT_DIR
94 "screenshot", // SCREENSHOT_DIR
95 "spc", // SPC_DIR
96 "cheat", // CHEAT_DIR
97 "patch", // PATCH_DIR
98 "bios", // BIOS_DIR
99 "log", // LOG_DIR
100 ""
101};
102
103struct SUnixSettings
104{
105 bool8 JoystickEnabled;
106 bool8 ThreadSound;
107 uint32 SoundBufferSize;
108 uint32 SoundFragmentSize;
109 uint32 rewindBufferSize;
110 uint32 rewindGranularity;
111};
112
113struct SoundStatus
114{
115 int sound_fd;
116 uint32 fragment_size;
117 uint32 err_counter;
118 uint32 err_rate;
119 int32 samples_mixed_so_far;
120 int32 play_position;
121};
122
123
124static int frame_advance = 0;
125static SUnixSettings unixSettings;
126static SoundStatus so;
127
128static bool8 rewinding;
129
130#ifndef NOSOUND
131static uint8 Buf[SOUND_BUFFER_SIZE];
132#endif
133
134#ifdef USE_THREADS
135static pthread_t thread;
136static pthread_mutex_t mutex;
137#endif
138
139#ifdef JOYSTICK_SUPPORT
140static uint8 js_mod[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
141static int js_fd[8] = { -1, -1, -1, -1, -1, -1, -1, -1 };
142static const char *js_device[8] = { "/dev/js0", "/dev/js1", "/dev/js2", "/dev/js3", "/dev/js4", "/dev/js5", "/dev/js6", "/dev/js7" };
143static bool8 js_unplugged[8] = { FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE };
144#endif
145
146#ifdef NETPLAY_SUPPORT
147static uint32 joypads[8];
148static uint32 old_joypads[8];
149#endif
150
151bool8 S9xMapDisplayInput (const char *, s9xcommand_t *);
152s9xcommand_t S9xGetDisplayCommandT (const char *);
153char * S9xGetDisplayCommandName (s9xcommand_t);
154void S9xHandleDisplayCommand (s9xcommand_t, int16, int16);
155bool S9xDisplayPollButton (uint32, bool *);
156bool S9xDisplayPollAxis (uint32, int16 *);
157bool S9xDisplayPollPointer (uint32, int16 *, int16 *);
158
159static long log2 (long);
160static void SoundTrigger (void);
161static void InitTimer (void);
162static void NSRTControllerSetup (void);
163static int make_snes9x_dirs (void);
164#ifndef NOSOUND
165static void * S9xProcessSound (void *);
166#endif
167#ifdef JOYSTICK_SUPPORT
168static void InitJoysticks (void);
169static bool8 ReadJoysticks (void);
170void S9xLatchJSEvent();
171#endif
172
173
174void _splitpath (const char *path, char *drive, char *dir, char *fname, char *ext)
175{
176 *drive = 0;
177
178 const char *slash = strrchr(path, SLASH_CHAR),
179 *dot = strrchr(path, '.');
180
181 if (dot && slash && dot < slash)
182 dot = NULL;
183
184 if (!slash)
185 {
186 *dir = 0;
187
188 strcpy(fname, path);
189
190 if (dot)
191 {
192 fname[dot - path] = 0;
193 strcpy(ext, dot + 1);
194 }
195 else
196 *ext = 0;
197 }
198 else
199 {
200 strcpy(dir, path);
201 dir[slash - path] = 0;
202
203 strcpy(fname, slash + 1);
204
205 if (dot)
206 {
207 fname[dot - slash - 1] = 0;
208 strcpy(ext, dot + 1);
209 }
210 else
211 *ext = 0;
212 }
213}
214
215void _makepath (char *path, const char *, const char *dir, const char *fname, const char *ext)
216{
217 if (dir && *dir)
218 {
219 strcpy(path, dir);
220 strcat(path, SLASH_STR);
221 }
222 else
223 *path = 0;
224
225 strcat(path, fname);
226
227 if (ext && *ext)
228 {
229 strcat(path, ".");
230 strcat(path, ext);
231 }
232}
233
234static long log2 (long num)
235{
236 long n = 0;
237
238 while (num >>= 1)
239 n++;
240
241 return (n);
242}
243
244void S9xExtraUsage (void)
245{
246 /* 12345678901234567890123456789012345678901234567890123456789012345678901234567890 */
247
248 S9xMessage(S9X_INFO, S9X_USAGE, "-multi Enable multi cartridge system");
249 S9xMessage(S9X_INFO, S9X_USAGE, "-carta <filename> ROM in slot A (use with -multi)");
250 S9xMessage(S9X_INFO, S9X_USAGE, "-cartb <filename> ROM in slot B (use with -multi)");
251 S9xMessage(S9X_INFO, S9X_USAGE, "");
252
253#ifdef JOYSTICK_SUPPORT
254 S9xMessage(S9X_INFO, S9X_USAGE, "-nogamepad Disable gamepad reading");
255 S9xMessage(S9X_INFO, S9X_USAGE, "-paddev1 <string> Specify gamepad device 1");
256 S9xMessage(S9X_INFO, S9X_USAGE, "-paddev1 <string> Specify gamepad device 2");
257 S9xMessage(S9X_INFO, S9X_USAGE, "-paddev1 <string> Specify gamepad device 3");
258 S9xMessage(S9X_INFO, S9X_USAGE, "-paddev1 <string> Specify gamepad device 4");
259 S9xMessage(S9X_INFO, S9X_USAGE, "");
260#endif
261
262#ifdef USE_THREADS
263 S9xMessage(S9X_INFO, S9X_USAGE, "-threadsound Use a separate thread to output sound");
264#endif
265 S9xMessage(S9X_INFO, S9X_USAGE, "-buffersize Sound generating buffer size in millisecond");
266 S9xMessage(S9X_INFO, S9X_USAGE, "-fragmentsize Sound playback buffer fragment size in bytes");
267 S9xMessage(S9X_INFO, S9X_USAGE, "-sounddev <string> Specify sound device");
268 S9xMessage(S9X_INFO, S9X_USAGE, "");
269
270 S9xMessage(S9X_INFO, S9X_USAGE, "-loadsnapshot Load snapshot file at start");
271 S9xMessage(S9X_INFO, S9X_USAGE, "-playmovie <filename> Start emulator playing the .smv file");
272 S9xMessage(S9X_INFO, S9X_USAGE, "-recordmovie <filename> Start emulator recording the .smv file");
273 S9xMessage(S9X_INFO, S9X_USAGE, "-dumpstreams Save audio/video data to disk");
274 S9xMessage(S9X_INFO, S9X_USAGE, "-dumpmaxframes <num> Stop emulator after saving specified number of");
275 S9xMessage(S9X_INFO, S9X_USAGE, " frames (use with -dumpstreams)");
276 S9xMessage(S9X_INFO, S9X_USAGE, "");
277
278 S9xMessage(S9X_INFO, S9X_USAGE, "-rwbuffersize Rewind buffer size in MB");
279 S9xMessage(S9X_INFO, S9X_USAGE, "-rwgranularity Rewind granularity in frames");
280 S9xMessage(S9X_INFO, S9X_USAGE, "");
281
282 S9xExtraDisplayUsage();
283}
284
285void S9xParseArg (char **argv, int &i, int argc)
286{
287 if (!strcasecmp(argv[i], "-multi"))
288 Settings.Multi = TRUE;
289 else
290 if (!strcasecmp(argv[i], "-carta"))
291 {
292 if (i + 1 < argc)
293 strncpy(Settings.CartAName, argv[++i], _MAX_PATH);
294 else
295 S9xUsage();
296 }
297 else
298 if (!strcasecmp(argv[i], "-cartb"))
299 {
300 if (i + 1 < argc)
301 strncpy(Settings.CartBName, argv[++i], _MAX_PATH);
302 else
303 S9xUsage();
304 }
305 else
306#ifdef JOYSTICK_SUPPORT
307 if (!strcasecmp(argv[i], "-nogamepad"))
308 unixSettings.JoystickEnabled = FALSE;
309 else
310 if (!strncasecmp(argv[i], "-paddev", 7) &&
311 argv[i][7] >= '1' && argv[i][7] <= '8' && argv[i][8] == '\0')
312 {
313 int j = argv[i][7] - '1';
314
315 if (i + 1 < argc)
316 js_device[j] = argv[++i];
317 else
318 S9xUsage();
319 }
320 else
321#endif
322#ifdef USE_THREADS
323 if (!strcasecmp(argv[i], "-threadsound"))
324 unixSettings.ThreadSound = TRUE;
325 else
326#endif
327 if (!strcasecmp(argv[i], "-buffersize"))
328 {
329 if (i + 1 < argc)
330 unixSettings.SoundBufferSize = atoi(argv[++i]);
331 else
332 S9xUsage();
333 }
334 else
335 if (!strcasecmp(argv[i], "-fragmentsize"))
336 {
337 if (i + 1 < argc)
338 unixSettings.SoundFragmentSize = atoi(argv[++i]);
339 else
340 S9xUsage();
341 }
342 else
343 if (!strcasecmp(argv[i], "-sounddev"))
344 {
345 if (i + 1 < argc)
346 sound_device = argv[++i];
347 else
348 S9xUsage();
349 }
350 else
351 if (!strcasecmp(argv[i], "-loadsnapshot"))
352 {
353 if (i + 1 < argc)
354 snapshot_filename = argv[++i];
355 else
356 S9xUsage();
357 }
358 else
359 if (!strcasecmp(argv[i], "-playmovie"))
360 {
361 if (i + 1 < argc)
362 play_smv_filename = argv[++i];
363 else
364 S9xUsage();
365 }
366 else
367 if (!strcasecmp(argv[i], "-recordmovie"))
368 {
369 if (i + 1 < argc)
370 record_smv_filename = argv[++i];
371 else
372 S9xUsage();
373 }
374 else
375 if (!strcasecmp(argv[i], "-dumpstreams"))
376 Settings.DumpStreams = TRUE;
377 else
378 if (!strcasecmp(argv[i], "-dumpmaxframes"))
379 Settings.DumpStreamsMaxFrames = atoi(argv[++i]);
380 else
381 if (!strcasecmp(argv[i], "-rwbuffersize"))
382 {
383 if (i + 1 < argc)
384 unixSettings.rewindBufferSize = atoi(argv[++i]);
385 else
386 S9xUsage();
387 }
388 else
389 if (!strcasecmp(argv[i], "-rwgranularity"))
390 {
391 if (i + 1 < argc)
392 unixSettings.rewindGranularity = atoi(argv[++i]);
393 else
394 S9xUsage();
395 }
396 else
397 S9xParseDisplayArg(argv, i, argc);
398}
399
400static void NSRTControllerSetup (void)
401{
402 if (!strncmp((const char *) Memory.NSRTHeader + 24, "NSRT", 4))
403 {
404 // First plug in both, they'll change later as needed
405 S9xSetController(0, CTL_JOYPAD, 0, 0, 0, 0);
406 S9xSetController(1, CTL_JOYPAD, 1, 0, 0, 0);
407
408 switch (Memory.NSRTHeader[29])
409 {
410 case 0x00: // Everything goes
411 break;
412
413 case 0x10: // Mouse in Port 0
414 S9xSetController(0, CTL_MOUSE, 0, 0, 0, 0);
415 break;
416
417 case 0x01: // Mouse in Port 1
418 S9xSetController(1, CTL_MOUSE, 1, 0, 0, 0);
419 break;
420
421 case 0x03: // Super Scope in Port 1
422 S9xSetController(1, CTL_SUPERSCOPE, 0, 0, 0, 0);
423 break;
424
425 case 0x06: // Multitap in Port 1
426 S9xSetController(1, CTL_MP5, 1, 2, 3, 4);
427 break;
428
429 case 0x66: // Multitap in Ports 0 and 1
430 S9xSetController(0, CTL_MP5, 0, 1, 2, 3);
431 S9xSetController(1, CTL_MP5, 4, 5, 6, 7);
432 break;
433
434 case 0x08: // Multitap in Port 1, Mouse in new Port 1
435 S9xSetController(1, CTL_MOUSE, 1, 0, 0, 0);
436 // There should be a toggle here for putting in Multitap instead
437 break;
438
439 case 0x04: // Pad or Super Scope in Port 1
440 S9xSetController(1, CTL_SUPERSCOPE, 0, 0, 0, 0);
441 // There should be a toggle here for putting in a pad instead
442 break;
443
444 case 0x05: // Justifier - Must ask user...
445 S9xSetController(1, CTL_JUSTIFIER, 1, 0, 0, 0);
446 // There should be a toggle here for how many justifiers
447 break;
448
449 case 0x20: // Pad or Mouse in Port 0
450 S9xSetController(0, CTL_MOUSE, 0, 0, 0, 0);
451 // There should be a toggle here for putting in a pad instead
452 break;
453
454 case 0x22: // Pad or Mouse in Port 0 & 1
455 S9xSetController(0, CTL_MOUSE, 0, 0, 0, 0);
456 S9xSetController(1, CTL_MOUSE, 1, 0, 0, 0);
457 // There should be a toggles here for putting in pads instead
458 break;
459
460 case 0x24: // Pad or Mouse in Port 0, Pad or Super Scope in Port 1
461 // There should be a toggles here for what to put in, I'm leaving it at gamepad for now
462 break;
463
464 case 0x27: // Pad or Mouse in Port 0, Pad or Mouse or Super Scope in Port 1
465 // There should be a toggles here for what to put in, I'm leaving it at gamepad for now
466 break;
467
468 // Not Supported yet
469 case 0x99: // Lasabirdie
470 break;
471
472 case 0x0A: // Barcode Battler
473 break;
474 }
475 }
476}
477
478void S9xParsePortConfig (ConfigFile &conf, int pass)
479{
480 s9x_base_dir = conf.GetStringDup("Unix::BaseDir", default_dir);
481 snapshot_filename = conf.GetStringDup("Unix::SnapshotFilename", NULL);
482 play_smv_filename = conf.GetStringDup("Unix::PlayMovieFilename", NULL);
483 record_smv_filename = conf.GetStringDup("Unix::RecordMovieFilename", NULL);
484
485#ifdef JOYSTICK_SUPPORT
486 unixSettings.JoystickEnabled = conf.GetBool ("Unix::EnableGamePad", true);
487 js_device[0] = conf.GetStringDup("Unix::PadDevice1", NULL);
488 js_device[1] = conf.GetStringDup("Unix::PadDevice2", NULL);
489 js_device[2] = conf.GetStringDup("Unix::PadDevice3", NULL);
490 js_device[3] = conf.GetStringDup("Unix::PadDevice4", NULL);
491 js_device[4] = conf.GetStringDup("Unix::PadDevice5", NULL);
492 js_device[5] = conf.GetStringDup("Unix::PadDevice6", NULL);
493 js_device[6] = conf.GetStringDup("Unix::PadDevice7", NULL);
494 js_device[7] = conf.GetStringDup("Unix::PadDevice8", NULL);
495#endif
496
497#ifdef USE_THREADS
498 unixSettings.ThreadSound = conf.GetBool ("Unix::ThreadSound", false);
499#endif
500 unixSettings.SoundBufferSize = conf.GetUInt ("Unix::SoundBufferSize", 100);
501 unixSettings.SoundFragmentSize = conf.GetUInt ("Unix::SoundFragmentSize", 2048);
502 sound_device = conf.GetStringDup("Unix::SoundDevice", "/dev/dsp");
503
504 keymaps.clear();
505 if (!conf.GetBool("Unix::ClearAllControls", false))
506 {
507 #if 0
508 // Using an axis to control Pseudo-pointer #1
509 keymaps.push_back(strpair_t("J00:Axis0", "AxisToPointer 1h Var"));
510 keymaps.push_back(strpair_t("J00:Axis1", "AxisToPointer 1v Var"));
511 keymaps.push_back(strpair_t("PseudoPointer1", "Pointer C=2 White/Black Superscope"));
512 #elif 0
513 // Using an Axis for Pseudo-buttons
514 keymaps.push_back(strpair_t("J00:Axis0", "AxisToButtons 1/0 T=50%"));
515 keymaps.push_back(strpair_t("J00:Axis1", "AxisToButtons 3/2 T=50%"));
516 keymaps.push_back(strpair_t("PseudoButton0", "Joypad1 Right"));
517 keymaps.push_back(strpair_t("PseudoButton1", "Joypad1 Left"));
518 keymaps.push_back(strpair_t("PseudoButton2", "Joypad1 Down"));
519 keymaps.push_back(strpair_t("PseudoButton3", "Joypad1 Up"));
520 #else
521 // Using 'Joypad# Axis'
522 keymaps.push_back(strpair_t("J00:Axis0", "Joypad1 Axis Left/Right T=50%"));
523 keymaps.push_back(strpair_t("J00:Axis1", "Joypad1 Axis Up/Down T=50%"));
524 #endif
525 keymaps.push_back(strpair_t("J00:B0", "Joypad1 X"));
526 keymaps.push_back(strpair_t("J00:B1", "Joypad1 A"));
527 keymaps.push_back(strpair_t("J00:B2", "Joypad1 B"));
528 keymaps.push_back(strpair_t("J00:B3", "Joypad1 Y"));
529 #if 1
530 keymaps.push_back(strpair_t("J00:B6", "Joypad1 L"));
531 #else
532 // Show off joypad-meta
533 keymaps.push_back(strpair_t("J00:X+B6", "JS1 Meta1"));
534 keymaps.push_back(strpair_t("J00:M1+B1", "Joypad1 Turbo A"));
535 #endif
536 keymaps.push_back(strpair_t("J00:B7", "Joypad1 R"));
537 keymaps.push_back(strpair_t("J00:B8", "Joypad1 Select"));
538 keymaps.push_back(strpair_t("J00:B11", "Joypad1 Start"));
539 }
540
541 std::string section = S9xParseDisplayConfig(conf, 1);
542
543 ConfigFile::secvec_t sec = conf.GetSection((section + " Controls").c_str());
544 for (ConfigFile::secvec_t::iterator c = sec.begin(); c != sec.end(); c++)
545 keymaps.push_back(*c);
546}
547
548static int make_snes9x_dirs (void)
549{
550 if (strlen(s9x_base_dir) + 1 + sizeof(dirNames[0]) > PATH_MAX + 1)
551 return (-1);
552
553 mkdir(s9x_base_dir, 0755);
554
555 for (int i = 0; i < LAST_DIR; i++)
556 {
557 if (dirNames[i][0])
558 {
559 char s[PATH_MAX + 1];
560 snprintf(s, PATH_MAX + 1, "%s%s%s", s9x_base_dir, SLASH_STR, dirNames[i]);
561 mkdir(s, 0755);
562 }
563 }
564
565 return (0);
566}
567
568const char * S9xGetDirectory (enum s9x_getdirtype dirtype)
569{
570 static char s[PATH_MAX + 1];
571
572 if (dirNames[dirtype][0])
573 snprintf(s, PATH_MAX + 1, "%s%s%s", s9x_base_dir, SLASH_STR, dirNames[dirtype]);
574 else
575 {
576 switch (dirtype)
577 {
578 case DEFAULT_DIR:
579 strncpy(s, s9x_base_dir, PATH_MAX + 1);
580 s[PATH_MAX] = 0;
581 break;
582
583 case HOME_DIR:
584 strncpy(s, getenv("HOME"), PATH_MAX + 1);
585 s[PATH_MAX] = 0;
586 break;
587
588 case ROMFILENAME_DIR:
589 strncpy(s, Memory.ROMFilename, PATH_MAX + 1);
590 s[PATH_MAX] = 0;
591
592 for (int i = strlen(s); i >= 0; i--)
593 {
594 if (s[i] == SLASH_CHAR)
595 {
596 s[i] = 0;
597 break;
598 }
599 }
600
601 break;
602
603 default:
604 s[0] = 0;
605 break;
606 }
607 }
608
609 return (s);
610}
611
612const char * S9xGetFilename (const char *ex, enum s9x_getdirtype dirtype)
613{
614 static char s[PATH_MAX + 1];
615 char drive[_MAX_DRIVE + 1], dir[_MAX_DIR + 1], fname[_MAX_FNAME + 1], ext[_MAX_EXT + 1];
616
617 _splitpath(Memory.ROMFilename, drive, dir, fname, ext);
618 snprintf(s, PATH_MAX + 1, "%s%s%s%s", S9xGetDirectory(dirtype), SLASH_STR, fname, ex);
619
620 return (s);
621}
622
623const char * S9xGetFilenameInc (const char *ex, enum s9x_getdirtype dirtype)
624{
625 static char s[PATH_MAX + 1];
626 char drive[_MAX_DRIVE + 1], dir[_MAX_DIR + 1], fname[_MAX_FNAME + 1], ext[_MAX_EXT + 1];
627
628 unsigned int i = 0;
629 const char *d;
630 struct stat buf;
631
632 _splitpath(Memory.ROMFilename, drive, dir, fname, ext);
633 d = S9xGetDirectory(dirtype);
634
635 do
636 snprintf(s, PATH_MAX + 1, "%s%s%s.%03d%s", d, SLASH_STR, fname, i++, ex);
637 while (stat(s, &buf) == 0 && i < 1000);
638
639 return (s);
640}
641
642const char * S9xBasename (const char *f)
643{
644 const char *p;
645
646 if ((p = strrchr(f, '/')) != NULL || (p = strrchr(f, '\\')) != NULL)
647 return (p + 1);
648
649 return (f);
650}
651
652const char * S9xChooseFilename (bool8 read_only)
653{
654 char s[PATH_MAX + 1];
655 char drive[_MAX_DRIVE + 1], dir[_MAX_DIR + 1], fname[_MAX_FNAME + 1], ext[_MAX_EXT + 1];
656
657 const char *filename;
658 char title[64];
659
660 _splitpath(Memory.ROMFilename, drive, dir, fname, ext);
661 snprintf(s, PATH_MAX + 1, "%s.frz", fname);
662 sprintf(title, "%s snapshot filename", read_only ? "Select load" : "Choose save");
663
664 S9xSetSoundMute(TRUE);
665 filename = S9xSelectFilename(s, S9xGetDirectory(SNAPSHOT_DIR), "frz", title);
666 S9xSetSoundMute(FALSE);
667
668 return (filename);
669}
670
671const char * S9xChooseMovieFilename (bool8 read_only)
672{
673 char s[PATH_MAX + 1];
674 char drive[_MAX_DRIVE + 1], dir[_MAX_DIR + 1], fname[_MAX_FNAME + 1], ext[_MAX_EXT + 1];
675
676 const char *filename;
677 char title[64];
678
679 _splitpath(Memory.ROMFilename, drive, dir, fname, ext);
680 snprintf(s, PATH_MAX + 1, "%s.smv", fname);
681 sprintf(title, "Choose movie %s filename", read_only ? "playback" : "record");
682
683 S9xSetSoundMute(TRUE);
684 filename = S9xSelectFilename(s, S9xGetDirectory(HOME_DIR), "smv", title);
685 S9xSetSoundMute(FALSE);
686
687 return (filename);
688}
689
690bool8 S9xOpenSnapshotFile (const char *filename, bool8 read_only, STREAM *file)
691{
692 char s[PATH_MAX + 1];
693 char drive[_MAX_DRIVE + 1], dir[_MAX_DIR + 1], fname[_MAX_FNAME + 1], ext[_MAX_EXT + 1];
694
695 _splitpath(filename, drive, dir, fname, ext);
696
697 if (*drive || *dir == SLASH_CHAR || (strlen(dir) > 1 && *dir == '.' && *(dir + 1) == SLASH_CHAR))
698 {
699 strncpy(s, filename, PATH_MAX + 1);
700 s[PATH_MAX] = 0;
701 }
702 else
703 snprintf(s, PATH_MAX + 1, "%s%s%s", S9xGetDirectory(SNAPSHOT_DIR), SLASH_STR, fname);
704
705 if (!*ext && strlen(s) <= PATH_MAX - 4)
706 strcat(s, ".frz");
707
708 if ((*file = OPEN_STREAM(s, read_only ? "rb" : "wb")))
709 return (TRUE);
710
711 return (FALSE);
712}
713
714void S9xCloseSnapshotFile (STREAM file)
715{
716 CLOSE_STREAM(file);
717}
718
719bool8 S9xInitUpdate (void)
720{
721 return (TRUE);
722}
723
724bool8 S9xDeinitUpdate (int width, int height)
725{
726 S9xPutImage(width, height);
727 return (TRUE);
728}
729
730bool8 S9xContinueUpdate (int width, int height)
731{
732 return (TRUE);
733}
734
735void S9xToggleSoundChannel (int c)
736{
737 static uint8 sound_switch = 255;
738
739 if (c == 8)
740 sound_switch = 255;
741 else
742 sound_switch ^= 1 << c;
743
744 S9xSetSoundControl(sound_switch);
745}
746
747void S9xAutoSaveSRAM (void)
748{
749 Memory.SaveSRAM(S9xGetFilename(".srm", SRAM_DIR));
750}
751
752void S9xSyncSpeed (void)
753{
754#ifndef NOSOUND
755 if (Settings.SoundSync)
756 {
757 return;
758 }
759#endif
760
761 if (Settings.DumpStreams)
762 return;
763
764#ifdef NETPLAY_SUPPORT
765 if (Settings.NetPlay && NetPlay.Connected)
766 {
767 #if defined(NP_DEBUG) && NP_DEBUG == 2
768 printf("CLIENT: SyncSpeed @%d\n", S9xGetMilliTime());
769 #endif
770
771 S9xNPSendJoypadUpdate(old_joypads[0]);
772 for (int J = 0; J < 8; J++)
773 joypads[J] = S9xNPGetJoypad(J);
774
775 if (!S9xNPCheckForHeartBeat())
776 {
777 NetPlay.PendingWait4Sync = !S9xNPWaitForHeartBeatDelay(100);
778 #if defined(NP_DEBUG) && NP_DEBUG == 2
779 if (NetPlay.PendingWait4Sync)
780 printf("CLIENT: PendingWait4Sync1 @%d\n", S9xGetMilliTime());
781 #endif
782
783 IPPU.RenderThisFrame = TRUE;
784 IPPU.SkippedFrames = 0;
785 }
786 else
787 {
788 NetPlay.PendingWait4Sync = !S9xNPWaitForHeartBeatDelay(200);
789 #if defined(NP_DEBUG) && NP_DEBUG == 2
790 if (NetPlay.PendingWait4Sync)
791 printf("CLIENT: PendingWait4Sync2 @%d\n", S9xGetMilliTime());
792 #endif
793
794 if (IPPU.SkippedFrames < NetPlay.MaxFrameSkip)
795 {
796 IPPU.RenderThisFrame = FALSE;
797 IPPU.SkippedFrames++;
798 }
799 else
800 {
801 IPPU.RenderThisFrame = TRUE;
802 IPPU.SkippedFrames = 0;
803 }
804 }
805
806 if (!NetPlay.PendingWait4Sync)
807 {
808 NetPlay.FrameCount++;
809 S9xNPStepJoypadHistory();
810 }
811
812 return;
813 }
814#endif
815
816 if (Settings.HighSpeedSeek > 0)
817 Settings.HighSpeedSeek--;
818
819 if (Settings.TurboMode)
820 {
821 if ((++IPPU.FrameSkip >= Settings.TurboSkipFrames) && !Settings.HighSpeedSeek)
822 {
823 IPPU.FrameSkip = 0;
824 IPPU.SkippedFrames = 0;
825 IPPU.RenderThisFrame = TRUE;
826 }
827 else
828 {
829 IPPU.SkippedFrames++;
830 IPPU.RenderThisFrame = FALSE;
831 }
832
833 return;
834 }
835
836 static struct timeval next1 = { 0, 0 };
837 struct timeval now;
838
839 while (gettimeofday(&now, NULL) == -1) ;
840
841 // If there is no known "next" frame, initialize it now.
842 if (next1.tv_sec == 0)
843 {
844 next1 = now;
845 next1.tv_usec++;
846 }
847
848 // If we're on AUTO_FRAMERATE, we'll display frames always only if there's excess time.
849 // Otherwise we'll display the defined amount of frames.
850 unsigned limit = (Settings.SkipFrames == AUTO_FRAMERATE) ? (timercmp(&next1, &now, <) ? 10 : 1) : Settings.SkipFrames;
851
852 IPPU.RenderThisFrame = (++IPPU.SkippedFrames >= limit) ? TRUE : FALSE;
853
854 if (IPPU.RenderThisFrame)
855 IPPU.SkippedFrames = 0;
856 else
857 {
858 // If we were behind the schedule, check how much it is.
859 if (timercmp(&next1, &now, <))
860 {
861 unsigned lag = (now.tv_sec - next1.tv_sec) * 1000000 + now.tv_usec - next1.tv_usec;
862 if (lag >= 500000)
863 {
864 // More than a half-second behind means probably pause.
865 // The next line prevents the magic fast-forward effect.
866 next1 = now;
867 }
868 }
869 }
870
871 // Delay until we're completed this frame.
872 // Can't use setitimer because the sound code already could be using it. We don't actually need it either.
873 while (timercmp(&next1, &now, >))
874 {
875 // If we're ahead of time, sleep a while.
876 unsigned timeleft = (next1.tv_sec - now.tv_sec) * 1000000 + next1.tv_usec - now.tv_usec;
877 usleep(timeleft);
878
879 while (gettimeofday(&now, NULL) == -1) ;
880 // Continue with a while-loop because usleep() could be interrupted by a signal.
881 }
882
883 // Calculate the timestamp of the next frame.
884 next1.tv_usec += Settings.FrameTime;
885 if (next1.tv_usec >= 1000000)
886 {
887 next1.tv_sec += next1.tv_usec / 1000000;
888 next1.tv_usec %= 1000000;
889 }
890}
891
892bool8 S9xMapInput (const char *n, s9xcommand_t *cmd)
893{
894 int i, j, d;
895 char *c;
896 char buf[4] = "M1+";
897
898 if (!strncmp(n, "PseudoPointer", 13) && n[13] >= '1' && n[13] <= '8' && n[14] == '\0')
899 return (S9xMapPointer(PseudoPointerBase + (n[13] - '1'), *cmd, false));
900
901 if (!strncmp(n, "PseudoButton", 12) && isdigit(n[12]) && (j = strtol(n + 12, &c, 10)) < 256 && (c == NULL || *c == '\0'))
902 return (S9xMapButton(PseudoButtonBase + j, *cmd, false));
903
904 if (n[0] != 'J' || !isdigit(n[1]) || !isdigit(n[2]) || n[3] != ':')
905 goto unrecog;
906
907 d = ((n[1] - '0') * 10 + (n[2] - '0')) << 24;
908 d |= 0x80000000;
909 i = 4;
910 if (!strncmp(n + i, "X+", 2))
911 {
912 d |= 0x4000;
913 i += 2;
914 }
915 else
916 {
917 for (buf[1] = '1'; buf[1] <= '8'; buf[1]++)
918 {
919 if (!strncmp(n + i, buf, 3))
920 {
921 d |= 1 << (buf[1] - '1' + 16);
922 i += 3;
923 }
924 }
925 }
926
927 if (!strncmp(n + i, "Axis", 4))
928 {
929 d |= 0x8000;
930 i += 4;
931 }
932 else
933 if (n[i] == 'B')
934 i++;
935 else
936 goto unrecog;
937
938 d |= j = strtol(n + i, &c, 10);
939 if ((c != NULL && *c != '\0') || j > 0x3fff)
940 goto unrecog;
941
942 if (d & 0x8000)
943 return (S9xMapAxis(d, *cmd, false));
944
945 return (S9xMapButton(d, *cmd, false));
946
947unrecog:
948 return (S9xMapDisplayInput(n, cmd));
949}
950
951bool S9xPollButton (uint32 id, bool *pressed)
952{
953 return (S9xDisplayPollButton(id, pressed));
954}
955
956bool S9xPollAxis (uint32 id, int16 *value)
957{
958 return (S9xDisplayPollAxis(id, value));
959}
960
961bool S9xPollPointer (uint32 id, int16 *x, int16 *y)
962{
963 return (S9xDisplayPollPointer(id, x, y));
964}
965
966s9xcommand_t S9xGetPortCommandT (const char *n)
967{
968 s9xcommand_t cmd;
969
970 cmd.type = S9xBadMapping;
971 cmd.multi_press = 0;
972 cmd.button_norpt = 0;
973 cmd.port[0] = 0;
974 cmd.port[1] = 0;
975 cmd.port[2] = 0;
976 cmd.port[3] = 0;
977
978 if (!strncmp(n, "JS", 2) && n[2] >= '1' && n[2] <= '8')
979 {
980 if (!strncmp(n + 3, " Meta", 5) && n[8] >= '1' && n[8] <= '8' && n[9] == '\0')
981 {
982 cmd.type = S9xButtonPort;
983 cmd.port[1] = 0;
984 cmd.port[2] = n[2] - '1';
985 cmd.port[3] = 1 << (n[8] - '1');
986
987 return (cmd);
988 }
989 else
990 if (!strncmp(n + 3, " ToggleMeta", 11) && n[14] >= '1' && n[14] <= '8' && n[15] == '\0')
991 {
992 cmd.type = S9xButtonPort;
993 cmd.port[1] = 1;
994 cmd.port[2] = n[2] - '1';
995 cmd.port[3] = 1 << (n[13] - '1');
996
997 return (cmd);
998 }
999 } else
1000 if (!strcmp(n,"Rewind"))
1001 {
1002 cmd.type = S9xButtonPort;
1003 cmd.port[1] = 2;
1004
1005 return (cmd);
1006 }
1007 else if (!strcmp(n, "Advance"))
1008 {
1009 cmd.type = S9xButtonPort;
1010 cmd.port[1] = 3;
1011 return (cmd);
1012 }
1013
1014 return (S9xGetDisplayCommandT(n));
1015}
1016
1017char * S9xGetPortCommandName (s9xcommand_t cmd)
1018{
1019 std::string x;
1020
1021 switch (cmd.type)
1022 {
1023 case S9xButtonPort:
1024 if (cmd.port[0] != 0)
1025 break;
1026
1027 switch (cmd.port[1])
1028 {
1029 case 0:
1030 x = "JS";
1031 x += (int) cmd.port[2];
1032 x += " Meta";
1033 x += (int) cmd.port[3];
1034 return (strdup(x.c_str()));
1035
1036 case 1:
1037 x = "JS";
1038 x += (int) cmd.port[2];
1039 x += " ToggleMeta";
1040 x += (int) cmd.port[3];
1041 return (strdup(x.c_str()));
1042
1043 case 2:
1044 return (strdup("Rewind"));
1045
1046 case 3:
1047 return (strdup("Advance"));
1048 }
1049
1050 break;
1051
1052 case S9xAxisPort:
1053 break;
1054
1055 case S9xPointerPort:
1056 break;
1057 }
1058
1059 return (S9xGetDisplayCommandName(cmd));
1060}
1061
1062void S9xHandlePortCommand (s9xcommand_t cmd, int16 data1, int16 data2)
1063{
1064#ifdef JOYSTICK_SUPPORT
1065 switch (cmd.type)
1066 {
1067 case S9xButtonPort:
1068 if (cmd.port[0] != 0)
1069 break;
1070
1071 switch (cmd.port[1])
1072 {
1073 case 0:
1074 if (data1)
1075 js_mod[cmd.port[2]] |= cmd.port[3];
1076 else
1077 js_mod[cmd.port[2]] &= ~cmd.port[3];
1078 break;
1079
1080 case 1:
1081 if (data1)
1082 js_mod[cmd.port[2]] ^= cmd.port[3];
1083 break;
1084
1085 case 2:
1086 rewinding = (bool8) data1;
1087 break;
1088
1089 case 3:
1090 frame_advance = (bool8) data1;
1091 }
1092
1093 break;
1094
1095 case S9xAxisPort:
1096 break;
1097
1098 case S9xPointerPort:
1099 break;
1100 }
1101
1102 S9xHandleDisplayCommand(cmd, data1, data2);
1103#endif
1104}
1105
1106void S9xSetupDefaultKeymap (void)
1107{
1108 s9xcommand_t cmd;
1109
1110 S9xUnmapAllControls();
1111
1112 for (ConfigFile::secvec_t::iterator i = keymaps.begin(); i != keymaps.end(); i++)
1113 {
1114 cmd = S9xGetPortCommandT(i->second.c_str());
1115
1116 if (cmd.type == S9xBadMapping)
1117 {
1118 cmd = S9xGetCommandT(i->second.c_str());
1119 if (cmd.type == S9xBadMapping)
1120 {
1121 std::string s("Unrecognized command '");
1122 s += i->second + "'";
1123 perror(s.c_str());
1124 continue;
1125 }
1126 }
1127
1128 if (!S9xMapInput(i->first.c_str(), &cmd))
1129 {
1130 std::string s("Could not map '");
1131 s += i->second + "' to '" + i->first + "'";
1132 perror(s.c_str());
1133 continue;
1134 }
1135 }
1136
1137 keymaps.clear();
1138}
1139
1140void S9xInitInputDevices (void)
1141{
1142#ifdef JOYSTICK_SUPPORT
1143 InitJoysticks();
1144#endif
1145}
1146
1147#ifdef JOYSTICK_SUPPORT
1148
1149static void InitJoysticks (void)
1150{
1151#ifdef JSIOCGVERSION
1152 int version;
1153 unsigned char axes, buttons;
1154
1155 if ((js_fd[0] = open(js_device[0], O_RDONLY | O_NONBLOCK)) == -1)
1156 {
1157 fprintf(stderr, "joystick: No joystick found.\n");
1158 return;
1159 }
1160
1161 if (ioctl(js_fd[0], JSIOCGVERSION, &version) == -1)
1162 {
1163 fprintf(stderr, "joystick: You need at least driver version 1.0 for joystick support.\n");
1164 close(js_fd[0]);
1165 return;
1166 }
1167
1168 for (int i = 1; i < 8; i++)
1169 js_fd[i] = open(js_device[i], O_RDONLY | O_NONBLOCK);
1170
1171#ifdef JSIOCGNAME
1172 char name[130];
1173
1174 bzero(name, 128);
1175
1176 if (ioctl(js_fd[0], JSIOCGNAME(128), name) > 0)
1177 {
1178 printf("Using %s (%s) as joystick1\n", name, js_device[0]);
1179
1180 for (int i = 1; i < 8; i++)
1181 {
1182 if (js_fd[i] > 0)
1183 {
1184 ioctl(js_fd[i], JSIOCGNAME(128), name);
1185 printf (" and %s (%s) as joystick%d\n", name, js_device[i], i + 1);
1186 }
1187 }
1188 }
1189 else
1190#endif
1191 {
1192 ioctl(js_fd[0], JSIOCGAXES, &axes);
1193 ioctl(js_fd[0], JSIOCGBUTTONS, &buttons);
1194 printf("Using %d-axis %d-button joystick (%s) as joystick1\n", axes, buttons, js_device[0]);
1195
1196 for (int i = 1; i < 8; i++)
1197 {
1198 if (js_fd[i] > 0)
1199 {
1200 ioctl(js_fd[i], JSIOCGAXES, &axes);
1201 ioctl(js_fd[i], JSIOCGBUTTONS, &buttons);
1202 printf(" and %d-axis %d-button joystick (%s) as joystick%d\n", axes, buttons, js_device[i], i + 1);
1203 }
1204 }
1205 }
1206#endif
1207}
1208
1209static bool8 ReadJoysticks (void)
1210{
1211 // track if ANY joystick event happened this frame
1212 int js_latch = FALSE;
1213#ifdef JSIOCGVERSION
1214 struct js_event js_ev;
1215
1216 for (int i = 0; i < 8; i++)
1217 {
1218 // Try to reopen unplugged sticks
1219 if (js_unplugged[i])
1220 {
1221 js_fd[i] = open(js_device[i], O_RDONLY | O_NONBLOCK);
1222 if (js_fd[i] >= 0)
1223 {
1224 fprintf(stderr,"Joystick %d reconnected.\n",i);
1225 js_unplugged[i] = FALSE;
1226 js_latch = TRUE;
1227 }
1228 }
1229
1230 // skip sticks without valid file desc
1231 if (js_fd[i] < 0) continue;
1232
1233 while (read(js_fd[i], &js_ev, sizeof(struct js_event)) == sizeof(struct js_event))
1234 {
1235 switch (js_ev.type)
1236 {
1237 case JS_EVENT_AXIS:
1238 S9xReportAxis(0x8000c000 | (i << 24) | js_ev.number, js_ev.value);
1239 S9xReportAxis(0x80008000 | (i << 24) | (js_mod[i] << 16) | js_ev.number, js_ev.value);
1240 js_latch = TRUE;
1241 break;
1242
1243 case JS_EVENT_BUTTON:
1244 case JS_EVENT_BUTTON | JS_EVENT_INIT:
1245 S9xReportButton(0x80004000 | (i << 24) | js_ev.number, js_ev.value);
1246 S9xReportButton(0x80000000 | (i << 24) | (js_mod[i] << 16) | js_ev.number, js_ev.value);
1247 js_latch = TRUE;
1248 break;
1249 }
1250 }
1251
1252 /* EAGAIN is returned when the queue is empty */
1253 if (errno != EAGAIN) {
1254 // Error reading joystick.
1255 fprintf(stderr,"Error reading joystick %d!\n",i);
1256
1257 // Mark for reconnect attempt.
1258 js_unplugged[i] = TRUE;
1259
1260 for (unsigned int j = 0; j < 16; j++)
1261 {
1262 // Center all axis
1263 S9xReportAxis(0x8000c000 | (i << 24) | j, 0);
1264 S9xReportAxis(0x80008000 | (i << 24) | (js_mod[i] << 16) | j, 0);
1265 // Unpress all buttons.
1266 S9xReportButton(0x80004000 | (i << 24) | j, 0);
1267 S9xReportButton(0x80000000 | (i << 24) | (js_mod[i] << 16) | j, 0);
1268 }
1269
1270 js_latch = TRUE;
1271 }
1272 }
1273#endif
1274 return js_latch;
1275}
1276
1277#endif
1278
1279void S9xSamplesAvailable(void *data)
1280{
1281#ifndef NOSOUND
1282
1283 audio_buf_info info;
1284 int samples_to_write;
1285 int bytes_to_write;
1286 int bytes_written;
1287 static uint8 *sound_buffer = NULL;
1288 static int sound_buffer_size = 0;
1289
1290
1291 ioctl(so.sound_fd, SNDCTL_DSP_GETOSPACE, &info);
1292
1293 if (Settings.DynamicRateControl)
1294 {
1295 S9xUpdateDynamicRate(info.bytes, so.fragment_size * 4);
1296 }
1297
1298 samples_to_write = S9xGetSampleCount();
1299
1300 if (Settings.DynamicRateControl && !Settings.SoundSync)
1301 {
1302 // Using rate control, we should always keep the emulator's sound buffers empty to
1303 // maintain an accurate measurement.
1304 if (samples_to_write > (info.bytes >> 1))
1305 {
1306 S9xClearSamples();
1307 return;
1308 }
1309 }
1310
1311 if (Settings.SoundSync && !Settings.TurboMode && !Settings.Mute)
1312 {
1313 while (info.bytes >> 1 < samples_to_write)
1314 {
1315 int usec_to_sleep = ((samples_to_write >> 1) - (info.bytes >> 2)) * 10000 /
1316 (Settings.SoundPlaybackRate / 100);
1317 usleep(usec_to_sleep > 0 ? usec_to_sleep : 0);
1318 ioctl(so.sound_fd, SNDCTL_DSP_GETOSPACE, &info);
1319 }
1320 }
1321 else
1322 {
1323 samples_to_write = MIN(info.bytes >> 1, samples_to_write) & ~1;
1324 }
1325
1326 if (samples_to_write < 0)
1327 return;
1328
1329 if (sound_buffer_size < samples_to_write * 2)
1330 {
1331 sound_buffer = (uint8 *)realloc(sound_buffer, samples_to_write * 2);
1332 sound_buffer_size = samples_to_write * 2;
1333 }
1334
1335 S9xMixSamples(sound_buffer, samples_to_write);
1336
1337 bytes_written = 0;
1338 bytes_to_write = samples_to_write * 2;
1339
1340 while (bytes_to_write > bytes_written)
1341 {
1342 int result;
1343
1344 result = write(so.sound_fd,
1345 ((char *)sound_buffer) + bytes_written,
1346 bytes_to_write - bytes_written);
1347
1348 if (result < 0)
1349 break;
1350
1351 bytes_written += result;
1352 }
1353#endif
1354}
1355
1356bool8 S9xOpenSoundDevice (void)
1357{
1358#ifndef NOSOUND
1359 int J, K;
1360
1361 so.sound_fd = open(sound_device, O_WRONLY | O_NONBLOCK);
1362 if (so.sound_fd == -1)
1363 {
1364 fprintf(stderr, "ERROR: Failed to open sound device %s for writing.\n\t(Try loading snd-pcm-oss module?)\n", sound_device);
1365 return (FALSE);
1366 }
1367
1368 J = log2(unixSettings.SoundFragmentSize) | (4 << 16);
1369 if (ioctl(so.sound_fd, SNDCTL_DSP_SETFRAGMENT, &J) == -1)
1370 return (FALSE);
1371
1372 J = K = AFMT_S16_NE;
1373 if (ioctl(so.sound_fd, SNDCTL_DSP_SETFMT, &J) == -1 || J != K)
1374 return (FALSE);
1375
1376 J = K = 1;
1377 if (ioctl(so.sound_fd, SNDCTL_DSP_STEREO, &J) == -1 || J != K)
1378 return (FALSE);
1379
1380 J = K = Settings.SoundPlaybackRate;
1381 if (ioctl(so.sound_fd, SNDCTL_DSP_SPEED, &J) == -1 || J != K)
1382 return (FALSE);
1383
1384 J = 0;
1385 if (ioctl(so.sound_fd, SNDCTL_DSP_GETBLKSIZE, &J) == -1)
1386 return (FALSE);
1387
1388 so.fragment_size = J;
1389 printf("fragment size: %d\n", J);
1390#endif
1391
1392 S9xSetSamplesAvailableCallback(S9xSamplesAvailable, NULL);
1393
1394 return (TRUE);
1395}
1396
1397#ifndef NOSOUND
1398
1399
1400#endif
1401
1402void S9xExit (void)
1403{
1404 S9xMovieShutdown();
1405
1406 S9xSetSoundMute(TRUE);
1407 Settings.StopEmulation = TRUE;
1408
1409#ifdef NETPLAY_SUPPORT
1410 if (Settings.NetPlay)
1411 S9xNPDisconnect();
1412#endif
1413
1414 Memory.SaveSRAM(S9xGetFilename(".srm", SRAM_DIR));
1415 S9xResetSaveTimer(FALSE);
1416 S9xSaveCheatFile(S9xGetFilename(".cht", CHEAT_DIR));
1417 S9xUnmapAllControls();
1418 S9xDeinitDisplay();
1419 Memory.Deinit();
1420 S9xDeinitAPU();
1421
1422 exit(0);
1423}
1424
1425#ifdef DEBUGGER
1426static void sigbrkhandler (int)
1427{
1428 CPU.Flags |= DEBUG_MODE_FLAG;
1429 signal(SIGINT, (SIG_PF) sigbrkhandler);
1430}
1431#endif
1432
1433int main (int argc, char **argv)
1434{
1435 if (argc < 2)
1436 S9xUsage();
1437
1438 printf("\n\nSnes9x " VERSION " for unix\n");
1439
1440 snprintf(default_dir, PATH_MAX + 1, "%s%s%s", getenv("HOME"), SLASH_STR, ".snes9x");
1441 s9x_base_dir = default_dir;
1442
1443 memset(&Settings, 0, sizeof(Settings));
1444 Settings.MouseMaster = TRUE;
1445 Settings.SuperScopeMaster = TRUE;
1446 Settings.JustifierMaster = TRUE;
1447 Settings.MultiPlayer5Master = TRUE;
1448 Settings.FrameTimePAL = 20000;
1449 Settings.FrameTimeNTSC = 16667;
1450 Settings.SixteenBitSound = TRUE;
1451 Settings.Stereo = TRUE;
1452 Settings.SoundPlaybackRate = 48000;
1453 Settings.SoundInputRate = 31950;
1454 Settings.SupportHiRes = TRUE;
1455 Settings.Transparency = TRUE;
1456 Settings.AutoDisplayMessages = TRUE;
1457 Settings.InitialInfoStringTimeout = 120;
1458 Settings.HDMATimingHack = 100;
1459 Settings.BlockInvalidVRAMAccessMaster = TRUE;
1460 Settings.StopEmulation = TRUE;
1461 Settings.WrongMovieStateProtection = TRUE;
1462 Settings.DumpStreamsMaxFrames = -1;
1463 Settings.StretchScreenshots = 1;
1464 Settings.SnapshotScreenshots = TRUE;
1465 Settings.SkipFrames = AUTO_FRAMERATE;
1466 Settings.TurboSkipFrames = 15;
1467 Settings.CartAName[0] = 0;
1468 Settings.CartBName[0] = 0;
1469#ifdef NETPLAY_SUPPORT
1470 Settings.ServerName[0] = 0;
1471#endif
1472
1473#ifdef JOYSTICK_SUPPORT
1474 unixSettings.JoystickEnabled = TRUE;
1475#else
1476 unixSettings.JoystickEnabled = FALSE;
1477#endif
1478 unixSettings.ThreadSound = TRUE;
1479 unixSettings.SoundBufferSize = 100;
1480 unixSettings.SoundFragmentSize = 2048;
1481
1482 unixSettings.rewindBufferSize = 0;
1483 unixSettings.rewindGranularity = 1;
1484
1485 memset(&so, 0, sizeof(so));
1486
1487 rewinding = false;
1488
1489 CPU.Flags = 0;
1490
1491 S9xLoadConfigFiles(argv, argc);
1492 rom_filename = S9xParseArgs(argv, argc);
1493 S9xDeleteCheats();
1494
1495 make_snes9x_dirs();
1496
1497 if (!Memory.Init() || !S9xInitAPU())
1498 {
1499 fprintf(stderr, "Snes9x: Memory allocation failure - not enough RAM/virtual memory available.\nExiting...\n");
1500 Memory.Deinit();
1501 S9xDeinitAPU();
1502 exit(1);
1503 }
1504
1505 S9xInitSound(0);
1506 S9xSetSoundMute(TRUE);
1507
1508 S9xReportControllers();
1509
1510 uint32 saved_flags = CPU.Flags;
1511 bool8 loaded = FALSE;
1512
1513 if (Settings.Multi)
1514 {
1515 loaded = Memory.LoadMultiCart(Settings.CartAName, Settings.CartBName);
1516
1517 if (!loaded)
1518 {
1519 char s1[PATH_MAX + 1], s2[PATH_MAX + 1];
1520 char drive[_MAX_DRIVE + 1], dir[_MAX_DIR + 1], fname[_MAX_FNAME + 1], ext[_MAX_EXT + 1];
1521
1522 s1[0] = s2[0] = 0;
1523
1524 if (Settings.CartAName[0])
1525 {
1526 _splitpath(Settings.CartAName, drive, dir, fname, ext);
1527 snprintf(s1, PATH_MAX + 1, "%s%s%s", S9xGetDirectory(ROM_DIR), SLASH_STR, fname);
1528 if (ext[0] && (strlen(s1) <= PATH_MAX - 1 - strlen(ext)))
1529 {
1530 strcat(s1, ".");
1531 strcat(s1, ext);
1532 }
1533 }
1534
1535 if (Settings.CartBName[0])
1536 {
1537 _splitpath(Settings.CartBName, drive, dir, fname, ext);
1538 snprintf(s2, PATH_MAX + 1, "%s%s%s", S9xGetDirectory(ROM_DIR), SLASH_STR, fname);
1539 if (ext[0] && (strlen(s2) <= PATH_MAX - 1 - strlen(ext)))
1540 {
1541 strcat(s2, ".");
1542 strcat(s2, ext);
1543 }
1544 }
1545
1546 loaded = Memory.LoadMultiCart(s1, s2);
1547 }
1548 }
1549 else
1550 if (rom_filename)
1551 {
1552 loaded = Memory.LoadROM(rom_filename);
1553
1554 if (!loaded && rom_filename[0])
1555 {
1556 char s[PATH_MAX + 1];
1557 char drive[_MAX_DRIVE + 1], dir[_MAX_DIR + 1], fname[_MAX_FNAME + 1], ext[_MAX_EXT + 1];
1558
1559 _splitpath(rom_filename, drive, dir, fname, ext);
1560 snprintf(s, PATH_MAX + 1, "%s%s%s", S9xGetDirectory(ROM_DIR), SLASH_STR, fname);
1561 if (ext[0] && (strlen(s) <= PATH_MAX - 1 - strlen(ext)))
1562 {
1563 strcat(s, ".");
1564 strcat(s, ext);
1565 }
1566
1567 loaded = Memory.LoadROM(s);
1568 }
1569 }
1570
1571 if (!loaded)
1572 {
1573 fprintf(stderr, "Error opening the ROM file.\n");
1574 exit(1);
1575 }
1576
1577 S9xDeleteCheats();
1578 S9xCheatsEnable();
1579 NSRTControllerSetup();
1580 Memory.LoadSRAM(S9xGetFilename(".srm", SRAM_DIR));
1581
1582 if (Settings.ApplyCheats)
1583 {
1584 S9xLoadCheatFile(S9xGetFilename(".cht", CHEAT_DIR));
1585 }
1586
1587 S9xParseArgsForCheats(argv, argc);
1588
1589 CPU.Flags = saved_flags;
1590 Settings.StopEmulation = FALSE;
1591
1592#ifdef DEBUGGER
1593 struct sigaction sa;
1594 sa.sa_handler = sigbrkhandler;
1595#ifdef SA_RESTART
1596 sa.sa_flags = SA_RESTART;
1597#else
1598 sa.sa_flags = 0;
1599#endif
1600 sigemptyset(&sa.sa_mask);
1601 sigaction(SIGINT, &sa, NULL);
1602#endif
1603
1604 S9xInitInputDevices();
1605 S9xInitDisplay(argc, argv);
1606 S9xSetupDefaultKeymap();
1607 S9xTextMode();
1608
1609#ifdef NETPLAY_SUPPORT
1610 if (strlen(Settings.ServerName) == 0)
1611 {
1612 char *server = getenv("S9XSERVER");
1613 if (server)
1614 {
1615 strncpy(Settings.ServerName, server, 127);
1616 Settings.ServerName[127] = 0;
1617 }
1618 }
1619
1620 char *port = getenv("S9XPORT");
1621 if (Settings.Port >= 0 && port)
1622 Settings.Port = atoi(port);
1623 else
1624 if (Settings.Port < 0)
1625 Settings.Port = -Settings.Port;
1626
1627 if (Settings.NetPlay)
1628 {
1629 NetPlay.MaxFrameSkip = 10;
1630
1631 unixSettings.rewindBufferSize = 0;
1632
1633 if (!S9xNPConnectToServer(Settings.ServerName, Settings.Port, Memory.ROMName))
1634 {
1635 fprintf(stderr, "Failed to connect to server %s on port %d.\n", Settings.ServerName, Settings.Port);
1636 S9xExit();
1637 }
1638
1639 fprintf(stderr, "Connected to server %s on port %d as player #%d playing %s.\n", Settings.ServerName, Settings.Port, NetPlay.Player, Memory.ROMName);
1640 }
1641#endif
1642
1643 if (play_smv_filename)
1644 {
1645 uint32 flags = CPU.Flags & (DEBUG_MODE_FLAG | TRACE_FLAG);
1646 if (S9xMovieOpen(play_smv_filename, TRUE) != SUCCESS)
1647 exit(1);
1648 CPU.Flags |= flags;
1649 }
1650 else
1651 if (record_smv_filename)
1652 {
1653 uint32 flags = CPU.Flags & (DEBUG_MODE_FLAG | TRACE_FLAG);
1654 if (S9xMovieCreate(record_smv_filename, 0xFF, MOVIE_OPT_FROM_RESET, NULL, 0) != SUCCESS)
1655 exit(1);
1656 CPU.Flags |= flags;
1657 }
1658 else
1659 {
1660 if (snapshot_filename)
1661 {
1662 uint32 flags = CPU.Flags & (DEBUG_MODE_FLAG | TRACE_FLAG);
1663 if (!S9xUnfreezeGame(snapshot_filename))
1664 exit(1);
1665 CPU.Flags |= flags;
1666 }
1667 if (unixSettings.rewindBufferSize)
1668 {
1669 stateMan.init(unixSettings.rewindBufferSize * 1024 * 1024);
1670 }
1671 }
1672
1673 S9xGraphicsMode();
1674
1675 sprintf(String, "\"%s\" %s: %s", Memory.ROMName, TITLE, VERSION);
1676 S9xSetTitle(String);
1677
1678#ifdef JOYSTICK_SUPPORT
1679 uint32 JoypadSkip = 0;
1680#endif
1681
1682 S9xSetSoundMute(FALSE);
1683
1684#ifdef NETPLAY_SUPPORT
1685 bool8 NP_Activated = Settings.NetPlay;
1686#endif
1687
1688 while (1)
1689 {
1690 #ifdef NETPLAY_SUPPORT
1691 if (NP_Activated)
1692 {
1693 if (NetPlay.PendingWait4Sync && !S9xNPWaitForHeartBeatDelay(100))
1694 {
1695 S9xProcessEvents(FALSE);
1696 continue;
1697 }
1698
1699 for (int J = 0; J < 8; J++)
1700 old_joypads[J] = MovieGetJoypad(J);
1701
1702 for (int J = 0; J < 8; J++)
1703 MovieSetJoypad(J, joypads[J]);
1704
1705 if (NetPlay.Connected)
1706 {
1707 if (NetPlay.PendingWait4Sync)
1708 {
1709 NetPlay.PendingWait4Sync = FALSE;
1710 NetPlay.FrameCount++;
1711 S9xNPStepJoypadHistory();
1712 }
1713 }
1714 else
1715 {
1716 fprintf(stderr, "Lost connection to server.\n");
1717 S9xExit();
1718 }
1719 }
1720 #endif
1721
1722 #ifdef DEBUGGER
1723 if (!Settings.Paused || (CPU.Flags & (DEBUG_MODE_FLAG | SINGLE_STEP_FLAG)))
1724 #else
1725 if (!Settings.Paused)
1726 #endif
1727 {
1728 if(rewinding)
1729 {
1730 uint16 joypads[8];
1731 for (int i = 0; i < 8; i++)
1732 joypads[i] = MovieGetJoypad(i);
1733
1734 rewinding = stateMan.pop();
1735
1736 for (int i = 0; i < 8; i++)
1737 MovieSetJoypad (i, joypads[i]);
1738 }
1739 else if(IPPU.TotalEmulatedFrames % unixSettings.rewindGranularity == 0)
1740 stateMan.push();
1741
1742 S9xMainLoop();
1743 }
1744 if (Settings.Paused && frame_advance)
1745 {
1746 S9xMainLoop();
1747 frame_advance = 0;
1748 }
1749
1750 #ifdef NETPLAY_SUPPORT
1751 if (NP_Activated)
1752 {
1753 for (int J = 0; J < 8; J++)
1754 MovieSetJoypad(J, old_joypads[J]);
1755 }
1756 #endif
1757
1758 #ifdef DEBUGGER
1759 if (Settings.Paused || (CPU.Flags & DEBUG_MODE_FLAG))
1760 #else
1761 if (Settings.Paused)
1762 #endif
1763 S9xSetSoundMute(TRUE);
1764
1765 #ifdef DEBUGGER
1766 if (CPU.Flags & DEBUG_MODE_FLAG)
1767 S9xDoDebug();
1768 else
1769 #endif
1770 if (Settings.Paused)
1771 {
1772 S9xProcessEvents(FALSE);
1773 usleep(100000);
1774 }
1775
1776 #ifdef JOYSTICK_SUPPORT
1777 if (unixSettings.JoystickEnabled && (JoypadSkip++ & 1) == 0)
1778 {
1779 if (ReadJoysticks() == TRUE) {
1780 S9xLatchJSEvent();
1781 }
1782 }
1783 #endif
1784
1785 S9xProcessEvents(FALSE);
1786
1787 #ifdef DEBUGGER
1788 if (!Settings.Paused && !(CPU.Flags & DEBUG_MODE_FLAG))
1789 #else
1790 if (!Settings.Paused)
1791 #endif
1792 S9xSetSoundMute(FALSE);
1793 }
1794
1795 return (0);
1796}
1797