Implement autobooting into homebrew (3DS and DSi modes)

Add config option to autoboot into 3DS and DSi homebrew menu, without
going through Home Menu (nor launching it).

For 3DS homebrew, this requires homebrew built with libctru v2.0.0 or
later (v2.0.0 was released 2.5y ago).

We simulate a "reboot into title" to achieve this. This being said, when
launching stuff like Pokemon US/UM on O3DS, Home Menu reboots into
itself and not the game directly. This will cause Home Menu to crash if
you use this feature and configure it to use a non-default memory layout
(but if you don't, Home Menu will work just fine).
This commit is contained in:
TuxSH 2023-01-03 15:30:07 +01:00
parent 0a6b6865ca
commit fe4bb0857b
19 changed files with 439 additions and 33 deletions

Binary file not shown.

View File

@ -319,8 +319,14 @@ static int configIniHandler(void* user, const char* section, const char* name, c
} else {
CHECK_PARSE_OPTION(-1);
}
}
} else if (strcmp(name, "autoboot_mode") == 0) {
s64 opt;
CHECK_PARSE_OPTION(parseDecIntOption(&opt, value, 0, 2));
cfg->multiConfig |= (u32)opt << (2 * (u32)AUTOBOOTMODE);
return 1;
} else {
CHECK_PARSE_OPTION(-1);
}
} else if (strcmp(section, "rosalina") == 0) {
// Rosalina options
if (strcmp(name, "hbldr_3dsx_titleid") == 0) {
@ -347,6 +353,20 @@ static int configIniHandler(void* user, const char* section, const char* name, c
else {
CHECK_PARSE_OPTION(-1);
}
} else if (strcmp(section, "autoboot") == 0) {
if (strcmp(name, "autoboot_dsi_titleid") == 0) {
u64 opt;
CHECK_PARSE_OPTION(parseHexIntOption(&opt, value, 0, 0xFFFFFFFFFFFFFFFFull));
cfg->autobootTwlTitleId = opt;
return 1;
} else if (strcmp(name, "autoboot_3ds_app_mem_type") == 0) {
s64 opt;
CHECK_PARSE_OPTION(parseDecIntOption(&opt, value, 0, 4));
cfg->autobootCtrAppmemtype = (u8)opt;
return 1;
} else {
CHECK_PARSE_OPTION(-1);
}
} else if (strcmp(section, "misc") == 0) {
for (size_t i = 0; i < sizeof(singleOptionIniNamesMisc)/sizeof(singleOptionIniNamesMisc[0]); i++) {
if (strcmp(name, singleOptionIniNamesMisc[i]) == 0) {
@ -414,11 +434,13 @@ static size_t saveLumaIniConfigToStr(char *out)
1 + (int)MULTICONFIG(DEFAULTEMU), 4 - (int)MULTICONFIG(BRIGHTNESS),
splashPosStr, (unsigned int)cfg->splashDurationMsec,
pinNumDigits, n3dsCpuStr,
pinNumDigits, n3dsCpuStr, (int)MULTICONFIG(AUTOBOOTMODE),
cfg->hbldr3dsxTitleId, rosalinaMenuComboStr,
(int)cfg->screenFiltersCct, (int)cfg->ntpTzOffetMinutes,
cfg->autobootTwlTitleId, (int)cfg->autobootCtrAppmemtype,
(int)CONFIG(PATCHUNITINFO), (int)CONFIG(DISABLEARM11EXCHANDLERS),
(int)CONFIG(ENABLESAFEFIRMROSALINA)
);
@ -522,9 +544,10 @@ bool readConfig(void)
configData.formatVersionMinor = CONFIG_VERSIONMINOR;
configData.config |= 1u << PATCHVERSTRING;
configData.splashDurationMsec = 3000;
configData.hbldr3dsxTitleId = 0x000400000D921E00ull;
configData.hbldr3dsxTitleId = HBLDR_DEFAULT_3DSX_TID;
configData.rosalinaMenuCombo = 1u << 9 | 1u << 7 | 1u << 2; // L+Start+Select
configData.screenFiltersCct = 6500; // default temp, no-op
configData.autobootTwlTitleId = AUTOBOOT_DEFAULT_TWL_TID;
ret = false;
}
else
@ -566,6 +589,7 @@ void configMenu(bool oldPinStatus, u32 oldPinMode)
"Splash: Off( ) Before( ) After( ) payloads",
"PIN lock: Off( ) 4( ) 6( ) 8( ) digits",
"New 3DS CPU: Off( ) Clock( ) L2( ) Clock+L2( )",
"Homebrew autoboot: Off( ) 3DS( ) DSi( )",
};
static const char *singleOptionsText[] = { "( ) Autoboot EmuNAND",
@ -607,6 +631,14 @@ void configMenu(bool oldPinStatus, u32 oldPinMode)
"'Clock+L2' can cause issues with some\n"
"games.",
"Enable autobooting into homebrew,\n"
"either into 3DS or DSi mode.\n\n"
"Autobooting into a gamecard title is\n"
"not supported.\n\n"
"Refer to the \"autoboot\" section in the\n"
"configuration file to configure\n"
"this feature.",
"If enabled, an EmuNAND\n"
"will be launched on boot.\n\n"
"Otherwise, SysNAND will.\n\n"
@ -675,6 +707,7 @@ void configMenu(bool oldPinStatus, u32 oldPinMode)
{ .visible = true },
{ .visible = true },
{ .visible = ISN3DS },
{ .visible = true },
};
struct singleOption {

View File

@ -28,13 +28,16 @@
#include "types.h"
#define HBLDR_DEFAULT_3DSX_TID 0x000400000D921E00ull
#define AUTOBOOT_DEFAULT_TWL_TID 0x0003000448424C41ull
#define CONFIG(a) (((configData.config >> (a)) & 1) != 0)
#define MULTICONFIG(a) ((configData.multiConfig >> (2 * (a))) & 3)
#define BOOTCONFIG(a, b) ((configData.bootConfig >> (a)) & (b))
#define CONFIG_FILE "config.bin"
#define CONFIG_VERSIONMAJOR 3
#define CONFIG_VERSIONMINOR 0
#define CONFIG_VERSIONMINOR 1
#define BOOTCFG_NAND BOOTCONFIG(0, 7)
#define BOOTCFG_FIRM BOOTCONFIG(3, 7)
@ -47,7 +50,8 @@ enum multiOptions
BRIGHTNESS,
SPLASH,
PIN,
NEWCPU
NEWCPU,
AUTOBOOTMODE,
};
enum singleOptions

209
arm9/source/deliver_arg.c Normal file
View File

@ -0,0 +1,209 @@
/*
* This file is part of Luma3DS
* Copyright (C) 2022 Aurora Wright, TuxSH
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* Additional Terms 7.b and 7.c of GPLv3 apply to this file:
* * Requiring preservation of specified reasonable legal notices or
* author attributions in that material or in the Appropriate Legal
* Notices displayed by works containing it.
* * Prohibiting misrepresentation of the origin of that material,
* or requiring that modified versions of such material be marked in
* reasonable ways as different from the original version.
*/
#include "deliver_arg.h"
#include "utils.h"
#include "memory.h"
#include "config.h"
u8 *loadDeliverArg(void)
{
static u8 deliverArg[0x1000] = {0};
static bool deliverArgLoaded = false;
if (!deliverArgLoaded)
{
u32 bootenv = CFG_BOOTENV; // this register is preserved across reboots
if ((bootenv & 1) == 0) // true coldboot
{
memset(deliverArg, 0, 0x1000);
}
else
{
u32 mode = bootenv >> 1;
if (mode == 0) // CTR mode
{
memcpy(deliverArg, (const void *)0x20000000, 0x1000);
// Validate deliver arg
u32 testPattern = *(u32 *)(deliverArg + 0x438);
u32 crc = *(u32 *)(deliverArg + 0x43C);
u32 expectedCrc = crc32(deliverArg + 0x400, 0x140, 0xFFFFFFFF);
if (testPattern != 0xFFFF || crc != expectedCrc)
memset(deliverArg, 0, 0x1000);
}
else // Legacy modes
{
// Copy TWL deliver arg stuff as-is (0...0x300)
copyFromLegacyModeFcram(deliverArg, (const void *)0x20000000, 0x400);
// Validate TLNC (TWL launcher params) block
// Note: Nintendo doesn't do crcLen bound check
u8 *tlnc = deliverArg + 0x300;
bool hasMagic = memcmp(tlnc, "TLNC", 4) == 0;
u8 crcLen = tlnc[5];
u16 crc = *(u16 *)(tlnc + 6);
if (!hasMagic || crcLen <= 248 || crc != crc16(tlnc + 8, crcLen, 0xFFFF))
memset(tlnc, 0, 0x100);
memset(deliverArg + 0x400, 0, 0xC00);
}
}
deliverArgLoaded = true;
}
return deliverArg;
}
void commitDeliverArg(void)
{
u8 *deliverArg = loadDeliverArg();
u32 bootenv = CFG_BOOTENV;
if ((bootenv & 1) == 0) // if true coldboot, set bootenv to "CTR mode reboot"
{
bootenv = 1;
CFG_BOOTENV = 1;
}
u32 mode = bootenv >> 1;
if (mode == 0) // CTR mode
{
*(u32 *)(deliverArg + 0x438) = 0xFFFF;
*(u32 *)(deliverArg + 0x43C) = crc32(deliverArg + 0x400, 0x140, 0xFFFFFFFF);
memcpy((void *)0x20000000, deliverArg, 0x1000);
}
else // Legacy modes (just TWL mode, really)
{
copyToLegacyModeFcram((void *)0x20000000, deliverArg, 0x400);
}
}
bool hasValidTlncAutobootParams(void)
{
u8 *tlnc = loadDeliverArg() + 0x300; // loadDeliverArg clears invalid TLNC blocks
return memcmp(tlnc, "TLNC", 4) == 0 && (*(u16 *)(tlnc + 0x18) & 1) != 0;
}
bool isTwlToCtrLaunch(void)
{
// assumes TLNC block is valid
u8 *tlnc = loadDeliverArg() + 0x300; // loadDeliverArg clears invalid TLNC blocks
u64 twlTid = *(u64 *)(tlnc + 0x10);
switch (twlTid & ~0xFFull)
{
case 0x0000000000000000ull: // TWL Launcher -> Home menu (note: NS checks full TID)
case 0x00030015484E4200ull: // TWL System Settings -> CTR System Settings (mset)
return true;
default:
return false;
}
}
static bool configureHomebrewAutobootCtr(u8 *deliverArg)
{
static const u8 appmemtypesO3ds[] = { 0, 2, 3, 4, 5 };
static const u8 appmemtypesN3ds[] = { 6, 7, 7, 7, 7 };
u64 hbldrTid = configData.hbldr3dsxTitleId;
hbldrTid = hbldrTid == 0 ? HBLDR_DEFAULT_3DSX_TID : hbldrTid; // replicate Loader's behavior
if ((hbldrTid >> 46) != 0x10) // Not a CTR titleId. Bail out
return false;
u8 memtype = configData.autobootCtrAppmemtype;
deliverArg[0x400] = ISN3DS ? appmemtypesN3ds[memtype] : appmemtypesO3ds[memtype];
// Determine whether to load from the SD card or from NAND. We don't support gamecards for this
u32 category = (hbldrTid >> 32) & 0xFFFF;
bool isSdApp = (category & 0x10) == 0 && category != 1; // not a system app nor a DLP child
*(u64 *)(deliverArg + 0x440) = hbldrTid;
*(u64 *)(deliverArg + 0x448) = isSdApp ? 1 : 0;
// Tell NS to run the title, and that it's not a title jump from legacy mode
*(u32 *)(deliverArg + 0x460) = (0 << 1) | (1 << 0);
CFG_BOOTENV = 1;
return true;
}
static bool configureHomebrewAutobootTwl(u8 *deliverArg)
{
// Here, we pretend to be a TWL app rebooting into another TWL app.
// We get NS to do all the heavy lifting (starting NWM and AM, etc.) this way.
memset(deliverArg + 0x000, 0, 0x300); // zero TWL deliver arg params
// Now onto TLNC (launcher params):
u8 *tlnc = deliverArg + 0x300;
memset(tlnc, 0, 0x100);
memcpy(tlnc, "TLNC", 4);
tlnc[4] = 1; // version
tlnc[5] = 0x18; // length of data to calculate CRC over
*(u64 *)(tlnc + 8) = 0; // old title ID
*(u64 *)(tlnc + 0x10) = configData.autobootTwlTitleId; // new title ID
// bit4: "skip logo" ; bits2:1: NAND boot ; bit0: valid
*(u16 *)(tlnc + 0x18) = (1 << 4) | (3 << 1) | (1 << 0);
*(u16 *)(tlnc + 6) = crc16(tlnc + 8, 0x18, 0xFFFF);
CFG_BOOTENV = 3;
return true;
}
bool configureHomebrewAutoboot(void)
{
bool ret;
u8 *deliverArg = loadDeliverArg();
u32 bootenv = CFG_BOOTENV;
u32 mode = bootenv >> 1;
u32 testPattern = *(u32 *)(deliverArg + 0x438);
if (mode != 0 || testPattern == 0xFFFF)
return false; // bail out if this isn't a coldboot/plain reboot
switch (MULTICONFIG(AUTOBOOTMODE))
{
case 1:
ret = configureHomebrewAutobootCtr(deliverArg);
break;
case 2:
ret = configureHomebrewAutobootTwl(deliverArg);
break;
case 0:
default:
ret = false;
break;
}
if (ret)
commitDeliverArg();
return ret;
}

37
arm9/source/deliver_arg.h Normal file
View File

@ -0,0 +1,37 @@
/*
* This file is part of Luma3DS
* Copyright (C) 2022 Aurora Wright, TuxSH
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* Additional Terms 7.b and 7.c of GPLv3 apply to this file:
* * Requiring preservation of specified reasonable legal notices or
* author attributions in that material or in the Appropriate Legal
* Notices displayed by works containing it.
* * Prohibiting misrepresentation of the origin of that material,
* or requiring that modified versions of such material be marked in
* reasonable ways as different from the original version.
*/
#pragma once
#include "types.h"
u8 *loadDeliverArg(void);
void commitDeliverArg(void);
bool hasValidTlncAutobootParams(void);
bool isTwlToCtrLaunch(void); // assumes TLNC block is valid
bool configureHomebrewAutoboot(void);

View File

@ -1,6 +1,6 @@
/*
* This file is part of Luma3DS
* Copyright (C) 2016-2020 Aurora Wright, TuxSH
* Copyright (C) 2016-2022 Aurora Wright, TuxSH
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -35,6 +35,7 @@
#include "pin.h"
#include "crypto.h"
#include "memory.h"
#include "deliver_arg.h"
#include "screen.h"
#include "i2c.h"
#include "fmt.h"
@ -216,8 +217,15 @@ void main(int argc, char **argv, u32 magicWord)
//If it's a MCU reboot, try to force boot options
if(CFG_BOOTENV && needConfig != CREATE_CONFIGURATION)
{
//Always force a SysNAND boot when quitting AGB_FIRM
if(CFG_BOOTENV == 7)
u32 bootenv = CFG_BOOTENV;
bool validTlnc = bootenv == 3 && hasValidTlncAutobootParams();
bool twlIntoCtr = validTlnc && isTwlToCtrLaunch();
if (validTlnc)
needToInitSd = true;
//Always force a SysNAND boot when quitting AGB_FIRM, or when doing a TWL -> (ns ->) TWL reboot
if(bootenv == 7 || (validTlnc && !twlIntoCtr))
{
nandType = FIRMWARE_SYSNAND;
firmSource = (BOOTCFG_NAND != 0) == (BOOTCFG_FIRM != 0) ? FIRMWARE_SYSNAND : (FirmwareSource)BOOTCFG_FIRM;
@ -228,16 +236,9 @@ void main(int argc, char **argv, u32 magicWord)
goto boot;
}
//Account for DSiWare soft resets if exiting TWL_FIRM
if(CFG_BOOTENV == 3)
{
static const u8 TLNC[] = {0x54, 0x4C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4E, 0x43};
if(memcmp((void *)0x20000C00, TLNC, 10) == 0) needToInitSd = true;
}
/* Force the last used boot options if autobooting a TWL title, or unless a button is pressed
/* Force the last used boot options if doing TWL->CTR, or unless a button is pressed
or the no-forcing flag is set */
if(needToInitSd || memcmp((void *)0x20000300, "TLNC", 4) == 0 || (!pressed && !BOOTCFG_NOFORCEFLAG))
if(twlIntoCtr || !(pressed || BOOTCFG_NOFORCEFLAG))
{
nandType = (FirmwareSource)BOOTCFG_NAND;
firmSource = (FirmwareSource)BOOTCFG_FIRM;
@ -309,6 +310,17 @@ void main(int argc, char **argv, u32 magicWord)
goto boot;
}
// Set-up autoboot, and if we're booting into TWL mode, always use SysNAND
if (MULTICONFIG(AUTOBOOTMODE) != 0)
{
bool ok = configureHomebrewAutoboot();
if (ok && MULTICONFIG(AUTOBOOTMODE) == 2)
{
nandType = FIRMWARE_SYSNAND;
firmSource = FIRMWARE_SYSNAND;
}
}
//If booting from CTRNAND, always use SysNAND
if(!isSdMode) nandType = FIRMWARE_SYSNAND;
@ -387,8 +399,10 @@ boot:
switch(firmType)
{
case NATIVE_FIRM:
{
res = patchNativeFirm(firmVersion, nandType, loadFromStorage, isFirmProtEnabled, needToInitSd, doUnitinfoPatch);
break;
}
case TWL_FIRM:
res = patchTwlFirm(firmVersion, loadFromStorage, doUnitinfoPatch);
break;

View File

@ -54,3 +54,27 @@ u8 *memsearch(u8 *startPos, const void *pattern, u32 size, u32 patternSize)
return NULL;
}
void *copyFromLegacyModeFcram(void *dst, const void *src, size_t size)
{
// Copy 2 bytes with a stride of 8
const u16 *src16 = (const u16 *)src;
u16 *dst16 = (u16 *)dst;
for (size_t i = 0; i < size / 2; i++)
dst16[i] = src16[4 * i];
return dst;
}
void *copyToLegacyModeFcram(void *dst, const void *src, size_t size)
{
// Copy 2 bytes with a stride of 8
const u16 *src16 = (const u16 *)src;
u16 *dst16 = (u16 *)dst;
for (size_t i = 0; i < size / 2; i++)
dst16[4 * i] = src16[i];
return dst;
}

View File

@ -34,3 +34,5 @@
#include "types.h"
u8 *memsearch(u8 *startPos, const void *pattern, u32 size, u32 patternSize);
void *copyFromLegacyModeFcram(void *dst, const void *src, size_t size);
void *copyToLegacyModeFcram(void *dst, const void *src, size_t size);

View File

@ -134,6 +134,8 @@ u32 installK11Extension(u8 *pos, u32 size, bool needToInitSd, u32 baseK11VA, u32
u32 rosalinaMenuCombo;
u16 screenFiltersCct;
s16 ntpTzOffetMinutes;
u64 autobootTwlTitleId;
u8 autobootCtrAppmemtype;
} info;
};
@ -209,6 +211,8 @@ u32 installK11Extension(u8 *pos, u32 size, bool needToInitSd, u32 baseK11VA, u32
info->rosalinaMenuCombo = configData.rosalinaMenuCombo;
info->screenFiltersCct = configData.screenFiltersCct;
info->ntpTzOffetMinutes = configData.ntpTzOffetMinutes;
info->autobootTwlTitleId = configData.autobootTwlTitleId;
info->autobootCtrAppmemtype = configData.autobootCtrAppmemtype;
info->versionMajor = VERSION_MAJOR;
info->versionMinor = VERSION_MINOR;
info->versionBuild = VERSION_BUILD;

View File

@ -71,6 +71,9 @@ typedef struct {
u32 rosalinaMenuCombo;
u16 screenFiltersCct;
s16 ntpTzOffetMinutes;
u64 autobootTwlTitleId;
u8 autobootCtrAppmemtype;
} CfgData;
typedef struct

View File

@ -1,6 +1,6 @@
/*
* This file is part of Luma3DS
* Copyright (C) 2016-2020 Aurora Wright, TuxSH
* Copyright (C) 2016-2022 Aurora Wright, TuxSH
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -154,3 +154,55 @@ void error(const char *fmt, ...)
mcuPowerOff();
}
u16 crc16(const void *data, size_t size, u16 initialValue)
{
static u16 lut[256] = {0};
static bool lutInitialized = false;
if (!lutInitialized)
{
static const u16 poly = 0xA001;
for (u32 i = 0; i < 256; i++)
{
u16 r = i;
for (u32 j = 0; j < 8; j++)
r = (r >> 1) ^ ((r & 1) != 0 ? poly : 0);
lut[i] = r;
}
lutInitialized = true;
}
u16 r = initialValue;
const u8 *data8 = (const u8 *)data;
for (size_t i = 0; i < size; i++)
r = (r >> 8) ^ lut[(r ^ data8[i]) & 0xFF];
return r;
}
u32 crc32(const void *data, size_t size, u32 initialValue)
{
static u32 lut[256] = {0};
static bool lutInitialized = false;
if (!lutInitialized)
{
static const u32 poly = 0xEDB88320;
for (u32 i = 0; i < 256; i++)
{
u32 r = i;
for (u32 j = 0; j < 8; j++)
r = (r >> 1) ^ ((r & 1) != 0 ? poly : 0);
lut[i] = r;
}
lutInitialized = true;
}
u32 r = initialValue;
const u8 *data8 = (const u8 *)data;
for (size_t i = 0; i < size; i++)
r = (r >> 8) ^ lut[(r ^ data8[i]) & 0xFF];
return ~r;
}

View File

@ -1,6 +1,6 @@
/*
* This file is part of Luma3DS
* Copyright (C) 2016-2020 Aurora Wright, TuxSH
* Copyright (C) 2016-2022 Aurora Wright, TuxSH
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -46,3 +46,6 @@ u32 waitInput(bool isMenu);
void mcuPowerOff(void);
void wait(u64 amount);
void error(const char *fmt, ...);
u16 crc16(const void *data, size_t size, u16 initialValue);
u32 crc32(const void *data, size_t size, u32 initialValue);

View File

@ -20,7 +20,8 @@ enum multiOptions
BRIGHTNESS,
SPLASH,
PIN,
NEWCPU
NEWCPU,
AUTOBOOTMODE,
};
enum singleOptions

View File

@ -132,6 +132,8 @@ typedef struct CfwInfo
u32 rosalinaMenuCombo;
u16 screenFiltersCct;
s16 ntpTzOffetMinutes;
u64 autobootTwlTitleId;
u8 autobootCtrAppmemtype;
} CfwInfo;
extern CfwInfo cfwInfo;

View File

@ -61,6 +61,13 @@ Result GetSystemInfoHook(s64 *out, s32 type, s32 param)
*out = cfwInfo.splashDurationMsec;
break;
case 0x10:
*out = (s64)cfwInfo.autobootTwlTitleId;
break;
case 0x11:
*out = cfwInfo.autobootCtrAppmemtype;
break;
case 0x100:
*out = (s64)cfwInfo.hbldr3dsxTitleId;
break;

View File

@ -22,7 +22,8 @@ enum multiOptions
BRIGHTNESS,
SPLASH,
PIN,
NEWCPU
NEWCPU,
AUTOBOOTMODE,
};
enum singleOptions

View File

@ -88,13 +88,6 @@ void RosalinaMenu_ShowDebugInfo(void)
u32 kernelVer = osGetKernelVersion();
FS_SdMmcSpeedInfo speedInfo;
Handle hm = 0;
OpenProcessByName("menu", &hm);
s64 out = 0;
svcGetHandleInfo(&out, hm, 0);
svcCloseHandle(hm);
u64 timeToBootHm = 1000u * out / SYSCLOCK_ARM11;
do
{
Draw_Lock();
@ -130,11 +123,10 @@ void RosalinaMenu_ShowDebugInfo(void)
(int)speedInfo.highSpeedModeEnabled, SYSCLOCK_SDMMC / (1000 * clkDiv)
);
}
if (timeToBootHm != 0)
{
posY = Draw_DrawFormattedString(
10, posY, COLOR_WHITE, "Time to boot to Home Menu: %llums\n",
timeToBootHm
10, posY, COLOR_WHITE, "APPMEMTYPE: %lu\n",
OS_KernelConfig->app_memtype
);
}
Draw_FlushFramebuffer();

View File

@ -62,7 +62,8 @@ enum multiOptions
BRIGHTNESS,
SPLASH,
PIN,
NEWCPU
NEWCPU,
AUTOBOOTMODE,
};
typedef struct DspFirmSegmentHeader {
u32 offset;
@ -99,6 +100,9 @@ typedef struct CfgData {
u32 rosalinaMenuCombo;
u16 screenFiltersCct;
s16 ntpTzOffetMinutes;
u64 autobootTwlTitleId;
u8 autobootCtrAppmemtype;
} CfgData;
Menu miscellaneousMenu = {
@ -291,11 +295,13 @@ static size_t saveLumaIniConfigToStr(char *out, const CfgData *cfg)
1 + (int)MULTICONFIG(DEFAULTEMU), 4 - (int)MULTICONFIG(BRIGHTNESS),
splashPosStr, (unsigned int)cfg->splashDurationMsec,
pinNumDigits, n3dsCpuStr,
pinNumDigits, n3dsCpuStr, (int)MULTICONFIG(AUTOBOOTMODE),
cfg->hbldr3dsxTitleId, rosalinaMenuComboStr,
(int)cfg->screenFiltersCct, (int)cfg->ntpTzOffetMinutes,
cfg->autobootTwlTitleId, (int)cfg->autobootCtrAppmemtype,
(int)CONFIG(PATCHUNITINFO), (int)CONFIG(DISABLEARM11EXCHANDLERS),
(int)CONFIG(ENABLESAFEFIRMROSALINA)
);
@ -318,8 +324,12 @@ void MiscellaneousMenu_SaveSettings(void)
u32 config, multiConfig, bootConfig;
u32 splashDurationMsec;
u8 autobootCtrAppmemtype;
u64 autobootTwlTitleId;
s64 out;
bool isSdMode;
svcGetSystemInfo(&out, 0x10000, 2);
formatVersion = (u32)out;
svcGetSystemInfo(&out, 0x10000, 3);
@ -330,6 +340,12 @@ void MiscellaneousMenu_SaveSettings(void)
bootConfig = (u32)out;
svcGetSystemInfo(&out, 0x10000, 6);
splashDurationMsec = (u32)out;
svcGetSystemInfo(&out, 0x10000, 0x10);
autobootTwlTitleId = (u64)out;
svcGetSystemInfo(&out, 0x10000, 0x11);
autobootCtrAppmemtype = (u8)out;
svcGetSystemInfo(&out, 0x10000, 0x203);
isSdMode = (bool)out;
@ -343,6 +359,8 @@ void MiscellaneousMenu_SaveSettings(void)
configData.rosalinaMenuCombo = menuCombo;
configData.screenFiltersCct = (u16)screenFiltersCurrentTemperature;
configData.ntpTzOffetMinutes = (s16)lastNtpTzOffset;
configData.autobootTwlTitleId = autobootTwlTitleId;
configData.autobootCtrAppmemtype = autobootCtrAppmemtype;
size_t n = saveLumaIniConfigToStr(inibuf, &configData);
FS_ArchiveID archiveId = isSdMode ? ARCHIVE_SDMC : ARCHIVE_NAND_RW;