diff --git a/include/arm9/menu.h b/include/arm9/menu.h index 2809231..ab93234 100644 --- a/include/arm9/menu.h +++ b/include/arm9/menu.h @@ -3,9 +3,10 @@ enum menu_state_type { MENU_STATE_MAIN = 0, MENU_STATE_NAND_MENU, + MENU_STATE_OPTIONS_MENU, + MENU_STATE_OPTIONS_MODE, MENU_STATE_NAND_BACKUP, MENU_STATE_NAND_RESTORE, - MENU_STATE_OPTIONS_MENU, MENU_STATE_FIRM_LAUNCH, MENU_STATE_BROWSER, MENU_STATE_TEST_CONFIG, diff --git a/source/arm9/config.c b/source/arm9/config.c new file mode 100644 index 0000000..0aa5324 --- /dev/null +++ b/source/arm9/config.c @@ -0,0 +1,474 @@ + +/* This is a parser for the loadercfg.txt file. It's not vulnerable, I swear. */ + +#include +#include +#include +#include +#include "types.h" +#include "mem_map.h" +#include "fatfs/ff.h" +#include "arm9/console.h" +#include "arm9/main.h" +#include "arm9/interrupt.h" +#include "util.h" +#include "hid.h" +#include "arm9/config.h" + +#define MAX_FILE_SIZE 0x4000 - 1 + +static const char *filepath = "sdmc:\\loadercfg.txt"; + +static FIL file; + +typedef struct { + char *textData; // ptr to the first char of an attribute's data inside filebuf + u32 textLength; // length of the above data, without '\0' ofc + void *data; // used to store the final native data +} AttributeEntryType; + +typedef struct { + // function to convert the textData to arbitrary native data + bool (*parse)(AttributeEntryType *attr); + // opposite of parse(), used to update/create an entry in the file + bool (*write)(AttributeEntryType *attr, void *newData, int key); +} FunctionsEntryType; + + +static bool parseConfigFile(); +static bool parseBootOption(AttributeEntryType *attr); +static bool parseBootOptionPad(AttributeEntryType *attr); +static bool parseBootMode(AttributeEntryType *attr); +static bool writeBootMode(AttributeEntryType *attr, void *newData, int key); + +static const char *keyStrings[] = { + "BOOT_OPTION1", + "BOOT_OPTION2", + "BOOT_OPTION3", + "BOOT_OPTION1_NAND_IMAGE", + "BOOT_OPTION2_NAND_IMAGE", + "BOOT_OPTION3_NAND_IMAGE", + "BOOT_OPTION1_BUTTONS", + "BOOT_OPTION2_BUTTONS", + "BOOT_OPTION3_BUTTONS", + "BOOT_MODE" +}; + +static FunctionsEntryType keyFunctions[] = { + { parseBootOption, NULL }, + { parseBootOption, NULL }, + { parseBootOption, NULL }, + { parseBootOption, NULL }, + { parseBootOption, NULL }, + { parseBootOption, NULL }, + { parseBootOptionPad, NULL }, + { parseBootOptionPad, NULL }, + { parseBootOptionPad, NULL }, + { parseBootMode, NULL } +}; + +static AttributeEntryType attributes[numKeys]; + +static char *filebuf = NULL; + +/* This loads the config file from SD card and parses it */ +bool loadConfigFile() +{ + FILINFO fileStat; + u32 fileSize; + unsigned bytesRead; + + if(f_stat(filepath, &fileStat) != FR_OK) + { + printf("Failed to get config-file status!\n"); + goto fail; + } + + fileSize = fileStat.fsize; + + if(fileSize > MAX_FILE_SIZE || fileSize == 0) + { + printf("Invalid config-file size!\n"); + goto fail; + } + + filebuf = (char *) malloc(MAX_FILE_SIZE); + + if(!filebuf) + { + printf("Out of memory!\n"); + goto fail; + } + + if(f_open(&file, filepath, FA_READ) != FR_OK) + { + printf("Failed to open config-file for reading!\n"); + goto fail; + } + + if(f_read(&file, filebuf, fileSize, &bytesRead) != FR_OK || bytesRead != fileSize) + { + printf("Failed to read from config-file!\n"); + f_close(&file); + goto fail; + } + + // terminate string buf + filebuf[fileSize] = '\0'; + + + return parseConfigFile(); + +fail: + + return false; +} + +/* returns the start of an attribute's data. */ +static char *findDefinition(const char *attrName) +{ + char *start; + char c; + + start = strstr(filebuf, attrName); + + if(start) + { + /* verify definition char */ + start = start + strlen(attrName); + + while(*start == ' ') start++; + + c = *start; + if(c != '=') + start = NULL; + else + { + start ++; + while(*start == ' ') start++; + } + } + + return start; +} + +/* returns the length of an attribute's data. */ +static u32 parseDefinition(char *attrData) +{ + static const char LF = 0x0A, CR = 0x0D, NEL = 0x15; + u32 len = 0; + char *cur = attrData; + char c; + + if(!attrData) + return 0; + + // look for linebreaks that mark the end of a definition + for(; (c = *cur) != '\0'; cur++) + { + if(c == LF || c == CR || c == NEL) + break; + len ++; + c = *cur; + } + + return len; +} + +static bool parseConfigFile() +{ + AttributeEntryType *curAttr; + u32 len; + char *text; + + // pass #1: look for definitions and populate our lookup table. + for(u32 i=0; itextData = text; + if(!text) + { + curAttr->textLength = 0; + continue; + } + len = parseDefinition(text); + curAttr->textLength = len; + } + + // pass #2: parse text-data and convert it into a native form + for(u32 i=0; itextData) + { + curAttr->data = NULL; + continue; + } + else + { // TODO: what to do if ret == false ? + //printf("Parsing attr text with size %i\n%s\n", curAttr->textLength, curAttr->textData); + keyFunctions[i].parse(curAttr); + } + } + + return true; +} + +// Add a new definition in our file +static char *writeAddDefinitionText(const char *keyName, const char *textData) +{ + const char *def = " = "; + u32 curLen = strlen(filebuf); + u32 remainingLen = MAX_FILE_SIZE - curLen - 1; + u32 totalLen = strlen(textData); + u32 defLen = strlen(def); + totalLen += strlen(keyName); + totalLen += defLen; + totalLen += 2; // new line encoding + + if(totalLen > remainingLen) + return NULL; + + filebuf[curLen++] = 0x0d; + filebuf[curLen++] = 0x0a; + sprintf(&filebuf[curLen], "%s%s%s", keyName, def, textData); + + return &filebuf[curLen + defLen]; +} + +static u32 writeUpdateDefinitionText(char *textData, u32 curTextLen, const char *newText) +{ + u32 diff; + u32 newLen = strlen(newText); + u32 curLen = strlen(filebuf); + u32 remainingLen = MAX_FILE_SIZE - curLen - 1; + + if(newLen > remainingLen) + return 0; + + diff = newLen - curTextLen; + + if(diff) + { + if(curTextLen < newLen) + memcpy(textData + newLen - curTextLen, textData + curTextLen, + curLen - (filebuf - textData + newLen - curTextLen)); + else + memcpy(textData + newLen, textData + curTextLen, + curLen - (filebuf - textData + curTextLen)); + + for(u32 key=0; key textData) + { + attributes[key].textData += diff; + } + } + } + + memcpy(textData, newText, newLen); + + return newLen; +} + +static bool isValidPath(char *path) +{ + char c; + bool gotMountpoint = false; + + for(; (c = *path) != '\0'; path++) + { + // no multiple mountpoints + if(c == ':') + { + if(gotMountpoint) + return false; + else + gotMountpoint = true; + } + + // no dir-return + if(c == '.' && path[1] == '.') + return false; + + // no space after dir slash + if((c == '\\' || c == '/') && path[1] == ' ') + return false; + + // no non ASCII chars + if(!isascii(c)) + return false; + } + + if(!gotMountpoint) + return false; + + return true; +} + +static bool parseBootOption(AttributeEntryType *attr) +{ + attr->data = NULL; + + char *buf = (char *) malloc(attr->textLength + 1); + if(!buf) return false; + + memcpy(buf, attr->textData, attr->textLength); + buf[attr->textLength] = '\0'; + + if(!isValidPath(buf)) + { + free(buf); + return false; + } + + attr->data = buf; + + return true; +} + +static bool parseBootOptionPad(AttributeEntryType *attr) +{ + char c; + char *textData = attr->textData; + u32 padValue = 0; + u32 i; + + static const char * convTable[] = { + "A", "B", "SELECT", "START", "RIGHT", "LEFT", + "UP", "DOWN", "R", "L", "X", "Y" + }; + + for(; (c = *textData) != '\0' && textData < attr->textData + attr->textLength;) + { + for(i=0; idata = (u32 *) malloc(sizeof(u32)); + if(!attr->data) + return false; + + *(u32 *)attr->data = padValue; + return true; +} + +static const char * modeTable[] = { + "Normal", "Quick", "Quiet" +}; + +static bool parseBootMode(AttributeEntryType *attr) +{ + char *textData = attr->textData; + u32 i; + + for(i=0; i<3; i++) + { + if(strnicmp(textData, modeTable[i], strlen(modeTable[i])) == 0) + break; + } + + if(i >= 3) + { + attr->data = NULL; + return false; + } + + attr->data = (u32 *) malloc(sizeof(u32)); + if(!attr->data) + return false; + + *(u32 *)attr->data = i; + return true; +} + +static bool writeBootMode(AttributeEntryType *attr, void *newData, int key) +{ + u32 mode; + + if(!newData) + return false; + + mode = *(u32 *)newData; + + if(mode > BootModeQuiet) + return false; + + if(!attr->data) + attr->data = (u32 *) malloc(sizeof(u32)); + if(!attr->data) + return false; + + *(u32 *)attr->data = mode; + + const char *data = modeTable[mode]; + + if(!attr->textData) + { + attr->textData = writeAddDefinitionText(configGetKeyText(key), data); + attr->textLength = strlen(attr->textData); + } + else + { + attr->textLength = writeUpdateDefinitionText(attr->textData, attr->textLength, data); + } + + return true; +} + +void *configCopyText(int key) +{ + if(key < 0 || key >= KLast) + return NULL; + + if(!attributes[key].textData) + return NULL; + + char *buf = (char *) malloc(attributes[key].textLength + 1); + if(!buf) + return NULL; + + memcpy(buf, attributes[key].textData, attributes[key].textLength); + buf[attributes[key].textLength] = '\0'; + + return buf; +} + +const void *configGetData(int key) +{ + if(key < 0 || key >= KLast) + return NULL; + + return attributes[key].data; +} + +const char *configGetKeyText(int key) +{ + if(key < 0 || key >= KLast) + return NULL; + + return keyStrings[key]; +} + +void configSetKeyData(int key, void *data) +{ + AttributeEntryType *attr; + + if(key < 0 || key >= KLast) + return NULL; + + attr = &attributes[key]; + + keyFunctions[key].write(attr, data, key); +} + diff --git a/source/arm9/config_parser.c b/source/arm9/config_parser.c deleted file mode 100644 index ab4bb99..0000000 --- a/source/arm9/config_parser.c +++ /dev/null @@ -1,371 +0,0 @@ - -/* This is a parser for the loadercfg.txt file. It's not vulnerable, I swear. */ - -#include -#include -#include -#include -#include "types.h" -#include "mem_map.h" -#include "fatfs/ff.h" -#include "arm9/console.h" -#include "arm9/main.h" -#include "arm9/interrupt.h" -#include "util.h" -#include "hid.h" -#include "arm9/config.h" - -#define MAX_FILE_SIZE 0x4000 - 1 - -static const char *filepath = "sdmc:\\loadercfg.txt"; - -static FIL file; - -typedef struct { - char *textData; // ptr to the first char of an attribute's data inside filebuf - u32 textLength; // length of the above data, without '\0' ofc - void *data; // used to store the final native data -} AttributeEntryType; - -typedef struct { - // function to convert the textData to arbitrary native data - bool (*parse)(AttributeEntryType *attr); - // opposite of parse(), used to update/create an entry in the file - bool (*write)(AttributeEntryType *attr); -} FunctionsEntryType; - - -static bool parseConfigFile(); -static bool parseBootOption(AttributeEntryType *attr); -static bool parseBootOptionPad(AttributeEntryType *attr); -static bool parseBootMode(AttributeEntryType *attr); - -static const char *keyStrings[] = { - "BOOT_OPTION1", - "BOOT_OPTION2", - "BOOT_OPTION3", - "BOOT_OPTION1_NAND_IMAGE", - "BOOT_OPTION2_NAND_IMAGE", - "BOOT_OPTION3_NAND_IMAGE", - "BOOT_OPTION1_BUTTONS", - "BOOT_OPTION2_BUTTONS", - "BOOT_OPTION3_BUTTONS", - "BOOT_MODE" -}; - -static FunctionsEntryType keyFunctions[] = { - { parseBootOption, NULL }, - { parseBootOption, NULL }, - { parseBootOption, NULL }, - { parseBootOption, NULL }, - { parseBootOption, NULL }, - { parseBootOption, NULL }, - { parseBootOptionPad, NULL }, - { parseBootOptionPad, NULL }, - { parseBootOptionPad, NULL }, - { parseBootMode, NULL } -}; - -static AttributeEntryType attributes[numKeys]; - -static char *filebuf = NULL; - -/* This loads the config file from SD card and parses it */ -bool loadConfigFile() -{ - FILINFO fileStat; - u32 fileSize; - unsigned bytesRead; - - if(f_stat(filepath, &fileStat) != FR_OK) - { - printf("Failed to get config-file status!\n"); - goto fail; - } - - fileSize = fileStat.fsize; - - if(fileSize > MAX_FILE_SIZE || fileSize == 0) - { - printf("Invalid config-file size!\n"); - goto fail; - } - - filebuf = (char *) malloc(fileSize + 1); - - if(!filebuf) - { - printf("Out of memory!\n"); - goto fail; - } - - if(f_open(&file, filepath, FA_READ) != FR_OK) - { - printf("Failed to open config-file for reading!\n"); - goto fail; - } - - if(f_read(&file, filebuf, fileSize, &bytesRead) != FR_OK || bytesRead != fileSize) - { - printf("Failed to read from config-file!\n"); - f_close(&file); - goto fail; - } - - // terminate string buf - filebuf[fileSize] = '\0'; - - - return parseConfigFile(); - -fail: - - return false; -} - -/* returns the start of an attribute's data. */ -static char *findDefinition(const char *attrName) -{ - char *start; - char c; - - start = strstr(filebuf, attrName); - - if(start) - { - /* verify definition char */ - start = start + strlen(attrName); - - while(*start == ' ') start++; - - c = *start; - if(c != '=') - start = NULL; - else - { - start ++; - while(*start == ' ') start++; - } - } - - return start; -} - -/* returns the length of an attribute's data. */ -static u32 parseDefinition(char *attrData) -{ - static const char LF = 0x0A, CR = 0x0D, NEL = 0x15; - u32 len = 0; - char *cur = attrData; - char c; - - if(!attrData) - return 0; - - // look for linebreaks that mark the end of a definition - for(; (c = *cur) != '\0'; cur++) - { - if(c == LF || c == CR || c == NEL) - break; - len ++; - c = *cur; - } - - return len; -} - -static bool parseConfigFile() -{ - AttributeEntryType *curAttr; - u32 len; - char *text; - - // pass #1: look for definitions and populate our lookup table. - for(u32 i=0; itextData = text; - if(!text) - { - curAttr->textLength = 0; - continue; - } - len = parseDefinition(text); - curAttr->textLength = len; - } - - // pass #2: parse text-data and convert it into a native form - for(u32 i=0; itextData) - { - curAttr->data = NULL; - continue; - } - else - { // TODO: what to do if ret == false ? - //printf("Parsing attr text with size %i\n%s\n", curAttr->textLength, curAttr->textData); - keyFunctions[i].parse(curAttr); - } - } - - return true; -} - -static bool isValidPath(char *path) -{ - char c; - bool gotMountpoint = false; - - for(; (c = *path) != '\0'; path++) - { - // no multiple mountpoints - if(c == ':') - { - if(gotMountpoint) - return false; - else - gotMountpoint = true; - } - - // no dir-return - if(c == '.' && path[1] == '.') - return false; - - // no space after dir slash - if((c == '\\' || c == '/') && path[1] == ' ') - return false; - - // no non ASCII chars - if(!isascii(c)) - return false; - } - - if(!gotMountpoint) - return false; - - return true; -} - -static bool parseBootOption(AttributeEntryType *attr) -{ - attr->data = NULL; - - char *buf = (char *) malloc(attr->textLength + 1); - if(!buf) return false; - - memcpy(buf, attr->textData, attr->textLength); - buf[attr->textLength] = '\0'; - - if(!isValidPath(buf)) - { - free(buf); - return false; - } - - attr->data = buf; - - return true; -} - -static bool parseBootOptionPad(AttributeEntryType *attr) -{ - char c; - char *textData = attr->textData; - u32 padValue = 0; - u32 i; - - static const char * convTable[] = { - "A", "B", "SELECT", "START", "RIGHT", "LEFT", - "UP", "DOWN", "R", "L", "X", "Y" - }; - - for(; (c = *textData) != '\0' && textData < attr->textData + attr->textLength;) - { - for(i=0; idata = (u32 *) malloc(sizeof(u32)); - if(!attr->data) - return false; - - *(u32 *)attr->data = padValue; - return true; -} - -static bool parseBootMode(AttributeEntryType *attr) -{ - char *textData = attr->textData; - u32 i; - - static const char * table[] = { - "Normal", "Quick", "Quiet" - }; - - for(i=0; i<3; i++) - { - if(strnicmp(textData, table[i], strlen(table[i])) == 0) - break; - } - - if(i >= 3) - { - attr->data = NULL; - return false; - } - - attr->data = (u32 *) malloc(sizeof(u32)); - if(!attr->data) - return false; - - *(u32 *)attr->data = i; - return true; -} - -void *configCopyText(int key) -{ - if(key < 0 || key >= KLast) - return NULL; - - if(!attributes[key].textData) - return NULL; - - char *buf = (char *) malloc(attributes[key].textLength + 1); - if(!buf) - return NULL; - - memcpy(buf, attributes[key].textData, attributes[key].textLength); - buf[attributes[key].textLength] = '\0'; - - return buf; -} - -const void *configGetData(int key) -{ - if(key < 0 || key >= KLast) - return NULL; - - return attributes[key].data; -} - -const char *configGetKeyText(int key) -{ - if(key < 0 || key >= KLast) - return NULL; - - return keyStrings[key]; -} - - diff --git a/source/arm9/menu.c b/source/arm9/menu.c index 6b7e1c6..0fd984b 100644 --- a/source/arm9/menu.c +++ b/source/arm9/menu.c @@ -41,10 +41,18 @@ } }; +const menu_state_options menu_options = { + 1, + { + {"Change Boot Mode...", MENU_STATE_OPTIONS_MODE} + } +}; + // menu_state_type -> menu_state_options instance const menu_state_options *options_lookup[] = { &menu_main, // STATE_MAIN - &menu_nand // STATE_NAND_MENU + &menu_nand, // STATE_NAND_MENU + &menu_options // MENU_STATE_OPTIONS_MENU }; enum menu_state_type menu_state; @@ -88,6 +96,8 @@ { u32 keys; int cursor_pos; + const char* path; + const char *firmPath = NULL; menu_event_state = MENU_EVENT_NONE; @@ -118,9 +128,9 @@ switch(menu_state) { - case MENU_STATE_MAIN: case MENU_STATE_NAND_MENU: + case MENU_STATE_OPTIONS_MENU: menu_main_draw_top(); @@ -152,36 +162,27 @@ break; case MENU_STATE_NAND_BACKUP: - consoleSelect(&con_top); - consoleClear(); - dumpNand("sdmc:/nand.bin"); - menuSetReturnToState(STATE_PREVIOUS); + menuDumpNand("sdmc:/nand.bin"); + clearConsoles(); break; case MENU_STATE_NAND_RESTORE: - consoleSelect(&con_top); - consoleClear(); - restoreNand("sdmc:/nand.bin"); - menuSetReturnToState(STATE_PREVIOUS); + menuRestoreNand("sdmc:/nand.bin"); + clearConsoles(); break; case MENU_STATE_FIRM_LAUNCH: - consoleClear(); - consoleSelect(&con_top); - consoleClear(); - printf("OOPS!\n"); - for(;;); + if(!menuLaunchFirm(firmPath)) + clearConsoles(); break; case MENU_STATE_BROWSER: - consoleClear(); - consoleSelect(&con_top); - consoleClear(); - const char* path = browseForFile("sdmc:"); - consoleClear(); - printf("selected file:\n%s\n", path); - if(!menuLaunchFirm(path)) - clearConsoles(); + path = browseForFile("sdmc:"); + clearConsoles(); + firmPath = path; + if(!path) // no file selected + break; + menuSetEnterNextState(MENU_STATE_FIRM_LAUNCH); break; case MENU_STATE_TEST_CONFIG: @@ -358,7 +359,6 @@ menuSetReturnToState(MENU_STATE_MAIN); break; case MENU_EVENT_STATE_CHANGE: - clearConsoles(); menu_state = menu_next_state; break; case MENU_EVENT_SD_CARD_INSERTED: diff --git a/source/arm9/menu_filebrowse.c b/source/arm9/menu_filebrowse.c index a8eca5a..79d0d97 100644 --- a/source/arm9/menu_filebrowse.c +++ b/source/arm9/menu_filebrowse.c @@ -179,6 +179,8 @@ curEntriesCount = 0; indexStackPtr = indexStack; + clearConsoles(); + dirEntries = (EntryType *) malloc(MAX_CACHED_ENTRIES * sizeof(EntryType)); if(!dirEntries) { @@ -323,7 +325,8 @@ free(dirEntries); menuActState(); - + menuSetReturnToState(STATE_PREVIOUS); + return curPath; fail: @@ -332,6 +335,7 @@ free(dirEntries); menuActState(); - + menuSetReturnToState(STATE_PREVIOUS); + return NULL; } diff --git a/source/arm9/menu_firmloader.c b/source/arm9/menu_firmloader.c index 49ded80..a894da9 100644 --- a/source/arm9/menu_firmloader.c +++ b/source/arm9/menu_firmloader.c @@ -19,6 +19,9 @@ { u32 keys; + if(!filePath) + return false; + consoleSelect(&con_bottom); consoleClear(); consoleSelect(&con_top); @@ -31,6 +34,14 @@ if(!tryLoadFirmware(filePath)) { printf("\x1B[31mBad firmware.\e[0m\n"); + printf("Press B to return.\n"); + + do { + hidScanInput(); + keys = hidKeysDown(); + } + while(!(keys & KEY_B)); + goto fail; } diff --git a/source/arm9/menu_nand.c b/source/arm9/menu_nand.c index b39e0bf..f46e121 100644 --- a/source/arm9/menu_nand.c +++ b/source/arm9/menu_nand.c @@ -24,7 +24,7 @@ return ((u64)(freeClusters * fs->csize)) * 512; } -bool dumpNand(const char *filePath) +bool menuDumpNand(const char *filePath) { const u32 sectorCount = dev_rawnand->get_sector_count(); const u32 sectorBlkSize = 0x400; // 512 KB in sectors @@ -32,6 +32,7 @@ FRESULT fres; UINT bytesWritten; + clearConsoles(); consoleSelect(&con_bottom); printf("\n\t\tPress B to cancel"); @@ -109,20 +110,28 @@ f_close(&file); free(buf); + + menuSetReturnToState(STATE_PREVIOUS); + return true; fail: free(buf); wait(0x8000000); + + menuSetReturnToState(STATE_PREVIOUS); + return false; } -bool restoreNand(const char *filePath) +bool menuRestoreNand(const char *filePath) { const u32 sectorBlkSize = 0x400; // 512 KB in sectors FIL file; UINT bytesRead; + clearConsoles(); + consoleSelect(&con_bottom); printf("\n\t\tPress B to cancel"); consoleSelect(&con_top); @@ -203,11 +212,17 @@ f_close(&file); free(buf); remount_nand_fs(); + + menuSetReturnToState(STATE_PREVIOUS); + return true; fail: free(buf); remount_nand_fs(); wait(0x8000000); + + menuSetReturnToState(STATE_PREVIOUS); + return false; }