all repos — mgba @ 33a4c45f3ff72c83a3f68b5f4e5730764018a743

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