/*********************************************************************************** Snes9x - Portable Super Nintendo Entertainment System (TM) emulator. See CREDITS file to find the copyright owners of this file. SDL Input/Audio/Video code (many lines of code come from snes9x & drnoksnes) (c) Copyright 2011 Makoto Sugano (makoto.sugano@gmail.com) Snes9x homepage: http://www.snes9x.com/ Permission to use, copy, modify and/or distribute Snes9x in both binary and source form, for non-commercial purposes, is hereby granted without fee, providing that this license information and copyright notice appear with all copies and any derived work. This software is provided 'as-is', without any express or implied warranty. In no event shall the authors be held liable for any damages arising from the use of this software or it's derivatives. Snes9x is freeware for PERSONAL USE only. Commercial users should seek permission of the copyright holders first. Commercial use includes, but is not limited to, charging money for Snes9x or software derived from Snes9x, including Snes9x or derivatives in commercial game bundles, and/or using Snes9x as a promotion for your commercial product. The copyright holders request that bug fixes and improvements to the code should be forwarded to them so everyone can benefit from the modifications in future versions. Super NES and Super Nintendo Entertainment System are trademarks of Nintendo Co., Limited and its subsidiary companies. ***********************************************************************************/ #include #include #include #include #include #include #ifdef HAVE_STRINGS_H #include #endif #include #include #include #include "sdl_snes9x.h" #include "snes9x.h" #include "memmap.h" #include "apu/apu.h" #include "gfx.h" #include "snapshot.h" #include "controls.h" #include "cheats.h" #include "movie.h" #include "logger.h" #include "display.h" #include "conffile.h" #ifdef NETPLAY_SUPPORT #include "netplay.h" #endif #ifdef DEBUGGER #include "debug.h" #endif #ifdef NETPLAY_SUPPORT #ifdef _DEBUG #define NP_DEBUG 2 #endif #endif static const char *s9x_base_dir = NULL, *rom_filename = NULL, *snapshot_filename = NULL, *play_smv_filename = NULL, *record_smv_filename = NULL; extern uint32 sound_buffer_size; // used in sdlaudio static char default_dir[PATH_MAX + 1]; static const char dirNames[13][32] = { "", // DEFAULT_DIR "", // HOME_DIR "", // ROMFILENAME_DIR "rom", // ROM_DIR "sram", // SRAM_DIR "savestate", // SNAPSHOT_DIR "screenshot", // SCREENSHOT_DIR "spc", // SPC_DIR "cheat", // CHEAT_DIR "patch", // IPS_DIR "bios", // BIOS_DIR "log", // LOG_DIR "" }; #ifdef NETPLAY_SUPPORT static uint32 joypads[8]; static uint32 old_joypads[8]; #endif void S9xParseInputConfig(ConfigFile &, int pass); // defined in sdlinput static long log2 (long); static void NSRTControllerSetup (void); static int make_snes9x_dirs (void); void _splitpath (const char *path, char *drive, char *dir, char *fname, char *ext) { *drive = 0; const char *slash = strrchr(path, SLASH_CHAR), *dot = strrchr(path, '.'); if (dot && slash && dot < slash) dot = NULL; if (!slash) { *dir = 0; strcpy(fname, path); if (dot) { fname[dot - path] = 0; strcpy(ext, dot + 1); } else *ext = 0; } else { strcpy(dir, path); dir[slash - path] = 0; strcpy(fname, slash + 1); if (dot) { fname[dot - slash - 1] = 0; strcpy(ext, dot + 1); } else *ext = 0; } } void _makepath (char *path, const char *, const char *dir, const char *fname, const char *ext) { if (dir && *dir) { strcpy(path, dir); strcat(path, SLASH_STR); } else *path = 0; strcat(path, fname); if (ext && *ext) { strcat(path, "."); strcat(path, ext); } } static long log2 (long num) { long n = 0; while (num >>= 1) n++; return (n); } void S9xExtraUsage (void) // domaemon: ExtraUsage -> ExtraDisplayUsage { /* 12345678901234567890123456789012345678901234567890123456789012345678901234567890 */ S9xMessage(S9X_INFO, S9X_USAGE, "-multi Enable multi cartridge system"); S9xMessage(S9X_INFO, S9X_USAGE, "-carta ROM in slot A (use with -multi)"); S9xMessage(S9X_INFO, S9X_USAGE, "-cartb ROM in slot B (use with -multi)"); S9xMessage(S9X_INFO, S9X_USAGE, ""); S9xMessage(S9X_INFO, S9X_USAGE, "-buffersize Sound generating buffer size in millisecond"); S9xMessage(S9X_INFO, S9X_USAGE, ""); S9xMessage(S9X_INFO, S9X_USAGE, "-loadsnapshot Load snapshot file at start"); S9xMessage(S9X_INFO, S9X_USAGE, "-playmovie Start emulator playing the .smv file"); S9xMessage(S9X_INFO, S9X_USAGE, "-recordmovie Start emulator recording the .smv file"); S9xMessage(S9X_INFO, S9X_USAGE, "-dumpstreams Save audio/video data to disk"); S9xMessage(S9X_INFO, S9X_USAGE, "-dumpmaxframes Stop emulator after saving specified number of"); S9xMessage(S9X_INFO, S9X_USAGE, " frames (use with -dumpstreams)"); S9xMessage(S9X_INFO, S9X_USAGE, ""); S9xExtraDisplayUsage(); } /* * domaemon: arg is parsed as ParseArg -> ParseDisplayArg */ void S9xParseArg (char **argv, int &i, int argc) { if (!strcasecmp(argv[i], "-multi")) Settings.Multi = TRUE; else if (!strcasecmp(argv[i], "-carta")) { if (i + 1 < argc) strncpy(Settings.CartAName, argv[++i], _MAX_PATH); else S9xUsage(); } else if (!strcasecmp(argv[i], "-cartb")) { if (i + 1 < argc) strncpy(Settings.CartBName, argv[++i], _MAX_PATH); else S9xUsage(); } else if (!strcasecmp(argv[i], "-buffersize")) { if (i + 1 < argc) sound_buffer_size = atoi(argv[++i]); else S9xUsage(); } else if (!strcasecmp(argv[i], "-loadsnapshot")) { if (i + 1 < argc) snapshot_filename = argv[++i]; else S9xUsage(); } else if (!strcasecmp(argv[i], "-playmovie")) { if (i + 1 < argc) play_smv_filename = argv[++i]; else S9xUsage(); } else if (!strcasecmp(argv[i], "-recordmovie")) { if (i + 1 < argc) record_smv_filename = argv[++i]; else S9xUsage(); } else if (!strcasecmp(argv[i], "-dumpstreams")) Settings.DumpStreams = TRUE; else if (!strcasecmp(argv[i], "-dumpmaxframes")) Settings.DumpStreamsMaxFrames = atoi(argv[++i]); else S9xParseDisplayArg(argv, i, argc); } static void NSRTControllerSetup (void) { if (!strncmp((const char *) Memory.NSRTHeader + 24, "NSRT", 4)) { // First plug in both, they'll change later as needed S9xSetController(0, CTL_JOYPAD, 0, 0, 0, 0); S9xSetController(1, CTL_JOYPAD, 1, 0, 0, 0); switch (Memory.NSRTHeader[29]) { case 0x00: // Everything goes break; case 0x10: // Mouse in Port 0 S9xSetController(0, CTL_MOUSE, 0, 0, 0, 0); break; case 0x01: // Mouse in Port 1 S9xSetController(1, CTL_MOUSE, 1, 0, 0, 0); break; case 0x03: // Super Scope in Port 1 S9xSetController(1, CTL_SUPERSCOPE, 0, 0, 0, 0); break; case 0x06: // Multitap in Port 1 S9xSetController(1, CTL_MP5, 1, 2, 3, 4); break; case 0x66: // Multitap in Ports 0 and 1 S9xSetController(0, CTL_MP5, 0, 1, 2, 3); S9xSetController(1, CTL_MP5, 4, 5, 6, 7); break; case 0x08: // Multitap in Port 1, Mouse in new Port 1 S9xSetController(1, CTL_MOUSE, 1, 0, 0, 0); // There should be a toggle here for putting in Multitap instead break; case 0x04: // Pad or Super Scope in Port 1 S9xSetController(1, CTL_SUPERSCOPE, 0, 0, 0, 0); // There should be a toggle here for putting in a pad instead break; case 0x05: // Justifier - Must ask user... S9xSetController(1, CTL_JUSTIFIER, 1, 0, 0, 0); // There should be a toggle here for how many justifiers break; case 0x20: // Pad or Mouse in Port 0 S9xSetController(0, CTL_MOUSE, 0, 0, 0, 0); // There should be a toggle here for putting in a pad instead break; case 0x22: // Pad or Mouse in Port 0 & 1 S9xSetController(0, CTL_MOUSE, 0, 0, 0, 0); S9xSetController(1, CTL_MOUSE, 1, 0, 0, 0); // There should be a toggles here for putting in pads instead break; case 0x24: // Pad or Mouse in Port 0, Pad or Super Scope in Port 1 // There should be a toggles here for what to put in, I'm leaving it at gamepad for now break; case 0x27: // Pad or Mouse in Port 0, Pad or Mouse or Super Scope in Port 1 // There should be a toggles here for what to put in, I'm leaving it at gamepad for now break; // Not Supported yet case 0x99: // Lasabirdie break; case 0x0A: // Barcode Battler break; } } } /* * domaemon: config is parsed as * * ParsePortConfig -> ParseInputConfig * ParsePortConfig -> ParseDisplayConfig */ void S9xParsePortConfig (ConfigFile &conf, int pass) { s9x_base_dir = conf.GetStringDup("Unix::BaseDir", default_dir); snapshot_filename = conf.GetStringDup("Unix::SnapshotFilename", NULL); play_smv_filename = conf.GetStringDup("Unix::PlayMovieFilename", NULL); record_smv_filename = conf.GetStringDup("Unix::RecordMovieFilename", NULL); sound_buffer_size = conf.GetUInt ("Unix::SoundBufferSize", 100); // domaemon: default input configuration S9xParseInputConfig(conf, 1); std::string section = S9xParseDisplayConfig(conf, 1); ConfigFile::secvec_t sec = conf.GetSection((section + " Controls").c_str()); for (ConfigFile::secvec_t::iterator c = sec.begin(); c != sec.end(); c++) keymaps.push_back(*c); } static int make_snes9x_dirs (void) { if (strlen(s9x_base_dir) + 1 + sizeof(dirNames[0]) > PATH_MAX + 1) return (-1); mkdir(s9x_base_dir, 0755); for (int i = 0; i < LAST_DIR; i++) { if (dirNames[i][0]) { char s[PATH_MAX + 1]; snprintf(s, PATH_MAX + 1, "%s%s%s", s9x_base_dir, SLASH_STR, dirNames[i]); mkdir(s, 0755); } } return (0); } const char * S9xGetDirectory (enum s9x_getdirtype dirtype) { static char s[PATH_MAX + 1]; if (dirNames[dirtype][0]) snprintf(s, PATH_MAX + 1, "%s%s%s", s9x_base_dir, SLASH_STR, dirNames[dirtype]); else { switch (dirtype) { case DEFAULT_DIR: strncpy(s, s9x_base_dir, PATH_MAX + 1); s[PATH_MAX] = 0; break; case HOME_DIR: strncpy(s, getenv("HOME"), PATH_MAX + 1); s[PATH_MAX] = 0; break; case ROMFILENAME_DIR: strncpy(s, Memory.ROMFilename, PATH_MAX + 1); s[PATH_MAX] = 0; for (int i = strlen(s); i >= 0; i--) { if (s[i] == SLASH_CHAR) { s[i] = 0; break; } } break; default: s[0] = 0; break; } } return (s); } const char * S9xGetFilename (const char *ex, enum s9x_getdirtype dirtype) { static char s[PATH_MAX + 1]; char drive[_MAX_DRIVE + 1], dir[_MAX_DIR + 1], fname[_MAX_FNAME + 1], ext[_MAX_EXT + 1]; _splitpath(Memory.ROMFilename, drive, dir, fname, ext); snprintf(s, PATH_MAX + 1, "%s%s%s%s", S9xGetDirectory(dirtype), SLASH_STR, fname, ex); return (s); } const char * S9xGetFilenameInc (const char *ex, enum s9x_getdirtype dirtype) { static char s[PATH_MAX + 1]; char drive[_MAX_DRIVE + 1], dir[_MAX_DIR + 1], fname[_MAX_FNAME + 1], ext[_MAX_EXT + 1]; unsigned int i = 0; const char *d; struct stat buf; _splitpath(Memory.ROMFilename, drive, dir, fname, ext); d = S9xGetDirectory(dirtype); do snprintf(s, PATH_MAX + 1, "%s%s%s.%03d%s", d, SLASH_STR, fname, i++, ex); while (stat(s, &buf) == 0 && i < 1000); return (s); } const char * S9xBasename (const char *f) { const char *p; if ((p = strrchr(f, '/')) != NULL || (p = strrchr(f, '\\')) != NULL) return (p + 1); return (f); } const char * S9xSelectFilename (const char *def, const char *dir1, const char *ext1, const char *title) { static char s[PATH_MAX + 1]; char buffer[PATH_MAX + 1]; printf("\n%s (default: %s): ", title, def); fflush(stdout); if (fgets(buffer, PATH_MAX + 1, stdin)) { char drive[_MAX_DRIVE + 1], dir[_MAX_DIR + 1], fname[_MAX_FNAME + 1], ext[_MAX_EXT + 1]; char *p = buffer; while (isspace(*p)) p++; if (!*p) { strncpy(buffer, def, PATH_MAX + 1); buffer[PATH_MAX] = 0; p = buffer; } char *q = strrchr(p, '\n'); if (q) *q = 0; _splitpath(p, drive, dir, fname, ext); _makepath(s, drive, *dir ? dir : dir1, fname, *ext ? ext : ext1); return (s); } return (NULL); } const char * S9xChooseFilename (bool8 read_only) { char s[PATH_MAX + 1]; char drive[_MAX_DRIVE + 1], dir[_MAX_DIR + 1], fname[_MAX_FNAME + 1], ext[_MAX_EXT + 1]; const char *filename; char title[64]; _splitpath(Memory.ROMFilename, drive, dir, fname, ext); snprintf(s, PATH_MAX + 1, "%s.frz", fname); sprintf(title, "%s snapshot filename", read_only ? "Select load" : "Choose save"); S9xSetSoundMute(TRUE); filename = S9xSelectFilename(s, S9xGetDirectory(SNAPSHOT_DIR), "frz", title); S9xSetSoundMute(FALSE); return (filename); } const char * S9xChooseMovieFilename (bool8 read_only) { char s[PATH_MAX + 1]; char drive[_MAX_DRIVE + 1], dir[_MAX_DIR + 1], fname[_MAX_FNAME + 1], ext[_MAX_EXT + 1]; const char *filename; char title[64]; _splitpath(Memory.ROMFilename, drive, dir, fname, ext); snprintf(s, PATH_MAX + 1, "%s.smv", fname); sprintf(title, "Choose movie %s filename", read_only ? "playback" : "record"); S9xSetSoundMute(TRUE); filename = S9xSelectFilename(s, S9xGetDirectory(HOME_DIR), "smv", title); S9xSetSoundMute(FALSE); return (filename); } bool8 S9xOpenSnapshotFile (const char *filename, bool8 read_only, STREAM *file) { char s[PATH_MAX + 1]; char drive[_MAX_DRIVE + 1], dir[_MAX_DIR + 1], fname[_MAX_FNAME + 1], ext[_MAX_EXT + 1]; _splitpath(filename, drive, dir, fname, ext); if (*drive || *dir == SLASH_CHAR || (strlen(dir) > 1 && *dir == '.' && *(dir + 1) == SLASH_CHAR)) { strncpy(s, filename, PATH_MAX + 1); s[PATH_MAX] = 0; } else snprintf(s, PATH_MAX + 1, "%s%s%s", S9xGetDirectory(SNAPSHOT_DIR), SLASH_STR, fname); if (!*ext && strlen(s) <= PATH_MAX - 4) strcat(s, ".frz"); if ((*file = OPEN_STREAM(s, read_only ? "rb" : "wb"))) return (TRUE); return (FALSE); } void S9xCloseSnapshotFile (STREAM file) { CLOSE_STREAM(file); } bool8 S9xInitUpdate (void) { return (TRUE); } bool8 S9xDeinitUpdate (int width, int height) { S9xPutImage(width, height); return (TRUE); } bool8 S9xContinueUpdate (int width, int height) { return (TRUE); } void S9xAutoSaveSRAM (void) { Memory.SaveSRAM(S9xGetFilename(".srm", SRAM_DIR)); } void S9xSyncSpeed (void) { // doemaemon: not sure how crucial this is atm. if (Settings.SoundSync) { while (!S9xSyncSound()) usleep(0); } if (Settings.DumpStreams) return; #ifdef NETPLAY_SUPPORT if (Settings.NetPlay && NetPlay.Connected) { #if defined(NP_DEBUG) && NP_DEBUG == 2 printf("CLIENT: SyncSpeed @%d\n", S9xGetMilliTime()); #endif S9xNPSendJoypadUpdate(old_joypads[0]); for (int J = 0; J < 8; J++) joypads[J] = S9xNPGetJoypad(J); if (!S9xNPCheckForHeartBeat()) { NetPlay.PendingWait4Sync = !S9xNPWaitForHeartBeatDelay(100); #if defined(NP_DEBUG) && NP_DEBUG == 2 if (NetPlay.PendingWait4Sync) printf("CLIENT: PendingWait4Sync1 @%d\n", S9xGetMilliTime()); #endif IPPU.RenderThisFrame = TRUE; IPPU.SkippedFrames = 0; } else { NetPlay.PendingWait4Sync = !S9xNPWaitForHeartBeatDelay(200); #if defined(NP_DEBUG) && NP_DEBUG == 2 if (NetPlay.PendingWait4Sync) printf("CLIENT: PendingWait4Sync2 @%d\n", S9xGetMilliTime()); #endif if (IPPU.SkippedFrames < NetPlay.MaxFrameSkip) { IPPU.RenderThisFrame = FALSE; IPPU.SkippedFrames++; } else { IPPU.RenderThisFrame = TRUE; IPPU.SkippedFrames = 0; } } if (!NetPlay.PendingWait4Sync) { NetPlay.FrameCount++; S9xNPStepJoypadHistory(); } return; } #endif if (Settings.HighSpeedSeek > 0) Settings.HighSpeedSeek--; if (Settings.TurboMode) { if ((++IPPU.FrameSkip >= Settings.TurboSkipFrames) && !Settings.HighSpeedSeek) { IPPU.FrameSkip = 0; IPPU.SkippedFrames = 0; IPPU.RenderThisFrame = TRUE; } else { IPPU.SkippedFrames++; IPPU.RenderThisFrame = FALSE; } return; } static struct timeval next1 = { 0, 0 }; struct timeval now; while (gettimeofday(&now, NULL) == -1) ; // If there is no known "next" frame, initialize it now. if (next1.tv_sec == 0) { next1 = now; next1.tv_usec++; } // If we're on AUTO_FRAMERATE, we'll display frames always only if there's excess time. // Otherwise we'll display the defined amount of frames. unsigned limit = (Settings.SkipFrames == AUTO_FRAMERATE) ? (timercmp(&next1, &now, <) ? 10 : 1) : Settings.SkipFrames; IPPU.RenderThisFrame = (++IPPU.SkippedFrames >= limit) ? TRUE : FALSE; if (IPPU.RenderThisFrame) IPPU.SkippedFrames = 0; else { // If we were behind the schedule, check how much it is. if (timercmp(&next1, &now, <)) { unsigned lag = (now.tv_sec - next1.tv_sec) * 1000000 + now.tv_usec - next1.tv_usec; if (lag >= 500000) { // More than a half-second behind means probably pause. // The next line prevents the magic fast-forward effect. next1 = now; } } } // Delay until we're completed this frame. // Can't use setitimer because the sound code already could be using it. We don't actually need it either. while (timercmp(&next1, &now, >)) { // If we're ahead of time, sleep a while. unsigned timeleft = (next1.tv_sec - now.tv_sec) * 1000000 + next1.tv_usec - now.tv_usec; usleep(timeleft); while (gettimeofday(&now, NULL) == -1) ; // Continue with a while-loop because usleep() could be interrupted by a signal. } // Calculate the timestamp of the next frame. next1.tv_usec += Settings.FrameTime; if (next1.tv_usec >= 1000000) { next1.tv_sec += next1.tv_usec / 1000000; next1.tv_usec %= 1000000; } } void S9xExit (void) { S9xMovieShutdown(); S9xSetSoundMute(TRUE); Settings.StopEmulation = TRUE; #ifdef NETPLAY_SUPPORT if (Settings.NetPlay) S9xNPDisconnect(); #endif Memory.SaveSRAM(S9xGetFilename(".srm", SRAM_DIR)); S9xSaveCheatFile(S9xGetFilename(".cht", CHEAT_DIR)); S9xResetSaveTimer(FALSE); S9xUnmapAllControls(); S9xDeinitDisplay(); Memory.Deinit(); S9xDeinitAPU(); exit(0); } #ifdef DEBUGGER static void sigbrkhandler (int) { CPU.Flags |= DEBUG_MODE_FLAG; signal(SIGINT, (SIG_PF) sigbrkhandler); } #endif int main (int argc, char **argv) { if (argc < 2) S9xUsage(); printf("\n\nSnes9x " VERSION " for unix/SDL\n"); snprintf(default_dir, PATH_MAX + 1, "%s%s%s", getenv("HOME"), SLASH_STR, ".snes9x"); s9x_base_dir = default_dir; memset(&Settings,0,sizeof(Settings)); Settings.MouseMaster = TRUE; Settings.SuperScopeMaster = TRUE; Settings.JustifierMaster = TRUE; Settings.MultiPlayer5Master = TRUE; Settings.FrameTimePAL = 20000; Settings.FrameTimeNTSC = 16667; Settings.SixteenBitSound = TRUE; Settings.Stereo = TRUE; Settings.SoundPlaybackRate = 32000; Settings.SoundInputRate = 32000; Settings.SupportHiRes = TRUE; Settings.Transparency = TRUE; Settings.AutoDisplayMessages = TRUE; Settings.InitialInfoStringTimeout = 120; Settings.HDMATimingHack = 100; Settings.BlockInvalidVRAMAccessMaster = TRUE; Settings.StopEmulation = TRUE; Settings.WrongMovieStateProtection = TRUE; Settings.DumpStreamsMaxFrames = -1; Settings.StretchScreenshots = 1; Settings.SnapshotScreenshots = TRUE; Settings.SkipFrames = AUTO_FRAMERATE; Settings.TurboSkipFrames = 15; Settings.CartAName[0] = 0; Settings.CartBName[0] = 0; #ifdef NETPLAY_SUPPORT Settings.ServerName[0] = 0; #endif CPU.Flags = 0; S9xLoadConfigFiles(argv, argc); rom_filename = S9xParseArgs(argv, argc); make_snes9x_dirs(); if (!Memory.Init() || !S9xInitAPU()) { fprintf(stderr, "Snes9x: Memory allocation failure - not enough RAM/virtual memory available.\nExiting...\n"); Memory.Deinit(); S9xDeinitAPU(); exit(1); } S9xInitSound(sound_buffer_size, 0); S9xSetSoundMute(TRUE); S9xReportControllers(); #ifdef GFX_MULTI_FORMAT S9xSetRenderPixelFormat(RGB565); #endif uint32 saved_flags = CPU.Flags; bool8 loaded = FALSE; if (Settings.Multi) { loaded = Memory.LoadMultiCart(Settings.CartAName, Settings.CartBName); if (!loaded) { char s1[PATH_MAX + 1], s2[PATH_MAX + 1]; char drive[_MAX_DRIVE + 1], dir[_MAX_DIR + 1], fname[_MAX_FNAME + 1], ext[_MAX_EXT + 1]; s1[0] = s2[0] = 0; if (Settings.CartAName[0]) { _splitpath(Settings.CartAName, drive, dir, fname, ext); snprintf(s1, PATH_MAX + 1, "%s%s%s", S9xGetDirectory(ROM_DIR), SLASH_STR, fname); if (ext[0] && (strlen(s1) <= PATH_MAX - 1 - strlen(ext))) { strcat(s1, "."); strcat(s1, ext); } } if (Settings.CartBName[0]) { _splitpath(Settings.CartBName, drive, dir, fname, ext); snprintf(s2, PATH_MAX + 1, "%s%s%s", S9xGetDirectory(ROM_DIR), SLASH_STR, fname); if (ext[0] && (strlen(s2) <= PATH_MAX - 1 - strlen(ext))) { strcat(s2, "."); strcat(s2, ext); } } loaded = Memory.LoadMultiCart(s1, s2); } } else if (rom_filename) { loaded = Memory.LoadROM(rom_filename); if (!loaded && rom_filename[0]) { char s[PATH_MAX + 1]; char drive[_MAX_DRIVE + 1], dir[_MAX_DIR + 1], fname[_MAX_FNAME + 1], ext[_MAX_EXT + 1]; _splitpath(rom_filename, drive, dir, fname, ext); snprintf(s, PATH_MAX + 1, "%s%s%s", S9xGetDirectory(ROM_DIR), SLASH_STR, fname); if (ext[0] && (strlen(s) <= PATH_MAX - 1 - strlen(ext))) { strcat(s, "."); strcat(s, ext); } loaded = Memory.LoadROM(s); } } if (!loaded) { fprintf(stderr, "Error opening the ROM file.\n"); exit(1); } NSRTControllerSetup(); Memory.LoadSRAM(S9xGetFilename(".srm", SRAM_DIR)); S9xLoadCheatFile(S9xGetFilename(".cht", CHEAT_DIR)); CPU.Flags = saved_flags; Settings.StopEmulation = FALSE; #ifdef DEBUGGER struct sigaction sa; sa.sa_handler = sigbrkhandler; #ifdef SA_RESTART sa.sa_flags = SA_RESTART; #else sa.sa_flags = 0; #endif sigemptyset(&sa.sa_mask); sigaction(SIGINT, &sa, NULL); #endif S9xInitInputDevices(); S9xInitDisplay(argc, argv); S9xSetupDefaultKeymap(); #ifdef NETPLAY_SUPPORT if (strlen(Settings.ServerName) == 0) { char *server = getenv("S9XSERVER"); if (server) { strncpy(Settings.ServerName, server, 127); Settings.ServerName[127] = 0; } } char *port = getenv("S9XPORT"); if (Settings.Port >= 0 && port) Settings.Port = atoi(port); else if (Settings.Port < 0) Settings.Port = -Settings.Port; if (Settings.NetPlay) { NetPlay.MaxFrameSkip = 10; if (!S9xNPConnectToServer(Settings.ServerName, Settings.Port, Memory.ROMName)) { fprintf(stderr, "Failed to connect to server %s on port %d.\n", Settings.ServerName, Settings.Port); S9xExit(); } fprintf(stderr, "Connected to server %s on port %d as player #%d playing %s.\n", Settings.ServerName, Settings.Port, NetPlay.Player, Memory.ROMName); } #endif if (play_smv_filename) { uint32 flags = CPU.Flags & (DEBUG_MODE_FLAG | TRACE_FLAG); if (S9xMovieOpen(play_smv_filename, TRUE) != SUCCESS) exit(1); CPU.Flags |= flags; } else if (record_smv_filename) { uint32 flags = CPU.Flags & (DEBUG_MODE_FLAG | TRACE_FLAG); if (S9xMovieCreate(record_smv_filename, 0xFF, MOVIE_OPT_FROM_RESET, NULL, 0) != SUCCESS) exit(1); CPU.Flags |= flags; } else if (snapshot_filename) { uint32 flags = CPU.Flags & (DEBUG_MODE_FLAG | TRACE_FLAG); if (!S9xUnfreezeGame(snapshot_filename)) exit(1); CPU.Flags |= flags; } sprintf(String, "\"%s\" %s: %s", Memory.ROMName, TITLE, VERSION); // domaemon: setting the title on the window bar S9xSetTitle(String); S9xSetSoundMute(FALSE); #ifdef NETPLAY_SUPPORT bool8 NP_Activated = Settings.NetPlay; #endif while (1) { #ifdef NETPLAY_SUPPORT if (NP_Activated) { if (NetPlay.PendingWait4Sync && !S9xNPWaitForHeartBeatDelay(100)) { S9xProcessEvents(FALSE); continue; } for (int J = 0; J < 8; J++) old_joypads[J] = MovieGetJoypad(J); for (int J = 0; J < 8; J++) MovieSetJoypad(J, joypads[J]); if (NetPlay.Connected) { if (NetPlay.PendingWait4Sync) { NetPlay.PendingWait4Sync = FALSE; NetPlay.FrameCount++; S9xNPStepJoypadHistory(); } } else { fprintf(stderr, "Lost connection to server.\n"); S9xExit(); } } #endif #ifdef DEBUGGER if (!Settings.Paused || (CPU.Flags & (DEBUG_MODE_FLAG | SINGLE_STEP_FLAG))) #else if (!Settings.Paused) #endif S9xMainLoop(); #ifdef NETPLAY_SUPPORT if (NP_Activated) { for (int J = 0; J < 8; J++) MovieSetJoypad(J, old_joypads[J]); } #endif #ifdef DEBUGGER if (Settings.Paused || (CPU.Flags & DEBUG_MODE_FLAG)) #else if (Settings.Paused) #endif S9xSetSoundMute(TRUE); #ifdef DEBUGGER if (CPU.Flags & DEBUG_MODE_FLAG) S9xDoDebug(); else #endif if (Settings.Paused) { S9xProcessEvents(FALSE); usleep(100000); } S9xProcessEvents(FALSE); #ifdef DEBUGGER if (!Settings.Paused && !(CPU.Flags & DEBUG_MODE_FLAG)) #else if (!Settings.Paused) #endif S9xSetSoundMute(FALSE); } return (0); }