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