all repos — mgba @ f8fb86ef7986d6935234589972a792349c3c1d71

mGBA Game Boy Advance Emulator

src/feature/commandline.c (view raw)

  1/* Copyright (c) 2013-2015 Jeffrey Pfau
  2 *
  3 * This Source Code Form is subject to the terms of the Mozilla Public
  4 * License, v. 2.0. If a copy of the MPL was not distributed with this
  5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  6#include <mgba/feature/commandline.h>
  7
  8#include <mgba/core/config.h>
  9#include <mgba/core/version.h>
 10#include <mgba-util/string.h>
 11
 12#include <fcntl.h>
 13#ifdef _MSC_VER
 14#include <mgba-util/platform/windows/getopt.h>
 15#else
 16#include <getopt.h>
 17#endif
 18
 19#define GRAPHICS_OPTIONS "123456f"
 20#define GRAPHICS_USAGE \
 21	"\nGraphics options:\n" \
 22	"  -1               1x viewport\n" \
 23	"  -2               2x viewport\n" \
 24	"  -3               3x viewport\n" \
 25	"  -4               4x viewport\n" \
 26	"  -5               5x viewport\n" \
 27	"  -6               6x viewport\n" \
 28	"  -f               Start full-screen"
 29
 30static const struct option _options[] = {
 31	{ "bios",      required_argument, 0, 'b' },
 32	{ "cheats",    required_argument, 0, 'c' },
 33	{ "frameskip", required_argument, 0, 's' },
 34#ifdef USE_EDITLINE
 35	{ "debug",     no_argument, 0, 'd' },
 36#endif
 37#ifdef USE_GDB_STUB
 38	{ "gdb",       no_argument, 0, 'g' },
 39#endif
 40	{ "help",      no_argument, 0, 'h' },
 41	{ "log-level", required_argument, 0, 'l' },
 42	{ "savestate", required_argument, 0, 't' },
 43	{ "patch",     required_argument, 0, 'p' },
 44	{ "version",   no_argument, 0, '\0' },
 45	{ 0, 0, 0, 0 }
 46};
 47
 48static bool _parseGraphicsArg(struct mSubParser* parser, int option, const char* arg);
 49static void _applyGraphicsArgs(struct mSubParser* parser, struct mCoreConfig* config);
 50
 51static void _tableInsert(struct Table* table, const char* pair) {
 52	char* eq = strchr(pair, '=');
 53	if (eq) {
 54		char option[128] = "";
 55		strncpy(option, pair, eq - pair);
 56		option[sizeof(option) - 1] = '\0';
 57		HashTableInsert(table, option, strdup(&eq[1]));
 58	} else {
 59		HashTableInsert(table, pair, strdup("1"));
 60	}
 61}
 62
 63static void _tableApply(const char* key, void* value, void* user) {
 64	struct mCoreConfig* config = user;
 65	mCoreConfigSetOverrideValue(config, key, value);
 66}
 67
 68bool parseArguments(struct mArguments* args, int argc, char* const* argv, struct mSubParser* subparser) {
 69	int ch;
 70	char options[64] =
 71		"b:c:C:hl:p:s:t:"
 72#ifdef USE_EDITLINE
 73		"d"
 74#endif
 75#ifdef USE_GDB_STUB
 76		"g"
 77#endif
 78	;
 79	memset(args, 0, sizeof(*args));
 80	args->frameskip = -1;
 81	args->logLevel = INT_MIN;
 82	HashTableInit(&args->configOverrides, 0, free);
 83	if (subparser && subparser->extraOptions) {
 84		// TODO: modularize options to subparsers
 85		strncat(options, subparser->extraOptions, sizeof(options) - strlen(options) - 1);
 86	}
 87	int index = 0;
 88	while ((ch = getopt_long(argc, argv, options, _options, &index)) != -1) {
 89		const struct option* opt = &_options[index];
 90		switch (ch) {
 91		case '\0':
 92			if (strcmp(opt->name, "version") == 0) {
 93				args->showVersion = true;
 94			} else {
 95				return false;
 96			}
 97			break;
 98		case 'b':
 99			args->bios = strdup(optarg);
100			break;
101		case 'c':
102			args->cheatsFile = strdup(optarg);
103			break;
104		case 'C':
105			_tableInsert(&args->configOverrides, optarg);
106			break;
107#ifdef USE_EDITLINE
108		case 'd':
109			if (args->debuggerType != DEBUGGER_NONE) {
110				return false;
111			}
112			args->debuggerType = DEBUGGER_CLI;
113			break;
114#endif
115#ifdef USE_GDB_STUB
116		case 'g':
117			if (args->debuggerType != DEBUGGER_NONE) {
118				return false;
119			}
120			args->debuggerType = DEBUGGER_GDB;
121			break;
122#endif
123		case 'h':
124			args->showHelp = true;
125			break;
126		case 'l':
127			args->logLevel = atoi(optarg);
128			break;
129		case 'p':
130			args->patch = strdup(optarg);
131			break;
132		case 's':
133			args->frameskip = atoi(optarg);
134			break;
135		case 't':
136			args->savestate = strdup(optarg);
137			break;
138		default:
139			if (subparser) {
140				if (!subparser->parse(subparser, ch, optarg)) {
141					return false;
142				}
143			}
144			break;
145		}
146	}
147	argc -= optind;
148	argv += optind;
149	if (argc > 1) {
150		return false;
151	} else if (argc == 1) {
152		args->fname = strdup(argv[0]);
153	} else {
154		args->fname = NULL;
155	}
156	return true;
157}
158
159void applyArguments(const struct mArguments* args, struct mSubParser* subparser, struct mCoreConfig* config) {
160	if (args->frameskip >= 0) {
161		mCoreConfigSetOverrideIntValue(config, "frameskip", args->frameskip);
162	}
163	if (args->logLevel > INT_MIN) {
164		mCoreConfigSetOverrideIntValue(config, "logLevel", args->logLevel);
165	}
166	if (args->bios) {
167		mCoreConfigSetOverrideValue(config, "bios", args->bios);
168	}
169	HashTableEnumerate(&args->configOverrides, _tableApply, config);
170	if (subparser) {
171		subparser->apply(subparser, config);
172	}
173}
174
175void freeArguments(struct mArguments* args) {
176	free(args->fname);
177	args->fname = 0;
178
179	free(args->patch);
180	args->patch = 0;
181
182	free(args->savestate);
183	args->savestate = 0;
184
185	free(args->cheatsFile);
186	args->cheatsFile = 0;
187
188	free(args->bios);
189	args->bios = 0;
190
191	HashTableDeinit(&args->configOverrides);
192}
193
194void initParserForGraphics(struct mSubParser* parser, struct mGraphicsOpts* opts) {
195	parser->usage = GRAPHICS_USAGE;
196	parser->opts = opts;
197	parser->parse = _parseGraphicsArg;
198	parser->apply = _applyGraphicsArgs;
199	parser->extraOptions = GRAPHICS_OPTIONS;
200	opts->multiplier = 0;
201	opts->fullscreen = false;
202}
203
204bool _parseGraphicsArg(struct mSubParser* parser, int option, const char* arg) {
205	UNUSED(arg);
206	struct mGraphicsOpts* graphicsOpts = parser->opts;
207	switch (option) {
208	case 'f':
209		graphicsOpts->fullscreen = true;
210		return true;
211	case '1':
212	case '2':
213	case '3':
214	case '4':
215	case '5':
216	case '6':
217		if (graphicsOpts->multiplier) {
218			return false;
219		}
220		graphicsOpts->multiplier = option - '0';
221		return true;
222	default:
223		return false;
224	}
225}
226
227void _applyGraphicsArgs(struct mSubParser* parser, struct mCoreConfig* config) {
228	struct mGraphicsOpts* graphicsOpts = parser->opts;
229	if (graphicsOpts->fullscreen) {
230		mCoreConfigSetOverrideIntValue(config, "fullscreen", graphicsOpts->fullscreen);
231	}
232}
233
234void usage(const char* arg0, const char* extraOptions) {
235	printf("usage: %s [option ...] file\n", arg0);
236	puts("\nGeneric options:");
237	puts("  -b, --bios FILE            GBA BIOS file to use");
238	puts("  -c, --cheats FILE          Apply cheat codes from a file");
239	puts("  -C, --config OPTION=VALUE  Override config value");
240#ifdef USE_EDITLINE
241	puts("  -d, --debug                Use command-line debugger");
242#endif
243#ifdef USE_GDB_STUB
244	puts("  -g, --gdb                  Start GDB session (default port 2345)");
245#endif
246	puts("  -l, --log-level N          Log level mask");
247	puts("  -t, --savestate FILE       Load savestate when starting");
248	puts("  -p, --patch FILE           Apply a specified patch file when running");
249	puts("  -s, --frameskip N          Skip every N frames");
250	puts("  --version                  Print version and exit");
251	if (extraOptions) {
252		puts(extraOptions);
253	}
254}
255
256void version(const char* arg0) {
257	printf("%s %s (%s)\n", arg0, projectVersion, gitCommit);
258}