Rewrote config menu to allow for multiple choice settings, made the N3DS CPU patch configurable in the NTR way, changed the config.bin format to be more future-proof (settings are on the leftmost part), added more macros to read settings

This commit is contained in:
Aurora 2016-04-12 23:18:07 +02:00
parent 4fabe1f704
commit e9449f86bf
6 changed files with 162 additions and 125 deletions

View File

@ -5,6 +5,9 @@
#ifndef PATH_MAX #ifndef PATH_MAX
#define PATH_MAX 255 #define PATH_MAX 255
#define CONFIG(a) ((config >> (a + 16)) & 1)
#define MULTICONFIG(a) ((config >> (a * 2 + 6)) & 3)
#define BOOTCONFIG(a, b) ((config >> a) & b)
#endif #endif
static u32 config = 0; static u32 config = 0;
@ -206,11 +209,11 @@ void patchCode(u64 progId, u8 *code, u32 size)
case 0x0004001000027000LL: // KOR MSET case 0x0004001000027000LL: // KOR MSET
case 0x0004001000028000LL: // TWN MSET case 0x0004001000028000LL: // TWN MSET
{ {
if(R_SUCCEEDED(loadConfig()) && ((config >> 6) & 1)) if(R_SUCCEEDED(loadConfig()) && CONFIG(6))
{ {
static const u16 verPattern[] = u"Ver."; static const u16 verPattern[] = u"Ver.";
const u32 currentNand = ((config >> 16) & 3); const u32 currentNand = BOOTCONFIG(0, 3);
const u32 matchingFirm = ((config >> 18) & 1) == (currentNand != 0); const u32 matchingFirm = BOOTCONFIG(2, 1) == (currentNand != 0);
//Patch Ver. string //Patch Ver. string
patchMemory(code, size, patchMemory(code, size,
@ -242,7 +245,7 @@ void patchCode(u64 progId, u8 *code, u32 size)
sizeof(stopCartUpdatesPatch), 2 sizeof(stopCartUpdatesPatch), 2
); );
if(R_SUCCEEDED(loadConfig()) && ((config >> 4) & 1)) if(R_SUCCEEDED(loadConfig()) && MULTICONFIG(1))
{ {
static const u8 cfgN3dsCpuPattern[] = { static const u8 cfgN3dsCpuPattern[] = {
0x40, 0xA0, 0xE1, 0x07, 0x00 0x40, 0xA0, 0xE1, 0x07, 0x00
@ -254,7 +257,7 @@ void patchCode(u64 progId, u8 *code, u32 size)
if(cfgN3dsCpuLoc != NULL) if(cfgN3dsCpuLoc != NULL)
{ {
*(u32 *)(cfgN3dsCpuLoc + 3) = 0xE1A00000; *(u32 *)(cfgN3dsCpuLoc + 3) = 0xE1A00000;
*(u32 *)(cfgN3dsCpuLoc + 0x1F) = 0xE3A00003; *(u32 *)(cfgN3dsCpuLoc + 0x1F) = 0xE3A00000 + MULTICONFIG(1);
} }
} }

View File

@ -19,128 +19,162 @@ void configureCFW(const char *configPath)
drawString(CONFIG_TITLE, 10, 10, COLOR_TITLE); drawString(CONFIG_TITLE, 10, 10, COLOR_TITLE);
drawString("Press A to select, START to save and reboot", 10, 30, COLOR_WHITE); drawString("Press A to select, START to save and reboot", 10, 30, COLOR_WHITE);
const char *optionsText[] = { "Screen-init brightness: 4( ) 3( ) 2( ) 1( )", const char *multiOptionsText[] = { "Screen-init brightness: 4( ) 3( ) 2( ) 1( )",
"( ) Autoboot SysNAND", "New 3DS CPU: Off( ) L2( ) Clock( ) L2+Clock( )" };
"( ) Updated SysNAND mode (A9LH-only)",
"( ) Force A9LH detection",
"( ) Use second EmuNAND as default",
"( ) Force max N3DS clock speed and L2 cache",
"( ) Use developer UNITINFO",
"( ) Show current NAND in System Settings",
"( ) Show GBA boot screen in patched AGB_FIRM",
"( ) Enable splash screen with no screen-init" };
u32 optionsAmount = sizeof(optionsText) / sizeof(char *); const char *singleOptionsText[] = { "( ) Autoboot SysNAND",
"( ) Updated SysNAND mode (A9LH-only)",
"( ) Force A9LH detection",
"( ) Use second EmuNAND as default",
"( ) Use developer UNITINFO",
"( ) Show current NAND in System Settings",
"( ) Show GBA boot screen in patched AGB_FIRM",
"( ) Enable splash screen with no screen-init" };
struct option { struct multiOption {
int posY; int posY;
u32 enabled; u32 enabled;
} options[optionsAmount]; int posXs[4];
} multiOptions[] = {
{ .posXs = {26, 31, 36, 41} },
{ .posXs = {17, 23, 32, 44} }
};
//Parse the existing configuration //Calculate the amount of the various kinds of options and pre-select the first single one
options[0].enabled = CONFIG(14, 3); u32 multiOptionsAmount = sizeof(multiOptions) / sizeof(struct multiOption),
for(u32 i = optionsAmount - 1; i; i--) singleOptionsAmount = sizeof(singleOptionsText) / sizeof(char *),
options[i].enabled = CONFIG((i - 1), 1); totalOptions = multiOptionsAmount + singleOptionsAmount,
selectedOption = multiOptionsAmount;
//Pre-select the first configuration option struct singleOption {
u32 selectedOption = 1, int posY;
oldSelectedOption = 0, u32 enabled;
optionChanged = 0; } singleOptions[singleOptionsAmount];
//Parse the existing options
for(u32 i = 0; i < multiOptionsAmount; i++)
multiOptions[i].enabled = MULTICONFIG(i);
for(u32 i = 0; i < singleOptionsAmount; i++)
singleOptions[i].enabled = CONFIG(i);
//Character to display a selected option //Character to display a selected option
char selected = 'x'; char selected = 'x';
//Starting position int endPos = 42;
options[0].posY = 52;
//Display all the normal options in white, brightness will be displayed later //Display all the normal options in white
for(u32 i = 1; i < optionsAmount; i++) for(u32 i = 0; i < multiOptionsAmount; i++)
{ {
static int endPos = 59; multiOptions[i].posY = endPos + SPACING_Y;
options[i].posY = endPos + SPACING_Y; endPos = drawString(multiOptionsText[i], 10, multiOptions[i].posY, COLOR_WHITE);
endPos = drawString(optionsText[i], 10, options[i].posY, COLOR_WHITE); drawCharacter(selected, 10 + multiOptions[i].posXs[multiOptions[i].enabled] * SPACING_X, multiOptions[i].posY, COLOR_WHITE);
if(options[i].enabled) drawCharacter(selected, 10 + SPACING_X, options[i].posY, COLOR_WHITE);
} }
//Boring configuration menu //Display the first normal option in red
while(1) singleOptions[0].posY = endPos + SPACING_Y + SPACING_Y / 2;
endPos = drawString(singleOptionsText[0], 10, singleOptions[0].posY, COLOR_RED);
//Display all the multiple choice options in white except for the first
for(u32 i = 1; i < singleOptionsAmount; i++)
{ {
u32 pressed = 0; singleOptions[i].posY = endPos + SPACING_Y;
endPos = drawString(singleOptionsText[i], 10, singleOptions[i].posY, COLOR_WHITE);
if(singleOptions[i].enabled) drawCharacter(selected, 10 + SPACING_X, singleOptions[i].posY, COLOR_WHITE);
}
u32 pressed = 0;
//Boring configuration menu
while(pressed != BUTTON_START)
{
pressed = 0;
do { do {
//The status of the selected option changed, black out the previously visible 'x' if needed //In any case, if the current option is enabled (or a multiple choice option is selected) we must display a red 'x'
if(optionChanged) if(selectedOption < multiOptionsAmount)
{ drawCharacter(selected, 10 + multiOptions[selectedOption].posXs[multiOptions[selectedOption].enabled] * SPACING_X, multiOptions[selectedOption].posY, COLOR_RED);
if(!selectedOption) else if(singleOptions[selectedOption - multiOptionsAmount].enabled)
drawCharacter(selected, 10 + (26 + 5 * (optionChanged - 1)) * SPACING_X, options[0].posY, COLOR_BLACK); drawCharacter(selected, 10 + SPACING_X, singleOptions[selectedOption - multiOptionsAmount].posY, COLOR_RED);
else if(!options[selectedOption].enabled)
drawCharacter(selected, 10 + SPACING_X, options[selectedOption].posY, COLOR_BLACK);
optionChanged = 0;
}
//The selected option changed, draw the new one in red and the old one (with its 'x') in white
else if(selectedOption != oldSelectedOption)
{
drawString(optionsText[oldSelectedOption], 10, options[oldSelectedOption].posY, COLOR_WHITE);
drawString(optionsText[selectedOption], 10, options[selectedOption].posY, COLOR_RED);
if(!oldSelectedOption)
drawCharacter(selected, 10 + (26 + 5 * options[0].enabled) * SPACING_X, options[0].posY, COLOR_WHITE);
else if(options[oldSelectedOption].enabled)
drawCharacter(selected, 10 + SPACING_X, options[oldSelectedOption].posY, COLOR_WHITE);
}
//In any case, if the current option is enabled (or brightness is selected) we must display a red 'x'
if(!selectedOption)
drawCharacter(selected, 10 + (26 + 5 * options[0].enabled) * SPACING_X, options[0].posY, COLOR_RED);
else if(options[selectedOption].enabled)
drawCharacter(selected, 10 + SPACING_X, options[selectedOption].posY, COLOR_RED);
pressed = waitInput(); pressed = waitInput();
} }
while(!(pressed & MENU_BUTTONS)); while(!(pressed & MENU_BUTTONS));
//Remember the previously selected option //Remember the previously selected option
static u32 oldSelectedOption;
oldSelectedOption = selectedOption; oldSelectedOption = selectedOption;
switch(pressed) if(pressed != BUTTON_A)
{ {
case BUTTON_UP: switch(pressed)
selectedOption = !selectedOption ? optionsAmount - 1 : selectedOption - 1; {
break; case BUTTON_UP:
case BUTTON_DOWN: selectedOption = !selectedOption ? totalOptions - 1 : selectedOption - 1;
selectedOption = selectedOption == (optionsAmount - 1) ? 1 : selectedOption + 1; break;
break; case BUTTON_DOWN:
case BUTTON_LEFT: selectedOption = selectedOption == (totalOptions - 1) ? 0 : selectedOption + 1;
selectedOption = 1; break;
break; case BUTTON_LEFT:
case BUTTON_RIGHT: if(!selectedOption) continue;
selectedOption = optionsAmount - 1; selectedOption = 0;
break; break;
case BUTTON_A: case BUTTON_RIGHT:
optionChanged = 1; if(selectedOption == totalOptions - 1) continue;
if(selectedOption) options[selectedOption].enabled = !options[selectedOption].enabled; selectedOption = totalOptions - 1;
else break;
{ default:
optionChanged += options[0].enabled; continue;
options[0].enabled = options[0].enabled == 3 ? 0 : options[0].enabled + 1; }
}
break;
}
if(pressed == BUTTON_START) break; //The user moved to a different option, print the old option in white and the new one in white. Only print 'x's if necessary
if(oldSelectedOption < multiOptionsAmount)
{
drawString(multiOptionsText[oldSelectedOption], 10, multiOptions[oldSelectedOption].posY, COLOR_WHITE);
drawCharacter(selected, 10 + multiOptions[oldSelectedOption].posXs[multiOptions[oldSelectedOption].enabled] * SPACING_X, multiOptions[oldSelectedOption].posY, COLOR_WHITE);
}
else
{
u32 singleOldSelected = oldSelectedOption - multiOptionsAmount;
drawString(singleOptionsText[singleOldSelected], 10, singleOptions[singleOldSelected].posY, COLOR_WHITE);
if(singleOptions[singleOldSelected].enabled) drawCharacter(selected, 10 + SPACING_X, singleOptions[singleOldSelected].posY, COLOR_WHITE);
}
if(selectedOption < multiOptionsAmount)
drawString(multiOptionsText[selectedOption], 10, multiOptions[selectedOption].posY, COLOR_RED);
else
{
u32 singleSelected = selectedOption - multiOptionsAmount;
drawString(singleOptionsText[singleSelected], 10, singleOptions[singleSelected].posY, COLOR_RED);
}
}
else
{
//The selected option's status changed, print the 'x's accordingly.
if(selectedOption < multiOptionsAmount)
{
u32 oldEnabled = multiOptions[selectedOption].enabled;
drawCharacter(selected, 10 + multiOptions[selectedOption].posXs[oldEnabled] * SPACING_X, multiOptions[selectedOption].posY, COLOR_BLACK);
multiOptions[selectedOption].enabled = oldEnabled == 3 ? 0 : oldEnabled + 1;
}
else
{
u32 oldEnabled = singleOptions[selectedOption - multiOptionsAmount].enabled;
singleOptions[selectedOption - multiOptionsAmount].enabled = !oldEnabled;
if(oldEnabled) drawCharacter(selected, 10 + SPACING_X, singleOptions[selectedOption - multiOptionsAmount].posY, COLOR_BLACK);
}
}
} }
//Preserve the last-used boot options (last 12 bits) //Preserve the last-used boot options (last 12 bits)
config &= 0xFF0000; config &= 0x3F;
//Parse and write the new configuration //Parse and write the new configuration
config |= options[0].enabled << 14; for(u32 i = 0; i < multiOptionsAmount; i++)
for(u32 i = optionsAmount - 1; i; i--) config |= multiOptions[i].enabled << (i * 2 + 6);
config |= options[i].enabled << (i - 1); for(u32 i = 0; i < singleOptionsAmount; i++)
config |= singleOptions[i].enabled << (i + 16);
fileWrite(&config, configPath, 3); fileWrite(&config, configPath, 4);
//Zero the last booted FIRM flag //Zero the last booted FIRM flag
CFG_BOOTENV = 0; CFG_BOOTENV = 0;

View File

@ -9,7 +9,10 @@
#include "types.h" #include "types.h"
#define CFG_BOOTENV (*(vu32 *)0x10010000) #define CFG_BOOTENV (*(vu32 *)0x10010000)
#define CONFIG(a, b) ((config >> a) & b)
#define CONFIG(a) ((config >> (a + 16)) & 1)
#define MULTICONFIG(a) ((config >> (a * 2 + 6)) & 3)
#define BOOTCONFIG(a, b) ((config >> a) & b)
extern u32 config; extern u32 config;

View File

@ -48,7 +48,7 @@ void main(void)
//Attempt to read the configuration file //Attempt to read the configuration file
const char configPath[] = "/aurei/config.bin"; const char configPath[] = "/aurei/config.bin";
if(fileRead(&config, configPath, 3)) needConfig = 1; if(fileRead(&config, configPath, 4)) needConfig = 1;
else else
{ {
config = 0; config = 0;
@ -63,10 +63,10 @@ void main(void)
//'0' = NATIVE_FIRM, '1' = TWL_FIRM, '2' = AGB_FIRM //'0' = NATIVE_FIRM, '1' = TWL_FIRM, '2' = AGB_FIRM
firmType = *(vu8 *)0x23F00005 - '0'; firmType = *(vu8 *)0x23F00005 - '0';
nandType = CONFIG(16, 3); nandType = BOOTCONFIG(0, 3);
firmSource = CONFIG(18, 1); firmSource = BOOTCONFIG(2, 1);
a9lhInstalled = CONFIG(19, 1); a9lhInstalled = BOOTCONFIG(3, 1);
updatedSys = (a9lhInstalled && CONFIG(1, 1)) ? 1 : 0; updatedSys = (a9lhInstalled && CONFIG(1)) ? 1 : 0;
} }
else else
{ {
@ -83,10 +83,10 @@ void main(void)
u32 pressed = HID_PAD; u32 pressed = HID_PAD;
//Determine if we need to autoboot sysNAND //Determine if we need to autoboot sysNAND
u32 autoBootSys = CONFIG(0, 1); u32 autoBootSys = CONFIG(0);
//Determine if A9LH is installed and the user has an updated sysNAND //Determine if A9LH is installed and the user has an updated sysNAND
if(a9lhBoot || CONFIG(2, 1)) if(a9lhBoot || CONFIG(2))
{ {
if(pressed == SAFE_MODE) if(pressed == SAFE_MODE)
error("Using Safe Mode would brick you, or remove A9LH!"); error("Using Safe Mode would brick you, or remove A9LH!");
@ -94,7 +94,7 @@ void main(void)
a9lhInstalled = 1; a9lhInstalled = 1;
//Check setting for > 9.2 sysNAND //Check setting for > 9.2 sysNAND
updatedSys = CONFIG(1, 1); updatedSys = CONFIG(1);
} }
else else
{ {
@ -102,7 +102,7 @@ void main(void)
updatedSys = 0; updatedSys = 0;
} }
newConfig = a9lhInstalled << 19; newConfig = a9lhInstalled << 3;
/* If booting with A9LH, it's a MCU reboot and a previous configuration exists, /* If booting with A9LH, it's a MCU reboot and a previous configuration exists,
try to force boot options */ try to force boot options */
@ -112,18 +112,18 @@ void main(void)
if(previousFirm == 7) if(previousFirm == 7)
{ {
nandType = 0; nandType = 0;
firmSource = updatedSys ? 0 : CONFIG(18, 1); firmSource = updatedSys ? 0 : BOOTCONFIG(0, 3);
needConfig = 0; needConfig = 0;
//Flag to prevent multiple boot options-forcing //Flag to prevent multiple boot options-forcing
newConfig |= 1 << 20; newConfig |= 1 << 4;
} }
/* Else, force the last used boot options unless a payload button or A/L/R are pressed /* Else, force the last used boot options unless a payload button or A/L/R are pressed
or the no-forcing flag is set */ or the no-forcing flag is set */
else if(!(pressed & OVERRIDE_BUTTONS) && !CONFIG(20, 1)) else if(!(pressed & OVERRIDE_BUTTONS) && !BOOTCONFIG(4, 1))
{ {
nandType = CONFIG(16, 3); nandType = BOOTCONFIG(0, 3);
firmSource = CONFIG(18, 1); firmSource = BOOTCONFIG(2, 1);
needConfig = 0; needConfig = 0;
} }
} }
@ -142,14 +142,14 @@ void main(void)
configureCFW(configPath); configureCFW(configPath);
//If screens are inited or the corresponding option is set, load splash screen //If screens are inited or the corresponding option is set, load splash screen
if(PDN_GPU_CNT != 1 || CONFIG(8, 1)) loadSplash(); if(PDN_GPU_CNT != 1 || CONFIG(7)) loadSplash();
//Determine if we need to boot an emuNAND or sysNAND //Determine if we need to boot an emuNAND or sysNAND
nandType = (pressed & BUTTON_L1) ? autoBootSys : ((pressed & BUTTON_R1) ? updatedSys : !autoBootSys); nandType = (pressed & BUTTON_L1) ? autoBootSys : ((pressed & BUTTON_R1) ? updatedSys : !autoBootSys);
/* If we're booting emuNAND the second emuNAND is set as default and B isn't pressed, /* If we're booting emuNAND the second emuNAND is set as default and B isn't pressed,
or vice-versa, boot the second emuNAND */ or vice-versa, boot the second emuNAND */
if(nandType && ((!(pressed & BUTTON_B)) == CONFIG(3, 1))) nandType++; if(nandType && ((!(pressed & BUTTON_B)) == CONFIG(3))) nandType++;
//Determine the NAND we should take the FIRM from //Determine the NAND we should take the FIRM from
firmSource = (pressed & BUTTON_R1) ? !nandType : (nandType != 0); firmSource = (pressed & BUTTON_R1) ? !nandType : (nandType != 0);
@ -169,16 +169,16 @@ void main(void)
if(!bootType) if(!bootType)
{ {
newConfig |= (nandType << 16) | (firmSource << 18); newConfig |= nandType | (firmSource << 2);
/* If the boot configuration is different from previously, overwrite it. /* If the boot configuration is different from previously, overwrite it.
Just the no-forcing flag being set is not enough */ Just the no-forcing flag being set is not enough */
if((newConfig & 0xEF0000) != (config & 0xFF0000)) if((newConfig & 0x2F) != (config & 0xFF))
{ {
//Preserve user settings (first 2 bytes) //Preserve user settings (first 2 bytes)
newConfig |= config & 0xFFFF; newConfig |= config & 0xFFFFFFC0;
fileWrite(&newConfig, configPath, 3); fileWrite(&newConfig, configPath, 4);
} }
} }
@ -271,7 +271,7 @@ static inline void patchNativeFirm(u32 nandType, u32 emuHeader, u32 a9lhInstalle
*(u16 *)sigOffset2 = sigPatch[0]; *(u16 *)sigOffset2 = sigPatch[0];
*((u16 *)sigOffset2 + 1) = sigPatch[1]; *((u16 *)sigOffset2 + 1) = sigPatch[1];
if(CONFIG(5, 1)) if(CONFIG(4))
{ {
//Apply UNITINFO patch //Apply UNITINFO patch
u8 *unitInfoOffset = getUnitInfoValueSet(arm9Section, section[2].size); u8 *unitInfoOffset = getUnitInfoValueSet(arm9Section, section[2].size);
@ -383,7 +383,7 @@ static inline void patchTwlAgbFirm(u32 firmType)
/* Calculate the amount of patches to apply. Only count the boot screen patch for AGB_FIRM /* Calculate the amount of patches to apply. Only count the boot screen patch for AGB_FIRM
if the matching option was enabled (keep it as last) */ if the matching option was enabled (keep it as last) */
u32 numPatches = firmType == 1 ? (sizeof(twlPatches) / sizeof(patchData)) : (sizeof(agbPatches) / sizeof(patchData) - !CONFIG(7, 1)); u32 numPatches = firmType == 1 ? (sizeof(twlPatches) / sizeof(patchData)) : (sizeof(agbPatches) / sizeof(patchData) - !CONFIG(6));
const patchData *patches = firmType == 1 ? twlPatches : agbPatches; const patchData *patches = firmType == 1 ? twlPatches : agbPatches;
//Patch //Patch

View File

@ -96,8 +96,8 @@ void firmRead(void *dest, const char *firmFolder)
u32 tempId = 0; u32 tempId = 0;
for(char *tmp = info.fname; (*tmp) != '.'; tmp++) for(char *tmp = info.fname; (*tmp) != '.'; tmp++)
{ {
if((*tmp) > '9') tempId = (tempId << 4) + ((*tmp) - 'A') + 9; tempId <<= 4;
else tempId = (tempId << 4) + (*tmp) - '0'; tempId += *tmp > '9' ? (*tmp - 'A') + 9 : (*tmp) - '0';
} }
//Found a newer cxi //Found a newer cxi
@ -116,13 +116,10 @@ void firmRead(void *dest, const char *firmFolder)
while(id > 15) while(id > 15)
{ {
u32 remainder = (id % 16); u32 remainder = (id % 16);
if(remainder > 9) path[i] = remainder - 9 + 'A'; path[i--] = remainder > 9 ? remainder - 9 + 'A' : remainder + '0';
else path[i] = remainder + '0';
id /= 16; id /= 16;
i--;
} }
if(id > 9) path[i] = id - 9 + 'A'; path[i] = id > 9 ? id - 9 + 'A' : id + '0';
else path[i] = id + '0';
fileRead(dest, path, 0); fileRead(dest, path, 0);
} }

View File

@ -54,7 +54,7 @@ void initScreens(void)
memcpy((void *)SCREENINIT_ADDRESS, screeninit, screeninit_size); memcpy((void *)SCREENINIT_ADDRESS, screeninit, screeninit_size);
//Write brightness level for the stub to pick up //Write brightness level for the stub to pick up
*(vu32 *)(SCREENINIT_ADDRESS + 8) = CONFIG(14, 3); *(vu32 *)(SCREENINIT_ADDRESS + 8) = MULTICONFIG(0);
*arm11Entry = SCREENINIT_ADDRESS; *arm11Entry = SCREENINIT_ADDRESS;
while(*arm11Entry); while(*arm11Entry);