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
54static int mSDLRun(struct mSDLRenderer* renderer, struct mArguments* args);
55
56int main(int argc, char** argv) {
57 struct mSDLRenderer renderer = {};
58
59 struct mCoreOptions opts = {
60 .width = 0,
61 .height = 0,
62 .useBios = true,
63 .rewindEnable = true,
64 .audioBuffers = 512,
65 .videoSync = false,
66 .audioSync = true,
67 };
68
69 struct mArguments args;
70 struct mGraphicsOpts graphicsOpts;
71
72 struct mSubParser subparser;
73
74 initParserForGraphics(&subparser, &graphicsOpts);
75 bool parsed = parseArguments(&args, argc, argv, &subparser);
76 if (!parsed || args.showHelp) {
77 usage(argv[0], subparser.usage);
78 mCoreConfigFreeOpts(&opts);
79 return !parsed;
80 }
81 if (args.showVersion) {
82 version(argv[0]);
83 mCoreConfigFreeOpts(&opts);
84 return 0;
85 }
86
87 enum mPlatform platform = PLATFORM_NONE;
88
89 if (args.fname) {
90 struct VFile* vf = VFileOpen(args.fname, O_RDONLY);
91 if (!vf) {
92 printf("Could not open game. Are you sure the file exists?\n");
93 freeArguments(&args);
94 mCoreConfigFreeOpts(&opts);
95 return 1;
96 }
97#ifdef M_CORE_GBA
98 else if (GBAIsROM(vf)) {
99 platform = PLATFORM_GBA;
100 if (!opts.width) {
101 opts.width = VIDEO_HORIZONTAL_PIXELS;
102 }
103 if (!opts.height) {
104 opts.height = VIDEO_VERTICAL_PIXELS;
105 }
106 renderer.core = GBACoreCreate();
107#ifdef BUILD_GL
108 mSDLGLCreateGBA(&renderer);
109#elif defined(BUILD_GLES2) || defined(USE_EPOXY)
110 mSDLGLES2Create(&renderer);
111#else
112 mSDLSWCreate(&renderer);
113#endif
114 }
115#endif
116#ifdef M_CORE_GB
117 else if (GBIsROM(vf)) {
118 platform = PLATFORM_GB;
119 if (!opts.width) {
120 opts.width = GB_VIDEO_HORIZONTAL_PIXELS;
121 }
122 if (!opts.height) {
123 opts.height = GB_VIDEO_VERTICAL_PIXELS;
124 }
125 renderer.core = GBCoreCreate();
126#ifdef BUILD_GL
127 mSDLGLCreateGB(&renderer);
128#elif defined(BUILD_GLES2) || defined(USE_EPOXY)
129 mSDLGLES2CreateGB(&renderer);
130#else
131 mSDLSWCreateGB(&renderer);
132#endif
133 }
134#endif
135 else {
136 printf("Could not run game. Are you sure the file exists and is a compatible game?\n");
137 freeArguments(&args);
138 mCoreConfigFreeOpts(&opts);
139 return 1;
140 }
141 }
142
143 mInputMapInit(&renderer.core->inputMap, &GBAInputInfo);
144 mCoreInitConfig(renderer.core, PORT);
145 applyArguments(&args, &subparser, &renderer.core->config);
146
147 mCoreConfigLoadDefaults(&renderer.core->config, &opts);
148
149 // TODO: Load from config
150 renderer.viewportWidth = opts.width;
151 renderer.viewportHeight = opts.height;
152#if SDL_VERSION_ATLEAST(2, 0, 0)
153 renderer.player.fullscreen = opts.fullscreen;
154 renderer.player.windowUpdated = 0;
155#else
156 renderer.fullscreen = opts.fullscreen;
157#endif
158
159 renderer.ratio = graphicsOpts.multiplier;
160 if (renderer.ratio == 0) {
161 renderer.ratio = 1;
162 }
163
164 renderer.lockAspectRatio = opts.lockAspectRatio;
165 renderer.filter = opts.resampleVideo;
166
167 if (!mSDLInit(&renderer)) {
168 freeArguments(&args);
169 mCoreConfigFreeOpts(&opts);
170 mCoreConfigDeinit(&renderer.core->config);
171 return 1;
172 }
173
174 if (renderer.core) {
175 // TODO: Check return code
176 renderer.core->init(renderer.core);
177 }
178 mCoreLoadConfig(renderer.core);
179
180 renderer.player.bindings = &renderer.core->inputMap;
181 mSDLInitBindingsGBA(&renderer.core->inputMap);
182 mSDLInitEvents(&renderer.events);
183 mSDLEventsLoadConfig(&renderer.events, mCoreConfigGetInput(&renderer.core->config));
184 mSDLAttachPlayer(&renderer.events, &renderer.player);
185 mSDLPlayerLoadConfig(&renderer.player, mCoreConfigGetInput(&renderer.core->config));
186
187 int ret;
188
189 // TODO: Use opts and config
190 ret = mSDLRun(&renderer, &args);
191 mSDLDetachPlayer(&renderer.events, &renderer.player);
192 mInputMapDeinit(&renderer.core->inputMap);
193
194 mSDLDeinit(&renderer);
195
196 freeArguments(&args);
197 mCoreConfigFreeOpts(&opts);
198 mCoreConfigDeinit(&renderer.core->config);
199
200 return ret;
201}
202
203int mSDLRun(struct mSDLRenderer* renderer, struct mArguments* args) {
204 struct mCoreThread thread = {
205 .core = renderer->core
206 };
207 if (!mCoreLoadFile(renderer->core, args->fname)) {
208 return 1;
209 }
210 mCoreAutoloadSave(renderer->core);
211 // TODO: Create debugger
212
213 if (args->patch) {
214 struct VFile* patch = VFileOpen(args->patch, O_RDONLY);
215 if (patch) {
216 renderer->core->loadPatch(renderer->core, patch);
217 }
218 } else {
219 mCoreAutoloadPatch(renderer->core);
220 }
221
222 renderer->audio.samples = renderer->core->opts.audioBuffers;
223 renderer->audio.sampleRate = 44100;
224
225 bool didFail = !mSDLInitAudio(&renderer->audio, 0);
226 if (!didFail) {
227#if SDL_VERSION_ATLEAST(2, 0, 0)
228 mSDLSetScreensaverSuspendable(&renderer->events, renderer->core->opts.suspendScreensaver);
229 mSDLSuspendScreensaver(&renderer->events);
230#endif
231 renderer->audio.core = renderer->core;
232 renderer->audio.sync = &thread.sync;
233
234 if (mCoreThreadStart(&thread)) {
235 mSDLResumeAudio(&renderer->audio);
236 renderer->runloop(renderer, &thread);
237 mCoreThreadJoin(&thread);
238 } else {
239 didFail = true;
240 printf("Could not run game. Are you sure the file exists and is a compatible game?\n");
241 }
242
243#if SDL_VERSION_ATLEAST(2, 0, 0)
244 mSDLResumeScreensaver(&renderer->events);
245 mSDLSetScreensaverSuspendable(&renderer->events, false);
246#endif
247
248 if (mCoreThreadHasCrashed(&thread)) {
249 didFail = true;
250 printf("The game crashed!\n");
251 }
252 }
253 renderer->core->unloadROM(renderer->core);
254 return didFail;
255}
256
257static bool mSDLInit(struct mSDLRenderer* renderer) {
258 if (SDL_Init(SDL_INIT_VIDEO) < 0) {
259 printf("Could not initialize video: %s\n", SDL_GetError());
260 return false;
261 }
262
263 return renderer->init(renderer);
264}
265
266static void mSDLDeinit(struct mSDLRenderer* renderer) {
267 mSDLDeinitEvents(&renderer->events);
268 mSDLDeinitAudio(&renderer->audio);
269#if SDL_VERSION_ATLEAST(2, 0, 0)
270 SDL_DestroyWindow(renderer->window);
271#endif
272
273 renderer->deinit(renderer);
274
275 SDL_Quit();
276}