all repos — mgba @ 8beac67f5620196669fbef0c538e9d428c993ced

mGBA Game Boy Advance Emulator

src/platform/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 "commandline.h"
  7
  8#include "debugger/debugger.h"
  9
 10#ifdef USE_CLI_DEBUGGER
 11#include "debugger/cli-debugger.h"
 12#include "gba/supervisor/cli.h"
 13#endif
 14
 15#ifdef USE_GDB_STUB
 16#include "debugger/gdb-stub.h"
 17#endif
 18
 19#include "gba/video.h"
 20#include "util/string.h"
 21
 22#include <fcntl.h>
 23#include <getopt.h>
 24
 25#define GRAPHICS_OPTIONS "123456f"
 26#define GRAPHICS_USAGE \
 27	"\nGraphics options:\n" \
 28	"  -1               1x viewport\n" \
 29	"  -2               2x viewport\n" \
 30	"  -3               3x viewport\n" \
 31	"  -4               4x viewport\n" \
 32	"  -5               5x viewport\n" \
 33	"  -6               6x viewport\n" \
 34	"  -f               Start full-screen"
 35
 36static const struct option _options[] = {
 37	{ "bios",      required_argument, 0, 'b' },
 38	{ "cheats",    required_argument, 0, 'c' },
 39	{ "frameskip", required_argument, 0, 's' },
 40#ifdef USE_CLI_DEBUGGER
 41	{ "debug",     no_argument, 0, 'd' },
 42#endif
 43#ifdef USE_GDB_STUB
 44	{ "gdb",       no_argument, 0, 'g' },
 45#endif
 46	{ "help",      no_argument, 0, 'h' },
 47	{ "movie",     required_argument, 0, 'v' },
 48	{ "patch",     required_argument, 0, 'p' },
 49	{ "version",   no_argument, 0, '\0' },
 50	{ 0, 0, 0, 0 }
 51};
 52
 53static bool _parseGraphicsArg(struct mSubParser* parser, int option, const char* arg);
 54static void _applyGraphicsArgs(struct mSubParser* parser, struct mCoreConfig* config);
 55
 56bool parseArguments(struct mArguments* args, int argc, char* const* argv, struct mSubParser* subparser) {
 57	int ch;
 58	char options[64] =
 59		"b:c:hl:p:s:v:"
 60#ifdef USE_CLI_DEBUGGER
 61		"d"
 62#endif
 63#ifdef USE_GDB_STUB
 64		"g"
 65#endif
 66	;
 67	memset(args, 0, sizeof(*args));
 68	args->frameskip = -1;
 69	args->logLevel = INT_MIN;
 70	if (subparser && subparser->extraOptions) {
 71		// TODO: modularize options to subparsers
 72		strncat(options, subparser->extraOptions, sizeof(options) - strlen(options) - 1);
 73	}
 74	int index = 0;
 75	while ((ch = getopt_long(argc, argv, options, _options, &index)) != -1) {
 76		const struct option* opt = &_options[index];
 77		switch (ch) {
 78		case '\0':
 79			if (strcmp(opt->name, "version") == 0) {
 80				args->showVersion = true;
 81			} else {
 82				return false;
 83			}
 84			break;
 85		case 'b':
 86			args->bios = strdup(optarg);
 87			break;
 88		case 'c':
 89			args->cheatsFile = strdup(optarg);
 90			break;
 91#ifdef USE_CLI_DEBUGGER
 92		case 'd':
 93			if (args->debuggerType != DEBUGGER_NONE) {
 94				return false;
 95			}
 96			args->debuggerType = DEBUGGER_CLI;
 97			break;
 98#endif
 99#ifdef USE_GDB_STUB
100		case 'g':
101			if (args->debuggerType != DEBUGGER_NONE) {
102				return false;
103			}
104			args->debuggerType = DEBUGGER_GDB;
105			break;
106#endif
107		case 'h':
108			args->showHelp = true;
109			break;
110		case 'l':
111			args->logLevel = atoi(optarg);
112			break;
113		case 'p':
114			args->patch = strdup(optarg);
115			break;
116		case 's':
117			args->frameskip = atoi(optarg);
118			break;
119		case 'v':
120			args->movie = strdup(optarg);
121			break;
122		default:
123			if (subparser) {
124				if (!subparser->parse(subparser, ch, optarg)) {
125					return false;
126				}
127			}
128			break;
129		}
130	}
131	argc -= optind;
132	argv += optind;
133	if (argc != 1) {
134		return args->showHelp || args->showVersion;
135	}
136	args->fname = strdup(argv[0]);
137	return true;
138}
139
140void applyArguments(const struct mArguments* args, struct mSubParser* subparser, struct mCoreConfig* config) {
141	if (args->frameskip >= 0) {
142		mCoreConfigSetOverrideIntValue(config, "frameskip", args->frameskip);
143	}
144	if (args->logLevel > INT_MIN) {
145		mCoreConfigSetOverrideIntValue(config, "logLevel", args->logLevel);
146	}
147	if (args->bios) {
148		mCoreConfigSetOverrideValue(config, "bios", args->bios);
149	}
150	if (subparser) {
151		subparser->apply(subparser, config);
152	}
153}
154
155void freeArguments(struct mArguments* args) {
156	free(args->fname);
157	args->fname = 0;
158
159	free(args->patch);
160	args->patch = 0;
161
162	free(args->movie);
163	args->movie = 0;
164
165	free(args->cheatsFile);
166	args->cheatsFile = 0;
167
168	free(args->bios);
169	args->bios = 0;
170}
171
172void initParserForGraphics(struct mSubParser* parser, struct mGraphicsOpts* opts) {
173	parser->usage = GRAPHICS_USAGE;
174	parser->opts = opts;
175	parser->parse = _parseGraphicsArg;
176	parser->apply = _applyGraphicsArgs;
177	parser->extraOptions = GRAPHICS_OPTIONS;
178	opts->multiplier = 0;
179	opts->fullscreen = false;
180}
181
182bool _parseGraphicsArg(struct mSubParser* parser, int option, const char* arg) {
183	UNUSED(arg);
184	struct mGraphicsOpts* graphicsOpts = parser->opts;
185	switch (option) {
186	case 'f':
187		graphicsOpts->fullscreen = true;
188		return true;
189	case '1':
190	case '2':
191	case '3':
192	case '4':
193	case '5':
194	case '6':
195		if (graphicsOpts->multiplier) {
196			return false;
197		}
198		graphicsOpts->multiplier = option - '0';
199		return true;
200	default:
201		return false;
202	}
203}
204
205void _applyGraphicsArgs(struct mSubParser* parser, struct mCoreConfig* config) {
206	struct mGraphicsOpts* graphicsOpts = parser->opts;
207	mCoreConfigSetOverrideIntValue(config, "fullscreen", graphicsOpts->fullscreen);
208}
209
210struct Debugger* createDebugger(struct mArguments* opts, struct mCore* core) {
211#ifndef USE_CLI_DEBUGGER
212	UNUSED(core);
213#endif
214	union DebugUnion {
215		struct Debugger d;
216#ifdef USE_CLI_DEBUGGER
217		struct CLIDebugger cli;
218#endif
219#ifdef USE_GDB_STUB
220		struct GDBStub gdb;
221#endif
222	};
223
224	union DebugUnion* debugger = malloc(sizeof(union DebugUnion));
225
226	switch (opts->debuggerType) {
227#ifdef USE_CLI_DEBUGGER
228	case DEBUGGER_CLI:
229		CLIDebuggerCreate(&debugger->cli);
230		struct GBACLIDebugger* gbaDebugger = GBACLIDebuggerCreate(core);
231		CLIDebuggerAttachSystem(&debugger->cli, &gbaDebugger->d);
232		break;
233#endif
234#ifdef USE_GDB_STUB
235	case DEBUGGER_GDB:
236		GDBStubCreate(&debugger->gdb);
237		GDBStubListen(&debugger->gdb, 2345, 0);
238		break;
239#endif
240	case DEBUGGER_NONE:
241	case DEBUGGER_MAX:
242		free(debugger);
243		return 0;
244		break;
245	}
246
247	return &debugger->d;
248}
249
250void usage(const char* arg0, const char* extraOptions) {
251	printf("usage: %s [option ...] file\n", arg0);
252	puts("\nGeneric options:");
253	puts("  -b, --bios FILE     GBA BIOS file to use");
254	puts("  -c, --cheats FILE   Apply cheat codes from a file");
255#ifdef USE_CLI_DEBUGGER
256	puts("  -d, --debug         Use command-line debugger");
257#endif
258#ifdef USE_GDB_STUB
259	puts("  -g, --gdb           Start GDB session (default port 2345)");
260#endif
261	puts("  -v, --movie FILE    Play back a movie of recorded input");
262	puts("  -p, --patch FILE    Apply a specified patch file when running");
263	puts("  -s, --frameskip N   Skip every N frames");
264	puts("  --version           Print version and exit");
265	if (extraOptions) {
266		puts(extraOptions);
267	}
268}
269
270void version(const char* arg0) {
271	printf("%s %s (%s)\n", arg0, projectVersion, gitCommit);
272}