Added button remapping. Closes #25.
This commit is contained in:
parent
726a4f3355
commit
014c341e67
45
README.md
45
README.md
|
@ -82,6 +82,51 @@ Audio settings.
|
|||
`u8 audioOut` - Audio output. 0 = auto, 1 = speakers, 2 = headphones.
|
||||
* Default: `0`
|
||||
|
||||
### Input
|
||||
Input settings. Each entry allows one or multiple buttons. Buttons are separated by a `,` without spaces.
|
||||
Allowed buttons are `A B SELECT START RIGHT LEFT UP DOWN R L X Y TOUCH CP_RIGHT CP_LEFT CP_UP CP_DOWN`.
|
||||
TOUCH reacts to all touchscreen presses. The CP in front is short for Circle-Pad.
|
||||
|
||||
Note that button mappings can cause input lag of up to 1 frame depending on when the game reads inputs. For this reason the default mapping of the Circle-Pad to D-Pad is no longer provided.
|
||||
|
||||
`A` - Button map for the A button.
|
||||
* Default: `none`
|
||||
|
||||
`B` - Button map for the B button.
|
||||
* Default: `none`
|
||||
|
||||
`SELECT` - Button map for the SELECT button.
|
||||
* Default: `none`
|
||||
|
||||
`START` - Button map for the START button.
|
||||
* Default: `none`
|
||||
|
||||
`RIGHT` - Button map for the RIGHT button.
|
||||
* Default: `none`
|
||||
|
||||
`LEFT` - Button map for the LEFT button.
|
||||
* Default: `none`
|
||||
|
||||
`UP` - Button map for the UP button.
|
||||
* Default: `none`
|
||||
|
||||
`DOWN` - Button map for the DOWN button.
|
||||
* Default: `none`
|
||||
|
||||
`R` - Button map for the R button.
|
||||
* Default: `none`
|
||||
|
||||
`L` - Button map for the L button.
|
||||
* Default: `none`
|
||||
|
||||
Example:
|
||||
```
|
||||
RIGHT=RIGHT,CP_RIGHT
|
||||
LEFT=LEFT,CP_LEFT
|
||||
UP=UP,CP_UP
|
||||
DOWN=DOWN,CP_DOWN
|
||||
```
|
||||
|
||||
### Game
|
||||
Game-specific settings. Only intended to be used in the per-game settings (romName.ini in `/3ds/open_agb_firm/saves`).
|
||||
|
||||
|
|
|
@ -25,7 +25,8 @@
|
|||
#include "util.h"
|
||||
#include "drivers/sha.h"
|
||||
#include "arm11/drivers/hid.h"
|
||||
#include "drivers/lgy.h"
|
||||
#include "arm11/drivers/lgy11.h"
|
||||
#include "drivers/lgy_common.h"
|
||||
#include "arm11/drivers/lgyfb.h"
|
||||
#include "arm11/console.h"
|
||||
#include "arm11/fmt.h"
|
||||
|
@ -63,23 +64,27 @@
|
|||
"saveOverride=false\n" \
|
||||
"defaultSave=14"
|
||||
|
||||
|
||||
typedef struct
|
||||
{
|
||||
// [general]
|
||||
u8 backlight; // Both LCDs.
|
||||
u8 backlight; // Both LCDs.
|
||||
u8 backlightSteps;
|
||||
bool directBoot;
|
||||
bool useGbaDb;
|
||||
|
||||
// [video]
|
||||
u8 scaler; // 0 = 1:1, 1 = bilinear (GPU) x1.5, 2 = matrix (hardware) x1.5.
|
||||
u8 scaler; // 0 = 1:1, 1 = bilinear (GPU) x1.5, 2 = matrix (hardware) x1.5.
|
||||
float gbaGamma;
|
||||
float lcdGamma;
|
||||
float contrast;
|
||||
float brightness;
|
||||
|
||||
// [audio]
|
||||
u8 audioOut; // 0 = auto, 1 = speakers, 2 = headphones.
|
||||
u8 audioOut; // 0 = auto, 1 = speakers, 2 = headphones.
|
||||
|
||||
// [input]
|
||||
u32 buttonMaps[10]; // A, B, Select, Start, Right, Left, Up, Down, R, L.
|
||||
|
||||
// [game]
|
||||
u8 saveSlot;
|
||||
|
@ -118,6 +123,20 @@ static OafConfig g_oafConfig =
|
|||
// [audio]
|
||||
0, // Automatic audio output.
|
||||
|
||||
// [input]
|
||||
{ // buttonMaps
|
||||
0, // A
|
||||
0, // B
|
||||
0, // Select
|
||||
0, // Start
|
||||
0, // Right
|
||||
0, // Left
|
||||
0, // Up
|
||||
0, // Down
|
||||
0, // R
|
||||
0 // L
|
||||
},
|
||||
|
||||
// [game]
|
||||
0, // saveSlot
|
||||
0xFF, // saveType
|
||||
|
@ -128,19 +147,21 @@ static OafConfig g_oafConfig =
|
|||
};
|
||||
static KHandle g_frameReadyEvent = 0;
|
||||
|
||||
|
||||
|
||||
static u32 fixRomPadding(u32 romFileSize)
|
||||
{
|
||||
// Pad unused ROM area with 0xFFs (trimmed ROMs).
|
||||
// Smallest retail ROM chip is 8 Mbit (1 MiB).
|
||||
u32 romSize = nextPow2(romFileSize);
|
||||
if(romSize < 0x100000u) romSize = 0x100000u;
|
||||
memset((void*)(ROM_LOC + romFileSize), 0xFFFFFFFFu, romSize - romFileSize);
|
||||
memset((void*)(LGY_ROM_LOC + romFileSize), 0xFFFFFFFFu, romSize - romFileSize);
|
||||
if(romSize > 0x100000u) // >1 MiB.
|
||||
{
|
||||
// Fake "open bus" padding.
|
||||
u32 padding = (ROM_LOC + romSize) / 2;
|
||||
u32 padding = (LGY_ROM_LOC + romSize) / 2;
|
||||
padding = __pkhbt(padding, padding + 1, 16); // Copy lower half + 1 to upper half.
|
||||
for(uintptr_t i = ROM_LOC + romSize; i < ROM_LOC + MAX_ROM_SIZE; i += 4)
|
||||
for(uintptr_t i = LGY_ROM_LOC + romSize; i < LGY_ROM_LOC + LGY_MAX_ROM_SIZE; i += 4)
|
||||
{
|
||||
*(u32*)i = padding;
|
||||
padding = __uadd16(padding, 0x00020002u); // Unsigned parallel halfword-wise addition.
|
||||
|
@ -151,10 +172,10 @@ static u32 fixRomPadding(u32 romFileSize)
|
|||
|
||||
// ROM mirroring (Classic NES Series/possibly others with 8 Mbit ROM).
|
||||
// Mirror ROM across the entire 32 MiB area.
|
||||
for(uintptr_t i = ROM_LOC + romSize; i < ROM_LOC + MAX_ROM_SIZE; i += romSize)
|
||||
for(uintptr_t i = LGY_ROM_LOC + romSize; i < LGY_ROM_LOC + LGY_MAX_ROM_SIZE; i += romSize)
|
||||
{
|
||||
//memcpy((void*)i, (void*)(i - romSize), romSize); // 0x23A15DD
|
||||
memcpy((void*)i, (void*)ROM_LOC, romSize); // 0x237109B
|
||||
memcpy((void*)i, (void*)LGY_ROM_LOC, romSize); // 0x237109B
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -168,14 +189,14 @@ static Result loadGbaRom(const char *const path, u32 *const romSizeOut)
|
|||
if((res = fOpen(&f, path, FA_OPEN_EXISTING | FA_READ)) == RES_OK)
|
||||
{
|
||||
u32 fileSize = fSize(f);
|
||||
if(fileSize > MAX_ROM_SIZE)
|
||||
if(fileSize > LGY_MAX_ROM_SIZE)
|
||||
{
|
||||
fileSize = MAX_ROM_SIZE;
|
||||
fileSize = LGY_MAX_ROM_SIZE;
|
||||
ee_puts("Warning: ROM file is too big. Expect crashes.");
|
||||
}
|
||||
|
||||
u32 read;
|
||||
res = fRead(f, (u8*)ROM_LOC, fileSize, &read);
|
||||
res = fRead(f, (u8*)LGY_ROM_LOC, fileSize, &read);
|
||||
fClose(f);
|
||||
|
||||
if(read == fileSize) *romSizeOut = fixRomPadding(fileSize); //, path);
|
||||
|
@ -202,7 +223,7 @@ static u16 checkSaveOverride(u32 gameCode) // Save type overrides for modern hom
|
|||
|
||||
static u16 detectSaveType(u32 romSize)
|
||||
{
|
||||
const u32 *romPtr = (u32*)ROM_LOC;
|
||||
const u32 *romPtr = (u32*)LGY_ROM_LOC;
|
||||
u16 saveType;
|
||||
if((saveType = checkSaveOverride(romPtr[0xAC / 4])) != 0xFF)
|
||||
{
|
||||
|
@ -219,7 +240,7 @@ static u16 detectSaveType(u32 romSize)
|
|||
else
|
||||
saveType = defaultSave;
|
||||
|
||||
for(; romPtr < (u32*)(ROM_LOC + romSize); romPtr++)
|
||||
for(; romPtr < (u32*)(LGY_ROM_LOC + romSize); romPtr++)
|
||||
{
|
||||
u32 tmp = *romPtr;
|
||||
|
||||
|
@ -341,7 +362,7 @@ static u16 getSaveType(u32 romSize, const char *const savePath)
|
|||
const bool saveExists = fStat(savePath, &fi) == RES_OK;
|
||||
|
||||
u64 sha1[3];
|
||||
sha((u32*)ROM_LOC, romSize, (u32*)sha1, SHA_IN_BIG | SHA_1_MODE, SHA_OUT_BIG);
|
||||
sha((u32*)LGY_ROM_LOC, romSize, (u32*)sha1, SHA_IN_BIG | SHA_1_MODE, SHA_OUT_BIG);
|
||||
|
||||
Result res;
|
||||
GameDbEntry dbEntry;
|
||||
|
@ -528,6 +549,42 @@ static void gbaGfxHandler(void *args)
|
|||
taskExit();
|
||||
}
|
||||
|
||||
static u32 parseButtons(const char *str)
|
||||
{
|
||||
if(str == NULL || *str == '\0') return 0;
|
||||
|
||||
char buf[32]; // Should be enough for for all useful mappings.
|
||||
buf[31] = '\0';
|
||||
strncpy(buf, str, 31);
|
||||
|
||||
char *bufPtr = buf;
|
||||
static const char *const buttonStrLut[32] =
|
||||
{
|
||||
"A", "B", "SELECT", "START", "RIGHT", "LEFT", "UP", "DOWN",
|
||||
"R", "L", "X", "Y", "", "", "ZL", "ZR",
|
||||
"", "", "", "", "TOUCH", "", "", "",
|
||||
"CS_RIGHT", "CS_LEFT", "CS_UP", "CS_DOWN", "CP_RIGHT", "CP_LEFT", "CP_UP", "CP_DOWN"
|
||||
};
|
||||
u32 map = 0;
|
||||
while(1)
|
||||
{
|
||||
char *const nextDelimiter = strchr(bufPtr, ',');
|
||||
if(nextDelimiter != NULL) *nextDelimiter = '\0';
|
||||
|
||||
unsigned i = 0;
|
||||
while(i < 32 && strcmp(buttonStrLut[i], bufPtr) != 0) ++i;
|
||||
if(i == 32) break;
|
||||
map |= 1u<<i;
|
||||
|
||||
if(nextDelimiter == NULL) break;
|
||||
|
||||
bufPtr = nextDelimiter + 1; // Skip delimiter.
|
||||
}
|
||||
|
||||
// Empty strings will match the entry for bit 12.
|
||||
return map & ~(1u<<12);
|
||||
}
|
||||
|
||||
static int cfgIniCallback(void* user, const char* section, const char* name, const char* value)
|
||||
{
|
||||
OafConfig *const config = (OafConfig*)user;
|
||||
|
@ -561,6 +618,17 @@ static int cfgIniCallback(void* user, const char* section, const char* name, con
|
|||
if(strcmp(name, "audioOut") == 0)
|
||||
config->audioOut = (u8)strtoul(value, NULL, 10);
|
||||
}
|
||||
else if(strcmp(section, "input") == 0)
|
||||
{
|
||||
const u32 button = parseButtons(name) & 0x3FFu; // Only allow GBA buttons.
|
||||
if(button != 0)
|
||||
{
|
||||
// If the config option happens to abuse parseButtons() we will only use the highest bit.
|
||||
const u32 shift = 31u - __builtin_clzl(button);
|
||||
const u32 map = parseButtons(value);
|
||||
config->buttonMaps[shift] = map;
|
||||
}
|
||||
}
|
||||
else if(strcmp(section, "game") == 0)
|
||||
{
|
||||
if(strcmp(name, "saveSlot") == 0)
|
||||
|
@ -815,10 +883,17 @@ Result oafInitAndRun(void)
|
|||
createTask(0x800, 3, gbaGfxHandler, (void*)frameReadyEvent);
|
||||
g_frameReadyEvent = frameReadyEvent;
|
||||
|
||||
// Adjust gamma table and sync LgyFb start with LCD VBlank.
|
||||
// Adjust gamma table and setup button overrides.
|
||||
adjustGammaTableForGba();
|
||||
const u32 *const maps = g_oafConfig.buttonMaps;
|
||||
u16 overrides = 0;
|
||||
for(unsigned i = 0; i < 10; i++)
|
||||
if(maps[i] != 0) overrides |= 1u<<i;
|
||||
LGY11_selectInput(overrides);
|
||||
|
||||
// Sync LgyFb start with LCD VBlank.
|
||||
GFX_waitForVBlank0();
|
||||
LGY_switchMode();
|
||||
LGY11_switchMode();
|
||||
}
|
||||
} while(0);
|
||||
}
|
||||
|
@ -831,7 +906,16 @@ Result oafInitAndRun(void)
|
|||
|
||||
void oafUpdate(void)
|
||||
{
|
||||
LGY_handleOverrides();
|
||||
const u32 *const maps = g_oafConfig.buttonMaps;
|
||||
const u32 kHeld = hidKeysHeld();
|
||||
u16 pressed = 0;
|
||||
for(unsigned i = 0; i < 10; i++)
|
||||
{
|
||||
if((kHeld & maps[i]) != 0)
|
||||
pressed |= 1u<<i;
|
||||
}
|
||||
LGY11_setInputState(pressed);
|
||||
|
||||
updateBacklight();
|
||||
waitForEvent(g_frameReadyEvent);
|
||||
}
|
||||
|
@ -844,5 +928,5 @@ void oafFinish(void)
|
|||
deleteEvent(g_frameReadyEvent); // gbaGfxHandler() will automatically terminate.
|
||||
g_frameReadyEvent = 0;
|
||||
}
|
||||
LGY_deinit();
|
||||
LGY11_deinit();
|
||||
}
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
#include "oaf_error_codes.h"
|
||||
#include "util.h"
|
||||
#include "arm11/drivers/hid.h"
|
||||
#include "drivers/lgy.h"
|
||||
#include "drivers/lgy_common.h"
|
||||
#include "arm11/fmt.h"
|
||||
#include "fs.h"
|
||||
#include "arm11/patch.h"
|
||||
|
@ -93,7 +93,7 @@ static Result patchIPS(const FHandle patchHandle) {
|
|||
if(res != RES_OK) break;
|
||||
|
||||
u16 tempLen = (buffer[0]<<8) + (buffer[1]);
|
||||
memset((void*)(ROM_LOC + offset), buffer[2], tempLen*sizeof(char));
|
||||
memset((void*)(LGY_ROM_LOC + offset), buffer[2], tempLen*sizeof(char));
|
||||
}
|
||||
//regular hunks
|
||||
else {
|
||||
|
@ -102,7 +102,7 @@ static Result patchIPS(const FHandle patchHandle) {
|
|||
res = fRead(patchHandle, buffer, bufferSize, NULL);
|
||||
if(res != RES_OK) break;
|
||||
for(u16 j=0; j<bufferSize; ++j) {
|
||||
*(char*)(ROM_LOC+offset+(bufferSize*i)+j) = buffer[j];
|
||||
*(char*)(LGY_ROM_LOC+offset+(bufferSize*i)+j) = buffer[j];
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -111,7 +111,7 @@ static Result patchIPS(const FHandle patchHandle) {
|
|||
res = fRead(patchHandle, buffer, remaining, NULL);
|
||||
if(res != RES_OK) break;
|
||||
for(u16 j=0; j<remaining; ++j) {
|
||||
*(char*)(ROM_LOC+offset+(fullCount*bufferSize)+j) = buffer[j];
|
||||
*(char*)(LGY_ROM_LOC+offset+(fullCount*bufferSize)+j) = buffer[j];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -193,21 +193,21 @@ static Result patchUPS(const FHandle patchHandle, u32 *romSize) {
|
|||
//scale up rom
|
||||
*romSize = nextPow2(patchedRomSize);
|
||||
//check if upscaled rom is too big
|
||||
if(*romSize > MAX_ROM_SIZE) {
|
||||
if(*romSize > LGY_MAX_ROM_SIZE) {
|
||||
ee_puts("Patched ROM exceeds 32MB! Skipping patching...");
|
||||
free(cache.buffer);
|
||||
return RES_INVALID_PATCH;
|
||||
}
|
||||
|
||||
memset((char*)(ROM_LOC + baseRomSize), 0xFFu, *romSize - baseRomSize); //fill out extra rom space
|
||||
memset((char*)(ROM_LOC + baseRomSize), 0x00u, patchedRomSize - baseRomSize); //fill new patch area with 0's
|
||||
memset((char*)(LGY_ROM_LOC + baseRomSize), 0xFFu, *romSize - baseRomSize); //fill out extra rom space
|
||||
memset((char*)(LGY_ROM_LOC + baseRomSize), 0x00u, patchedRomSize - baseRomSize); //fill new patch area with 0's
|
||||
}
|
||||
|
||||
uintmax_t patchFileSize = fSize(patchHandle);
|
||||
|
||||
uintmax_t offset = 0;
|
||||
u8 readByte = 0;
|
||||
u8 *romBytes = ((u8*)ROM_LOC);
|
||||
u8 *romBytes = ((u8*)LGY_ROM_LOC);
|
||||
|
||||
while(fTell(patchHandle) < (patchFileSize-12) && res==RES_OK) {
|
||||
offset += read_vuint(patchHandle, &res, &cache);
|
||||
|
@ -305,7 +305,7 @@ cleanup:
|
|||
#ifndef NDEBUG
|
||||
else {
|
||||
u64 sha1[3];
|
||||
sha((u32*)ROM_LOC, *romSize, (u32*)sha1, SHA_IN_BIG | SHA_1_MODE, SHA_OUT_BIG);
|
||||
sha((u32*)LGY_ROM_LOC, *romSize, (u32*)sha1, SHA_IN_BIG | SHA_1_MODE, SHA_OUT_BIG);
|
||||
debug_printf("New hash: '%016" PRIX64 "'\n", __builtin_bswap64(sha1[0]));
|
||||
}
|
||||
#endif
|
||||
|
|
Loading…
Reference in New Issue