all repos — mgba @ 3157a3d9495100bec7362f69356624cfa895e9b1

mGBA Game Boy Advance Emulator

src/platform/3ds/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
  7#include "gba/renderers/video-software.h"
  8#include "gba/context/context.h"
  9#include "gba/video.h"
 10#include "util/gui.h"
 11#include "util/gui/file-select.h"
 12#include "util/gui/font.h"
 13#include "util/memory.h"
 14
 15#include "3ds-vfs.h"
 16
 17#include <3ds.h>
 18#include <sf2d.h>
 19
 20#define AUDIO_SAMPLES 0x800
 21
 22FS_archive sdmcArchive;
 23
 24struct GBA3DSRotationSource {
 25	struct GBARotationSource d;
 26	accelVector accel;
 27	angularRate gyro;
 28};
 29
 30extern bool allocateRomBuffer(void);
 31static void GBA3DSLog(struct GBAThread* thread, enum GBALogLevel level, const char* format, va_list args);
 32static struct VFile* logFile;
 33
 34static void _drawStart(void) {
 35	sf2d_start_frame(GFX_BOTTOM, GFX_LEFT);
 36}
 37static void _drawEnd(void) {
 38	sf2d_end_frame();
 39	sf2d_swapbuffers();
 40}
 41
 42static int _pollInput(void) {
 43	hidScanInput();
 44	int keys = 0;
 45	int activeKeys = hidKeysHeld();
 46	if (activeKeys & KEY_X) {
 47		keys |= 1 << GUI_INPUT_CANCEL;
 48	}
 49	if (activeKeys & KEY_B) {
 50		keys |= 1 << GUI_INPUT_BACK;
 51	}
 52	if (activeKeys & KEY_A) {
 53		keys |= 1 << GUI_INPUT_SELECT;
 54	}
 55	if (activeKeys & KEY_LEFT) {
 56		keys |= 1 << GUI_INPUT_LEFT;
 57	}
 58	if (activeKeys & KEY_RIGHT) {
 59		keys |= 1 << GUI_INPUT_RIGHT;
 60	}
 61	if (activeKeys & KEY_UP) {
 62		keys |= 1 << GUI_INPUT_UP;
 63	}
 64	if (activeKeys & KEY_DOWN) {
 65		keys |= 1 << GUI_INPUT_DOWN;
 66	}
 67	return keys;
 68}
 69
 70static void _sampleRotation(struct GBARotationSource* source) {
 71	struct GBA3DSRotationSource* rotation = (struct GBA3DSRotationSource*) source;
 72	// Work around ctrulib getting the entries wrong
 73	rotation->accel = *(accelVector*)& hidSharedMem[0x48];
 74	rotation->gyro = *(angularRate*)& hidSharedMem[0x5C];
 75}
 76
 77static int32_t _readTiltX(struct GBARotationSource* source) {
 78	struct GBA3DSRotationSource* rotation = (struct GBA3DSRotationSource*) source;
 79	return rotation->accel.x << 18L;
 80}
 81
 82static int32_t _readTiltY(struct GBARotationSource* source) {
 83	struct GBA3DSRotationSource* rotation = (struct GBA3DSRotationSource*) source;
 84	return rotation->accel.y << 18L;
 85}
 86
 87static int32_t _readGyroZ(struct GBARotationSource* source) {
 88	struct GBA3DSRotationSource* rotation = (struct GBA3DSRotationSource*) source;
 89	return rotation->gyro.y << 18L; // Yes, y
 90}
 91
 92int main() {
 93	bool hasSound = !csndInit();
 94
 95	struct GBAContext context;
 96	struct GBA3DSRotationSource rotation;
 97	rotation.d.sample = _sampleRotation;
 98	rotation.d.readTiltX = _readTiltX;
 99	rotation.d.readTiltY = _readTiltY;
100	rotation.d.readGyroZ = _readGyroZ;
101
102	if (!allocateRomBuffer()) {
103		return 1;
104	}
105
106	int16_t* audioLeft = 0;
107	int16_t* audioRight = 0;
108	size_t audioPos = 0;
109	if (hasSound) {
110		audioLeft = linearAlloc(AUDIO_SAMPLES * 2 * sizeof(int16_t));
111		audioRight = linearAlloc(AUDIO_SAMPLES * 2 * sizeof(int16_t));
112	}
113
114	sf2d_init();
115	sf2d_set_clear_color(0);
116	sf2d_texture* tex = sf2d_create_texture(256, 256, TEXFMT_RGB565, SF2D_PLACE_RAM);
117	memset(tex->data, 0, 256 * 256 * 2);
118
119	sdmcArchive = (FS_archive) {
120		ARCH_SDMC,
121		(FS_path) { PATH_EMPTY, 1, (const u8*)"" },
122		0, 0
123	};
124	FSUSER_OpenArchive(0, &sdmcArchive);
125
126	logFile = VFileOpen("/mgba.log", O_WRONLY | O_CREAT | O_TRUNC);
127	struct GUIFont* font = GUIFontCreate();
128
129	GBAContextInit(&context, 0);
130	struct GBAOptions opts = {
131		.useBios = true,
132		.logLevel = 0,
133		.idleOptimization = IDLE_LOOP_DETECT
134	};
135	GBAConfigLoadDefaults(&context.config, &opts);
136	context.gba->logHandler = GBA3DSLog;
137	context.gba->rotationSource = &rotation.d;
138
139	struct GBAVideoSoftwareRenderer renderer;
140	GBAVideoSoftwareRendererCreate(&renderer);
141	renderer.outputBuffer = anonymousMemoryMap(256 * VIDEO_VERTICAL_PIXELS * 2);
142	renderer.outputBufferStride = 256;
143	context.renderer = &renderer.d;
144
145	if (!font) {
146		goto cleanup;
147	}
148
149	struct GUIParams params = {
150		320, 240,
151		font, "/", _drawStart, _drawEnd, _pollInput, 0, 0,
152
153		GUI_PARAMS_TRAIL
154	};
155	GUIInit(&params);
156
157	while (aptMainLoop()) {
158		char path[256];
159		if (!GUISelectFile(&params, path, sizeof(path), GBAIsROM)) {
160			break;
161		}
162		_drawStart();
163		GUIFontPrintf(font, 160, (GUIFontHeight(font) + 240) / 2, GUI_TEXT_CENTER, 0xFFFFFFFF, "Loading...");
164		_drawEnd();
165		if (!GBAContextLoadROM(&context, path, true)) {
166			continue;
167		}
168		if (!GBAContextStart(&context)) {
169			continue;
170		}
171
172		if (context.gba->memory.hw.devices & HW_TILT) {
173			HIDUSER_EnableAccelerometer();
174		}
175		if (context.gba->memory.hw.devices & HW_GYRO) {
176			HIDUSER_EnableGyroscope();
177		}
178
179#if RESAMPLE_LIBRARY == RESAMPLE_BLIP_BUF
180		blip_set_rates(context.gba->audio.left,  GBA_ARM7TDMI_FREQUENCY, 0x8000);
181		blip_set_rates(context.gba->audio.right, GBA_ARM7TDMI_FREQUENCY, 0x8000);
182#endif
183		memset(audioLeft, 0, AUDIO_SAMPLES * 2 * sizeof(int16_t));
184		memset(audioRight, 0, AUDIO_SAMPLES * 2 * sizeof(int16_t));
185
186		while (aptMainLoop()) {
187			hidScanInput();
188			uint32_t activeKeys = hidKeysHeld() & 0xF00003FF;
189			activeKeys |= activeKeys >> 24;
190			if (hidKeysDown() & KEY_X) {
191				break;
192			}
193			GBAContextFrame(&context, activeKeys);
194			GX_SetDisplayTransfer(0, renderer.outputBuffer, GX_BUFFER_DIM(256, VIDEO_VERTICAL_PIXELS), tex->data, GX_BUFFER_DIM(256, VIDEO_VERTICAL_PIXELS), 0x000002202);
195			GSPGPU_FlushDataCache(0, tex->data, 256 * VIDEO_VERTICAL_PIXELS * 2);
196#if RESAMPLE_LIBRARY == RESAMPLE_BLIP_BUF
197			if (hasSound) {
198				memset(&audioLeft[audioPos], 0, AUDIO_SAMPLES);
199				memset(&audioRight[audioPos], 0, AUDIO_SAMPLES);
200				size_t samples = blip_read_samples(context.gba->audio.left, &audioLeft[audioPos], AUDIO_SAMPLES, false);
201				blip_read_samples(context.gba->audio.right, &audioRight[audioPos], AUDIO_SAMPLES, false);
202				size_t audioPosNew = (audioPos + AUDIO_SAMPLES) % (AUDIO_SAMPLES * 2);
203				GSPGPU_FlushDataCache(0, (void*) audioLeft, AUDIO_SAMPLES * 2 * sizeof(int16_t));
204				GSPGPU_FlushDataCache(0, (void*) audioRight, AUDIO_SAMPLES * 2 * sizeof(int16_t));
205				csndPlaySound(0x8, SOUND_ONE_SHOT | SOUND_FORMAT_16BIT, 0x8000, 1.0, -1.0, &audioLeft[audioPos], &audioLeft[audioPosNew], samples * 2);
206				csndPlaySound(0x9, SOUND_ONE_SHOT | SOUND_FORMAT_16BIT, 0x8000, 1.0, 1.0, &audioRight[audioPos], &audioLeft[audioPosNew], samples * 2);
207				audioPos = audioPosNew;
208			} else {
209				blip_clear(context.gba->audio.left);
210				blip_clear(context.gba->audio.right);
211			}
212#endif
213			gspWaitForPPF();
214			_drawStart();
215			sf2d_draw_texture_scale(tex, 40, 296, 1, -1);
216			_drawEnd();
217		}
218		GBAContextStop(&context);
219
220		CSND_SetPlayState(8, 0);
221		CSND_SetPlayState(9, 0);
222		csndExecCmds(0);
223
224		if (context.gba->memory.hw.devices & HW_TILT) {
225			HIDUSER_DisableAccelerometer();
226		}
227		if (context.gba->memory.hw.devices & HW_GYRO) {
228			HIDUSER_DisableGyroscope();
229		}
230	}
231	GBAContextDeinit(&context);
232
233cleanup:
234	mappedMemoryFree(renderer.outputBuffer, 0);
235
236	if (logFile) {
237		logFile->close(logFile);
238	}
239
240	sf2d_free_texture(tex);
241	sf2d_fini();
242
243	if (hasSound) {
244		linearFree(audioLeft);
245		linearFree(audioRight);
246	}
247	csndExit();
248	return 0;
249}
250
251static void GBA3DSLog(struct GBAThread* thread, enum GBALogLevel level, const char* format, va_list args) {
252	UNUSED(thread);
253	UNUSED(level);
254	if (!logFile) {
255		return;
256	}
257	char out[256];
258	size_t len = vsnprintf(out, sizeof(out), format, args);
259	if (len >= sizeof(out)) {
260		len = sizeof(out) - 1;
261	}
262	out[len] = '\n';
263	logFile->write(logFile, out, len + 1);
264}