all repos — mgba @ 6fb1ed176dce349e3b27f36815d41270c47e5a7a

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