15 #define DIE(format, ...) do { \
16 fprintf(stderr, "Died at %s:%d: ", __FILE__, __LINE__ ); \
17 fprintf(stderr, format "\n", ## __VA_ARGS__); \
23 /** Path to current rom file, with extension. */
24 static char * romFile;
25 /** Path to rom file, without extension.
26 * Used as a simple optimization to S9xGetFilename
28 static char * basePath;
30 static struct poptOption commonOptionsTable[] = {
31 { "disable-audio", 'a', POPT_ARG_NONE, 0, 1,
32 "disable emulation and output of audio", 0 },
33 { "display-framerate", 'r', POPT_ARG_NONE, 0, 2,
34 "show frames per second counter in lower left corner", 0 },
35 { "skip-frames", 's', POPT_ARG_INT, 0, 3,
36 "render only 1 in every N frames", "NUM" },
37 { "fullscreen", 'f', POPT_ARG_NONE, 0, 4,
38 "start in fullscreen mode", 0 },
39 { "transparency", 'y', POPT_ARG_NONE, 0, 5,
40 "enable transparency effects (slower)", 0 },
41 { "scaler", 'S', POPT_ARG_STRING, 0, 6,
42 "select scaler to use", 0 },
43 { "pal", 'p', POPT_ARG_NONE, 0, 7,
44 "run in PAL mode", 0 },
45 { "ntsc", 'n', POPT_ARG_NONE, 0, 8,
46 "run in NTSC mode", 0 },
47 { "turbo", 't', POPT_ARG_NONE, 0, 9,
48 "turbo mode (do not try to sleep between frames)", 0 },
49 { "conf", 'c', POPT_ARG_STRING, 0, 10,
50 "extra configuration file to load", "FILE" },
51 { "mouse", 'm', POPT_ARG_INT | POPT_ARGFLAG_OPTIONAL, 0, 11,
52 "enable mouse on controller NUM", "NUM"},
53 { "superscope", 'e', POPT_ARG_NONE, 0, 12,
54 "enable SuperScope", 0},
55 { "snapshot", 'o', POPT_ARG_NONE, 0, 13,
56 "unfreeze previous game on start and freeze game on exit", 0 },
57 { "audio-rate", 'u', POPT_ARG_INT, 0, 14,
58 "audio output rate", "HZ" },
59 { "audio-buffer-size", 'b', POPT_ARG_INT, 0, 15,
60 "audio output buffer size", "SAMPLES" },
61 { "touchscreen", 'd', POPT_ARG_NONE, 0, 16,
62 "enable touchscreen controls", 0 },
63 { "touchscreen-grid", 'D', POPT_ARG_NONE, 0, 17,
64 "enable touchscreen controls and show grid", 0 },
65 { "hacks", 'h', POPT_ARG_NONE, 0, 18,
66 "enable safe subset of speedhacks", 0 },
67 { "all-hacks", 'H', POPT_ARG_NONE, 0, 19,
68 "enable all speedhacks (may break sound)", 0 },
69 { "saver", 'R', POPT_ARG_NONE, 0, 20,
70 "save&exit when the emulator window is unfocused", 0 },
74 static struct poptOption configOptionsTable[] = {
75 { "scancode", '\0', POPT_ARG_INT, 0, 100,
76 "scancode to map", "CODE" },
77 { "button", '\0', POPT_ARG_STRING, 0, 101,
78 "SNES Button to press (A, B, X, Y, L, R, Up, Down, Left, Right)", "name" },
79 { "button2", '\0', POPT_ARG_STRING, 0, 102,
80 "SNES Button to press for joypad 2", "name" },
81 { "action", '\0', POPT_ARG_STRING, 0, 110,
82 "emulator action to do (fullscreen, quit, ...)", "action" },
83 { "hacks-file", '\0', POPT_ARG_STRING, 0, 200,
84 "path to snesadvance.dat file", "FILE" },
88 static struct poptOption optionsTable[] = {
89 { 0, '\0', POPT_ARG_INCLUDE_TABLE, commonOptionsTable, 0,
90 "Common options", 0 },
91 { 0, '\0', POPT_ARG_INCLUDE_TABLE, configOptionsTable, 0,
92 "Configuration file options", 0 },
97 static unsigned short buttonNameToBit(const char *s) {
98 if (strcasecmp(s, "A") == 0) {
100 } else if (strcasecmp(s, "B") == 0) {
102 } else if (strcasecmp(s, "X") == 0) {
104 } else if (strcasecmp(s, "Y") == 0) {
106 } else if (strcasecmp(s, "L") == 0) {
108 } else if (strcasecmp(s, "R") == 0) {
110 } else if (strcasecmp(s, "UP") == 0) {
112 } else if (strcasecmp(s, "DOWN") == 0) {
113 return SNES_DOWN_MASK;
114 } else if (strcasecmp(s, "LEFT") == 0) {
115 return SNES_LEFT_MASK;
116 } else if (strcasecmp(s, "RIGHT") == 0) {
117 return SNES_RIGHT_MASK;
118 } else if (strcasecmp(s, "START") == 0) {
119 return SNES_START_MASK;
120 } else if (strcasecmp(s, "SELECT") == 0) {
121 return SNES_SELECT_MASK;
123 DIE("Bad button name: %s\n", s);
127 static unsigned char actionNameToBit(const char *s) {
128 if (strcasecmp(s, "quit") == 0) {
130 } else if (strcasecmp(s, "fullscreen") == 0) {
131 return kActionToggleFullscreen;
132 } else if (strcasecmp(s, "quickload1") == 0) {
133 return kActionQuickLoad1;
134 } else if (strcasecmp(s, "quicksave1") == 0) {
135 return kActionQuickSave1;
136 } else if (strcasecmp(s, "quickload2") == 0) {
137 return kActionQuickLoad2;
138 } else if (strcasecmp(s, "quicksave2") == 0) {
139 return kActionQuickSave2;
141 DIE("Bad action name: %s\n", s);
145 const char * S9xGetFilename(FileTypes file)
147 static char filename[PATH_MAX + 1];
164 case FILE_SCREENSHOT:
175 snprintf(filename, PATH_MAX, "%s.%s", basePath, ext);
179 const char * S9xGetQuickSaveFilename(unsigned int slot)
181 static char filename[PATH_MAX + 1];
182 snprintf(filename, PATH_MAX, "%s.frz.%u.gz", basePath, slot);
186 static void loadDefaults()
188 ZeroMemory(&Settings, sizeof(Settings));
189 ZeroMemory(&Config, sizeof(Config));
194 Config.quitting = false;
195 Config.saver = false;
196 Config.enableAudio = true;
197 Config.fullscreen = false;
199 Config.hacksFile = 0;
200 Config.player1Enabled = false;
201 Config.player2Enabled = false;
202 Config.touchscreenInput = false;
203 Config.touchscreenShow = false;
205 Settings.JoystickEnabled = FALSE;
206 Settings.SoundPlaybackRate = 22050;
207 Settings.Stereo = TRUE;
208 Settings.SoundBufferSize = 512; // in samples
209 Settings.CyclesPercentage = 100;
210 Settings.DisableSoundEcho = FALSE;
211 Settings.APUEnabled = FALSE;
212 Settings.H_Max = SNES_CYCLES_PER_SCANLINE;
213 Settings.SkipFrames = AUTO_FRAMERATE;
214 Settings.Shutdown = Settings.ShutdownMaster = TRUE;
215 Settings.FrameTimePAL = 20; // in msecs
216 Settings.FrameTimeNTSC = 16;
217 Settings.FrameTime = Settings.FrameTimeNTSC;
218 Settings.DisableSampleCaching = FALSE;
219 Settings.DisableMasterVolume = FALSE;
220 Settings.Mouse = FALSE;
221 Settings.SuperScope = FALSE;
222 Settings.MultiPlayer5 = FALSE;
223 Settings.ControllerOption = SNES_JOYPAD;
225 Settings.ForceTransparency = FALSE;
226 Settings.Transparency = FALSE;
227 Settings.SixteenBit = TRUE;
229 Settings.SupportHiRes = FALSE;
230 Settings.NetPlay = FALSE;
231 Settings.ServerName [0] = 0;
232 Settings.AutoSaveDelay = 30;
233 Settings.ApplyCheats = FALSE;
234 Settings.TurboMode = FALSE;
235 Settings.TurboSkipFrames = 15;
237 Settings.ForcePAL = FALSE;
238 Settings.ForceNTSC = FALSE;
240 Settings.HacksEnabled = FALSE;
241 Settings.HacksFilter = FALSE;
243 Settings.HBlankStart = (256 * Settings.H_Max) / SNES_HCOUNTER_MAX;
245 Settings.AutoSaveDelay = 15*60; // Autosave each 15 minutes.
248 void S9xSetRomFile(const char * path)
255 romFile = strndup(path, PATH_MAX);
256 basePath = strdup(romFile);
258 // Truncate base path at the last '.' char
259 char * c = strrchr(basePath, '.');
261 if (strcasecmp(c, ".gz") == 0) {
262 // Ignore the .gz part when truncating
264 c = strrchr(basePath, '.');
274 static bool gotRomFile()
276 return romFile ? true : false;
279 static void loadConfig(poptContext optCon, const char * file)
283 const char ** newargv;
286 fp = fopen (file, "r");
288 fprintf(stderr, "Cannot open config file %s\n", file);
292 ret = poptConfigFileToString (fp, &out, 0);
294 DIE("Cannot parse config file %s. ret=%d\n", file, ret);
296 poptParseArgvString(out, &newargc, &newargv);
298 poptStuffArgs(optCon, newargv);
302 /* XXX: currently leaking newargv */
305 static void parseArgs(poptContext optCon)
308 unsigned char scancode = 0;
310 while ((rc = poptGetNextOpt(optCon)) > 0) {
314 Config.enableAudio = false;
317 Settings.DisplayFrameRate = TRUE;
320 Settings.SkipFrames = atoi(poptGetOptArg(optCon));
323 Config.fullscreen = true;
326 Settings.SixteenBit = TRUE;
327 Settings.Transparency = TRUE;
331 Config.scaler = strdup(poptGetOptArg(optCon));
334 Settings.ForcePAL = TRUE;
337 Settings.ForceNTSC = TRUE;
340 Settings.TurboMode = TRUE;
343 loadConfig(optCon, poptGetOptArg(optCon));
346 val = poptGetOptArg(optCon);
347 Settings.Mouse = TRUE;
348 if (!val || atoi(val) <= 1) {
349 // Enable mouse on first controller
350 Settings.ControllerOption = SNES_MOUSE_SWAPPED;
352 // Enable mouse on second controller
353 Settings.ControllerOption = SNES_MOUSE;
357 Settings.SuperScope = TRUE;
358 Settings.ControllerOption = SNES_SUPERSCOPE;
361 Config.snapshotLoad = true;
362 Config.snapshotSave = true;
365 Settings.SoundPlaybackRate = atoi(poptGetOptArg(optCon));
368 Settings.SoundBufferSize = atoi(poptGetOptArg(optCon));
371 Config.touchscreenInput = true;
374 Config.touchscreenInput = true;
375 Config.touchscreenShow = true;
378 Settings.HacksEnabled = TRUE;
379 Settings.HacksFilter = TRUE;
382 Settings.HacksEnabled = TRUE;
383 Settings.HacksFilter = FALSE;
389 scancode = atoi(poptGetOptArg(optCon));
392 Config.joypad1Mapping[scancode] |=
393 buttonNameToBit(poptGetOptArg(optCon));
394 Config.joypad1Enabled = true;
397 Config.joypad2Mapping[scancode] |=
398 buttonNameToBit(poptGetOptArg(optCon));
399 Config.joypad2Enabled = true;
402 Config.action[scancode] |=
403 actionNameToBit(poptGetOptArg(optCon));
406 free(Config.hacksFile);
407 Config.hacksFile = strdup(poptGetOptArg(optCon));
410 DIE("Invalid popt argument (this is a bug): %d", rc);
416 /* an error occurred during option processing */
417 fprintf(stderr, "%s: %s\n",
418 poptBadOption(optCon, 0),
423 /* if there's an extra unparsed arg it's our rom file */
424 const char * extra_arg = poptGetArg(optCon);
426 S9xSetRomFile(extra_arg);
429 void S9xLoadConfig(int argc, char ** argv)
431 poptContext optCon = poptGetContext("drnoksnes",
432 argc, const_cast<const char **>(argv), optionsTable, 0);
433 poptSetOtherOptionHelp(optCon, "<rom>");
438 // Read config file ~/.config/drnoksnes.conf
439 char defConfFile[PATH_MAX];
440 sprintf(defConfFile, "%s/%s", getenv("HOME"), ".config/drnoksnes.conf");
441 loadConfig(optCon, defConfFile);
443 // Command line parameters (including --conf args)
451 // User did not specify a ROM file in the command line
452 fprintf(stderr, "You need to specify a ROM, like this:\n");
453 poptPrintUsage(optCon, stdout, 0);
454 poptFreeContext(optCon);
459 poptFreeContext(optCon);
462 void S9xUnloadConfig()
472 if (Config.hacksFile) {
473 free(Config.hacksFile);
474 Config.hacksFile = 0;