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);
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 GBAVideoSoftwareRendererCreate(&renderer.d);
120#ifdef BUILD_GL
121 mSDLGLCreate(&renderer);
122#elif defined(BUILD_GLES2) || defined(USE_EPOXY)
123 mSDLGLES2Create(&renderer);
124#else
125 mSDLSWCreate(&renderer);
126#endif
127 }
128#endif
129#ifdef M_CORE_GB
130 else if (GBIsROM(vf)) {
131 platform = PLATFORM_GB;
132 if (!opts.width) {
133 opts.width = /*GB_*/VIDEO_HORIZONTAL_PIXELS;
134 }
135 if (!opts.height) {
136 opts.height = /*GB_*/VIDEO_VERTICAL_PIXELS;
137 }
138 GBVideoSoftwareRendererCreate(&renderer.gb);
139#ifdef BUILD_GL
140 mSDLGLCreateGB(&renderer);
141#elif defined(BUILD_GLES2) || defined(USE_EPOXY)
142 mSDLGLES2CreateGB(&renderer);
143#else
144 mSDLSWCreateGB(&renderer);
145#endif
146 }
147#endif
148 else {
149 printf("Could not run game. Are you sure the file exists and is a Game Boy Advance game?\n");
150 freeArguments(&args);
151 GBAConfigFreeOpts(&opts);
152 GBAConfigDeinit(&config);
153 return 1;
154 }
155 }
156
157 GBAConfigLoadDefaults(&config, &opts);
158 GBAConfigMap(&config, &opts);
159
160 renderer.viewportWidth = opts.width;
161 renderer.viewportHeight = opts.height;
162#if SDL_VERSION_ATLEAST(2, 0, 0)
163 renderer.player.fullscreen = opts.fullscreen;
164 renderer.player.windowUpdated = 0;
165#else
166 renderer.fullscreen = opts.fullscreen;
167#endif
168 renderer.ratio = graphicsOpts.multiplier;
169 if (renderer.ratio == 0) {
170 renderer.ratio = 1;
171 }
172
173 renderer.lockAspectRatio = opts.lockAspectRatio;
174 renderer.filter = opts.resampleVideo;
175
176 if (!mSDLInit(&renderer)) {
177 freeArguments(&args);
178 GBAConfigFreeOpts(&opts);
179 GBAConfigDeinit(&config);
180 return 1;
181 }
182
183 renderer.player.bindings = &inputMap;
184 GBASDLInitBindings(&inputMap);
185 GBASDLInitEvents(&renderer.events);
186 GBASDLEventsLoadConfig(&renderer.events, GBAConfigGetInput(&config));
187 GBASDLAttachPlayer(&renderer.events, &renderer.player);
188 GBASDLPlayerLoadConfig(&renderer.player, GBAConfigGetInput(&config));
189
190 int ret;
191
192 switch (platform) {
193 case PLATFORM_GBA:
194 ret = mSDLRunGBA(&renderer, &args, &opts, &config);
195 break;
196 case PLATFORM_GB:
197 ret = mSDLRunGB(&renderer, &args);
198 break;
199 default:
200 ret = 1;
201 break;
202 }
203 GBASDLDetachPlayer(&renderer.events, &renderer.player);
204 GBAInputMapDeinit(&inputMap);
205
206 mSDLDeinit(&renderer);
207
208 freeArguments(&args);
209 GBAConfigFreeOpts(&opts);
210 GBAConfigDeinit(&config);
211
212 return ret;
213}
214
215#ifdef M_CORE_GBA
216int mSDLRunGBA(struct mSDLRenderer* renderer, struct GBAArguments* args, struct GBAOptions* opts, struct GBAConfig* config) {
217 struct GBAThread context = {
218 .renderer = &renderer->d.d,
219 .userData = renderer
220 };
221
222 context.debugger = createDebugger(args, &context);
223
224 GBAMapOptionsToContext(opts, &context);
225 GBAMapArgumentsToContext(args, &context);
226 context.overrides = GBAConfigGetOverrides(config);
227
228 bool didFail = false;
229
230 renderer->audio.samples = context.audioBuffers;
231 renderer->audio.sampleRate = 44100;
232 if (opts->sampleRate) {
233 renderer->audio.sampleRate = opts->sampleRate;
234 }
235 if (!GBASDLInitAudio(&renderer->audio, &context)) {
236 didFail = true;
237 }
238
239 if (!didFail) {
240#if SDL_VERSION_ATLEAST(2, 0, 0)
241 GBASDLSetScreensaverSuspendable(&renderer->events, opts->suspendScreensaver);
242 GBASDLSuspendScreensaver(&renderer->events);
243#endif
244 if (GBAThreadStart(&context)) {
245 renderer->runloop(renderer, &context);
246 GBAThreadJoin(&context);
247 } else {
248 didFail = true;
249 printf("Could not run game. Are you sure the file exists and is a Game Boy Advance game?\n");
250 }
251
252#if SDL_VERSION_ATLEAST(2, 0, 0)
253 GBASDLResumeScreensaver(&renderer->events);
254 GBASDLSetScreensaverSuspendable(&renderer->events, false);
255#endif
256
257 if (GBAThreadHasCrashed(&context)) {
258 didFail = true;
259 printf("The game crashed!\n");
260 }
261 }
262 free(context.debugger);
263 GBADirectorySetDeinit(&context.dirs);
264
265 return didFail;
266}
267#endif
268
269#ifdef M_CORE_GB
270int mSDLRunGB(struct mSDLRenderer* renderer, struct GBAArguments* args) {
271 struct LR35902Core cpu;
272 struct GB gb;
273
274 GBCreate(&gb);
275 LR35902SetComponents(&cpu, &gb.d, 0, 0);
276 LR35902Init(&cpu);
277
278 GBVideoAssociateRenderer(&gb.video, &renderer->gb.d);
279 struct VFile* vf = VFileOpen(args->fname, O_RDONLY);
280 struct VFile* savVf = 0;
281
282 {
283 char savepath[PATH_MAX];
284 char dirname[PATH_MAX];
285 char basename[PATH_MAX];
286 separatePath(args->fname, dirname, basename, 0);
287 snprintf(savepath, sizeof(savepath), "%s" PATH_SEP "%s.sav", dirname, basename);
288 savVf = VFileOpen(savepath, O_RDWR | O_CREAT);
289 }
290
291 GBLoadROM(&gb, vf, savVf, args->fname);
292
293 LR35902Reset(&cpu);
294 renderer->runloop(renderer, &gb);
295 vf->close(vf);
296 return 0;
297}
298#endif
299
300static bool mSDLInit(struct mSDLRenderer* renderer) {
301 if (SDL_Init(SDL_INIT_VIDEO) < 0) {
302 printf("Could not initialize video: %s\n", SDL_GetError());
303 return false;
304 }
305
306 return renderer->init(renderer);
307}
308
309static void mSDLDeinit(struct mSDLRenderer* renderer) {
310 GBASDLDeinitEvents(&renderer->events);
311 GBASDLDeinitAudio(&renderer->audio);
312#if SDL_VERSION_ATLEAST(2, 0, 0)
313 SDL_DestroyWindow(renderer->window);
314#endif
315
316 renderer->deinit(renderer);
317
318 SDL_Quit();
319}