Compare commits

...

13 Commits

Author SHA1 Message Date
TuxSH
e35972ea82 rosalina: fix volume slider override value calculation
Below 50%, the volume slider moves the dB value twice as fast. When
linearly interpolating the value in dB (lerp in log-scale), we need to
account for that fact to match how the MCU firmware handles the actual
volume slider.
2026-02-11 02:41:03 +01:00
fangrong
21f0d64ee8 rosalina: Further fix for ReturnToHomeMenu 2026-01-21 22:41:35 +01:00
TuxSH
7f1dd962af Bump ini output buffer size and add checks 2026-01-19 18:26:59 +01:00
TuxSH
a608ad8241 Add patch that removes the Year 2050 limit in System Settings date picker
This restriction was completely artificial, rest of system supports 01
Jan 2000 - 31 Dec 2099 just fine.

NNID user's year-of-birth seems to have a similar Year 2050 restriction,
I'm not removing that as long as any NNID stuff is still active.
2026-01-06 01:23:38 +01:00
fangrong
c04c7254ed plgloader: Code cleanup 2025-12-26 23:40:25 +01:00
Angel Martin
db639a80c9
Add "Return to Home Menu" button for consoles with broken Home Buttons (#2143) 2025-12-12 21:39:16 +01:00
fangrong
4cfca3c55d Optimize the ASM code of romfsredir, saving 2 bytes so that it does not exceed the size of throwFatalError function 2025-12-12 21:09:47 +01:00
fangrong
d3d9a63bf2 lfs: Call UnMountArchive before Mount, Prevent potential issues in the RegisterArchive interface 2025-12-12 21:09:47 +01:00
PabloMK7
407c18e6fd
plugin_loader: Make plugin reply timeout configurable (#2183) 2025-09-26 17:34:31 +02:00
TuxSH
456a6b4ad7 arm9/util: use hardcoded crc luts 2025-09-08 03:29:23 +02:00
TuxSH
b3282abbf7 Patch lgy k11 to avoid allocating unused pages (bug) which was preventing 2 threads to be created 2025-08-04 18:01:14 +02:00
TuxSH
119499d28d Patch lgy k11 to skip devunit check on kernelpanic, allowing red screens to be displayed on panic
L+R+Start+Select to remove LCD fill
2025-08-04 18:00:47 +02:00
TuxSH
12b15812f2 rosalina: make sure pixel data is aligned in screenshots 2025-07-30 12:53:46 +02:00
16 changed files with 269 additions and 105 deletions

View File

@ -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);
}

View File

@ -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;
}

View File

@ -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;

View File

@ -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)
@ -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,33 +734,65 @@ 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
{
static const u16 pattern[] = u"Ve";
const u16 *patch;
u32 patchSize = 0,
currentNand = BOOTCFG_NAND;
u16 customVerString[19];
loadCustomVerString(customVerString, &patchSize, currentNand);
if(patchSize != 0) patch = customVerString;
else
if (CONFIG(PATCHVERSTRING))
{
patchSize = 8;
static const u16 pattern[] = u"Ve";
const u16 *patch;
u32 patchSize = 0,
currentNand = BOOTCFG_NAND;
static const u16 *const verStringNandEmu[] = { u" Emu", u"Emu2", u"Emu3", u"Emu4" };
patch = currentNand == 0 ? u" Sys" : verStringNandEmu[BOOTCFG_EMUINDEX];
u16 customVerString[19];
loadCustomVerString(customVerString, &patchSize, currentNand);
if(patchSize != 0) patch = customVerString;
else
{
patchSize = 8;
static const u16 *const verStringNandEmu[] = { u" Emu", u"Emu2", u"Emu3", u"Emu4" };
patch = currentNand == 0 ? u" Sys" : verStringNandEmu[BOOTCFG_EMUINDEX];
}
//Patch Ver. string
if(!patchMemory(code, textSize,
pattern,
sizeof(pattern) - 2, 0,
patch,
patchSize, 1
)) goto error;
}
//Patch Ver. string
if(!patchMemory(code, textSize,
pattern,
sizeof(pattern) - 2, 0,
patch,
patchSize, 1
)) 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

View File

@ -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;

View File

@ -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:

View File

@ -62,7 +62,7 @@ typedef struct MenuItem {
typedef struct Menu {
const char *title;
MenuItem items[24];
MenuItem items[25];
} Menu;
extern u32 menuCombo;

View File

@ -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);

View File

@ -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;
} PluginHeader;
_Static_assert(sizeof(PluginHeader) == 0x100, "Invalid PluginHeader size");
typedef void (*OnPlgLdrEventCb_t)(s32 eventType);

View File

@ -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"

View File

@ -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);

View File

@ -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);

View File

@ -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 },
@ -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};

View File

@ -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
{

View File

@ -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;

View File

@ -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;
@ -96,7 +93,7 @@ void PluginLoader__UpdateMenu(void)
static ControlApplicationMemoryModeOverrideConfig g_memorymodeoverridebackup = { 0 };
Result PluginLoader__SetMode3AppMode(bool enable)
{
Handle loaderHandle;
Handle loaderHandle;
Result res = srvGetServiceHandle(&loaderHandle, "Loader");
if (R_FAILED(res)) return res;
@ -131,15 +128,11 @@ Result PluginLoader__SetMode3AppMode(bool enable)
}
}
svcCloseHandle(loaderHandle);
svcCloseHandle(loaderHandle);
return res;
}
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);
}