all repos — mgba @ 77886d4cf437044faf241c011616f2c041a648de

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