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 { "movie", required_argument, 0, 'v' },
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:v:"
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 'v':
136 args->movie = 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->movie);
183 args->movie = 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(" -v, --movie FILE Play back a movie of recorded input");
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}