GBA Context: Add GBAGUIRunner for ports and use it in the Wii version

This commit is contained in:
Jeffrey Pfau 2015-08-30 16:25:32 -07:00
parent e9c64a87bc
commit 178612a471
8 changed files with 349 additions and 227 deletions

View File

@ -42,18 +42,6 @@ bool GBAContextInit(struct GBAContext* context, const char* port) {
}
void GBAContextDeinit(struct GBAContext* context) {
if (context->bios) {
context->bios->close(context->bios);
context->bios = 0;
}
if (context->rom) {
context->rom->close(context->rom);
context->rom = 0;
}
if (context->save) {
context->save->close(context->save);
context->save = 0;
}
ARMDeinit(context->cpu);
GBADestroy(context->gba);
mappedMemoryFree(context->gba, 0);
@ -79,6 +67,22 @@ bool GBAContextLoadROM(struct GBAContext* context, const char* path, bool autolo
return true;
}
void GBAContextUnloadROM(struct GBAContext* context) {
GBAUnloadROM(context->gba);
if (context->bios) {
context->bios->close(context->bios);
context->bios = 0;
}
if (context->rom) {
context->rom->close(context->rom);
context->rom = 0;
}
if (context->save) {
context->save->close(context->save);
context->save = 0;
}
}
bool GBAContextLoadROMFromVFile(struct GBAContext* context, struct VFile* rom, struct VFile* save) {
context->rom = rom;
if (!GBAIsROM(context->rom)) {

View File

@ -31,6 +31,7 @@ bool GBAContextLoadROM(struct GBAContext* context, const char* path, bool autolo
bool GBAContextLoadROMFromVFile(struct GBAContext* context, struct VFile* rom, struct VFile* save);
bool GBAContextLoadBIOS(struct GBAContext* context, const char* path);
bool GBAContextLoadBIOSFromVFile(struct GBAContext* context, struct VFile* bios);
void GBAContextUnloadROM(struct GBAContext* context);
bool GBAContextStart(struct GBAContext* context);
void GBAContextStop(struct GBAContext* context);

View File

@ -0,0 +1,82 @@
/* 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/. */
#include "gui-runner.h"
#include "util/gui/file-select.h"
#include "util/gui/font.h"
void GBAGUIInit(struct GBAGUIRunner* runner, const char* port) {
GUIInit(&runner->params);
GBAContextInit(&runner->context, port);
if (runner->setup) {
runner->setup(runner);
}
}
void GBAGUIDeinit(struct GBAGUIRunner* runner) {
if (runner->teardown) {
runner->teardown(runner);
}
GBAContextDeinit(&runner->context);
}
void GBAGUIRunloop(struct GBAGUIRunner* runner) {
while (true) {
if (runner->params.guiPrepare) {
runner->params.guiPrepare();
}
char path[256];
if (!GUISelectFile(&runner->params, path, sizeof(path), GBAIsROM)) {
if (runner->params.guiFinish) {
runner->params.guiFinish();
}
return;
}
if (runner->params.guiFinish) {
runner->params.guiFinish();
}
// TODO: Message box API
runner->params.drawStart();
GUIFontPrint(runner->params.font, runner->params.width / 2, (GUIFontHeight(runner->params.font) + runner->params.height) / 2, GUI_TEXT_CENTER, 0xFFFFFFFF, "Loading...");
runner->params.drawEnd();
runner->params.drawStart();
GUIFontPrint(runner->params.font, runner->params.width / 2, (GUIFontHeight(runner->params.font) + runner->params.height) / 2, GUI_TEXT_CENTER, 0xFFFFFFFF, "Loading...");
runner->params.drawEnd();
if (!GBAContextLoadROM(&runner->context, path, true)) {
int i;
for (i = 0; i < 300; ++i) {
runner->params.drawStart();
GUIFontPrint(runner->params.font, runner->params.width / 2, (GUIFontHeight(runner->params.font) + runner->params.height) / 2, GUI_TEXT_CENTER, 0xFFFFFFFF, "Load failed!");
runner->params.drawEnd();
}
}
GBAContextStart(&runner->context);
if (runner->gameLoaded) {
runner->gameLoaded(runner);
}
while (true) {
int guiKeys = runner->params.pollInput();
if (guiKeys & (1 << GUI_INPUT_CANCEL)) {
break;
}
uint16_t keys = runner->pollGameInput(runner);
if (runner->prepareForFrame) {
runner->prepareForFrame(runner);
}
GBAContextFrame(&runner->context, keys);
if (runner->drawFrame) {
runner->drawFrame(runner, false);
}
}
GBAContextStop(&runner->context);
if (runner->gameUnloaded) {
runner->gameUnloaded(runner);
}
GBAContextUnloadROM(&runner->context);
}
}

View File

@ -0,0 +1,29 @@
/* 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/. */
#ifndef GUI_RUNNER_H
#define GUI_RUNNER_H
#include "gba/context/context.h"
#include "util/gui.h"
struct GBAGUIRunner {
struct GBAContext context;
struct GUIParams params;
void (*setup)(struct GBAGUIRunner*);
void (*teardown)(struct GBAGUIRunner*);
void (*gameLoaded)(struct GBAGUIRunner*);
void (*gameUnloaded)(struct GBAGUIRunner*);
void (*prepareForFrame)(struct GBAGUIRunner*);
void (*drawFrame)(struct GBAGUIRunner*, bool faded);
uint16_t (*pollGameInput)(struct GBAGUIRunner*);
};
void GBAGUIInit(struct GBAGUIRunner*, const char* port);
void GBAGUIDeinit(struct GBAGUIRunner*);
void GBAGUIRunloop(struct GBAGUIRunner*);
#endif

View File

@ -148,7 +148,7 @@ int main() {
struct GUIParams params = {
320, 240,
font, "/", _drawStart, _drawEnd, _pollInput,
font, "/", _drawStart, _drawEnd, _pollInput, 0, 0,
GUI_PARAMS_TRAIL
};

View File

@ -66,7 +66,7 @@ int main() {
GBAPSP2Setup();
struct GUIParams params = {
PSP2_HORIZONTAL_PIXELS, PSP2_VERTICAL_PIXELS,
font, "cache0:", _drawStart, _drawEnd, _pollInput,
font, "cache0:", _drawStart, _drawEnd, _pollInput, 0, 0,
GUI_PARAMS_TRAIL
};

View File

@ -14,6 +14,7 @@
#include "gba/renderers/video-software.h"
#include "gba/context/context.h"
#include "gba/context/gui-runner.h"
#include "util/gui.h"
#include "util/gui/file-select.h"
#include "util/gui/font.h"
@ -22,10 +23,7 @@
#define SAMPLES 1024
static void GBAWiiLog(struct GBAThread* thread, enum GBALogLevel level, const char* format, va_list args);
static void GBAWiiFrame(void);
static bool GBAWiiLoadGame(const char* path);
static void _postVideoFrame(struct GBAAVStream*, struct GBAVideoRenderer* renderer);
static void _audioDMA(void);
static void _setRumble(struct GBARumble* rumble, int enable);
static void _sampleRotation(struct GBARotationSource* source);
@ -36,10 +34,16 @@ static int32_t _readGyroZ(struct GBARotationSource* source);
static void _drawStart(void);
static void _drawEnd(void);
static int _pollInput(void);
static void _guiPrepare(void);
static void _setup(struct GBAGUIRunner* runner);
static void _gameLoaded(struct GBAGUIRunner* runner);
static void _gameUnloaded(struct GBAGUIRunner* runner);
static void _prepareForFrame(struct GBAGUIRunner* runner);
static void _drawFrame(struct GBAGUIRunner* runner, bool faded);
static uint16_t _pollGameInput(struct GBAGUIRunner* runner);
static struct GBAContext context;
static struct GBAVideoSoftwareRenderer renderer;
static struct GBAAVStream stream;
static struct GBARumble rumble;
static struct GBARotationSource rotation;
static FILE* logfile;
@ -109,9 +113,11 @@ int main() {
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);
@ -122,8 +128,6 @@ int main() {
GX_InvVtxCache();
GX_InvalidateTexAll();
Mtx44 proj;
guVector cam = { 0.0f, 0.0f, 0.0f };
guVector up = { 0.0f, 1.0f, 0.0f };
guVector look = { 0.0f, 0.0f, -1.0f };
@ -144,10 +148,6 @@ int main() {
logfile = fopen("/mgba.log", "w");
stream.postAudioFrame = 0;
stream.postAudioBuffer = 0;
stream.postVideoFrame = _postVideoFrame;
rumble.setRumble = _setRumble;
rotation.sample = _sampleRotation;
@ -155,212 +155,36 @@ int main() {
rotation.readTiltY = _readTiltY;
rotation.readGyroZ = _readGyroZ;
GBAContextInit(&context, 0);
struct GBAOptions opts = {
.useBios = true,
.logLevel = 0,
.idleOptimization = IDLE_LOOP_DETECT
struct GBAGUIRunner runner = {
.params = {
352, 230,
font, "/",
_drawStart, _drawEnd, _pollInput,
_guiPrepare, 0,
GUI_PARAMS_TRAIL
},
.setup = _setup,
.teardown = 0,
.gameLoaded = _gameLoaded,
.gameUnloaded = _gameUnloaded,
.prepareForFrame = _prepareForFrame,
.drawFrame = _drawFrame,
.pollGameInput = _pollGameInput
};
GBAConfigLoadDefaults(&context.config, &opts);
context.gba->logHandler = GBAWiiLog;
context.gba->stream = &stream;
context.gba->rumble = &rumble;
context.gba->rotationSource = &rotation;
GBAVideoSoftwareRendererCreate(&renderer);
renderer.outputBuffer = memalign(32, 256 * 256 * BYTES_PER_PIXEL);
renderer.outputBufferStride = 256;
context.renderer = &renderer.d;
GBAAudioResizeBuffer(&context.gba->audio, SAMPLES);
#if RESAMPLE_LIBRARY == RESAMPLE_BLIP_BUF
blip_set_rates(context.gba->audio.left, GBA_ARM7TDMI_FREQUENCY, 48000);
blip_set_rates(context.gba->audio.right, GBA_ARM7TDMI_FREQUENCY, 48000);
#endif
struct GUIParams params = {
352, 230,
font, "/", _drawStart, _drawEnd, _pollInput,
GUI_PARAMS_TRAIL
};
GUIInit(&params);
while (true) {
char path[256];
guOrtho(proj, -20, 240, 0, 352, 0, 300);
GX_LoadProjectionMtx(proj, GX_ORTHOGRAPHIC);
GX_SetVtxDesc(GX_VA_CLR0, GX_DIRECT);
if (!GUISelectFile(&params, path, sizeof(path), GBAIsROM) || !GBAWiiLoadGame(path)) {
break;
}
WPAD_SetDataFormat(0, WPAD_FMT_BTNS_ACC);
GBAContextStart(&context);
if (context.gba->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);
}
}
guOrtho(proj, -10, VIDEO_VERTICAL_PIXELS + 10, 0, VIDEO_HORIZONTAL_PIXELS, 0, 300);
GX_LoadProjectionMtx(proj, GX_ORTHOGRAPHIC);
GX_SetVtxDesc(GX_VA_CLR0, GX_NONE);
while (true) {
#if RESAMPLE_LIBRARY == RESAMPLE_BLIP_BUF
int available = blip_samples_avail(context.gba->audio.left);
if (available + audioBufferSize > SAMPLES) {
available = SAMPLES - audioBufferSize;
}
available &= ~((32 / sizeof(struct GBAStereoSample)) - 1); // Force align to 32 bytes
if (available > 0) {
blip_read_samples(context.gba->audio.left, &audioBuffer[currentAudioBuffer][audioBufferSize].left, available, true);
blip_read_samples(context.gba->audio.right, &audioBuffer[currentAudioBuffer][audioBufferSize].right, available, true);
audioBufferSize += available;
}
if (audioBufferSize == SAMPLES && !AUDIO_GetDMAEnableFlag()) {
_audioDMA();
AUDIO_StartDMA();
}
#endif
PAD_ScanPads();
u16 padkeys = PAD_ButtonsHeld(0);
WPAD_ScanPads();
u32 wiiPad = WPAD_ButtonsHeld(0);
u32 ext = 0;
uint16_t keys = 0;
WPAD_Probe(0, &ext);
if ((padkeys & PAD_BUTTON_A) || (wiiPad & WPAD_BUTTON_2) ||
((ext == WPAD_EXP_CLASSIC) && (wiiPad & (WPAD_CLASSIC_BUTTON_A | WPAD_CLASSIC_BUTTON_Y)))) {
keys |= 1 << GBA_KEY_A;
}
if ((padkeys & PAD_BUTTON_B) || (wiiPad & WPAD_BUTTON_1) ||
((ext == WPAD_EXP_CLASSIC) && (wiiPad & (WPAD_CLASSIC_BUTTON_B | WPAD_CLASSIC_BUTTON_X)))) {
keys |= 1 << GBA_KEY_B;
}
if ((padkeys & PAD_TRIGGER_L) || (wiiPad & WPAD_BUTTON_B) ||
((ext == WPAD_EXP_CLASSIC) && (wiiPad & WPAD_CLASSIC_BUTTON_FULL_L))) {
keys |= 1 << GBA_KEY_L;
}
if ((padkeys & PAD_TRIGGER_R) || (wiiPad & WPAD_BUTTON_A) ||
((ext == WPAD_EXP_CLASSIC) && (wiiPad & WPAD_CLASSIC_BUTTON_FULL_R))) {
keys |= 1 << GBA_KEY_R;
}
if ((padkeys & PAD_BUTTON_START) || (wiiPad & WPAD_BUTTON_PLUS) ||
((ext == WPAD_EXP_CLASSIC) && (wiiPad & WPAD_CLASSIC_BUTTON_PLUS))) {
keys |= 1 << GBA_KEY_START;
}
if ((padkeys & (PAD_BUTTON_X | PAD_BUTTON_Y)) || (wiiPad & WPAD_BUTTON_MINUS) ||
((ext == WPAD_EXP_CLASSIC) && (wiiPad & WPAD_CLASSIC_BUTTON_MINUS))) {
keys |= 1 << GBA_KEY_SELECT;
}
if ((padkeys & PAD_BUTTON_LEFT) || (wiiPad & WPAD_BUTTON_UP) ||
((ext == WPAD_EXP_CLASSIC) && (wiiPad & WPAD_CLASSIC_BUTTON_LEFT))) {
keys |= 1 << GBA_KEY_LEFT;
}
if ((padkeys & PAD_BUTTON_RIGHT) || (wiiPad & WPAD_BUTTON_DOWN) ||
((ext == WPAD_EXP_CLASSIC) && (wiiPad & WPAD_CLASSIC_BUTTON_RIGHT))) {
keys |= 1 << GBA_KEY_RIGHT;
}
if ((padkeys & PAD_BUTTON_UP) || (wiiPad & WPAD_BUTTON_RIGHT) ||
((ext == WPAD_EXP_CLASSIC) && (wiiPad & WPAD_CLASSIC_BUTTON_UP))) {
keys |= 1 << GBA_KEY_UP;
}
if ((padkeys & PAD_BUTTON_DOWN) || (wiiPad & WPAD_BUTTON_LEFT) ||
((ext == WPAD_EXP_CLASSIC) && (wiiPad & WPAD_CLASSIC_BUTTON_DOWN))) {
keys |= 1 << GBA_KEY_DOWN;
}
int x = PAD_StickX(0);
int y = PAD_StickY(0);
if (x < -0x40) {
keys |= 1 << GBA_KEY_LEFT;
}
if (x > 0x40) {
keys |= 1 << GBA_KEY_RIGHT;
}
if (y < -0x40) {
keys |= 1 << GBA_KEY_DOWN;
}
if (y > 0x40) {
keys |= 1 << GBA_KEY_UP;
}
if ((padkeys & PAD_TRIGGER_Z) || (wiiPad & WPAD_BUTTON_HOME) || (wiiPad & WPAD_CLASSIC_BUTTON_HOME)) {
break;
}
GBAContextFrame(&context, keys);
}
AUDIO_StopDMA();
GBAContextStop(&context);
}
GBAGUIInit(&runner, 0);
GBAGUIRunloop(&runner);
GBAGUIDeinit(&runner);
fclose(logfile);
free(fifo);
GBAContextDeinit(&context);
free(renderer.outputBuffer);
GUIFontDestroy(font);
return 0;
}
static void GBAWiiFrame(void) {
size_t x, y;
uint64_t* texdest = (uint64_t*) texmem;
uint64_t* texsrc = (uint64_t*) renderer.outputBuffer;
for (y = 0; y < VIDEO_VERTICAL_PIXELS; y += 4) {
for (x = 0; x < VIDEO_HORIZONTAL_PIXELS >> 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, 256 * 256 * BYTES_PER_PIXEL);
_drawStart();
GX_SetBlendMode(GX_BM_NONE, GX_BL_ONE, GX_BL_ZERO, GX_LO_SET);
GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_TEX0, GX_TEX_ST, GX_S16, 0);
GX_InvalidateTexAll();
GX_LoadTexObj(&tex, GX_TEXMAP0);
GX_Begin(GX_QUADS, GX_VTXFMT0, 4);
GX_Position2s16(0, 256);
GX_TexCoord2s16(0, 1);
GX_Position2s16(256, 256);
GX_TexCoord2s16(1, 1);
GX_Position2s16(256, 0);
GX_TexCoord2s16(1, 0);
GX_Position2s16(0, 0);
GX_TexCoord2s16(0, 0);
GX_End();
_drawEnd();
}
bool GBAWiiLoadGame(const char* path) {
_drawStart();
GUIFontPrintf(font, 176, 120, GUI_TEXT_CENTER, 0xFFFFFFFF, "Loading...");
_drawEnd();
_drawStart();
GUIFontPrintf(font, 176, 120, GUI_TEXT_CENTER, 0xFFFFFFFF, "Loading...");
_drawEnd();
return GBAContextLoadROM(&context, path, true);
}
void GBAWiiLog(struct GBAThread* thread, enum GBALogLevel level, const char* format, va_list args) {
UNUSED(thread);
UNUSED(level);
@ -372,12 +196,6 @@ void GBAWiiLog(struct GBAThread* thread, enum GBALogLevel level, const char* for
fflush(logfile);
}
static void _postVideoFrame(struct GBAAVStream* stream, struct GBAVideoRenderer* renderer) {
UNUSED(stream);
UNUSED(renderer);
GBAWiiFrame();
}
static void _audioDMA(void) {
if (!audioBufferSize) {
return;
@ -461,6 +279,192 @@ static int _pollInput(void) {
return keys;
}
void _guiPrepare(void) {
Mtx44 proj;
guOrtho(proj, -20, 240, 0, 352, 0, 300);
GX_LoadProjectionMtx(proj, GX_ORTHOGRAPHIC);
}
void _setup(struct GBAGUIRunner* runner) {
struct GBAOptions opts = {
.useBios = true,
.logLevel = 0,
.idleOptimization = IDLE_LOOP_DETECT
};
GBAConfigLoadDefaults(&runner->context.config, &opts);
runner->context.gba->logHandler = GBAWiiLog;
runner->context.gba->rumble = &rumble;
runner->context.gba->rotationSource = &rotation;
GBAVideoSoftwareRendererCreate(&renderer);
renderer.outputBuffer = memalign(32, 256 * 256 * BYTES_PER_PIXEL);
renderer.outputBufferStride = 256;
runner->context.renderer = &renderer.d;
GBAAudioResizeBuffer(&runner->context.gba->audio, SAMPLES);
#if RESAMPLE_LIBRARY == RESAMPLE_BLIP_BUF
blip_set_rates(runner->context.gba->audio.left, GBA_ARM7TDMI_FREQUENCY, 48000);
blip_set_rates(runner->context.gba->audio.right, GBA_ARM7TDMI_FREQUENCY, 48000);
#endif
}
void _gameUnloaded(struct GBAGUIRunner* runner) {
UNUSED(runner);
AUDIO_StopDMA();
}
void _gameLoaded(struct GBAGUIRunner* runner) {
WPAD_SetDataFormat(0, WPAD_FMT_BTNS_ACC);
if (runner->context.gba->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);
}
}
Mtx44 proj;
guOrtho(proj, -10, VIDEO_VERTICAL_PIXELS + 10, 0, VIDEO_HORIZONTAL_PIXELS, 0, 300);
GX_LoadProjectionMtx(proj, GX_ORTHOGRAPHIC);
}
void _prepareForFrame(struct GBAGUIRunner* runner) {
#if RESAMPLE_LIBRARY == RESAMPLE_BLIP_BUF
int available = blip_samples_avail(runner->context.gba->audio.left);
if (available + audioBufferSize > SAMPLES) {
available = SAMPLES - audioBufferSize;
}
available &= ~((32 / sizeof(struct GBAStereoSample)) - 1); // Force align to 32 bytes
if (available > 0) {
blip_read_samples(runner->context.gba->audio.left, &audioBuffer[currentAudioBuffer][audioBufferSize].left, available, true);
blip_read_samples(runner->context.gba->audio.right, &audioBuffer[currentAudioBuffer][audioBufferSize].right, available, true);
audioBufferSize += available;
}
if (audioBufferSize == SAMPLES && !AUDIO_GetDMAEnableFlag()) {
_audioDMA();
AUDIO_StartDMA();
}
#endif
}
void _drawFrame(struct GBAGUIRunner* runner, bool faded) {
uint32_t color = 0xFFFFFF3F;
if (!faded) {
color |= 0xC0;
}
size_t x, y;
uint64_t* texdest = (uint64_t*) texmem;
uint64_t* texsrc = (uint64_t*) renderer.outputBuffer;
for (y = 0; y < VIDEO_VERTICAL_PIXELS; y += 4) {
for (x = 0; x < VIDEO_HORIZONTAL_PIXELS >> 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, 256 * 256 * BYTES_PER_PIXEL);
_drawStart();
GX_SetBlendMode(GX_BM_NONE, GX_BL_ONE, GX_BL_ZERO, GX_LO_SET);
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_InvalidateTexAll();
GX_LoadTexObj(&tex, GX_TEXMAP0);
GX_Begin(GX_QUADS, GX_VTXFMT0, 4);
GX_Position2s16(0, 256);
GX_Color1u32(color);
GX_TexCoord2s16(0, 1);
GX_Position2s16(256, 256);
GX_Color1u32(color);
GX_TexCoord2s16(1, 1);
GX_Position2s16(256, 0);
GX_Color1u32(color);
GX_TexCoord2s16(1, 0);
GX_Position2s16(0, 0);
GX_Color1u32(color);
GX_TexCoord2s16(0, 0);
GX_End();
_drawEnd();
}
uint16_t _pollGameInput(struct GBAGUIRunner* runner) {
UNUSED(runner);
PAD_ScanPads();
u16 padkeys = PAD_ButtonsHeld(0);
WPAD_ScanPads();
u32 wiiPad = WPAD_ButtonsHeld(0);
u32 ext = 0;
uint16_t keys = 0;
WPAD_Probe(0, &ext);
if ((padkeys & PAD_BUTTON_A) || (wiiPad & WPAD_BUTTON_2) ||
((ext == WPAD_EXP_CLASSIC) && (wiiPad & (WPAD_CLASSIC_BUTTON_A | WPAD_CLASSIC_BUTTON_Y)))) {
keys |= 1 << GBA_KEY_A;
}
if ((padkeys & PAD_BUTTON_B) || (wiiPad & WPAD_BUTTON_1) ||
((ext == WPAD_EXP_CLASSIC) && (wiiPad & (WPAD_CLASSIC_BUTTON_B | WPAD_CLASSIC_BUTTON_X)))) {
keys |= 1 << GBA_KEY_B;
}
if ((padkeys & PAD_TRIGGER_L) || (wiiPad & WPAD_BUTTON_B) ||
((ext == WPAD_EXP_CLASSIC) && (wiiPad & WPAD_CLASSIC_BUTTON_FULL_L))) {
keys |= 1 << GBA_KEY_L;
}
if ((padkeys & PAD_TRIGGER_R) || (wiiPad & WPAD_BUTTON_A) ||
((ext == WPAD_EXP_CLASSIC) && (wiiPad & WPAD_CLASSIC_BUTTON_FULL_R))) {
keys |= 1 << GBA_KEY_R;
}
if ((padkeys & PAD_BUTTON_START) || (wiiPad & WPAD_BUTTON_PLUS) ||
((ext == WPAD_EXP_CLASSIC) && (wiiPad & WPAD_CLASSIC_BUTTON_PLUS))) {
keys |= 1 << GBA_KEY_START;
}
if ((padkeys & (PAD_BUTTON_X | PAD_BUTTON_Y)) || (wiiPad & WPAD_BUTTON_MINUS) ||
((ext == WPAD_EXP_CLASSIC) && (wiiPad & WPAD_CLASSIC_BUTTON_MINUS))) {
keys |= 1 << GBA_KEY_SELECT;
}
if ((padkeys & PAD_BUTTON_LEFT) || (wiiPad & WPAD_BUTTON_UP) ||
((ext == WPAD_EXP_CLASSIC) && (wiiPad & WPAD_CLASSIC_BUTTON_LEFT))) {
keys |= 1 << GBA_KEY_LEFT;
}
if ((padkeys & PAD_BUTTON_RIGHT) || (wiiPad & WPAD_BUTTON_DOWN) ||
((ext == WPAD_EXP_CLASSIC) && (wiiPad & WPAD_CLASSIC_BUTTON_RIGHT))) {
keys |= 1 << GBA_KEY_RIGHT;
}
if ((padkeys & PAD_BUTTON_UP) || (wiiPad & WPAD_BUTTON_RIGHT) ||
((ext == WPAD_EXP_CLASSIC) && (wiiPad & WPAD_CLASSIC_BUTTON_UP))) {
keys |= 1 << GBA_KEY_UP;
}
if ((padkeys & PAD_BUTTON_DOWN) || (wiiPad & WPAD_BUTTON_LEFT) ||
((ext == WPAD_EXP_CLASSIC) && (wiiPad & WPAD_CLASSIC_BUTTON_DOWN))) {
keys |= 1 << GBA_KEY_DOWN;
}
int x = PAD_StickX(0);
int y = PAD_StickY(0);
if (x < -0x40) {
keys |= 1 << GBA_KEY_LEFT;
}
if (x > 0x40) {
keys |= 1 << GBA_KEY_RIGHT;
}
if (y < -0x40) {
keys |= 1 << GBA_KEY_DOWN;
}
if (y > 0x40) {
keys |= 1 << GBA_KEY_UP;
}
return keys;
}
void _setRumble(struct GBARumble* rumble, int enable) {
UNUSED(rumble);
WPAD_Rumble(0, enable);

View File

@ -35,6 +35,8 @@ struct GUIParams {
void (*drawStart)(void);
void (*drawEnd)(void);
int (*pollInput)(void);
void (*guiPrepare)(void);
void (*guiFinish)(void);
// State
int inputHistory[GUI_INPUT_MAX];
@ -44,7 +46,7 @@ struct GUIParams {
size_t fileIndex;
};
#define GUI_PARAMS_TRAIL {}, ""
#define GUI_PARAMS_TRAIL {}, "", 0
void GUIInit(struct GUIParams* params);
void GUIPollInput(struct GUIParams* params, int* newInput, int* heldInput);