mirror of
https://github.com/LumaTeam/Luma3DS.git
synced 2026-02-22 01:44:38 +00:00
Compare commits
23 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e35972ea82 | ||
|
|
21f0d64ee8 | ||
|
|
7f1dd962af | ||
|
|
a608ad8241 | ||
|
|
c04c7254ed | ||
|
|
db639a80c9 | ||
|
|
4cfca3c55d | ||
|
|
d3d9a63bf2 | ||
|
|
407c18e6fd | ||
|
|
456a6b4ad7 | ||
|
|
b3282abbf7 | ||
|
|
119499d28d | ||
|
|
12b15812f2 | ||
|
|
55d694fbdd | ||
|
|
1c980b95dc | ||
|
|
da66af3f8a | ||
|
|
97cc70d35d | ||
|
|
59543da23d | ||
|
|
581e591070 | ||
|
|
8a6b766894 | ||
|
|
5f848124c4 | ||
|
|
b4273f9569 | ||
|
|
856a4b3acf |
@ -195,6 +195,28 @@ static void deinitScreens(void)
|
||||
*(vu32 *)0x10202014 = 0;
|
||||
}
|
||||
|
||||
static void zerofillN3dsAblRegisters(void)
|
||||
{
|
||||
// It should be fine to write to these regs even on O3DS as they
|
||||
// are RAZ/WI
|
||||
|
||||
// TODO: read from calibration, but null values should do just
|
||||
// fine. From testing, LUT explicitly ignores null values, and
|
||||
// it is probably the case of reg @ 0x54 as well.
|
||||
*(vu32 *)0x10202250 = 0; // unknown 24-bit value, seen: 0
|
||||
*(vu32 *)0x10202254 = 0; // unknown 24-bit value, seen: nonzero
|
||||
|
||||
*(vu32 *)0x10202A50 = 0; // unknown 24-bit value, seen: 0
|
||||
*(vu32 *)0x10202A54 = 0; // unknown 24-bit value, seen: nonzero
|
||||
|
||||
for (u32 i = 0; i < 64; i++) {
|
||||
// Blend colors (w/ color multiplication) for each group
|
||||
// of 4 relative-luminance Rs
|
||||
*(vu32 *)(0x10202300 + 4*i) = 0;
|
||||
*(vu32 *)(0x10202B00 + 4*i) = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void main(void)
|
||||
{
|
||||
operation = ARM11_READY;
|
||||
@ -223,6 +245,9 @@ void main(void)
|
||||
case DEINIT_SCREENS:
|
||||
deinitScreens();
|
||||
break;
|
||||
case ZEROFILL_N3DS_ABL_REGISTERS:
|
||||
zerofillN3dsAblRegisters();
|
||||
break;
|
||||
case PREPARE_ARM11_FOR_FIRMLAUNCH:
|
||||
memcpy((void *)0x1FFFFC00, (void *)prepareForFirmlaunch, prepareForFirmlaunchSize);
|
||||
*(vu32 *)0x1FFFFFFC = 0;
|
||||
|
||||
@ -60,6 +60,7 @@ typedef enum
|
||||
SWAP_FRAMEBUFFERS,
|
||||
UPDATE_BRIGHTNESS,
|
||||
DEINIT_SCREENS,
|
||||
ZEROFILL_N3DS_ABL_REGISTERS,
|
||||
PREPARE_ARM11_FOR_FIRMLAUNCH,
|
||||
ARM11_READY,
|
||||
} Arm11Operation;
|
||||
|
||||
@ -694,7 +694,7 @@ static size_t saveLumaIniConfigToStr(char *out)
|
||||
return n < 0 ? 0 : (size_t)n;
|
||||
}
|
||||
|
||||
static char tmpIniBuffer[0x2000];
|
||||
static char tmpIniBuffer[0x2000 + 0x400]; // eyeballed. TODO use #embed
|
||||
|
||||
static bool readLumaIniConfig(void)
|
||||
{
|
||||
@ -709,6 +709,13 @@ static bool readLumaIniConfig(void)
|
||||
static bool writeLumaIniConfig(void)
|
||||
{
|
||||
size_t n = saveLumaIniConfigToStr(tmpIniBuffer);
|
||||
|
||||
// FIXME: this is UB we should port snprintf sometime (as well as fix other tech debt)
|
||||
if (n + 1 >= sizeof(tmpIniBuffer)) {
|
||||
error("Configuration data buffer overflow, please report this issue");
|
||||
__builtin_unreachable();
|
||||
}
|
||||
|
||||
return n != 0 && fileWrite(tmpIniBuffer, "config.ini", n);
|
||||
}
|
||||
|
||||
|
||||
@ -30,6 +30,7 @@
|
||||
#include "config.h"
|
||||
#include "fs.h"
|
||||
#include "i2c.h"
|
||||
#include "screen.h"
|
||||
|
||||
u8 *loadDeliverArg(void)
|
||||
{
|
||||
@ -71,7 +72,7 @@ u8 *loadDeliverArg(void)
|
||||
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))
|
||||
if (!hasMagic || (8 + crcLen) > 0x100 || crc != crc16(tlnc + 8, crcLen, 0xFFFF))
|
||||
memset(tlnc, 0, 0x100);
|
||||
|
||||
memset(deliverArg + 0x400, 0, 0xC00);
|
||||
@ -141,6 +142,8 @@ static bool configureHomebrewAutobootCtr(u8 *deliverArg)
|
||||
return false;
|
||||
|
||||
u8 memtype = configData.autobootCtrAppmemtype;
|
||||
// autobootCtrAppmemtype already checked, but it doesn't hurt to check again
|
||||
memtype = memtype >= 5 ? 0 : memtype;
|
||||
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
|
||||
@ -198,6 +201,29 @@ static bool configureHomebrewAutobootTwl(u8 *deliverArg)
|
||||
|
||||
*(u16 *)(tlnc + 6) = crc16(tlnc + 8, 0x18, 0xFFFF);
|
||||
|
||||
// Even though (when running TWL/AGB FIRM) the SoC is in O3DS mode, and the GPU also is,
|
||||
// as well as most other components behaving as such (external RAM, L2C not usable, etc.),
|
||||
// this is NOT the case for the LCD and adaptive backlight logic which retains FULL N3DS
|
||||
// functionality, including a feature where the window is blended with a given color depending
|
||||
// on the overall relative luminance of that window.
|
||||
|
||||
// However, Nintendo's own code mistakenly assumes the opposite, and clearly so ("if GPU in N3DS mode"
|
||||
// checks, not passing N3DS extra adaptive backlight (ABL) to TWL/AGB_FIRM). This has implications:
|
||||
|
||||
// - Powersaving (ABL) settings in TWL/AGB_FIRM is inconsistent with *both* O3DS (because the new RGB blend LUT
|
||||
// has been set to its current value by NATIVE_FIRM) and N3DS (because "pwn_cnt" and "inertia" are missing
|
||||
// their N3DS-only bits)
|
||||
// - "rave party" when booting into TWL/AGB_FIRM or O3DS NATIVE_FIRM without these regs (well, the LUT) initialized.
|
||||
// Easiest way to do so is by leveraging the "DSi autooboot" feature Luma provides. It is worth noting at least
|
||||
// the LUT survives hardware reboots (if Nintendo were using DSi software that was using TLNC-based reboots,
|
||||
// they wouldn't have noticed).
|
||||
|
||||
// As such, zerofill these registers (from testing, hardware explicitly discards null values, so this
|
||||
// should be fine). For now, only touch the Luma-initiated autoboot path
|
||||
|
||||
if (ISN3DS)
|
||||
zerofillN3dsAblRegisters();
|
||||
|
||||
CFG_BOOTENV = 3;
|
||||
|
||||
return true;
|
||||
|
||||
@ -137,6 +137,7 @@ bool fileWrite(const void *buffer, const char *path, u32 size)
|
||||
return result == FR_OK && (u32)written == size;
|
||||
}
|
||||
case FR_NO_PATH:
|
||||
// Only create the last dir in the hierarchy
|
||||
for(u32 i = 1; path[i] != 0; i++)
|
||||
if(path[i] == '/')
|
||||
{
|
||||
@ -178,6 +179,7 @@ bool fileCopy(const char *pathSrc, const char *pathDst, bool replace, void *tmpB
|
||||
}
|
||||
else if (res == FR_NO_PATH)
|
||||
{
|
||||
// Only create the last dir in the hierarchy
|
||||
const char *c;
|
||||
for (c = pathDst + strlen(pathDst); *c != '/' && c >= pathDst; --c);
|
||||
if (c >= pathDst && c - pathDst <= FF_MAX_LFN && *c != '\0')
|
||||
@ -441,21 +443,38 @@ static bool backupEssentialFiles(void)
|
||||
{
|
||||
size_t sz = sizeof(fileCopyBuffer);
|
||||
|
||||
u32 deviceID = *(vu32*)0x01FFB804;
|
||||
char pathStart[0x20];
|
||||
sprintf(pathStart, "backups/%08lX/", deviceID);
|
||||
char fullPath[0x80];
|
||||
|
||||
// Since the other funcs in this file don't create directories recursively (only the last one),
|
||||
// and nor does f_mkdir, create the directories anyway and ignore the result
|
||||
f_mkdir("backups");
|
||||
f_mkdir(pathStart);
|
||||
|
||||
bool ok = true;
|
||||
ok = ok && fileCopy("nand:/ro/sys/HWCAL0.dat", "backups/HWCAL0.dat", false, fileCopyBuffer, sz);
|
||||
ok = ok && fileCopy("nand:/ro/sys/HWCAL1.dat", "backups/HWCAL1.dat", false, fileCopyBuffer, sz);
|
||||
sprintf(fullPath, "%sHWCAL0.dat", pathStart);
|
||||
ok = ok && fileCopy("nand:/ro/sys/HWCAL0.dat", fullPath, false, fileCopyBuffer, sz);
|
||||
sprintf(fullPath, "%sHWCAL1.dat", pathStart);
|
||||
ok = ok && fileCopy("nand:/ro/sys/HWCAL1.dat", fullPath, false, fileCopyBuffer, sz);
|
||||
|
||||
ok = ok && fileCopy("nand:/rw/sys/LocalFriendCodeSeed_A", "backups/LocalFriendCodeSeed_A", false, fileCopyBuffer, sz); // often doesn't exist
|
||||
ok = ok && fileCopy("nand:/rw/sys/LocalFriendCodeSeed_B", "backups/LocalFriendCodeSeed_B", false, fileCopyBuffer, sz);
|
||||
sprintf(fullPath, "%sLocalFriendCodeSeed_A", pathStart);
|
||||
ok = ok && fileCopy("nand:/rw/sys/LocalFriendCodeSeed_A", fullPath, false, fileCopyBuffer, sz); // often doesn't exist
|
||||
sprintf(fullPath, "%sLocalFriendCodeSeed_B", pathStart);
|
||||
ok = ok && fileCopy("nand:/rw/sys/LocalFriendCodeSeed_B", fullPath, false, fileCopyBuffer, sz);
|
||||
|
||||
ok = ok && fileCopy("nand:/rw/sys/SecureInfo_A", "backups/SecureInfo_A", false, fileCopyBuffer, sz);
|
||||
ok = ok && fileCopy("nand:/rw/sys/SecureInfo_B", "backups/SecureInfo_B", false, fileCopyBuffer, sz); // often doesn't exist
|
||||
sprintf(fullPath, "%sSecureInfo_A", pathStart);
|
||||
ok = ok && fileCopy("nand:/rw/sys/SecureInfo_A", fullPath, false, fileCopyBuffer, sz);
|
||||
sprintf(fullPath, "%sSecureInfo_B", pathStart);
|
||||
ok = ok && fileCopy("nand:/rw/sys/SecureInfo_B", fullPath, false, fileCopyBuffer, sz); // often doesn't exist
|
||||
|
||||
if (!ok) return false;
|
||||
|
||||
alignedseqmemcpy(fileCopyBuffer, (const void *)0x10012000, 0x100);
|
||||
if (getFileSize("backups/otp.bin") != 0x100)
|
||||
ok = ok && fileWrite(fileCopyBuffer, "backups/otp.bin", 0x100);
|
||||
sprintf(fullPath, "%sotp.bin", pathStart);
|
||||
if (getFileSize(fullPath) != 0x100)
|
||||
ok = ok && fileWrite(fileCopyBuffer, fullPath, 0x100);
|
||||
|
||||
if (!ok) return false;
|
||||
|
||||
@ -463,19 +482,22 @@ static bool backupEssentialFiles(void)
|
||||
u8 c = mcuConsoleInfo[0];
|
||||
if (c == 2 || c == 4 || (ISN3DS && c == 5) || c == 6)
|
||||
{
|
||||
sprintf(fullPath, "%sHWCAL_01_EEPROM.dat", pathStart);
|
||||
I2C_readRegBuf(I2C_DEV_EEPROM, 0, fileCopyBuffer, 0x1000); // Up to two instances of hwcal, with the second one @0x800
|
||||
if (getFileSize("backups/HWCAL_01_EEPROM.dat") != 0x1000)
|
||||
ok = ok && fileWrite(fileCopyBuffer, "backups/HWCAL_01_EEPROM.dat", 0x1000);
|
||||
if (getFileSize(fullPath) != 0x1000)
|
||||
ok = ok && fileWrite(fileCopyBuffer, fullPath, 0x1000);
|
||||
}
|
||||
|
||||
// B9S bootrom backups
|
||||
u32 hash[32/4];
|
||||
sha(hash, (const void *)0x08080000, 0x10000, SHA_256_MODE);
|
||||
if (memcmp(hash, boot9Sha256, 32) == 0 && getFileSize("backups/boot9.bin") != 0x10000)
|
||||
ok = ok && fileWrite((const void *)0x08080000, "backups/boot9.bin", 0x10000);
|
||||
sprintf(fullPath, "%sboot9.bin", pathStart);
|
||||
if (memcmp(hash, boot9Sha256, 32) == 0 && getFileSize(fullPath) != 0x10000)
|
||||
ok = ok && fileWrite((const void *)0x08080000, fullPath, 0x10000);
|
||||
sha(hash, (const void *)0x08090000, 0x10000, SHA_256_MODE);
|
||||
if (memcmp(hash, boot11Sha256, 32) == 0 && getFileSize("backups/boot11.bin") != 0x10000)
|
||||
ok = ok && fileWrite((const void *)0x08090000, "backups/boot11.bin", 0x10000);
|
||||
sprintf(fullPath, "%sboot11.bin", pathStart);
|
||||
if (memcmp(hash, boot11Sha256, 32) == 0 && getFileSize(fullPath) != 0x10000)
|
||||
ok = ok && fileWrite((const void *)0x08090000, fullPath, 0x10000);
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
@ -40,6 +40,8 @@ typedef enum
|
||||
I2C_DEV_CAMERA = 1, // Unconfirmed
|
||||
I2C_DEV_CAMERA2 = 2, // Unconfirmed
|
||||
I2C_DEV_MCU = 3,
|
||||
I2C_DEV_LCD_TOP = 5,
|
||||
I2C_DEV_LCD_BOT = 6,
|
||||
I2C_DEV_GYRO = 10,
|
||||
I2C_DEV_DEBUG_PAD = 12,
|
||||
I2C_DEV_IR = 13,
|
||||
|
||||
@ -843,6 +843,24 @@ u32 patchLgyK11(u8 *section1, u32 section1Size, u8 *section2, u32 section2Size)
|
||||
return 1;
|
||||
*off2 = 0xE01A;
|
||||
|
||||
// Patch kernelpanic to skip devunit check, so that it sets the LCD fill regs
|
||||
// which are useful to detect such panics
|
||||
u16 *off3;
|
||||
for (off3 = (u16 *)section1; (u8 *)off3 <= section1 + section1Size && (off3[0] != 0x481D || off3[1] != 0xB570); off3++);
|
||||
if ((u8 *)off3 >= section1 + section1Size)
|
||||
return 1;
|
||||
off3[2] = 0x2001; // movs r0, #1
|
||||
|
||||
// Patch kernel to avoid allocating the two "configuration memory" pages, freeing
|
||||
// 0x2000 bytes of kernel "heap" (which is 0xD000 AXIWRAM bytes on LGY K11 instead
|
||||
// of the entire FCRAM on NFIRM). This is indeed a bug because if prevents two of the
|
||||
// 12 KThread objects from being created
|
||||
u16 *off4;
|
||||
for (off4 = (u16 *)section1; (u8 *)off4 <= section1 + section1Size && (off4[0] != 0xB570 || off4[1] != 0x2200); off4++);
|
||||
if ((u8 *)off4 >= section1 + section1Size)
|
||||
return 1;
|
||||
*off4 = 0x4770; // bx lr
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@ -70,6 +70,14 @@ void prepareArm11ForFirmlaunch(void)
|
||||
void deinitScreens(void)
|
||||
{
|
||||
if(ARESCREENSINITIALIZED) invokeArm11Function(DEINIT_SCREENS);
|
||||
|
||||
// Backlight voltage off
|
||||
I2C_writeReg(I2C_DEV_MCU, 0x22, 0x14);
|
||||
wait(50);
|
||||
|
||||
// LCD panel voltage off
|
||||
I2C_writeReg(I2C_DEV_MCU, 0x22, 0x01);
|
||||
wait(50);
|
||||
}
|
||||
|
||||
void updateBrightness(u32 brightnessIndex)
|
||||
@ -102,8 +110,31 @@ void initScreens(void)
|
||||
memcpy((void *)(ARM11_PARAMETERS_ADDRESS + 4), fbs, sizeof(fbs));
|
||||
invokeArm11Function(INIT_SCREENS);
|
||||
|
||||
//Turn on backlight
|
||||
I2C_writeReg(I2C_DEV_MCU, 0x22, 0x2A);
|
||||
// Fragile code, needs proper fix/total rewrite of the baremetal components anyway
|
||||
// Assume controller revision is not 0x00 for either screen (this revision is extremely
|
||||
// old and shouldn't be seen in retail units nor normal devunits)
|
||||
|
||||
// Controller reset off
|
||||
I2C_writeReg(I2C_DEV_LCD_TOP, 0xFE, 0xAA);
|
||||
I2C_writeReg(I2C_DEV_LCD_BOT, 0xFE, 0xAA);
|
||||
wait(5);
|
||||
|
||||
// Controller power on
|
||||
I2C_writeReg(I2C_DEV_LCD_TOP, 0x01, 0x10);
|
||||
I2C_writeReg(I2C_DEV_LCD_BOT, 0x01, 0x10);
|
||||
wait(5);
|
||||
|
||||
// Clear error flag
|
||||
I2C_writeReg(I2C_DEV_LCD_TOP, 0x60, 0x00);
|
||||
I2C_writeReg(I2C_DEV_LCD_BOT, 0x60, 0x00);
|
||||
wait(5);
|
||||
|
||||
// LCD panel (bias ?) voltage on
|
||||
I2C_writeReg(I2C_DEV_MCU, 0x22, 0x02);
|
||||
wait(50);
|
||||
|
||||
// Backlight voltage on
|
||||
I2C_writeReg(I2C_DEV_MCU, 0x22, 0x28);
|
||||
wait(5);
|
||||
}
|
||||
else updateBrightness(MULTICONFIG(BRIGHTNESS));
|
||||
@ -118,3 +149,8 @@ void initScreens(void)
|
||||
clearScreens(false);
|
||||
swapFramebuffers(false);
|
||||
}
|
||||
|
||||
void zerofillN3dsAblRegisters(void)
|
||||
{
|
||||
invokeArm11Function(ZEROFILL_N3DS_ABL_REGISTERS);
|
||||
}
|
||||
|
||||
@ -59,6 +59,7 @@ typedef enum
|
||||
SWAP_FRAMEBUFFERS,
|
||||
UPDATE_BRIGHTNESS,
|
||||
DEINIT_SCREENS,
|
||||
ZEROFILL_N3DS_ABL_REGISTERS,
|
||||
PREPARE_ARM11_FOR_FIRMLAUNCH,
|
||||
ARM11_READY,
|
||||
} Arm11Operation;
|
||||
@ -73,3 +74,4 @@ void swapFramebuffers(bool isAlternate);
|
||||
void updateBrightness(u32 brightnessIndex);
|
||||
void clearScreens(bool isAlternate);
|
||||
void initScreens(void);
|
||||
void zerofillN3dsAblRegisters(void);
|
||||
|
||||
@ -158,23 +158,44 @@ void error(const char *fmt, ...)
|
||||
mcuPowerOff();
|
||||
}
|
||||
|
||||
|
||||
// CRC-16/MODBUS
|
||||
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;
|
||||
}
|
||||
static const u16 lut[256] = {
|
||||
0x0000,0xC0C1,0xC181,0x0140,0xC301,0x03C0,0x0280,0xC241,
|
||||
0xC601,0x06C0,0x0780,0xC741,0x0500,0xC5C1,0xC481,0x0440,
|
||||
0xCC01,0x0CC0,0x0D80,0xCD41,0x0F00,0xCFC1,0xCE81,0x0E40,
|
||||
0x0A00,0xCAC1,0xCB81,0x0B40,0xC901,0x09C0,0x0880,0xC841,
|
||||
0xD801,0x18C0,0x1980,0xD941,0x1B00,0xDBC1,0xDA81,0x1A40,
|
||||
0x1E00,0xDEC1,0xDF81,0x1F40,0xDD01,0x1DC0,0x1C80,0xDC41,
|
||||
0x1400,0xD4C1,0xD581,0x1540,0xD701,0x17C0,0x1680,0xD641,
|
||||
0xD201,0x12C0,0x1380,0xD341,0x1100,0xD1C1,0xD081,0x1040,
|
||||
0xF001,0x30C0,0x3180,0xF141,0x3300,0xF3C1,0xF281,0x3240,
|
||||
0x3600,0xF6C1,0xF781,0x3740,0xF501,0x35C0,0x3480,0xF441,
|
||||
0x3C00,0xFCC1,0xFD81,0x3D40,0xFF01,0x3FC0,0x3E80,0xFE41,
|
||||
0xFA01,0x3AC0,0x3B80,0xFB41,0x3900,0xF9C1,0xF881,0x3840,
|
||||
0x2800,0xE8C1,0xE981,0x2940,0xEB01,0x2BC0,0x2A80,0xEA41,
|
||||
0xEE01,0x2EC0,0x2F80,0xEF41,0x2D00,0xEDC1,0xEC81,0x2C40,
|
||||
0xE401,0x24C0,0x2580,0xE541,0x2700,0xE7C1,0xE681,0x2640,
|
||||
0x2200,0xE2C1,0xE381,0x2340,0xE101,0x21C0,0x2080,0xE041,
|
||||
0xA001,0x60C0,0x6180,0xA141,0x6300,0xA3C1,0xA281,0x6240,
|
||||
0x6600,0xA6C1,0xA781,0x6740,0xA501,0x65C0,0x6480,0xA441,
|
||||
0x6C00,0xACC1,0xAD81,0x6D40,0xAF01,0x6FC0,0x6E80,0xAE41,
|
||||
0xAA01,0x6AC0,0x6B80,0xAB41,0x6900,0xA9C1,0xA881,0x6840,
|
||||
0x7800,0xB8C1,0xB981,0x7940,0xBB01,0x7BC0,0x7A80,0xBA41,
|
||||
0xBE01,0x7EC0,0x7F80,0xBF41,0x7D00,0xBDC1,0xBC81,0x7C40,
|
||||
0xB401,0x74C0,0x7580,0xB541,0x7700,0xB7C1,0xB681,0x7640,
|
||||
0x7200,0xB2C1,0xB381,0x7340,0xB101,0x71C0,0x7080,0xB041,
|
||||
0x5000,0x90C1,0x9181,0x5140,0x9301,0x53C0,0x5280,0x9241,
|
||||
0x9601,0x56C0,0x5780,0x9741,0x5500,0x95C1,0x9481,0x5440,
|
||||
0x9C01,0x5CC0,0x5D80,0x9D41,0x5F00,0x9FC1,0x9E81,0x5E40,
|
||||
0x5A00,0x9AC1,0x9B81,0x5B40,0x9901,0x59C0,0x5880,0x9841,
|
||||
0x8801,0x48C0,0x4980,0x8941,0x4B00,0x8BC1,0x8A81,0x4A40,
|
||||
0x4E00,0x8EC1,0x8F81,0x4F40,0x8D01,0x4DC0,0x4C80,0x8C41,
|
||||
0x4400,0x84C1,0x8581,0x4540,0x8701,0x47C0,0x4680,0x8641,
|
||||
0x8201,0x42C0,0x4380,0x8341,0x4100,0x81C1,0x8081,0x4040,
|
||||
};
|
||||
|
||||
u16 r = initialValue;
|
||||
const u8 *data8 = (const u8 *)data;
|
||||
@ -186,21 +207,40 @@ u16 crc16(const void *data, size_t size, u16 initialValue)
|
||||
|
||||
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;
|
||||
}
|
||||
static const u32 lut[256] = {
|
||||
0x00000000,0x77073096,0xEE0E612C,0x990951BA,0x076DC419,0x706AF48F,0xE963A535,0x9E6495A3,
|
||||
0x0EDB8832,0x79DCB8A4,0xE0D5E91E,0x97D2D988,0x09B64C2B,0x7EB17CBD,0xE7B82D07,0x90BF1D91,
|
||||
0x1DB71064,0x6AB020F2,0xF3B97148,0x84BE41DE,0x1ADAD47D,0x6DDDE4EB,0xF4D4B551,0x83D385C7,
|
||||
0x136C9856,0x646BA8C0,0xFD62F97A,0x8A65C9EC,0x14015C4F,0x63066CD9,0xFA0F3D63,0x8D080DF5,
|
||||
0x3B6E20C8,0x4C69105E,0xD56041E4,0xA2677172,0x3C03E4D1,0x4B04D447,0xD20D85FD,0xA50AB56B,
|
||||
0x35B5A8FA,0x42B2986C,0xDBBBC9D6,0xACBCF940,0x32D86CE3,0x45DF5C75,0xDCD60DCF,0xABD13D59,
|
||||
0x26D930AC,0x51DE003A,0xC8D75180,0xBFD06116,0x21B4F4B5,0x56B3C423,0xCFBA9599,0xB8BDA50F,
|
||||
0x2802B89E,0x5F058808,0xC60CD9B2,0xB10BE924,0x2F6F7C87,0x58684C11,0xC1611DAB,0xB6662D3D,
|
||||
0x76DC4190,0x01DB7106,0x98D220BC,0xEFD5102A,0x71B18589,0x06B6B51F,0x9FBFE4A5,0xE8B8D433,
|
||||
0x7807C9A2,0x0F00F934,0x9609A88E,0xE10E9818,0x7F6A0DBB,0x086D3D2D,0x91646C97,0xE6635C01,
|
||||
0x6B6B51F4,0x1C6C6162,0x856530D8,0xF262004E,0x6C0695ED,0x1B01A57B,0x8208F4C1,0xF50FC457,
|
||||
0x65B0D9C6,0x12B7E950,0x8BBEB8EA,0xFCB9887C,0x62DD1DDF,0x15DA2D49,0x8CD37CF3,0xFBD44C65,
|
||||
0x4DB26158,0x3AB551CE,0xA3BC0074,0xD4BB30E2,0x4ADFA541,0x3DD895D7,0xA4D1C46D,0xD3D6F4FB,
|
||||
0x4369E96A,0x346ED9FC,0xAD678846,0xDA60B8D0,0x44042D73,0x33031DE5,0xAA0A4C5F,0xDD0D7CC9,
|
||||
0x5005713C,0x270241AA,0xBE0B1010,0xC90C2086,0x5768B525,0x206F85B3,0xB966D409,0xCE61E49F,
|
||||
0x5EDEF90E,0x29D9C998,0xB0D09822,0xC7D7A8B4,0x59B33D17,0x2EB40D81,0xB7BD5C3B,0xC0BA6CAD,
|
||||
0xEDB88320,0x9ABFB3B6,0x03B6E20C,0x74B1D29A,0xEAD54739,0x9DD277AF,0x04DB2615,0x73DC1683,
|
||||
0xE3630B12,0x94643B84,0x0D6D6A3E,0x7A6A5AA8,0xE40ECF0B,0x9309FF9D,0x0A00AE27,0x7D079EB1,
|
||||
0xF00F9344,0x8708A3D2,0x1E01F268,0x6906C2FE,0xF762575D,0x806567CB,0x196C3671,0x6E6B06E7,
|
||||
0xFED41B76,0x89D32BE0,0x10DA7A5A,0x67DD4ACC,0xF9B9DF6F,0x8EBEEFF9,0x17B7BE43,0x60B08ED5,
|
||||
0xD6D6A3E8,0xA1D1937E,0x38D8C2C4,0x4FDFF252,0xD1BB67F1,0xA6BC5767,0x3FB506DD,0x48B2364B,
|
||||
0xD80D2BDA,0xAF0A1B4C,0x36034AF6,0x41047A60,0xDF60EFC3,0xA867DF55,0x316E8EEF,0x4669BE79,
|
||||
0xCB61B38C,0xBC66831A,0x256FD2A0,0x5268E236,0xCC0C7795,0xBB0B4703,0x220216B9,0x5505262F,
|
||||
0xC5BA3BBE,0xB2BD0B28,0x2BB45A92,0x5CB36A04,0xC2D7FFA7,0xB5D0CF31,0x2CD99E8B,0x5BDEAE1D,
|
||||
0x9B64C2B0,0xEC63F226,0x756AA39C,0x026D930A,0x9C0906A9,0xEB0E363F,0x72076785,0x05005713,
|
||||
0x95BF4A82,0xE2B87A14,0x7BB12BAE,0x0CB61B38,0x92D28E9B,0xE5D5BE0D,0x7CDCEFB7,0x0BDBDF21,
|
||||
0x86D3D2D4,0xF1D4E242,0x68DDB3F8,0x1FDA836E,0x81BE16CD,0xF6B9265B,0x6FB077E1,0x18B74777,
|
||||
0x88085AE6,0xFF0F6A70,0x66063BCA,0x11010B5C,0x8F659EFF,0xF862AE69,0x616BFFD3,0x166CCF45,
|
||||
0xA00AE278,0xD70DD2EE,0x4E048354,0x3903B3C2,0xA7672661,0xD06016F7,0x4969474D,0x3E6E77DB,
|
||||
0xAED16A4A,0xD9D65ADC,0x40DF0B66,0x37D83BF0,0xA9BCAE53,0xDEBB9EC5,0x47B2CF7F,0x30B5FFE9,
|
||||
0xBDBDF21C,0xCABAC28A,0x53B39330,0x24B4A3A6,0xBAD03605,0xCDD70693,0x54DE5729,0x23D967BF,
|
||||
0xB3667A2E,0xC4614AB8,0x5D681B02,0x2A6F2B94,0xB40BBE37,0xC30C8EA1,0x5A05DF1B,0x2D02EF8D,
|
||||
};
|
||||
|
||||
u32 r = initialValue;
|
||||
const u8 *data8 = (const u8 *)data;
|
||||
|
||||
@ -34,6 +34,12 @@
|
||||
|
||||
extern bool isN3DS;
|
||||
|
||||
// Note: just switch to [[attribute]] once we use clangd and cmake
|
||||
// vscode-cpptools has (or had?) some issues with C23 support
|
||||
#if __GNUC__ >= 15
|
||||
// Required by GCC 15+ but ignored (with warning) before
|
||||
__attribute__((nonstring))
|
||||
#endif
|
||||
static const char serviceList[34][8] =
|
||||
{
|
||||
"APT:U",
|
||||
|
||||
@ -148,7 +148,7 @@ static u32 findFunctionStart(u8 *code, u32 pos)
|
||||
return 0xFFFFFFFF;
|
||||
}
|
||||
|
||||
static inline bool findLayeredFsSymbols(u8 *code, u32 size, u32 *fsMountArchive, u32 *fsRegisterArchive, u32 *fsTryOpenFile, u32 *fsOpenFileDirectly)
|
||||
static inline bool findLayeredFsSymbols(u8 *code, u32 size, u32 *fsMountArchive, u32 *fsRegisterArchive, u32 *fsTryOpenFile, u32 *fsOpenFileDirectly, u32 *fsUnMountArchive)
|
||||
{
|
||||
u32 found = 0,
|
||||
*temp = NULL;
|
||||
@ -165,6 +165,12 @@ static inline bool findLayeredFsSymbols(u8 *code, u32 size, u32 *fsMountArchive,
|
||||
case 0xE24DD028:
|
||||
if(addr <= size - 16 && *fsMountArchive == 0xFFFFFFFF && addr32[1] == 0xE1A04000 && addr32[2] == 0xE59F60A8 && addr32[3] == 0xE3A0C001) temp = fsMountArchive;
|
||||
break;
|
||||
case 0xE2844001:
|
||||
if(addr <= size - 12 && *fsUnMountArchive == 0xFFFFFFFF && addr32[1] == 0xE3540020 && addr32[2] == 0x3AFFFFF0) temp = fsUnMountArchive;
|
||||
break;
|
||||
case 0xE353003A:
|
||||
if(addr <= size - 12 && *fsUnMountArchive == 0xFFFFFFFF && (addr32[1] & 0xFFFFFF0F) == 0x0A000009 && (addr32[2] & 0xFFFF0FF0) == 0xE1A00400) temp = fsUnMountArchive;
|
||||
break;
|
||||
case 0xE3500008:
|
||||
if(addr <= size - 12 && *fsRegisterArchive == 0xFFFFFFFF && (addr32[1] & 0xFFF00FF0) == 0xE1800400 && (addr32[2] & 0xFFF00FF0) == 0xE1800FC0) temp = fsRegisterArchive;
|
||||
break;
|
||||
@ -183,14 +189,14 @@ static inline bool findLayeredFsSymbols(u8 *code, u32 size, u32 *fsMountArchive,
|
||||
if(*temp != 0xFFFFFFFF)
|
||||
{
|
||||
found++;
|
||||
if(found == 4) break;
|
||||
if(found == 5) break;
|
||||
}
|
||||
|
||||
temp = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return found == 4;
|
||||
return found == 5;
|
||||
}
|
||||
|
||||
static inline bool findLayeredFsPayloadOffset(u8 *code, u32 size, u32 roSize, u32 dataSize, u32 roAddress, u32 dataAddress, u32 *payloadOffset, u32 *pathOffset, u32 *pathAddress)
|
||||
@ -478,7 +484,7 @@ static inline bool loadTitleLocaleConfig(u64 progId, u8 *mask, u8 *regionId, u8
|
||||
if(R_FAILED(IFile_GetSize(&file, &fileSize)) || fileSize < 3) goto exit;
|
||||
if(fileSize >= 12) fileSize = 12;
|
||||
|
||||
char buf[12] = "------------";
|
||||
char buf[12+1] = "------------";
|
||||
u64 total;
|
||||
|
||||
if(R_FAILED(IFile_Read(&file, &total, buf, fileSize))) goto exit;
|
||||
@ -569,6 +575,7 @@ static inline bool patchLayeredFs(u64 progId, u8 *code, u32 size, u32 textSize,
|
||||
if(!archiveId) return true;
|
||||
|
||||
u32 fsMountArchive = 0xFFFFFFFF,
|
||||
fsUnMountArchive = 0xFFFFFFFF,
|
||||
fsRegisterArchive = 0xFFFFFFFF,
|
||||
fsTryOpenFile = 0xFFFFFFFF,
|
||||
fsOpenFileDirectly = 0xFFFFFFFF,
|
||||
@ -576,7 +583,7 @@ static inline bool patchLayeredFs(u64 progId, u8 *code, u32 size, u32 textSize,
|
||||
pathOffset = 0,
|
||||
pathAddress = 0xDEADCAFE;
|
||||
|
||||
if(!findLayeredFsSymbols(code, textSize, &fsMountArchive, &fsRegisterArchive, &fsTryOpenFile, &fsOpenFileDirectly) ||
|
||||
if(!findLayeredFsSymbols(code, textSize, &fsMountArchive, &fsRegisterArchive, &fsTryOpenFile, &fsOpenFileDirectly, &fsUnMountArchive) ||
|
||||
!findLayeredFsPayloadOffset(code, textSize, roSize, dataSize, roAddress, dataAddress, &payloadOffset, &pathOffset, &pathAddress)) return false;
|
||||
|
||||
static const char *updateRomFsMounts[] = { "ro2:",
|
||||
@ -624,8 +631,9 @@ static inline bool patchLayeredFs(u64 progId, u8 *code, u32 size, u32 textSize,
|
||||
romfsRedirPatchSubstituted2 = *(u32 *)(code + fsTryOpenFile);
|
||||
romfsRedirPatchHook2 = MAKE_BRANCH(payloadOffset + (u32)&romfsRedirPatchHook2 - (u32)romfsRedirPatch, fsTryOpenFile + 4);
|
||||
romfsRedirPatchCustomPath = pathAddress;
|
||||
romfsRedirPatchFsMountArchive = 0x100000 + fsMountArchive;
|
||||
romfsRedirPatchFsRegisterArchive = 0x100000 + fsRegisterArchive;
|
||||
romfsRedirPatchFsMountArchive = MAKE_BRANCH_LINK(payloadOffset + (u32)&romfsRedirPatchFsMountArchive - (u32)romfsRedirPatch, fsMountArchive);
|
||||
romfsRedirPatchFsUnMountArchive = MAKE_BRANCH_LINK(payloadOffset + (u32)&romfsRedirPatchFsUnMountArchive - (u32)romfsRedirPatch, fsUnMountArchive);
|
||||
romfsRedirPatchFsRegisterArchive = MAKE_BRANCH_LINK(payloadOffset + (u32)&romfsRedirPatchFsRegisterArchive - (u32)romfsRedirPatch, fsRegisterArchive);
|
||||
romfsRedirPatchArchiveId = archiveId;
|
||||
memcpy(&romfsRedirPatchUpdateRomFsMount, updateRomFsMount, 4);
|
||||
|
||||
@ -726,8 +734,9 @@ void patchCode(u64 progId, u16 progVer, u8 *code, u32 size, u32 textSize, u32 ro
|
||||
progId == 0x0004001000022000LL || //EUR MSET
|
||||
progId == 0x0004001000026000LL || //CHN MSET
|
||||
progId == 0x0004001000027000LL || //KOR MSET
|
||||
progId == 0x0004001000028000LL) //TWN MSET
|
||||
&& CONFIG(PATCHVERSTRING))
|
||||
progId == 0x0004001000028000LL)) //TWN MSET
|
||||
{
|
||||
if (CONFIG(PATCHVERSTRING))
|
||||
{
|
||||
static const u16 pattern[] = u"Ve";
|
||||
const u16 *patch;
|
||||
@ -755,6 +764,37 @@ void patchCode(u64 progId, u16 progVer, u8 *code, u32 size, u32 textSize, u32 ro
|
||||
)) goto error;
|
||||
}
|
||||
|
||||
// Allow date picker to select year up to 2099, not just 2050.
|
||||
// NNID user's year-of-birth seems to have a similar restriction,
|
||||
// I'm not removing that as long as any NNID stuff is still active.
|
||||
|
||||
// Patch date picker check on entry (date load):
|
||||
// Look for:
|
||||
// 32 00 5x E3 CMP Rx, #0x32
|
||||
// ...
|
||||
// 32 x0 A0 C3 MOVGT Rx, #0x32
|
||||
u32 *off = (u32 *)code;
|
||||
for (; (u8 *)off < code + textSize && ((off[0] & 0xFFF0FFFF) != 0xE3500032 || (off[2] & 0xFFFF0FFF) != 0xC3A00032); off++)
|
||||
{
|
||||
if (((off[0] >> 16) & 0xF) != ((off[2] >> 12) & 0xF)) // ensure same register used
|
||||
continue;
|
||||
}
|
||||
if ((u8 *)off >= code + textSize) goto error;
|
||||
off[0] = (off[0] & ~0xFF) | 99;
|
||||
off[2] = (off[2] & ~0xFF) | 99;
|
||||
|
||||
// Patch date picker actions:
|
||||
// Look for:
|
||||
// 01 00 80 E2 ADD R0, R0, #1
|
||||
// 32 00 50 E3 CMP R0, #0x32
|
||||
off = (u32 *)code;
|
||||
for (; (u8 *)off < code + textSize && (off[0] != 0xE2800001 || off[1] != 0xE3500032); off++);
|
||||
if ((u8 *)off >= code + textSize) goto error;
|
||||
|
||||
off[1] = (off[1] & ~0xFF) | 99; // patch increment wrap-around compare instruction
|
||||
off[9] = (off[9] & ~0xFF) | 99; // patch decrement wrap-around conditional move instruction
|
||||
}
|
||||
|
||||
else if(progId == 0x0004013000008002LL) //NS
|
||||
{
|
||||
if(progVer > 4)
|
||||
|
||||
@ -10,6 +10,7 @@ extern u32 romfsRedirPatchSubstituted2, romfsRedirPatchHook2;
|
||||
|
||||
extern u32 romfsRedirPatchArchiveName;
|
||||
extern u32 romfsRedirPatchFsMountArchive;
|
||||
extern u32 romfsRedirPatchFsUnMountArchive;
|
||||
extern u32 romfsRedirPatchFsRegisterArchive;
|
||||
extern u32 romfsRedirPatchArchiveId;
|
||||
extern u32 romfsRedirPatchRomFsMount;
|
||||
|
||||
@ -30,17 +30,23 @@ romfsRedirPatch:
|
||||
cmp r3, #3
|
||||
bne romfsRedirPatchSubstituted1
|
||||
stmfd sp!, {r0-r4, lr}
|
||||
adr r0, romfsRedirPatchArchiveName
|
||||
.global romfsRedirPatchFsUnMountArchive
|
||||
romfsRedirPatchFsUnMountArchive:
|
||||
.word 0xdead0004
|
||||
sub sp, sp, #4
|
||||
ldr r1, romfsRedirPatchArchiveId
|
||||
mov r0, sp
|
||||
ldr r4, romfsRedirPatchFsMountArchive
|
||||
blx r4
|
||||
.global romfsRedirPatchFsMountArchive
|
||||
romfsRedirPatchFsMountArchive:
|
||||
.word 0xdead0005
|
||||
mov r3, #0
|
||||
mov r2, #0
|
||||
ldr r1, [sp]
|
||||
adr r0, romfsRedirPatchArchiveName
|
||||
ldr r4, romfsRedirPatchFsRegisterArchive
|
||||
blx r4
|
||||
.global romfsRedirPatchFsRegisterArchive
|
||||
romfsRedirPatchFsRegisterArchive:
|
||||
.word 0xdead0006
|
||||
add sp, sp, #4
|
||||
ldmfd sp!, {r0-r4, lr}
|
||||
b romfsRedirPatchSubstituted1
|
||||
@ -108,20 +114,16 @@ romfsRedirPatch:
|
||||
.balign 4
|
||||
|
||||
.global romfsRedirPatchArchiveName
|
||||
.global romfsRedirPatchFsMountArchive
|
||||
.global romfsRedirPatchFsRegisterArchive
|
||||
.global romfsRedirPatchArchiveId
|
||||
.global romfsRedirPatchRomFsMount
|
||||
.global romfsRedirPatchUpdateRomFsMount
|
||||
.global romfsRedirPatchCustomPath
|
||||
|
||||
romfsRedirPatchArchiveName : .ascii "lf:\0"
|
||||
romfsRedirPatchFsMountArchive : .word 0xdead0005
|
||||
romfsRedirPatchFsRegisterArchive : .word 0xdead0006
|
||||
romfsRedirPatchArchiveId : .word 0xdead0007
|
||||
romfsRedirPatchRomFsMount : .ascii "rom:"
|
||||
romfsRedirPatchUpdateRomFsMount : .word 0xdead0008
|
||||
romfsRedirPatchCustomPath : .word 0xdead0004
|
||||
romfsRedirPatchCustomPath : .word 0xdead0009
|
||||
|
||||
_romfsRedirPatchEnd:
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -62,7 +62,7 @@ typedef struct MenuItem {
|
||||
typedef struct Menu {
|
||||
const char *title;
|
||||
|
||||
MenuItem items[24];
|
||||
MenuItem items[25];
|
||||
} Menu;
|
||||
|
||||
extern u32 menuCombo;
|
||||
|
||||
@ -36,6 +36,7 @@ void RosalinaMenu_TakeScreenshot(void);
|
||||
void RosalinaMenu_ShowCredits(void);
|
||||
void RosalinaMenu_ProcessList(void);
|
||||
void RosalinaMenu_SaveSettings(void);
|
||||
void RosalinaMenu_ReturnToHomeMenu(void);
|
||||
void RosalinaMenu_Cheats(void);
|
||||
|
||||
void RosalinaMenu_PowerOffOrReboot(void);
|
||||
|
||||
@ -56,10 +56,12 @@ typedef struct
|
||||
s32* plgldrEvent; ///< Used for synchronization
|
||||
s32* plgldrReply; ///< Used for synchronization
|
||||
u8 notifyHomeEvent;
|
||||
u8 padding[3];
|
||||
u32 reserved[23];
|
||||
u8 padding[7];
|
||||
u64 waitForReplyTimeout;
|
||||
u32 reserved[20];
|
||||
u32 config[32];
|
||||
} PluginHeader;
|
||||
_Static_assert(sizeof(PluginHeader) == 0x100, "Invalid PluginHeader size");
|
||||
|
||||
typedef void (*OnPlgLdrEventCb_t)(s32 eventType);
|
||||
|
||||
|
||||
@ -30,6 +30,7 @@
|
||||
#include <3ds/srv.h>
|
||||
#include <3ds/result.h>
|
||||
#include <3ds/ipc.h>
|
||||
#include <assert.h>
|
||||
#include "csvc.h"
|
||||
#include "luma_shared_config.h"
|
||||
|
||||
|
||||
@ -268,14 +268,18 @@ static inline void Draw_WriteUnaligned(u8 *dst, u32 tmp, u32 size)
|
||||
void Draw_CreateBitmapHeader(u8 *dst, u32 width, u32 heigth)
|
||||
{
|
||||
static const u8 bmpHeaderTemplate[54] = {
|
||||
0x42, 0x4D, 0xCC, 0xCC, 0xCC, 0xCC, 0x00, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, 0x28, 0x00,
|
||||
0x00, 0x00, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0x01, 0x00, 0x18, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0xCC, 0xCC, 0xCC, 0xCC, 0x12, 0x0B, 0x00, 0x00, 0x12, 0x0B, 0x00, 0x00, 0x00, 0x00,
|
||||
// BITMAPFILEHEADER
|
||||
0x42, 0x4D, 0xCC, 0xCC, 0xCC, 0xCC, 0x00, 0x00, 0x00, 0x00, 0x40 /* data offset */, 0x00, 0x00, 0x00,
|
||||
|
||||
// BITMAPINFOHEADER
|
||||
0x28, 0x00, 0x00, 0x00, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0x01, 0x00, 0x18, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0xCC, 0xCC, 0xCC, 0xCC, 0x12, 0x0B, 0x00, 0x00, 0x12, 0x0B, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00
|
||||
};
|
||||
|
||||
memcpy(dst, bmpHeaderTemplate, 54);
|
||||
Draw_WriteUnaligned(dst + 2, 54 + 3 * width * heigth, 4);
|
||||
memset(dst + 54, 0, 64 - 54);
|
||||
Draw_WriteUnaligned(dst + 2, 64 + 3 * width * heigth, 4);
|
||||
Draw_WriteUnaligned(dst + 0x12, width, 4);
|
||||
Draw_WriteUnaligned(dst + 0x16, heigth, 4);
|
||||
Draw_WriteUnaligned(dst + 0x22, 3 * width * heigth, 4);
|
||||
|
||||
@ -204,7 +204,7 @@ void LumaConfig_RequestSaveSettings(void) {
|
||||
|
||||
Result LumaConfig_SaveSettings(void)
|
||||
{
|
||||
char inibuf[0x2000];
|
||||
char inibuf[0x2000 + 0x400]; // eyeballed. TODO use #embed
|
||||
|
||||
Result res;
|
||||
|
||||
@ -259,6 +259,12 @@ Result LumaConfig_SaveSettings(void)
|
||||
configData.autobootCtrAppmemtype = autobootCtrAppmemtype;
|
||||
|
||||
size_t n = LumaConfig_SaveLumaIniConfigToStr(inibuf, &configData);
|
||||
|
||||
// FIXME: this is UB we should port snprintf sometime (as well as fix other tech debt in Rosalina)
|
||||
if (n + 1 >= sizeof(inibuf)) {
|
||||
__builtin_trap();
|
||||
}
|
||||
|
||||
FS_ArchiveID archiveId = isSdMode ? ARCHIVE_SDMC : ARCHIVE_NAND_RW;
|
||||
if (n > 0)
|
||||
res = IFile_Open(&file, archiveId, fsMakePath(PATH_EMPTY, ""), fsMakePath(PATH_ASCII, "/luma/config.ini"), FS_OPEN_CREATE | FS_OPEN_WRITE);
|
||||
|
||||
@ -104,8 +104,13 @@ u32 getMinLuminancePreset(void)
|
||||
|
||||
u32 getMaxLuminancePreset(void)
|
||||
{
|
||||
// Unlike SetLuminanceLevel, SetLuminance doesn't
|
||||
// check if preset <= 5, and actually allows the lumiance
|
||||
// levels provisioned for the "brightness boost mode" (brighter
|
||||
// when adapter is plugged in), even when the feature is disabled
|
||||
// (it is disabled for anything but the OG model, iirc)
|
||||
readCalibration();
|
||||
return s_blPwmData.luminanceLevels[s_blPwmData.numLevels - 1];
|
||||
return s_blPwmData.luminanceLevels[6];
|
||||
}
|
||||
|
||||
u32 getCurrentLuminance(bool top)
|
||||
@ -114,7 +119,8 @@ u32 getCurrentLuminance(bool top)
|
||||
|
||||
readCalibration();
|
||||
|
||||
const float *coeffs = s_blPwmData.coeffs[top ? (isN3DS ? 2 : 1) : 0];
|
||||
bool is3d = (REG32(0x10202000 + 0x000) & 1) != 0;
|
||||
const float *coeffs = s_blPwmData.coeffs[top ? (is3d ? 2 : 1) : 0];
|
||||
u32 brightness = REG32(regbase + 0x40);
|
||||
float ratio = getPwmRatio(s_blPwmData.brightnessMax, REG32(regbase + 0x44));
|
||||
|
||||
|
||||
@ -55,6 +55,7 @@ Menu rosalinaMenu = {
|
||||
{ "System configuration...", MENU, .menu = &sysconfigMenu },
|
||||
{ "Miscellaneous options...", MENU, .menu = &miscellaneousMenu },
|
||||
{ "Save settings", METHOD, .method = &RosalinaMenu_SaveSettings },
|
||||
{ "Return To Home Menu", METHOD, .method = &RosalinaMenu_ReturnToHomeMenu },
|
||||
{ "Power off / reboot", METHOD, .method = &RosalinaMenu_PowerOffOrReboot },
|
||||
{ "System info", METHOD, .method = &RosalinaMenu_ShowSystemInfo },
|
||||
{ "Credits", METHOD, .method = &RosalinaMenu_ShowCredits },
|
||||
@ -227,7 +228,7 @@ void RosalinaMenu_ShowCredits(void)
|
||||
Draw_Lock();
|
||||
Draw_DrawString(10, 10, COLOR_TITLE, "Rosalina -- Luma3DS credits");
|
||||
|
||||
u32 posY = Draw_DrawString(10, 30, COLOR_WHITE, "Luma3DS (c) 2016-2024 AuroraWright, TuxSH") + SPACING_Y;
|
||||
u32 posY = Draw_DrawString(10, 30, COLOR_WHITE, "Luma3DS (c) 2016-2025 AuroraWright, TuxSH") + SPACING_Y;
|
||||
|
||||
posY = Draw_DrawString(10, posY + SPACING_Y, COLOR_WHITE, "3DSX loading code by fincs");
|
||||
posY = Draw_DrawString(10, posY + SPACING_Y, COLOR_WHITE, "Networking code & basic GDB functionality by Stary");
|
||||
@ -273,7 +274,8 @@ static Result RosalinaMenu_WriteScreenshot(IFile *file, u32 width, bool top, boo
|
||||
|
||||
u8 *buf = framebufferCache;
|
||||
Draw_CreateBitmapHeader(framebufferCache, width, numLinesScaled);
|
||||
buf += 54;
|
||||
const u32 headerSize = 0x40;
|
||||
buf += headerSize;
|
||||
|
||||
u32 y = 0;
|
||||
// Our buffer might be smaller than the size of the screenshot...
|
||||
@ -287,7 +289,7 @@ static Result RosalinaMenu_WriteScreenshot(IFile *file, u32 width, bool top, boo
|
||||
|
||||
s64 t1 = svcGetSystemTick();
|
||||
timeSpentConvertingScreenshot += t1 - t0;
|
||||
TRY(IFile_Write(file, &total, framebufferCache, (y == 0 ? 54 : 0) + lineSize * nlines * scaleFactorY, 0)); // don't forget to write the header
|
||||
TRY(IFile_Write(file, &total, framebufferCache, (y == 0 ? headerSize : 0) + lineSize * nlines * scaleFactorY, 0)); // don't forget to write the header
|
||||
timeSpentWritingScreenshot += svcGetSystemTick() - t1;
|
||||
|
||||
y += nlines;
|
||||
@ -300,6 +302,35 @@ static Result RosalinaMenu_WriteScreenshot(IFile *file, u32 width, bool top, boo
|
||||
return res;
|
||||
}
|
||||
|
||||
void RosalinaMenu_ReturnToHomeMenu(void)
|
||||
{
|
||||
Draw_Lock();
|
||||
Draw_ClearFramebuffer();
|
||||
Draw_FlushFramebuffer();
|
||||
Draw_Unlock();
|
||||
|
||||
do
|
||||
{
|
||||
Draw_Lock();
|
||||
Draw_DrawString(10, 10, COLOR_TITLE, "Return to Home Menu");
|
||||
Draw_DrawString(10, 30, COLOR_WHITE, "Press A to confirm.\nPress B to go back.");
|
||||
Draw_FlushFramebuffer();
|
||||
Draw_Unlock();
|
||||
|
||||
u32 pressed = waitInputWithTimeout(1000);
|
||||
|
||||
if(pressed & KEY_A)
|
||||
{
|
||||
menuLeave();
|
||||
srvPublishToSubscriber(0x204, 0);
|
||||
return;
|
||||
}
|
||||
else if(pressed & KEY_B)
|
||||
return;
|
||||
}
|
||||
while(!menuShouldExit);
|
||||
}
|
||||
|
||||
void RosalinaMenu_TakeScreenshot(void)
|
||||
{
|
||||
IFile file = {0};
|
||||
|
||||
@ -367,11 +367,6 @@ void MiscellaneousMenu_UpdateTimeDateNtp(void)
|
||||
}
|
||||
}
|
||||
|
||||
Draw_Lock();
|
||||
Draw_ClearFramebuffer();
|
||||
Draw_FlushFramebuffer();
|
||||
Draw_Unlock();
|
||||
|
||||
do
|
||||
{
|
||||
Draw_Lock();
|
||||
|
||||
@ -420,8 +420,30 @@ static Result SysConfigMenu_ApplyVolumeOverride(void)
|
||||
s8 i2s2Volume;
|
||||
if (currVolumeSliderOverride >= 0)
|
||||
{
|
||||
i2s1Volume = -128 + (((float)currVolumeSliderOverride/100.f) * 108);
|
||||
i2s2Volume = i2s1Volume;
|
||||
// Considering I found this table inside MCU fw bin (at around offset 0x1200 in the raw bin):
|
||||
// 127, 126, 125, ... 56
|
||||
// which corresponds to round(127 - 71 * (i/63)) modulo some rounding error, it is certain
|
||||
// that the MCU writes to "Page 0/Register 117: VOL/MICDET-Pin Gain" using linear interpolation
|
||||
// to map the slider position (0..63) to the raw gain values, using 127 ("reserved") as "mute".
|
||||
// Indeed, the value used in all calibration data for shutter volume is -10 dB, which maps to 56.
|
||||
|
||||
// However, if you look at the definition of reg 0,117 closely, you will notice that the mapping
|
||||
// to the 7-bit value is piecewise, with half the resolution (double the slope) for values mapping
|
||||
// to -28 dB or lower, which means that the slider increases volume twice as fast below 50% position.
|
||||
|
||||
// LERP like MCU does, round to nearest integer
|
||||
u8 rawPinGain = 127 - (71 * currVolumeSliderOverride + 50) / 100;
|
||||
s8 volume;
|
||||
|
||||
if (rawPinGain <= 90)
|
||||
volume = 36 - rawPinGain;
|
||||
else if (rawPinGain >= 91 && rawPinGain <= 126)
|
||||
volume = 126 - 2 * rawPinGain;
|
||||
else
|
||||
volume = -128; // mute
|
||||
|
||||
i2s1Volume = volume;
|
||||
i2s2Volume = volume;
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -560,7 +582,7 @@ void SysConfigMenu_ChangeScreenBrightness(void)
|
||||
posY = Draw_DrawString(10, posY, COLOR_WHITE, "Press A to start, B to exit.\n\n");
|
||||
|
||||
posY = Draw_DrawString(10, posY, COLOR_RED, "WARNING: \n");
|
||||
posY = Draw_DrawString(10, posY, COLOR_WHITE, " * value will be limited by the presets.\n");
|
||||
posY = Draw_DrawString(10, posY, COLOR_WHITE, " * value will be limited by calibration.\n");
|
||||
posY = Draw_DrawString(10, posY, COLOR_WHITE, " * bottom framebuffer will be restored until\nyou exit.");
|
||||
Draw_FlushFramebuffer();
|
||||
Draw_Unlock();
|
||||
|
||||
@ -270,6 +270,7 @@ bool TryToLoadPlugin(Handle process, bool isHomebrew)
|
||||
}
|
||||
|
||||
pluginHeader->version = header->version;
|
||||
pluginHeader->waitForReplyTimeout = 10000000000ULL;
|
||||
// Code size must be page aligned
|
||||
exeHdr = &header->executable;
|
||||
pluginHeader->exeSize = (sizeof(PluginHeader) + exeHdr->codeSize + exeHdr->rodataSize + exeHdr->dataSize + exeHdr->bssSize + 0x1000) & ~0xFFF;
|
||||
|
||||
@ -113,7 +113,7 @@ Result PLGLDR__SetPluginLoadParameters(PluginLoadParameters *parameters)
|
||||
}
|
||||
return res;
|
||||
}
|
||||
void Flash(u32 color);
|
||||
|
||||
Result PLGLDR__DisplayMenu(PluginMenu *menu)
|
||||
{
|
||||
Result res = 0;
|
||||
@ -136,7 +136,6 @@ Result PLGLDR__DisplayMenu(PluginMenu *menu)
|
||||
{
|
||||
res = cmdbuf[1];
|
||||
}
|
||||
else Flash(0xFF0000);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
@ -20,9 +20,6 @@ static const char *g_title = "Plugin loader";
|
||||
PluginLoaderContext PluginLoaderCtx;
|
||||
extern u32 g_blockMenuOpen;
|
||||
|
||||
void IR__Patch(void);
|
||||
void IR__Unpatch(void);
|
||||
|
||||
void PluginLoader__Init(void)
|
||||
{
|
||||
PluginLoaderContext *ctx = &PluginLoaderCtx;
|
||||
@ -136,10 +133,6 @@ Result PluginLoader__SetMode3AppMode(bool enable)
|
||||
}
|
||||
static void j_PluginLoader__SetMode3AppMode(void* arg) {(void)arg; PluginLoader__SetMode3AppMode(false);}
|
||||
|
||||
void CheckMemory(void);
|
||||
|
||||
void PLG__NotifyEvent(PLG_Event event, bool signal);
|
||||
|
||||
void PluginLoader__HandleCommands(void *_ctx)
|
||||
{
|
||||
(void)_ctx;
|
||||
@ -176,8 +169,6 @@ void PluginLoader__HandleCommands(void *_ctx)
|
||||
}
|
||||
REG32(0x10202204) = 0;
|
||||
}
|
||||
//if (!ctx->userLoadParameters.noIRPatch)
|
||||
// IR__Patch();
|
||||
PLG__SetConfigMemoryStatus(PLG_CFG_RUNNING);
|
||||
}
|
||||
else
|
||||
@ -516,7 +507,7 @@ void PLG__WaitForReply(void)
|
||||
{
|
||||
if (PluginLoaderCtx.eventsSelfManaged) return;
|
||||
__strex__(PluginLoaderCtx.plgReplyPA, PLG_WAIT);
|
||||
svcArbitrateAddress(PluginLoaderCtx.arbiter, (u32)PluginLoaderCtx.plgReplyPA, ARBITRATION_WAIT_IF_LESS_THAN_TIMEOUT, PLG_OK, 10000000000ULL);
|
||||
svcArbitrateAddress(PluginLoaderCtx.arbiter, (u32)PluginLoaderCtx.plgReplyPA, ARBITRATION_WAIT_IF_LESS_THAN_TIMEOUT, PLG_OK, MemoryBlock__GetMappedPluginHeader()->waitForReplyTimeout);
|
||||
}
|
||||
|
||||
void PLG__SetConfigMemoryStatus(u32 status)
|
||||
@ -561,8 +552,6 @@ static void WaitForProcessTerminated(void *arg)
|
||||
ctx->isMemPrivate = false;
|
||||
g_blockMenuOpen = 0;
|
||||
MemoryBlock__ResetSwapSettings();
|
||||
//if (!ctx->userLoadParameters.noIRPatch)
|
||||
// IR__Unpatch();
|
||||
}
|
||||
|
||||
void PluginLoader__HandleKernelEvent(u32 notifId)
|
||||
@ -648,7 +637,6 @@ void PluginLoader__HandleKernelEvent(u32 notifId)
|
||||
}
|
||||
ctx->pluginIsHome = !ctx->pluginIsHome;
|
||||
}
|
||||
|
||||
}
|
||||
srvPublishToSubscriber(0x1002, 0);
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user