Implement chainloader selector menu (can be called with START, the START payload can now be booted with L+START
This commit is contained in:
parent
bc167dde2d
commit
2e1b943805
@ -226,7 +226,9 @@ void configMenu(bool isSdMode, bool oldPinStatus, u32 oldPinMode)
|
|||||||
u32 multiOptionsAmount = sizeof(multiOptions) / sizeof(struct multiOption),
|
u32 multiOptionsAmount = sizeof(multiOptions) / sizeof(struct multiOption),
|
||||||
singleOptionsAmount = sizeof(singleOptions) / sizeof(struct singleOption),
|
singleOptionsAmount = sizeof(singleOptions) / sizeof(struct singleOption),
|
||||||
totalIndexes = multiOptionsAmount + singleOptionsAmount - 1,
|
totalIndexes = multiOptionsAmount + singleOptionsAmount - 1,
|
||||||
selectedOption;
|
selectedOption,
|
||||||
|
singleSelected;
|
||||||
|
bool isMultiOption;
|
||||||
|
|
||||||
//Parse the existing options
|
//Parse the existing options
|
||||||
for(u32 i = 0; i < multiOptionsAmount; i++)
|
for(u32 i = 0; i < multiOptionsAmount; i++)
|
||||||
@ -314,9 +316,22 @@ void configMenu(bool isSdMode, bool oldPinStatus, u32 oldPinMode)
|
|||||||
|
|
||||||
if(selectedOption < multiOptionsAmount)
|
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;
|
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(singleOptions[singleOldSelected].enabled) drawCharacter(selected, true, 10 + SPACING_X, singleOptions[singleOldSelected].posY, COLOR_WHITE);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(selectedOption < multiOptionsAmount)
|
if(isMultiOption) drawString(multiOptionsText[selectedOption], true, 10, multiOptions[selectedOption].posY, COLOR_RED);
|
||||||
drawString(multiOptionsText[selectedOption], true, 10, multiOptions[selectedOption].posY, COLOR_RED);
|
else drawString(singleOptionsText[singleSelected], true, 10, singleOptions[singleSelected].posY, COLOR_RED);
|
||||||
else
|
|
||||||
{
|
|
||||||
u32 singleSelected = selectedOption - multiOptionsAmount;
|
|
||||||
drawString(singleOptionsText[singleSelected], true, 10, singleOptions[singleSelected].posY, COLOR_RED);
|
|
||||||
}
|
|
||||||
|
|
||||||
clearScreens(false, true, false);
|
clearScreens(false, true, false);
|
||||||
drawString(optionsDescription[selectedOption], false, 10, 10, COLOR_WHITE);
|
drawString(optionsDescription[selectedOption], false, 10, 10, COLOR_WHITE);
|
||||||
@ -348,7 +358,7 @@ void configMenu(bool isSdMode, bool oldPinStatus, u32 oldPinMode)
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
//The selected option's status changed, print the 'x's accordingly
|
//The selected option's status changed, print the 'x's accordingly
|
||||||
if(selectedOption < multiOptionsAmount)
|
if(isMultiOption)
|
||||||
{
|
{
|
||||||
u32 oldEnabled = multiOptions[selectedOption].enabled;
|
u32 oldEnabled = multiOptions[selectedOption].enabled;
|
||||||
drawCharacter(selected, true, 10 + multiOptions[selectedOption].posXs[oldEnabled] * SPACING_X, multiOptions[selectedOption].posY, COLOR_BLACK);
|
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
|
else
|
||||||
{
|
{
|
||||||
bool oldEnabled = singleOptions[selectedOption - multiOptionsAmount].enabled;
|
bool oldEnabled = singleOptions[singleSelected].enabled;
|
||||||
singleOptions[selectedOption - multiOptionsAmount].enabled = !oldEnabled;
|
singleOptions[singleSelected].enabled = !oldEnabled;
|
||||||
if(oldEnabled) drawCharacter(selected, true, 10 + SPACING_X, singleOptions[selectedOption - multiOptionsAmount].posY, COLOR_BLACK);
|
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'
|
//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)
|
if(isMultiOption) drawCharacter(selected, true, 10 + multiOptions[selectedOption].posXs[multiOptions[selectedOption].enabled] * SPACING_X, multiOptions[selectedOption].posY, COLOR_RED);
|
||||||
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);
|
||||||
else
|
|
||||||
{
|
|
||||||
u32 singleSelected = selectedOption - multiOptionsAmount;
|
|
||||||
if(singleOptions[singleSelected].enabled) drawCharacter(selected, true, 10 + SPACING_X, singleOptions[singleSelected].posY, COLOR_RED);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//Preserve the last-used boot options (first 9 bits)
|
//Preserve the last-used boot options (first 9 bits)
|
||||||
|
101
source/fs.c
101
source/fs.c
@ -26,6 +26,8 @@
|
|||||||
#include "crypto.h"
|
#include "crypto.h"
|
||||||
#include "cache.h"
|
#include "cache.h"
|
||||||
#include "screen.h"
|
#include "screen.h"
|
||||||
|
#include "draw.h"
|
||||||
|
#include "utils.h"
|
||||||
#include "fatfs/ff.h"
|
#include "fatfs/ff.h"
|
||||||
#include "buttons.h"
|
#include "buttons.h"
|
||||||
#include "../build/bundled.h"
|
#include "../build/bundled.h"
|
||||||
@ -126,8 +128,15 @@ void fileDelete(const char *path)
|
|||||||
f_unlink(path);
|
f_unlink(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
void loadPayload(u32 pressed)
|
void loadPayload(u32 pressed, const char *payloadPath)
|
||||||
{
|
{
|
||||||
|
u32 *loaderAddress = (u32 *)0x24FFFE00;
|
||||||
|
u8 *payloadAddress = (u8 *)0x24F00000;
|
||||||
|
u32 payloadSize = 0,
|
||||||
|
maxPayloadSize = (u32)((u8 *)loaderAddress - payloadAddress);
|
||||||
|
|
||||||
|
if(payloadPath == NULL)
|
||||||
|
{
|
||||||
const char *pattern;
|
const char *pattern;
|
||||||
|
|
||||||
if(pressed & BUTTON_LEFT) pattern = PATTERN("left");
|
if(pressed & BUTTON_LEFT) pattern = PATTERN("left");
|
||||||
@ -144,9 +153,10 @@ void loadPayload(u32 pressed)
|
|||||||
|
|
||||||
DIR dir;
|
DIR dir;
|
||||||
FILINFO info;
|
FILINFO info;
|
||||||
|
FRESULT result;
|
||||||
char path[22] = "payloads";
|
char path[22] = "payloads";
|
||||||
|
|
||||||
FRESULT result = f_findfirst(&dir, &info, path, pattern);
|
result = f_findfirst(&dir, &info, path, pattern);
|
||||||
|
|
||||||
if(result == FR_OK)
|
if(result == FR_OK)
|
||||||
{
|
{
|
||||||
@ -154,18 +164,17 @@ void loadPayload(u32 pressed)
|
|||||||
|
|
||||||
if(info.fname[0] != 0)
|
if(info.fname[0] != 0)
|
||||||
{
|
{
|
||||||
u32 *loaderAddress = (u32 *)0x24FFFE00;
|
|
||||||
u8 *payloadAddress = (u8 *)0x24F00000;
|
|
||||||
|
|
||||||
memcpy(loaderAddress, loader_bin, loader_bin_size);
|
|
||||||
|
|
||||||
concatenateStrings(path, "/");
|
concatenateStrings(path, "/");
|
||||||
concatenateStrings(path, info.altname);
|
concatenateStrings(path, info.altname);
|
||||||
|
payloadSize = fileRead(payloadAddress, path, maxPayloadSize);
|
||||||
u32 payloadSize = fileRead(payloadAddress, path, (u32)((u8 *)loaderAddress - payloadAddress));
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else payloadSize = fileRead(payloadAddress, payloadPath, maxPayloadSize);
|
||||||
|
|
||||||
if(payloadSize > 0)
|
if(payloadSize > 0)
|
||||||
{
|
{
|
||||||
|
memcpy(loaderAddress, loader_bin, loader_bin_size);
|
||||||
loaderAddress[1] = payloadSize;
|
loaderAddress[1] = payloadSize;
|
||||||
|
|
||||||
backupAndRestoreShaHash(true);
|
backupAndRestoreShaHash(true);
|
||||||
@ -176,6 +185,80 @@ void loadPayload(u32 pressed)
|
|||||||
|
|
||||||
((void (*)())loaderAddress)();
|
((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(payloadNum > 0)
|
||||||
|
{
|
||||||
|
char selected = '*';
|
||||||
|
|
||||||
|
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, payloadList[selectedPayload]);
|
||||||
|
loadPayload(0, path);
|
||||||
|
error("The payload is too large or corrupted.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -31,6 +31,7 @@ u32 fileRead(void *dest, const char *path, u32 maxSize);
|
|||||||
u32 getFileSize(const char *path);
|
u32 getFileSize(const char *path);
|
||||||
bool fileWrite(const void *buffer, const char *path, u32 size);
|
bool fileWrite(const void *buffer, const char *path, u32 size);
|
||||||
void fileDelete(const char *path);
|
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);
|
u32 firmRead(void *dest, u32 firmType);
|
||||||
void findDumpFile(const char *path, char *fileName);
|
void findDumpFile(const char *path, char *fileName);
|
@ -157,12 +157,12 @@ void main(void)
|
|||||||
|
|
||||||
if(splashMode == 1 && loadSplash()) pressed = HID_PAD;
|
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,
|
/* If L and R/A/Select or one of the single payload buttons are pressed,
|
||||||
chainload an external payload */
|
chainload an external payload */
|
||||||
bool shouldLoadPayload = ((pressed & SINGLE_PAYLOAD_BUTTONS) && !(pressed & (BUTTON_L1 | BUTTON_R1 | BUTTON_A))) ||
|
else if(((pressed & SINGLE_PAYLOAD_BUTTONS) && !(pressed & (BUTTON_L1 | BUTTON_R1 | BUTTON_A))) ||
|
||||||
((pressed & L_PAYLOAD_BUTTONS) && (pressed & BUTTON_L1));
|
((pressed & L_PAYLOAD_BUTTONS) && (pressed & BUTTON_L1))) loadPayload(pressed, NULL);
|
||||||
|
|
||||||
if(shouldLoadPayload) loadPayload(pressed);
|
|
||||||
|
|
||||||
if(splashMode == 2) loadSplash();
|
if(splashMode == 2) loadSplash();
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user