Implement chainloader selector menu (can be called with START, the START payload can now be booted with L+START

This commit is contained in:
Aurora 2016-11-13 18:10:59 +01:00
parent bc167dde2d
commit 2e1b943805
4 changed files with 156 additions and 67 deletions

View File

@ -226,7 +226,9 @@ void configMenu(bool isSdMode, bool oldPinStatus, u32 oldPinMode)
u32 multiOptionsAmount = sizeof(multiOptions) / sizeof(struct multiOption),
singleOptionsAmount = sizeof(singleOptions) / sizeof(struct singleOption),
totalIndexes = multiOptionsAmount + singleOptionsAmount - 1,
selectedOption;
selectedOption,
singleSelected;
bool isMultiOption;
//Parse the existing options
for(u32 i = 0; i < multiOptionsAmount; i++)
@ -314,9 +316,22 @@ void configMenu(bool isSdMode, bool oldPinStatus, u32 oldPinMode)
if(selectedOption < multiOptionsAmount)
{
if(multiOptions[selectedOption].visible) break;
if(multiOptions[selectedOption].visible)
{
isMultiOption = true;
break;
}
}
else
{
singleSelected = selectedOption - multiOptionsAmount;
if(singleOptions[singleSelected].visible)
{
isMultiOption = false;
break;
}
}
else if(singleOptions[selectedOption - multiOptionsAmount].visible) break;
}
if(selectedOption == oldSelectedOption) continue;
@ -334,13 +349,8 @@ void configMenu(bool isSdMode, bool oldPinStatus, u32 oldPinMode)
if(singleOptions[singleOldSelected].enabled) drawCharacter(selected, true, 10 + SPACING_X, singleOptions[singleOldSelected].posY, COLOR_WHITE);
}
if(selectedOption < multiOptionsAmount)
drawString(multiOptionsText[selectedOption], true, 10, multiOptions[selectedOption].posY, COLOR_RED);
else
{
u32 singleSelected = selectedOption - multiOptionsAmount;
drawString(singleOptionsText[singleSelected], true, 10, singleOptions[singleSelected].posY, COLOR_RED);
}
if(isMultiOption) drawString(multiOptionsText[selectedOption], true, 10, multiOptions[selectedOption].posY, COLOR_RED);
else drawString(singleOptionsText[singleSelected], true, 10, singleOptions[singleSelected].posY, COLOR_RED);
clearScreens(false, true, false);
drawString(optionsDescription[selectedOption], false, 10, 10, COLOR_WHITE);
@ -348,7 +358,7 @@ void configMenu(bool isSdMode, bool oldPinStatus, u32 oldPinMode)
else
{
//The selected option's status changed, print the 'x's accordingly
if(selectedOption < multiOptionsAmount)
if(isMultiOption)
{
u32 oldEnabled = multiOptions[selectedOption].enabled;
drawCharacter(selected, true, 10 + multiOptions[selectedOption].posXs[oldEnabled] * SPACING_X, multiOptions[selectedOption].posY, COLOR_BLACK);
@ -358,20 +368,15 @@ void configMenu(bool isSdMode, bool oldPinStatus, u32 oldPinMode)
}
else
{
bool oldEnabled = singleOptions[selectedOption - multiOptionsAmount].enabled;
singleOptions[selectedOption - multiOptionsAmount].enabled = !oldEnabled;
if(oldEnabled) drawCharacter(selected, true, 10 + SPACING_X, singleOptions[selectedOption - multiOptionsAmount].posY, COLOR_BLACK);
bool oldEnabled = singleOptions[singleSelected].enabled;
singleOptions[singleSelected].enabled = !oldEnabled;
if(oldEnabled) drawCharacter(selected, true, 10 + SPACING_X, singleOptions[singleSelected].posY, COLOR_BLACK);
}
}
//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, true, 10 + multiOptions[selectedOption].posXs[multiOptions[selectedOption].enabled] * SPACING_X, multiOptions[selectedOption].posY, COLOR_RED);
else
{
u32 singleSelected = selectedOption - multiOptionsAmount;
if(singleOptions[singleSelected].enabled) drawCharacter(selected, true, 10 + SPACING_X, singleOptions[singleSelected].posY, COLOR_RED);
}
if(isMultiOption) drawCharacter(selected, true, 10 + multiOptions[selectedOption].posXs[multiOptions[selectedOption].enabled] * SPACING_X, multiOptions[selectedOption].posY, COLOR_RED);
else if(singleOptions[singleSelected].enabled) drawCharacter(selected, true, 10 + SPACING_X, singleOptions[singleSelected].posY, COLOR_RED);
}
//Preserve the last-used boot options (first 9 bits)

View File

@ -26,6 +26,8 @@
#include "crypto.h"
#include "cache.h"
#include "screen.h"
#include "draw.h"
#include "utils.h"
#include "fatfs/ff.h"
#include "buttons.h"
#include "../build/bundled.h"
@ -126,56 +128,137 @@ void fileDelete(const char *path)
f_unlink(path);
}
void loadPayload(u32 pressed)
void loadPayload(u32 pressed, const char *payloadPath)
{
const char *pattern;
u32 *loaderAddress = (u32 *)0x24FFFE00;
u8 *payloadAddress = (u8 *)0x24F00000;
u32 payloadSize = 0,
maxPayloadSize = (u32)((u8 *)loaderAddress - payloadAddress);
if(pressed & BUTTON_LEFT) pattern = PATTERN("left");
else if(pressed & BUTTON_RIGHT) pattern = PATTERN("right");
else if(pressed & BUTTON_UP) pattern = PATTERN("up");
else if(pressed & BUTTON_DOWN) pattern = PATTERN("down");
else if(pressed & BUTTON_START) pattern = PATTERN("start");
else if(pressed & BUTTON_B) pattern = PATTERN("b");
else if(pressed & BUTTON_X) pattern = PATTERN("x");
else if(pressed & BUTTON_Y) pattern = PATTERN("y");
else if(pressed & BUTTON_R1) pattern = PATTERN("r");
else if(pressed & BUTTON_A) pattern = PATTERN("a");
else pattern = PATTERN("select");
DIR dir;
FILINFO info;
char path[22] = "payloads";
FRESULT result = f_findfirst(&dir, &info, path, pattern);
if(result == FR_OK)
if(payloadPath == NULL)
{
const char *pattern;
if(pressed & BUTTON_LEFT) pattern = PATTERN("left");
else if(pressed & BUTTON_RIGHT) pattern = PATTERN("right");
else if(pressed & BUTTON_UP) pattern = PATTERN("up");
else if(pressed & BUTTON_DOWN) pattern = PATTERN("down");
else if(pressed & BUTTON_START) pattern = PATTERN("start");
else if(pressed & BUTTON_B) pattern = PATTERN("b");
else if(pressed & BUTTON_X) pattern = PATTERN("x");
else if(pressed & BUTTON_Y) pattern = PATTERN("y");
else if(pressed & BUTTON_R1) pattern = PATTERN("r");
else if(pressed & BUTTON_A) pattern = PATTERN("a");
else pattern = PATTERN("select");
DIR dir;
FILINFO info;
FRESULT result;
char path[22] = "payloads";
result = f_findfirst(&dir, &info, path, pattern);
if(result == FR_OK)
{
f_closedir(&dir);
if(info.fname[0] != 0)
{
concatenateStrings(path, "/");
concatenateStrings(path, info.altname);
payloadSize = fileRead(payloadAddress, path, maxPayloadSize);
}
}
}
else payloadSize = fileRead(payloadAddress, payloadPath, maxPayloadSize);
if(payloadSize > 0)
{
memcpy(loaderAddress, loader_bin, loader_bin_size);
loaderAddress[1] = payloadSize;
backupAndRestoreShaHash(true);
initScreens();
flushDCacheRange(loaderAddress, loader_bin_size);
flushICacheRange(loaderAddress, loader_bin_size);
((void (*)())loaderAddress)();
}
}
void payloadMenu(void)
{
DIR dir;
char path[_MAX_LFN + 10] = "payloads";
if(f_opendir(&dir, path) == FR_OK)
{
u32 payloadNum = 0;
FILINFO info;
char payloadList[21][_MAX_LFN + 1];
while(f_readdir(&dir, &info) == FR_OK && info.fname[0] != 0 && payloadNum < 21)
if(info.fname[0] != '.' && memcmp(info.altname + 8, ".BIN", 4) == 0 && strlen(info.fname) < 48)
memcpy(payloadList[payloadNum++], info.fname, sizeof(info.fname));
f_closedir(&dir);
if(info.fname[0] != 0)
if(payloadNum > 0)
{
u32 *loaderAddress = (u32 *)0x24FFFE00;
u8 *payloadAddress = (u8 *)0x24F00000;
char selected = '*';
memcpy(loaderAddress, loader_bin, loader_bin_size);
initScreens();
drawString("Luma3DS chainloader - Press A to select", true, 10, 10, COLOR_TITLE);
for(u32 i = 0, posY = 30; i < payloadNum; i++, posY += SPACING_Y)
drawString(payloadList[i], true, 10 + 2 * (SPACING_X), posY, COLOR_WHITE);
drawCharacter(selected, true, 10, 30, COLOR_RED);
u32 pressed = 0,
selectedPayload = 0;
while(pressed != BUTTON_A)
{
do
{
pressed = waitInput();
}
while(!(pressed & MENU_BUTTONS));
u32 oldSelectedPayload = selectedPayload;
switch(pressed)
{
case BUTTON_UP:
selectedPayload = !selectedPayload ? payloadNum - 1 : selectedPayload - 1;
break;
case BUTTON_DOWN:
selectedPayload = selectedPayload == payloadNum - 1 ? 0 : selectedPayload + 1;
break;
case BUTTON_LEFT:
selectedPayload = 0;
break;
case BUTTON_RIGHT:
selectedPayload = payloadNum - 1;
break;
default:
continue;
}
if(oldSelectedPayload == selectedPayload) continue;
drawCharacter(selected, true, 10, 30 + oldSelectedPayload * SPACING_Y, COLOR_BLACK);
drawCharacter(selected, true, 10, 30 + selectedPayload * SPACING_Y, COLOR_RED);
}
concatenateStrings(path, "/");
concatenateStrings(path, info.altname);
u32 payloadSize = fileRead(payloadAddress, path, (u32)((u8 *)loaderAddress - payloadAddress));
if(payloadSize > 0)
{
loaderAddress[1] = payloadSize;
backupAndRestoreShaHash(true);
initScreens();
flushDCacheRange(loaderAddress, loader_bin_size);
flushICacheRange(loaderAddress, loader_bin_size);
((void (*)())loaderAddress)();
}
concatenateStrings(path, payloadList[selectedPayload]);
loadPayload(0, path);
error("The payload is too large or corrupted.");
}
}
}

View File

@ -31,6 +31,7 @@ u32 fileRead(void *dest, const char *path, u32 maxSize);
u32 getFileSize(const char *path);
bool fileWrite(const void *buffer, const char *path, u32 size);
void fileDelete(const char *path);
void loadPayload(u32 pressed);
void loadPayload(u32 pressed, const char *payloadPath);
void payloadMenu(void);
u32 firmRead(void *dest, u32 firmType);
void findDumpFile(const char *path, char *fileName);

View File

@ -157,12 +157,12 @@ void main(void)
if(splashMode == 1 && loadSplash()) pressed = HID_PAD;
if((pressed & (BUTTON_START | BUTTON_L1)) == BUTTON_START) payloadMenu();
/* If L and R/A/Select or one of the single payload buttons are pressed,
chainload an external payload */
bool shouldLoadPayload = ((pressed & SINGLE_PAYLOAD_BUTTONS) && !(pressed & (BUTTON_L1 | BUTTON_R1 | BUTTON_A))) ||
((pressed & L_PAYLOAD_BUTTONS) && (pressed & BUTTON_L1));
if(shouldLoadPayload) loadPayload(pressed);
else if(((pressed & SINGLE_PAYLOAD_BUTTONS) && !(pressed & (BUTTON_L1 | BUTTON_R1 | BUTTON_A))) ||
((pressed & L_PAYLOAD_BUTTONS) && (pressed & BUTTON_L1))) loadPayload(pressed, NULL);
if(splashMode == 2) loadSplash();