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
54bool _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 GBAConfigSetDefaultValue(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 GBAConfigSetDefaultValue(config, "logLevel", optarg);
113 break;
114 case 'p':
115 opts->patch = strdup(optarg);
116 break;
117 case 's':
118 GBAConfigSetDefaultValue(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}
159
160bool _parseGraphicsArg(struct SubParser* parser, struct GBAConfig* config, int option, const char* arg) {
161 UNUSED(arg);
162 struct GraphicsOpts* graphicsOpts = parser->opts;
163 switch (option) {
164 case 'f':
165 GBAConfigSetDefaultIntValue(config, "fullscreen", 1);
166 return true;
167 case '1':
168 case '2':
169 case '3':
170 case '4':
171 case '5':
172 case '6':
173 if (graphicsOpts->multiplier) {
174 return false;
175 }
176 graphicsOpts->multiplier = option - '0';
177 GBAConfigSetDefaultIntValue(config, "width", VIDEO_HORIZONTAL_PIXELS * graphicsOpts->multiplier);
178 GBAConfigSetDefaultIntValue(config, "height", VIDEO_VERTICAL_PIXELS * graphicsOpts->multiplier);
179 return true;
180 default:
181 return false;
182 }
183}
184
185struct ARMDebugger* createDebugger(struct GBAArguments* opts, struct GBAThread* context) {
186#ifndef USE_CLI_DEBUGGER
187 UNUSED(context);
188#endif
189 union DebugUnion {
190 struct ARMDebugger d;
191#ifdef USE_CLI_DEBUGGER
192 struct CLIDebugger cli;
193#endif
194#ifdef USE_GDB_STUB
195 struct GDBStub gdb;
196#endif
197 };
198
199 union DebugUnion* debugger = malloc(sizeof(union DebugUnion));
200
201 switch (opts->debuggerType) {
202#ifdef USE_CLI_DEBUGGER
203 case DEBUGGER_CLI:
204 CLIDebuggerCreate(&debugger->cli);
205 struct GBACLIDebugger* gbaDebugger = GBACLIDebuggerCreate(context);
206 CLIDebuggerAttachSystem(&debugger->cli, &gbaDebugger->d);
207 break;
208#endif
209#ifdef USE_GDB_STUB
210 case DEBUGGER_GDB:
211 GDBStubCreate(&debugger->gdb);
212 GDBStubListen(&debugger->gdb, 2345, 0);
213 break;
214#endif
215 case DEBUGGER_NONE:
216 case DEBUGGER_MAX:
217 free(debugger);
218 return 0;
219 break;
220 }
221
222 return &debugger->d;
223}
224
225void usage(const char* arg0, const char* extraOptions) {
226 printf("usage: %s [option ...] file\n", arg0);
227 puts("\nGeneric options:");
228 puts(" -b, --bios FILE GBA BIOS file to use");
229 puts(" -c, --cheats FILE Apply cheat codes from a file");
230#ifdef USE_CLI_DEBUGGER
231 puts(" -d, --debug Use command-line debugger");
232#endif
233#ifdef USE_GDB_STUB
234 puts(" -g, --gdb Start GDB session (default port 2345)");
235#endif
236 puts(" -v, --movie FILE Play back a movie of recorded input");
237 puts(" -p, --patch FILE Apply a specified patch file when running");
238 puts(" -s, --frameskip N Skip every N frames");
239 puts(" --version Print version and exit");
240 if (extraOptions) {
241 puts(extraOptions);
242 }
243}
244
245void version(const char* arg0) {
246 printf("%s %s (%s)\n", arg0, projectVersion, gitCommit);
247}