all repos — mgba @ b325376f05945b2ca3d00b30e57de9187c5b0ef4

mGBA Game Boy Advance Emulator

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}