mirror of https://github.com/mgba-emu/mgba.git
1206 lines
33 KiB
C
1206 lines
33 KiB
C
/* Copyright (c) 2013-2015 Jeffrey Pfau
|
|
*
|
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
#define asm __asm__
|
|
|
|
#include <fat.h>
|
|
#include <gccore.h>
|
|
#include <ogc/machine/processor.h>
|
|
#include <malloc.h>
|
|
#include <unistd.h>
|
|
#include <wiiuse/wpad.h>
|
|
|
|
#include <mgba-util/common.h>
|
|
|
|
#include <mgba/core/blip_buf.h>
|
|
#include <mgba/core/core.h>
|
|
#include "feature/gui/gui-runner.h"
|
|
#include <mgba/internal/gb/video.h>
|
|
#include <mgba/internal/gba/audio.h>
|
|
#include <mgba/internal/gba/gba.h>
|
|
#include <mgba/internal/gba/input.h>
|
|
#include <mgba-util/gui.h>
|
|
#include <mgba-util/gui/file-select.h>
|
|
#include <mgba-util/gui/font.h>
|
|
#include <mgba-util/gui/menu.h>
|
|
#include <mgba-util/memory.h>
|
|
#include <mgba-util/vfs.h>
|
|
|
|
#define GCN1_INPUT 0x47434E31
|
|
#define GCN2_INPUT 0x47434E32
|
|
#define WIIMOTE_INPUT 0x5749494D
|
|
#define CLASSIC_INPUT 0x57494943
|
|
|
|
#define TEX_W 256
|
|
#define TEX_H 224
|
|
|
|
#define ANALOG_DEADZONE 0x30
|
|
|
|
static void _mapKey(struct mInputMap* map, uint32_t binding, int nativeKey, enum GBAKey key) {
|
|
mInputBindKey(map, binding, __builtin_ctz(nativeKey), key);
|
|
}
|
|
|
|
static enum ScreenMode {
|
|
SM_PA,
|
|
SM_SF,
|
|
SM_MAX
|
|
} screenMode = SM_PA;
|
|
|
|
static enum FilterMode {
|
|
FM_NEAREST,
|
|
FM_LINEAR_1x,
|
|
FM_LINEAR_2x,
|
|
FM_MAX
|
|
} filterMode = FM_NEAREST;
|
|
|
|
static enum VideoMode {
|
|
VM_AUTODETECT,
|
|
VM_480i,
|
|
VM_480p,
|
|
VM_240p,
|
|
// TODO: PAL support
|
|
VM_MAX
|
|
} videoMode = VM_AUTODETECT;
|
|
|
|
#define SAMPLES 512
|
|
#define BUFFERS 8
|
|
#define GUI_SCALE 1.35f
|
|
#define GUI_SCALE_240p 2.0f
|
|
|
|
static void _retraceCallback(u32 count);
|
|
|
|
static void _postAudioBuffer(struct mAVStream* stream, blip_t* left, blip_t* right);
|
|
static void _audioDMA(void);
|
|
static void _setRumble(struct mRumble* rumble, int enable);
|
|
static void _sampleRotation(struct mRotationSource* source);
|
|
static int32_t _readTiltX(struct mRotationSource* source);
|
|
static int32_t _readTiltY(struct mRotationSource* source);
|
|
static int32_t _readGyroZ(struct mRotationSource* source);
|
|
|
|
static void _drawStart(void);
|
|
static void _drawEnd(void);
|
|
static uint32_t _pollInput(const struct mInputMap*);
|
|
static enum GUICursorState _pollCursor(unsigned* x, unsigned* y);
|
|
static void _guiPrepare(void);
|
|
|
|
static void _setup(struct mGUIRunner* runner);
|
|
static void _gameLoaded(struct mGUIRunner* runner);
|
|
static void _gameUnloaded(struct mGUIRunner* runner);
|
|
static void _unpaused(struct mGUIRunner* runner);
|
|
static void _prepareForFrame(struct mGUIRunner* runner);
|
|
static void _drawFrame(struct mGUIRunner* runner, bool faded);
|
|
static uint16_t _pollGameInput(struct mGUIRunner* runner);
|
|
static void _setFrameLimiter(struct mGUIRunner* runner, bool limit);
|
|
static void _incrementScreenMode(struct mGUIRunner* runner);
|
|
|
|
static s8 WPAD_StickX(u8 chan, u8 right);
|
|
static s8 WPAD_StickY(u8 chan, u8 right);
|
|
|
|
static void* outputBuffer;
|
|
static struct mAVStream stream;
|
|
static struct mRumble rumble;
|
|
static struct mRotationSource rotation;
|
|
static GXRModeObj* vmode;
|
|
static float wAdjust;
|
|
static float hAdjust;
|
|
static float wStretch = 0.9f;
|
|
static float hStretch = 0.9f;
|
|
static float guiScale = GUI_SCALE;
|
|
static Mtx model, view, modelview;
|
|
static uint16_t* texmem;
|
|
static GXTexObj tex;
|
|
static uint16_t* rescaleTexmem;
|
|
static GXTexObj rescaleTex;
|
|
static uint16_t* interframeTexmem;
|
|
static GXTexObj interframeTex;
|
|
static bool sgbCrop = false;
|
|
static int32_t tiltX;
|
|
static int32_t tiltY;
|
|
static int32_t gyroZ;
|
|
static uint32_t retraceCount;
|
|
static uint32_t referenceRetraceCount;
|
|
static bool frameLimiter = true;
|
|
static int scaleFactor;
|
|
static unsigned corew, coreh;
|
|
static bool interframeBlending = true;
|
|
|
|
uint32_t* romBuffer;
|
|
size_t romBufferSize;
|
|
|
|
static void* framebuffer[2] = { 0, 0 };
|
|
static int whichFb = 0;
|
|
|
|
static struct AudioBuffer {
|
|
struct GBAStereoSample samples[SAMPLES] __attribute__((__aligned__(32)));
|
|
volatile size_t size;
|
|
} audioBuffer[BUFFERS] = {0};
|
|
static volatile int currentAudioBuffer = 0;
|
|
static volatile int nextAudioBuffer = 0;
|
|
static double audioSampleRate = 60.0 / 1.001;
|
|
|
|
static struct GUIFont* font;
|
|
|
|
static void reconfigureScreen(struct mGUIRunner* runner) {
|
|
if (runner) {
|
|
unsigned mode;
|
|
if (mCoreConfigGetUIntValue(&runner->config, "videoMode", &mode) && mode < VM_MAX) {
|
|
videoMode = mode;
|
|
}
|
|
}
|
|
wAdjust = 1.f;
|
|
hAdjust = 1.f;
|
|
guiScale = GUI_SCALE;
|
|
audioSampleRate = 60.0 / 1.001;
|
|
|
|
s32 signalMode = CONF_GetVideo();
|
|
|
|
switch (videoMode) {
|
|
case VM_AUTODETECT:
|
|
default:
|
|
vmode = VIDEO_GetPreferredMode(0);
|
|
break;
|
|
case VM_480i:
|
|
switch (signalMode) {
|
|
case CONF_VIDEO_NTSC:
|
|
vmode = &TVNtsc480IntDf;
|
|
break;
|
|
case CONF_VIDEO_MPAL:
|
|
vmode = &TVMpal480IntDf;
|
|
break;
|
|
case CONF_VIDEO_PAL:
|
|
vmode = &TVEurgb60Hz480IntDf;
|
|
break;
|
|
}
|
|
break;
|
|
case VM_480p:
|
|
switch (signalMode) {
|
|
case CONF_VIDEO_NTSC:
|
|
vmode = &TVNtsc480Prog;
|
|
break;
|
|
case CONF_VIDEO_MPAL:
|
|
vmode = &TVMpal480Prog;
|
|
break;
|
|
case CONF_VIDEO_PAL:
|
|
vmode = &TVEurgb60Hz480Prog;
|
|
break;
|
|
}
|
|
break;
|
|
case VM_240p:
|
|
switch (signalMode) {
|
|
case CONF_VIDEO_NTSC:
|
|
vmode = &TVNtsc240Ds;
|
|
break;
|
|
case CONF_VIDEO_MPAL:
|
|
vmode = &TVMpal240Ds;
|
|
break;
|
|
case CONF_VIDEO_PAL:
|
|
vmode = &TVEurgb60Hz240Ds;
|
|
break;
|
|
}
|
|
wAdjust = 0.5f;
|
|
audioSampleRate = 90.0 / 1.50436;
|
|
guiScale = GUI_SCALE_240p;
|
|
break;
|
|
}
|
|
|
|
vmode->viWidth = 704;
|
|
vmode->viXOrigin = 8;
|
|
|
|
VIDEO_SetBlack(true);
|
|
VIDEO_Configure(vmode);
|
|
|
|
free(framebuffer[0]);
|
|
free(framebuffer[1]);
|
|
|
|
framebuffer[0] = SYS_AllocateFramebuffer(vmode);
|
|
framebuffer[1] = SYS_AllocateFramebuffer(vmode);
|
|
VIDEO_ClearFrameBuffer(vmode, MEM_K0_TO_K1(framebuffer[0]), COLOR_BLACK);
|
|
VIDEO_ClearFrameBuffer(vmode, MEM_K0_TO_K1(framebuffer[1]), COLOR_BLACK);
|
|
|
|
VIDEO_SetNextFramebuffer(MEM_K0_TO_K1(framebuffer[whichFb]));
|
|
VIDEO_Flush();
|
|
VIDEO_WaitVSync();
|
|
if (vmode->viTVMode & VI_NON_INTERLACE) {
|
|
VIDEO_WaitVSync();
|
|
}
|
|
GX_SetViewport(0, 0, vmode->fbWidth, vmode->efbHeight, 0, 1);
|
|
|
|
f32 yscale = GX_GetYScaleFactor(vmode->efbHeight, vmode->xfbHeight);
|
|
u32 xfbHeight = GX_SetDispCopyYScale(yscale);
|
|
GX_SetScissor(0, 0, vmode->viWidth, vmode->viWidth);
|
|
GX_SetDispCopySrc(0, 0, vmode->fbWidth, vmode->efbHeight);
|
|
GX_SetDispCopyDst(vmode->fbWidth, xfbHeight);
|
|
GX_SetCopyFilter(vmode->aa, vmode->sample_pattern, GX_TRUE, vmode->vfilter);
|
|
GX_SetFieldMode(vmode->field_rendering, ((vmode->viHeight == 2 * vmode->xfbHeight) ? GX_ENABLE : GX_DISABLE));
|
|
|
|
if (runner) {
|
|
runner->params.width = vmode->fbWidth * guiScale * wAdjust;
|
|
runner->params.height = vmode->efbHeight * guiScale * hAdjust;
|
|
if (runner->core) {
|
|
double ratio = GBAAudioCalculateRatio(1, audioSampleRate, 1);
|
|
blip_set_rates(runner->core->getAudioChannel(runner->core, 0), runner->core->frequency(runner->core), 48000 * ratio);
|
|
blip_set_rates(runner->core->getAudioChannel(runner->core, 1), runner->core->frequency(runner->core), 48000 * ratio);
|
|
}
|
|
}
|
|
}
|
|
|
|
int main(int argc, char* argv[]) {
|
|
VIDEO_Init();
|
|
VIDEO_SetBlack(true);
|
|
VIDEO_Flush();
|
|
VIDEO_WaitVSync();
|
|
PAD_Init();
|
|
WPAD_Init();
|
|
WPAD_SetDataFormat(0, WPAD_FMT_BTNS_ACC_IR);
|
|
AUDIO_Init(0);
|
|
AUDIO_SetDSPSampleRate(AI_SAMPLERATE_48KHZ);
|
|
AUDIO_RegisterDMACallback(_audioDMA);
|
|
|
|
memset(audioBuffer, 0, sizeof(audioBuffer));
|
|
#ifdef FIXED_ROM_BUFFER
|
|
romBufferSize = SIZE_CART0;
|
|
romBuffer = SYS_GetArena2Lo();
|
|
SYS_SetArena2Lo((void*)((intptr_t) romBuffer + SIZE_CART0));
|
|
#endif
|
|
|
|
#if !defined(COLOR_16_BIT) && !defined(COLOR_5_6_5)
|
|
#error This pixel format is unsupported. Please use -DCOLOR_16-BIT -DCOLOR_5_6_5
|
|
#endif
|
|
|
|
GXColor bg = { 0, 0, 0, 0xFF };
|
|
void* fifo = memalign(32, 0x40000);
|
|
memset(fifo, 0, 0x40000);
|
|
GX_Init(fifo, 0x40000);
|
|
GX_SetCopyClear(bg, 0x00FFFFFF);
|
|
|
|
GX_SetCullMode(GX_CULL_NONE);
|
|
GX_SetDispCopyGamma(GX_GM_1_0);
|
|
|
|
GX_ClearVtxDesc();
|
|
GX_SetVtxDesc(GX_VA_POS, GX_DIRECT);
|
|
GX_SetVtxDesc(GX_VA_TEX0, GX_DIRECT);
|
|
GX_SetVtxDesc(GX_VA_CLR0, GX_DIRECT);
|
|
|
|
GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_POS, GX_POS_XY, GX_S16, 0);
|
|
GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_TEX0, GX_TEX_ST, GX_S16, 0);
|
|
GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_CLR0, GX_CLR_RGBA, GX_RGBA8, 0);
|
|
|
|
GX_SetNumTevStages(1);
|
|
GX_SetNumChans(1);
|
|
GX_SetNumTexGens(1);
|
|
GX_SetTevOrder(GX_TEVSTAGE0, GX_TEXCOORD0, GX_TEXMAP0, GX_COLOR0A0);
|
|
GX_SetTevOrder(GX_TEVSTAGE1, GX_TEXCOORD0, GX_TEXMAP1, GX_COLOR0A0);
|
|
GX_SetTevOp(GX_TEVSTAGE0, GX_MODULATE);
|
|
GX_SetTevColorOp(GX_TEVSTAGE1, GX_TEV_ADD, GX_TB_ZERO, GX_CS_DIVIDE_2, GX_TRUE, GX_TEVPREV);
|
|
GX_SetTevAlphaOp(GX_TEVSTAGE1, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_TRUE, GX_TEVPREV);
|
|
GX_SetTevColorIn(GX_TEVSTAGE1, GX_CC_ZERO, GX_CC_TEXC, GX_CC_ONE, GX_CC_CPREV);
|
|
GX_SetTevAlphaIn(GX_TEVSTAGE1, GX_CA_ZERO, GX_CA_ZERO, GX_CA_ZERO, GX_CA_APREV);
|
|
|
|
GX_SetTexCoordGen(GX_TEXCOORD0, GX_TG_MTX2x4, GX_TG_TEX0, GX_IDENTITY);
|
|
GX_InvVtxCache();
|
|
GX_InvalidateTexAll();
|
|
|
|
guVector cam = { 0.0f, 0.0f, 0.0f };
|
|
guVector up = { 0.0f, 1.0f, 0.0f };
|
|
guVector look = { 0.0f, 0.0f, -1.0f };
|
|
guLookAt(view, &cam, &up, &look);
|
|
|
|
guMtxIdentity(model);
|
|
guMtxTransApply(model, model, 0.0f, 0.0f, -6.0f);
|
|
guMtxConcat(view, model, modelview);
|
|
GX_LoadPosMtxImm(modelview, GX_PNMTX0);
|
|
|
|
texmem = memalign(32, TEX_W * TEX_H * BYTES_PER_PIXEL);
|
|
GX_InitTexObj(&tex, texmem, TEX_W, TEX_H, GX_TF_RGB565, GX_CLAMP, GX_CLAMP, GX_FALSE);
|
|
interframeTexmem = memalign(32, TEX_W * TEX_H * BYTES_PER_PIXEL);
|
|
GX_InitTexObj(&interframeTex, interframeTexmem, TEX_W, TEX_H, GX_TF_RGB565, GX_CLAMP, GX_CLAMP, GX_FALSE);
|
|
rescaleTexmem = memalign(32, TEX_W * TEX_H * 4 * BYTES_PER_PIXEL);
|
|
GX_InitTexObj(&rescaleTex, rescaleTexmem, TEX_W * 2, TEX_H * 2, GX_TF_RGB565, GX_CLAMP, GX_CLAMP, GX_FALSE);
|
|
GX_InitTexObjFilterMode(&rescaleTex, GX_LINEAR, GX_LINEAR);
|
|
|
|
VIDEO_SetPostRetraceCallback(_retraceCallback);
|
|
|
|
font = GUIFontCreate();
|
|
|
|
fatInitDefault();
|
|
|
|
rumble.setRumble = _setRumble;
|
|
|
|
rotation.sample = _sampleRotation;
|
|
rotation.readTiltX = _readTiltX;
|
|
rotation.readTiltY = _readTiltY;
|
|
rotation.readGyroZ = _readGyroZ;
|
|
|
|
stream.videoDimensionsChanged = NULL;
|
|
stream.postVideoFrame = NULL;
|
|
stream.postAudioFrame = NULL;
|
|
stream.postAudioBuffer = _postAudioBuffer;
|
|
|
|
struct mGUIRunner runner = {
|
|
.params = {
|
|
640, 480,
|
|
font, "",
|
|
_drawStart, _drawEnd,
|
|
_pollInput, _pollCursor,
|
|
0,
|
|
_guiPrepare, 0,
|
|
},
|
|
.keySources = (struct GUIInputKeys[]) {
|
|
{
|
|
.name = "GameCube Input (1)",
|
|
.id = GCN1_INPUT,
|
|
.keyNames = (const char*[]) {
|
|
"D-Pad Left",
|
|
"D-Pad Right",
|
|
"D-Pad Down",
|
|
"D-Pad Up",
|
|
"Z",
|
|
"R",
|
|
"L",
|
|
0,
|
|
"A",
|
|
"B",
|
|
"X",
|
|
"Y",
|
|
"Start"
|
|
},
|
|
.nKeys = 13
|
|
},
|
|
{
|
|
.name = "GameCube Input (2)",
|
|
.id = GCN2_INPUT,
|
|
.keyNames = (const char*[]) {
|
|
"D-Pad Left",
|
|
"D-Pad Right",
|
|
"D-Pad Down",
|
|
"D-Pad Up",
|
|
"Z",
|
|
"R",
|
|
"L",
|
|
0,
|
|
"A",
|
|
"B",
|
|
"X",
|
|
"Y",
|
|
"Start"
|
|
},
|
|
.nKeys = 13
|
|
},
|
|
{
|
|
.name = "Wii Remote Input",
|
|
.id = WIIMOTE_INPUT,
|
|
.keyNames = (const char*[]) {
|
|
"2",
|
|
"1",
|
|
"B",
|
|
"A",
|
|
"-",
|
|
0,
|
|
0,
|
|
"\1\xE",
|
|
"Left",
|
|
"Right",
|
|
"Down",
|
|
"Up",
|
|
"+",
|
|
0,
|
|
0,
|
|
0,
|
|
"Z",
|
|
"C",
|
|
},
|
|
.nKeys = 18
|
|
},
|
|
{
|
|
.name = "Classic Controller Input",
|
|
.id = CLASSIC_INPUT,
|
|
.keyNames = (const char*[]) {
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
"Up",
|
|
"Left",
|
|
"ZR",
|
|
"X",
|
|
"A",
|
|
"Y",
|
|
"B",
|
|
"ZL",
|
|
0,
|
|
"R",
|
|
"+",
|
|
"\1\xE",
|
|
"-",
|
|
"L",
|
|
"Down",
|
|
"Right",
|
|
},
|
|
.nKeys = 32
|
|
},
|
|
{ .id = 0 }
|
|
},
|
|
.configExtra = (struct GUIMenuItem[]) {
|
|
{
|
|
.title = "Video mode",
|
|
.data = "videoMode",
|
|
.submenu = 0,
|
|
.state = 0,
|
|
.validStates = (const char*[]) {
|
|
"Autodetect (recommended)",
|
|
"480i",
|
|
"480p",
|
|
"240p",
|
|
},
|
|
.nStates = 4
|
|
},
|
|
{
|
|
.title = "Screen mode",
|
|
.data = "screenMode",
|
|
.submenu = 0,
|
|
.state = 0,
|
|
.validStates = (const char*[]) {
|
|
"Pixel-Accurate",
|
|
"Stretched",
|
|
},
|
|
.nStates = 2
|
|
},
|
|
{
|
|
.title = "Filtering",
|
|
.data = "filter",
|
|
.submenu = 0,
|
|
.state = 0,
|
|
.validStates = (const char*[]) {
|
|
"Pixelated",
|
|
"Bilinear (smoother)",
|
|
"Bilinear (pixelated)",
|
|
},
|
|
.nStates = 3
|
|
},
|
|
{
|
|
.title = "Horizontal stretch",
|
|
.data = "stretchWidth",
|
|
.submenu = 0,
|
|
.state = 7,
|
|
.validStates = (const char*[]) {
|
|
"1/2x", "0.6x", "2/3x", "0.7x", "3/4x", "0.8x", "0.9x", "1.0x"
|
|
},
|
|
.stateMappings = (const struct GUIVariant[]) {
|
|
GUI_V_F(0.5f),
|
|
GUI_V_F(0.6f),
|
|
GUI_V_F(2.f / 3.f),
|
|
GUI_V_F(0.7f),
|
|
GUI_V_F(0.75f),
|
|
GUI_V_F(0.8f),
|
|
GUI_V_F(0.9f),
|
|
GUI_V_F(1.0f),
|
|
},
|
|
.nStates = 8
|
|
},
|
|
{
|
|
.title = "Vertical stretch",
|
|
.data = "stretchHeight",
|
|
.submenu = 0,
|
|
.state = 6,
|
|
.validStates = (const char*[]) {
|
|
"1/2x", "0.6x", "2/3x", "0.7x", "3/4x", "0.8x", "0.9x", "1.0x"
|
|
},
|
|
.stateMappings = (const struct GUIVariant[]) {
|
|
GUI_V_F(0.5f),
|
|
GUI_V_F(0.6f),
|
|
GUI_V_F(2.f / 3.f),
|
|
GUI_V_F(0.7f),
|
|
GUI_V_F(0.75f),
|
|
GUI_V_F(0.8f),
|
|
GUI_V_F(0.9f),
|
|
GUI_V_F(1.0f),
|
|
},
|
|
.nStates = 8
|
|
},
|
|
},
|
|
.nConfigExtra = 5,
|
|
.setup = _setup,
|
|
.teardown = 0,
|
|
.gameLoaded = _gameLoaded,
|
|
.gameUnloaded = _gameUnloaded,
|
|
.prepareForFrame = _prepareForFrame,
|
|
.drawFrame = _drawFrame,
|
|
.paused = _gameUnloaded,
|
|
.unpaused = _unpaused,
|
|
.incrementScreenMode = _incrementScreenMode,
|
|
.setFrameLimiter = _setFrameLimiter,
|
|
.pollGameInput = _pollGameInput
|
|
};
|
|
mGUIInit(&runner, "wii");
|
|
reconfigureScreen(&runner);
|
|
|
|
// Make sure screen is properly initialized by drawing a blank frame
|
|
_drawStart();
|
|
_drawEnd();
|
|
|
|
_mapKey(&runner.params.keyMap, GCN1_INPUT, PAD_BUTTON_A, GUI_INPUT_SELECT);
|
|
_mapKey(&runner.params.keyMap, GCN1_INPUT, PAD_BUTTON_B, GUI_INPUT_BACK);
|
|
_mapKey(&runner.params.keyMap, GCN1_INPUT, PAD_TRIGGER_Z, GUI_INPUT_CANCEL);
|
|
_mapKey(&runner.params.keyMap, GCN1_INPUT, PAD_BUTTON_UP, GUI_INPUT_UP);
|
|
_mapKey(&runner.params.keyMap, GCN1_INPUT, PAD_BUTTON_DOWN, GUI_INPUT_DOWN);
|
|
_mapKey(&runner.params.keyMap, GCN1_INPUT, PAD_BUTTON_LEFT, GUI_INPUT_LEFT);
|
|
_mapKey(&runner.params.keyMap, GCN1_INPUT, PAD_BUTTON_RIGHT, GUI_INPUT_RIGHT);
|
|
|
|
_mapKey(&runner.params.keyMap, WIIMOTE_INPUT, WPAD_BUTTON_2, GUI_INPUT_SELECT);
|
|
_mapKey(&runner.params.keyMap, WIIMOTE_INPUT, WPAD_BUTTON_1, GUI_INPUT_BACK);
|
|
_mapKey(&runner.params.keyMap, WIIMOTE_INPUT, WPAD_BUTTON_HOME, GUI_INPUT_CANCEL);
|
|
_mapKey(&runner.params.keyMap, WIIMOTE_INPUT, WPAD_BUTTON_RIGHT, GUI_INPUT_UP);
|
|
_mapKey(&runner.params.keyMap, WIIMOTE_INPUT, WPAD_BUTTON_LEFT, GUI_INPUT_DOWN);
|
|
_mapKey(&runner.params.keyMap, WIIMOTE_INPUT, WPAD_BUTTON_UP, GUI_INPUT_LEFT);
|
|
_mapKey(&runner.params.keyMap, WIIMOTE_INPUT, WPAD_BUTTON_DOWN, GUI_INPUT_RIGHT);
|
|
|
|
_mapKey(&runner.params.keyMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_A, GUI_INPUT_SELECT);
|
|
_mapKey(&runner.params.keyMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_B, GUI_INPUT_BACK);
|
|
_mapKey(&runner.params.keyMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_HOME, GUI_INPUT_CANCEL);
|
|
_mapKey(&runner.params.keyMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_UP, GUI_INPUT_UP);
|
|
_mapKey(&runner.params.keyMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_DOWN, GUI_INPUT_DOWN);
|
|
_mapKey(&runner.params.keyMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_LEFT, GUI_INPUT_LEFT);
|
|
_mapKey(&runner.params.keyMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_RIGHT, GUI_INPUT_RIGHT);
|
|
|
|
|
|
float stretch = 0;
|
|
if (mCoreConfigGetFloatValue(&runner.config, "stretchWidth", &stretch)) {
|
|
wStretch = fminf(1.0f, fmaxf(0.5f, stretch));
|
|
}
|
|
if (mCoreConfigGetFloatValue(&runner.config, "stretchHeight", &stretch)) {
|
|
hStretch = fminf(1.0f, fmaxf(0.5f, stretch));
|
|
}
|
|
|
|
if (argc > 1) {
|
|
size_t i;
|
|
for (i = 0; runner.keySources[i].id; ++i) {
|
|
mInputMapLoad(&runner.params.keyMap, runner.keySources[i].id, mCoreConfigGetInput(&runner.config));
|
|
}
|
|
mGUIRun(&runner, argv[1]);
|
|
} else {
|
|
mGUIRunloop(&runner);
|
|
}
|
|
VIDEO_SetBlack(true);
|
|
VIDEO_Flush();
|
|
VIDEO_WaitVSync();
|
|
mGUIDeinit(&runner);
|
|
|
|
free(fifo);
|
|
free(texmem);
|
|
free(rescaleTexmem);
|
|
free(interframeTexmem);
|
|
|
|
free(outputBuffer);
|
|
GUIFontDestroy(font);
|
|
|
|
free(framebuffer[0]);
|
|
free(framebuffer[1]);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void _audioDMA(void) {
|
|
struct AudioBuffer* buffer = &audioBuffer[currentAudioBuffer];
|
|
if (buffer->size != SAMPLES) {
|
|
return;
|
|
}
|
|
DCFlushRange(buffer->samples, SAMPLES * sizeof(struct GBAStereoSample));
|
|
AUDIO_InitDMA((u32) buffer->samples, SAMPLES * sizeof(struct GBAStereoSample));
|
|
buffer->size = 0;
|
|
currentAudioBuffer = (currentAudioBuffer + 1) % BUFFERS;
|
|
}
|
|
|
|
static void _postAudioBuffer(struct mAVStream* stream, blip_t* left, blip_t* right) {
|
|
UNUSED(stream);
|
|
|
|
u32 level = 0;
|
|
_CPU_ISR_Disable(level);
|
|
struct AudioBuffer* buffer = &audioBuffer[nextAudioBuffer];
|
|
int available = blip_samples_avail(left);
|
|
if (available + buffer->size > SAMPLES) {
|
|
available = SAMPLES - buffer->size;
|
|
}
|
|
if (available > 0) {
|
|
// These appear to be reversed for AUDIO_InitDMA
|
|
blip_read_samples(left, &buffer->samples[buffer->size].right, available, true);
|
|
blip_read_samples(right, &buffer->samples[buffer->size].left, available, true);
|
|
buffer->size += available;
|
|
}
|
|
if (buffer->size == SAMPLES) {
|
|
int next = (nextAudioBuffer + 1) % BUFFERS;
|
|
if ((currentAudioBuffer + BUFFERS - next) % BUFFERS != 1) {
|
|
nextAudioBuffer = next;
|
|
}
|
|
if (!AUDIO_GetDMAEnableFlag()) {
|
|
_audioDMA();
|
|
AUDIO_StartDMA();
|
|
}
|
|
}
|
|
_CPU_ISR_Restore(level);
|
|
}
|
|
|
|
static void _drawStart(void) {
|
|
VIDEO_SetBlack(false);
|
|
|
|
u32 level = 0;
|
|
_CPU_ISR_Disable(level);
|
|
if (referenceRetraceCount > retraceCount) {
|
|
if (frameLimiter) {
|
|
VIDEO_WaitVSync();
|
|
}
|
|
referenceRetraceCount = retraceCount;
|
|
} else if (frameLimiter && referenceRetraceCount < retraceCount - 1) {
|
|
referenceRetraceCount = retraceCount - 1;
|
|
}
|
|
_CPU_ISR_Restore(level);
|
|
|
|
GX_SetZMode(GX_TRUE, GX_LEQUAL, GX_TRUE);
|
|
GX_SetColorUpdate(GX_TRUE);
|
|
|
|
GX_SetViewport(0, 0, vmode->fbWidth, vmode->efbHeight, 0, 1);
|
|
}
|
|
|
|
static void _drawEnd(void) {
|
|
GX_CopyDisp(framebuffer[whichFb], GX_TRUE);
|
|
GX_DrawDone();
|
|
VIDEO_SetNextFramebuffer(MEM_K0_TO_K1(framebuffer[whichFb]));
|
|
VIDEO_Flush();
|
|
whichFb = !whichFb;
|
|
|
|
u32 level = 0;
|
|
_CPU_ISR_Disable(level);
|
|
++referenceRetraceCount;
|
|
_CPU_ISR_Restore(level);
|
|
}
|
|
|
|
static void _setFrameLimiter(struct mGUIRunner* runner, bool limit) {
|
|
UNUSED(runner);
|
|
frameLimiter = limit;
|
|
}
|
|
|
|
static uint32_t _pollInput(const struct mInputMap* map) {
|
|
PAD_ScanPads();
|
|
u16 padkeys = PAD_ButtonsHeld(0);
|
|
|
|
WPAD_ScanPads();
|
|
u32 wiiPad = WPAD_ButtonsHeld(0);
|
|
u32 ext = 0;
|
|
WPAD_Probe(0, &ext);
|
|
|
|
int keys = 0;
|
|
keys |= mInputMapKeyBits(map, GCN1_INPUT, padkeys, 0);
|
|
keys |= mInputMapKeyBits(map, GCN2_INPUT, padkeys, 0);
|
|
keys |= mInputMapKeyBits(map, WIIMOTE_INPUT, wiiPad, 0);
|
|
if (ext == WPAD_EXP_CLASSIC) {
|
|
keys |= mInputMapKeyBits(map, CLASSIC_INPUT, wiiPad, 0);
|
|
}
|
|
int x = PAD_StickX(0);
|
|
int y = PAD_StickY(0);
|
|
int w_x = WPAD_StickX(0, 0);
|
|
int w_y = WPAD_StickY(0, 0);
|
|
if (x < -ANALOG_DEADZONE || w_x < -ANALOG_DEADZONE) {
|
|
keys |= 1 << GUI_INPUT_LEFT;
|
|
}
|
|
if (x > ANALOG_DEADZONE || w_x > ANALOG_DEADZONE) {
|
|
keys |= 1 << GUI_INPUT_RIGHT;
|
|
}
|
|
if (y < -ANALOG_DEADZONE || w_y < -ANALOG_DEADZONE) {
|
|
keys |= 1 << GUI_INPUT_DOWN;
|
|
}
|
|
if (y > ANALOG_DEADZONE || w_y > ANALOG_DEADZONE) {
|
|
keys |= 1 << GUI_INPUT_UP;
|
|
}
|
|
return keys;
|
|
}
|
|
|
|
static enum GUICursorState _pollCursor(unsigned* x, unsigned* y) {
|
|
ir_t ir;
|
|
WPAD_IR(0, &ir);
|
|
if (!ir.smooth_valid) {
|
|
return GUI_CURSOR_NOT_PRESENT;
|
|
}
|
|
*x = ir.sx;
|
|
*y = ir.sy;
|
|
WPAD_ScanPads();
|
|
u32 wiiPad = WPAD_ButtonsHeld(0);
|
|
if (wiiPad & WPAD_BUTTON_A) {
|
|
return GUI_CURSOR_DOWN;
|
|
}
|
|
return GUI_CURSOR_UP;
|
|
}
|
|
|
|
void _reproj(int w, int h) {
|
|
Mtx44 proj;
|
|
int top = (vmode->efbHeight * hAdjust - h) / 2;
|
|
int left = (vmode->fbWidth * wAdjust - w) / 2;
|
|
guOrtho(proj, -top, top + h, -left, left + w, 0, 300);
|
|
GX_LoadProjectionMtx(proj, GX_ORTHOGRAPHIC);
|
|
}
|
|
|
|
void _reproj2(int w, int h) {
|
|
Mtx44 proj;
|
|
int top = h * (1.0 - hStretch) / 2;
|
|
int left = w * (1.0 - wStretch) / 2;
|
|
guOrtho(proj, -top, h + top, -left, w + left, 0, 300);
|
|
GX_LoadProjectionMtx(proj, GX_ORTHOGRAPHIC);
|
|
}
|
|
|
|
void _guiPrepare(void) {
|
|
GX_SetNumTevStages(1);
|
|
_reproj2(vmode->fbWidth * guiScale * wAdjust, vmode->efbHeight * guiScale * hAdjust);
|
|
}
|
|
|
|
void _setup(struct mGUIRunner* runner) {
|
|
runner->core->setPeripheral(runner->core, mPERIPH_ROTATION, &rotation);
|
|
runner->core->setPeripheral(runner->core, mPERIPH_RUMBLE, &rumble);
|
|
runner->core->setAVStream(runner->core, &stream);
|
|
|
|
_mapKey(&runner->core->inputMap, GCN1_INPUT, PAD_BUTTON_A, GBA_KEY_A);
|
|
_mapKey(&runner->core->inputMap, GCN1_INPUT, PAD_BUTTON_B, GBA_KEY_B);
|
|
_mapKey(&runner->core->inputMap, GCN1_INPUT, PAD_BUTTON_START, GBA_KEY_START);
|
|
_mapKey(&runner->core->inputMap, GCN1_INPUT, PAD_BUTTON_X, GBA_KEY_SELECT);
|
|
_mapKey(&runner->core->inputMap, GCN2_INPUT, PAD_BUTTON_Y, GBA_KEY_SELECT);
|
|
_mapKey(&runner->core->inputMap, GCN1_INPUT, PAD_BUTTON_UP, GBA_KEY_UP);
|
|
_mapKey(&runner->core->inputMap, GCN1_INPUT, PAD_BUTTON_DOWN, GBA_KEY_DOWN);
|
|
_mapKey(&runner->core->inputMap, GCN1_INPUT, PAD_BUTTON_LEFT, GBA_KEY_LEFT);
|
|
_mapKey(&runner->core->inputMap, GCN1_INPUT, PAD_BUTTON_RIGHT, GBA_KEY_RIGHT);
|
|
_mapKey(&runner->core->inputMap, GCN1_INPUT, PAD_TRIGGER_L, GBA_KEY_L);
|
|
_mapKey(&runner->core->inputMap, GCN1_INPUT, PAD_TRIGGER_R, GBA_KEY_R);
|
|
|
|
_mapKey(&runner->core->inputMap, WIIMOTE_INPUT, WPAD_BUTTON_2, GBA_KEY_A);
|
|
_mapKey(&runner->core->inputMap, WIIMOTE_INPUT, WPAD_BUTTON_1, GBA_KEY_B);
|
|
_mapKey(&runner->core->inputMap, WIIMOTE_INPUT, WPAD_BUTTON_PLUS, GBA_KEY_START);
|
|
_mapKey(&runner->core->inputMap, WIIMOTE_INPUT, WPAD_BUTTON_MINUS, GBA_KEY_SELECT);
|
|
_mapKey(&runner->core->inputMap, WIIMOTE_INPUT, WPAD_BUTTON_RIGHT, GBA_KEY_UP);
|
|
_mapKey(&runner->core->inputMap, WIIMOTE_INPUT, WPAD_BUTTON_LEFT, GBA_KEY_DOWN);
|
|
_mapKey(&runner->core->inputMap, WIIMOTE_INPUT, WPAD_BUTTON_UP, GBA_KEY_LEFT);
|
|
_mapKey(&runner->core->inputMap, WIIMOTE_INPUT, WPAD_BUTTON_DOWN, GBA_KEY_RIGHT);
|
|
_mapKey(&runner->core->inputMap, WIIMOTE_INPUT, WPAD_BUTTON_B, GBA_KEY_L);
|
|
_mapKey(&runner->core->inputMap, WIIMOTE_INPUT, WPAD_BUTTON_A, GBA_KEY_R);
|
|
|
|
_mapKey(&runner->core->inputMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_A, GBA_KEY_A);
|
|
_mapKey(&runner->core->inputMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_B, GBA_KEY_B);
|
|
_mapKey(&runner->core->inputMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_PLUS, GBA_KEY_START);
|
|
_mapKey(&runner->core->inputMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_MINUS, GBA_KEY_SELECT);
|
|
_mapKey(&runner->core->inputMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_UP, GBA_KEY_UP);
|
|
_mapKey(&runner->core->inputMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_DOWN, GBA_KEY_DOWN);
|
|
_mapKey(&runner->core->inputMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_LEFT, GBA_KEY_LEFT);
|
|
_mapKey(&runner->core->inputMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_RIGHT, GBA_KEY_RIGHT);
|
|
_mapKey(&runner->core->inputMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_FULL_L, GBA_KEY_L);
|
|
_mapKey(&runner->core->inputMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_FULL_R, GBA_KEY_R);
|
|
|
|
struct mInputAxis desc = { GBA_KEY_RIGHT, GBA_KEY_LEFT, ANALOG_DEADZONE, -ANALOG_DEADZONE };
|
|
mInputBindAxis(&runner->core->inputMap, GCN1_INPUT, 0, &desc);
|
|
mInputBindAxis(&runner->core->inputMap, CLASSIC_INPUT, 0, &desc);
|
|
desc = (struct mInputAxis) { GBA_KEY_UP, GBA_KEY_DOWN, ANALOG_DEADZONE, -ANALOG_DEADZONE };
|
|
mInputBindAxis(&runner->core->inputMap, GCN1_INPUT, 1, &desc);
|
|
mInputBindAxis(&runner->core->inputMap, CLASSIC_INPUT, 1, &desc);
|
|
|
|
outputBuffer = memalign(32, TEX_W * TEX_H * BYTES_PER_PIXEL);
|
|
runner->core->setVideoBuffer(runner->core, outputBuffer, TEX_W);
|
|
|
|
nextAudioBuffer = 0;
|
|
currentAudioBuffer = 0;
|
|
int i;
|
|
for (i = 0; i < BUFFERS; ++i) {
|
|
audioBuffer[i].size = 0;
|
|
}
|
|
runner->core->setAudioBufferSize(runner->core, SAMPLES);
|
|
|
|
double ratio = GBAAudioCalculateRatio(1, audioSampleRate, 1);
|
|
blip_set_rates(runner->core->getAudioChannel(runner->core, 0), runner->core->frequency(runner->core), 48000 * ratio);
|
|
blip_set_rates(runner->core->getAudioChannel(runner->core, 1), runner->core->frequency(runner->core), 48000 * ratio);
|
|
|
|
frameLimiter = true;
|
|
}
|
|
|
|
void _gameUnloaded(struct mGUIRunner* runner) {
|
|
UNUSED(runner);
|
|
AUDIO_StopDMA();
|
|
frameLimiter = true;
|
|
VIDEO_SetBlack(true);
|
|
VIDEO_Flush();
|
|
VIDEO_WaitVSync();
|
|
}
|
|
|
|
void _gameLoaded(struct mGUIRunner* runner) {
|
|
reconfigureScreen(runner);
|
|
if (runner->core->platform(runner->core) == PLATFORM_GBA && ((struct GBA*) runner->core->board)->memory.hw.devices & HW_GYRO) {
|
|
int i;
|
|
for (i = 0; i < 6; ++i) {
|
|
u32 result = WPAD_SetMotionPlus(0, 1);
|
|
if (result == WPAD_ERR_NONE) {
|
|
break;
|
|
}
|
|
sleep(1);
|
|
}
|
|
}
|
|
memset(texmem, 0, TEX_W * TEX_H * BYTES_PER_PIXEL);
|
|
memset(interframeTexmem, 0, TEX_W * TEX_H * BYTES_PER_PIXEL);
|
|
_unpaused(runner);
|
|
}
|
|
|
|
void _unpaused(struct mGUIRunner* runner) {
|
|
u32 level = 0;
|
|
VIDEO_WaitVSync();
|
|
_CPU_ISR_Disable(level);
|
|
referenceRetraceCount = retraceCount;
|
|
_CPU_ISR_Restore(level);
|
|
|
|
unsigned mode;
|
|
if (mCoreConfigGetUIntValue(&runner->config, "videoMode", &mode) && mode < VM_MAX) {
|
|
if (mode != videoMode) {
|
|
reconfigureScreen(runner);
|
|
}
|
|
}
|
|
if (mCoreConfigGetUIntValue(&runner->config, "screenMode", &mode) && mode < SM_MAX) {
|
|
screenMode = mode;
|
|
}
|
|
if (mCoreConfigGetUIntValue(&runner->config, "filter", &mode) && mode < FM_MAX) {
|
|
filterMode = mode;
|
|
switch (mode) {
|
|
case FM_NEAREST:
|
|
case FM_LINEAR_2x:
|
|
default:
|
|
GX_InitTexObjFilterMode(&tex, GX_NEAR, GX_NEAR);
|
|
break;
|
|
case FM_LINEAR_1x:
|
|
GX_InitTexObjFilterMode(&tex, GX_LINEAR, GX_LINEAR);
|
|
break;
|
|
}
|
|
}
|
|
int fakeBool;
|
|
if (mCoreConfigGetIntValue(&runner->config, "interframeBlending", &fakeBool)) {
|
|
interframeBlending = fakeBool;
|
|
}
|
|
if (mCoreConfigGetIntValue(&runner->config, "sgb.borderCrop", &fakeBool)) {
|
|
sgbCrop = fakeBool;
|
|
}
|
|
|
|
float stretch;
|
|
if (mCoreConfigGetFloatValue(&runner->config, "stretchWidth", &stretch)) {
|
|
wStretch = fminf(1.0f, fmaxf(0.5f, stretch));
|
|
}
|
|
if (mCoreConfigGetFloatValue(&runner->config, "stretchHeight", &stretch)) {
|
|
hStretch = fminf(1.0f, fmaxf(0.5f, stretch));
|
|
}
|
|
}
|
|
|
|
void _prepareForFrame(struct mGUIRunner* runner) {
|
|
if (interframeBlending) {
|
|
memcpy(interframeTexmem, texmem, TEX_W * TEX_H * BYTES_PER_PIXEL);
|
|
}
|
|
}
|
|
|
|
void _drawFrame(struct mGUIRunner* runner, bool faded) {
|
|
runner->core->desiredVideoDimensions(runner->core, &corew, &coreh);
|
|
uint32_t color = 0xFFFFFF3F;
|
|
if (!faded) {
|
|
color |= 0xC0;
|
|
}
|
|
size_t x, y;
|
|
uint64_t* texdest = (uint64_t*) texmem;
|
|
uint64_t* texsrc = (uint64_t*) outputBuffer;
|
|
for (y = 0; y < coreh; y += 4) {
|
|
for (x = 0; x < corew >> 2; ++x) {
|
|
texdest[0 + x * 4 + y * 64] = texsrc[0 + x + y * 64];
|
|
texdest[1 + x * 4 + y * 64] = texsrc[64 + x + y * 64];
|
|
texdest[2 + x * 4 + y * 64] = texsrc[128 + x + y * 64];
|
|
texdest[3 + x * 4 + y * 64] = texsrc[192 + x + y * 64];
|
|
}
|
|
}
|
|
DCFlushRange(texdest, TEX_W * TEX_H * BYTES_PER_PIXEL);
|
|
if (interframeBlending) {
|
|
DCFlushRange(interframeTexmem, TEX_W * TEX_H * BYTES_PER_PIXEL);
|
|
}
|
|
|
|
if (faded || interframeBlending) {
|
|
GX_SetBlendMode(GX_BM_BLEND, GX_BL_SRCALPHA, GX_BL_INVSRCALPHA, GX_LO_NOOP);
|
|
} else {
|
|
GX_SetBlendMode(GX_BM_NONE, GX_BL_ONE, GX_BL_ZERO, GX_LO_NOOP);
|
|
}
|
|
GX_InvalidateTexAll();
|
|
if (interframeBlending) {
|
|
GX_LoadTexObj(&interframeTex, GX_TEXMAP0);
|
|
GX_LoadTexObj(&tex, GX_TEXMAP1);
|
|
GX_SetNumTevStages(2);
|
|
} else {
|
|
GX_LoadTexObj(&tex, GX_TEXMAP0);
|
|
GX_SetNumTevStages(1);
|
|
}
|
|
|
|
GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_TEX0, GX_TEX_ST, GX_F32, 0);
|
|
s16 vertWidth = corew;
|
|
s16 vertHeight = coreh;
|
|
|
|
if (filterMode == FM_LINEAR_2x) {
|
|
Mtx44 proj;
|
|
guOrtho(proj, 0, vmode->efbHeight, 0, vmode->fbWidth, 0, 300);
|
|
GX_LoadProjectionMtx(proj, GX_ORTHOGRAPHIC);
|
|
|
|
GX_Begin(GX_QUADS, GX_VTXFMT0, 4);
|
|
GX_Position2s16(0, TEX_H * 2);
|
|
GX_Color1u32(0xFFFFFFFF);
|
|
GX_TexCoord2f32(0, 1);
|
|
|
|
GX_Position2s16(TEX_W * 2, TEX_H * 2);
|
|
GX_Color1u32(0xFFFFFFFF);
|
|
GX_TexCoord2f32(1, 1);
|
|
|
|
GX_Position2s16(TEX_W * 2, 0);
|
|
GX_Color1u32(0xFFFFFFFF);
|
|
GX_TexCoord2f32(1, 0);
|
|
|
|
GX_Position2s16(0, 0);
|
|
GX_Color1u32(0xFFFFFFFF);
|
|
GX_TexCoord2f32(0, 0);
|
|
GX_End();
|
|
|
|
GX_SetTexCopySrc(0, 0, TEX_W * 2, TEX_H * 2);
|
|
GX_SetTexCopyDst(TEX_W * 2, TEX_H * 2, GX_TF_RGB565, GX_FALSE);
|
|
GX_CopyTex(rescaleTexmem, GX_TRUE);
|
|
GX_LoadTexObj(&rescaleTex, GX_TEXMAP0);
|
|
GX_SetNumTevStages(1);
|
|
if (!faded) {
|
|
GX_SetBlendMode(GX_BM_NONE, GX_BL_ONE, GX_BL_ZERO, GX_LO_NOOP);
|
|
}
|
|
}
|
|
|
|
if (screenMode == SM_PA) {
|
|
unsigned factorWidth = corew;
|
|
unsigned factorHeight = coreh;
|
|
if (sgbCrop && factorWidth == 256 && factorHeight == 224) {
|
|
factorWidth = GB_VIDEO_HORIZONTAL_PIXELS;
|
|
factorHeight = GB_VIDEO_VERTICAL_PIXELS;
|
|
}
|
|
|
|
int hfactor = (vmode->fbWidth * wStretch) / (factorWidth * wAdjust);
|
|
int vfactor = (vmode->efbHeight * hStretch) / (factorHeight * hAdjust);
|
|
if (hfactor > vfactor) {
|
|
scaleFactor = vfactor;
|
|
} else {
|
|
scaleFactor = hfactor;
|
|
}
|
|
|
|
vertWidth *= scaleFactor;
|
|
vertHeight *= scaleFactor;
|
|
|
|
_reproj(corew * scaleFactor, coreh * scaleFactor);
|
|
} else {
|
|
_reproj2(corew, coreh);
|
|
}
|
|
|
|
GX_Begin(GX_QUADS, GX_VTXFMT0, 4);
|
|
GX_Position2s16(0, vertHeight);
|
|
GX_Color1u32(color);
|
|
GX_TexCoord2f32(0, coreh / (float) TEX_H);
|
|
|
|
GX_Position2s16(vertWidth, vertHeight);
|
|
GX_Color1u32(color);
|
|
GX_TexCoord2f32(corew / (float) TEX_W, coreh / (float) TEX_H);
|
|
|
|
GX_Position2s16(vertWidth, 0);
|
|
GX_Color1u32(color);
|
|
GX_TexCoord2f32(corew / (float) TEX_W, 0);
|
|
|
|
GX_Position2s16(0, 0);
|
|
GX_Color1u32(color);
|
|
GX_TexCoord2f32(0, 0);
|
|
GX_End();
|
|
}
|
|
|
|
uint16_t _pollGameInput(struct mGUIRunner* runner) {
|
|
UNUSED(runner);
|
|
PAD_ScanPads();
|
|
u16 padkeys = PAD_ButtonsHeld(0);
|
|
WPAD_ScanPads();
|
|
u32 wiiPad = WPAD_ButtonsHeld(0);
|
|
u32 ext = 0;
|
|
WPAD_Probe(0, &ext);
|
|
uint16_t keys = mInputMapKeyBits(&runner->core->inputMap, GCN1_INPUT, padkeys, 0);
|
|
keys |= mInputMapKeyBits(&runner->core->inputMap, GCN2_INPUT, padkeys, 0);
|
|
keys |= mInputMapKeyBits(&runner->core->inputMap, WIIMOTE_INPUT, wiiPad, 0);
|
|
|
|
enum GBAKey angles = mInputMapAxis(&runner->core->inputMap, GCN1_INPUT, 0, PAD_StickX(0));
|
|
if (angles != GBA_KEY_NONE) {
|
|
keys |= 1 << angles;
|
|
}
|
|
angles = mInputMapAxis(&runner->core->inputMap, GCN1_INPUT, 1, PAD_StickY(0));
|
|
if (angles != GBA_KEY_NONE) {
|
|
keys |= 1 << angles;
|
|
}
|
|
if (ext == WPAD_EXP_CLASSIC) {
|
|
keys |= mInputMapKeyBits(&runner->core->inputMap, CLASSIC_INPUT, wiiPad, 0);
|
|
angles = mInputMapAxis(&runner->core->inputMap, CLASSIC_INPUT, 0, WPAD_StickX(0, 0));
|
|
if (angles != GBA_KEY_NONE) {
|
|
keys |= 1 << angles;
|
|
}
|
|
angles = mInputMapAxis(&runner->core->inputMap, CLASSIC_INPUT, 1, WPAD_StickY(0, 0));
|
|
if (angles != GBA_KEY_NONE) {
|
|
keys |= 1 << angles;
|
|
}
|
|
}
|
|
|
|
return keys;
|
|
}
|
|
|
|
void _incrementScreenMode(struct mGUIRunner* runner) {
|
|
UNUSED(runner);
|
|
int mode = screenMode | (filterMode << 1);
|
|
++mode;
|
|
screenMode = mode % SM_MAX;
|
|
filterMode = (mode >> 1) % FM_MAX;
|
|
mCoreConfigSetUIntValue(&runner->config, "screenMode", screenMode);
|
|
mCoreConfigSetUIntValue(&runner->config, "filter", filterMode);
|
|
switch (filterMode) {
|
|
case FM_NEAREST:
|
|
case FM_LINEAR_2x:
|
|
default:
|
|
GX_InitTexObjFilterMode(&tex, GX_NEAR, GX_NEAR);
|
|
break;
|
|
case FM_LINEAR_1x:
|
|
GX_InitTexObjFilterMode(&tex, GX_LINEAR, GX_LINEAR);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void _setRumble(struct mRumble* rumble, int enable) {
|
|
UNUSED(rumble);
|
|
WPAD_Rumble(0, enable);
|
|
if (enable) {
|
|
PAD_ControlMotor(0, PAD_MOTOR_RUMBLE);
|
|
} else {
|
|
PAD_ControlMotor(0, PAD_MOTOR_STOP);
|
|
}
|
|
}
|
|
|
|
void _sampleRotation(struct mRotationSource* source) {
|
|
UNUSED(source);
|
|
vec3w_t accel;
|
|
WPAD_Accel(0, &accel);
|
|
// These are swapped
|
|
tiltX = (0x1EA - accel.y) << 22;
|
|
tiltY = (0x1EA - accel.x) << 22;
|
|
|
|
// This doesn't seem to work at all with -TR remotes
|
|
struct expansion_t exp;
|
|
WPAD_Expansion(0, &exp);
|
|
if (exp.type != EXP_MOTION_PLUS) {
|
|
return;
|
|
}
|
|
gyroZ = exp.mp.rz - 0x1FA0;
|
|
gyroZ <<= 18;
|
|
}
|
|
|
|
int32_t _readTiltX(struct mRotationSource* source) {
|
|
UNUSED(source);
|
|
return tiltX;
|
|
}
|
|
|
|
int32_t _readTiltY(struct mRotationSource* source) {
|
|
UNUSED(source);
|
|
return tiltY;
|
|
}
|
|
|
|
int32_t _readGyroZ(struct mRotationSource* source) {
|
|
UNUSED(source);
|
|
return gyroZ;
|
|
}
|
|
|
|
static s8 WPAD_StickX(u8 chan, u8 right) {
|
|
struct expansion_t exp;
|
|
WPAD_Expansion(chan, &exp);
|
|
struct joystick_t* js = NULL;
|
|
|
|
switch (exp.type) {
|
|
case WPAD_EXP_NUNCHUK:
|
|
case WPAD_EXP_GUITARHERO3:
|
|
if (right == 0) {
|
|
js = &exp.nunchuk.js;
|
|
}
|
|
break;
|
|
case WPAD_EXP_CLASSIC:
|
|
if (right == 0) {
|
|
js = &exp.classic.ljs;
|
|
} else {
|
|
js = &exp.classic.rjs;
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (!js) {
|
|
return 0;
|
|
}
|
|
int centered = (int) js->pos.x - (int) js->center.x;
|
|
int range = (int) js->max.x - (int) js->min.x;
|
|
int value = (centered * 0xFF) / range;
|
|
if (value > 0x7F) {
|
|
return 0x7F;
|
|
}
|
|
if (value < -0x80) {
|
|
return -0x80;
|
|
}
|
|
return value;
|
|
}
|
|
|
|
static s8 WPAD_StickY(u8 chan, u8 right) {
|
|
struct expansion_t exp;
|
|
WPAD_Expansion(chan, &exp);
|
|
struct joystick_t* js = NULL;
|
|
|
|
switch (exp.type) {
|
|
case WPAD_EXP_NUNCHUK:
|
|
case WPAD_EXP_GUITARHERO3:
|
|
if (right == 0) {
|
|
js = &exp.nunchuk.js;
|
|
}
|
|
break;
|
|
case WPAD_EXP_CLASSIC:
|
|
if (right == 0) {
|
|
js = &exp.classic.ljs;
|
|
} else {
|
|
js = &exp.classic.rjs;
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (!js) {
|
|
return 0;
|
|
}
|
|
int centered = (int) js->pos.y - (int) js->center.y;
|
|
int range = (int) js->max.y - (int) js->min.y;
|
|
int value = (centered * 0xFF) / range;
|
|
if (value > 0x7F) {
|
|
return 0x7F;
|
|
}
|
|
if (value < -0x80) {
|
|
return -0x80;
|
|
}
|
|
return value;
|
|
}
|
|
|
|
void _retraceCallback(u32 count) {
|
|
u32 level = 0;
|
|
_CPU_ISR_Disable(level);
|
|
retraceCount = count;
|
|
_CPU_ISR_Restore(level);
|
|
}
|