all repos — mgba @ bc1a094bead8ffa1d9d03beebfa8676511d7ac0b

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#ifdef M_CORE_GBA
 19#include "gba/gba.h"
 20#include "gba/supervisor/thread.h"
 21#include "gba/video.h"
 22#endif
 23#ifdef M_CORE_GB
 24#include "gb/core.h"
 25#include "gb/gb.h"
 26#include "gb/video.h"
 27#endif
 28#include "platform/commandline.h"
 29#include "util/configuration.h"
 30#include "util/vfs.h"
 31
 32#include <SDL.h>
 33
 34#include <errno.h>
 35#include <signal.h>
 36#include <sys/time.h>
 37
 38#define PORT "sdl"
 39
 40// TODO: Move somewhere
 41enum mPlatform {
 42	PLATFORM_NONE = -1,
 43	PLATFORM_GBA,
 44	PLATFORM_GB
 45};
 46
 47static bool mSDLInit(struct mSDLRenderer* renderer);
 48static void mSDLDeinit(struct mSDLRenderer* renderer);
 49
 50// TODO: Clean up signatures
 51#ifdef M_CORE_GBA
 52static int mSDLRunGBA(struct mSDLRenderer* renderer, struct GBAArguments* args, struct GBAOptions* opts, struct mCoreConfig* config);
 53#endif
 54#ifdef M_CORE_GB
 55static int mSDLRunGB(struct mSDLRenderer* renderer, struct GBAArguments* args);
 56#endif
 57
 58
 59int main(int argc, char** argv) {
 60	struct mSDLRenderer renderer = {};
 61
 62	struct GBAInputMap inputMap;
 63	GBAInputMapInit(&inputMap);
 64
 65	struct mCoreConfig config;
 66	mCoreConfigInit(&config, PORT);
 67	mCoreConfigLoad(&config);
 68
 69	struct GBAOptions opts = {
 70		.width = 0,
 71		.height = 0,
 72		.useBios = true,
 73		.rewindEnable = true,
 74		.audioBuffers = 512,
 75		.videoSync = false,
 76		.audioSync = true,
 77	};
 78
 79	struct GBAArguments args;
 80	struct GraphicsOpts graphicsOpts;
 81
 82	struct SubParser subparser;
 83
 84	initParserForGraphics(&subparser, &graphicsOpts);
 85	bool parsed = parseArguments(&args, &config, argc, argv, &subparser);
 86	if (!parsed || args.showHelp) {
 87		usage(argv[0], subparser.usage);
 88		freeArguments(&args);
 89		mCoreConfigFreeOpts(&opts);
 90		mCoreConfigDeinit(&config);
 91		return !parsed;
 92	}
 93	if (args.showVersion) {
 94		version(argv[0]);
 95		freeArguments(&args);
 96		mCoreConfigFreeOpts(&opts);
 97		mCoreConfigDeinit(&config);
 98		return 0;
 99	}
100
101	enum mPlatform platform = PLATFORM_NONE;
102
103	if (args.fname) {
104		struct VFile* vf = VFileOpen(args.fname, O_RDONLY);
105		if (!vf) {
106			printf("Could not open game. Are you sure the file exists?\n");
107			freeArguments(&args);
108			mCoreConfigFreeOpts(&opts);
109			mCoreConfigDeinit(&config);
110			return 1;
111		}
112#ifdef M_CORE_GBA
113		else if (GBAIsROM(vf)) {
114			platform = PLATFORM_GBA;
115			if (!opts.width) {
116				opts.width = VIDEO_HORIZONTAL_PIXELS;
117			}
118			if (!opts.height) {
119				opts.height = VIDEO_VERTICAL_PIXELS;
120			}
121			GBAVideoSoftwareRendererCreate(&renderer.d);
122#ifdef BUILD_GL
123			mSDLGLCreate(&renderer);
124#elif defined(BUILD_GLES2) || defined(USE_EPOXY)
125			mSDLGLES2Create(&renderer);
126#else
127			mSDLSWCreate(&renderer);
128#endif
129		}
130#endif
131#ifdef M_CORE_GB
132		else if (GBIsROM(vf)) {
133			platform = PLATFORM_GB;
134			if (!opts.width) {
135				opts.width = /*GB_*/VIDEO_HORIZONTAL_PIXELS;
136			}
137			if (!opts.height) {
138				opts.height = /*GB_*/VIDEO_VERTICAL_PIXELS;
139			}
140			renderer.core = GBCoreCreate();
141#ifdef BUILD_GL
142			mSDLGLCreateGB(&renderer);
143#elif defined(BUILD_GLES2) || defined(USE_EPOXY)
144			mSDLGLES2CreateGB(&renderer);
145#else
146			mSDLSWCreateGB(&renderer);
147#endif
148		}
149#endif
150		else {
151			printf("Could not run game. Are you sure the file exists and is a Game Boy Advance game?\n");
152			freeArguments(&args);
153			mCoreConfigFreeOpts(&opts);
154			mCoreConfigDeinit(&config);
155			return 1;
156		}
157	}
158
159	mCoreConfigLoadDefaults(&config, &opts);
160	mCoreConfigMap(&config, &opts);
161
162	renderer.viewportWidth = opts.width;
163	renderer.viewportHeight = opts.height;
164#if SDL_VERSION_ATLEAST(2, 0, 0)
165	renderer.player.fullscreen = opts.fullscreen;
166	renderer.player.windowUpdated = 0;
167#else
168	renderer.fullscreen = opts.fullscreen;
169#endif
170	renderer.ratio = graphicsOpts.multiplier;
171	if (renderer.ratio == 0) {
172		renderer.ratio = 1;
173	}
174
175	renderer.lockAspectRatio = opts.lockAspectRatio;
176	renderer.filter = opts.resampleVideo;
177
178	if (!mSDLInit(&renderer)) {
179		freeArguments(&args);
180		mCoreConfigFreeOpts(&opts);
181		mCoreConfigDeinit(&config);
182		return 1;
183	}
184
185	if (renderer.core) {
186		// TODO: Check return code
187		renderer.core->init(renderer.core);
188	}
189
190	renderer.player.bindings = &inputMap;
191	GBASDLInitBindings(&inputMap);
192	GBASDLInitEvents(&renderer.events);
193	GBASDLEventsLoadConfig(&renderer.events, mCoreConfigGetInput(&config));
194	GBASDLAttachPlayer(&renderer.events, &renderer.player);
195	GBASDLPlayerLoadConfig(&renderer.player, mCoreConfigGetInput(&config));
196
197	int ret;
198
199	switch (platform) {
200	case PLATFORM_GBA:
201		ret = mSDLRunGBA(&renderer, &args, &opts, &config);
202		break;
203	case PLATFORM_GB:
204		ret = mSDLRunGB(&renderer, &args);
205		break;
206	default:
207		ret = 1;
208		break;
209	}
210	GBASDLDetachPlayer(&renderer.events, &renderer.player);
211	GBAInputMapDeinit(&inputMap);
212
213	mSDLDeinit(&renderer);
214
215	freeArguments(&args);
216	mCoreConfigFreeOpts(&opts);
217	mCoreConfigDeinit(&config);
218
219	return ret;
220}
221
222#ifdef M_CORE_GBA
223int mSDLRunGBA(struct mSDLRenderer* renderer, struct GBAArguments* args, struct GBAOptions* opts, struct mCoreConfig* config) {
224	struct GBAThread context = {
225		.renderer = &renderer->d.d,
226		.userData = renderer
227	};
228
229	context.debugger = createDebugger(args, &context);
230
231	GBAMapOptionsToContext(opts, &context);
232	GBAMapArgumentsToContext(args, &context);
233	context.overrides = mCoreConfigGetOverrides(config);
234
235	bool didFail = false;
236
237	renderer->audio.samples = context.audioBuffers;
238	renderer->audio.sampleRate = 44100;
239	if (opts->sampleRate) {
240		renderer->audio.sampleRate = opts->sampleRate;
241	}
242	if (!GBASDLInitAudio(&renderer->audio, &context)) {
243		didFail = true;
244	}
245
246	if (!didFail) {
247#if SDL_VERSION_ATLEAST(2, 0, 0)
248		GBASDLSetScreensaverSuspendable(&renderer->events, opts->suspendScreensaver);
249		GBASDLSuspendScreensaver(&renderer->events);
250#endif
251		if (GBAThreadStart(&context)) {
252			renderer->runloop(renderer, &context);
253			GBAThreadJoin(&context);
254		} else {
255			didFail = true;
256			printf("Could not run game. Are you sure the file exists and is a Game Boy Advance game?\n");
257		}
258
259#if SDL_VERSION_ATLEAST(2, 0, 0)
260		GBASDLResumeScreensaver(&renderer->events);
261		GBASDLSetScreensaverSuspendable(&renderer->events, false);
262#endif
263
264		if (GBAThreadHasCrashed(&context)) {
265			didFail = true;
266			printf("The game crashed!\n");
267		}
268	}
269	free(context.debugger);
270	mDirectorySetDeinit(&context.dirs);
271
272	return didFail;
273}
274#endif
275
276#ifdef M_CORE_GB
277int mSDLRunGB(struct mSDLRenderer* renderer, struct GBAArguments* args) {
278	struct VFile* vf = VFileOpen(args->fname, O_RDONLY);
279	struct VFile* savVf = 0;
280
281	{
282		char savepath[PATH_MAX];
283		char dirname[PATH_MAX];
284		char basename[PATH_MAX];
285		separatePath(args->fname, dirname, basename, 0);
286		snprintf(savepath, sizeof(savepath), "%s" PATH_SEP "%s.sav", dirname, basename);
287		savVf = VFileOpen(savepath, O_RDWR | O_CREAT);
288	}
289
290	renderer->core->loadROM(renderer->core, vf, savVf, args->fname);
291	renderer->core->reset(renderer->core);
292	renderer->runloop(renderer, NULL);
293	renderer->core->unloadROM(renderer->core);
294	vf->close(vf);
295	return 0;
296}
297#endif
298
299static bool mSDLInit(struct mSDLRenderer* renderer) {
300	if (SDL_Init(SDL_INIT_VIDEO) < 0) {
301		printf("Could not initialize video: %s\n", SDL_GetError());
302		return false;
303	}
304
305	return renderer->init(renderer);
306}
307
308static void mSDLDeinit(struct mSDLRenderer* renderer) {
309	GBASDLDeinitEvents(&renderer->events);
310	GBASDLDeinitAudio(&renderer->audio);
311#if SDL_VERSION_ATLEAST(2, 0, 0)
312	SDL_DestroyWindow(renderer->window);
313#endif
314
315	renderer->deinit(renderer);
316
317	SDL_Quit();
318}