12 #define DIE(format, ...) do { \
13 fprintf(stderr, "Died at %s:%d: ", __FILE__, __LINE__ ); \
14 fprintf(stderr, format "\n", ## __VA_ARGS__); \
20 /** Path to current rom file, with extension. */
21 static char * romFile;
22 /** Path to rom file, without extension.
23 * Used as a simple optimization to S9xGetFilename
25 static char * basePath;
27 static struct poptOption commonOptionsTable[] = {
28 { "disable-audio", 'a', POPT_ARG_NONE, 0, 1,
29 "disable emulation and output of audio", 0 },
30 { "display-framerate", 'r', POPT_ARG_NONE, 0, 2,
31 "Show frames per second counter in lower left corner", 0 },
32 { "skip-frames", 's', POPT_ARG_INT, 0, 3,
33 "Render only 1 in every N frames", "NUM" },
34 { "fullscreen", 'f', POPT_ARG_NONE, 0, 4,
35 "Start in fullscreen mode", 0 },
36 { "transparency", 'y', POPT_ARG_NONE, 0, 5,
37 "Enable transparency effects (slower)", 0 },
38 { "hacks", 'h', POPT_ARG_STRING | POPT_ARGFLAG_OPTIONAL, 0, 6,
39 "Enable hacks (yes, speed-only, no)", "option" },
40 { "pal", 'p', POPT_ARG_NONE, 0, 7,
41 "Run in PAL mode", 0 },
42 { "ntsc", 'n', POPT_ARG_NONE, 0, 8,
43 "Run in NTSC mode", 0 },
44 { "turbo", 't', POPT_ARG_NONE, 0, 9,
45 "Turbo mode (do not try to sleep between frames)", 0 },
46 { "conf", 'c', POPT_ARG_STRING, 0, 10,
47 "Extra configuration file to load", "FILE" },
48 { "mouse", 'm', POPT_ARG_INT | POPT_ARGFLAG_OPTIONAL, 0, 11,
49 "Enable mouse on controller NUM", "NUM"},
50 { "superscope", 'e', POPT_ARG_NONE, 0, 12,
51 "Enable SuperScope", 0},
52 { "snapshot", 'o', POPT_ARG_NONE, 0, 13,
53 "Unfreeze previous game on start and freeze game on exit", 0 },
54 { "audio-rate", 'u', POPT_ARG_INT, 0, 14,
55 "Audio output rate", "HZ" },
56 { "audio-buffer-size", 'b', POPT_ARG_INT, 0, 15,
57 "Audio output buffer size", "SAMPLES" },
61 static struct poptOption configOptionsTable[] = {
62 { "scancode", '\0', POPT_ARG_INT, 0, 100,
63 "Scancode to map", "CODE" },
64 { "button", '\0', POPT_ARG_STRING, 0, 101,
65 "SNES Button to press (A, B, X, Y, L, R, Up, Down, Left, Right)", "name" },
66 { "action", '\0', POPT_ARG_STRING, 0, 102,
67 "Emulator action to do (fullscreen, quit, ...)", "action" },
68 { "hacks-file", '\0', POPT_ARG_STRING, 0, 200,
69 "Path to snesadvance.dat file", "FILE" },
73 static struct poptOption optionsTable[] = {
74 { 0, '\0', POPT_ARG_INCLUDE_TABLE, commonOptionsTable, 0,
75 "Common options", 0 },
76 { 0, '\0', POPT_ARG_INCLUDE_TABLE, configOptionsTable, 0,
77 "Configuration file options", 0 },
82 static unsigned short buttonNameToBit(const char *s) {
83 if (strcasecmp(s, "A") == 0) {
85 } else if (strcasecmp(s, "B") == 0) {
87 } else if (strcasecmp(s, "X") == 0) {
89 } else if (strcasecmp(s, "Y") == 0) {
91 } else if (strcasecmp(s, "L") == 0) {
93 } else if (strcasecmp(s, "R") == 0) {
95 } else if (strcasecmp(s, "UP") == 0) {
97 } else if (strcasecmp(s, "DOWN") == 0) {
98 return SNES_DOWN_MASK;
99 } else if (strcasecmp(s, "LEFT") == 0) {
100 return SNES_LEFT_MASK;
101 } else if (strcasecmp(s, "RIGHT") == 0) {
102 return SNES_RIGHT_MASK;
103 } else if (strcasecmp(s, "START") == 0) {
104 return SNES_START_MASK;
105 } else if (strcasecmp(s, "SELECT") == 0) {
106 return SNES_SELECT_MASK;
108 DIE("Bad button name: %s\n", s);
112 static unsigned char actionNameToBit(const char *s) {
113 if (strcasecmp(s, "quit") == 0) {
115 } else if (strcasecmp(s, "fullscreen") == 0) {
116 return kActionToggleFullscreen;
118 DIE("Bad action name: %s\n", s);
122 const char * S9xGetFilename(FileTypes file)
124 static char filename [PATH_MAX + 1];
141 case FILE_SCREENSHOT:
152 snprintf(filename, PATH_MAX, "%s.%s", basePath, ext);
156 static void loadDefaults()
158 ZeroMemory(&Settings, sizeof(Settings));
159 ZeroMemory(&Config, sizeof(Config));
164 Config.quitting = false;
165 Config.enableAudio = true;
166 Config.fullscreen = false;
168 Config.hacksFile = 0;
170 Settings.JoystickEnabled = FALSE;
171 Settings.SoundPlaybackRate = 22050;
172 Settings.Stereo = TRUE;
173 Settings.SoundBufferSize = 512; // in samples
174 Settings.CyclesPercentage = 100;
175 Settings.DisableSoundEcho = FALSE;
176 Settings.APUEnabled = FALSE;
177 Settings.H_Max = SNES_CYCLES_PER_SCANLINE;
178 Settings.SkipFrames = AUTO_FRAMERATE;
179 Settings.Shutdown = Settings.ShutdownMaster = TRUE;
180 Settings.FrameTimePAL = 20; // in msecs
181 Settings.FrameTimeNTSC = 16;
182 Settings.FrameTime = Settings.FrameTimeNTSC;
183 Settings.DisableSampleCaching = FALSE;
184 Settings.DisableMasterVolume = FALSE;
185 Settings.Mouse = FALSE;
186 Settings.SuperScope = FALSE;
187 Settings.MultiPlayer5 = FALSE;
188 Settings.ControllerOption = SNES_JOYPAD;
190 Settings.ForceTransparency = FALSE;
191 Settings.Transparency = FALSE;
192 Settings.SixteenBit = TRUE;
194 Settings.SupportHiRes = FALSE;
195 Settings.NetPlay = FALSE;
196 Settings.ServerName [0] = 0;
197 Settings.AutoSaveDelay = 30;
198 Settings.ApplyCheats = FALSE;
199 Settings.TurboMode = FALSE;
200 Settings.TurboSkipFrames = 15;
202 Settings.ForcePAL = FALSE;
203 Settings.ForceNTSC = FALSE;
205 Settings.HacksEnabled = FALSE;
206 Settings.HacksFilter = FALSE;
208 Settings.HBlankStart = (256 * Settings.H_Max) / SNES_HCOUNTER_MAX;
210 Settings.AutoSaveDelay = 15*60; // Autosave each 15 minutes.
213 void S9xSetRomFile(const char * path)
220 romFile = strndup(path, PATH_MAX);
221 basePath = strdup(romFile);
223 // Truncate base path at the last '.' char
224 char * c = strrchr(basePath, '.');
230 static bool gotRomFile()
232 return romFile ? true : false;
235 static void setHacks(const char * value)
237 // Unconditionally enable hacks even if no argument passed
238 Settings.HacksEnabled = TRUE;
242 if (strcasecmp(value, "speed-only") == 0 ||
243 strcasecmp(value, "speed") == 0 ||
244 strcasecmp(value, "s") == 0) {
245 Settings.HacksFilter = TRUE;
246 } else if (strcasecmp(value, "yes") == 0 ||
247 strcasecmp(value, "y") == 0) {
249 } else if (strcasecmp(value, "no") == 0 ||
250 strcasecmp(value, "n") == 0) {
251 Settings.HacksEnabled = FALSE;
253 // Hack: the user probably wants to enable hacks
254 // and use this argument as the ROM file.
255 // Wonder why popt does not support this or if there's a better way.
256 S9xSetRomFile(value);
260 static void loadConfig(poptContext optCon, const char * file)
264 const char ** newargv;
267 fp = fopen (file, "r");
269 fprintf(stderr, "Cannot open config file %s\n", file);
273 ret = poptConfigFileToString (fp, &out, 0);
275 DIE("Cannot parse config file %s. ret=%d\n", file, ret);
277 poptParseArgvString(out, &newargc, &newargv);
279 poptStuffArgs(optCon, newargv);
283 /* XXX: currently leaking newargv */
286 static void parseArgs(poptContext optCon)
289 unsigned char scancode = 0;
291 while ((rc = poptGetNextOpt(optCon)) > 0) {
295 Config.enableAudio = false;
298 Settings.DisplayFrameRate = TRUE;
301 Settings.SkipFrames = atoi(poptGetOptArg(optCon));
304 Config.fullscreen = true;
307 Settings.SixteenBit = TRUE;
308 Settings.Transparency = TRUE;
311 Settings.HacksEnabled = TRUE;
312 setHacks(poptGetOptArg(optCon));
315 Settings.ForcePAL = TRUE;
318 Settings.ForceNTSC = TRUE;
321 Settings.TurboMode = TRUE;
324 loadConfig(optCon, poptGetOptArg(optCon));
327 val = poptGetOptArg(optCon);
328 Settings.Mouse = TRUE;
329 if (!val || atoi(val) <= 1) {
330 // Enable mouse on first controller
331 Settings.ControllerOption = SNES_MOUSE_SWAPPED;
333 // Enable mouse on second controller
334 Settings.ControllerOption = SNES_MOUSE;
338 Settings.SuperScope = TRUE;
339 Settings.ControllerOption = SNES_SUPERSCOPE;
342 Config.snapshotLoad = true;
343 Config.snapshotSave = true;
346 Settings.SoundPlaybackRate = atoi(poptGetOptArg(optCon));
349 Settings.SoundBufferSize = atoi(poptGetOptArg(optCon));
352 scancode = atoi(poptGetOptArg(optCon));
355 Config.joypad1Mapping[scancode] |=
356 buttonNameToBit(poptGetOptArg(optCon));
359 Config.action[scancode] |=
360 actionNameToBit(poptGetOptArg(optCon));
363 free(Config.hacksFile);
364 Config.hacksFile = strdup(poptGetOptArg(optCon));
367 DIE("Invalid popt argument (this is a bug): %d", rc);
373 /* an error occurred during option processing */
374 fprintf(stderr, "%s: %s\n",
375 poptBadOption(optCon, 0),
380 /* if there's an extra unparsed arg it's our rom file */
381 const char * extra_arg = poptGetArg(optCon);
383 S9xSetRomFile(extra_arg);
386 void S9xLoadConfig(int argc, const char ** argv)
389 poptGetContext("drnoksnes", argc, argv, optionsTable, 0);
390 poptSetOtherOptionHelp(optCon, "<rom>");
395 // Read config file ~/apps/DrNokSnes.txt
396 char defConfFile[PATH_MAX];
397 sprintf(defConfFile, "%s/%s", getenv("HOME"), "apps/DrNokSnes.txt");
398 loadConfig(optCon, defConfFile);
400 // Command line parameters (including --conf args)
403 if (!gotRomFile() && !hgwLaunched) {
404 // User did not specify a ROM file,
405 // and we're not being launched from D-Bus.
406 fprintf(stderr, "You need to specify a ROM, like this:\n");
407 poptPrintUsage(optCon, stdout, 0);
408 poptFreeContext(optCon);
412 poptFreeContext(optCon);
415 void S9xUnloadConfig()
425 if (Config.hacksFile) {
426 free(Config.hacksFile);
427 Config.hacksFile = 0;