all repos — mgba @ a4e29886c9aab8a54a1232bac1bf4a4b7eb1b3db

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