src/platform/sdl/main.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 "main.h"
7
8#ifdef USE_CLI_DEBUGGER
9#include "debugger/cli-debugger.h"
10#endif
11
12#ifdef USE_GDB_STUB
13#include "debugger/gdb-stub.h"
14#endif
15
16#ifdef M_CORE_GBA
17#include "gba/gba.h"
18#include "gba/context/config.h"
19#include "gba/supervisor/thread.h"
20#include "gba/video.h"
21#endif
22#ifdef M_CORE_GB
23#include "gb/gb.h"
24#include "gb/video.h"
25#endif
26#include "platform/commandline.h"
27#include "util/configuration.h"
28#include "util/vfs.h"
29
30#include <SDL.h>
31
32#include <errno.h>
33#include <signal.h>
34#include <sys/time.h>
35
36#define PORT "sdl"
37
38// TODO: Move somewhere
39enum mPlatform {
40 PLATFORM_NONE = -1,
41 PLATFORM_GBA,
42 PLATFORM_GB
43};
44
45static bool mSDLInit(struct mSDLRenderer* renderer);
46static void mSDLDeinit(struct mSDLRenderer* renderer);
47
48// TODO: Clean up signatures
49#ifdef M_CORE_GBA
50static int mSDLRunGBA(struct mSDLRenderer* renderer, struct GBAArguments* args, struct GBAOptions* opts, struct GBAConfig* config, struct GBAInputMap* inputMap);
51#endif
52#ifdef M_CORE_GB
53static int mSDLRunGB(struct mSDLRenderer* renderer, struct GBAArguments* args);
54#endif
55
56
57int main(int argc, char** argv) {
58 struct mSDLRenderer renderer;
59
60 struct GBAInputMap inputMap;
61 GBAInputMapInit(&inputMap);
62
63 struct GBAConfig config;
64 GBAConfigInit(&config, PORT);
65 GBAConfigLoad(&config);
66
67 struct GBAOptions opts = {
68 .width = 0,
69 .height = 0,
70 .useBios = true,
71 .rewindEnable = true,
72 .audioBuffers = 512,
73 .videoSync = false,
74 .audioSync = true,
75 };
76
77 struct GBAArguments args;
78 struct GraphicsOpts graphicsOpts;
79
80 struct SubParser subparser;
81
82 initParserForGraphics(&subparser, &graphicsOpts);
83 bool parsed = parseArguments(&args, &config, argc, argv, &subparser);
84 if (!parsed || args.showHelp) {
85 usage(argv[0], subparser.usage);
86 freeArguments(&args);
87 GBAConfigFreeOpts(&opts);
88 GBAConfigDeinit(&config);
89 return !parsed;
90 }
91 if (args.showVersion) {
92 version(argv[0]);
93 freeArguments(&args);
94 GBAConfigFreeOpts(&opts);
95 GBAConfigDeinit(&config);
96 return 0;
97 }
98
99 enum mPlatform platform = PLATFORM_NONE;
100
101 if (args.fname) {
102 struct VFile* vf = VFileOpen(args.fname, O_RDONLY);
103 if (!vf) {
104 printf("Could not open game. Are you sure the file exists?\n");
105 freeArguments(&args);
106 GBAConfigFreeOpts(&opts);
107 GBAConfigDeinit(&config);
108 return 1;
109 }
110#ifdef M_CORE_GBA
111 else if (GBAIsROM(vf)) {
112 platform = PLATFORM_GBA;
113 if (!opts.width) {
114 opts.width = VIDEO_HORIZONTAL_PIXELS;
115 }
116 if (!opts.height) {
117 opts.height = VIDEO_VERTICAL_PIXELS;
118 }
119 }
120#endif
121#ifdef M_CORE_GB
122 else if (GBIsROM(vf)) {
123 platform = PLATFORM_GB;
124 if (!opts.width) {
125 opts.width = /*GB_*/VIDEO_HORIZONTAL_PIXELS;
126 }
127 if (!opts.height) {
128 opts.height = /*GB_*/VIDEO_VERTICAL_PIXELS;
129 }
130 }
131#endif
132 else {
133 printf("Could not run game. Are you sure the file exists and is a Game Boy Advance game?\n");
134 freeArguments(&args);
135 GBAConfigFreeOpts(&opts);
136 GBAConfigDeinit(&config);
137 return 1;
138 }
139 }
140
141 GBAConfigLoadDefaults(&config, &opts);
142 GBAConfigMap(&config, &opts);
143
144 renderer.viewportWidth = opts.width;
145 renderer.viewportHeight = opts.height;
146#if SDL_VERSION_ATLEAST(2, 0, 0)
147 renderer.player.fullscreen = opts.fullscreen;
148 renderer.player.windowUpdated = 0;
149#else
150 renderer.fullscreen = opts.fullscreen;
151#endif
152 renderer.ratio = graphicsOpts.multiplier;
153 if (renderer.ratio == 0) {
154 renderer.ratio = 1;
155 }
156
157 renderer.lockAspectRatio = opts.lockAspectRatio;
158 renderer.filter = opts.resampleVideo;
159
160 int ret;
161
162 switch (platform) {
163 case PLATFORM_GBA:
164 ret = mSDLRunGBA(&renderer, &args, &opts, &config, &inputMap);
165 break;
166 case PLATFORM_GB:
167 ret = mSDLRunGB(&renderer, &args);
168 break;
169 default:
170 ret = 1;
171 break;
172 }
173
174 freeArguments(&args);
175 GBAConfigFreeOpts(&opts);
176 GBAConfigDeinit(&config);
177
178 return ret;
179}
180
181#ifdef M_CORE_GBA
182int mSDLRunGBA(struct mSDLRenderer* renderer, struct GBAArguments* args, struct GBAOptions* opts, struct GBAConfig* config, struct GBAInputMap* inputMap) {
183 GBAVideoSoftwareRendererCreate(&renderer->d);
184#ifdef BUILD_GL
185 mSDLGLCreate(renderer);
186#elif defined(BUILD_GLES2) || defined(USE_EPOXY)
187 mSDLGLES2Create(renderer);
188#else
189 mSDLSWCreate(renderer);
190#endif
191
192 if (!mSDLInit(renderer)) {
193 return 1;
194 }
195
196 struct GBAThread context = {
197 .renderer = &renderer->d.d,
198 .userData = renderer
199 };
200
201 context.debugger = createDebugger(args, &context);
202
203 GBAMapOptionsToContext(opts, &context);
204 GBAMapArgumentsToContext(args, &context);
205
206 bool didFail = false;
207
208 renderer->audio.samples = context.audioBuffers;
209 renderer->audio.sampleRate = 44100;
210 if (opts->sampleRate) {
211 renderer->audio.sampleRate = opts->sampleRate;
212 }
213 if (!GBASDLInitAudio(&renderer->audio, &context)) {
214 didFail = true;
215 }
216
217 renderer->player.bindings = inputMap;
218 GBASDLInitBindings(inputMap);
219 GBASDLInitEvents(&renderer->events);
220 GBASDLEventsLoadConfig(&renderer->events, GBAConfigGetInput(config));
221 GBASDLAttachPlayer(&renderer->events, &renderer->player);
222 GBASDLPlayerLoadConfig(&renderer->player, GBAConfigGetInput(config));
223 context.overrides = GBAConfigGetOverrides(config);
224
225 if (!didFail) {
226#if SDL_VERSION_ATLEAST(2, 0, 0)
227 GBASDLSetScreensaverSuspendable(&renderer->events, opts->suspendScreensaver);
228 GBASDLSuspendScreensaver(&renderer->events);
229#endif
230 if (GBAThreadStart(&context)) {
231 renderer->runloop(renderer, &context);
232 GBAThreadJoin(&context);
233 } else {
234 didFail = true;
235 printf("Could not run game. Are you sure the file exists and is a Game Boy Advance game?\n");
236 }
237
238#if SDL_VERSION_ATLEAST(2, 0, 0)
239 GBASDLResumeScreensaver(&renderer->events);
240 GBASDLSetScreensaverSuspendable(&renderer->events, false);
241#endif
242
243 if (GBAThreadHasCrashed(&context)) {
244 didFail = true;
245 printf("The game crashed!\n");
246 }
247 }
248 free(context.debugger);
249 GBADirectorySetDeinit(&context.dirs);
250 GBASDLDetachPlayer(&renderer->events, &renderer->player);
251 GBAInputMapDeinit(inputMap);
252
253 mSDLDeinit(renderer);
254
255 return didFail;
256}
257#endif
258
259#ifdef M_CORE_GB
260int mSDLRunGB(struct mSDLRenderer* renderer, struct GBAArguments* args) {
261 GBVideoSoftwareRendererCreate(&renderer->gb);
262#ifdef BUILD_GL
263 mSDLGLCreateGB(renderer);
264#elif defined(BUILD_GLES2)
265 mSDLGLES2CreateGB(renderer);
266#else
267 mSDLSWCreateGB(renderer);
268#endif
269
270 if (!mSDLInit(renderer)) {
271 return 1;
272 }
273
274 struct LR35902Core cpu;
275 struct GB gb;
276
277 GBCreate(&gb);
278 LR35902SetComponents(&cpu, &gb.d, 0, 0);
279 LR35902Init(&cpu);
280
281 GBVideoAssociateRenderer(&gb.video, &renderer->gb.d);
282 struct VFile* vf = VFileOpen(args->fname, O_RDONLY);
283 GBLoadROM(&gb, vf, 0, args->fname);
284
285 LR35902Reset(&cpu);
286 renderer->runloop(renderer, &gb);
287 vf->close(vf);
288
289 mSDLDeinit(renderer);
290 return 0;
291}
292#endif
293
294static bool mSDLInit(struct mSDLRenderer* renderer) {
295 if (SDL_Init(SDL_INIT_VIDEO) < 0) {
296 printf("Could not initialize video: %s\n", SDL_GetError());
297 return false;
298 }
299
300 return renderer->init(renderer);
301}
302
303static void mSDLDeinit(struct mSDLRenderer* renderer) {
304 GBASDLDeinitEvents(&renderer->events);
305 GBASDLDeinitAudio(&renderer->audio);
306#if SDL_VERSION_ATLEAST(2, 0, 0)
307 SDL_DestroyWindow(renderer->window);
308#endif
309
310 renderer->deinit(renderer);
311
312 SDL_Quit();
313}