all repos — mgba @ 04f3fdf21ea5bc446c461fc427eec2f0eb46417f

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