all repos — mgba @ 8dbef1f0e33cec18552573c1e907588b98a1b0ee

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