all repos — mgba @ 7a53c35123f9d3033e18afc05660b017c956181c

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