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}