1071 lines
29 KiB
C
1071 lines
29 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/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
|
|
|
|
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 1024
|
|
#define GUI_SCALE 1.35f
|
|
#define GUI_SCALE_240p 2.0f
|
|
|
|
static void _retraceCallback(u32 count);
|
|
|
|
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 _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 mRumble rumble;
|
|
static struct mRotationSource rotation;
|
|
static GXRModeObj* vmode;
|
|
static float wAdjust;
|
|
static float hAdjust;
|
|
static float wStretch = 1.0f;
|
|
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 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;
|
|
|
|
uint32_t* romBuffer;
|
|
size_t romBufferSize;
|
|
|
|
static void* framebuffer[2] = { 0, 0 };
|
|
static int whichFb = 0;
|
|
|
|
static struct GBAStereoSample audioBuffer[3][SAMPLES] __attribute__((__aligned__(32)));
|
|
static volatile size_t audioBufferSize = 0;
|
|
static volatile int currentAudioBuffer = 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;
|
|
}
|
|
|
|
free(framebuffer[0]);
|
|
free(framebuffer[1]);
|
|
|
|
VIDEO_SetBlack(true);
|
|
VIDEO_Configure(vmode);
|
|
|
|
framebuffer[0] = SYS_AllocateFramebuffer(vmode);
|
|
framebuffer[1] = SYS_AllocateFramebuffer(vmode);
|
|
VIDEO_ClearFrameBuffer(vmode, framebuffer[0], COLOR_BLACK);
|
|
VIDEO_ClearFrameBuffer(vmode, framebuffer[1], COLOR_BLACK);
|
|
|
|
VIDEO_SetNextFramebuffer(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);
|
|
|
|
runner->core->desiredVideoDimensions(runner->core, &corew, &coreh);
|
|
int hfactor = vmode->fbWidth / (corew * wAdjust);
|
|
int vfactor = vmode->efbHeight / (coreh * hAdjust);
|
|
if (hfactor > vfactor) {
|
|
scaleFactor = vfactor;
|
|
} else {
|
|
scaleFactor = hfactor;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
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 = anonymousMemoryMap(romBufferSize);
|
|
#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_SetNumChans(1);
|
|
GX_SetNumTexGens(1);
|
|
GX_SetTevOrder(GX_TEVSTAGE0, GX_TEXCOORD0, GX_TEXMAP0, GX_COLOR0A0);
|
|
GX_SetTevOp(GX_TEVSTAGE0, GX_MODULATE);
|
|
|
|
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);
|
|
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;
|
|
|
|
struct mGUIRunner runner = {
|
|
.params = {
|
|
720, 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
|
|
}
|
|
},
|
|
.nConfigExtra = 3,
|
|
.setup = _setup,
|
|
.teardown = 0,
|
|
.gameLoaded = _gameLoaded,
|
|
.gameUnloaded = _gameUnloaded,
|
|
.prepareForFrame = 0,
|
|
.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_Y, 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_X, 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);
|
|
|
|
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);
|
|
|
|
#ifdef FIXED_ROM_BUFFER
|
|
mappedMemoryFree(romBuffer, romBufferSize);
|
|
#endif
|
|
|
|
free(fifo);
|
|
free(texmem);
|
|
free(rescaleTexmem);
|
|
|
|
free(outputBuffer);
|
|
GUIFontDestroy(font);
|
|
|
|
free(framebuffer[0]);
|
|
free(framebuffer[1]);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void _audioDMA(void) {
|
|
if (!audioBufferSize) {
|
|
return;
|
|
}
|
|
DCFlushRange(audioBuffer[currentAudioBuffer], audioBufferSize * sizeof(struct GBAStereoSample));
|
|
AUDIO_InitDMA((u32) audioBuffer[currentAudioBuffer], audioBufferSize * sizeof(struct GBAStereoSample));
|
|
currentAudioBuffer = (currentAudioBuffer + 1) % 3;
|
|
audioBufferSize = 0;
|
|
}
|
|
|
|
static void _drawStart(void) {
|
|
VIDEO_SetBlack(false);
|
|
|
|
u32 level = 0;
|
|
_CPU_ISR_Disable(level);
|
|
if (referenceRetraceCount > retraceCount) {
|
|
if (frameLimiter) {
|
|
VIDEO_WaitVSync();
|
|
}
|
|
referenceRetraceCount = retraceCount;
|
|
}
|
|
_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(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 < -0x20 || w_x < -0x20) {
|
|
keys |= 1 << GUI_INPUT_LEFT;
|
|
}
|
|
if (x > 0x20 || w_x > 0x20) {
|
|
keys |= 1 << GUI_INPUT_RIGHT;
|
|
}
|
|
if (y < -0x20 || w_y <- 0x20) {
|
|
keys |= 1 << GUI_INPUT_DOWN;
|
|
}
|
|
if (y > 0x20 || w_y > 0x20) {
|
|
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) {
|
|
_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);
|
|
|
|
_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, 0x20, -0x20 };
|
|
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, 0x20, -0x20 };
|
|
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);
|
|
|
|
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);
|
|
_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;
|
|
}
|
|
}
|
|
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 _drawFrame(struct mGUIRunner* runner, bool faded) {
|
|
int available = blip_samples_avail(runner->core->getAudioChannel(runner->core, 0));
|
|
if (available + audioBufferSize > SAMPLES) {
|
|
available = SAMPLES - audioBufferSize;
|
|
}
|
|
available &= ~((32 / sizeof(struct GBAStereoSample)) - 1); // Force align to 32 bytes
|
|
if (available > 0) {
|
|
// These appear to be reversed for AUDIO_InitDMA
|
|
blip_read_samples(runner->core->getAudioChannel(runner->core, 0), &audioBuffer[currentAudioBuffer][audioBufferSize].right, available, true);
|
|
blip_read_samples(runner->core->getAudioChannel(runner->core, 1), &audioBuffer[currentAudioBuffer][audioBufferSize].left, available, true);
|
|
audioBufferSize += available;
|
|
}
|
|
if (audioBufferSize == SAMPLES && !AUDIO_GetDMAEnableFlag()) {
|
|
_audioDMA();
|
|
AUDIO_StartDMA();
|
|
}
|
|
|
|
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 (faded) {
|
|
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();
|
|
GX_LoadTexObj(&tex, GX_TEXMAP0);
|
|
|
|
GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_TEX0, GX_TEX_ST, GX_S16, 0);
|
|
s16 vertWidth = TEX_W;
|
|
s16 vertHeight = TEX_H;
|
|
|
|
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_TexCoord2s16(0, 1);
|
|
|
|
GX_Position2s16(TEX_W * 2, TEX_H * 2);
|
|
GX_Color1u32(0xFFFFFFFF);
|
|
GX_TexCoord2s16(1, 1);
|
|
|
|
GX_Position2s16(TEX_W * 2, 0);
|
|
GX_Color1u32(0xFFFFFFFF);
|
|
GX_TexCoord2s16(1, 0);
|
|
|
|
GX_Position2s16(0, 0);
|
|
GX_Color1u32(0xFFFFFFFF);
|
|
GX_TexCoord2s16(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);
|
|
}
|
|
|
|
if (screenMode == SM_PA) {
|
|
vertWidth *= scaleFactor;
|
|
vertHeight *= scaleFactor;
|
|
}
|
|
|
|
if (screenMode == SM_PA) {
|
|
_reproj(corew * scaleFactor, coreh * scaleFactor);
|
|
} else {
|
|
_reproj2(corew, coreh);
|
|
}
|
|
|
|
GX_Begin(GX_QUADS, GX_VTXFMT0, 4);
|
|
GX_Position2s16(0, vertHeight);
|
|
GX_Color1u32(color);
|
|
GX_TexCoord2s16(0, 1);
|
|
|
|
GX_Position2s16(vertWidth, vertHeight);
|
|
GX_Color1u32(color);
|
|
GX_TexCoord2s16(1, 1);
|
|
|
|
GX_Position2s16(vertWidth, 0);
|
|
GX_Color1u32(color);
|
|
GX_TexCoord2s16(1, 0);
|
|
|
|
GX_Position2s16(0, 0);
|
|
GX_Color1u32(color);
|
|
GX_TexCoord2s16(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) {
|
|
float mag = 0.0;
|
|
float ang = 0.0;
|
|
WPADData *data = WPAD_Data(chan);
|
|
|
|
switch (data->exp.type) {
|
|
case WPAD_EXP_NUNCHUK:
|
|
case WPAD_EXP_GUITARHERO3:
|
|
if (right == 0) {
|
|
mag = data->exp.nunchuk.js.mag;
|
|
ang = data->exp.nunchuk.js.ang;
|
|
}
|
|
break;
|
|
case WPAD_EXP_CLASSIC:
|
|
if (right == 0) {
|
|
mag = data->exp.classic.ljs.mag;
|
|
ang = data->exp.classic.ljs.ang;
|
|
} else {
|
|
mag = data->exp.classic.rjs.mag;
|
|
ang = data->exp.classic.rjs.ang;
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
/* calculate X value (angle need to be converted into radian) */
|
|
if (mag > 1.0) {
|
|
mag = 1.0;
|
|
} else if (mag < -1.0) {
|
|
mag = -1.0;
|
|
}
|
|
double val = mag * sinf(M_PI * ang / 180.0f);
|
|
|
|
return (s8)(val * 128.0f);
|
|
}
|
|
|
|
static s8 WPAD_StickY(u8 chan, u8 right) {
|
|
float mag = 0.0;
|
|
float ang = 0.0;
|
|
WPADData *data = WPAD_Data(chan);
|
|
|
|
switch (data->exp.type) {
|
|
case WPAD_EXP_NUNCHUK:
|
|
case WPAD_EXP_GUITARHERO3:
|
|
if (right == 0) {
|
|
mag = data->exp.nunchuk.js.mag;
|
|
ang = data->exp.nunchuk.js.ang;
|
|
}
|
|
break;
|
|
case WPAD_EXP_CLASSIC:
|
|
if (right == 0) {
|
|
mag = data->exp.classic.ljs.mag;
|
|
ang = data->exp.classic.ljs.ang;
|
|
} else {
|
|
mag = data->exp.classic.rjs.mag;
|
|
ang = data->exp.classic.rjs.ang;
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
/* calculate X value (angle need to be converted into radian) */
|
|
if (mag > 1.0) {
|
|
mag = 1.0;
|
|
} else if (mag < -1.0) {
|
|
mag = -1.0;
|
|
}
|
|
double val = mag * cosf(M_PI * ang / 180.0f);
|
|
|
|
return (s8)(val * 128.0f);
|
|
}
|
|
|
|
void _retraceCallback(u32 count) {
|
|
u32 level = 0;
|
|
_CPU_ISR_Disable(level);
|
|
retraceCount = count;
|
|
_CPU_ISR_Restore(level);
|
|
}
|