all repos — mgba @ fbb29de1606f1489fc5aa7f7492e9a610cd95fcd

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		.keySources = (struct GUIInputKeys[]) {
 315			{
 316				.name = "GameCube Input (1)",
 317				.id = GCN1_INPUT,
 318				.keyNames = (const char*[]) {
 319					"D-Pad Left",
 320					"D-Pad Right",
 321					"D-Pad Down",
 322					"D-Pad Up",
 323					"Z",
 324					"R",
 325					"L",
 326					0,
 327					"A",
 328					"B",
 329					"X",
 330					"Y",
 331					"Start"
 332				},
 333				.nKeys = 13
 334			},
 335			{
 336				.name = "GameCube Input (2)",
 337				.id = GCN2_INPUT,
 338				.keyNames = (const char*[]) {
 339					"D-Pad Left",
 340					"D-Pad Right",
 341					"D-Pad Down",
 342					"D-Pad Up",
 343					"Z",
 344					"R",
 345					"L",
 346					0,
 347					"A",
 348					"B",
 349					"X",
 350					"Y",
 351					"Start"
 352				},
 353				.nKeys = 13
 354			},
 355			{
 356				.name = "Wii Remote Input",
 357				.id = WIIMOTE_INPUT,
 358				.keyNames = (const char*[]) {
 359					"2",
 360					"1",
 361					"B",
 362					"A",
 363					"-",
 364					0,
 365					0,
 366					"\1\xE",
 367					"Left",
 368					"Right",
 369					"Down",
 370					"Up",
 371					"+",
 372					0,
 373					0,
 374					0,
 375					"Z",
 376					"C",
 377				},
 378				.nKeys = 18
 379			},
 380			{
 381				.name = "Classic Controller Input",
 382				.id = CLASSIC_INPUT,
 383				.keyNames = (const char*[]) {
 384					0,
 385					0,
 386					0,
 387					0,
 388					0,
 389					0,
 390					0,
 391					0,
 392					0,
 393					0,
 394					0,
 395					0,
 396					0,
 397					0,
 398					0,
 399					0,
 400					"Up",
 401					"Left",
 402					"ZR",
 403					"X",
 404					"A",
 405					"Y",
 406					"B",
 407					"ZL",
 408					0,
 409					"R",
 410					"+",
 411					"\1\xE",
 412					"-",
 413					"L",
 414					"Down",
 415					"Right",
 416				},
 417				.nKeys = 32
 418			},
 419			{ .id = 0 }
 420		},
 421		.configExtra = (struct GUIMenuItem[]) {
 422			{
 423				.title = "Video mode",
 424				.data = "videoMode",
 425				.submenu = 0,
 426				.state = 0,
 427				.validStates = (const char*[]) {
 428					"Autodetect (recommended)",
 429					"480i",
 430					"480p",
 431					"240p",
 432				},
 433				.nStates = 4
 434			},
 435			{
 436				.title = "Screen mode",
 437				.data = "screenMode",
 438				.submenu = 0,
 439				.state = 0,
 440				.validStates = (const char*[]) {
 441					"Pixel-Accurate",
 442					"Stretched",
 443				},
 444				.nStates = 2
 445			},
 446			{
 447				.title = "Filtering",
 448				.data = "filter",
 449				.submenu = 0,
 450				.state = 0,
 451				.validStates = (const char*[]) {
 452					"Pixelated",
 453					"Bilinear (smoother)",
 454					"Bilinear (pixelated)",
 455				},
 456				.nStates = 3
 457			}
 458		},
 459		.nConfigExtra = 3,
 460		.setup = _setup,
 461		.teardown = 0,
 462		.gameLoaded = _gameLoaded,
 463		.gameUnloaded = _gameUnloaded,
 464		.prepareForFrame = 0,
 465		.drawFrame = _drawFrame,
 466		.paused = _gameUnloaded,
 467		.unpaused = _unpaused,
 468		.incrementScreenMode = _incrementScreenMode,
 469		.setFrameLimiter = _setFrameLimiter,
 470		.pollGameInput = _pollGameInput
 471	};
 472	mGUIInit(&runner, "wii");
 473	reconfigureScreen(&runner);
 474
 475	_mapKey(&runner.params.keyMap, GCN1_INPUT, PAD_BUTTON_A, GUI_INPUT_SELECT);
 476	_mapKey(&runner.params.keyMap, GCN1_INPUT, PAD_BUTTON_B, GUI_INPUT_BACK);
 477	_mapKey(&runner.params.keyMap, GCN1_INPUT, PAD_TRIGGER_Z, GUI_INPUT_CANCEL);
 478	_mapKey(&runner.params.keyMap, GCN1_INPUT, PAD_BUTTON_UP, GUI_INPUT_UP);
 479	_mapKey(&runner.params.keyMap, GCN1_INPUT, PAD_BUTTON_DOWN, GUI_INPUT_DOWN);
 480	_mapKey(&runner.params.keyMap, GCN1_INPUT, PAD_BUTTON_LEFT, GUI_INPUT_LEFT);
 481	_mapKey(&runner.params.keyMap, GCN1_INPUT, PAD_BUTTON_RIGHT, GUI_INPUT_RIGHT);
 482
 483	_mapKey(&runner.params.keyMap, WIIMOTE_INPUT, WPAD_BUTTON_2, GUI_INPUT_SELECT);
 484	_mapKey(&runner.params.keyMap, WIIMOTE_INPUT, WPAD_BUTTON_1, GUI_INPUT_BACK);
 485	_mapKey(&runner.params.keyMap, WIIMOTE_INPUT, WPAD_BUTTON_HOME, GUI_INPUT_CANCEL);
 486	_mapKey(&runner.params.keyMap, WIIMOTE_INPUT, WPAD_BUTTON_RIGHT, GUI_INPUT_UP);
 487	_mapKey(&runner.params.keyMap, WIIMOTE_INPUT, WPAD_BUTTON_LEFT, GUI_INPUT_DOWN);
 488	_mapKey(&runner.params.keyMap, WIIMOTE_INPUT, WPAD_BUTTON_UP, GUI_INPUT_LEFT);
 489	_mapKey(&runner.params.keyMap, WIIMOTE_INPUT, WPAD_BUTTON_DOWN, GUI_INPUT_RIGHT);
 490
 491	_mapKey(&runner.params.keyMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_A, GUI_INPUT_SELECT);
 492	_mapKey(&runner.params.keyMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_Y, GUI_INPUT_SELECT);
 493	_mapKey(&runner.params.keyMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_B, GUI_INPUT_BACK);
 494	_mapKey(&runner.params.keyMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_X, GUI_INPUT_BACK);
 495	_mapKey(&runner.params.keyMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_HOME, GUI_INPUT_CANCEL);
 496	_mapKey(&runner.params.keyMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_UP, GUI_INPUT_UP);
 497	_mapKey(&runner.params.keyMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_DOWN, GUI_INPUT_DOWN);
 498	_mapKey(&runner.params.keyMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_LEFT, GUI_INPUT_LEFT);
 499	_mapKey(&runner.params.keyMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_RIGHT, GUI_INPUT_RIGHT);
 500
 501	if (argc > 1) {
 502		size_t i;
 503		for (i = 0; runner.keySources[i].id; ++i) {
 504			mInputMapLoad(&runner.params.keyMap, runner.keySources[i].id, mCoreConfigGetInput(&runner.config));
 505		}
 506		mGUIRun(&runner, argv[1]);
 507	} else {
 508		mGUIRunloop(&runner);
 509	}
 510	mGUIDeinit(&runner);
 511
 512	free(fifo);
 513	free(texmem);
 514	free(rescaleTexmem);
 515
 516	free(outputBuffer);
 517	GUIFontDestroy(font);
 518
 519	free(framebuffer[0]);
 520	free(framebuffer[1]);
 521
 522	return 0;
 523}
 524
 525static void _audioDMA(void) {
 526	if (!audioBufferSize) {
 527		return;
 528	}
 529	DCFlushRange(audioBuffer[currentAudioBuffer], audioBufferSize * sizeof(struct GBAStereoSample));
 530	AUDIO_InitDMA((u32) audioBuffer[currentAudioBuffer], audioBufferSize * sizeof(struct GBAStereoSample));
 531	currentAudioBuffer = (currentAudioBuffer + 1) % 3;
 532	audioBufferSize = 0;
 533}
 534
 535static void _drawStart(void) {
 536	u32 level = 0;
 537	_CPU_ISR_Disable(level);
 538	if (referenceRetraceCount > retraceCount) {
 539		if (frameLimiter) {
 540			VIDEO_WaitVSync();
 541		}
 542		referenceRetraceCount = retraceCount;
 543	}
 544	_CPU_ISR_Restore(level);
 545
 546	GX_SetZMode(GX_TRUE, GX_LEQUAL, GX_TRUE);
 547	GX_SetColorUpdate(GX_TRUE);
 548
 549	GX_SetViewport(0, 0, vmode->fbWidth, vmode->efbHeight, 0, 1);
 550}
 551
 552static void _drawEnd(void) {
 553	whichFb = !whichFb;
 554
 555	GX_CopyDisp(framebuffer[whichFb], GX_TRUE);
 556	GX_DrawDone();
 557	VIDEO_SetNextFramebuffer(framebuffer[whichFb]);
 558	VIDEO_Flush();
 559
 560	u32 level = 0;
 561	_CPU_ISR_Disable(level);
 562	++referenceRetraceCount;
 563	_CPU_ISR_Restore(level);
 564}
 565
 566static void _setFrameLimiter(struct mGUIRunner* runner, bool limit) {
 567	UNUSED(runner);
 568	frameLimiter = limit;
 569}
 570
 571static uint32_t _pollInput(const struct mInputMap* map) {
 572	PAD_ScanPads();
 573	u16 padkeys = PAD_ButtonsHeld(0);
 574
 575	WPAD_ScanPads();
 576	u32 wiiPad = WPAD_ButtonsHeld(0);
 577	u32 ext = 0;
 578	WPAD_Probe(0, &ext);
 579
 580	int keys = 0;
 581	keys |= mInputMapKeyBits(map, GCN1_INPUT, padkeys, 0);
 582	keys |= mInputMapKeyBits(map, GCN2_INPUT, padkeys, 0);
 583	keys |= mInputMapKeyBits(map, WIIMOTE_INPUT, wiiPad, 0);
 584	if (ext == WPAD_EXP_CLASSIC) {
 585		keys |= mInputMapKeyBits(map, CLASSIC_INPUT, wiiPad, 0);
 586	}
 587	int x = PAD_StickX(0);
 588	int y = PAD_StickY(0);
 589	int w_x = WPAD_StickX(0, 0);
 590	int w_y = WPAD_StickY(0, 0);
 591	if (x < -0x20 || w_x < -0x20) {
 592		keys |= 1 << GUI_INPUT_LEFT;
 593	}
 594	if (x > 0x20 || w_x > 0x20) {
 595		keys |= 1 << GUI_INPUT_RIGHT;
 596	}
 597	if (y < -0x20 || w_y <- 0x20) {
 598		keys |= 1 << GUI_INPUT_DOWN;
 599	}
 600	if (y > 0x20 || w_y > 0x20) {
 601		keys |= 1 << GUI_INPUT_UP;
 602	}
 603	return keys;
 604}
 605
 606static enum GUICursorState _pollCursor(unsigned* x, unsigned* y) {
 607	ir_t ir;
 608	WPAD_IR(0, &ir);
 609	if (!ir.smooth_valid) {
 610		return GUI_CURSOR_NOT_PRESENT;
 611	}
 612	*x = ir.sx;
 613	*y = ir.sy;
 614	WPAD_ScanPads();
 615	u32 wiiPad = WPAD_ButtonsHeld(0);
 616	if (wiiPad & WPAD_BUTTON_A) {
 617		return GUI_CURSOR_DOWN;
 618	}
 619	return GUI_CURSOR_UP;
 620}
 621
 622void _reproj(int w, int h) {
 623	Mtx44 proj;
 624	int top = (vmode->efbHeight * hAdjust - h) / 2;
 625	int left = (vmode->fbWidth * wAdjust - w) / 2;
 626	guOrtho(proj, -top, top + h, -left, left + w, 0, 300);
 627	GX_LoadProjectionMtx(proj, GX_ORTHOGRAPHIC);
 628}
 629
 630void _reproj2(int w, int h) {
 631	Mtx44 proj;
 632	int top = h * (1.0 - hStretch) / 2;
 633	int left = w * (1.0 - wStretch) / 2;
 634	guOrtho(proj, -top, h + top, -left, w + left, 0, 300);
 635	GX_LoadProjectionMtx(proj, GX_ORTHOGRAPHIC);
 636}
 637
 638void _guiPrepare(void) {
 639	_reproj2(vmode->fbWidth * guiScale * wAdjust, vmode->efbHeight * guiScale * hAdjust);
 640}
 641
 642void _setup(struct mGUIRunner* runner) {
 643	runner->core->setRotation(runner->core, &rotation);
 644	runner->core->setRumble(runner->core, &rumble);
 645
 646	_mapKey(&runner->core->inputMap, GCN1_INPUT, PAD_BUTTON_A, GBA_KEY_A);
 647	_mapKey(&runner->core->inputMap, GCN1_INPUT, PAD_BUTTON_B, GBA_KEY_B);
 648	_mapKey(&runner->core->inputMap, GCN1_INPUT, PAD_BUTTON_START, GBA_KEY_START);
 649	_mapKey(&runner->core->inputMap, GCN1_INPUT, PAD_BUTTON_X, GBA_KEY_SELECT);
 650	_mapKey(&runner->core->inputMap, GCN2_INPUT, PAD_BUTTON_Y, GBA_KEY_SELECT);
 651	_mapKey(&runner->core->inputMap, GCN1_INPUT, PAD_BUTTON_UP, GBA_KEY_UP);
 652	_mapKey(&runner->core->inputMap, GCN1_INPUT, PAD_BUTTON_DOWN, GBA_KEY_DOWN);
 653	_mapKey(&runner->core->inputMap, GCN1_INPUT, PAD_BUTTON_LEFT, GBA_KEY_LEFT);
 654	_mapKey(&runner->core->inputMap, GCN1_INPUT, PAD_BUTTON_RIGHT, GBA_KEY_RIGHT);
 655	_mapKey(&runner->core->inputMap, GCN1_INPUT, PAD_TRIGGER_L, GBA_KEY_L);
 656	_mapKey(&runner->core->inputMap, GCN1_INPUT, PAD_TRIGGER_R, GBA_KEY_R);
 657
 658	_mapKey(&runner->core->inputMap, WIIMOTE_INPUT, WPAD_BUTTON_2, GBA_KEY_A);
 659	_mapKey(&runner->core->inputMap, WIIMOTE_INPUT, WPAD_BUTTON_1, GBA_KEY_B);
 660	_mapKey(&runner->core->inputMap, WIIMOTE_INPUT, WPAD_BUTTON_PLUS, GBA_KEY_START);
 661	_mapKey(&runner->core->inputMap, WIIMOTE_INPUT, WPAD_BUTTON_MINUS, GBA_KEY_SELECT);
 662	_mapKey(&runner->core->inputMap, WIIMOTE_INPUT, WPAD_BUTTON_RIGHT, GBA_KEY_UP);
 663	_mapKey(&runner->core->inputMap, WIIMOTE_INPUT, WPAD_BUTTON_LEFT, GBA_KEY_DOWN);
 664	_mapKey(&runner->core->inputMap, WIIMOTE_INPUT, WPAD_BUTTON_UP, GBA_KEY_LEFT);
 665	_mapKey(&runner->core->inputMap, WIIMOTE_INPUT, WPAD_BUTTON_DOWN, GBA_KEY_RIGHT);
 666	_mapKey(&runner->core->inputMap, WIIMOTE_INPUT, WPAD_BUTTON_B, GBA_KEY_L);
 667	_mapKey(&runner->core->inputMap, WIIMOTE_INPUT, WPAD_BUTTON_A, GBA_KEY_R);
 668
 669	_mapKey(&runner->core->inputMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_A, GBA_KEY_A);
 670	_mapKey(&runner->core->inputMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_B, GBA_KEY_B);
 671	_mapKey(&runner->core->inputMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_PLUS, GBA_KEY_START);
 672	_mapKey(&runner->core->inputMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_MINUS, GBA_KEY_SELECT);
 673	_mapKey(&runner->core->inputMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_UP, GBA_KEY_UP);
 674	_mapKey(&runner->core->inputMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_DOWN, GBA_KEY_DOWN);
 675	_mapKey(&runner->core->inputMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_LEFT, GBA_KEY_LEFT);
 676	_mapKey(&runner->core->inputMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_RIGHT, GBA_KEY_RIGHT);
 677	_mapKey(&runner->core->inputMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_FULL_L, GBA_KEY_L);
 678	_mapKey(&runner->core->inputMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_FULL_R, GBA_KEY_R);
 679
 680	struct mInputAxis desc = { GBA_KEY_RIGHT, GBA_KEY_LEFT, 0x20, -0x20 };
 681	mInputBindAxis(&runner->core->inputMap, GCN1_INPUT, 0, &desc);
 682	mInputBindAxis(&runner->core->inputMap, CLASSIC_INPUT, 0, &desc);
 683	desc = (struct mInputAxis) { GBA_KEY_UP, GBA_KEY_DOWN, 0x20, -0x20 };
 684	mInputBindAxis(&runner->core->inputMap, GCN1_INPUT, 1, &desc);
 685	mInputBindAxis(&runner->core->inputMap, CLASSIC_INPUT, 1, &desc);
 686
 687	outputBuffer = memalign(32, TEX_W * TEX_H * BYTES_PER_PIXEL);
 688	runner->core->setVideoBuffer(runner->core, outputBuffer, TEX_W);
 689
 690	runner->core->setAudioBufferSize(runner->core, SAMPLES);
 691
 692	double ratio = GBAAudioCalculateRatio(1, audioSampleRate, 1);
 693	blip_set_rates(runner->core->getAudioChannel(runner->core, 0), runner->core->frequency(runner->core), 48000 * ratio);
 694	blip_set_rates(runner->core->getAudioChannel(runner->core, 1), runner->core->frequency(runner->core), 48000 * ratio);
 695
 696	frameLimiter = true;
 697}
 698
 699void _gameUnloaded(struct mGUIRunner* runner) {
 700	UNUSED(runner);
 701	AUDIO_StopDMA();
 702	frameLimiter = true;
 703}
 704
 705void _gameLoaded(struct mGUIRunner* runner) {
 706	reconfigureScreen(runner);
 707	if (runner->core->platform(runner->core) == PLATFORM_GBA && ((struct GBA*) runner->core->board)->memory.hw.devices & HW_GYRO) {
 708		int i;
 709		for (i = 0; i < 6; ++i) {
 710			u32 result = WPAD_SetMotionPlus(0, 1);
 711			if (result == WPAD_ERR_NONE) {
 712				break;
 713			}
 714			sleep(1);
 715		}
 716	}
 717	memset(texmem, 0, TEX_W * TEX_H * BYTES_PER_PIXEL);
 718	_unpaused(runner);
 719}
 720
 721void _unpaused(struct mGUIRunner* runner) {
 722	u32 level = 0;
 723	_CPU_ISR_Disable(level);
 724	referenceRetraceCount = retraceCount;
 725	_CPU_ISR_Restore(level);
 726
 727	unsigned mode;
 728	if (mCoreConfigGetUIntValue(&runner->config, "videoMode", &mode) && mode < VM_MAX) {
 729		if (mode != videoMode) {
 730			reconfigureScreen(runner);
 731		}
 732	}
 733	if (mCoreConfigGetUIntValue(&runner->config, "screenMode", &mode) && mode < SM_MAX) {
 734		screenMode = mode;
 735	}
 736	if (mCoreConfigGetUIntValue(&runner->config, "filter", &mode) && mode < FM_MAX) {
 737		filterMode = mode;
 738		switch (mode) {
 739		case FM_NEAREST:
 740		case FM_LINEAR_2x:
 741		default:
 742			GX_InitTexObjFilterMode(&tex, GX_NEAR, GX_NEAR);
 743			break;
 744		case FM_LINEAR_1x:
 745			GX_InitTexObjFilterMode(&tex, GX_LINEAR, GX_LINEAR);
 746			break;
 747		}
 748	}
 749	float stretch;
 750	if (mCoreConfigGetFloatValue(&runner->config, "stretchWidth", &stretch)) {
 751		wStretch = fminf(1.0f, fmaxf(0.5f, stretch));
 752	}
 753	if (mCoreConfigGetFloatValue(&runner->config, "stretchHeight", &stretch)) {
 754		hStretch = fminf(1.0f, fmaxf(0.5f, stretch));
 755	}
 756}
 757
 758void _drawFrame(struct mGUIRunner* runner, bool faded) {
 759	int available = blip_samples_avail(runner->core->getAudioChannel(runner->core, 0));
 760	if (available + audioBufferSize > SAMPLES) {
 761		available = SAMPLES - audioBufferSize;
 762	}
 763	available &= ~((32 / sizeof(struct GBAStereoSample)) - 1); // Force align to 32 bytes
 764	if (available > 0) {
 765		// These appear to be reversed for AUDIO_InitDMA
 766		blip_read_samples(runner->core->getAudioChannel(runner->core, 0), &audioBuffer[currentAudioBuffer][audioBufferSize].right, available, true);
 767		blip_read_samples(runner->core->getAudioChannel(runner->core, 1), &audioBuffer[currentAudioBuffer][audioBufferSize].left, available, true);
 768		audioBufferSize += available;
 769	}
 770	if (audioBufferSize == SAMPLES && !AUDIO_GetDMAEnableFlag()) {
 771		_audioDMA();
 772		AUDIO_StartDMA();
 773	}
 774
 775	uint32_t color = 0xFFFFFF3F;
 776	if (!faded) {
 777		color |= 0xC0;
 778	}
 779	size_t x, y;
 780	uint64_t* texdest = (uint64_t*) texmem;
 781	uint64_t* texsrc = (uint64_t*) outputBuffer;
 782	for (y = 0; y < coreh; y += 4) {
 783		for (x = 0; x < corew >> 2; ++x) {
 784			texdest[0 + x * 4 + y * 64] = texsrc[0   + x + y * 64];
 785			texdest[1 + x * 4 + y * 64] = texsrc[64  + x + y * 64];
 786			texdest[2 + x * 4 + y * 64] = texsrc[128 + x + y * 64];
 787			texdest[3 + x * 4 + y * 64] = texsrc[192 + x + y * 64];
 788		}
 789	}
 790	DCFlushRange(texdest, TEX_W * TEX_H * BYTES_PER_PIXEL);
 791
 792	if (faded) {
 793		GX_SetBlendMode(GX_BM_BLEND, GX_BL_SRCALPHA, GX_BL_INVSRCALPHA, GX_LO_NOOP);
 794	} else {
 795		GX_SetBlendMode(GX_BM_NONE, GX_BL_ONE, GX_BL_ZERO, GX_LO_NOOP);
 796	}
 797	GX_InvalidateTexAll();
 798	GX_LoadTexObj(&tex, GX_TEXMAP0);
 799
 800	GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_TEX0, GX_TEX_ST, GX_S16, 0);
 801	s16 vertWidth = TEX_W;
 802	s16 vertHeight = TEX_H;
 803
 804	if (filterMode == FM_LINEAR_2x) {
 805		Mtx44 proj;
 806		guOrtho(proj, 0, vmode->efbHeight, 0, vmode->fbWidth, 0, 300);
 807		GX_LoadProjectionMtx(proj, GX_ORTHOGRAPHIC);
 808
 809		GX_Begin(GX_QUADS, GX_VTXFMT0, 4);
 810		GX_Position2s16(0, TEX_H * 2);
 811		GX_Color1u32(0xFFFFFFFF);
 812		GX_TexCoord2s16(0, 1);
 813
 814		GX_Position2s16(TEX_W * 2, TEX_H * 2);
 815		GX_Color1u32(0xFFFFFFFF);
 816		GX_TexCoord2s16(1, 1);
 817
 818		GX_Position2s16(TEX_W * 2, 0);
 819		GX_Color1u32(0xFFFFFFFF);
 820		GX_TexCoord2s16(1, 0);
 821
 822		GX_Position2s16(0, 0);
 823		GX_Color1u32(0xFFFFFFFF);
 824		GX_TexCoord2s16(0, 0);
 825		GX_End();
 826
 827		GX_SetTexCopySrc(0, 0, TEX_W * 2, TEX_H * 2);
 828		GX_SetTexCopyDst(TEX_W * 2, TEX_H * 2, GX_TF_RGB565, GX_FALSE);
 829		GX_CopyTex(rescaleTexmem, GX_TRUE);
 830		GX_LoadTexObj(&rescaleTex, GX_TEXMAP0);
 831	}
 832
 833	if (screenMode == SM_PA) {
 834		vertWidth *= scaleFactor;
 835		vertHeight *= scaleFactor;
 836	}
 837
 838	if (screenMode == SM_PA) {
 839		_reproj(corew * scaleFactor, coreh * scaleFactor);
 840	} else {
 841		_reproj2(corew, coreh);
 842	}
 843
 844	GX_Begin(GX_QUADS, GX_VTXFMT0, 4);
 845	GX_Position2s16(0, vertHeight);
 846	GX_Color1u32(color);
 847	GX_TexCoord2s16(0, 1);
 848
 849	GX_Position2s16(vertWidth, vertHeight);
 850	GX_Color1u32(color);
 851	GX_TexCoord2s16(1, 1);
 852
 853	GX_Position2s16(vertWidth, 0);
 854	GX_Color1u32(color);
 855	GX_TexCoord2s16(1, 0);
 856
 857	GX_Position2s16(0, 0);
 858	GX_Color1u32(color);
 859	GX_TexCoord2s16(0, 0);
 860	GX_End();
 861}
 862
 863uint16_t _pollGameInput(struct mGUIRunner* runner) {
 864	UNUSED(runner);
 865	PAD_ScanPads();
 866	u16 padkeys = PAD_ButtonsHeld(0);
 867	WPAD_ScanPads();
 868	u32 wiiPad = WPAD_ButtonsHeld(0);
 869	u32 ext = 0;
 870	WPAD_Probe(0, &ext);
 871	uint16_t keys = mInputMapKeyBits(&runner->core->inputMap, GCN1_INPUT, padkeys, 0);
 872	keys |= mInputMapKeyBits(&runner->core->inputMap, GCN2_INPUT, padkeys, 0);
 873	keys |= mInputMapKeyBits(&runner->core->inputMap, WIIMOTE_INPUT, wiiPad, 0);
 874
 875	enum GBAKey angles = mInputMapAxis(&runner->core->inputMap, GCN1_INPUT, 0, PAD_StickX(0));
 876	if (angles != GBA_KEY_NONE) {
 877		keys |= 1 << angles;
 878	}
 879	angles = mInputMapAxis(&runner->core->inputMap, GCN1_INPUT, 1, PAD_StickY(0));
 880	if (angles != GBA_KEY_NONE) {
 881		keys |= 1 << angles;
 882	}
 883	if (ext == WPAD_EXP_CLASSIC) {
 884		keys |= mInputMapKeyBits(&runner->core->inputMap, CLASSIC_INPUT, wiiPad, 0);
 885		angles = mInputMapAxis(&runner->core->inputMap, CLASSIC_INPUT, 0, WPAD_StickX(0, 0));
 886		if (angles != GBA_KEY_NONE) {
 887			keys |= 1 << angles;
 888		}
 889		angles = mInputMapAxis(&runner->core->inputMap, CLASSIC_INPUT, 1, WPAD_StickY(0, 0));
 890		if (angles != GBA_KEY_NONE) {
 891			keys |= 1 << angles;
 892		}
 893	}
 894
 895	return keys;
 896}
 897
 898void _incrementScreenMode(struct mGUIRunner* runner) {
 899	UNUSED(runner);
 900	int mode = screenMode | (filterMode << 1);
 901	++mode;
 902	screenMode = mode % SM_MAX;
 903	filterMode = (mode >> 1) % FM_MAX;
 904	mCoreConfigSetUIntValue(&runner->config, "screenMode", screenMode);
 905	mCoreConfigSetUIntValue(&runner->config, "filter", filterMode);
 906	switch (filterMode) {
 907	case FM_NEAREST:
 908	case FM_LINEAR_2x:
 909	default:
 910		GX_InitTexObjFilterMode(&tex, GX_NEAR, GX_NEAR);
 911		break;
 912	case FM_LINEAR_1x:
 913		GX_InitTexObjFilterMode(&tex, GX_LINEAR, GX_LINEAR);
 914		break;
 915	}
 916}
 917
 918void _setRumble(struct mRumble* rumble, int enable) {
 919	UNUSED(rumble);
 920	WPAD_Rumble(0, enable);
 921	if (enable) {
 922		PAD_ControlMotor(0, PAD_MOTOR_RUMBLE);
 923	} else {
 924		PAD_ControlMotor(0, PAD_MOTOR_STOP);
 925	}
 926}
 927
 928void _sampleRotation(struct mRotationSource* source) {
 929	UNUSED(source);
 930	vec3w_t accel;
 931	WPAD_Accel(0, &accel);
 932	// These are swapped
 933	tiltX = (0x1EA - accel.y) << 22;
 934	tiltY = (0x1EA - accel.x) << 22;
 935
 936	// This doesn't seem to work at all with -TR remotes
 937	struct expansion_t exp;
 938	WPAD_Expansion(0, &exp);
 939	if (exp.type != EXP_MOTION_PLUS) {
 940		return;
 941	}
 942	gyroZ = exp.mp.rz - 0x1FA0;
 943	gyroZ <<= 18;
 944}
 945
 946int32_t _readTiltX(struct mRotationSource* source) {
 947	UNUSED(source);
 948	return tiltX;
 949}
 950
 951int32_t _readTiltY(struct mRotationSource* source) {
 952	UNUSED(source);
 953	return tiltY;
 954}
 955
 956int32_t _readGyroZ(struct mRotationSource* source) {
 957	UNUSED(source);
 958	return gyroZ;
 959}
 960
 961static s8 WPAD_StickX(u8 chan, u8 right) {
 962	float mag = 0.0;
 963	float ang = 0.0;
 964	WPADData *data = WPAD_Data(chan);
 965
 966	switch (data->exp.type)	{
 967	case WPAD_EXP_NUNCHUK:
 968	case WPAD_EXP_GUITARHERO3:
 969		if (right == 0) {
 970			mag = data->exp.nunchuk.js.mag;
 971			ang = data->exp.nunchuk.js.ang;
 972		}
 973		break;
 974	case WPAD_EXP_CLASSIC:
 975		if (right == 0) {
 976			mag = data->exp.classic.ljs.mag;
 977			ang = data->exp.classic.ljs.ang;
 978		} else {
 979			mag = data->exp.classic.rjs.mag;
 980			ang = data->exp.classic.rjs.ang;
 981		}
 982		break;
 983	default:
 984		break;
 985	}
 986
 987	/* calculate X value (angle need to be converted into radian) */
 988	if (mag > 1.0) {
 989		mag = 1.0;
 990	} else if (mag < -1.0) {
 991		mag = -1.0;
 992	}
 993	double val = mag * sinf(M_PI * ang / 180.0f);
 994 
 995	return (s8)(val * 128.0f);
 996}
 997
 998static s8 WPAD_StickY(u8 chan, u8 right) {
 999	float mag = 0.0;
1000	float ang = 0.0;
1001	WPADData *data = WPAD_Data(chan);
1002
1003	switch (data->exp.type) {
1004	case WPAD_EXP_NUNCHUK:
1005	case WPAD_EXP_GUITARHERO3:
1006		if (right == 0) {
1007			mag = data->exp.nunchuk.js.mag;
1008			ang = data->exp.nunchuk.js.ang;
1009		}
1010		break;
1011	case WPAD_EXP_CLASSIC:
1012		if (right == 0) {
1013			mag = data->exp.classic.ljs.mag;
1014			ang = data->exp.classic.ljs.ang;
1015		} else {
1016			mag = data->exp.classic.rjs.mag;
1017			ang = data->exp.classic.rjs.ang;
1018		}
1019		break;
1020	default:
1021		break;
1022	}
1023
1024	/* calculate X value (angle need to be converted into radian) */
1025	if (mag > 1.0) { 
1026		mag = 1.0;
1027	} else if (mag < -1.0) {
1028		mag = -1.0;
1029	}
1030	double val = mag * cosf(M_PI * ang / 180.0f);
1031 
1032	return (s8)(val * 128.0f);
1033}
1034
1035void _retraceCallback(u32 count) {
1036	u32 level = 0;
1037	_CPU_ISR_Disable(level);
1038	retraceCount = count;
1039	_CPU_ISR_Restore(level);
1040}