Skip to content

Commit

Permalink
Added button remapping. Closes #25.
Browse files Browse the repository at this point in the history
  • Loading branch information
profi200 committed Aug 6, 2023
1 parent 726a4f3 commit 014c341
Show file tree
Hide file tree
Showing 3 changed files with 157 additions and 28 deletions.
45 changes: 45 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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`).

Expand Down
122 changes: 103 additions & 19 deletions source/arm11/open_agb_firm.c
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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
Expand All @@ -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.
Expand All @@ -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
}
}

Expand All @@ -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);
Expand All @@ -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)
{
Expand All @@ -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;

Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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);
}
Expand All @@ -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);
}
Expand All @@ -844,5 +928,5 @@ void oafFinish(void)
deleteEvent(g_frameReadyEvent); // gbaGfxHandler() will automatically terminate.
g_frameReadyEvent = 0;
}
LGY_deinit();
LGY11_deinit();
}
18 changes: 9 additions & 9 deletions source/arm11/patch.c
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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 {
Expand All @@ -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];
}
}

Expand All @@ -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];
}
}
}
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -305,7 +305,7 @@ Result patchRom(const char *const gamePath, u32 *romSize) {
#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
Expand Down

0 comments on commit 014c341

Please sign in to comment.