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}