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/video.h"
25#endif
26#ifdef M_CORE_GB
27#include "gb/core.h"
28#include "gb/gb.h"
29#include "gb/video.h"
30#endif
31#include "feature/commandline.h"
32#include "util/configuration.h"
33#include "util/vfs.h"
34
35#include <SDL.h>
36
37#include <errno.h>
38#include <signal.h>
39#include <sys/time.h>
40
41#define PORT "sdl"
42
43static bool mSDLInit(struct mSDLRenderer* renderer);
44static void mSDLDeinit(struct mSDLRenderer* renderer);
45
46static int mSDLRun(struct mSDLRenderer* renderer, struct mArguments* args);
47
48int main(int argc, char** argv) {
49 struct mSDLRenderer renderer = {};
50
51 struct mCoreOptions opts = {
52 .useBios = true,
53 .rewindEnable = true,
54 .audioBuffers = 512,
55 .videoSync = false,
56 .audioSync = true,
57 .volume = 0x100,
58 };
59
60 struct mArguments args;
61 struct mGraphicsOpts graphicsOpts;
62
63 struct mSubParser subparser;
64
65 initParserForGraphics(&subparser, &graphicsOpts);
66 bool parsed = parseArguments(&args, argc, argv, &subparser);
67 if (!args.fname) {
68 parsed = false;
69 }
70 if (!parsed || args.showHelp) {
71 usage(argv[0], subparser.usage);
72 freeArguments(&args);
73 return !parsed;
74 }
75 if (args.showVersion) {
76 version(argv[0]);
77 freeArguments(&args);
78 return 0;
79 }
80
81 renderer.core = mCoreFind(args.fname);
82 if (!renderer.core) {
83 printf("Could not run game. Are you sure the file exists and is a compatible game?\n");
84 freeArguments(&args);
85 return 1;
86 }
87 renderer.core->desiredVideoDimensions(renderer.core, &renderer.width, &renderer.height);
88#ifdef BUILD_GL
89 mSDLGLCreate(&renderer);
90#elif defined(BUILD_GLES2) || defined(USE_EPOXY)
91 mSDLGLES2Create(&renderer);
92#else
93 mSDLSWCreate(&renderer);
94#endif
95
96 renderer.ratio = graphicsOpts.multiplier;
97 if (renderer.ratio == 0) {
98 renderer.ratio = 1;
99 }
100 opts.width = renderer.width * renderer.ratio;
101 opts.height = renderer.height * renderer.ratio;
102
103 if (!renderer.core->init(renderer.core)) {
104 freeArguments(&args);
105 return 1;
106 }
107
108 mInputMapInit(&renderer.core->inputMap, &GBAInputInfo);
109 mCoreInitConfig(renderer.core, PORT);
110 applyArguments(&args, &subparser, &renderer.core->config);
111
112 mCoreConfigLoadDefaults(&renderer.core->config, &opts);
113 mCoreLoadConfig(renderer.core);
114
115 renderer.viewportWidth = renderer.core->opts.width;
116 renderer.viewportHeight = renderer.core->opts.height;
117#if SDL_VERSION_ATLEAST(2, 0, 0)
118 renderer.player.fullscreen = renderer.core->opts.fullscreen;
119 renderer.player.windowUpdated = 0;
120#else
121 renderer.fullscreen = renderer.core->opts.fullscreen;
122#endif
123
124 renderer.lockAspectRatio = renderer.core->opts.lockAspectRatio;
125 renderer.filter = renderer.core->opts.resampleVideo;
126
127 if (!mSDLInit(&renderer)) {
128 freeArguments(&args);
129 renderer.core->deinit(renderer.core);
130 return 1;
131 }
132
133 renderer.player.bindings = &renderer.core->inputMap;
134 mSDLInitBindingsGBA(&renderer.core->inputMap);
135 mSDLInitEvents(&renderer.events);
136 mSDLEventsLoadConfig(&renderer.events, mCoreConfigGetInput(&renderer.core->config));
137 mSDLAttachPlayer(&renderer.events, &renderer.player);
138 mSDLPlayerLoadConfig(&renderer.player, mCoreConfigGetInput(&renderer.core->config));
139
140 int ret;
141
142 // TODO: Use opts and config
143 ret = mSDLRun(&renderer, &args);
144 mSDLDetachPlayer(&renderer.events, &renderer.player);
145 mInputMapDeinit(&renderer.core->inputMap);
146
147 mSDLDeinit(&renderer);
148
149 freeArguments(&args);
150 mCoreConfigFreeOpts(&opts);
151 mCoreConfigDeinit(&renderer.core->config);
152 renderer.core->deinit(renderer.core);
153
154 return ret;
155}
156
157int mSDLRun(struct mSDLRenderer* renderer, struct mArguments* args) {
158 struct mCoreThread thread = {
159 .core = renderer->core
160 };
161 if (!mCoreLoadFile(renderer->core, args->fname)) {
162 return 1;
163 }
164 mCoreAutoloadSave(renderer->core);
165 struct mDebugger* debugger = mDebuggerCreate(args->debuggerType, renderer->core);
166 if (debugger) {
167 mDebuggerAttach(debugger, renderer->core);
168 mDebuggerEnter(debugger, DEBUGGER_ENTER_MANUAL, NULL);
169 }
170
171 if (args->patch) {
172 struct VFile* patch = VFileOpen(args->patch, O_RDONLY);
173 if (patch) {
174 renderer->core->loadPatch(renderer->core, patch);
175 }
176 } else {
177 mCoreAutoloadPatch(renderer->core);
178 }
179
180 renderer->audio.samples = renderer->core->opts.audioBuffers;
181 renderer->audio.sampleRate = 44100;
182
183 bool didFail = !mSDLInitAudio(&renderer->audio, &thread);
184 if (!didFail) {
185#if SDL_VERSION_ATLEAST(2, 0, 0)
186 mSDLSetScreensaverSuspendable(&renderer->events, renderer->core->opts.suspendScreensaver);
187 mSDLSuspendScreensaver(&renderer->events);
188#endif
189 if (mCoreThreadStart(&thread)) {
190 renderer->runloop(renderer, &thread);
191 mSDLPauseAudio(&renderer->audio);
192 mCoreThreadJoin(&thread);
193 } else {
194 didFail = true;
195 printf("Could not run game. Are you sure the file exists and is a compatible game?\n");
196 }
197
198#if SDL_VERSION_ATLEAST(2, 0, 0)
199 mSDLResumeScreensaver(&renderer->events);
200 mSDLSetScreensaverSuspendable(&renderer->events, false);
201#endif
202
203 if (mCoreThreadHasCrashed(&thread)) {
204 didFail = true;
205 printf("The game crashed!\n");
206 }
207 }
208 renderer->core->unloadROM(renderer->core);
209 return didFail;
210}
211
212static bool mSDLInit(struct mSDLRenderer* renderer) {
213 if (SDL_Init(SDL_INIT_VIDEO) < 0) {
214 printf("Could not initialize video: %s\n", SDL_GetError());
215 return false;
216 }
217
218 return renderer->init(renderer);
219}
220
221static void mSDLDeinit(struct mSDLRenderer* renderer) {
222 mSDLDeinitEvents(&renderer->events);
223 mSDLDeinitAudio(&renderer->audio);
224#if SDL_VERSION_ATLEAST(2, 0, 0)
225 SDL_DestroyWindow(renderer->window);
226#endif
227
228 renderer->deinit(renderer);
229
230 SDL_Quit();
231}