Skip to content

Commit 15cf1bf

Browse files
committed
Added per-game config support. Place "romName.ini" in "/3ds/open_agb_firm/saves" to change per-game settings. Currently only saveSlot is configurable.
Updated README.md.
1 parent 247cb19 commit 15cf1bf

File tree

2 files changed

+61
-29
lines changed

2 files changed

+61
-29
lines changed

README.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,12 @@ Video-related settings.
6262
`float brightness` - Screen lift
6363
* Default: `0.0`
6464

65+
### Game
66+
Game-specific settings. Only intended to be used in the per-game settings (romName.ini in `/3ds/open_agb_firm/saves`).
67+
68+
`u8 saveSlot` - Savegame slot (0-9)
69+
* Default: `0`
70+
6571
### Advanced
6672
Options for advanced users. No pun intended.
6773

source/arm11/open_agb_firm.c

Lines changed: 55 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,9 @@ typedef struct
7070
float contrast;
7171
float brightness;
7272

73+
// [game]
74+
u8 saveSlot;
75+
7376
// [advanced]
7477
bool saveOverride;
7578
u16 defaultSave;
@@ -98,6 +101,9 @@ static OafConfig g_oafConfig =
98101
1.f, // contrast
99102
0.f, // brightness
100103

104+
// [game]
105+
0, // saveSlot
106+
101107
// [advanced]
102108
false, // saveOverride
103109
14 // defaultSave
@@ -492,7 +498,7 @@ static void gbaGfxHandler(void *args)
492498
taskExit();
493499
}
494500

495-
static int confIniHandler(void* user, const char* section, const char* name, const char* value)
501+
static int cfgIniCallback(void* user, const char* section, const char* name, const char* value)
496502
{
497503
OafConfig *const config = (OafConfig*)user;
498504

@@ -516,6 +522,11 @@ static int confIniHandler(void* user, const char* section, const char* name, con
516522
else if(strcmp(name, "brightness") == 0)
517523
config->brightness = str2float(value);
518524
}
525+
else if(strcmp(section, "game") == 0)
526+
{
527+
if(strcmp(name, "saveSlot") == 0)
528+
config->saveSlot = (u8)strtoul(value, NULL, 10);
529+
}
519530
else if(strcmp(section, "advanced") == 0)
520531
{
521532
if(strcmp(name, "saveOverride") == 0)
@@ -528,14 +539,14 @@ static int confIniHandler(void* user, const char* section, const char* name, con
528539
return 1; // 1 is no error? Really?
529540
}
530541

531-
static Result parseConfig(const char *const path, void *config)
542+
static Result parseOafConfig(const char *const path, const bool writeDefaultCfg)
532543
{
533544
char *iniBuf = (char*)calloc(INI_BUF_SIZE, 1);
534545
if(iniBuf == NULL) return RES_OUT_OF_MEM;
535546

536547
Result res = fsQuickRead(path, iniBuf, INI_BUF_SIZE - 1);
537-
if(res == RES_OK) ini_parse_string(iniBuf, confIniHandler, config);
538-
else
548+
if(res == RES_OK) ini_parse_string(iniBuf, cfgIniCallback, &g_oafConfig);
549+
else if(writeDefaultCfg)
539550
{
540551
const char *const defaultConfig = DEFAULT_CONFIG;
541552
res = fsQuickWrite(path, defaultConfig, strlen(defaultConfig));
@@ -591,27 +602,33 @@ static Result showFileBrowser(char romAndSavePath[512])
591602
return res;
592603
}
593604

594-
static void rom2SavePath(char romPath[512], const u8 saveSlot)
605+
static void rom2GameCfgPath(char romPath[512])
606+
{
607+
// Extract the file name and change the extension.
608+
// For cfg2SavePath() we need to reserve 2 extra bytes/chars.
609+
char tmpSaveFileName[256];
610+
safeStrcpy(tmpSaveFileName, strrchr(romPath, '/') + 1, 256 - 2);
611+
strcpy(tmpSaveFileName + strlen(tmpSaveFileName) - 4, ".ini");
612+
613+
// Construct the new path.
614+
strcpy(romPath, OAF_SAVE_DIR "/");
615+
strcat(romPath, tmpSaveFileName);
616+
}
617+
618+
static void gameCfg2SavePath(char cfgPath[512], const u8 saveSlot)
595619
{
596620
if(saveSlot > 9)
597621
{
598-
*romPath = '\0'; // Prevent using the ROM as save file.
622+
*cfgPath = '\0'; // Prevent using the ROM as save file.
599623
return;
600624
}
601625

602-
char tmpSaveFileName[256];
603626
static char numberedExt[7] = {'.', 'X', '.', 's', 'a', 'v', '\0'};
604627

628+
// Change the extension.
629+
// This relies on rom2GameCfgPath() to reserve 2 extra bytes/chars.
605630
numberedExt[1] = '0' + saveSlot;
606-
607-
// Extract the file name and change the extension.
608-
// The numbered extension is 2 chars longer than unnumbered.
609-
safeStrcpy(tmpSaveFileName, strrchr(romPath, '/') + 1, 256 - 2);
610-
strcpy(tmpSaveFileName + strlen(tmpSaveFileName) - 4, (saveSlot == 0 ? ".sav" : numberedExt));
611-
612-
// Construct the new path.
613-
strcpy(romPath, OAF_SAVE_DIR "/");
614-
strcat(romPath, tmpSaveFileName);
631+
strcpy(cfgPath + strlen(cfgPath) - 4, (saveSlot == 0 ? ".sav" : numberedExt));
615632
}
616633

617634
Result oafParseConfigEarly(void)
@@ -627,7 +644,7 @@ Result oafParseConfigEarly(void)
627644
if((res = fMkdir(OAF_SAVE_DIR)) != RES_OK && res != RES_FR_EXIST) break;
628645

629646
// Parse the config.
630-
res = parseConfig("config.ini", &g_oafConfig);
647+
res = parseOafConfig("config.ini", true);
631648
} while(0);
632649

633650
return res;
@@ -641,38 +658,47 @@ u8 oafGetBacklightConfig(void)
641658
Result oafInitAndRun(void)
642659
{
643660
Result res;
644-
char *const romAndSavePath = (char*)calloc(512, 1);
645-
if(romAndSavePath != NULL)
661+
char *const filePath = (char*)calloc(512, 1);
662+
if(filePath != NULL)
646663
{
647664
do
648665
{
649-
if((res = fsLoadPathFromFile("autoboot.txt", romAndSavePath)) == RES_FR_NO_FILE)
666+
// Try to load the ROM path from autoboot.txt.
667+
// If this file doesn't exist show the file browser.
668+
if((res = fsLoadPathFromFile("autoboot.txt", filePath)) == RES_FR_NO_FILE)
650669
{
651-
if((res = showFileBrowser(romAndSavePath)) != RES_OK || *romAndSavePath == '\0') break;
670+
if((res = showFileBrowser(filePath)) != RES_OK || *filePath == '\0') break;
652671
ee_puts("Loading...");
653672
}
654673
else if(res != RES_OK) break;
655674

675+
// Load the ROM file.
656676
u32 romSize;
657-
if((res = loadGbaRom(romAndSavePath, &romSize)) != RES_OK) break;
677+
if((res = loadGbaRom(filePath, &romSize)) != RES_OK) break;
678+
679+
// Load the per-game config.
680+
rom2GameCfgPath(filePath);
681+
if((res = parseOafConfig(filePath, false)) != RES_OK && res != RES_FR_NO_FILE) break;
658682

659-
// Adjust path for the save file and get save type.
660-
rom2SavePath(romAndSavePath, 0); // TODO: Save slot config.
683+
// Adjust the path for the save file and get save type.
684+
gameCfg2SavePath(filePath, g_oafConfig.saveSlot);
661685
u16 saveType;
662686
if(g_oafConfig.useGbaDb || g_oafConfig.saveOverride)
663-
saveType = getSaveType(romSize, romAndSavePath);
687+
saveType = getSaveType(romSize, filePath);
664688
else
665689
saveType = detectSaveType(romSize);
666690

667-
// Prepare ARM9 for GBA mode + settings and save loading.
668-
if((res = LGY_prepareGbaMode(g_oafConfig.directBoot, saveType, romAndSavePath)) == RES_OK)
691+
// Prepare ARM9 for GBA mode + save loading.
692+
if((res = LGY_prepareGbaMode(g_oafConfig.directBoot, saveType, filePath)) == RES_OK)
669693
{
670694
#ifdef NDEBUG
695+
// Force black and turn the backlight off on the bottom screen.
696+
// Don't turn the backlight off on 2DS (1 panel).
671697
GFX_setForceBlack(false, true);
672-
// Don't turn the backlight off on 2DS.
673698
if(MCU_getSystemModel() != 3) GFX_powerOffBacklights(GFX_BLIGHT_BOT);
674699
#endif
675700

701+
// Initialize the legacy frame buffer and frame handler.
676702
const KHandle frameReadyEvent = createEvent(false);
677703
LGYFB_init(frameReadyEvent); // Setup Legacy Framebuffer.
678704
createTask(0x800, 3, gbaGfxHandler, (void*)frameReadyEvent);
@@ -687,7 +713,7 @@ Result oafInitAndRun(void)
687713
}
688714
else res = RES_OUT_OF_MEM;
689715

690-
free(romAndSavePath);
716+
free(filePath);
691717

692718
return res;
693719
}

0 commit comments

Comments
 (0)