GUI: Input remapping, part 1
jump to
@@ -1,4 +1,4 @@
-/* Copyright (c) 2013-2015 Jeffrey Pfau +/* Copyright (c) 2013-2016 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@@ -6,8 +6,13 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "gui-config.h" #include "gba/gui/gui-runner.h" +#include "gba/gui/remap.h" #include "util/gui/file-select.h" #include "util/gui/menu.h" + +#ifndef GUI_MAX_INPUTS +#define GUI_MAX_INPUTS 7 +#endif void GBAGUIShowConfig(struct GBAGUIRunner* runner, struct GUIMenuItem* extra, size_t nExtra) { struct GUIMenu menu = {@@ -22,8 +27,9 @@ .data = "frameskip",
.submenu = 0, .state = 0, .validStates = (const char*[]) { - "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", 0 - } + "0", "1", "2", "3", "4", "5", "6", "7", "8", "9" + }, + .nStates = 10 }; *GUIMenuItemListAppend(&menu.items) = (struct GUIMenuItem) { .title = "Show framerate",@@ -31,8 +37,9 @@ .data = "fpsCounter",
.submenu = 0, .state = false, .validStates = (const char*[]) { - "Off", "On", 0 - } + "Off", "On" + }, + .nStates = 2 }; *GUIMenuItemListAppend(&menu.items) = (struct GUIMenuItem) { .title = "Use BIOS if found",@@ -40,20 +47,38 @@ .data = "useBios",
.submenu = 0, .state = true, .validStates = (const char*[]) { - "Off", "On", 0 - } + "Off", "On" + }, + .nStates = 2 }; *GUIMenuItemListAppend(&menu.items) = (struct GUIMenuItem) { .title = "Select BIOS path", .data = "bios", }; size_t i; + const char* mapNames[GUI_MAX_INPUTS + 1]; + if (runner->keySources) { + for (i = 0; runner->keySources[i].id && i < GUI_MAX_INPUTS; ++i) { + mapNames[i] = runner->keySources[i].name; + } + if (i == 1) { + // Don't display a name if there's only one input source + i = 0; + } + *GUIMenuItemListAppend(&menu.items) = (struct GUIMenuItem) { + .title = "Remap controls", + .data = "*REMAP", + .state = 0, + .validStates = i ? mapNames : 0, + .nStates = i + }; + } for (i = 0; i < nExtra; ++i) { *GUIMenuItemListAppend(&menu.items) = extra[i]; } *GUIMenuItemListAppend(&menu.items) = (struct GUIMenuItem) { .title = "Save", - .data = "[SAVE]", + .data = "*SAVE", }; *GUIMenuItemListAppend(&menu.items) = (struct GUIMenuItem) { .title = "Cancel",@@ -76,7 +101,7 @@ reason = GUIShowMenu(&runner->params, &menu, &item);
if (reason != GUI_MENU_EXIT_ACCEPT || !item->data) { break; } - if (!strcmp(item->data, "[SAVE]")) { + if (!strcmp(item->data, "*SAVE")) { if (biosPath[0]) { GBAConfigSetValue(&runner->context.config, "bios", biosPath); }@@ -90,6 +115,10 @@ }
GBAConfigSave(&runner->context.config); break; } + if (!strcmp(item->data, "*REMAP")) { + GBAGUIRemapKeys(&runner->params, &runner->context.inputMap, &runner->keySources[item->state]); + continue; + } if (!strcmp(item->data, "bios")) { // TODO: show box if failed if (!GUISelectFile(&runner->params, biosPath, sizeof(biosPath), GBAIsBIOS)) {@@ -99,7 +128,7 @@ continue;
} if (item->validStates) { ++item->state; - if (!item->validStates[item->state]) { + if (item->state >= item->nStates) { item->state = 0; } }
@@ -116,6 +116,13 @@ CircleBufferInit(&runner->fpsBuffer, FPS_BUFFER_SIZE * sizeof(uint32_t));
if (runner->setup) { runner->setup(runner); } + + if (runner->context.config.port && runner->keySources) { + size_t i; + for (i = 0; runner->keySources[i].id; ++i) { + GBAInputMapLoad(&runner->context.inputMap, runner->keySources[i].id, GBAConfigGetInput(&runner->context.config)); + } + } } void GBAGUIDeinit(struct GBAGUIRunner* runner) {@@ -123,6 +130,12 @@ if (runner->teardown) {
runner->teardown(runner); } if (runner->context.config.port) { + if (runner->keySources) { + size_t i; + for (i = 0; runner->keySources[i].id; ++i) { + GBAInputMapSave(&runner->context.inputMap, runner->keySources[i].id, GBAConfigGetInput(&runner->context.config)); + } + } GBAConfigSave(&runner->context.config); } CircleBufferDeinit(&runner->fpsBuffer);
@@ -1,4 +1,4 @@
-/* Copyright (c) 2013-2015 Jeffrey Pfau +/* Copyright (c) 2013-2016 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@@ -7,6 +7,7 @@ #ifndef GUI_RUNNER_H
#define GUI_RUNNER_H #include "gba/context/context.h" +#include "gba/gui/remap.h" #include "util/circle-buffer.h" #include "util/gui.h"@@ -38,6 +39,8 @@ struct GBAGUIRunnerLux luminanceSource;
struct GUIMenuItem* configExtra; size_t nConfigExtra; + + struct GUIInputKeys* keySources; float fps; int64_t lastFpsCheck;
@@ -0,0 +1,62 @@
+/* Copyright (c) 2013-2016 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 "remap.h" + +#include "gba/input.h" +#include "util/gui.h" +#include "util/gui/menu.h" + +void GBAGUIRemapKeys(struct GUIParams* params, struct GBAInputMap* map, const struct GUIInputKeys* keys) { + struct GUIMenu menu = { + .title = "Remap keys", + .index = 0, + .background = 0 + }; + GUIMenuItemListInit(&menu.items, 0); + const char* keyNames[keys->nKeys + 1]; + memcpy(&keyNames[1], keys->keyNames, keys->nKeys * sizeof(keyNames[0])); + keyNames[0] = "Unmapped"; + size_t i; + for (i = 0; i < GBA_KEY_MAX; ++i) { + *GUIMenuItemListAppend(&menu.items) = (struct GUIMenuItem) { + .title = GBAKeyNames[i], + .data = (void*) (GUI_INPUT_MAX + i), + .submenu = 0, + .state = GBAInputQueryBinding(map, keys->id, i) + 1, + .validStates = keyNames, + .nStates = keys->nKeys + 1 + }; + } + *GUIMenuItemListAppend(&menu.items) = (struct GUIMenuItem) { + .title = "Save", + .data = (void*) (GUI_INPUT_MAX + GBA_KEY_MAX + 2), + }; + *GUIMenuItemListAppend(&menu.items) = (struct GUIMenuItem) { + .title = "Cancel", + .data = 0, + }; + + struct GUIMenuItem* item; + while (true) { + enum GUIMenuExitReason reason; + reason = GUIShowMenu(params, &menu, &item); + if (reason != GUI_MENU_EXIT_ACCEPT || !item->data) { + break; + } + if (item->data == (void*) (GUI_INPUT_MAX + GBA_KEY_MAX + 2)) { + for (i = 0; i < GUIMenuItemListSize(&menu.items); ++i) { + item = GUIMenuItemListGetPointer(&menu.items, i); + if (i < GBA_KEY_MAX) { + GBAInputBindKey(map, keys->id, item->state - 1, i); + } + } + break; + } + if (item->validStates) { + // TODO: Open remap menu + } + } +}
@@ -0,0 +1,23 @@
+/* Copyright (c) 2013-2016 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_REMAP_H +#define GUI_REMAP_H + +#include "util/common.h" + +struct GUIInputKeys { + const char* name; + uint32_t id; + const char* const* keyNames; + size_t nKeys; +}; + +struct GUIParams; +struct GBAInputMap; + +void GBAGUIRemapKeys(struct GUIParams*, struct GBAInputMap*, const struct GUIInputKeys*); + +#endif
@@ -30,6 +30,8 @@ SM_SF_TOP,
SM_MAX } screenMode = SM_PA_TOP; +#define _3DS_INPUT 0x3344534B + #define AUDIO_SAMPLES 0x80 #define AUDIO_SAMPLE_BUFFER (AUDIO_SAMPLES * 24)@@ -67,6 +69,10 @@ SCREEN_CLEANUP_BOTTOM = SCREEN_CLEANUP_BOTTOM_1 | SCREEN_CLEANUP_BOTTOM_2,
}; extern bool allocateRomBuffer(void); + +static void _map3DSKey(struct GBAInputMap* map, int ctrKey, enum GBAKey key) { + GBAInputBindKey(map, _3DS_INPUT, __builtin_ctz(ctrKey), key); +} static void _csndPlaySound(u32 flags, u32 sampleRate, float vol, void* left, void* right, u32 size) {@@ -185,6 +191,17 @@ if (hasSound) {
runner->context.gba->stream = &stream; } + _map3DSKey(&runner->context.inputMap, KEY_A, GBA_KEY_A); + _map3DSKey(&runner->context.inputMap, KEY_B, GBA_KEY_B); + _map3DSKey(&runner->context.inputMap, KEY_START, GBA_KEY_START); + _map3DSKey(&runner->context.inputMap, KEY_SELECT, GBA_KEY_SELECT); + _map3DSKey(&runner->context.inputMap, KEY_UP, GBA_KEY_UP); + _map3DSKey(&runner->context.inputMap, KEY_DOWN, GBA_KEY_DOWN); + _map3DSKey(&runner->context.inputMap, KEY_LEFT, GBA_KEY_LEFT); + _map3DSKey(&runner->context.inputMap, KEY_RIGHT, GBA_KEY_RIGHT); + _map3DSKey(&runner->context.inputMap, KEY_L, GBA_KEY_L); + _map3DSKey(&runner->context.inputMap, KEY_R, GBA_KEY_R); + GBAVideoSoftwareRendererCreate(&renderer); renderer.outputBuffer = linearMemAlign(256 * VIDEO_VERTICAL_PIXELS * 2, 0x80); renderer.outputBufferStride = 256;@@ -343,9 +360,10 @@ static uint16_t _pollGameInput(struct GBAGUIRunner* runner) {
UNUSED(runner); hidScanInput(); - uint32_t activeKeys = hidKeysHeld() & 0xF00003FF; - activeKeys |= activeKeys >> 24; - return activeKeys; + uint32_t activeKeys = hidKeysHeld(); + uint16_t keys = GBAInputMapKeyBits(&runner->context.inputMap, _3DS_INPUT, activeKeys, 0); + keys |= (activeKeys >> 24) & 0xF0; + return keys; } static void _incrementScreenMode(struct GBAGUIRunner* runner) {@@ -519,6 +537,26 @@ _guiPrepare, _guiFinish,
GUI_PARAMS_TRAIL }, + .keySources = (struct GUIInputKeys[]) { + { + .name = "3DS Input", + .id = _3DS_INPUT, + .keyNames = (const char*[]) { + "A", + "B", + "Select", + "Start", + "D-Pad Right", + "D-Pad Left", + "D-Pad Up", + "D-Pad Down", + "R", + "L", + }, + .nKeys = 10 + }, + { .id = 0 } + }, .configExtra = (struct GUIMenuItem[]) { { .title = "Screen mode",@@ -532,8 +570,8 @@ "Stretched/Bottom",
"Pixel-Accurate/Top", "Aspect-Ratio Fit/Top", "Stretched/Top", - 0 - } + }, + .nStates = 6 } }, .nConfigExtra = 1,
@@ -117,9 +117,35 @@ .validStates = (const char*[]) {
"With Background", "Without Background", "Stretched", - 0 - } + }, + .nStates = 3 } + }, + .keySources = (struct GUIInputKeys[]) { + { + .name = "Vita Input", + .id = PSP2_INPUT, + .keyNames = (const char*[]) { + "Select", + 0, + 0, + "Start", + "Up", + "Right", + "Down", + "Left", + "L", + "R", + 0, // L2? + 0, // R2? + "Triangle", + "Circle", + "Cross", + "Square" + }, + .nKeys = 16 + }, + { .id = 0 } }, .nConfigExtra = 1, .setup = GBAPSP2Setup,
@@ -11,4 +11,6 @@
#define PSP2_HORIZONTAL_PIXELS 960 #define PSP2_VERTICAL_PIXELS 544 +#define PSP2_INPUT 0x50535032 + #endif
@@ -48,7 +48,6 @@
extern const uint8_t _binary_backdrop_png_start[]; static vita2d_texture* backdrop = 0; -#define PSP2_INPUT 0x50535032 #define PSP2_SAMPLES 64 #define PSP2_AUDIO_BUFFER_SIZE (PSP2_SAMPLES * 19)@@ -138,11 +137,6 @@ }
void GBAPSP2Setup(struct GBAGUIRunner* runner) { scePowerSetArmClockFrequency(80); - struct GBAOptions opts = { - .useBios = true, - .idleOptimization = IDLE_LOOP_DETECT - }; - GBAConfigLoadDefaults(&runner->context.config, &opts); _mapVitaKey(&runner->context.inputMap, PSP2_CTRL_CROSS, GBA_KEY_A); _mapVitaKey(&runner->context.inputMap, PSP2_CTRL_CIRCLE, GBA_KEY_B); _mapVitaKey(&runner->context.inputMap, PSP2_CTRL_START, GBA_KEY_START);@@ -158,7 +152,6 @@ struct GBAAxis desc = { GBA_KEY_DOWN, GBA_KEY_UP, 192, 64 };
GBAInputBindAxis(&runner->context.inputMap, PSP2_INPUT, 0, &desc); desc = (struct GBAAxis) { GBA_KEY_RIGHT, GBA_KEY_LEFT, 192, 64 }; GBAInputBindAxis(&runner->context.inputMap, PSP2_INPUT, 1, &desc); - GBAInputMapLoad(&runner->context.inputMap, PSP2_INPUT, GBAConfigGetInput(&runner->context.config)); tex = vita2d_create_empty_texture_format(256, 256, SCE_GXM_TEXTURE_FORMAT_X8U8U8U8_1BGR); screenshot = vita2d_create_empty_texture_format(256, 256, SCE_GXM_TEXTURE_FORMAT_X8U8U8U8_1BGR);@@ -221,7 +214,6 @@ scePowerSetArmClockFrequency(80);
} void GBAPSP2Teardown(struct GBAGUIRunner* runner) { - GBAInputMapSave(&runner->context.inputMap, PSP2_INPUT, GBAConfigGetInput(&runner->context.config)); vita2d_free_texture(tex); vita2d_free_texture(screenshot); }
@@ -63,7 +63,6 @@ static void _guiPrepare(void);
static void _guiFinish(void); static void _setup(struct GBAGUIRunner* runner); -static void _teardown(struct GBAGUIRunner* runner); static void _gameLoaded(struct GBAGUIRunner* runner); static void _gameUnloaded(struct GBAGUIRunner* runner); static void _unpaused(struct GBAGUIRunner* runner);@@ -215,6 +214,113 @@ _guiPrepare, _guiFinish,
GUI_PARAMS_TRAIL }, + .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", + "Minus", + 0, + 0, + "Home", + "Left", + "Right", + "Down", + "Up", + "Plus", + 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", + "Plus", + "Home", + "Minus", + "L", + "Down", + "Right", + }, + .nKeys = 32 + }, + { .id = 0 } + }, .configExtra = (struct GUIMenuItem[]) { { .title = "Screen mode",@@ -224,8 +330,8 @@ .state = 0,
.validStates = (const char*[]) { "Pixel-Accurate", "Stretched", - 0 - } + }, + .nStates = 2 }, { .title = "Filtering",@@ -235,13 +341,13 @@ .state = 0,
.validStates = (const char*[]) { "Pixelated", "Resampled", - 0 - } + }, + .nStates = 2 } }, .nConfigExtra = 2, .setup = _setup, - .teardown = _teardown, + .teardown = 0, .gameLoaded = _gameLoaded, .gameUnloaded = _gameUnloaded, .prepareForFrame = 0,@@ -407,11 +513,6 @@ void _setup(struct GBAGUIRunner* runner) {
runner->context.gba->rumble = &rumble; runner->context.gba->rotationSource = &rotation; - struct GBAOptions opts = { - .useBios = true, - .idleOptimization = IDLE_LOOP_DETECT - }; - GBAConfigLoadDefaults(&runner->context.config, &opts); _mapKey(&runner->context.inputMap, GCN1_INPUT, PAD_BUTTON_A, GBA_KEY_A); _mapKey(&runner->context.inputMap, GCN1_INPUT, PAD_BUTTON_B, GBA_KEY_B); _mapKey(&runner->context.inputMap, GCN1_INPUT, PAD_BUTTON_START, GBA_KEY_START);@@ -452,10 +553,6 @@ GBAInputBindAxis(&runner->context.inputMap, CLASSIC_INPUT, 0, &desc);
desc = (struct GBAAxis) { GBA_KEY_UP, GBA_KEY_DOWN, 0x40, -0x40 }; GBAInputBindAxis(&runner->context.inputMap, GCN1_INPUT, 1, &desc); GBAInputBindAxis(&runner->context.inputMap, CLASSIC_INPUT, 1, &desc); - GBAInputMapLoad(&runner->context.inputMap, GCN1_INPUT, GBAConfigGetInput(&runner->context.config)); - GBAInputMapLoad(&runner->context.inputMap, GCN2_INPUT, GBAConfigGetInput(&runner->context.config)); - GBAInputMapLoad(&runner->context.inputMap, WIIMOTE_INPUT, GBAConfigGetInput(&runner->context.config)); - GBAInputMapLoad(&runner->context.inputMap, CLASSIC_INPUT, GBAConfigGetInput(&runner->context.config)); GBAVideoSoftwareRendererCreate(&renderer); renderer.outputBuffer = memalign(32, 256 * 256 * BYTES_PER_PIXEL);@@ -469,13 +566,6 @@ double ratio = GBAAudioCalculateRatio(1, 60 / 1.001, 1);
blip_set_rates(runner->context.gba->audio.left, GBA_ARM7TDMI_FREQUENCY, 48000 * ratio); blip_set_rates(runner->context.gba->audio.right, GBA_ARM7TDMI_FREQUENCY, 48000 * ratio); #endif -} - -void _teardown(struct GBAGUIRunner* runner) { - GBAInputMapSave(&runner->context.inputMap, GCN1_INPUT, GBAConfigGetInput(&runner->context.config)); - GBAInputMapSave(&runner->context.inputMap, GCN2_INPUT, GBAConfigGetInput(&runner->context.config)); - GBAInputMapSave(&runner->context.inputMap, WIIMOTE_INPUT, GBAConfigGetInput(&runner->context.config)); - GBAInputMapSave(&runner->context.inputMap, CLASSIC_INPUT, GBAConfigGetInput(&runner->context.config)); } void _gameUnloaded(struct GBAGUIRunner* runner) {