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