all repos — mgba @ 0ac6f0862c61b1d49a1cfb97c8189dc763085b2e

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