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