all repos — mgba @ c657255009d8a3143e1a2ecd390f9f895374f60c

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	_CPU_ISR_Disable(level);
 737	referenceRetraceCount = retraceCount;
 738	_CPU_ISR_Restore(level);
 739
 740	unsigned mode;
 741	if (mCoreConfigGetUIntValue(&runner->config, "videoMode", &mode) && mode < VM_MAX) {
 742		if (mode != videoMode) {
 743			reconfigureScreen(runner);
 744		}
 745	}
 746	if (mCoreConfigGetUIntValue(&runner->config, "screenMode", &mode) && mode < SM_MAX) {
 747		screenMode = mode;
 748	}
 749	if (mCoreConfigGetUIntValue(&runner->config, "filter", &mode) && mode < FM_MAX) {
 750		filterMode = mode;
 751		switch (mode) {
 752		case FM_NEAREST:
 753		case FM_LINEAR_2x:
 754		default:
 755			GX_InitTexObjFilterMode(&tex, GX_NEAR, GX_NEAR);
 756			break;
 757		case FM_LINEAR_1x:
 758			GX_InitTexObjFilterMode(&tex, GX_LINEAR, GX_LINEAR);
 759			break;
 760		}
 761	}
 762	float stretch;
 763	if (mCoreConfigGetFloatValue(&runner->config, "stretchWidth", &stretch)) {
 764		wStretch = fminf(1.0f, fmaxf(0.5f, stretch));
 765	}
 766	if (mCoreConfigGetFloatValue(&runner->config, "stretchHeight", &stretch)) {
 767		hStretch = fminf(1.0f, fmaxf(0.5f, stretch));
 768	}
 769}
 770
 771void _drawFrame(struct mGUIRunner* runner, bool faded) {
 772	int available = blip_samples_avail(runner->core->getAudioChannel(runner->core, 0));
 773	if (available + audioBufferSize > SAMPLES) {
 774		available = SAMPLES - audioBufferSize;
 775	}
 776	available &= ~((32 / sizeof(struct GBAStereoSample)) - 1); // Force align to 32 bytes
 777	if (available > 0) {
 778		// These appear to be reversed for AUDIO_InitDMA
 779		blip_read_samples(runner->core->getAudioChannel(runner->core, 0), &audioBuffer[currentAudioBuffer][audioBufferSize].right, available, true);
 780		blip_read_samples(runner->core->getAudioChannel(runner->core, 1), &audioBuffer[currentAudioBuffer][audioBufferSize].left, available, true);
 781		audioBufferSize += available;
 782	}
 783	if (audioBufferSize == SAMPLES && !AUDIO_GetDMAEnableFlag()) {
 784		_audioDMA();
 785		AUDIO_StartDMA();
 786	}
 787
 788	uint32_t color = 0xFFFFFF3F;
 789	if (!faded) {
 790		color |= 0xC0;
 791	}
 792	size_t x, y;
 793	uint64_t* texdest = (uint64_t*) texmem;
 794	uint64_t* texsrc = (uint64_t*) outputBuffer;
 795	for (y = 0; y < coreh; y += 4) {
 796		for (x = 0; x < corew >> 2; ++x) {
 797			texdest[0 + x * 4 + y * 64] = texsrc[0   + x + y * 64];
 798			texdest[1 + x * 4 + y * 64] = texsrc[64  + x + y * 64];
 799			texdest[2 + x * 4 + y * 64] = texsrc[128 + x + y * 64];
 800			texdest[3 + x * 4 + y * 64] = texsrc[192 + x + y * 64];
 801		}
 802	}
 803	DCFlushRange(texdest, TEX_W * TEX_H * BYTES_PER_PIXEL);
 804
 805	if (faded) {
 806		GX_SetBlendMode(GX_BM_BLEND, GX_BL_SRCALPHA, GX_BL_INVSRCALPHA, GX_LO_NOOP);
 807	} else {
 808		GX_SetBlendMode(GX_BM_NONE, GX_BL_ONE, GX_BL_ZERO, GX_LO_NOOP);
 809	}
 810	GX_InvalidateTexAll();
 811	GX_LoadTexObj(&tex, GX_TEXMAP0);
 812
 813	GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_TEX0, GX_TEX_ST, GX_S16, 0);
 814	s16 vertWidth = TEX_W;
 815	s16 vertHeight = TEX_H;
 816
 817	if (filterMode == FM_LINEAR_2x) {
 818		Mtx44 proj;
 819		guOrtho(proj, 0, vmode->efbHeight, 0, vmode->fbWidth, 0, 300);
 820		GX_LoadProjectionMtx(proj, GX_ORTHOGRAPHIC);
 821
 822		GX_Begin(GX_QUADS, GX_VTXFMT0, 4);
 823		GX_Position2s16(0, TEX_H * 2);
 824		GX_Color1u32(0xFFFFFFFF);
 825		GX_TexCoord2s16(0, 1);
 826
 827		GX_Position2s16(TEX_W * 2, TEX_H * 2);
 828		GX_Color1u32(0xFFFFFFFF);
 829		GX_TexCoord2s16(1, 1);
 830
 831		GX_Position2s16(TEX_W * 2, 0);
 832		GX_Color1u32(0xFFFFFFFF);
 833		GX_TexCoord2s16(1, 0);
 834
 835		GX_Position2s16(0, 0);
 836		GX_Color1u32(0xFFFFFFFF);
 837		GX_TexCoord2s16(0, 0);
 838		GX_End();
 839
 840		GX_SetTexCopySrc(0, 0, TEX_W * 2, TEX_H * 2);
 841		GX_SetTexCopyDst(TEX_W * 2, TEX_H * 2, GX_TF_RGB565, GX_FALSE);
 842		GX_CopyTex(rescaleTexmem, GX_TRUE);
 843		GX_LoadTexObj(&rescaleTex, GX_TEXMAP0);
 844	}
 845
 846	if (screenMode == SM_PA) {
 847		vertWidth *= scaleFactor;
 848		vertHeight *= scaleFactor;
 849	}
 850
 851	if (screenMode == SM_PA) {
 852		_reproj(corew * scaleFactor, coreh * scaleFactor);
 853	} else {
 854		_reproj2(corew, coreh);
 855	}
 856
 857	GX_Begin(GX_QUADS, GX_VTXFMT0, 4);
 858	GX_Position2s16(0, vertHeight);
 859	GX_Color1u32(color);
 860	GX_TexCoord2s16(0, 1);
 861
 862	GX_Position2s16(vertWidth, vertHeight);
 863	GX_Color1u32(color);
 864	GX_TexCoord2s16(1, 1);
 865
 866	GX_Position2s16(vertWidth, 0);
 867	GX_Color1u32(color);
 868	GX_TexCoord2s16(1, 0);
 869
 870	GX_Position2s16(0, 0);
 871	GX_Color1u32(color);
 872	GX_TexCoord2s16(0, 0);
 873	GX_End();
 874}
 875
 876uint16_t _pollGameInput(struct mGUIRunner* runner) {
 877	UNUSED(runner);
 878	PAD_ScanPads();
 879	u16 padkeys = PAD_ButtonsHeld(0);
 880	WPAD_ScanPads();
 881	u32 wiiPad = WPAD_ButtonsHeld(0);
 882	u32 ext = 0;
 883	WPAD_Probe(0, &ext);
 884	uint16_t keys = mInputMapKeyBits(&runner->core->inputMap, GCN1_INPUT, padkeys, 0);
 885	keys |= mInputMapKeyBits(&runner->core->inputMap, GCN2_INPUT, padkeys, 0);
 886	keys |= mInputMapKeyBits(&runner->core->inputMap, WIIMOTE_INPUT, wiiPad, 0);
 887
 888	enum GBAKey angles = mInputMapAxis(&runner->core->inputMap, GCN1_INPUT, 0, PAD_StickX(0));
 889	if (angles != GBA_KEY_NONE) {
 890		keys |= 1 << angles;
 891	}
 892	angles = mInputMapAxis(&runner->core->inputMap, GCN1_INPUT, 1, PAD_StickY(0));
 893	if (angles != GBA_KEY_NONE) {
 894		keys |= 1 << angles;
 895	}
 896	if (ext == WPAD_EXP_CLASSIC) {
 897		keys |= mInputMapKeyBits(&runner->core->inputMap, CLASSIC_INPUT, wiiPad, 0);
 898		angles = mInputMapAxis(&runner->core->inputMap, CLASSIC_INPUT, 0, WPAD_StickX(0, 0));
 899		if (angles != GBA_KEY_NONE) {
 900			keys |= 1 << angles;
 901		}
 902		angles = mInputMapAxis(&runner->core->inputMap, CLASSIC_INPUT, 1, WPAD_StickY(0, 0));
 903		if (angles != GBA_KEY_NONE) {
 904			keys |= 1 << angles;
 905		}
 906	}
 907
 908	return keys;
 909}
 910
 911void _incrementScreenMode(struct mGUIRunner* runner) {
 912	UNUSED(runner);
 913	int mode = screenMode | (filterMode << 1);
 914	++mode;
 915	screenMode = mode % SM_MAX;
 916	filterMode = (mode >> 1) % FM_MAX;
 917	mCoreConfigSetUIntValue(&runner->config, "screenMode", screenMode);
 918	mCoreConfigSetUIntValue(&runner->config, "filter", filterMode);
 919	switch (filterMode) {
 920	case FM_NEAREST:
 921	case FM_LINEAR_2x:
 922	default:
 923		GX_InitTexObjFilterMode(&tex, GX_NEAR, GX_NEAR);
 924		break;
 925	case FM_LINEAR_1x:
 926		GX_InitTexObjFilterMode(&tex, GX_LINEAR, GX_LINEAR);
 927		break;
 928	}
 929}
 930
 931void _setRumble(struct mRumble* rumble, int enable) {
 932	UNUSED(rumble);
 933	WPAD_Rumble(0, enable);
 934	if (enable) {
 935		PAD_ControlMotor(0, PAD_MOTOR_RUMBLE);
 936	} else {
 937		PAD_ControlMotor(0, PAD_MOTOR_STOP);
 938	}
 939}
 940
 941void _sampleRotation(struct mRotationSource* source) {
 942	UNUSED(source);
 943	vec3w_t accel;
 944	WPAD_Accel(0, &accel);
 945	// These are swapped
 946	tiltX = (0x1EA - accel.y) << 22;
 947	tiltY = (0x1EA - accel.x) << 22;
 948
 949	// This doesn't seem to work at all with -TR remotes
 950	struct expansion_t exp;
 951	WPAD_Expansion(0, &exp);
 952	if (exp.type != EXP_MOTION_PLUS) {
 953		return;
 954	}
 955	gyroZ = exp.mp.rz - 0x1FA0;
 956	gyroZ <<= 18;
 957}
 958
 959int32_t _readTiltX(struct mRotationSource* source) {
 960	UNUSED(source);
 961	return tiltX;
 962}
 963
 964int32_t _readTiltY(struct mRotationSource* source) {
 965	UNUSED(source);
 966	return tiltY;
 967}
 968
 969int32_t _readGyroZ(struct mRotationSource* source) {
 970	UNUSED(source);
 971	return gyroZ;
 972}
 973
 974static s8 WPAD_StickX(u8 chan, u8 right) {
 975	float mag = 0.0;
 976	float ang = 0.0;
 977	WPADData *data = WPAD_Data(chan);
 978
 979	switch (data->exp.type)	{
 980	case WPAD_EXP_NUNCHUK:
 981	case WPAD_EXP_GUITARHERO3:
 982		if (right == 0) {
 983			mag = data->exp.nunchuk.js.mag;
 984			ang = data->exp.nunchuk.js.ang;
 985		}
 986		break;
 987	case WPAD_EXP_CLASSIC:
 988		if (right == 0) {
 989			mag = data->exp.classic.ljs.mag;
 990			ang = data->exp.classic.ljs.ang;
 991		} else {
 992			mag = data->exp.classic.rjs.mag;
 993			ang = data->exp.classic.rjs.ang;
 994		}
 995		break;
 996	default:
 997		break;
 998	}
 999
1000	/* calculate X value (angle need to be converted into radian) */
1001	if (mag > 1.0) {
1002		mag = 1.0;
1003	} else if (mag < -1.0) {
1004		mag = -1.0;
1005	}
1006	double val = mag * sinf(M_PI * ang / 180.0f);
1007 
1008	return (s8)(val * 128.0f);
1009}
1010
1011static s8 WPAD_StickY(u8 chan, u8 right) {
1012	float mag = 0.0;
1013	float ang = 0.0;
1014	WPADData *data = WPAD_Data(chan);
1015
1016	switch (data->exp.type) {
1017	case WPAD_EXP_NUNCHUK:
1018	case WPAD_EXP_GUITARHERO3:
1019		if (right == 0) {
1020			mag = data->exp.nunchuk.js.mag;
1021			ang = data->exp.nunchuk.js.ang;
1022		}
1023		break;
1024	case WPAD_EXP_CLASSIC:
1025		if (right == 0) {
1026			mag = data->exp.classic.ljs.mag;
1027			ang = data->exp.classic.ljs.ang;
1028		} else {
1029			mag = data->exp.classic.rjs.mag;
1030			ang = data->exp.classic.rjs.ang;
1031		}
1032		break;
1033	default:
1034		break;
1035	}
1036
1037	/* calculate X value (angle need to be converted into radian) */
1038	if (mag > 1.0) { 
1039		mag = 1.0;
1040	} else if (mag < -1.0) {
1041		mag = -1.0;
1042	}
1043	double val = mag * cosf(M_PI * ang / 180.0f);
1044 
1045	return (s8)(val * 128.0f);
1046}
1047
1048void _retraceCallback(u32 count) {
1049	u32 level = 0;
1050	_CPU_ISR_Disable(level);
1051	retraceCount = count;
1052	_CPU_ISR_Restore(level);
1053}