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