all repos — mgba @ 192f85259aa0f0e2c0eff2f48bd091f66ed15518

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