all repos — mgba @ c6dd9b6e6498059f28b8a5847ddb8826d07f782d

mGBA Game Boy Advance Emulator

src/platform/wii/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#define asm __asm__
  7
  8#include <fat.h>
  9#include <gccore.h>
 10#include <ogc/machine/processor.h>
 11#include <malloc.h>
 12#include <unistd.h>
 13#include <wiiuse/wpad.h>
 14
 15#include "util/common.h"
 16
 17#include "core/core.h"
 18#include "feature/gui/gui-runner.h"
 19#include "gba/audio.h"
 20#include "gba/gba.h"
 21#include "gba/input.h"
 22#include "util/gui.h"
 23#include "util/gui/file-select.h"
 24#include "util/gui/font.h"
 25#include "util/gui/menu.h"
 26#include "util/vfs.h"
 27
 28#define GCN1_INPUT 0x47434E31
 29#define GCN2_INPUT 0x47434E32
 30#define WIIMOTE_INPUT 0x5749494D
 31#define CLASSIC_INPUT 0x57494943
 32
 33static void _mapKey(struct mInputMap* map, uint32_t binding, int nativeKey, enum GBAKey key) {
 34	mInputBindKey(map, binding, __builtin_ctz(nativeKey), key);
 35}
 36
 37static enum ScreenMode {
 38	SM_PA,
 39	SM_SF,
 40	SM_MAX
 41} screenMode = SM_PA;
 42
 43enum FilterMode {
 44	FM_NEAREST,
 45	FM_LINEAR,
 46	FM_MAX
 47} filterMode = FM_NEAREST;
 48
 49#define SAMPLES 1024
 50#define GUI_SCALE 1.35
 51
 52static void _retraceCallback(u32 count);
 53
 54static void _audioDMA(void);
 55static void _setRumble(struct mRumble* rumble, int enable);
 56static void _sampleRotation(struct mRotationSource* source);
 57static int32_t _readTiltX(struct mRotationSource* source);
 58static int32_t _readTiltY(struct mRotationSource* source);
 59static int32_t _readGyroZ(struct mRotationSource* source);
 60
 61static void _drawStart(void);
 62static void _drawEnd(void);
 63static uint32_t _pollInput(const struct mInputMap*);
 64static enum GUICursorState _pollCursor(unsigned* x, unsigned* y);
 65static void _guiPrepare(void);
 66static void _guiFinish(void);
 67
 68static void _setup(struct mGUIRunner* runner);
 69static void _gameLoaded(struct mGUIRunner* runner);
 70static void _gameUnloaded(struct mGUIRunner* runner);
 71static void _unpaused(struct mGUIRunner* runner);
 72static void _drawFrame(struct mGUIRunner* runner, bool faded);
 73static uint16_t _pollGameInput(struct mGUIRunner* runner);
 74static void _incrementScreenMode(struct mGUIRunner* runner);
 75
 76static s8 WPAD_StickX(u8 chan, u8 right);
 77static s8 WPAD_StickY(u8 chan, u8 right);
 78
 79static void* outputBuffer;
 80static struct mRumble rumble;
 81static struct mRotationSource rotation;
 82static GXRModeObj* vmode;
 83static Mtx model, view, modelview;
 84static uint16_t* texmem;
 85static GXTexObj tex;
 86static int32_t tiltX;
 87static int32_t tiltY;
 88static int32_t gyroZ;
 89static uint32_t retraceCount;
 90static uint32_t referenceRetraceCount;
 91static int scaleFactor;
 92static unsigned corew, coreh;
 93
 94static void* framebuffer[2] = { 0, 0 };
 95static int whichFb = 0;
 96
 97static struct GBAStereoSample audioBuffer[3][SAMPLES] __attribute__((__aligned__(32)));
 98static volatile size_t audioBufferSize = 0;
 99static volatile int currentAudioBuffer = 0;
100
101static struct GUIFont* font;
102
103static void reconfigureScreen(struct mCore* core, GXRModeObj* vmode) {
104	free(framebuffer[0]);
105	free(framebuffer[1]);
106
107	framebuffer[0] = SYS_AllocateFramebuffer(vmode);
108	framebuffer[1] = SYS_AllocateFramebuffer(vmode);
109
110	VIDEO_SetBlack(true);
111	VIDEO_Configure(vmode);
112	VIDEO_SetNextFramebuffer(framebuffer[whichFb]);
113	VIDEO_SetBlack(false);
114	VIDEO_Flush();
115	VIDEO_WaitVSync();
116	if (vmode->viTVMode & VI_NON_INTERLACE) {
117		VIDEO_WaitVSync();
118	}
119	GX_SetViewport(0, 0, vmode->fbWidth, vmode->efbHeight, 0, 1);
120
121	f32 yscale = GX_GetYScaleFactor(vmode->efbHeight, vmode->xfbHeight);
122	u32 xfbHeight = GX_SetDispCopyYScale(yscale);
123	GX_SetScissor(0, 0, vmode->viWidth, vmode->viWidth);
124	GX_SetDispCopySrc(0, 0, vmode->fbWidth, vmode->efbHeight);
125	GX_SetDispCopyDst(vmode->fbWidth, xfbHeight);
126	GX_SetCopyFilter(vmode->aa, vmode->sample_pattern, GX_TRUE, vmode->vfilter);
127	GX_SetFieldMode(vmode->field_rendering, ((vmode->viHeight == 2 * vmode->xfbHeight) ? GX_ENABLE : GX_DISABLE));
128
129	if (core) {
130		core->desiredVideoDimensions(core, &corew, &coreh);
131		int hfactor = vmode->fbWidth / corew;
132		int vfactor = vmode->efbHeight / coreh;
133		if (hfactor > vfactor) {
134			scaleFactor = vfactor;
135		} else {
136			scaleFactor = hfactor;
137		}
138	}
139};
140
141int main(int argc, char* argv[]) {
142	VIDEO_Init();
143	PAD_Init();
144	WPAD_Init();
145	WPAD_SetDataFormat(0, WPAD_FMT_BTNS_ACC_IR);
146	AUDIO_Init(0);
147	AUDIO_SetDSPSampleRate(AI_SAMPLERATE_48KHZ);
148	AUDIO_RegisterDMACallback(_audioDMA);
149
150	memset(audioBuffer, 0, sizeof(audioBuffer));
151
152#if !defined(COLOR_16_BIT) && !defined(COLOR_5_6_5)
153#error This pixel format is unsupported. Please use -DCOLOR_16-BIT -DCOLOR_5_6_5
154#endif
155
156	vmode = VIDEO_GetPreferredMode(0);
157
158	GXColor bg = { 0, 0, 0, 0xFF };
159	void* fifo = memalign(32, 0x40000);
160	memset(fifo, 0, 0x40000);
161	GX_Init(fifo, 0x40000);
162	GX_SetCopyClear(bg, 0x00FFFFFF);
163
164	reconfigureScreen(NULL, vmode);
165
166	GX_SetCullMode(GX_CULL_NONE);
167	GX_SetDispCopyGamma(GX_GM_1_0);
168
169	GX_ClearVtxDesc();
170	GX_SetVtxDesc(GX_VA_POS, GX_DIRECT);
171	GX_SetVtxDesc(GX_VA_TEX0, GX_DIRECT);
172	GX_SetVtxDesc(GX_VA_CLR0, GX_DIRECT);
173
174	GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_POS, GX_POS_XY, GX_S16, 0);
175	GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_TEX0, GX_TEX_ST, GX_S16, 0);
176	GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_CLR0, GX_CLR_RGBA, GX_RGBA8, 0);
177
178	GX_SetNumChans(1);
179	GX_SetNumTexGens(1);
180	GX_SetTevOrder(GX_TEVSTAGE0, GX_TEXCOORD0, GX_TEXMAP0, GX_COLOR0A0);
181	GX_SetTevOp(GX_TEVSTAGE0, GX_MODULATE);
182
183	GX_SetTexCoordGen(GX_TEXCOORD0, GX_TG_MTX2x4, GX_TG_TEX0, GX_IDENTITY);
184	GX_InvVtxCache();
185	GX_InvalidateTexAll();
186
187	guVector cam = { 0.0f, 0.0f, 0.0f };
188	guVector up = { 0.0f, 1.0f, 0.0f };
189	guVector look = { 0.0f, 0.0f, -1.0f };
190	guLookAt(view, &cam, &up, &look);
191
192	guMtxIdentity(model);
193	guMtxTransApply(model, model, 0.0f, 0.0f, -6.0f);
194	guMtxConcat(view, model, modelview);
195	GX_LoadPosMtxImm(modelview, GX_PNMTX0);
196
197	texmem = memalign(32, 256 * 256 * BYTES_PER_PIXEL);
198	memset(texmem, 0, 256 * 256 * BYTES_PER_PIXEL);
199	GX_InitTexObj(&tex, texmem, 256, 256, GX_TF_RGB565, GX_CLAMP, GX_CLAMP, GX_FALSE);
200
201	VIDEO_SetPostRetraceCallback(_retraceCallback);
202
203	font = GUIFontCreate();
204
205	fatInitDefault();
206
207	rumble.setRumble = _setRumble;
208
209	rotation.sample = _sampleRotation;
210	rotation.readTiltX = _readTiltX;
211	rotation.readTiltY = _readTiltY;
212	rotation.readGyroZ = _readGyroZ;
213
214	struct mGUIRunner runner = {
215		.params = {
216			vmode->fbWidth * GUI_SCALE, vmode->efbHeight * GUI_SCALE,
217			font, "",
218			_drawStart, _drawEnd,
219			_pollInput, _pollCursor,
220			0,
221			_guiPrepare, _guiFinish,
222
223			GUI_PARAMS_TRAIL
224		},
225		.keySources = (struct GUIInputKeys[]) {
226			{
227				.name = "GameCube Input (1)",
228				.id = GCN1_INPUT,
229				.keyNames = (const char*[]) {
230					"D-Pad Left",
231					"D-Pad Right",
232					"D-Pad Down",
233					"D-Pad Up",
234					"Z",
235					"R",
236					"L",
237					0,
238					"A",
239					"B",
240					"X",
241					"Y",
242					"Start"
243				},
244				.nKeys = 13
245			},
246			{
247				.name = "GameCube Input (2)",
248				.id = GCN2_INPUT,
249				.keyNames = (const char*[]) {
250					"D-Pad Left",
251					"D-Pad Right",
252					"D-Pad Down",
253					"D-Pad Up",
254					"Z",
255					"R",
256					"L",
257					0,
258					"A",
259					"B",
260					"X",
261					"Y",
262					"Start"
263				},
264				.nKeys = 13
265			},
266			{
267				.name = "Wii Remote Input",
268				.id = WIIMOTE_INPUT,
269				.keyNames = (const char*[]) {
270					"2",
271					"1",
272					"B",
273					"A",
274					"-",
275					0,
276					0,
277					"\1\xE",
278					"Left",
279					"Right",
280					"Down",
281					"Up",
282					"+",
283					0,
284					0,
285					0,
286					"Z",
287					"C",
288				},
289				.nKeys = 18
290			},
291			{
292				.name = "Classic Controller Input",
293				.id = CLASSIC_INPUT,
294				.keyNames = (const char*[]) {
295					0,
296					0,
297					0,
298					0,
299					0,
300					0,
301					0,
302					0,
303					0,
304					0,
305					0,
306					0,
307					0,
308					0,
309					0,
310					0,
311					"Up",
312					"Left",
313					"ZR",
314					"X",
315					"A",
316					"Y",
317					"B",
318					"ZL",
319					0,
320					"R",
321					"+",
322					"\1\xE",
323					"-",
324					"L",
325					"Down",
326					"Right",
327				},
328				.nKeys = 32
329			},
330			{ .id = 0 }
331		},
332		.configExtra = (struct GUIMenuItem[]) {
333			{
334				.title = "Screen mode",
335				.data = "screenMode",
336				.submenu = 0,
337				.state = 0,
338				.validStates = (const char*[]) {
339					"Pixel-Accurate",
340					"Stretched",
341				},
342				.nStates = 2
343			},
344			{
345				.title = "Filtering",
346				.data = "filter",
347				.submenu = 0,
348				.state = 0,
349				.validStates = (const char*[]) {
350					"Pixelated",
351					"Resampled",
352				},
353				.nStates = 2
354			}
355		},
356		.nConfigExtra = 2,
357		.setup = _setup,
358		.teardown = 0,
359		.gameLoaded = _gameLoaded,
360		.gameUnloaded = _gameUnloaded,
361		.prepareForFrame = 0,
362		.drawFrame = _drawFrame,
363		.paused = _gameUnloaded,
364		.unpaused = _unpaused,
365		.incrementScreenMode = _incrementScreenMode,
366		.pollGameInput = _pollGameInput
367	};
368	mGUIInit(&runner, "wii");
369
370	_mapKey(&runner.params.keyMap, GCN1_INPUT, PAD_BUTTON_A, GUI_INPUT_SELECT);
371	_mapKey(&runner.params.keyMap, GCN1_INPUT, PAD_BUTTON_B, GUI_INPUT_BACK);
372	_mapKey(&runner.params.keyMap, GCN1_INPUT, PAD_TRIGGER_Z, GUI_INPUT_CANCEL);
373	_mapKey(&runner.params.keyMap, GCN1_INPUT, PAD_BUTTON_UP, GUI_INPUT_UP);
374	_mapKey(&runner.params.keyMap, GCN1_INPUT, PAD_BUTTON_DOWN, GUI_INPUT_DOWN);
375	_mapKey(&runner.params.keyMap, GCN1_INPUT, PAD_BUTTON_LEFT, GUI_INPUT_LEFT);
376	_mapKey(&runner.params.keyMap, GCN1_INPUT, PAD_BUTTON_RIGHT, GUI_INPUT_RIGHT);
377
378	_mapKey(&runner.params.keyMap, WIIMOTE_INPUT, WPAD_BUTTON_2, GUI_INPUT_SELECT);
379	_mapKey(&runner.params.keyMap, WIIMOTE_INPUT, WPAD_BUTTON_1, GUI_INPUT_BACK);
380	_mapKey(&runner.params.keyMap, WIIMOTE_INPUT, WPAD_BUTTON_HOME, GUI_INPUT_CANCEL);
381	_mapKey(&runner.params.keyMap, WIIMOTE_INPUT, WPAD_BUTTON_RIGHT, GUI_INPUT_UP);
382	_mapKey(&runner.params.keyMap, WIIMOTE_INPUT, WPAD_BUTTON_LEFT, GUI_INPUT_DOWN);
383	_mapKey(&runner.params.keyMap, WIIMOTE_INPUT, WPAD_BUTTON_UP, GUI_INPUT_LEFT);
384	_mapKey(&runner.params.keyMap, WIIMOTE_INPUT, WPAD_BUTTON_DOWN, GUI_INPUT_RIGHT);
385
386	_mapKey(&runner.params.keyMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_A, GUI_INPUT_SELECT);
387	_mapKey(&runner.params.keyMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_Y, GUI_INPUT_SELECT);
388	_mapKey(&runner.params.keyMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_B, GUI_INPUT_BACK);
389	_mapKey(&runner.params.keyMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_X, GUI_INPUT_BACK);
390	_mapKey(&runner.params.keyMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_HOME, GUI_INPUT_CANCEL);
391	_mapKey(&runner.params.keyMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_UP, GUI_INPUT_UP);
392	_mapKey(&runner.params.keyMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_DOWN, GUI_INPUT_DOWN);
393	_mapKey(&runner.params.keyMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_LEFT, GUI_INPUT_LEFT);
394	_mapKey(&runner.params.keyMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_RIGHT, GUI_INPUT_RIGHT);
395
396	if (argc > 1) {
397		size_t i;
398		for (i = 0; runner.keySources[i].id; ++i) {
399			mInputMapLoad(&runner.params.keyMap, runner.keySources[i].id, mCoreConfigGetInput(&runner.config));
400		}
401		mGUIRun(&runner, argv[1]);
402	} else {
403		mGUIRunloop(&runner);
404	}
405	mGUIDeinit(&runner);
406
407	free(fifo);
408
409	free(outputBuffer);
410	GUIFontDestroy(font);
411
412	free(framebuffer[0]);
413	free(framebuffer[1]);
414
415	return 0;
416}
417
418static void _audioDMA(void) {
419	if (!audioBufferSize) {
420		return;
421	}
422	DCFlushRange(audioBuffer[currentAudioBuffer], audioBufferSize * sizeof(struct GBAStereoSample));
423	AUDIO_InitDMA((u32) audioBuffer[currentAudioBuffer], audioBufferSize * sizeof(struct GBAStereoSample));
424	currentAudioBuffer = (currentAudioBuffer + 1) % 3;
425	audioBufferSize = 0;
426}
427
428static void _drawStart(void) {
429	u32 level = 0;
430	_CPU_ISR_Disable(level);
431	if (referenceRetraceCount >= retraceCount) {
432		VIDEO_WaitVSync();
433	}
434	referenceRetraceCount = retraceCount;
435	_CPU_ISR_Restore(level);
436
437	GX_SetZMode(GX_TRUE, GX_LEQUAL, GX_TRUE);
438	GX_SetColorUpdate(GX_TRUE);
439
440	GX_SetViewport(0, 0, vmode->fbWidth, vmode->efbHeight, 0, 1);
441}
442
443static void _drawEnd(void) {
444	whichFb = !whichFb;
445
446	GX_CopyDisp(framebuffer[whichFb], GX_TRUE);
447	GX_DrawDone();
448	VIDEO_SetNextFramebuffer(framebuffer[whichFb]);
449	VIDEO_Flush();
450
451	u32 level = 0;
452	_CPU_ISR_Disable(level);
453	++referenceRetraceCount;
454	_CPU_ISR_Restore(level);
455}
456
457static uint32_t _pollInput(const struct mInputMap* map) {
458	PAD_ScanPads();
459	u16 padkeys = PAD_ButtonsHeld(0);
460
461	WPAD_ScanPads();
462	u32 wiiPad = WPAD_ButtonsHeld(0);
463	u32 ext = 0;
464	WPAD_Probe(0, &ext);
465
466	int keys = 0;
467	keys |= mInputMapKeyBits(map, GCN1_INPUT, padkeys, 0);
468	keys |= mInputMapKeyBits(map, GCN2_INPUT, padkeys, 0);
469	keys |= mInputMapKeyBits(map, WIIMOTE_INPUT, wiiPad, 0);
470	if (ext == WPAD_EXP_CLASSIC) {
471		keys |= mInputMapKeyBits(map, CLASSIC_INPUT, wiiPad, 0);
472	}
473	int x = PAD_StickX(0);
474	int y = PAD_StickY(0);
475	int w_x = WPAD_StickX(0, 0);
476	int w_y = WPAD_StickY(0, 0);
477	if (x < -0x20 || w_x < -0x20) {
478		keys |= 1 << GUI_INPUT_LEFT;
479	}
480	if (x > 0x20 || w_x > 0x20) {
481		keys |= 1 << GUI_INPUT_RIGHT;
482	}
483	if (y < -0x20 || w_y <- 0x20) {
484		keys |= 1 << GUI_INPUT_DOWN;
485	}
486	if (y > 0x20 || w_y > 0x20) {
487		keys |= 1 << GUI_INPUT_UP;
488	}
489	return keys;
490}
491
492static enum GUICursorState _pollCursor(unsigned* x, unsigned* y) {
493	ir_t ir;
494	WPAD_IR(0, &ir);
495	if (!ir.smooth_valid) {
496		return GUI_CURSOR_NOT_PRESENT;
497	}
498	*x = ir.sx;
499	*y = ir.sy;
500	WPAD_ScanPads();
501	u32 wiiPad = WPAD_ButtonsHeld(0);
502	if (wiiPad & WPAD_BUTTON_A) {
503		return GUI_CURSOR_DOWN;
504	}
505	return GUI_CURSOR_UP;
506}
507
508void _reproj(int w, int h) {
509	Mtx44 proj;
510	int top = (vmode->efbHeight - h) / 2;
511	int left = (vmode->fbWidth - w) / 2;
512	guOrtho(proj, -top, top + h, -left, left + w, 0, 300);
513	GX_LoadProjectionMtx(proj, GX_ORTHOGRAPHIC);
514}
515
516void _reproj2(int w, int h) {
517	Mtx44 proj;
518	s16 top = 20;
519	guOrtho(proj, -top, top + h, 0, w, 0, 300);
520	GX_LoadProjectionMtx(proj, GX_ORTHOGRAPHIC);
521}
522
523void _guiPrepare(void) {
524	_reproj2(vmode->fbWidth * GUI_SCALE, vmode->efbHeight * GUI_SCALE);
525}
526
527void _guiFinish(void) {
528	if (screenMode == SM_PA) {
529		_reproj(corew * scaleFactor, coreh * scaleFactor);
530	} else {
531		_reproj2(corew, coreh);
532	}
533}
534
535void _setup(struct mGUIRunner* runner) {
536	runner->core->setRotation(runner->core, &rotation);
537	runner->core->setRumble(runner->core, &rumble);
538
539	_mapKey(&runner->core->inputMap, GCN1_INPUT, PAD_BUTTON_A, GBA_KEY_A);
540	_mapKey(&runner->core->inputMap, GCN1_INPUT, PAD_BUTTON_B, GBA_KEY_B);
541	_mapKey(&runner->core->inputMap, GCN1_INPUT, PAD_BUTTON_START, GBA_KEY_START);
542	_mapKey(&runner->core->inputMap, GCN1_INPUT, PAD_BUTTON_X, GBA_KEY_SELECT);
543	_mapKey(&runner->core->inputMap, GCN2_INPUT, PAD_BUTTON_Y, GBA_KEY_SELECT);
544	_mapKey(&runner->core->inputMap, GCN1_INPUT, PAD_BUTTON_UP, GBA_KEY_UP);
545	_mapKey(&runner->core->inputMap, GCN1_INPUT, PAD_BUTTON_DOWN, GBA_KEY_DOWN);
546	_mapKey(&runner->core->inputMap, GCN1_INPUT, PAD_BUTTON_LEFT, GBA_KEY_LEFT);
547	_mapKey(&runner->core->inputMap, GCN1_INPUT, PAD_BUTTON_RIGHT, GBA_KEY_RIGHT);
548	_mapKey(&runner->core->inputMap, GCN1_INPUT, PAD_TRIGGER_L, GBA_KEY_L);
549	_mapKey(&runner->core->inputMap, GCN1_INPUT, PAD_TRIGGER_R, GBA_KEY_R);
550
551	_mapKey(&runner->core->inputMap, WIIMOTE_INPUT, WPAD_BUTTON_2, GBA_KEY_A);
552	_mapKey(&runner->core->inputMap, WIIMOTE_INPUT, WPAD_BUTTON_1, GBA_KEY_B);
553	_mapKey(&runner->core->inputMap, WIIMOTE_INPUT, WPAD_BUTTON_PLUS, GBA_KEY_START);
554	_mapKey(&runner->core->inputMap, WIIMOTE_INPUT, WPAD_BUTTON_MINUS, GBA_KEY_SELECT);
555	_mapKey(&runner->core->inputMap, WIIMOTE_INPUT, WPAD_BUTTON_RIGHT, GBA_KEY_UP);
556	_mapKey(&runner->core->inputMap, WIIMOTE_INPUT, WPAD_BUTTON_LEFT, GBA_KEY_DOWN);
557	_mapKey(&runner->core->inputMap, WIIMOTE_INPUT, WPAD_BUTTON_UP, GBA_KEY_LEFT);
558	_mapKey(&runner->core->inputMap, WIIMOTE_INPUT, WPAD_BUTTON_DOWN, GBA_KEY_RIGHT);
559	_mapKey(&runner->core->inputMap, WIIMOTE_INPUT, WPAD_BUTTON_B, GBA_KEY_L);
560	_mapKey(&runner->core->inputMap, WIIMOTE_INPUT, WPAD_BUTTON_A, GBA_KEY_R);
561
562	_mapKey(&runner->core->inputMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_A, GBA_KEY_A);
563	_mapKey(&runner->core->inputMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_B, GBA_KEY_B);
564	_mapKey(&runner->core->inputMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_PLUS, GBA_KEY_START);
565	_mapKey(&runner->core->inputMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_MINUS, GBA_KEY_SELECT);
566	_mapKey(&runner->core->inputMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_UP, GBA_KEY_UP);
567	_mapKey(&runner->core->inputMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_DOWN, GBA_KEY_DOWN);
568	_mapKey(&runner->core->inputMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_LEFT, GBA_KEY_LEFT);
569	_mapKey(&runner->core->inputMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_RIGHT, GBA_KEY_RIGHT);
570	_mapKey(&runner->core->inputMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_FULL_L, GBA_KEY_L);
571	_mapKey(&runner->core->inputMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_FULL_R, GBA_KEY_R);
572
573	struct mInputAxis desc = { GBA_KEY_RIGHT, GBA_KEY_LEFT, 0x20, -0x20 };
574	mInputBindAxis(&runner->core->inputMap, GCN1_INPUT, 0, &desc);
575	mInputBindAxis(&runner->core->inputMap, CLASSIC_INPUT, 0, &desc);
576	desc = (struct mInputAxis) { GBA_KEY_UP, GBA_KEY_DOWN, 0x20, -0x20 };
577	mInputBindAxis(&runner->core->inputMap, GCN1_INPUT, 1, &desc);
578	mInputBindAxis(&runner->core->inputMap, CLASSIC_INPUT, 1, &desc);
579
580	outputBuffer = memalign(32, 256 * 256 * BYTES_PER_PIXEL);
581	runner->core->setVideoBuffer(runner->core, outputBuffer, 256);
582
583	runner->core->setAudioBufferSize(runner->core, SAMPLES);
584
585	double ratio = GBAAudioCalculateRatio(1, 60 / 1.001, 1);
586	blip_set_rates(runner->core->getAudioChannel(runner->core, 0), runner->core->frequency(runner->core), 48000 * ratio);
587	blip_set_rates(runner->core->getAudioChannel(runner->core, 1), runner->core->frequency(runner->core), 48000 * ratio);
588}
589
590void _gameUnloaded(struct mGUIRunner* runner) {
591	UNUSED(runner);
592	AUDIO_StopDMA();
593}
594
595void _gameLoaded(struct mGUIRunner* runner) {
596	reconfigureScreen(runner->core, vmode);
597	if (runner->core->platform(runner->core) == PLATFORM_GBA && ((struct GBA*) runner->core->board)->memory.hw.devices & HW_GYRO) {
598		int i;
599		for (i = 0; i < 6; ++i) {
600			u32 result = WPAD_SetMotionPlus(0, 1);
601			if (result == WPAD_ERR_NONE) {
602				break;
603			}
604			sleep(1);
605		}
606	}
607	_unpaused(runner);
608}
609
610void _unpaused(struct mGUIRunner* runner) {
611	u32 level = 0;
612	_CPU_ISR_Disable(level);
613	referenceRetraceCount = retraceCount;
614	_CPU_ISR_Restore(level);
615
616	unsigned mode;
617	if (mCoreConfigGetUIntValue(&runner->config, "screenMode", &mode) && mode < SM_MAX) {
618		screenMode = mode;
619	}
620	if (mCoreConfigGetUIntValue(&runner->config, "filter", &mode) && mode < FM_MAX) {
621		filterMode = mode;
622		switch (mode) {
623		case FM_NEAREST:
624		default:
625			GX_InitTexObjFilterMode(&tex, GX_NEAR, GX_NEAR);
626			break;
627		case FM_LINEAR:
628			GX_InitTexObjFilterMode(&tex, GX_LINEAR, GX_LINEAR);
629			break;
630		}
631	}
632	_guiFinish();
633}
634
635void _drawFrame(struct mGUIRunner* runner, bool faded) {
636	int available = blip_samples_avail(runner->core->getAudioChannel(runner->core, 0));
637	if (available + audioBufferSize > SAMPLES) {
638		available = SAMPLES - audioBufferSize;
639	}
640	available &= ~((32 / sizeof(struct GBAStereoSample)) - 1); // Force align to 32 bytes
641	if (available > 0) {
642		// These appear to be reversed for AUDIO_InitDMA
643		blip_read_samples(runner->core->getAudioChannel(runner->core, 0), &audioBuffer[currentAudioBuffer][audioBufferSize].right, available, true);
644		blip_read_samples(runner->core->getAudioChannel(runner->core, 1), &audioBuffer[currentAudioBuffer][audioBufferSize].left, available, true);
645		audioBufferSize += available;
646	}
647	if (audioBufferSize == SAMPLES && !AUDIO_GetDMAEnableFlag()) {
648		_audioDMA();
649		AUDIO_StartDMA();
650	}
651
652	uint32_t color = 0xFFFFFF3F;
653	if (!faded) {
654		color |= 0xC0;
655	}
656	size_t x, y;
657	uint64_t* texdest = (uint64_t*) texmem;
658	uint64_t* texsrc = (uint64_t*) outputBuffer;
659	for (y = 0; y < coreh; y += 4) {
660		for (x = 0; x < corew >> 2; ++x) {
661			texdest[0 + x * 4 + y * 64] = texsrc[0   + x + y * 64];
662			texdest[1 + x * 4 + y * 64] = texsrc[64  + x + y * 64];
663			texdest[2 + x * 4 + y * 64] = texsrc[128 + x + y * 64];
664			texdest[3 + x * 4 + y * 64] = texsrc[192 + x + y * 64];
665		}
666	}
667	DCFlushRange(texdest, 256 * 256 * BYTES_PER_PIXEL);
668
669	if (faded) {
670		GX_SetBlendMode(GX_BM_BLEND, GX_BL_SRCALPHA, GX_BL_INVSRCALPHA, GX_LO_NOOP);
671	} else {
672		GX_SetBlendMode(GX_BM_NONE, GX_BL_ONE, GX_BL_ZERO, GX_LO_NOOP);
673	}
674	GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_TEX0, GX_TEX_ST, GX_S16, 0);
675	GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_CLR0, GX_CLR_RGBA, GX_RGBA8, 0);
676	GX_InvalidateTexAll();
677	GX_LoadTexObj(&tex, GX_TEXMAP0);
678
679	s16 vertSize = 256;
680	if (screenMode == SM_PA) {
681		vertSize *= scaleFactor;
682	}
683
684	GX_Begin(GX_QUADS, GX_VTXFMT0, 4);
685	GX_Position2s16(0, vertSize);
686	GX_Color1u32(color);
687	GX_TexCoord2s16(0, 1);
688
689	GX_Position2s16(vertSize, vertSize);
690	GX_Color1u32(color);
691	GX_TexCoord2s16(1, 1);
692
693	GX_Position2s16(vertSize, 0);
694	GX_Color1u32(color);
695	GX_TexCoord2s16(1, 0);
696
697	GX_Position2s16(0, 0);
698	GX_Color1u32(color);
699	GX_TexCoord2s16(0, 0);
700	GX_End();
701}
702
703uint16_t _pollGameInput(struct mGUIRunner* runner) {
704	UNUSED(runner);
705	PAD_ScanPads();
706	u16 padkeys = PAD_ButtonsHeld(0);
707	WPAD_ScanPads();
708	u32 wiiPad = WPAD_ButtonsHeld(0);
709	u32 ext = 0;
710	WPAD_Probe(0, &ext);
711	uint16_t keys = mInputMapKeyBits(&runner->core->inputMap, GCN1_INPUT, padkeys, 0);
712	keys |= mInputMapKeyBits(&runner->core->inputMap, GCN2_INPUT, padkeys, 0);
713	keys |= mInputMapKeyBits(&runner->core->inputMap, WIIMOTE_INPUT, wiiPad, 0);
714
715	enum GBAKey angles = mInputMapAxis(&runner->core->inputMap, GCN1_INPUT, 0, PAD_StickX(0));
716	if (angles != GBA_KEY_NONE) {
717		keys |= 1 << angles;
718	}
719	angles = mInputMapAxis(&runner->core->inputMap, GCN1_INPUT, 1, PAD_StickY(0));
720	if (angles != GBA_KEY_NONE) {
721		keys |= 1 << angles;
722	}
723	if (ext == WPAD_EXP_CLASSIC) {
724		keys |= mInputMapKeyBits(&runner->core->inputMap, CLASSIC_INPUT, wiiPad, 0);
725		angles = mInputMapAxis(&runner->core->inputMap, CLASSIC_INPUT, 0, WPAD_StickX(0, 0));
726		if (angles != GBA_KEY_NONE) {
727			keys |= 1 << angles;
728		}
729		angles = mInputMapAxis(&runner->core->inputMap, CLASSIC_INPUT, 1, WPAD_StickY(0, 0));
730		if (angles != GBA_KEY_NONE) {
731			keys |= 1 << angles;
732		}
733	}
734
735	return keys;
736}
737
738void _incrementScreenMode(struct mGUIRunner* runner) {
739	UNUSED(runner);
740	int mode = screenMode | (filterMode << 1);
741	++mode;
742	screenMode = mode % SM_MAX;
743	filterMode = (mode >> 1) % FM_MAX;
744	mCoreConfigSetUIntValue(&runner->config, "screenMode", screenMode);
745	mCoreConfigSetUIntValue(&runner->config, "filter", filterMode);
746	if (screenMode == SM_PA) {
747		_reproj(corew * scaleFactor, coreh * scaleFactor);
748	} else {
749		_reproj2(corew, coreh);
750	}
751	switch (filterMode) {
752	case FM_NEAREST:
753	default:
754		GX_InitTexObjFilterMode(&tex, GX_NEAR, GX_NEAR);
755		break;
756	case FM_LINEAR:
757		GX_InitTexObjFilterMode(&tex, GX_LINEAR, GX_LINEAR);
758		break;
759	}
760}
761
762void _setRumble(struct mRumble* rumble, int enable) {
763	UNUSED(rumble);
764	WPAD_Rumble(0, enable);
765	if (enable) {
766		PAD_ControlMotor(0, PAD_MOTOR_RUMBLE);
767	} else {
768		PAD_ControlMotor(0, PAD_MOTOR_STOP);
769	}
770}
771
772void _sampleRotation(struct mRotationSource* source) {
773	UNUSED(source);
774	vec3w_t accel;
775	WPAD_Accel(0, &accel);
776	// These are swapped
777	tiltX = (0x1EA - accel.y) << 22;
778	tiltY = (0x1EA - accel.x) << 22;
779
780	// This doesn't seem to work at all with -TR remotes
781	struct expansion_t exp;
782	WPAD_Expansion(0, &exp);
783	if (exp.type != EXP_MOTION_PLUS) {
784		return;
785	}
786	gyroZ = exp.mp.rz - 0x1FA0;
787	gyroZ <<= 18;
788}
789
790int32_t _readTiltX(struct mRotationSource* source) {
791	UNUSED(source);
792	return tiltX;
793}
794
795int32_t _readTiltY(struct mRotationSource* source) {
796	UNUSED(source);
797	return tiltY;
798}
799
800int32_t _readGyroZ(struct mRotationSource* source) {
801	UNUSED(source);
802	return gyroZ;
803}
804
805static s8 WPAD_StickX(u8 chan, u8 right) {
806	float mag = 0.0;
807	float ang = 0.0;
808	WPADData *data = WPAD_Data(chan);
809
810	switch (data->exp.type)	{
811	case WPAD_EXP_NUNCHUK:
812	case WPAD_EXP_GUITARHERO3:
813		if (right == 0) {
814			mag = data->exp.nunchuk.js.mag;
815			ang = data->exp.nunchuk.js.ang;
816		}
817		break;
818	case WPAD_EXP_CLASSIC:
819		if (right == 0) {
820			mag = data->exp.classic.ljs.mag;
821			ang = data->exp.classic.ljs.ang;
822		} else {
823			mag = data->exp.classic.rjs.mag;
824			ang = data->exp.classic.rjs.ang;
825		}
826		break;
827	default:
828		break;
829	}
830
831	/* calculate X value (angle need to be converted into radian) */
832	if (mag > 1.0) {
833		mag = 1.0;
834	} else if (mag < -1.0) {
835		mag = -1.0;
836	}
837	double val = mag * sinf(M_PI * ang / 180.0f);
838 
839	return (s8)(val * 128.0f);
840}
841
842static s8 WPAD_StickY(u8 chan, u8 right) {
843	float mag = 0.0;
844	float ang = 0.0;
845	WPADData *data = WPAD_Data(chan);
846
847	switch (data->exp.type) {
848	case WPAD_EXP_NUNCHUK:
849	case WPAD_EXP_GUITARHERO3:
850		if (right == 0) {
851			mag = data->exp.nunchuk.js.mag;
852			ang = data->exp.nunchuk.js.ang;
853		}
854		break;
855	case WPAD_EXP_CLASSIC:
856		if (right == 0) {
857			mag = data->exp.classic.ljs.mag;
858			ang = data->exp.classic.ljs.ang;
859		} else {
860			mag = data->exp.classic.rjs.mag;
861			ang = data->exp.classic.rjs.ang;
862		}
863		break;
864	default:
865		break;
866	}
867
868	/* calculate X value (angle need to be converted into radian) */
869	if (mag > 1.0) { 
870		mag = 1.0;
871	} else if (mag < -1.0) {
872		mag = -1.0;
873	}
874	double val = mag * cosf(M_PI * ang / 180.0f);
875 
876	return (s8)(val * 128.0f);
877}
878
879void _retraceCallback(u32 count) {
880	u32 level = 0;
881	_CPU_ISR_Disable(level);
882	retraceCount = count;
883	_CPU_ISR_Restore(level);
884}