|
|
1.1 root 1: /*
2: Hatari - options.c
3:
4: This file is distributed under the GNU Public License, version 2 or at
5: your option any later version. Read the file gpl.txt for details.
6:
7: Functions for showing and parsing all of Hatari's command line options.
8:
9: To add a new option:
10: - Add option ID to the enum
11: - Add the option information to HatariOptions[]
12: - Add required actions for that ID to switch in Opt_ParseParameters()
13: */
14: const char Options_fileid[] = "Hatari options.c : " __DATE__ " " __TIME__;
15:
16: #include <ctype.h>
17: #include <stdio.h>
18: #include <stdlib.h>
19: #include <string.h>
20: #include <assert.h>
21: #include <SDL.h>
22:
23: #include "main.h"
24: #include "options.h"
25: #include "configuration.h"
26: #include "control.h"
27: #include "debugui.h"
28: #include "file.h"
29: #include "screen.h"
30: #include "video.h"
31: #include "log.h"
32: #include "avi_record.h"
33:
34: #include "hatari-glue.h"
35:
36:
37: bool bLoadAutoSave; /* Load autosave memory snapshot at startup */
38: bool bLoadMemorySave; /* Load memory snapshot provided via option at startup */
39: bool bBiosIntercept; /* whether UAE should intercept Bios & XBios calls */
40: bool AviRecordOnStartup; /* Start avi recording at startup */
41:
42: static bool bNoSDLParachute;
43:
44: /* List of supported options. */
45: enum {
46: OPT_HEADER, /* options section header */
47: OPT_HELP, /* general options */
48: OPT_VERSION,
49: OPT_CONFIRMQUIT,
50: OPT_CONFIGFILE,
51: OPT_FASTFORWARD,
52: OPT_MONO, /* display options */
53: OPT_MONITOR,
54: OPT_FULLSCREEN,
55: OPT_WINDOW,
56: OPT_GRAB,
57: OPT_ZOOM,
58: OPT_MAXWIDTH,
59: OPT_MAXHEIGHT,
60: OPT_ASPECT,
61: OPT_BORDERS,
62: OPT_FRAMESKIPS,
63: OPT_STATUSBAR,
64: OPT_DRIVE_LED,
65: OPT_SPEC512,
66: OPT_FORCEBPP,
67: OPT_VDI, /* VDI options */
68: OPT_VDI_PLANES,
69: OPT_VDI_WIDTH,
70: OPT_VDI_HEIGHT,
71: OPT_AVIRECORD, /* record options */
72: OPT_AVIRECORD_VCODEC,
73: OPT_AVIRECORD_FPS,
74: OPT_AVIRECORD_CROP,
75: OPT_AVIRECORD_FILE,
76: OPT_JOYSTICK, /* device options */
77: OPT_JOYSTICK0,
78: OPT_JOYSTICK1,
79: OPT_JOYSTICK2,
80: OPT_JOYSTICK3,
81: OPT_JOYSTICK4,
82: OPT_JOYSTICK5,
83: OPT_PRINTER,
84: OPT_MIDI_IN,
85: OPT_MIDI_OUT,
86: OPT_RS232_IN,
87: OPT_RS232_OUT,
88: OPT_DISKA, /* disk options */
89: OPT_DISKB,
90: OPT_SLOWFLOPPY,
91: OPT_WRITEPROT_FLOPPY,
92: OPT_WRITEPROT_HD,
93: OPT_HARDDRIVE,
94: OPT_ACSIHDIMAGE,
95: OPT_IDEMASTERHDIMAGE,
96: OPT_IDESLAVEHDIMAGE,
97: OPT_MEMSIZE, /* memory options */
98: OPT_TOS,
99: OPT_CARTRIDGE,
100: OPT_MEMSTATE,
101: OPT_CPULEVEL, /* CPU options */
102: OPT_CPUCLOCK,
103: OPT_COMPATIBLE,
104: OPT_MACHINE, /* system options */
105: OPT_BLITTER,
106: OPT_TIMERD,
107: OPT_DSP,
108: OPT_SOUND,
109: OPT_SOUNDBUFFERSIZE,
110: OPT_KEYMAPFILE,
111: OPT_DEBUG, /* debug options */
112: OPT_BIOSINTERCEPT,
113: OPT_TRACE,
114: OPT_TRACEFILE,
115: OPT_PARSE,
116: OPT_SAVECONFIG,
117: OPT_PARACHUTE,
118: OPT_CONTROLSOCKET,
119: OPT_LOGFILE,
120: OPT_LOGLEVEL,
121: OPT_ALERTLEVEL,
122: OPT_RUNVBLS,
123: OPT_ERROR,
124: OPT_CONTINUE
125: };
126:
127: typedef struct {
128: unsigned int id; /* option ID */
129: const char *chr; /* short option */
130: const char *str; /* long option */
131: const char *arg; /* type name for argument, if any */
132: const char *desc; /* option description */
133: } opt_t;
134:
135: /* it's easier to edit these if they are kept in the same order as the enums */
136: static const opt_t HatariOptions[] = {
137:
138: { OPT_HEADER, NULL, NULL, NULL, "General" },
139: { OPT_HELP, "-h", "--help",
140: NULL, "Print this help text and exit" },
141: { OPT_VERSION, "-v", "--version",
142: NULL, "Print version number and exit" },
143: { OPT_CONFIRMQUIT, NULL, "--confirm-quit",
144: "<bool>", "Whether Hatari confirms quit" },
145: { OPT_CONFIGFILE, "-c", "--configfile",
146: "<file>", "Use <file> instead of the default hatari config file" },
147: { OPT_FASTFORWARD, NULL, "--fast-forward",
148: "<bool>", "Help skipping stuff on fast machine" },
149:
150: { OPT_HEADER, NULL, NULL, NULL, "Display" },
151: { OPT_MONO, "-m", "--mono",
152: NULL, "Start in monochrome mode instead of color" },
153: { OPT_MONITOR, NULL, "--monitor",
154: "<x>", "Select monitor type (x = mono/rgb/vga/tv)" },
155: { OPT_FULLSCREEN,"-f", "--fullscreen",
156: NULL, "Start emulator in fullscreen mode" },
157: { OPT_WINDOW, "-w", "--window",
158: NULL, "Start emulator in window mode" },
159: { OPT_GRAB, NULL, "--grab",
160: NULL, "Grab mouse (also) in window mode" },
161: { OPT_ZOOM, "-z", "--zoom",
162: "<x>", "Double small resolutions (1=no, 2=yes)" },
163: { OPT_MAXWIDTH, NULL, "--max-width",
164: "<x>", "Maximum window width for zooming (Falcon/TT only)" },
165: { OPT_MAXHEIGHT, NULL, "--max-height",
166: "<x>", "Maximum window height for zooming (Falcon/TT only)" },
167: { OPT_ASPECT, NULL, "--aspect",
168: "<bool>", "Monitor aspect ratio correction (Falcon/TT only)" },
169: { OPT_BORDERS, NULL, "--borders",
170: "<bool>", "Show ST/STE screen borders (for overscan demos etc)" },
171: { OPT_FRAMESKIPS, NULL, "--frameskips",
172: "<x>", "Skip <x> frames after each shown frame (0=off, >4=auto/max)" },
173: { OPT_STATUSBAR, NULL, "--statusbar",
174: "<bool>", "Show statusbar (floppy leds etc)" },
175: { OPT_DRIVE_LED, NULL, "--drive-led",
176: "<bool>", "Show overlay drive led when statusbar isn't shown" },
177: { OPT_SPEC512, NULL, "--spec512",
178: "<x>", "Spec512 palette threshold (0 <= x <= 512, 0=disable)" },
179: { OPT_FORCEBPP, NULL, "--bpp",
180: "<x>", "Force internal bitdepth (x = 8/15/16/32, 0=disable)" },
181:
182: { OPT_HEADER, NULL, NULL, NULL, "VDI" },
183: { OPT_VDI, NULL, "--vdi",
184: "<bool>", "Whether to use VDI screen mode" },
185: { OPT_VDI_PLANES,NULL, "--vdi-planes",
186: "<x>", "VDI mode bit-depth (x = 1/2/4)" },
187: { OPT_VDI_WIDTH, NULL, "--vdi-width",
188: "<w>", "VDI mode width (320 < w <= 1280)" },
189: { OPT_VDI_HEIGHT, NULL, "--vdi-height",
190: "<h>", "VDI mode height (200 < h <= 960)" },
191:
192: { OPT_HEADER, NULL, NULL, NULL, "Video recording" },
193: { OPT_AVIRECORD, NULL, "--avirecord",
194: NULL, "Start AVI recording" },
195: { OPT_AVIRECORD_VCODEC, NULL, "--avi-vcodec",
196: "<x>", "Select avi video codec (x = bmp/png)" },
197: { OPT_AVIRECORD_FPS, NULL, "--avi-fps",
198: "<x>", "Force avi frame rate (x = 50/60/71/...)" },
199: { OPT_AVIRECORD_CROP, NULL, "--avi-crop",
200: "<bool>", "Remove status bar from the recorded file" },
201: { OPT_AVIRECORD_FILE, NULL, "--avi-file",
202: "<file>", "Use <file> to record avi" },
203:
204: { OPT_HEADER, NULL, NULL, NULL, "Devices" },
205: { OPT_JOYSTICK, "-j", "--joystick",
206: "<port>", "Emulate joystick with cursor keys in given port (0-5)" },
207: /* these have to be exactly the same as I'm relying compiler giving
208: * them the same same string pointer when strings are identical
209: * (Opt_ShowHelpSection() skips successive options with same help
210: * pointer).
211: */
212: { OPT_JOYSTICK0, NULL, "--joy<port>",
213: "<type>", "Set joystick type (none/keys/real) for given port" },
214: { OPT_JOYSTICK1, NULL, "--joy<port>",
215: "<type>", "Set joystick type (none/keys/real) for given port" },
216: { OPT_JOYSTICK2, NULL, "--joy<port>",
217: "<type>", "Set joystick type (none/keys/real) for given port" },
218: { OPT_JOYSTICK3, NULL, "--joy<port>",
219: "<type>", "Set joystick type (none/keys/real) for given port" },
220: { OPT_JOYSTICK4, NULL, "--joy<port>",
221: "<type>", "Set joystick type (none/keys/real) for given port" },
222: { OPT_JOYSTICK5, NULL, "--joy<port>",
223: "<type>", "Set joystick type (none/keys/real) for given port" },
224: { OPT_PRINTER, NULL, "--printer",
225: "<file>", "Enable printer support and write data to <file>" },
226: { OPT_MIDI_IN, NULL, "--midi-in",
227: "<file>", "Enable MIDI and use <file> as the input device" },
228: { OPT_MIDI_OUT, NULL, "--midi-out",
229: "<file>", "Enable MIDI and use <file> as the output device" },
230: { OPT_RS232_IN, NULL, "--rs232-in",
231: "<file>", "Enable serial port and use <file> as the input device" },
232: { OPT_RS232_OUT, NULL, "--rs232-out",
233: "<file>", "Enable serial port and use <file> as the output device" },
234:
235: { OPT_HEADER, NULL, NULL, NULL, "Disk" },
236: { OPT_DISKA, NULL, "--disk-a",
237: "<file>", "Set disk image for floppy drive A" },
238: { OPT_DISKB, NULL, "--disk-b",
239: "<file>", "Set disk image for floppy drive B" },
240: { OPT_SLOWFLOPPY, NULL, "--slowfdc",
241: "<bool>", "Slow down floppy disk access emulation" },
242: { OPT_WRITEPROT_FLOPPY, NULL, "--protect-floppy",
243: "<x>", "Write protect floppy image contents (on/off/auto)" },
244: { OPT_WRITEPROT_HD, NULL, "--protect-hd",
245: "<x>", "Write protect harddrive <dir> contents (on/off/auto)" },
246: { OPT_HARDDRIVE, "-d", "--harddrive",
247: "<dir>", "Emulate harddrive partition(s) with <dir> contents" },
248: { OPT_ACSIHDIMAGE, NULL, "--acsi",
249: "<file>", "Emulate an ACSI harddrive with an image <file>" },
250: { OPT_IDEMASTERHDIMAGE, NULL, "--ide-master",
251: "<file>", "Emulate an IDE master harddrive with an image <file>" },
252: { OPT_IDESLAVEHDIMAGE, NULL, "--ide-slave",
253: "<file>", "Emulate an IDE slave harddrive with an image <file>" },
254:
255: { OPT_HEADER, NULL, NULL, NULL, "Memory" },
256: { OPT_MEMSIZE, "-s", "--memsize",
257: "<x>", "ST RAM size (x = size in MiB from 0 to 14, 0 = 512KiB)" },
258: { OPT_TOS, "-t", "--tos",
259: "<file>", "Use TOS image <file>" },
260: { OPT_CARTRIDGE, NULL, "--cartridge",
261: "<file>", "Use ROM cartridge image <file>" },
262: { OPT_MEMSTATE, NULL, "--memstate",
263: "<file>", "Load memory snap-shot <file>" },
264:
265: { OPT_HEADER, NULL, NULL, NULL, "CPU" },
266: { OPT_CPULEVEL, NULL, "--cpulevel",
267: "<x>", "Set the CPU type (x => 680x0) (EmuTOS/TOS 2.06 only!)" },
268: { OPT_CPUCLOCK, NULL, "--cpuclock",
269: "<x>", "Set the CPU clock (x = 8/16/32)" },
270: { OPT_COMPATIBLE, NULL, "--compatible",
271: "<bool>", "Use a more compatible (but slower) 68000 CPU mode" },
272:
273: { OPT_HEADER, NULL, NULL, NULL, "Misc system" },
274: { OPT_MACHINE, NULL, "--machine",
275: "<x>", "Select machine type (x = st/ste/tt/falcon)" },
276: { OPT_BLITTER, NULL, "--blitter",
277: "<bool>", "Use blitter emulation (ST only)" },
278: { OPT_TIMERD, NULL, "--timer-d",
279: "<bool>", "Patch Timer-D (about doubles Hatari speed)" },
280: { OPT_DSP, NULL, "--dsp",
281: "<x>", "DSP emulation (x = none/dummy/emu, Falcon only)" },
282: { OPT_SOUND, NULL, "--sound",
283: "<x>", "Sound frequency (x=off/6000-50066, off=fastest)" },
284: { OPT_SOUNDBUFFERSIZE, NULL, "--sound-buffer-size",
285: "<x>", "Sound buffer size for SDL in ms (x=0/10-100, 0=use default buffer size)" },
286: { OPT_KEYMAPFILE, "-k", "--keymap",
287: "<file>", "Read (additional) keyboard mappings from <file>" },
288:
289: { OPT_HEADER, NULL, NULL, NULL, "Debug" },
290: { OPT_DEBUG, "-D", "--debug",
291: NULL, "Toggle whether CPU exceptions invoke debugger" },
292: { OPT_BIOSINTERCEPT, NULL, "--bios-intercept",
293: NULL, "Enable Bios/XBios interception (experimental)" },
294: { OPT_TRACE, NULL, "--trace",
295: "<trace1,...>", "Activate emulation tracing, see '--trace help'" },
296: { OPT_TRACEFILE, NULL, "--trace-file",
297: "<file>", "Save trace output to <file> (default=stderr)" },
298: { OPT_PARSE, NULL, "--parse",
299: "<file>", "Parse/execute debugger commands from <file>" },
300: { OPT_SAVECONFIG, NULL, "--saveconfig",
301: NULL, "Save current Hatari configuration and exit" },
302: { OPT_PARACHUTE, NULL, "--no-parachute",
303: NULL, "Disable SDL parachute to get Hatari core dumps" },
304: #if HAVE_UNIX_DOMAIN_SOCKETS
305: { OPT_CONTROLSOCKET, NULL, "--control-socket",
306: "<file>", "Hatari reads options from given socket at run-time" },
307: #endif
308: { OPT_LOGFILE, NULL, "--log-file",
309: "<file>", "Save log output to <file> (default=stderr)" },
310: { OPT_LOGLEVEL, NULL, "--log-level",
311: "<x>", "Log output level (x=debug/todo/info/warn/error/fatal)" },
312: { OPT_ALERTLEVEL, NULL, "--alert-level",
313: "<x>", "Show dialog for log messages above given level" },
314: { OPT_RUNVBLS, NULL, "--run-vbls",
315: "<x>", "Exit after x VBLs" },
316:
317: { OPT_ERROR, NULL, NULL, NULL, NULL }
318: };
319:
320:
321: /**
322: * Show version string and license.
323: */
324: static void Opt_ShowVersion(void)
325: {
326: printf("\n" PROG_NAME
327: " - the Atari ST, STE, TT and Falcon emulator.\n\n");
328: printf("Hatari is free software licensed under the GNU General"
329: " Public License.\n\n");
330: }
331:
332:
333: /**
334: * Calculate option + value len
335: */
336: static unsigned int Opt_OptionLen(const opt_t *opt)
337: {
338: unsigned int len;
339: len = strlen(opt->str);
340: if (opt->arg)
341: {
342: len += strlen(opt->arg);
343: len += 1;
344: /* with arg, short options go to another line */
345: }
346: else
347: {
348: if (opt->chr)
349: {
350: /* ' or -c' */
351: len += 6;
352: }
353: }
354: return len;
355: }
356:
357:
358: /**
359: * Show single option
360: */
361: static void Opt_ShowOption(const opt_t *opt, unsigned int maxlen)
362: {
363: char buf[64];
364: if (!maxlen)
365: {
366: maxlen = Opt_OptionLen(opt);
367: }
368: assert(maxlen < sizeof(buf));
369: if (opt->arg)
370: {
371: sprintf(buf, "%s %s", opt->str, opt->arg);
372: printf(" %-*s %s\n", maxlen, buf, opt->desc);
373: if (opt->chr)
374: {
375: printf(" or %s %s\n", opt->chr, opt->arg);
376: }
377: }
378: else
379: {
380: if (opt->chr)
381: {
382: sprintf(buf, "%s or %s", opt->str, opt->chr);
383: printf(" %-*s %s\n", maxlen, buf, opt->desc);
384: }
385: else
386: {
387: printf(" %-*s %s\n", maxlen, opt->str, opt->desc);
388: }
389: }
390: }
391:
392: /**
393: * Show options for section starting from 'start_opt',
394: * return next option after this section.
395: */
396: static const opt_t *Opt_ShowHelpSection(const opt_t *start_opt)
397: {
398: const opt_t *opt, *last;
399: unsigned int len, maxlen = 0;
400: const char *previous = NULL;
401:
402: /* find longest option name and check option IDs */
403: for (opt = start_opt; opt->id != OPT_HEADER && opt->id != OPT_ERROR; opt++)
404: {
405: len = Opt_OptionLen(opt);
406: if (len > maxlen)
407: {
408: maxlen = len;
409: }
410: }
411: last = opt;
412:
413: /* output all options */
414: for (opt = start_opt; opt != last; opt++)
415: {
416: if (previous != opt->str)
417: {
418: Opt_ShowOption(opt, maxlen);
419: }
420: previous = opt->str;
421: }
422: return last;
423: }
424:
425:
426: /**
427: * Show help text.
428: */
429: static void Opt_ShowHelp(void)
430: {
431: const opt_t *opt = HatariOptions;
432:
433: Opt_ShowVersion();
434: printf("Usage:\n hatari [options] [disk image name]\n");
435:
436: while(opt->id != OPT_ERROR)
437: {
438: if (opt->id == OPT_HEADER)
439: {
440: assert(opt->desc);
441: printf("\n%s options:\n", opt->desc);
442: opt++;
443: }
444: opt = Opt_ShowHelpSection(opt);
445: }
446: printf("\nSpecial option values:\n");
447: printf("<bool>\tDisable by using 'n', 'no', 'off', 'false', or '0'\n");
448: printf("\tEnable by using 'y', 'yes', 'on', 'true' or '1'\n");
449: printf("<file>\tDevices accept also special 'stdout' and 'stderr' file names\n");
450: printf("\t(if you use stdout for midi or printer, set log to stderr).\n");
451: printf("\tSetting the file to 'none', disables given device or disk\n");
452: }
453:
454:
455: /**
456: * Show Hatari version and usage.
457: * If 'error' given, show that error message.
458: * If 'optid' != OPT_ERROR, tells for which option the error is,
459: * otherwise 'value' is show as the option user gave.
460: * Return false if error string was given, otherwise true
461: */
462: static bool Opt_ShowError(unsigned int optid, const char *value, const char *error)
463: {
464: const opt_t *opt;
465:
466: Opt_ShowVersion();
467: printf("Usage:\n hatari [options] [disk image name]\n\n"
468: "Try option \"-h\" or \"--help\" to display more information.\n");
469:
470: if (error)
471: {
472: if (optid == OPT_ERROR)
473: {
474: fprintf(stderr, "\nError: %s (%s)\n", error, value);
475: }
476: else
477: {
478: for (opt = HatariOptions; opt->id != OPT_ERROR; opt++)
479: {
480: if (optid == opt->id)
481: break;
482: }
483: if (value != NULL)
484: {
485: fprintf(stderr,
486: "\nError while parsing argument \"%s\" for option \"%s\":\n"
487: " %s\n", value, opt->str, error);
488: }
489: else
490: {
491: fprintf(stderr, "\nError (%s): %s\n", opt->str, error);
492: }
493: fprintf(stderr, "\nOption usage:\n");
494: Opt_ShowOption(opt, 0);
495: }
496: return false;
497: }
498: return true;
499: }
500:
501:
502: /**
503: * If 'conf' given, set it:
504: * - true if given option 'arg' is y/yes/on/true/1
505: * - false if given option 'arg' is n/no/off/false/0
506: * Return false for any other value, otherwise true
507: */
508: static bool Opt_Bool(const char *arg, int optid, bool *conf)
509: {
510: const char *enablers[] = { "y", "yes", "on", "true", "1", NULL };
511: const char *disablers[] = { "n", "no", "off", "false", "0", NULL };
512: const char **bool_str, *orig = arg;
513: char *input, *str;
514:
515: input = strdup(arg);
516: str = input;
517: while (*str)
518: {
519: *str++ = tolower(*arg++);
520: }
521: for (bool_str = enablers; *bool_str; bool_str++)
522: {
523: if (strcmp(input, *bool_str) == 0)
524: {
525: free(input);
526: if (conf)
527: {
528: *conf = true;
529: }
530: return true;
531: }
532: }
533: for (bool_str = disablers; *bool_str; bool_str++)
534: {
535: if (strcmp(input, *bool_str) == 0)
536: {
537: free(input);
538: if (conf)
539: {
540: *conf = false;
541: }
542: return true;
543: }
544: }
545: free(input);
546: return Opt_ShowError(optid, orig, "Not a <bool> value");
547: }
548:
549:
550: /**
551: * checks str argument agaist options of type "--option<digit>".
552: * If match is found, returns ID for that, otherwise OPT_CONTINUE
553: * and OPT_ERROR for errors.
554: */
555: static int Opt_CheckBracketValue(const opt_t *opt, const char *str)
556: {
557: const char *bracket, *optstr;
558: size_t offset;
559: int digit, i;
560:
561: if (!opt->str)
562: {
563: return OPT_CONTINUE;
564: }
565: bracket = strchr(opt->str, '<');
566: if (!bracket)
567: {
568: return OPT_CONTINUE;
569: }
570: offset = bracket - opt->str;
571: if (strncmp(opt->str, str, offset) != 0)
572: {
573: return OPT_CONTINUE;
574: }
575: digit = str[offset] - '0';
576: if (digit < 0 || digit > 9)
577: {
578: return OPT_CONTINUE;
579: }
580: optstr = opt->str;
581: for (i = 0; opt->str == optstr; opt++, i++)
582: {
583: if (i == digit)
584: {
585: return opt->id;
586: }
587: }
588: /* fprintf(stderr, "opt: %s (%d), str: %s (%d), digit: %d\n",
589: opt->str, offset+1, str, strlen(str), digit);
590: */
591: return OPT_ERROR;
592: }
593:
594:
595: /**
596: * matches string under given index in the argv against all Hatari
597: * short and long options. If match is found, returns ID for that,
598: * otherwise shows help and returns OPT_ERROR.
599: *
600: * Checks also that if option is supposed to have argument,
601: * whether there's one.
602: */
603: static int Opt_WhichOption(int argc, const char *argv[], int idx)
604: {
605: const opt_t *opt;
606: const char *str = argv[idx];
607: int id;
608:
609: for (opt = HatariOptions; opt->id != OPT_ERROR; opt++)
610: {
611: /* exact option name matches? */
612: if (!((opt->str && !strcmp(str, opt->str)) ||
613: (opt->chr && !strcmp(str, opt->chr))))
614: {
615: /* no, maybe name<digit> matches? */
616: id = Opt_CheckBracketValue(opt, str);
617: if (id == OPT_CONTINUE)
618: {
619: continue;
620: }
621: if (id == OPT_ERROR)
622: {
623: break;
624: }
625: }
626: /* matched, check args */
627: if (opt->arg)
628: {
629: if (idx+1 >= argc)
630: {
631: Opt_ShowError(opt->id, NULL, "Missing argument");
632: return OPT_ERROR;
633: }
634: /* early check for bools */
635: if (strcmp(opt->arg, "<bool>") == 0)
636: {
637: if (!Opt_Bool(argv[idx+1], opt->id, NULL))
638: {
639: return OPT_ERROR;
640: }
641: }
642: }
643: return opt->id;
644: }
645: Opt_ShowError(OPT_ERROR, argv[idx], "Unrecognized option");
646: return OPT_ERROR;
647: }
648:
649:
650: /**
651: * If 'checkexits' is true, assume 'src' is a file and check whether it
652: * exists before copying 'src' to 'dst'. Otherwise just copy option src
653: * string to dst.
654: * If a pointer to (bool) 'option' is given, set that option to true.
655: * - However, if src is "none", leave dst unmodified & set option to false.
656: * ("none" is used to disable options related to file arguments)
657: * Return false if there were errors, otherwise true
658: */
659: static bool Opt_StrCpy(int optid, bool checkexist, char *dst, const char *src, size_t dstlen, bool *option)
660: {
661: if (option)
662: {
663: *option = false;
664: if(strcasecmp(src, "none") == 0)
665: {
666: return true;
667: }
668: }
669: if (strlen(src) >= dstlen)
670: {
671: return Opt_ShowError(optid, src, "File name too long!");
672: }
673: if (checkexist && !File_Exists(src))
674: {
675: return Opt_ShowError(optid, src, "Given file doesn't exist (or has wrong file permissions)!");
676: }
677: if (option)
678: {
679: *option = true;
680: }
681: strcpy(dst, src);
682: return true;
683: }
684:
685:
686: /**
687: * Return SDL_INIT_NOPARACHUTE flag if user requested SDL parachute
688: * to be disabled to get proper Hatari core dumps. By default returns
689: * zero so that SDL parachute will be used to restore video mode on
690: * unclean Hatari termination.
691: */
692: Uint32 Opt_GetNoParachuteFlag(void)
693: {
694: if (bNoSDLParachute) {
695: return SDL_INIT_NOPARACHUTE;
696: }
697: return 0;
698: }
699:
700:
701: /**
702: * parse all Hatari command line options and set Hatari state accordingly.
703: * Returns true if everything was OK, false otherwise.
704: */
705: bool Opt_ParseParameters(int argc, const char *argv[])
706: {
707: int ncpu, skips, zoom, planes, cpuclock, threshold, memsize, port, freq, temp;
708: const char *errstr;
709: int i, ok = true;
710: int val;
711:
712: /* Defaults for loading initial memory snap-shots */
713: bLoadMemorySave = false;
714: bLoadAutoSave = ConfigureParams.Memory.bAutoSave;
715:
716: for(i = 1; i < argc; i++)
717: {
718: if (argv[i][0] != '-')
719: {
720: continue;
721: }
722:
723: /* WhichOption() checks also that there is an argument,
724: * so we don't need to check that below
725: */
726: switch(Opt_WhichOption(argc, argv, i))
727: {
728:
729: /* general options */
730: case OPT_HELP:
731: Opt_ShowHelp();
732: return false;
733:
734: case OPT_VERSION:
735: Opt_ShowVersion();
736: return false;
737:
738: case OPT_CONFIRMQUIT:
739: ok = Opt_Bool(argv[++i], OPT_CONFIRMQUIT, &ConfigureParams.Log.bConfirmQuit);
740: break;
741:
742: case OPT_FASTFORWARD:
743: ok = Opt_Bool(argv[++i], OPT_FASTFORWARD, &ConfigureParams.System.bFastForward);
744: break;
745:
746: case OPT_CONFIGFILE:
747: i += 1;
748: /* true -> file needs to exist */
749: ok = Opt_StrCpy(OPT_CONFIGFILE, true, sConfigFileName,
750: argv[i], sizeof(sConfigFileName), NULL);
751: if (ok)
752: {
753: Configuration_Load(NULL);
754: bLoadAutoSave = ConfigureParams.Memory.bAutoSave;
755: }
756: break;
757:
758: /* display options */
759: case OPT_MONO:
760: ConfigureParams.Screen.nMonitorType = MONITOR_TYPE_MONO;
761: bLoadAutoSave = false;
762: break;
763:
764: case OPT_MONITOR:
765: i += 1;
766: if (strcasecmp(argv[i], "mono") == 0)
767: {
768: ConfigureParams.Screen.nMonitorType = MONITOR_TYPE_MONO;
769: }
770: else if (strcasecmp(argv[i], "rgb") == 0)
771: {
772: ConfigureParams.Screen.nMonitorType = MONITOR_TYPE_RGB;
773: }
774: else if (strcasecmp(argv[i], "vga") == 0)
775: {
776: ConfigureParams.Screen.nMonitorType = MONITOR_TYPE_VGA;
777: }
778: else if (strcasecmp(argv[i], "tv") == 0)
779: {
780: ConfigureParams.Screen.nMonitorType = MONITOR_TYPE_TV;
781: }
782: else
783: {
784: return Opt_ShowError(OPT_MONITOR, argv[i], "Unknown monitor type");
785: }
786: bLoadAutoSave = false;
787: break;
788:
789: case OPT_FULLSCREEN:
790: ConfigureParams.Screen.bFullScreen = true;
791: break;
792:
793: case OPT_WINDOW:
794: ConfigureParams.Screen.bFullScreen = false;
795: break;
796:
797: case OPT_GRAB:
798: bGrabMouse = true;
799: break;
800:
801: case OPT_ZOOM:
802: zoom = atoi(argv[++i]);
803: if (zoom < 1)
804: {
805: return Opt_ShowError(OPT_ZOOM, argv[i], "Invalid zoom value");
806: }
807: if (zoom > 1)
808: {
809: ConfigureParams.Screen.nMaxWidth = 1024;
810: ConfigureParams.Screen.nMaxHeight = 768;
811: }
812: else
813: {
814: ConfigureParams.Screen.nMaxWidth = 1024;
815: ConfigureParams.Screen.nMaxHeight = 768;
816: }
817: break;
818:
819: case OPT_MAXWIDTH:
820: ConfigureParams.Screen.nMaxWidth = atoi(argv[++i]);
821: break;
822:
823: case OPT_MAXHEIGHT:
824: ConfigureParams.Screen.nMaxHeight = atoi(argv[++i]);
825: break;
826:
827: case OPT_ASPECT:
828: ok = Opt_Bool(argv[++i], OPT_ASPECT, &ConfigureParams.Screen.bAspectCorrect);
829: break;
830:
831: case OPT_FRAMESKIPS:
832: skips = atoi(argv[++i]);
833: if (skips < 0)
834: {
835: return Opt_ShowError(OPT_FRAMESKIPS, argv[i],
836: "Invalid frame skip value");
837: }
838: else if (skips > 8)
839: {
840: Log_Printf(LOG_WARN, "Extravagant frame skip value %d!\n", skips);
841: }
842: ConfigureParams.Screen.nFrameSkips = skips;
843: break;
844:
845: case OPT_BORDERS:
846: ok = Opt_Bool(argv[++i], OPT_BORDERS, &ConfigureParams.Screen.bAllowOverscan);
847: break;
848:
849: case OPT_STATUSBAR:
850: ok = Opt_Bool(argv[++i], OPT_STATUSBAR, &ConfigureParams.Screen.bShowStatusbar);
851: break;
852:
853: case OPT_DRIVE_LED:
854: ok = Opt_Bool(argv[++i], OPT_DRIVE_LED, &ConfigureParams.Screen.bShowDriveLed);
855: break;
856:
857: case OPT_SPEC512:
858: threshold = atoi(argv[++i]);
859: if (threshold < 0 || threshold > 512)
860: {
861: return Opt_ShowError(OPT_SPEC512, argv[i],
862: "Invalid palette writes per line threshold for Spec512");
863: }
864: ConfigureParams.Screen.nSpec512Threshold = threshold;
865: break;
866:
867: case OPT_FORCEBPP:
868: planes = atoi(argv[++i]);
869: switch(planes)
870: {
871: case 32:
872: case 16:
873: case 15:
874: case 8:
875: break; /* supported */
876: case 24:
877: planes = 32; /* We do not support 24 bpp (yet) */
878: break;
879: default:
880: return Opt_ShowError(OPT_FORCEBPP, argv[i], "Invalid bit depth");
881: }
882: ConfigureParams.Screen.nForceBpp = planes;
883: break;
884:
885: /* avi recording options */
886: case OPT_AVIRECORD:
887: AviRecordOnStartup = true;
888: break;
889:
890: case OPT_AVIRECORD_VCODEC:
891: i += 1;
892: if (strcasecmp(argv[i], "bmp") == 0)
893: {
894: // AviRecordDefaultVcodec = AVI_RECORD_VIDEO_CODEC_BMP;
895: }
896: else if (strcasecmp(argv[i], "png") == 0)
897: {
898: // AviRecordDefaultVcodec = AVI_RECORD_VIDEO_CODEC_PNG;
899: }
900: else
901: {
902: return Opt_ShowError(OPT_AVIRECORD_VCODEC, argv[i], "Unknown video codec");
903: }
904: break;
905:
906: case OPT_AVIRECORD_FPS:
907: val = atoi(argv[++i]);
908: if (val < 0 || val > 100)
909: {
910: return Opt_ShowError(OPT_AVIRECORD_FPS, argv[i],
911: "Invalid frame rate for avi recording");
912: }
913: // AviRecordDefaultFps = val;
914: break;
915:
916: case OPT_AVIRECORD_CROP:
917: // ok = Opt_Bool(argv[++i], OPT_AVIRECORD_CROP, &AviRecordDefaultCrop);
918: break;
919:
920: case OPT_AVIRECORD_FILE:
921: i += 1;
922: /* false -> file is created if it doesn't exist */
923: // ok = Opt_StrCpy(OPT_AVIRECORD_FILE, false, AviRecordFile,
924: // argv[i], sizeof(AviRecordFile), NULL);
925: break;
926:
927:
928: /* Memory options */
929: case OPT_MEMSIZE:
930: memsize = atoi(argv[++i]);
931: if (memsize < 0 || memsize > 14)
932: {
933: return Opt_ShowError(OPT_MEMSIZE, argv[i], "Invalid memory size");
934: }
935: ConfigureParams.Memory.nMemorySize = memsize;
936: bLoadAutoSave = false;
937: break;
938:
939: case OPT_TOS:
940: i += 1;
941: ok = Opt_StrCpy(OPT_TOS, true, ConfigureParams.Rom.szTosImageFileName,
942: argv[i], sizeof(ConfigureParams.Rom.szTosImageFileName),
943: NULL);
944: if (ok)
945: {
946: bLoadAutoSave = false;
947: }
948: break;
949:
950: case OPT_CARTRIDGE:
951: i += 1;
952: ok = Opt_StrCpy(OPT_CARTRIDGE, true, ConfigureParams.Rom.szCartridgeImageFileName,
953: argv[i], sizeof(ConfigureParams.Rom.szCartridgeImageFileName),
954: NULL);
955: if (ok)
956: {
957: bLoadAutoSave = false;
958: }
959: break;
960:
961: case OPT_MEMSTATE:
962: i += 1;
963: ok = Opt_StrCpy(OPT_MEMSTATE, true, ConfigureParams.Memory.szMemoryCaptureFileName,
964: argv[i], sizeof(ConfigureParams.Memory.szMemoryCaptureFileName),
965: NULL);
966: if (ok)
967: {
968: bLoadMemorySave = true;
969: bLoadAutoSave = false;
970: }
971: break;
972:
973: /* CPU options */
974: case OPT_CPULEVEL:
975: /* UAE core uses cpu_level variable */
976: ncpu = atoi(argv[++i]);
977: if(ncpu < 0 || ncpu > 4)
978: {
979: return Opt_ShowError(OPT_CPULEVEL, argv[i], "Invalid CPU level");
980: }
981: ConfigureParams.System.nCpuLevel = ncpu;
982: bLoadAutoSave = false;
983: break;
984:
985: case OPT_CPUCLOCK:
986: cpuclock = atoi(argv[++i]);
987: if(cpuclock != 8 && cpuclock != 16 && cpuclock != 32)
988: {
989: return Opt_ShowError(OPT_CPUCLOCK, argv[i], "Invalid CPU clock");
990: }
991: ConfigureParams.System.nCpuFreq = cpuclock;
992: bLoadAutoSave = false;
993: break;
994:
995: case OPT_COMPATIBLE:
996: ok = Opt_Bool(argv[++i], OPT_COMPATIBLE, &ConfigureParams.System.bCompatibleCpu);
997: if (ok)
998: {
999: bLoadAutoSave = false;
1000: }
1001: break;
1002:
1003: /* system options */
1004: case OPT_MACHINE:
1005: i += 1;
1006: if (strcasecmp(argv[i], "st") == 0)
1007: {
1008: ConfigureParams.System.nMachineType = MACHINE_ST;
1009: ConfigureParams.System.nCpuLevel = 0;
1010: ConfigureParams.System.nCpuFreq = 8;
1011: }
1012: else if (strcasecmp(argv[i], "ste") == 0)
1013: {
1014: ConfigureParams.System.nMachineType = MACHINE_STE;
1015: ConfigureParams.System.nCpuLevel = 0;
1016: ConfigureParams.System.nCpuFreq = 8;
1017: }
1018: else if (strcasecmp(argv[i], "tt") == 0)
1019: {
1020: ConfigureParams.System.nMachineType = MACHINE_TT;
1021: ConfigureParams.System.nCpuLevel = 3;
1022: ConfigureParams.System.nCpuFreq = 32;
1023: }
1024: else if (strcasecmp(argv[i], "falcon") == 0)
1025: {
1026: ConfigureParams.System.nMachineType = MACHINE_FALCON;
1027: ConfigureParams.System.nCpuLevel = 3;
1028: ConfigureParams.System.nCpuFreq = 16;
1029: }
1030: else
1031: {
1032: return Opt_ShowError(OPT_MACHINE, argv[i], "Unknown machine type");
1033: }
1034: bLoadAutoSave = false;
1035: break;
1036:
1037:
1038: case OPT_TIMERD:
1039: ok = Opt_Bool(argv[++i], OPT_TIMERD, &ConfigureParams.System.bPatchTimerD);
1040: break;
1041:
1042:
1043: case OPT_SOUNDBUFFERSIZE:
1044: i += 1;
1045: temp = atoi(argv[i]);
1046: if ( temp == 0 ) /* use default setting for SDL */
1047: ;
1048: else if (temp < 10 || temp > 100)
1049: {
1050: return Opt_ShowError(OPT_SOUNDBUFFERSIZE, argv[i], "Unsupported sound buffer size");
1051: }
1052: ConfigureParams.Sound.SdlAudioBufferSize = temp;
1053: break;
1054:
1055: case OPT_KEYMAPFILE:
1056: i += 1;
1057: ok = Opt_StrCpy(OPT_KEYMAPFILE, true, ConfigureParams.Keyboard.szMappingFileName,
1058: argv[i], sizeof(ConfigureParams.Keyboard.szMappingFileName),
1059: NULL);
1060: if (ok)
1061: {
1062: ConfigureParams.Keyboard.nKeymapType = KEYMAP_LOADED;
1063: }
1064: break;
1065:
1066: /* debug options */
1067: case OPT_DEBUG:
1068: if (bExceptionDebugging)
1069: {
1070: fprintf(stderr, "Exception debugging disabled.\n");
1071: bExceptionDebugging = false;
1072: }
1073: else
1074: {
1075: fprintf(stderr, "Exception debugging enabled.\n");
1076: bExceptionDebugging = true;
1077: }
1078: break;
1079:
1080: case OPT_PARACHUTE:
1081: bNoSDLParachute = true;
1082: break;
1083:
1084:
1085: case OPT_TRACE:
1086: i += 1;
1087: errstr = Log_SetTraceOptions(argv[i]);
1088: if (errstr)
1089: {
1090: if (!errstr[0]) {
1091: /* silent parsing termination */
1092: return false;
1093: }
1094: return Opt_ShowError(OPT_TRACE, argv[i], errstr);
1095: }
1096: break;
1097:
1098: case OPT_TRACEFILE:
1099: i += 1;
1100: ok = Opt_StrCpy(OPT_TRACEFILE, false, ConfigureParams.Log.sTraceFileName,
1101: argv[i], sizeof(ConfigureParams.Log.sTraceFileName),
1102: NULL);
1103: break;
1104:
1105: case OPT_CONTROLSOCKET:
1106: i += 1;
1107: errstr = Control_SetSocket(argv[i]);
1108: if (errstr)
1109: {
1110: return Opt_ShowError(OPT_CONTROLSOCKET, argv[i], errstr);
1111: }
1112: break;
1113:
1114: case OPT_LOGFILE:
1115: i += 1;
1116: ok = Opt_StrCpy(OPT_LOGFILE, false, ConfigureParams.Log.sLogFileName,
1117: argv[i], sizeof(ConfigureParams.Log.sLogFileName),
1118: NULL);
1119: break;
1120:
1121: case OPT_PARSE:
1122: i += 1;
1123: ok = DebugUI_SetParseFile(argv[i]);
1124: break;
1125:
1126: case OPT_SAVECONFIG:
1127: /* Hatari-UI needs Hatari config to start */
1128: Configuration_Save();
1129: exit(0);
1130: break;
1131:
1132: case OPT_LOGLEVEL:
1133: i += 1;
1134: ConfigureParams.Log.nTextLogLevel = Log_ParseOptions(argv[i]);
1135: if (ConfigureParams.Log.nTextLogLevel == LOG_NONE)
1136: {
1137: return Opt_ShowError(OPT_LOGLEVEL, argv[i], "Unknown log level!");
1138: }
1139: break;
1140:
1141: case OPT_ALERTLEVEL:
1142: i += 1;
1143: ConfigureParams.Log.nAlertDlgLogLevel = Log_ParseOptions(argv[i]);
1144: if (ConfigureParams.Log.nAlertDlgLogLevel == LOG_NONE)
1145: {
1146: return Opt_ShowError(OPT_ALERTLEVEL, argv[i], "Unknown alert level!");
1147: }
1148: break;
1149:
1150: case OPT_RUNVBLS:
1151: nRunVBLs = atol(argv[++i]);
1152: break;
1153:
1154: case OPT_ERROR:
1155: /* unknown option or missing option parameter */
1156: return false;
1157:
1158: default:
1159: return Opt_ShowError(OPT_ERROR, argv[i], "Internal Hatari error, unhandled option");
1160: }
1161: if (!ok)
1162: {
1163: /* Opt_Bool() or Opt_StrCpy() failed */
1164: return false;
1165: }
1166: }
1167:
1168: return true;
1169: }
1170:
1171: /**
1172: * Readline match callback for option name completion.
1173: * STATE = 0 -> different text from previous one.
1174: * Return next match or NULL if no matches.
1175: */
1176: char *Opt_MatchOption(const char *text, int state)
1177: {
1178: static int i, len;
1179: const char *name;
1180:
1181: if (!state) {
1182: /* first match */
1183: len = strlen(text);
1184: i = 0;
1185: }
1186: /* next match */
1187: while (i < ARRAYSIZE(HatariOptions)) {
1188: name = HatariOptions[i++].str;
1189: if (name && strncasecmp(name, text, len) == 0)
1190: return (strdup(name));
1191: }
1192: return NULL;
1193: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.