snes9x/sdl/sdlmain.cpp

1074 lines
25 KiB
C++

/***********************************************************************************
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 <stdlib.h>
#include <unistd.h>
#include <ctype.h>
#include <dirent.h>
#include <errno.h>
#include <string.h>
#ifdef HAVE_STRINGS_H
#include <strings.h>
#endif
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/types.h>
#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 <filename> ROM in slot A (use with -multi)");
S9xMessage(S9X_INFO, S9X_USAGE, "-cartb <filename> 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 <filename> Start emulator playing the .smv file");
S9xMessage(S9X_INFO, S9X_USAGE, "-recordmovie <filename> Start emulator recording the .smv file");
S9xMessage(S9X_INFO, S9X_USAGE, "-dumpstreams Save audio/video data to disk");
S9xMessage(S9X_INFO, S9X_USAGE, "-dumpmaxframes <num> 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);
}