diff --git a/tm_msipl/payload/CMakeLists.txt b/tm_msipl/payload/CMakeLists.txt index d421619..5300bf6 100644 --- a/tm_msipl/payload/CMakeLists.txt +++ b/tm_msipl/payload/CMakeLists.txt @@ -1,6 +1,6 @@ add_library(payload_common STATIC) -target_sources(payload_common PRIVATE main.cpp) +target_sources(payload_common PRIVATE main.cpp config.cpp) target_link_libraries(payload_common PRIVATE tm_common::common iplsdk::crt iplsdk::ff_ro iplsdk::lowio iplsdk::cache iplsdk::ctrl iplsdk::ms iplsdk::syscon iplsdk::serial) diff --git a/tm_msipl/payload/config.cpp b/tm_msipl/payload/config.cpp new file mode 100644 index 0000000..e0aa617 --- /dev/null +++ b/tm_msipl/payload/config.cpp @@ -0,0 +1,175 @@ +#include +#include +#include +#include +#include + +static const u32 INVALID_BUTTON = 0xFFFFFFFF; + +static const u32 usefulButtonsMask = ~(PSP_CTRL_HOLD | PSP_CTRL_WLAN_UP | PSP_CTRL_REMOTE | PSP_CTRL_DISC | PSP_CTRL_MS); + +static bool validateConfigEntry(char *line, u32 line_len, char **combo, char **config) { + // format of valid config line is: + // BTN+BTN = "filename"; + int num_quotes = 0; + *combo = *config = NULL; + + for (u32 i = 0; i < line_len; ++i) { + // stop processing at a comment marker unless it's a part of the value + if (line[i] == '#' && num_quotes != 1) + break; + // = marks the border between key and value + if (line[i] == '=') { + *combo = line; + line[i] = '\0'; + // eat trailing spaces + for (s32 j = i-1; j > 0 && line[j] == ' '; --j) + line[j] = '\0'; + // eat leading spaces + while (**combo == ' ') { + *combo++; + } + // quotes surround our value + } else if (line[i] == '"') { + if (!*combo) // can only get a quote after the combo + break; + if (++num_quotes == 1) { + *config = line + i + 1; + } else { + // if we have more than one quote then we have our value + // and are just waiting on a semicolon for success + + // eat the quote + line[i] = '\0'; + } + // line must terminate with a semi-colon + } else if (line[i] == ';') { + // if we have our key, and a quote wrapped value then GO GO GO + if (*combo && *config && num_quotes > 1) + return true; + } + } + + return false; +} + +static u32 maskForButton(char *button) { + if(strcasecmp(button, "UP") == 0) { + return PSP_CTRL_UP; + } else if(strcasecmp(button, "RIGHT") == 0) { + return PSP_CTRL_RIGHT; + } else if(strcasecmp(button, "DOWN") == 0) { + return PSP_CTRL_DOWN; + } else if(strcasecmp(button, "LEFT") == 0) { + return PSP_CTRL_LEFT; + } else if(strcasecmp(button, "TRIANGLE") == 0) { + return PSP_CTRL_TRIANGLE; + } else if(strcasecmp(button, "CIRCLE") == 0) { + return PSP_CTRL_CIRCLE; + } else if(strcasecmp(button, "CROSS") == 0) { + return PSP_CTRL_CROSS; + } else if(strcasecmp(button, "SQUARE") == 0) { + return PSP_CTRL_SQUARE; + } else if(strcasecmp(button, "SELECT") == 0) { + return PSP_CTRL_SELECT; + } else if((strcasecmp(button, "LTRIGGER")) == 0 || (strcasecmp(button, "L") == 0)) { + return PSP_CTRL_LTRIGGER; + } else if((strcasecmp(button, "RTRIGGER")) == 0 || (strcasecmp(button, "R") == 0)) { + return PSP_CTRL_RTRIGGER; + } else if(strcasecmp(button, "START") == 0) { + return PSP_CTRL_START; + } else if(strcasecmp(button, "HOME") == 0) { + return PSP_CTRL_HOME; + } else if(strcasecmp(button, "WLAN") == 0) { + return PSP_CTRL_WLAN_UP; + } else if(strcasecmp(button, "VOLDOWN") == 0) { + return PSP_CTRL_VOLDOWN; + } else if(strcasecmp(button, "VOLUP") == 0) { + return PSP_CTRL_VOLUP; + } else if(strcasecmp(button, "HPREMOTE") == 0) { + return PSP_CTRL_REMOTE; + } else if(strcasecmp(button, "NOTE") == 0) { + return PSP_CTRL_NOTE; + } else if(strcasecmp(button, "LCD") == 0) { + return PSP_CTRL_SCREEN; + } else if(strcasecmp(button, "NOTHING") == 0) { + return 0; + } + + return INVALID_BUTTON; +} + +static u32 maskForButtonCombo(char *combo) { + char *ele_start = combo; + char *ele_next = ele_start; + u32 result = 0; + + if (!combo || strlen(combo) == 0) + return INVALID_BUTTON; + + while (*ele_next != '\0') { + if (*ele_next == '+') { + *ele_next = '\0'; + + // build up the mask for this button + result |= maskForButton(ele_start); + if (result == INVALID_BUTTON) + return result; + + // restore '+' so we could print the line later + *ele_next = '+'; + + // setup the next sub-string + ele_start = ++ele_next; + } else { + ele_next++; + } + } + + // ensure we've parsed everything + if (ele_start != ele_next) { + result |= maskForButton(ele_start); + } + + return result; +} + +static bool isMatchingConfigEntry(char *line, u32 lineLen, u32 buttons, char * const configOutput) { + char *combo; + char *config; + + if (validateConfigEntry(line, lineLen, &combo, &config)) { + u32 mask = maskForButtonCombo(combo); + + // check if the combo mask matches the useful subset of what's currently 'pressed' + if (mask == (usefulButtonsMask & buttons)) { + if constexpr (isDebug) { + printf("Config line: %s\n", line); + printf("Buttons: 0x%08x Selection: 0x%08x\n", usefulButtonsMask & buttons, mask); + } + strncpy(configOutput, config, TM_MAX_PATH_LENGTH); + return true; + } + } + + return false; +} + +int getMatchingConfigEntry(u32 buttons, char * const inputBuffer, u32 inputBufferSize, char * const configOutput) { + char *line_begin = inputBuffer; + char *line_end = NULL; + + // find a string + for (u32 i = 0; i < inputBufferSize; ++i) { + if (inputBuffer[i] == '\r' || inputBuffer[i] == '\n') { + line_end = inputBuffer + i; + if (line_end - line_begin > 0) { + if (isMatchingConfigEntry(line_begin, line_end - line_begin, buttons, configOutput)) + return 0; + } + line_begin = inputBuffer + i + 1; + } + } + + return 1; +} diff --git a/tm_msipl/payload/config.h b/tm_msipl/payload/config.h new file mode 100644 index 0000000..780b980 --- /dev/null +++ b/tm_msipl/payload/config.h @@ -0,0 +1,2 @@ +#pragma once +int getMatchingConfigEntry(u32 buttons, char * const inputBuffer, u32 inputBufferSize, char * const configOutput); diff --git a/tm_msipl/payload/main.cpp b/tm_msipl/payload/main.cpp index a19ef3d..4c2deee 100644 --- a/tm_msipl/payload/main.cpp +++ b/tm_msipl/payload/main.cpp @@ -10,19 +10,16 @@ #include #include +#include "config.h" #include "performBoot.h" constexpr inline u32 FILE_BUFFER_SIZE = 1024; -constexpr inline u32 LINE_BUFFER_SIZE = 256; -constexpr inline u32 BUTTON_BUFFER_SIZE = 128; constexpr inline u32 SECTOR_BUFFER_SIZE = 512; constexpr inline u32 BUFFER_START_ADDR = 0x40E0000; auto const g_FileBuffer = reinterpret_cast(BUFFER_START_ADDR); -auto const g_LineBuffer = reinterpret_cast(BUFFER_START_ADDR+FILE_BUFFER_SIZE); -auto const g_ButtonBuffer = reinterpret_cast(BUFFER_START_ADDR+FILE_BUFFER_SIZE+LINE_BUFFER_SIZE); -auto const g_ConfigBuffer = reinterpret_cast(BUFFER_START_ADDR+FILE_BUFFER_SIZE+LINE_BUFFER_SIZE+BUTTON_BUFFER_SIZE); +auto const g_ConfigBuffer = reinterpret_cast(BUFFER_START_ADDR+FILE_BUFFER_SIZE); FATFS fs; FIL fp; @@ -66,8 +63,6 @@ int main() { if constexpr (isDebug) { printf("g_FileBuffer %x\n", g_FileBuffer); - printf("g_LineBuffer %x\n", g_LineBuffer); - printf("g_ButtonBuffer %x\n", g_ButtonBuffer); printf("g_ConfigBuffer %x\n", g_ConfigBuffer); printf("resetValue %x\n", *resetPtr); // printf("g_PayloadLocation %x\n", g_PayloadLocation); @@ -147,159 +142,8 @@ int main() { u32 pos = 0; - while (pos < FILE_BUFFER_SIZE) { - //We have reached the end of the file, end the processing - if (g_FileBuffer[pos] == 0) { - break; - } - - //Read current line - u32 line_length = 0; - s32 comment_at = -1; - while (pos+line_length < FILE_BUFFER_SIZE) { - // printf("%c\n", g_FileBuffer[pos+line_length]); - //UNIX newline character combo - if (g_FileBuffer[pos+line_length] == 0xA) { - pos += 1; - break; - } - //Windows newline character combo - if (g_FileBuffer[pos+line_length] == 0xD && g_FileBuffer[pos+line_length+1] == 0xA) { - pos += 2; - break; - } - //Save the position of first encountered comment character '#', continue consuming characters - //(but the parser will ignore everyting after) - if (g_FileBuffer[pos+line_length] == '#' && comment_at < 0) { - comment_at = line_length; - } - - g_LineBuffer[line_length] = g_FileBuffer[pos+line_length]; - line_length++; - } - //Advance the file pointer, whole line was read - pos += line_length; - - //Comment was placed at the current line, trim the parsed line length to '#' character position - if (comment_at >= 0) { - line_length = comment_at; - } - - //If the line is empty, try the next line - if (line_length == 0) { - continue; - } - - //Proper line should end with ';', otherwise try the next line - if (g_LineBuffer[line_length-1] != ';') { - continue; - } - - //Find '=' token to later parse IPL path regardless if button parsing is interrupted by an error or not - u32 equals_pos; - for (equals_pos = 0; equals_pos < line_length; equals_pos++) { - if (g_LineBuffer[equals_pos] == '=') { - break; - } - } - - //No '=' found, try the next line - if (equals_pos == line_length) { - continue; - } - - g_LineBuffer[line_length] = '\0'; - - //Parse buttons - u32 selection = 0; - u32 button_length = 0, line_pos = 0; - while (line_pos+button_length < line_length) { - if (g_LineBuffer[line_pos+button_length] == ' ' || g_LineBuffer[line_pos+button_length] == '+') { - //Whole button name was read. NULL-terminate it and check if it matches any of the valid buttons - g_ButtonBuffer[button_length] = '\0'; - - if(strcasecmp(g_ButtonBuffer, "UP") == 0) { - selection |= PSP_CTRL_UP; - } else if(strcasecmp(g_ButtonBuffer, "RIGHT") == 0) { - selection |= PSP_CTRL_RIGHT; - } else if(strcasecmp(g_ButtonBuffer, "DOWN") == 0) { - selection |= PSP_CTRL_DOWN; - } else if(strcasecmp(g_ButtonBuffer, "LEFT") == 0) { - selection |= PSP_CTRL_LEFT; - } else if(strcasecmp(g_ButtonBuffer, "TRIANGLE") == 0) { - selection |= PSP_CTRL_TRIANGLE; - } else if(strcasecmp(g_ButtonBuffer, "CIRCLE") == 0) { - selection |= PSP_CTRL_CIRCLE; - } else if(strcasecmp(g_ButtonBuffer, "CROSS") == 0) { - selection |= PSP_CTRL_CROSS; - } else if(strcasecmp(g_ButtonBuffer, "SQUARE") == 0) { - selection |= PSP_CTRL_SQUARE; - } else if(strcasecmp(g_ButtonBuffer, "SELECT") == 0) { - selection |= PSP_CTRL_SELECT; - } else if((strcasecmp(g_ButtonBuffer, "LTRIGGER")) == 0 || (strcasecmp(g_ButtonBuffer, "L") == 0)) { - selection |= PSP_CTRL_LTRIGGER; - } else if((strcasecmp(g_ButtonBuffer, "RTRIGGER")) == 0 || (strcasecmp(g_ButtonBuffer, "R") == 0)) { - selection |= PSP_CTRL_RTRIGGER; - } else if(strcasecmp(g_ButtonBuffer, "START") == 0) { - selection |= PSP_CTRL_START; - } else if(strcasecmp(g_ButtonBuffer, "HOME") == 0) { - selection |= PSP_CTRL_HOME; - } else if(strcasecmp(g_ButtonBuffer, "WLAN") == 0) { - selection |= PSP_CTRL_WLAN_UP; - } else if(strcasecmp(g_ButtonBuffer, "VOLDOWN") == 0) { - selection |= PSP_CTRL_VOLDOWN; - } else if(strcasecmp(g_ButtonBuffer, "VOLUP") == 0) { - selection |= PSP_CTRL_VOLUP; - } else if(strcasecmp(g_ButtonBuffer, "HPREMOTE") == 0) { - selection |= PSP_CTRL_REMOTE; - } else if(strcasecmp(g_ButtonBuffer, "NOTE") == 0) { - selection |= PSP_CTRL_NOTE; - } else if(strcasecmp(g_ButtonBuffer, "LCD") == 0) { - selection |= PSP_CTRL_SCREEN; - } else if(strcasecmp(g_ButtonBuffer, "NOTHING") == 0) { - selection = 0; - } else { - //Invalid button name. Stop parsing buttons and continue to parse IPL path - break; - } - - //If the next character is '+', skip it and continue the button combo - if(g_LineBuffer[line_pos+button_length] == '+') { - line_pos += button_length+1; - button_length = 0; - } else { - //Otherwise we are finished, continue to parse IPL path - break; - } - } else { - g_ButtonBuffer[button_length] = g_LineBuffer[line_pos+button_length]; - button_length++; - } - } - - //Start after the '=' character and look for '"' - for (line_pos = equals_pos+1; line_pos < line_length; ++line_pos) { - if (g_LineBuffer[line_pos] == '"') { - break; - } - } - //No '"' found, try the next line - if (line_pos == line_length) { - continue; - } - - //Look for the other '"' - u32 endpos; - for (endpos = line_pos+1; endpos < line_length; ++endpos) { - if (g_LineBuffer[endpos] == '"') { - break; - } - } - //No second '"' found, try the next line - if (endpos == line_length) { - continue; - } - + if (0 == getMatchingConfigEntry(pad.Buttons, g_FileBuffer, bytes_read, g_ConfigBuffer)) { +/* if constexpr (isDebug) { for(u32 i = 0; i < line_length; i++) { printf("%c", g_LineBuffer[i]); @@ -308,46 +152,37 @@ int main() { printf("Buttons %x selection %x\n", pad.Buttons, selection); printf("Buttons & selection %x\n", pad.Buttons & selection); } +*/ + if constexpr (isDebug) { + printf("Buttons valid for: %s\n", g_ConfigBuffer); + } - //Do we have a match between the config line and pressed buttons? - if ((pad.Buttons & selection) == selection) { - //Copy the IPL as (potential) boot target - u32 const size = endpos - (line_pos+1); - strncpy(g_ConfigBuffer, &g_LineBuffer[line_pos+1], size > TM_MAX_PATH_LENGTH ? TM_MAX_PATH_LENGTH : size); - g_ConfigBuffer[size] = '\0'; - + if (strcasecmp(g_ConfigBuffer, "NAND") == 0) { //Chosen IPL is NAND if constexpr (isDebug) { - printf("Buttons valid for: %s\n", g_ConfigBuffer); + printf("Cold booting NAND\n"); } - if (strcasecmp(g_ConfigBuffer, "NAND") == 0) { //Chosen IPL is NAND - if constexpr (isDebug) { - printf("Cold booting NAND\n"); - } - - bootFromNand(g_ConfigBuffer); - } else if (f_open(&fp, g_ConfigBuffer, FA_OPEN_EXISTING | FA_READ) == FR_OK) { //Chosen IPL is from config, try to load it - sdkMsWriteSector(TM_CONFIG_SECTOR, g_ConfigBuffer); //Save current IPL path to config - - if constexpr (isDebug) { - printf("MS booting %s\n", g_ConfigBuffer); - } - - performMsBoot(fp); - } + bootFromNand(g_ConfigBuffer); + } else if (f_open(&fp, g_ConfigBuffer, FA_OPEN_EXISTING | FA_READ) == FR_OK) { // Chosen IPL is from config, try to load it + sdkMsWriteSector(TM_CONFIG_SECTOR, g_ConfigBuffer); // Save current IPL path to config if constexpr (isDebug) { - printf("Failed to load IPL from config\n"); + printf("MS booting %s\n", g_ConfigBuffer); } - //Failed to load the specified IPL. Continue to the next line + performMsBoot(fp); } + + if constexpr (isDebug) { + printf("Failed to load IPL from config\n"); + } + } if constexpr (isDebug) { printf("Config ended, defaulting to NAND\n"); } - //If the whole config was parsed and nothing was loaded, try to boot from NAND + // If the whole config was parsed and nothing was loaded, try to boot from NAND bootFromNand(g_ConfigBuffer); }