all repos — mgba @ f15bb076b826c7b3a993a7faf3c14bcae2bcc7ab

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