all repos — mgba @ e27e3333052238777f4acdb029f02f1fee90626c

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