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