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
#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
static u32 config = 0;
@ -206,11 +209,11 @@ void patchCode(u64 progId, u8 *code, u32 size)
case 0x0004001000027000LL: // KOR MSET
case 0x0004001000028000LL: // TWN MSET
{
if(R_SUCCEEDED(loadConfig()) && ((config >> 6) & 1))
if(R_SUCCEEDED(loadConfig()) && CONFIG(6))
{
static const u16 verPattern[] = u"Ver.";
const u32 currentNand = ((config >> 16) & 3);
const u32 matchingFirm = ((config >> 18) & 1) == (currentNand != 0);
const u32 currentNand = BOOTCONFIG(0, 3);
const u32 matchingFirm = BOOTCONFIG(2, 1) == (currentNand != 0);
//Patch Ver. string
patchMemory(code, size,
@ -242,7 +245,7 @@ void patchCode(u64 progId, u8 *code, u32 size)
sizeof(stopCartUpdatesPatch), 2
);
if(R_SUCCEEDED(loadConfig()) && ((config >> 4) & 1))
if(R_SUCCEEDED(loadConfig()) && MULTICONFIG(1))
{
static const u8 cfgN3dsCpuPattern[] = {
0x40, 0xA0, 0xE1, 0x07, 0x00
@ -254,7 +257,7 @@ void patchCode(u64 progId, u8 *code, u32 size)
if(cfgN3dsCpuLoc != NULL)
{
*(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("Press A to select, START to save and reboot", 10, 30, COLOR_WHITE);
const char *optionsText[] = { "Screen-init brightness: 4( ) 3( ) 2( ) 1( )",
"( ) Autoboot SysNAND",
const char *multiOptionsText[] = { "Screen-init brightness: 4( ) 3( ) 2( ) 1( )",
"New 3DS CPU: Off( ) L2( ) Clock( ) L2+Clock( )" };
const char *singleOptionsText[] = { "( ) Autoboot SysNAND",
"( ) 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 *);
struct option {
struct multiOption {
int posY;
u32 enabled;
} options[optionsAmount];
int posXs[4];
} multiOptions[] = {
{ .posXs = {26, 31, 36, 41} },
{ .posXs = {17, 23, 32, 44} }
};
//Parse the existing configuration
options[0].enabled = CONFIG(14, 3);
for(u32 i = optionsAmount - 1; i; i--)
options[i].enabled = CONFIG((i - 1), 1);
//Calculate the amount of the various kinds of options and pre-select the first single one
u32 multiOptionsAmount = sizeof(multiOptions) / sizeof(struct multiOption),
singleOptionsAmount = sizeof(singleOptionsText) / sizeof(char *),
totalOptions = multiOptionsAmount + singleOptionsAmount,
selectedOption = multiOptionsAmount;
//Pre-select the first configuration option
u32 selectedOption = 1,
oldSelectedOption = 0,
optionChanged = 0;
struct singleOption {
int posY;
u32 enabled;
} 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
char selected = 'x';
//Starting position
options[0].posY = 52;
int endPos = 42;
//Display all the normal options in white, brightness will be displayed later
for(u32 i = 1; i < optionsAmount; i++)
//Display all the normal options in white
for(u32 i = 0; i < multiOptionsAmount; i++)
{
static int endPos = 59;
options[i].posY = endPos + SPACING_Y;
endPos = drawString(optionsText[i], 10, options[i].posY, COLOR_WHITE);
if(options[i].enabled) drawCharacter(selected, 10 + SPACING_X, options[i].posY, COLOR_WHITE);
multiOptions[i].posY = endPos + SPACING_Y;
endPos = drawString(multiOptionsText[i], 10, multiOptions[i].posY, COLOR_WHITE);
drawCharacter(selected, 10 + multiOptions[i].posXs[multiOptions[i].enabled] * SPACING_X, multiOptions[i].posY, COLOR_WHITE);
}
//Boring configuration menu
while(1)
//Display the first normal option in red
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++)
{
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 {
//The status of the selected option changed, black out the previously visible 'x' if needed
if(optionChanged)
{
if(!selectedOption)
drawCharacter(selected, 10 + (26 + 5 * (optionChanged - 1)) * SPACING_X, options[0].posY, COLOR_BLACK);
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);
//In any case, if the current option is enabled (or a multiple choice option is selected) we must display a red 'x'
if(selectedOption < multiOptionsAmount)
drawCharacter(selected, 10 + multiOptions[selectedOption].posXs[multiOptions[selectedOption].enabled] * SPACING_X, multiOptions[selectedOption].posY, COLOR_RED);
else if(singleOptions[selectedOption - multiOptionsAmount].enabled)
drawCharacter(selected, 10 + SPACING_X, singleOptions[selectedOption - multiOptionsAmount].posY, COLOR_RED);
pressed = waitInput();
}
while(!(pressed & MENU_BUTTONS));
//Remember the previously selected option
static u32 oldSelectedOption;
oldSelectedOption = selectedOption;
if(pressed != BUTTON_A)
{
switch(pressed)
{
case BUTTON_UP:
selectedOption = !selectedOption ? optionsAmount - 1 : selectedOption - 1;
selectedOption = !selectedOption ? totalOptions - 1 : selectedOption - 1;
break;
case BUTTON_DOWN:
selectedOption = selectedOption == (optionsAmount - 1) ? 1 : selectedOption + 1;
selectedOption = selectedOption == (totalOptions - 1) ? 0 : selectedOption + 1;
break;
case BUTTON_LEFT:
selectedOption = 1;
if(!selectedOption) continue;
selectedOption = 0;
break;
case BUTTON_RIGHT:
selectedOption = optionsAmount - 1;
break;
case BUTTON_A:
optionChanged = 1;
if(selectedOption) options[selectedOption].enabled = !options[selectedOption].enabled;
else
{
optionChanged += options[0].enabled;
options[0].enabled = options[0].enabled == 3 ? 0 : options[0].enabled + 1;
}
if(selectedOption == totalOptions - 1) continue;
selectedOption = totalOptions - 1;
break;
default:
continue;
}
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)
config &= 0xFF0000;
config &= 0x3F;
//Parse and write the new configuration
config |= options[0].enabled << 14;
for(u32 i = optionsAmount - 1; i; i--)
config |= options[i].enabled << (i - 1);
for(u32 i = 0; i < multiOptionsAmount; i++)
config |= multiOptions[i].enabled << (i * 2 + 6);
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
CFG_BOOTENV = 0;

View File

@ -9,7 +9,10 @@
#include "types.h"
#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;

View File

@ -48,7 +48,7 @@ void main(void)
//Attempt to read the configuration file
const char configPath[] = "/aurei/config.bin";
if(fileRead(&config, configPath, 3)) needConfig = 1;
if(fileRead(&config, configPath, 4)) needConfig = 1;
else
{
config = 0;
@ -63,10 +63,10 @@ void main(void)
//'0' = NATIVE_FIRM, '1' = TWL_FIRM, '2' = AGB_FIRM
firmType = *(vu8 *)0x23F00005 - '0';
nandType = CONFIG(16, 3);
firmSource = CONFIG(18, 1);
a9lhInstalled = CONFIG(19, 1);
updatedSys = (a9lhInstalled && CONFIG(1, 1)) ? 1 : 0;
nandType = BOOTCONFIG(0, 3);
firmSource = BOOTCONFIG(2, 1);
a9lhInstalled = BOOTCONFIG(3, 1);
updatedSys = (a9lhInstalled && CONFIG(1)) ? 1 : 0;
}
else
{
@ -83,10 +83,10 @@ void main(void)
u32 pressed = HID_PAD;
//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
if(a9lhBoot || CONFIG(2, 1))
if(a9lhBoot || CONFIG(2))
{
if(pressed == SAFE_MODE)
error("Using Safe Mode would brick you, or remove A9LH!");
@ -94,7 +94,7 @@ void main(void)
a9lhInstalled = 1;
//Check setting for > 9.2 sysNAND
updatedSys = CONFIG(1, 1);
updatedSys = CONFIG(1);
}
else
{
@ -102,7 +102,7 @@ void main(void)
updatedSys = 0;
}
newConfig = a9lhInstalled << 19;
newConfig = a9lhInstalled << 3;
/* If booting with A9LH, it's a MCU reboot and a previous configuration exists,
try to force boot options */
@ -112,18 +112,18 @@ void main(void)
if(previousFirm == 7)
{
nandType = 0;
firmSource = updatedSys ? 0 : CONFIG(18, 1);
firmSource = updatedSys ? 0 : BOOTCONFIG(0, 3);
needConfig = 0;
//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
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);
firmSource = CONFIG(18, 1);
nandType = BOOTCONFIG(0, 3);
firmSource = BOOTCONFIG(2, 1);
needConfig = 0;
}
}
@ -142,14 +142,14 @@ void main(void)
configureCFW(configPath);
//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
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,
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
firmSource = (pressed & BUTTON_R1) ? !nandType : (nandType != 0);
@ -169,16 +169,16 @@ void main(void)
if(!bootType)
{
newConfig |= (nandType << 16) | (firmSource << 18);
newConfig |= nandType | (firmSource << 2);
/* If the boot configuration is different from previously, overwrite it.
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)
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 + 1) = sigPatch[1];
if(CONFIG(5, 1))
if(CONFIG(4))
{
//Apply UNITINFO patch
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
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;
//Patch

View File

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

View File

@ -54,7 +54,7 @@ void initScreens(void)
memcpy((void *)SCREENINIT_ADDRESS, screeninit, screeninit_size);
//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;
while(*arm11Entry);