all repos — mgba @ addc3abd74ad8da2236ab7e01bcf684cf01841df

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_InvalidateTexAll();
787	GX_LoadTexObj(&tex, GX_TEXMAP0);
788
789	GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_TEX0, GX_TEX_ST, GX_S16, 0);
790	s16 vertSize = 256;
791	if (screenMode == SM_PA) {
792		vertSize *= scaleFactor;
793	}
794
795	GX_Begin(GX_QUADS, GX_VTXFMT0, 4);
796	GX_Position2s16(0, vertSize);
797	GX_Color1u32(color);
798	GX_TexCoord2s16(0, 1);
799
800	GX_Position2s16(vertSize, vertSize);
801	GX_Color1u32(color);
802	GX_TexCoord2s16(1, 1);
803
804	GX_Position2s16(vertSize, 0);
805	GX_Color1u32(color);
806	GX_TexCoord2s16(1, 0);
807
808	GX_Position2s16(0, 0);
809	GX_Color1u32(color);
810	GX_TexCoord2s16(0, 0);
811	GX_End();
812}
813
814uint16_t _pollGameInput(struct mGUIRunner* runner) {
815	UNUSED(runner);
816	PAD_ScanPads();
817	u16 padkeys = PAD_ButtonsHeld(0);
818	WPAD_ScanPads();
819	u32 wiiPad = WPAD_ButtonsHeld(0);
820	u32 ext = 0;
821	WPAD_Probe(0, &ext);
822	uint16_t keys = mInputMapKeyBits(&runner->core->inputMap, GCN1_INPUT, padkeys, 0);
823	keys |= mInputMapKeyBits(&runner->core->inputMap, GCN2_INPUT, padkeys, 0);
824	keys |= mInputMapKeyBits(&runner->core->inputMap, WIIMOTE_INPUT, wiiPad, 0);
825
826	enum GBAKey angles = mInputMapAxis(&runner->core->inputMap, GCN1_INPUT, 0, PAD_StickX(0));
827	if (angles != GBA_KEY_NONE) {
828		keys |= 1 << angles;
829	}
830	angles = mInputMapAxis(&runner->core->inputMap, GCN1_INPUT, 1, PAD_StickY(0));
831	if (angles != GBA_KEY_NONE) {
832		keys |= 1 << angles;
833	}
834	if (ext == WPAD_EXP_CLASSIC) {
835		keys |= mInputMapKeyBits(&runner->core->inputMap, CLASSIC_INPUT, wiiPad, 0);
836		angles = mInputMapAxis(&runner->core->inputMap, CLASSIC_INPUT, 0, WPAD_StickX(0, 0));
837		if (angles != GBA_KEY_NONE) {
838			keys |= 1 << angles;
839		}
840		angles = mInputMapAxis(&runner->core->inputMap, CLASSIC_INPUT, 1, WPAD_StickY(0, 0));
841		if (angles != GBA_KEY_NONE) {
842			keys |= 1 << angles;
843		}
844	}
845
846	return keys;
847}
848
849void _incrementScreenMode(struct mGUIRunner* runner) {
850	UNUSED(runner);
851	int mode = screenMode | (filterMode << 1);
852	++mode;
853	screenMode = mode % SM_MAX;
854	filterMode = (mode >> 1) % FM_MAX;
855	mCoreConfigSetUIntValue(&runner->config, "screenMode", screenMode);
856	mCoreConfigSetUIntValue(&runner->config, "filter", filterMode);
857	if (screenMode == SM_PA) {
858		_reproj(corew * scaleFactor, coreh * scaleFactor);
859	} else {
860		_reproj2(corew, coreh);
861	}
862	switch (filterMode) {
863	case FM_NEAREST:
864	default:
865		GX_InitTexObjFilterMode(&tex, GX_NEAR, GX_NEAR);
866		break;
867	case FM_LINEAR:
868		GX_InitTexObjFilterMode(&tex, GX_LINEAR, GX_LINEAR);
869		break;
870	}
871}
872
873void _setRumble(struct mRumble* rumble, int enable) {
874	UNUSED(rumble);
875	WPAD_Rumble(0, enable);
876	if (enable) {
877		PAD_ControlMotor(0, PAD_MOTOR_RUMBLE);
878	} else {
879		PAD_ControlMotor(0, PAD_MOTOR_STOP);
880	}
881}
882
883void _sampleRotation(struct mRotationSource* source) {
884	UNUSED(source);
885	vec3w_t accel;
886	WPAD_Accel(0, &accel);
887	// These are swapped
888	tiltX = (0x1EA - accel.y) << 22;
889	tiltY = (0x1EA - accel.x) << 22;
890
891	// This doesn't seem to work at all with -TR remotes
892	struct expansion_t exp;
893	WPAD_Expansion(0, &exp);
894	if (exp.type != EXP_MOTION_PLUS) {
895		return;
896	}
897	gyroZ = exp.mp.rz - 0x1FA0;
898	gyroZ <<= 18;
899}
900
901int32_t _readTiltX(struct mRotationSource* source) {
902	UNUSED(source);
903	return tiltX;
904}
905
906int32_t _readTiltY(struct mRotationSource* source) {
907	UNUSED(source);
908	return tiltY;
909}
910
911int32_t _readGyroZ(struct mRotationSource* source) {
912	UNUSED(source);
913	return gyroZ;
914}
915
916static s8 WPAD_StickX(u8 chan, u8 right) {
917	float mag = 0.0;
918	float ang = 0.0;
919	WPADData *data = WPAD_Data(chan);
920
921	switch (data->exp.type)	{
922	case WPAD_EXP_NUNCHUK:
923	case WPAD_EXP_GUITARHERO3:
924		if (right == 0) {
925			mag = data->exp.nunchuk.js.mag;
926			ang = data->exp.nunchuk.js.ang;
927		}
928		break;
929	case WPAD_EXP_CLASSIC:
930		if (right == 0) {
931			mag = data->exp.classic.ljs.mag;
932			ang = data->exp.classic.ljs.ang;
933		} else {
934			mag = data->exp.classic.rjs.mag;
935			ang = data->exp.classic.rjs.ang;
936		}
937		break;
938	default:
939		break;
940	}
941
942	/* calculate X value (angle need to be converted into radian) */
943	if (mag > 1.0) {
944		mag = 1.0;
945	} else if (mag < -1.0) {
946		mag = -1.0;
947	}
948	double val = mag * sinf(M_PI * ang / 180.0f);
949 
950	return (s8)(val * 128.0f);
951}
952
953static s8 WPAD_StickY(u8 chan, u8 right) {
954	float mag = 0.0;
955	float ang = 0.0;
956	WPADData *data = WPAD_Data(chan);
957
958	switch (data->exp.type) {
959	case WPAD_EXP_NUNCHUK:
960	case WPAD_EXP_GUITARHERO3:
961		if (right == 0) {
962			mag = data->exp.nunchuk.js.mag;
963			ang = data->exp.nunchuk.js.ang;
964		}
965		break;
966	case WPAD_EXP_CLASSIC:
967		if (right == 0) {
968			mag = data->exp.classic.ljs.mag;
969			ang = data->exp.classic.ljs.ang;
970		} else {
971			mag = data->exp.classic.rjs.mag;
972			ang = data->exp.classic.rjs.ang;
973		}
974		break;
975	default:
976		break;
977	}
978
979	/* calculate X value (angle need to be converted into radian) */
980	if (mag > 1.0) { 
981		mag = 1.0;
982	} else if (mag < -1.0) {
983		mag = -1.0;
984	}
985	double val = mag * cosf(M_PI * ang / 180.0f);
986 
987	return (s8)(val * 128.0f);
988}
989
990void _retraceCallback(u32 count) {
991	u32 level = 0;
992	_CPU_ISR_Disable(level);
993	retraceCount = count;
994	_CPU_ISR_Restore(level);
995}