Lots of changes/new features
- To override the last used boot mode on soft reboot, you only need to press A if you want to boot to the default option. Holding L(+payload button)/R is enough for the other modes. - Added version number to the config menu - Replaced the memsearch algorithm with a faster one - Integrated 3ds_injector from @yifanlu. This brings us region free and all the other FreeMultiPatcher patches. Other than that, you now have the possibility to display the currently booted NAND/FIRM in System Settings! - Rewritten most code for the config menu. You now can navigate to the first/last options with left and right. - You can now choose the 9.0 FIRM to be default in the config menu. This will essentially switch "no buttons" and L in both modes. - You can now choose the second emuNAND to be default in the config menu. This will essentially switch "B is not pressed" and "B is pressed". - When the second emuNAND is booted, it will persist like the other boot options on soft reboot - Bugfixes
This commit is contained in:
@@ -13,10 +13,14 @@
|
||||
#define BUTTON_L1 (1 << 9)
|
||||
#define BUTTON_A 1
|
||||
#define BUTTON_B (1 << 1)
|
||||
#define BUTTON_SELECT (1 << 2)
|
||||
#define BUTTON_START (1 << 3)
|
||||
#define BUTTON_RIGHT (1 << 4)
|
||||
#define BUTTON_LEFT (1 << 5)
|
||||
#define BUTTON_UP (1 << 6)
|
||||
#define BUTTON_DOWN (1 << 7)
|
||||
#define BUTTON_START (1 << 3)
|
||||
#define BUTTON_SELECT (1 << 2)
|
||||
#define SAFE_MODE (BUTTON_R1 | BUTTON_L1 | BUTTON_A | BUTTON_UP)
|
||||
#define OPTION_BUTTONS (BUTTON_R1 | BUTTON_L1 | BUTTON_A)
|
||||
#define PAYLOAD_BUTTONS ((BUTTON_L1 | BUTTON_A) ^ 0xFFF)
|
||||
#define BUTTON_L1R1 ((1 << 8) | (1 << 9))
|
||||
#define SAFE_MODE (BUTTON_L1R1 | BUTTON_A | BUTTON_UP)
|
||||
#define OPTION_BUTTONS (BUTTON_L1R1 | BUTTON_A)
|
||||
#define PAYLOAD_BUTTONS ((BUTTON_L1 | BUTTON_A) ^ 0xFFF)
|
||||
#define MENU_BUTTONS (BUTTON_LEFT | BUTTON_RIGHT | BUTTON_UP | BUTTON_DOWN | BUTTON_A | BUTTON_START)
|
||||
@@ -42,41 +42,41 @@ void loadSplash(void){
|
||||
}
|
||||
}
|
||||
|
||||
void drawCharacter(char character, int pos_x, int pos_y, u32 color){
|
||||
void drawCharacter(char character, int posX, int posY, u32 color){
|
||||
u8 *const select = fb->top_left;
|
||||
|
||||
for(int y = 0; y < 8; y++){
|
||||
unsigned char char_pos = font[character * 8 + y];
|
||||
char charPos = font[character * 8 + y];
|
||||
|
||||
for(int x = 7; x >= 0; x--){
|
||||
int screen_pos = (pos_x * SCREEN_TOP_HEIGHT * 3 + (SCREEN_TOP_HEIGHT - y - pos_y - 1) * 3) + (7 - x) * 3 * SCREEN_TOP_HEIGHT;
|
||||
int screenPos = (posX * SCREEN_TOP_HEIGHT * 3 + (SCREEN_TOP_HEIGHT - y - posY - 1) * 3) + (7 - x) * 3 * SCREEN_TOP_HEIGHT;
|
||||
|
||||
if ((char_pos >> x) & 1) {
|
||||
select[screen_pos] = color >> 16;
|
||||
select[screen_pos + 1] = color >> 8;
|
||||
select[screen_pos + 2] = color;
|
||||
if ((charPos >> x) & 1) {
|
||||
select[screenPos] = color >> 16;
|
||||
select[screenPos + 1] = color >> 8;
|
||||
select[screenPos + 2] = color;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int drawString(const char *string, int pos_x, int pos_y, u32 color){
|
||||
int drawString(const char *string, int posX, int posY, u32 color){
|
||||
int length = strlen(string);
|
||||
|
||||
for(int i = 0, line_i = 0; i < length; i++, line_i++){
|
||||
if(string[i] == '\n'){
|
||||
pos_y += SPACING_VERT;
|
||||
posY += SPACING_Y;
|
||||
line_i = 0;
|
||||
i++;
|
||||
} else if(line_i >= (SCREEN_TOP_WIDTH - pos_x) / SPACING_HORIZ){
|
||||
} else if(line_i >= (SCREEN_TOP_WIDTH - posX) / SPACING_X){
|
||||
// Make sure we never get out of the screen.
|
||||
pos_y += SPACING_VERT;
|
||||
posY += SPACING_Y;
|
||||
line_i = 2; // Little offset so we know the same string continues.
|
||||
if(string[i] == ' ') i++; // Spaces at the start look weird
|
||||
}
|
||||
|
||||
drawCharacter(string[i], pos_x + line_i * SPACING_HORIZ, pos_y, color);
|
||||
drawCharacter(string[i], posX + line_i * SPACING_X, posY, color);
|
||||
}
|
||||
|
||||
return pos_y;
|
||||
return posY;
|
||||
}
|
||||
@@ -10,10 +10,10 @@
|
||||
|
||||
#include "types.h"
|
||||
|
||||
#define SPACING_VERT 10
|
||||
#define SPACING_HORIZ 8
|
||||
#define SPACING_Y 10
|
||||
#define SPACING_X 8
|
||||
|
||||
void loadSplash(void);
|
||||
void clearScreens(void);
|
||||
void drawCharacter(char character, int pos_x, int pos_y, u32 color);
|
||||
int drawString(const char *string, int pos_x, int pos_y, u32 color);
|
||||
void drawCharacter(char character, int posX, int posY, u32 color);
|
||||
int drawString(const char *string, int posX, int posY, u32 color);
|
||||
@@ -15,17 +15,17 @@ void getEmunandSect(u32 *off, u32 *head, u32 emuNAND){
|
||||
u32 nandOffset = emuNAND == 1 ? 0 :
|
||||
(nandSize > 0x200000 ? 0x400000 : 0x200000);
|
||||
|
||||
//Check for Gateway emuNAND
|
||||
if(sdmmc_sdcard_readsectors(nandOffset + nandSize, 1, temp) == 0){
|
||||
//Check for RedNAND
|
||||
if(sdmmc_sdcard_readsectors(nandOffset + 1, 1, temp) == 0){
|
||||
if(*(u32 *)(temp + 0x100) == NCSD_MAGIC){
|
||||
*off = nandOffset + 1;
|
||||
*head = nandOffset + 1;
|
||||
}
|
||||
//Check for Gateway emuNAND
|
||||
else if(sdmmc_sdcard_readsectors(nandOffset + nandSize, 1, temp) == 0){
|
||||
if(*(u32 *)(temp + 0x100) == NCSD_MAGIC){
|
||||
*off = nandOffset;
|
||||
*head = nandOffset + nandSize;
|
||||
}
|
||||
//Check for RedNAND
|
||||
else if(sdmmc_sdcard_readsectors(nandOffset + 1, 1, temp) == 0){
|
||||
if(*(u32 *)(temp + 0x100) == NCSD_MAGIC){
|
||||
*off = nandOffset + 1;
|
||||
*head = nandOffset + 1;
|
||||
}
|
||||
//Fallback to the first emuNAND if there's no second one
|
||||
else if(emuNAND == 2) getEmunandSect(off, head, 1);
|
||||
@@ -33,32 +33,32 @@ void getEmunandSect(u32 *off, u32 *head, u32 emuNAND){
|
||||
}
|
||||
}
|
||||
|
||||
u32 getSDMMC(void *pos, u32 size){
|
||||
u32 getSDMMC(u8 *pos, u32 size){
|
||||
//Look for struct code
|
||||
const u8 pattern[] = {0x21, 0x20, 0x18, 0x20};
|
||||
const u8 *off = (u8 *)memsearch(pos, pattern, size, 4) - 1;
|
||||
const u8 *off = memsearch(pos, pattern, size, 4) - 1;
|
||||
|
||||
return *(u32 *)(off + 0x0A) + *(u32 *)(off + 0x0E);
|
||||
}
|
||||
|
||||
void getEmuRW(void *pos, u32 size, u32 *readOff, u32 *writeOff){
|
||||
void getEmuRW(u8 *pos, u32 size, u32 *readOff, u32 *writeOff){
|
||||
//Look for read/write code
|
||||
const u8 pattern[] = {0x1E, 0x00, 0xC8, 0x05};
|
||||
|
||||
*writeOff = (u32)memsearch(pos, pattern, size, 4) - 6;
|
||||
*readOff = (u32)memsearch((void *)(*writeOff - 0x1000), pattern, 0x1000, 4) - 6;
|
||||
*readOff = (u32)memsearch(pos, pattern, size, 4) - 6;
|
||||
*writeOff = (u32)memsearch((u8 *)(*readOff + 0xA), pattern, 0x100, 4) - 6;
|
||||
}
|
||||
|
||||
u32 *getMPU(void *pos, u32 size){
|
||||
u32 *getMPU(u8 *pos, u32 size){
|
||||
//Look for MPU pattern
|
||||
const u8 pattern[] = {0x03, 0x00, 0x24, 0x00};
|
||||
|
||||
return (u32 *)memsearch(pos, pattern, size, 4);
|
||||
}
|
||||
|
||||
void *getEmuCode(u8 *pos, u32 size, u8 *proc9Offset){
|
||||
const u8 pattern[] = {0x00, 0xFF, 0xFF, 0xFF};
|
||||
|
||||
void *getEmuCode(u8 *proc9Offset){
|
||||
const u8 pattern[] = {0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00};
|
||||
|
||||
//Looking for the last free space before Process9
|
||||
return (u8 *)memsearch(pos, pattern, size - (size - (u32)(proc9Offset - pos)), 4) + 0xD;
|
||||
return memsearch(proc9Offset - 0x3000, pattern, 0x3000, 6) + 0x455;
|
||||
}
|
||||
@@ -11,7 +11,7 @@
|
||||
#define NCSD_MAGIC (0x4453434E)
|
||||
|
||||
void getEmunandSect(u32 *off, u32 *head, u32 emuNAND);
|
||||
u32 getSDMMC(void *pos, u32 size);
|
||||
void getEmuRW(void *pos, u32 size, u32 *readOff, u32 *writeOff);
|
||||
u32 *getMPU(void *pos, u32 size);
|
||||
void *getEmuCode(u8 *pos, u32 size, u8 *proc9Offset);
|
||||
u32 getSDMMC(u8 *pos, u32 size);
|
||||
void getEmuRW(u8 *pos, u32 size, u32 *readOff, u32 *writeOff);
|
||||
u32 *getMPU(u8 *pos, u32 size);
|
||||
void *getEmuCode(u8 *proc9Offset);
|
||||
@@ -18,7 +18,7 @@
|
||||
#include "../build/patches.h"
|
||||
|
||||
//FIRM patches version
|
||||
#define PATCH_VER 1
|
||||
#define PATCH_VER 2
|
||||
|
||||
static firmHeader *const firm = (firmHeader *)0x24000000;
|
||||
static const firmSectionHeader *section;
|
||||
@@ -48,8 +48,8 @@ void setupCFW(void){
|
||||
|
||||
//Attempt to read the configuration file
|
||||
const char configPath[] = "aurei/config.bin";
|
||||
u16 config = 0;
|
||||
u32 needConfig = fileRead(&config, configPath, 2) ? 1 : 2;
|
||||
u32 config = 0;
|
||||
u32 needConfig = fileRead(&config, configPath, 3) ? 1 : 2;
|
||||
|
||||
//Determine if A9LH is installed and the user has an updated sysNAND
|
||||
u32 updatedSys;
|
||||
@@ -66,21 +66,18 @@ void setupCFW(void){
|
||||
updatedSys = 0;
|
||||
}
|
||||
|
||||
//Determine if the user chose to use pre-patched FIRMs
|
||||
u32 usePatchedFirmSet = (config >> 1) & 0x1;
|
||||
|
||||
/* If booting with A9LH, it's a MCU reboot and a previous configuration exists,
|
||||
try to force boot options */
|
||||
if(a9lhBoot && previousFirm && needConfig == 1){
|
||||
//Always force a sysNAND boot when quitting AGB_FIRM
|
||||
if(previousFirm == 0x7){
|
||||
if(!updatedSys) mode = (config >> 8) & 0x1;
|
||||
if(!updatedSys) mode = (config >> 12) & 0x1;
|
||||
emuNAND = 0;
|
||||
needConfig = 0;
|
||||
//Else, force the last used boot options unless A, L or R are pressed
|
||||
} else if(!(pressed & OPTION_BUTTONS)){
|
||||
mode = (config >> 8) & 0x1;
|
||||
emuNAND = (config >> 9) & 0x1;
|
||||
mode = (config >> 12) & 0x1;
|
||||
emuNAND = (config >> 13) & 0x3;
|
||||
needConfig = 0;
|
||||
}
|
||||
}
|
||||
@@ -94,45 +91,45 @@ void setupCFW(void){
|
||||
|
||||
//If no configuration file exists or SELECT is held, load configuration menu
|
||||
if(needConfig == 2 || (pressed & BUTTON_SELECT))
|
||||
configureCFW(configPath);
|
||||
configureCFW(configPath, patchedFirms[3]);
|
||||
|
||||
//If screens are inited, load splash screen
|
||||
if(PDN_GPU_CNT != 0x1) loadSplash();
|
||||
|
||||
/* If L is pressed, boot 9.0 FIRM */
|
||||
mode = (pressed & BUTTON_L1) ? 0 : 1;
|
||||
mode = ((config >> 3) & 0x1) ? ((!(pressed & BUTTON_L1R1)) ? 0 : 1) :
|
||||
((pressed & BUTTON_L1) ? 0 : 1);
|
||||
|
||||
/* If L or R aren't pressed on a 9.0/9.2 sysNAND, or the 9.0 FIRM is selected
|
||||
or R is pressed on a > 9.2 sysNAND, boot emuNAND */
|
||||
if((updatedSys && (!mode || (pressed & BUTTON_R1))) ||
|
||||
(!updatedSys && mode && !(pressed & BUTTON_R1))){
|
||||
//If not 9.0 FIRM and B is pressed, attempt booting the second emuNAND
|
||||
emuNAND = (mode && (pressed & BUTTON_B)) ? 2 : 1;
|
||||
emuNAND = (mode && ((!(pressed & BUTTON_B)) == ((config >> 4) & 0x1))) ? 2 : 1;
|
||||
} else emuNAND = 0;
|
||||
|
||||
u32 tempConfig = (PATCH_VER << 17) | (a9lhSetup << 16) | (emuNAND << 13) | (mode << 12);
|
||||
|
||||
/* If tha FIRM patches version is different or user switched to/from A9LH,
|
||||
and "Use pre-patched FIRMs" is set, delete all patched FIRMs */
|
||||
u16 bootConfig = (PATCH_VER << 11) | (a9lhSetup << 10);
|
||||
if(usePatchedFirmSet && bootConfig != (config & 0xFC00))
|
||||
delete all patched FIRMs */
|
||||
if((tempConfig & 0xFF0000) != (config & 0xFF0000))
|
||||
deleteFirms(patchedFirms, sizeof(patchedFirms) / sizeof(char *));
|
||||
|
||||
//We also need to remember the used boot mode on A9LH
|
||||
if(a9lhBoot) bootConfig |= (mode << 8) | (emuNAND << 9);
|
||||
|
||||
//If the boot configuration is different from previously, overwrite it
|
||||
if(bootConfig != (config & 0xFF00)){
|
||||
//Preserve user settings (first byte)
|
||||
u16 tempConfig = ((config & 0xFF) | bootConfig);
|
||||
fileWrite(&tempConfig, configPath, 2);
|
||||
if((tempConfig & 0xFFF000) != (config & 0xFFF000)){
|
||||
//Preserve user settings (first 12 bits)
|
||||
tempConfig |= config & 0xFFF;
|
||||
fileWrite(&tempConfig, configPath, 3);
|
||||
}
|
||||
}
|
||||
|
||||
//Determine which patched FIRM we need to write or attempt to use (if any)
|
||||
/* Determine which patched FIRM we need to write or attempt to use (if any).
|
||||
Patched 9.0 FIRM is only needed if "Use pre-patched FIRMs" is set */
|
||||
selectedFirm = mode ? (emuNAND ? (emuNAND == 1 ? 2 : 3) : 1) :
|
||||
(usePatchedFirmSet ? 4 : 0);
|
||||
(((config >> 1) & 0x1) ? 4 : 0);
|
||||
|
||||
//Determine if we need to use a pre-patched FIRM
|
||||
usePatchedFirm = (usePatchedFirmSet && fileExists(patchedFirms[selectedFirm - 1])) ? 1 : 0;
|
||||
//If "Use pre-patched FIRMs" is set and the appropriate FIRM exists, use it
|
||||
usePatchedFirm = (((config >> 1) & 0x1) && fileExists(patchedFirms[selectedFirm - 1])) ? 1 : 0;
|
||||
}
|
||||
|
||||
//Load FIRM into FCRAM
|
||||
@@ -182,7 +179,7 @@ static void loadEmu(u8 *proc9Offset){
|
||||
if(!emuHeader) error("No emuNAND has been detected");
|
||||
|
||||
//Copy emuNAND code
|
||||
void *emuCodeOffset = getEmuCode(arm9Section, section[2].size, proc9Offset);
|
||||
void *emuCodeOffset = getEmuCode(proc9Offset);
|
||||
memcpy(emuCodeOffset, emunand, emunand_size);
|
||||
|
||||
//Add the data of the found emuNAND
|
||||
@@ -266,6 +263,18 @@ void patchFirm(void){
|
||||
*(u16 *)sigOffset2 = sigPatch[0];
|
||||
*((u16 *)sigOffset2 + 1) = sigPatch[1];
|
||||
|
||||
//Replace the FIRM loader with the injector
|
||||
u32 loaderOffset,
|
||||
loaderSize;
|
||||
|
||||
getLoader((u8 *)firm + section[0].offset, section[0].size, &loaderOffset, &loaderSize);
|
||||
if(injector_size <= (int)loaderSize){
|
||||
memset((void *)loaderOffset, 0, loaderSize);
|
||||
memcpy((void *)loaderOffset, injector, injector_size);
|
||||
*((u32 *)loaderOffset + 0x41) = loaderSize / 0x200;
|
||||
*((u32 *)loaderOffset + 0x69) = loaderSize / 0x200 - 5;
|
||||
}
|
||||
|
||||
//Patch ARM9 entrypoint on N3DS to skip arm9loader
|
||||
if(console)
|
||||
firm->arm9Entry = (u8 *)0x801B01C;
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
* memory.c
|
||||
* by Reisyukaku / Aurora Wright
|
||||
* Copyright (c) 2016 All Rights Reserved
|
||||
*
|
||||
* Quick Search algorithm adapted from http://igm.univ-mlv.fr/~lecroq/string/node19.html#SECTION00190
|
||||
*/
|
||||
|
||||
#include "memory.h"
|
||||
@@ -35,8 +37,25 @@ int memcmp(const void *buf1, const void *buf2, u32 size){
|
||||
return 0;
|
||||
}
|
||||
|
||||
void *memsearch(void *start_pos, const void *search, u32 size, u32 size_search){
|
||||
for(u8 *pos = (u8 *)start_pos + size - size_search; pos >= (u8 *)start_pos; pos--)
|
||||
if(memcmp(pos, search, size_search) == 0) return pos;
|
||||
u8 *memsearch(u8 *startPos, const void *pattern, u32 size, u32 patternSize){
|
||||
const u8 *patternc = (const u8 *)pattern;
|
||||
|
||||
//Preprocessing
|
||||
int table[256];
|
||||
|
||||
for(u32 i = 0; i < 256; ++i)
|
||||
table[i] = patternSize + 1;
|
||||
for(u32 i = 0; i < patternSize; ++i)
|
||||
table[patternc[i]] = patternSize - i;
|
||||
|
||||
//Searching
|
||||
u32 j = 0;
|
||||
|
||||
while(j <= size - patternSize){
|
||||
if(memcmp(patternc, startPos + j, patternSize) == 0)
|
||||
return startPos + j;
|
||||
j += table[startPos[j + patternSize]];
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
@@ -2,6 +2,8 @@
|
||||
* memory.h
|
||||
* by Reisyukaku / Aurora Wright
|
||||
* Copyright (c) 2016 All Rights Reserved
|
||||
*
|
||||
* Quick Search algorithm adapted from http://igm.univ-mlv.fr/~lecroq/string/node19.html#SECTION00190
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
@@ -12,4 +14,4 @@ void memcpy(void *dest, const void *src, u32 size);
|
||||
void memset(void *dest, int filler, u32 size);
|
||||
void memset32(void *dest, u32 filler, u32 size);
|
||||
int memcmp(const void *buf1, const void *buf2, u32 size);
|
||||
void *memsearch(void *start_pos, const void *search, u32 size, u32 size_search);
|
||||
u8 *memsearch(u8 *startPos, const void *pattern, u32 size, u32 patternSize);
|
||||
@@ -23,24 +23,24 @@ const u16 writeBlock[2] = {0x2000, 0x46C0};
|
||||
* Functions
|
||||
**************************************************/
|
||||
|
||||
u8 *getProc9(void *pos, u32 size){
|
||||
return (u8 *)memsearch(pos, "ess9", size, 4);
|
||||
u8 *getProc9(u8 *pos, u32 size){
|
||||
return memsearch(pos, "ess9", size, 4);
|
||||
}
|
||||
|
||||
void getSigChecks(void *pos, u32 size, u32 *off, u32 *off2){
|
||||
void getSigChecks(u8 *pos, u32 size, u32 *off, u32 *off2){
|
||||
//Look for signature checks
|
||||
const u8 pattern[] = {0xC0, 0x1C, 0x76, 0xE7};
|
||||
const u8 pattern2[] = {0xB5, 0x22, 0x4D, 0x0C};
|
||||
const u8 pattern[] = {0xC0, 0x1C, 0x76, 0xE7},
|
||||
pattern2[] = {0xB5, 0x22, 0x4D, 0x0C};
|
||||
|
||||
*off = (u32)memsearch(pos, pattern, size, 4);
|
||||
*off2 = (u32)memsearch(pos, pattern2, size, 4) - 1;
|
||||
}
|
||||
|
||||
void *getReboot(void *pos, u32 size){
|
||||
void *getReboot(u8 *pos, u32 size){
|
||||
//Look for FIRM reboot code
|
||||
const u8 pattern[] = {0xDE, 0x1F, 0x8D, 0xE2};
|
||||
|
||||
return (u8 *)memsearch(pos, pattern, size, 4) - 0x10;
|
||||
return memsearch(pos, pattern, size, 4) - 0x10;
|
||||
}
|
||||
|
||||
u32 getfOpen(u8 *proc9Offset, void *rebootOffset){
|
||||
@@ -53,10 +53,17 @@ u32 getfOpen(u8 *proc9Offset, void *rebootOffset){
|
||||
return (u32)rebootOffset + 9 - (-((*(u32 *)rebootOffset & 0x00FFFFFF) << 2) & 0xFFFFF) - p9CodeOff + p9MemAddr;
|
||||
}
|
||||
|
||||
u16 *getFirmWrite(void *pos, u32 size){
|
||||
u16 *getFirmWrite(u8 *pos, u32 size){
|
||||
//Look for FIRM writing code
|
||||
u8 *const off = (u8 *)memsearch(pos, "exe:", size, 4);
|
||||
u8 *const off = memsearch(pos, "exe:", size, 4);
|
||||
const u8 pattern[] = {0x00, 0x28, 0x01, 0xDA};
|
||||
|
||||
return (u16 *)memsearch(off - 0x100, pattern, 0x100, 4);
|
||||
}
|
||||
|
||||
void getLoader(u8 *pos, u32 size, u32 *loaderOffset, u32 *loaderSize){
|
||||
u8 *const off = memsearch(pos, "loade", size, 5);
|
||||
|
||||
*loaderOffset = (u32)off - 0x200;
|
||||
*loaderSize = *(u32 *)(off - 0xFC) * 0x200;
|
||||
}
|
||||
@@ -19,8 +19,9 @@ const u16 writeBlock[2];
|
||||
/**************************************************
|
||||
* Functions
|
||||
**************************************************/
|
||||
u8 *getProc9(void *pos, u32 size);
|
||||
void getSigChecks(void *pos, u32 size, u32 *off, u32 *off2);
|
||||
void *getReboot(void *pos, u32 size);
|
||||
u8 *getProc9(u8 *pos, u32 size);
|
||||
void getSigChecks(u8 *pos, u32 size, u32 *off, u32 *off2);
|
||||
void *getReboot(u8 *pos, u32 size);
|
||||
u32 getfOpen(u8 *proc9Offset, void *rebootOffset);
|
||||
u16 *getFirmWrite(void *pos, u32 size);
|
||||
u16 *getFirmWrite(u8 *pos, u32 size);
|
||||
void getLoader(u8 *pos, u32 size, u32 *loaderOffset, u32 *loaderSize);
|
||||
@@ -11,19 +11,14 @@
|
||||
#include "i2c.h"
|
||||
#include "buttons.h"
|
||||
|
||||
//Number of options that can be configured
|
||||
#define OPTIONS 3
|
||||
|
||||
#define COLOR_TITLE 0xFF9900
|
||||
#define COLOR_WHITE 0xFFFFFF
|
||||
#define COLOR_RED 0x0000FF
|
||||
#define COLOR_BLACK 0x000000
|
||||
|
||||
struct options {
|
||||
char *text[OPTIONS];
|
||||
int pos_y[OPTIONS];
|
||||
u32 enabled[OPTIONS];
|
||||
u32 selected;
|
||||
struct option {
|
||||
int posY;
|
||||
u32 enabled;
|
||||
};
|
||||
|
||||
static u16 waitInput(void){
|
||||
@@ -48,52 +43,75 @@ static u16 waitInput(void){
|
||||
return key;
|
||||
}
|
||||
|
||||
void configureCFW(const char *configPath){
|
||||
struct options options;
|
||||
|
||||
options.text[0] = "( ) Updated SysNAND mode (A9LH-only)";
|
||||
options.text[1] = "( ) Use pre-patched FIRMs";
|
||||
options.text[2] = "( ) Force A9LH detection";
|
||||
|
||||
void configureCFW(const char *configPath, const char *firm90Path){
|
||||
initScreens();
|
||||
|
||||
drawString(CONFIG_TITLE, 10, 10, COLOR_TITLE);
|
||||
drawString("Press A to select, START to save and reboot", 10, 30, COLOR_WHITE);
|
||||
|
||||
const char *optionsText[] = { "( ) Updated SysNAND mode (A9LH-only)",
|
||||
"( ) Use pre-patched FIRMs",
|
||||
"( ) Force A9LH detection",
|
||||
"( ) Use 9.0 FIRM as default",
|
||||
"( ) Use second EmuNAND as default",
|
||||
"( ) Show current NAND in System Settings" };
|
||||
|
||||
u32 optionsAmount = sizeof(optionsText) / sizeof(char *);
|
||||
struct option options[optionsAmount];
|
||||
|
||||
//Read and parse the existing configuration
|
||||
u16 tempConfig = 0;
|
||||
fileRead(&tempConfig, configPath, 2);
|
||||
for(u32 i = 0; i < OPTIONS; i++)
|
||||
options.enabled[i] = (tempConfig >> i) & 0x1;
|
||||
u32 tempConfig = 0;
|
||||
fileRead(&tempConfig, configPath, 3);
|
||||
for(u32 i = 0; i < optionsAmount; i++)
|
||||
options[i].enabled = (tempConfig >> i) & 0x1;
|
||||
|
||||
//Pre-select the first configuration option
|
||||
options.selected = 0;
|
||||
u32 selectedOption = 0;
|
||||
|
||||
//Boring configuration menu
|
||||
while(1){
|
||||
u16 pressed = 0;
|
||||
|
||||
do{
|
||||
for(u32 i = 0; i < OPTIONS; i++){
|
||||
options.pos_y[i] = drawString(options.text[i], 10, !i ? 60 : options.pos_y[i - 1] + SPACING_VERT, options.selected == i ? COLOR_RED : COLOR_WHITE);
|
||||
drawCharacter('x', 10 + SPACING_HORIZ, options.pos_y[i], options.enabled[i] ? (options.selected == i ? COLOR_RED : COLOR_WHITE) : COLOR_BLACK);
|
||||
for(u32 i = 0; i < optionsAmount; i++){
|
||||
options[i].posY = drawString(optionsText[i], 10, !i ? 60 : options[i - 1].posY + SPACING_Y, selectedOption == i ? COLOR_RED : COLOR_WHITE);
|
||||
drawCharacter('x', 10 + SPACING_X, options[i].posY, options[i].enabled ? (selectedOption == i ? COLOR_RED : COLOR_WHITE) : COLOR_BLACK);
|
||||
}
|
||||
pressed = waitInput();
|
||||
} while(!(pressed & (BUTTON_UP | BUTTON_DOWN | BUTTON_A | BUTTON_START)));
|
||||
} while(!(pressed & MENU_BUTTONS));
|
||||
|
||||
if(pressed == BUTTON_UP) options.selected = !options.selected ? OPTIONS - 1 : options.selected - 1;
|
||||
else if(pressed == BUTTON_DOWN) options.selected = options.selected == OPTIONS - 1 ? 0 : options.selected + 1;
|
||||
else if(pressed == BUTTON_A) options.enabled[options.selected] = !options.enabled[options.selected];
|
||||
else if(pressed == BUTTON_START) break;
|
||||
switch(pressed){
|
||||
case BUTTON_UP:
|
||||
selectedOption = !selectedOption ? optionsAmount - 1 : selectedOption - 1;
|
||||
break;
|
||||
case BUTTON_DOWN:
|
||||
selectedOption = selectedOption == optionsAmount - 1 ? 0 : selectedOption + 1;
|
||||
break;
|
||||
case BUTTON_LEFT:
|
||||
selectedOption = 0;
|
||||
break;
|
||||
case BUTTON_RIGHT:
|
||||
selectedOption = optionsAmount - 1;
|
||||
break;
|
||||
case BUTTON_A:
|
||||
options[selectedOption].enabled = !options[selectedOption].enabled;
|
||||
break;
|
||||
}
|
||||
|
||||
if(pressed == BUTTON_START) break;
|
||||
}
|
||||
|
||||
//Preserve the last-used boot options (second byte)
|
||||
tempConfig &= 0xFF00;
|
||||
//If the user has been using A9LH and the "Updated SysNAND" setting changed, delete the patched 9.0 FIRM
|
||||
if(((tempConfig >> 16) & 0x1) && ((tempConfig & 0x1) != options[0].enabled))
|
||||
fileDelete(firm90Path);
|
||||
|
||||
//Preserve the last-used boot options (last 12 bits)
|
||||
tempConfig &= 0xFFF000;
|
||||
|
||||
//Parse and write the selected options
|
||||
for(u32 i = 0; i < OPTIONS; i++)
|
||||
tempConfig |= options.enabled[i] << i;
|
||||
fileWrite(&tempConfig, configPath, 2);
|
||||
for(u32 i = 0; i < optionsAmount; i++)
|
||||
tempConfig |= options[i].enabled << i;
|
||||
fileWrite(&tempConfig, configPath, 3);
|
||||
|
||||
//Reboot
|
||||
i2cWriteRegister(I2C_DEV_MCU, 0x20, 1 << 2);
|
||||
@@ -111,8 +129,8 @@ void error(const char *message){
|
||||
initScreens();
|
||||
|
||||
drawString("An error has occurred:", 10, 10, COLOR_RED);
|
||||
int pos_y = drawString(message, 10, 30, COLOR_WHITE);
|
||||
drawString("Press any button to shutdown", 10, pos_y + 2 * SPACING_VERT, COLOR_WHITE);
|
||||
int posY = drawString(message, 10, 30, COLOR_WHITE);
|
||||
drawString("Press any button to shutdown", 10, posY + 2 * SPACING_Y, COLOR_WHITE);
|
||||
|
||||
waitInput();
|
||||
|
||||
|
||||
@@ -8,6 +8,6 @@
|
||||
|
||||
#include "types.h"
|
||||
|
||||
void configureCFW(const char *configPath);
|
||||
void configureCFW(const char *configPath, const char *firm90Path);
|
||||
void deleteFirms(const char *firmPaths[], u32 firms);
|
||||
void error(const char *message);
|
||||
Reference in New Issue
Block a user