mirror of
https://github.com/LumaTeam/Luma3DS.git
synced 2026-02-22 09:54:38 +00:00
Compare commits
No commits in common. "master" and "v13.2.1" have entirely different histories.
@ -195,28 +195,6 @@ 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;
|
||||
@ -245,9 +223,6 @@ 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,7 +60,6 @@ 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 + 0x400]; // eyeballed. TODO use #embed
|
||||
static char tmpIniBuffer[0x2000];
|
||||
|
||||
static bool readLumaIniConfig(void)
|
||||
{
|
||||
@ -709,13 +709,6 @@ 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,7 +30,6 @@
|
||||
#include "config.h"
|
||||
#include "fs.h"
|
||||
#include "i2c.h"
|
||||
#include "screen.h"
|
||||
|
||||
u8 *loadDeliverArg(void)
|
||||
{
|
||||
@ -72,7 +71,7 @@ u8 *loadDeliverArg(void)
|
||||
bool hasMagic = memcmp(tlnc, "TLNC", 4) == 0;
|
||||
u8 crcLen = tlnc[5];
|
||||
u16 crc = *(u16 *)(tlnc + 6);
|
||||
if (!hasMagic || (8 + crcLen) > 0x100 || crc != crc16(tlnc + 8, crcLen, 0xFFFF))
|
||||
if (!hasMagic || crcLen <= 248 || crc != crc16(tlnc + 8, crcLen, 0xFFFF))
|
||||
memset(tlnc, 0, 0x100);
|
||||
|
||||
memset(deliverArg + 0x400, 0, 0xC00);
|
||||
@ -142,8 +141,6 @@ 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
|
||||
@ -201,29 +198,6 @@ 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;
|
||||
|
||||
@ -111,23 +111,6 @@ void locateEmuNand(FirmwareSource *nandType, u32 *emunandIndex, bool configureCt
|
||||
else *nandType = FIRMWARE_SYSNAND;
|
||||
}
|
||||
|
||||
static inline u32 getProtoSdmmc(u32 *sdmmc, u32 firmVersion)
|
||||
{
|
||||
switch(firmVersion)
|
||||
{
|
||||
case 243: // SDK 0.9.x (0.9.7?)
|
||||
*sdmmc = (0x080AAA28 + 0x4e0);
|
||||
break;
|
||||
case 238: // SDK 0.10
|
||||
*sdmmc = (0x080BEA70 + 0x690);
|
||||
break;
|
||||
default:
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline u32 getOldSdmmc(u32 *sdmmc, u32 firmVersion)
|
||||
{
|
||||
switch(firmVersion)
|
||||
@ -183,111 +166,6 @@ static inline u32 patchNandRw(u8 *pos, u32 size, u32 hookAddr)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline u32 patchProtoNandRw(u8 *pos, u32 size, u32 hookAddr, u32 hookCidAddr)
|
||||
{
|
||||
//Look for read/write code
|
||||
static const u8 pattern[] = {
|
||||
0x03, 0x00, 0x51, 0xE3, // cmp r1, #3
|
||||
0x02, 0xC0, 0xA0, 0xE1, // mov r12, r2
|
||||
0x04, 0x00, 0x80, 0xE2, // add r0, r0, #4
|
||||
};
|
||||
|
||||
u32 *writeOffset = (u32 *)memsearch(pos, pattern, size, sizeof(pattern));
|
||||
|
||||
if(writeOffset == NULL) return 1;
|
||||
|
||||
u32 *readOffset = (u32 *)memsearch((u8 *)(writeOffset + 3), pattern, 0x400, sizeof(pattern));
|
||||
|
||||
if(readOffset == NULL) return 1;
|
||||
|
||||
// Find the sdmmc mount/init(?) function
|
||||
static const u8 mount_pattern[] = {
|
||||
0x20, 0x00, 0x84, 0xE2, // add r0, r4, 0x20
|
||||
0x01, 0x20, 0xA0, 0xE3, // mov r2, #1
|
||||
0x00, 0x10, 0xA0, 0xE3, // mov r1, #0
|
||||
};
|
||||
u32* mountOffset = (u32*) memsearch(pos, mount_pattern, size, sizeof(mount_pattern));
|
||||
if (mountOffset == NULL) return 1;
|
||||
|
||||
// Find the sdmmc read cid function.
|
||||
static const u8 readcid_pattern[] = {
|
||||
0x31, 0xFF, 0x2F, 0xE1, // blx r1
|
||||
0x20, 0x60, 0x9F, 0xE5, // ldr r6, [pc, #0x20] // =failing_result
|
||||
0x00, 0x00, 0x50, 0xE3, // cmp r0, #0
|
||||
};
|
||||
u32* readCidOffset = (u32*) memsearch(pos, readcid_pattern, size, sizeof(readcid_pattern));
|
||||
if (readCidOffset == NULL) return 1;
|
||||
readCidOffset -= 5;
|
||||
|
||||
mountOffset[1] = 0xe3a02000; // mov r2, #0 // sd-card
|
||||
|
||||
readOffset[0] = writeOffset[0] = 0xe52de004; // push {lr}
|
||||
readOffset[1] = writeOffset[1] = 0xe59fc000; // ldr r12, [pc, #0]
|
||||
readOffset[2] = writeOffset[2] = 0xe12fff3c; // blx r12
|
||||
readOffset[3] = writeOffset[3] = hookAddr;
|
||||
|
||||
readCidOffset[0] = 0xe59fc000; // ldr r12, [pc, #0]
|
||||
readCidOffset[1] = 0xe12fff3c; // blx r12
|
||||
readCidOffset[2] = hookCidAddr;
|
||||
|
||||
// Read the emmc cid into the place hook will copy it from
|
||||
sdmmc_get_cid(1, emunandPatchNandCid);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline u32 patchProtoNandRw238(u8 *pos, u32 size, u32 hookAddr, u32 hookCidAddr)
|
||||
{
|
||||
//Look for read/write code
|
||||
static const u8 pattern[] = {
|
||||
0x03, 0x00, 0x50, 0xE3, // cmp r0, #3
|
||||
0x00, 0x00, 0xA0, 0x13, // movne r0, #0
|
||||
0x01, 0x00, 0xA0, 0x03, // moveq r0, #1
|
||||
};
|
||||
|
||||
u32 *writeOffset = (u32 *)memsearch(pos, pattern, size, sizeof(pattern));
|
||||
|
||||
if(writeOffset == NULL) return 1;
|
||||
|
||||
u32 *readOffset = (u32 *)memsearch((u8 *)(writeOffset + 3), pattern, 0x400, sizeof(pattern));
|
||||
|
||||
if(readOffset == NULL) return 1;
|
||||
|
||||
// Find the mmc static ctor...
|
||||
static const u8 mount_pattern[] = {
|
||||
0x08, // last byte of some ptr to something in P9
|
||||
0x01, 0x01, 0x00, 0x00, // emmc controller id
|
||||
};
|
||||
u8* mountOffset = (u8*) memsearch(pos, mount_pattern, size, sizeof(mount_pattern));
|
||||
if (mountOffset == NULL) return 1;
|
||||
mountOffset++;
|
||||
|
||||
// Find the sdmmc read cid function.
|
||||
static const u8 readcid_pattern[] = {
|
||||
0x31, 0xFF, 0x2F, 0xE1, // blx r1
|
||||
0x20, 0x60, 0x9F, 0xE5, // ldr r6, [pc, #0x20] // =failing_result
|
||||
0x00, 0x00, 0x50, 0xE3, // cmp r0, #0
|
||||
};
|
||||
u32* readCidOffset = (u32*) memsearch(pos, readcid_pattern, size, sizeof(readcid_pattern));
|
||||
if (readCidOffset == NULL) return 1;
|
||||
readCidOffset -= 5;
|
||||
|
||||
*(u32*)mountOffset = 0x300; // sd card
|
||||
|
||||
readOffset[0] = writeOffset[0] = 0xe59fc000; // ldr r12, [pc, #0]
|
||||
readOffset[1] = writeOffset[1] = 0xe12fff3c; // blx r12
|
||||
readOffset[2] = writeOffset[2] = hookAddr;
|
||||
|
||||
readCidOffset[0] = 0xe59fc000; // ldr r12, [pc, #0]
|
||||
readCidOffset[1] = 0xe12fff3c; // blx r12
|
||||
readCidOffset[2] = hookCidAddr;
|
||||
|
||||
// Read the emmc cid into the place hook will copy it from
|
||||
sdmmc_get_cid(1, emunandPatchNandCid);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
u32 patchEmuNand(u8 *process9Offset, u32 process9Size, u32 firmVersion)
|
||||
{
|
||||
u32 ret = 0;
|
||||
@ -306,33 +184,3 @@ u32 patchEmuNand(u8 *process9Offset, u32 process9Size, u32 firmVersion)
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
u32 patchProtoEmuNand(u8 *process9Offset, u32 process9Size)
|
||||
{
|
||||
extern u32 firmProtoVersion;
|
||||
u32 ret = 0;
|
||||
|
||||
// Add the data of the found EmuNAND
|
||||
emunandPatchNandOffset = emuOffset;
|
||||
emunandPatchNcsdHeaderOffset = emuHeader;
|
||||
|
||||
// Find and add the SDMMC struct
|
||||
u32 sdmmc;
|
||||
ret += getProtoSdmmc(&sdmmc, firmProtoVersion);
|
||||
if(!ret) emunandPatchSdmmcStructPtr = sdmmc;
|
||||
|
||||
// Add EmuNAND hooks
|
||||
switch (firmProtoVersion) {
|
||||
case 243: // SDK 0.9.x (0.9.7?)
|
||||
ret += patchProtoNandRw(process9Offset, process9Size, (u32)emunandProtoPatch, (u32)emunandProtoCidPatch);
|
||||
break;
|
||||
case 238: // SDK 0.10.x
|
||||
ret += patchProtoNandRw238(process9Offset, process9Size, (u32)emunandProtoPatch238, (u32)emunandProtoCidPatch);
|
||||
break;
|
||||
default:
|
||||
ret++;
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
@ -39,4 +39,3 @@ extern u32 emuOffset,
|
||||
|
||||
void locateEmuNand(FirmwareSource *nandType, u32 *emunandIndex, bool configureCtrNandParams);
|
||||
u32 patchEmuNand(u8 *process9Offset, u32 process9Size, u32 firmVersion);
|
||||
u32 patchProtoEmuNand(u8 *process9Offset, u32 process9Size);
|
||||
@ -45,150 +45,19 @@ emunandPatch:
|
||||
|
||||
.pool
|
||||
|
||||
_emunandPatchEnd:
|
||||
|
||||
.global emunandProtoPatch
|
||||
emunandProtoPatch:
|
||||
@ Save registers
|
||||
push {r0-r3}
|
||||
|
||||
@ If we're already trying to access the SD, return
|
||||
ldr r2, [r0, #4]
|
||||
ldr r1, emunandPatchSdmmcStructPtr
|
||||
cmp r2, r1
|
||||
beq _out
|
||||
|
||||
ldrb r2, [r1, #0xc] @ Get sdmc->m_isInitialised
|
||||
cmp r2, #0 @ Is initialised?
|
||||
beq _pastSdmc @ if not, use "NAND" object, patched elsewhere to access SD
|
||||
str r1, [r0, #4] @ Set object to be SD
|
||||
_pastSdmc:
|
||||
ldr r2, [r0, #8] @ Get sector to read
|
||||
cmp r2, #0 @ For GW compatibility, see if we're trying to read the ncsd header (sector 0)
|
||||
|
||||
ldr r3, emunandPatchNandOffset
|
||||
add r2, r3 @ Add the offset to the NAND in the SD
|
||||
|
||||
ldreq r3, emunandPatchNcsdHeaderOffset
|
||||
addeq r2, r3 @ If we're reading the ncsd header, add the offset of that sector
|
||||
|
||||
str r2, [r0, #8] @ Store sector to read
|
||||
|
||||
_out:
|
||||
@ Restore registers
|
||||
pop {r0-r3}
|
||||
@ Execute original code that got patched.
|
||||
cmp r1, #3
|
||||
mov r12, r2
|
||||
add r0, r0, #4
|
||||
movne r1, #0
|
||||
moveq r1, #1
|
||||
@ r2 about to be overwritten, so it's free to use here.
|
||||
@ Save off our return address and restore lr.
|
||||
mov r2, lr
|
||||
pop {lr}
|
||||
@ r2+0 is return address (patched movne r1, #0)
|
||||
@ r2+4 is moveq r1, #1
|
||||
@ r2+8 is the following instruction (mov r2, r3)
|
||||
add r2, #8
|
||||
bx r2
|
||||
|
||||
.global emunandProtoCidPatch
|
||||
emunandProtoCidPatch:
|
||||
@ If we're already trying to access the SD, return
|
||||
ldr r4, emunandPatchSdmmcStructPtr
|
||||
cmp r0, r4
|
||||
beq _cid_return
|
||||
|
||||
@ Trying to access nand, so copy the NAND cid into r1
|
||||
adr r4, emunandPatchNandCid
|
||||
ldr r2, [r4, #0]
|
||||
ldr r3, [r4, #4]
|
||||
ldr r5, [r4, #8]
|
||||
ldr r6, [r4, #0xc]
|
||||
str r2, [r1, #0]
|
||||
str r3, [r1, #4]
|
||||
str r5, [r1, #8]
|
||||
str r6, [r1, #0xc]
|
||||
@ And return from whence we came
|
||||
mov r0, #0
|
||||
pop {r4-r6, pc}
|
||||
|
||||
_cid_return:
|
||||
@ Execute original code that got patched.
|
||||
mov r4, r0
|
||||
ldr r0, [r0]
|
||||
mov r5, r1
|
||||
@ lr+0 is return address (patched mov r5, r1)
|
||||
@ lr+4 is following instruction (ldr r1, [r0,#8])
|
||||
add lr, #4
|
||||
bx lr
|
||||
|
||||
.global emunandProtoPatch238
|
||||
emunandProtoPatch238:
|
||||
@ Save registers
|
||||
push {r0-r3}
|
||||
|
||||
@ If we're already trying to access the SD, return
|
||||
ldr r2, [r4, #4]
|
||||
ldr r1, emunandPatchSdmmcStructPtr
|
||||
cmp r2, r1
|
||||
beq _out238
|
||||
|
||||
ldr r2, [r1, #0x24] @ Get sdmc->m_someObjInitedLater
|
||||
cmp r2, #0 @ Is initialised?
|
||||
beq _pastSdmc238 @ if not, use "NAND" object, patched elsewhere to access SD
|
||||
str r1, [r4, #4] @ Set object to be SD
|
||||
_pastSdmc238:
|
||||
|
||||
ldr r2, [r4, #8] @ Get sector to read
|
||||
cmp r2, #0 @ For GW compatibility, see if we're trying to read the ncsd header (sector 0)
|
||||
|
||||
ldr r3, emunandPatchNandOffset
|
||||
add r2, r3 @ Add the offset to the NAND in the SD
|
||||
|
||||
ldreq r3, emunandPatchNcsdHeaderOffset
|
||||
addeq r2, r3 @ If we're reading the ncsd header, add the offset of that sector
|
||||
|
||||
str r2, [r4, #8] @ Store sector to read
|
||||
|
||||
_out238:
|
||||
@ Restore registers
|
||||
pop {r0-r3}
|
||||
@ Execute original code that got patched.
|
||||
cmp r0, #3
|
||||
movne r0, #0
|
||||
moveq r0, #1
|
||||
@ r1 about to be overwritten, so it's free to use here.
|
||||
@ Save off our return address.
|
||||
mov r1, lr
|
||||
@ r1+0 is return address (patched moveq r1, #1)
|
||||
@ r1+4 is tst r0, #0xff or sub sp, sp, #0xc
|
||||
add r1, #4
|
||||
bx r1
|
||||
|
||||
.pool
|
||||
|
||||
.global emunandPatchSdmmcStructPtr
|
||||
.global emunandPatchNandOffset
|
||||
.global emunandPatchNcsdHeaderOffset
|
||||
.global emunandPatchNandCid
|
||||
|
||||
_emunandPatchBssStart:
|
||||
emunandPatchSdmmcStructPtr: .word 0 @ Pointer to sdmmc struct
|
||||
emunandPatchNandOffset: .word 0 @ For rednand this should be 1
|
||||
emunandPatchNcsdHeaderOffset: .word 0 @ Depends on nand manufacturer + emunand type (GW/RED)
|
||||
emunandPatchNandCid: @ Store emmc cid here, to override "sdmc's" when trying to read emmc's
|
||||
.word 0,0,0,0
|
||||
_emunandPatchBssEnd:
|
||||
|
||||
.pool
|
||||
.balign 4
|
||||
|
||||
_emunandPatchEnd:
|
||||
|
||||
.global emunandPatchSize
|
||||
emunandPatchSize:
|
||||
.word _emunandPatchEnd - emunandPatch
|
||||
|
||||
.global emunandPatchBssSize
|
||||
emunandPatchBssSize:
|
||||
.word _emunandPatchBssEnd - _emunandPatchBssStart
|
||||
@ -39,7 +39,6 @@
|
||||
#include "chainloader.h"
|
||||
|
||||
static Firm *firm = (Firm *)0x20001000;
|
||||
u32 firmProtoVersion = 0;
|
||||
|
||||
static __attribute__((noinline)) bool overlaps(u32 as, u32 ae, u32 bs, u32 be)
|
||||
{
|
||||
@ -155,21 +154,7 @@ u32 loadNintendoFirm(FirmwareType *firmType, FirmwareSource nandType, bool loadF
|
||||
u32 firmVersion = 0xFFFFFFFF,
|
||||
firmSize;
|
||||
|
||||
bool ctrNandError = true;
|
||||
bool loadedFromStorage = false;
|
||||
bool storageLoadError = false;
|
||||
|
||||
// Try loading FIRM from sdmc first if specified.
|
||||
if (loadFromStorage) {
|
||||
firmSize = loadFirmFromStorage(*firmType);
|
||||
if (firmSize != 0) loadedFromStorage = true;
|
||||
else storageLoadError = true;
|
||||
}
|
||||
|
||||
// Remount ctrnand and load FIRM from it if loading from sdmc failed.
|
||||
if (!loadedFromStorage) {
|
||||
ctrNandError = isSdMode && !remountCtrNandPartition(false);
|
||||
}
|
||||
bool ctrNandError = isSdMode && !remountCtrNandPartition(false);
|
||||
|
||||
if(!ctrNandError)
|
||||
{
|
||||
@ -184,8 +169,10 @@ u32 loadNintendoFirm(FirmwareType *firmType, FirmwareSource nandType, bool loadF
|
||||
if(!firmSize || !checkFirm(firmSize)) ctrNandError = true;
|
||||
}
|
||||
}
|
||||
// If CTRNAND load failed, and it wasn't tried yet, load FIRM from sdmc.
|
||||
if (ctrNandError && !storageLoadError)
|
||||
|
||||
bool loadedFromStorage = false;
|
||||
|
||||
if(loadFromStorage || ctrNandError)
|
||||
{
|
||||
u32 result = loadFirmFromStorage(*firmType);
|
||||
|
||||
@ -194,69 +181,11 @@ u32 loadNintendoFirm(FirmwareType *firmType, FirmwareSource nandType, bool loadF
|
||||
loadedFromStorage = true;
|
||||
firmSize = result;
|
||||
}
|
||||
else storageLoadError = true;
|
||||
else if(ctrNandError) error("Unable to mount CTRNAND or load the CTRNAND FIRM.\nPlease use an external one.");
|
||||
}
|
||||
// If all attempts failed, panic.
|
||||
if(ctrNandError && storageLoadError) error("Unable to mount CTRNAND or load the CTRNAND FIRM.\nPlease use an external one.");
|
||||
|
||||
//Check that the FIRM is right for the console from the Arm9 section address
|
||||
bool isO3dsFirm = firm->section[3].offset == 0 && firm->section[2].address == (u8 *)0x8006800;
|
||||
|
||||
if(loadedFromStorage || ISDEVUNIT)
|
||||
{
|
||||
firmVersion = 0xFFFFFFFF;
|
||||
|
||||
if(isO3dsFirm && (*firmType == NATIVE_FIRM || *firmType == NATIVE_FIRM1X2X))
|
||||
{
|
||||
__attribute__((aligned(4))) static const u8 hashes[5][0x20] = {
|
||||
{0xD7, 0x43, 0x0F, 0x27, 0x8D, 0xC9, 0x3F, 0x4C, 0x96, 0xB5, 0xA8, 0x91, 0x48, 0xDB, 0x08, 0x8A,
|
||||
0x7E, 0x46, 0xB3, 0x95, 0x65, 0xA2, 0x05, 0xF1, 0xF2, 0x41, 0x21, 0xF1, 0x0C, 0x59, 0x6A, 0x9D},
|
||||
{0x93, 0xDF, 0x49, 0xA1, 0x24, 0x86, 0xBB, 0x6F, 0xAF, 0x49, 0x99, 0x2D, 0xD0, 0x8D, 0xB1, 0x88,
|
||||
0x8A, 0x00, 0xB6, 0xDD, 0x36, 0x89, 0xC0, 0xE2, 0xC9, 0xA9, 0x99, 0x62, 0x57, 0x5E, 0x6C, 0x23},
|
||||
{0x39, 0x75, 0xB5, 0x28, 0x24, 0x5E, 0x8B, 0x56, 0xBC, 0x83, 0x79, 0x41, 0x09, 0x2C, 0x42, 0xE6,
|
||||
0x26, 0xB6, 0x80, 0x59, 0xA5, 0x56, 0xF9, 0xF9, 0x6E, 0xF3, 0x63, 0x05, 0x58, 0xDF, 0x35, 0xEF},
|
||||
{0x81, 0x9E, 0x71, 0x58, 0xE5, 0x44, 0x73, 0xF7, 0x48, 0x78, 0x7C, 0xEF, 0x5E, 0x30, 0xE2, 0x28,
|
||||
0x78, 0x0B, 0x21, 0x23, 0x94, 0x63, 0xE8, 0x4E, 0x06, 0xBB, 0xD6, 0x8D, 0xA0, 0x99, 0xAE, 0x98},
|
||||
{0x1D, 0xD5, 0xB0, 0xC2, 0xD9, 0x4A, 0x4A, 0xF3, 0x23, 0xDD, 0x2F, 0x65, 0x21, 0x95, 0x9B, 0x7E,
|
||||
0xF2, 0x71, 0x7E, 0xB6, 0x7A, 0x3A, 0x74, 0x78, 0x0D, 0xE3, 0xB5, 0x0C, 0x2B, 0x7F, 0x85, 0x37},
|
||||
};
|
||||
|
||||
u32 i;
|
||||
for(i = 0; i < sizeof(hashes)/sizeof(hashes[0]); i++)
|
||||
{
|
||||
if(memcmp(firm->section[1].hash, hashes[i], 0x20) == 0) break;
|
||||
}
|
||||
|
||||
switch(i)
|
||||
{
|
||||
// Beta
|
||||
case 0:
|
||||
firmVersion = 0x0;
|
||||
firmProtoVersion = 243;
|
||||
*firmType = NATIVE_PROTOTYPE;
|
||||
break;
|
||||
case 1:
|
||||
firmVersion = 0x0;
|
||||
firmProtoVersion = 238;
|
||||
*firmType = NATIVE_PROTOTYPE;
|
||||
break;
|
||||
// Release
|
||||
case 2:
|
||||
firmVersion = 0x18;
|
||||
break;
|
||||
case 3:
|
||||
firmVersion = 0x1D;
|
||||
break;
|
||||
case 4:
|
||||
firmVersion = 0x1F;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(*firmType != NATIVE_PROTOTYPE && (firm->section[3].offset != 0 ? firm->section[3].address : firm->section[2].address) != (ISN3DS ? (u8 *)0x8006000 : (u8 *)0x8006800))
|
||||
if((firm->section[3].offset != 0 ? firm->section[3].address : firm->section[2].address) != (ISN3DS ? (u8 *)0x8006000 : (u8 *)0x8006800))
|
||||
error("The %s FIRM is not for this console.", loadedFromStorage ? "external" : "CTRNAND");
|
||||
|
||||
if(!ISN3DS && *firmType == NATIVE_FIRM && firm->section[0].address == (u8 *)0x1FF80000)
|
||||
@ -270,6 +199,41 @@ u32 loadNintendoFirm(FirmwareType *firmType, FirmwareSource nandType, bool loadF
|
||||
*firmType = NATIVE_FIRM1X2X;
|
||||
}
|
||||
|
||||
if(loadedFromStorage || ISDEVUNIT)
|
||||
{
|
||||
firmVersion = 0xFFFFFFFF;
|
||||
|
||||
if(!ISN3DS && *firmType == NATIVE_FIRM)
|
||||
{
|
||||
__attribute__((aligned(4))) static const u8 hashes[3][0x20] = {
|
||||
{0x39, 0x75, 0xB5, 0x28, 0x24, 0x5E, 0x8B, 0x56, 0xBC, 0x83, 0x79, 0x41, 0x09, 0x2C, 0x42, 0xE6,
|
||||
0x26, 0xB6, 0x80, 0x59, 0xA5, 0x56, 0xF9, 0xF9, 0x6E, 0xF3, 0x63, 0x05, 0x58, 0xDF, 0x35, 0xEF},
|
||||
{0x81, 0x9E, 0x71, 0x58, 0xE5, 0x44, 0x73, 0xF7, 0x48, 0x78, 0x7C, 0xEF, 0x5E, 0x30, 0xE2, 0x28,
|
||||
0x78, 0x0B, 0x21, 0x23, 0x94, 0x63, 0xE8, 0x4E, 0x06, 0xBB, 0xD6, 0x8D, 0xA0, 0x99, 0xAE, 0x98},
|
||||
{0x1D, 0xD5, 0xB0, 0xC2, 0xD9, 0x4A, 0x4A, 0xF3, 0x23, 0xDD, 0x2F, 0x65, 0x21, 0x95, 0x9B, 0x7E,
|
||||
0xF2, 0x71, 0x7E, 0xB6, 0x7A, 0x3A, 0x74, 0x78, 0x0D, 0xE3, 0xB5, 0x0C, 0x2B, 0x7F, 0x85, 0x37}
|
||||
};
|
||||
|
||||
u32 i;
|
||||
for(i = 0; i < 3; i++) if(memcmp(firm->section[1].hash, hashes[i], 0x20) == 0) break;
|
||||
|
||||
switch(i)
|
||||
{
|
||||
case 0:
|
||||
firmVersion = 0x18;
|
||||
break;
|
||||
case 1:
|
||||
firmVersion = 0x1D;
|
||||
break;
|
||||
case 2:
|
||||
firmVersion = 0x1F;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return firmVersion;
|
||||
}
|
||||
|
||||
@ -773,29 +737,6 @@ u32 patch1x2xNativeAndSafeFirm(void)
|
||||
return ret;
|
||||
}
|
||||
|
||||
u32 patchPrototypeNative(FirmwareSource nandType)
|
||||
{
|
||||
u8 *arm9Section = (u8 *)firm + firm->section[2].offset;
|
||||
|
||||
//Find the Process9 .code location, size and memory address
|
||||
u32 process9Size,
|
||||
process9MemAddr;
|
||||
u8 *process9Offset = getProcess9Info(arm9Section, firm->section[2].size, &process9Size, &process9MemAddr);
|
||||
|
||||
u32 kernel9Size = (u32)(process9Offset - arm9Section) - sizeof(Cxi) - 0x200,
|
||||
ret = 0;
|
||||
|
||||
ret += patchProtoNandSignatureCheck(process9Offset, process9Size);
|
||||
|
||||
//Arm9 exception handlers
|
||||
ret += patchArm9ExceptionHandlersInstall(arm9Section, kernel9Size);
|
||||
|
||||
//Apply EmuNAND patches
|
||||
if(nandType != FIRMWARE_SYSNAND) ret += patchProtoEmuNand(process9Offset, process9Size);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void launchFirm(int argc, char **argv)
|
||||
{
|
||||
prepareArm11ForFirmlaunch();
|
||||
|
||||
@ -35,5 +35,4 @@ u32 patchNativeFirm(u32 firmVersion, FirmwareSource nandType, bool loadFromStora
|
||||
u32 patchTwlFirm(u32 firmVersion, bool loadFromStorage, bool doUnitinfoPatch);
|
||||
u32 patchAgbFirm(bool loadFromStorage, bool doUnitinfoPatch);
|
||||
u32 patch1x2xNativeAndSafeFirm(void);
|
||||
u32 patchPrototypeNative(FirmwareSource nandType);
|
||||
void launchFirm(int argc, char **argv);
|
||||
|
||||
@ -137,7 +137,6 @@ 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] == '/')
|
||||
{
|
||||
@ -179,7 +178,6 @@ 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')
|
||||
@ -443,38 +441,21 @@ 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;
|
||||
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:/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, "%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/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, "%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
|
||||
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
|
||||
|
||||
if (!ok) return false;
|
||||
|
||||
alignedseqmemcpy(fileCopyBuffer, (const void *)0x10012000, 0x100);
|
||||
sprintf(fullPath, "%sotp.bin", pathStart);
|
||||
if (getFileSize(fullPath) != 0x100)
|
||||
ok = ok && fileWrite(fileCopyBuffer, fullPath, 0x100);
|
||||
if (getFileSize("backups/otp.bin") != 0x100)
|
||||
ok = ok && fileWrite(fileCopyBuffer, "backups/otp.bin", 0x100);
|
||||
|
||||
if (!ok) return false;
|
||||
|
||||
@ -482,22 +463,19 @@ 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(fullPath) != 0x1000)
|
||||
ok = ok && fileWrite(fileCopyBuffer, fullPath, 0x1000);
|
||||
if (getFileSize("backups/HWCAL_01_EEPROM.dat") != 0x1000)
|
||||
ok = ok && fileWrite(fileCopyBuffer, "backups/HWCAL_01_EEPROM.dat", 0x1000);
|
||||
}
|
||||
|
||||
// B9S bootrom backups
|
||||
u32 hash[32/4];
|
||||
sha(hash, (const void *)0x08080000, 0x10000, SHA_256_MODE);
|
||||
sprintf(fullPath, "%sboot9.bin", pathStart);
|
||||
if (memcmp(hash, boot9Sha256, 32) == 0 && getFileSize(fullPath) != 0x10000)
|
||||
ok = ok && fileWrite((const void *)0x08080000, fullPath, 0x10000);
|
||||
if (memcmp(hash, boot9Sha256, 32) == 0 && getFileSize("backups/boot9.bin") != 0x10000)
|
||||
ok = ok && fileWrite((const void *)0x08080000, "backups/boot9.bin", 0x10000);
|
||||
sha(hash, (const void *)0x08090000, 0x10000, SHA_256_MODE);
|
||||
sprintf(fullPath, "%sboot11.bin", pathStart);
|
||||
if (memcmp(hash, boot11Sha256, 32) == 0 && getFileSize(fullPath) != 0x10000)
|
||||
ok = ok && fileWrite((const void *)0x08090000, fullPath, 0x10000);
|
||||
if (memcmp(hash, boot11Sha256, 32) == 0 && getFileSize("backups/boot11.bin") != 0x10000)
|
||||
ok = ok && fileWrite((const void *)0x08090000, "backups/boot11.bin", 0x10000);
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
@ -40,8 +40,6 @@ 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,
|
||||
|
||||
@ -28,11 +28,9 @@
|
||||
|
||||
#include "types.h"
|
||||
|
||||
extern const u8 emunandPatch[], emunandProtoPatch[], emunandProtoCidPatch[];
|
||||
extern const u8 emunandProtoPatch238[];
|
||||
extern const u32 emunandPatchSize, emunandPatchBssSize;
|
||||
extern const u8 emunandPatch[];
|
||||
extern const u32 emunandPatchSize;
|
||||
extern u32 emunandPatchSdmmcStructPtr, emunandPatchNandOffset, emunandPatchNcsdHeaderOffset;
|
||||
extern u32 emunandPatchNandCid[4];
|
||||
|
||||
extern const u8 rebootPatch[];
|
||||
extern const u32 rebootPatchSize;
|
||||
|
||||
@ -386,9 +386,6 @@ boot:
|
||||
case NATIVE_FIRM1X2X:
|
||||
res = patch1x2xNativeAndSafeFirm();
|
||||
break;
|
||||
case NATIVE_PROTOTYPE:
|
||||
res = patchPrototypeNative(nandType);
|
||||
break;
|
||||
}
|
||||
|
||||
if(res != 0) error("Failed to apply %u FIRM patch(es).", res);
|
||||
|
||||
@ -45,7 +45,6 @@
|
||||
#define K11EXT_VA 0x70000000
|
||||
|
||||
extern u16 launchedPath[];
|
||||
extern u32 firmProtoVersion;
|
||||
|
||||
u8 *getProcess9Info(u8 *pos, u32 size, u32 *process9Size, u32 *process9MemAddr)
|
||||
{
|
||||
@ -55,20 +54,10 @@ u8 *getProcess9Info(u8 *pos, u32 size, u32 *process9Size, u32 *process9MemAddr)
|
||||
|
||||
Cxi *off = (Cxi *)(temp - 0x100);
|
||||
|
||||
*process9Size = (off->ncch.exeFsSize - 1) * 0x200;
|
||||
*process9MemAddr = off->exHeader.systemControlInfo.textCodeSet.address;
|
||||
|
||||
// Prototype FW has a different NCCH format
|
||||
if (firmProtoVersion && firmProtoVersion <= 243)
|
||||
{
|
||||
*process9Size = off->ncch.exeFsSize;
|
||||
return (u8 *)off + off->ncch.exeFsOffset;
|
||||
}
|
||||
else
|
||||
{
|
||||
*process9Size = (off->ncch.exeFsSize - 1) * 0x200;
|
||||
return (u8 *)off + (off->ncch.exeFsOffset + 1) * 0x200;
|
||||
}
|
||||
|
||||
return (u8 *)off + (off->ncch.exeFsOffset + 1) * 0x200;
|
||||
}
|
||||
|
||||
u32 *getKernel11Info(u8 *pos, u32 size, u32 *baseK11VA, u8 **freeK11Space, u32 **arm11SvcHandler, u32 **arm11ExceptionsPage)
|
||||
@ -843,56 +832,5 @@ 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;
|
||||
}
|
||||
|
||||
u32 patchProtoNandSignatureCheck(u8 *pos, u32 size) {
|
||||
if (firmProtoVersion == 243) {
|
||||
static const u8 pattern[] = {0x08, 0x31, 0x9F, 0xE5};
|
||||
|
||||
// Signature check function returns 0 if failed and 1 if succeeded.
|
||||
// Proc9 breaks if the returned value is 0, change it to break if
|
||||
// the returned value is 2 (never).
|
||||
u8 *off = memsearch(pos, pattern, size, sizeof(pattern));
|
||||
if (!off)
|
||||
return 1;
|
||||
|
||||
off[0x20] = 2;
|
||||
}
|
||||
|
||||
else if (firmProtoVersion == 238) { // SDK 0.10
|
||||
// Same patch as for v243 ported to the different ncsd_read() function
|
||||
static const u8 pattern[] = {
|
||||
0x00, 0x11, 0x9f, 0xe5,
|
||||
0x00, 0x51, 0x9f, 0xe5,
|
||||
};
|
||||
|
||||
u8 *off = memsearch(pos, pattern, size, sizeof(pattern));
|
||||
if (!off)
|
||||
return 1;
|
||||
|
||||
off[0x20] = 2;
|
||||
}
|
||||
|
||||
else return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -31,7 +31,6 @@
|
||||
* FIRM partition writes patches by delebile
|
||||
* Idea for svcBreak patches from yellows8 and others on #3dsdev
|
||||
* TWL_FIRM patches by Steveice10 and others
|
||||
* Signature patches for prototype FW by PabloMK7
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
@ -69,4 +68,3 @@ u32 patchTwlShaHashChecks(u8 *pos, u32 size);
|
||||
u32 patchAgbBootSplash(u8 *pos, u32 size);
|
||||
void patchTwlBg(u8 *pos, u32 size); // silently fails
|
||||
u32 patchLgyK11(u8 *section1, u32 section1Size, u8 *section2, u32 section2Size);
|
||||
u32 patchProtoNandSignatureCheck(u8 *pos, u32 size);
|
||||
@ -70,14 +70,6 @@ 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)
|
||||
@ -110,31 +102,8 @@ void initScreens(void)
|
||||
memcpy((void *)(ARM11_PARAMETERS_ADDRESS + 4), fbs, sizeof(fbs));
|
||||
invokeArm11Function(INIT_SCREENS);
|
||||
|
||||
// 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);
|
||||
//Turn on backlight
|
||||
I2C_writeReg(I2C_DEV_MCU, 0x22, 0x2A);
|
||||
wait(5);
|
||||
}
|
||||
else updateBrightness(MULTICONFIG(BRIGHTNESS));
|
||||
@ -149,8 +118,3 @@ void initScreens(void)
|
||||
clearScreens(false);
|
||||
swapFramebuffers(false);
|
||||
}
|
||||
|
||||
void zerofillN3dsAblRegisters(void)
|
||||
{
|
||||
invokeArm11Function(ZEROFILL_N3DS_ABL_REGISTERS);
|
||||
}
|
||||
|
||||
@ -59,7 +59,6 @@ typedef enum
|
||||
SWAP_FRAMEBUFFERS,
|
||||
UPDATE_BRIGHTNESS,
|
||||
DEINIT_SCREENS,
|
||||
ZEROFILL_N3DS_ABL_REGISTERS,
|
||||
PREPARE_ARM11_FOR_FIRMLAUNCH,
|
||||
ARM11_READY,
|
||||
} Arm11Operation;
|
||||
@ -74,4 +73,3 @@ void swapFramebuffers(bool isAlternate);
|
||||
void updateBrightness(u32 brightnessIndex);
|
||||
void clearScreens(bool isAlternate);
|
||||
void initScreens(void);
|
||||
void zerofillN3dsAblRegisters(void);
|
||||
|
||||
@ -134,8 +134,7 @@ typedef enum FirmwareType
|
||||
AGB_FIRM,
|
||||
SAFE_FIRM,
|
||||
SYSUPDATER_FIRM,
|
||||
NATIVE_FIRM1X2X,
|
||||
NATIVE_PROTOTYPE,
|
||||
NATIVE_FIRM1X2X
|
||||
} FirmwareType;
|
||||
|
||||
typedef enum bootType
|
||||
|
||||
@ -158,44 +158,23 @@ void error(const char *fmt, ...)
|
||||
mcuPowerOff();
|
||||
}
|
||||
|
||||
|
||||
// CRC-16/MODBUS
|
||||
u16 crc16(const void *data, size_t size, u16 initialValue)
|
||||
{
|
||||
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,
|
||||
};
|
||||
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;
|
||||
@ -207,40 +186,21 @@ u16 crc16(const void *data, size_t size, u16 initialValue)
|
||||
|
||||
u32 crc32(const void *data, size_t size, u32 initialValue)
|
||||
{
|
||||
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,
|
||||
};
|
||||
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;
|
||||
|
||||
@ -34,12 +34,6 @@
|
||||
|
||||
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",
|
||||
|
||||
@ -272,7 +272,7 @@ Result PLGLDR__IsPluginLoaderEnabled(bool *isEnabled)
|
||||
}
|
||||
|
||||
// Try to load a plugin for the game
|
||||
static Result PLGLDR_LoadPlugin(u32 processID, bool isHomebrew)
|
||||
static Result PLGLDR_LoadPlugin(u32 processID)
|
||||
{
|
||||
// Special case handling: games rebooting the 3DS on old models
|
||||
if (!isN3DS && g_exheaderInfo.aci.local_caps.core_info.o3ds_system_mode > 0)
|
||||
@ -288,9 +288,8 @@ static Result PLGLDR_LoadPlugin(u32 processID, bool isHomebrew)
|
||||
|
||||
u32* cmdbuf = getThreadCommandBuffer();
|
||||
|
||||
cmdbuf[0] = IPC_MakeHeader(1, 2, 0);
|
||||
cmdbuf[0] = IPC_MakeHeader(1, 1, 0);
|
||||
cmdbuf[1] = processID;
|
||||
cmdbuf[2] = isHomebrew;
|
||||
return svcSendSyncRequest(plgldrHandle);
|
||||
}
|
||||
|
||||
@ -453,12 +452,12 @@ static Result LoadProcessImpl(Handle *outProcessHandle, const ExHeader_Info *exh
|
||||
res = R_SUCCEEDED(res) ? 0 : res;
|
||||
|
||||
// check for plugin
|
||||
if (!res && ((u32)((titleId >> 0x20) & 0xFFFFFFEDULL) == 0x00040000))
|
||||
if (!res && !isHomebrew && ((u32)((titleId >> 0x20) & 0xFFFFFFEDULL) == 0x00040000))
|
||||
{
|
||||
u32 processID;
|
||||
assertSuccess(svcGetProcessId(&processID, *outProcessHandle));
|
||||
assertSuccess(plgldrInit());
|
||||
assertSuccess(PLGLDR_LoadPlugin(processID, isHomebrew));
|
||||
assertSuccess(PLGLDR_LoadPlugin(processID));
|
||||
plgldrExit();
|
||||
}
|
||||
}
|
||||
|
||||
@ -10,7 +10,7 @@
|
||||
#include "hbldr.h"
|
||||
|
||||
u32 config, multiConfig, bootConfig;
|
||||
bool isN3DS, isSdMode, nextGamePatchDisabled, isLumaWithKext;
|
||||
bool isN3DS, isSdMode, nextGamePatchDisabled;
|
||||
|
||||
// MAKE SURE fsreg has been init before calling this
|
||||
static Result fsldrPatchPermissions(void)
|
||||
@ -33,7 +33,7 @@ static inline void loadCFWInfo(void)
|
||||
s64 out;
|
||||
u64 hbldrTid = 0;
|
||||
|
||||
isLumaWithKext = svcGetSystemInfo(&out, 0x20000, 0) == 1;
|
||||
bool isLumaWithKext = svcGetSystemInfo(&out, 0x20000, 0) == 1;
|
||||
if (isLumaWithKext)
|
||||
{
|
||||
svcGetSystemInfo(&out, 0x10000, 3);
|
||||
@ -60,8 +60,7 @@ static inline void loadCFWInfo(void)
|
||||
panic(0xDEADCAFE);
|
||||
|
||||
#ifndef BUILD_FOR_EXPLOIT_DEV
|
||||
// Most options 0, except select ones
|
||||
config = BIT(PATCHVERSTRING) | BIT(PATCHGAMES) | BIT(LOADEXTFIRMSANDMODULES);
|
||||
config = 1u << PATCHVERSTRING; // all options 0, except maybe the MSET version display patch
|
||||
#else
|
||||
config = 0;
|
||||
#endif
|
||||
|
||||
@ -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, u32 *fsUnMountArchive)
|
||||
static inline bool findLayeredFsSymbols(u8 *code, u32 size, u32 *fsMountArchive, u32 *fsRegisterArchive, u32 *fsTryOpenFile, u32 *fsOpenFileDirectly)
|
||||
{
|
||||
u32 found = 0,
|
||||
*temp = NULL;
|
||||
@ -165,12 +165,6 @@ 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;
|
||||
@ -189,14 +183,14 @@ static inline bool findLayeredFsSymbols(u8 *code, u32 size, u32 *fsMountArchive,
|
||||
if(*temp != 0xFFFFFFFF)
|
||||
{
|
||||
found++;
|
||||
if(found == 5) break;
|
||||
if(found == 4) break;
|
||||
}
|
||||
|
||||
temp = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return found == 5;
|
||||
return found == 4;
|
||||
}
|
||||
|
||||
static inline bool findLayeredFsPayloadOffset(u8 *code, u32 size, u32 roSize, u32 dataSize, u32 roAddress, u32 dataAddress, u32 *payloadOffset, u32 *pathOffset, u32 *pathAddress)
|
||||
@ -484,7 +478,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+1] = "------------";
|
||||
char buf[12] = "------------";
|
||||
u64 total;
|
||||
|
||||
if(R_FAILED(IFile_Read(&file, &total, buf, fileSize))) goto exit;
|
||||
@ -575,7 +569,6 @@ 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,
|
||||
@ -583,7 +576,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, &fsUnMountArchive) ||
|
||||
if(!findLayeredFsSymbols(code, textSize, &fsMountArchive, &fsRegisterArchive, &fsTryOpenFile, &fsOpenFileDirectly) ||
|
||||
!findLayeredFsPayloadOffset(code, textSize, roSize, dataSize, roAddress, dataAddress, &payloadOffset, &pathOffset, &pathAddress)) return false;
|
||||
|
||||
static const char *updateRomFsMounts[] = { "ro2:",
|
||||
@ -631,9 +624,8 @@ 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 = 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);
|
||||
romfsRedirPatchFsMountArchive = 0x100000 + fsMountArchive;
|
||||
romfsRedirPatchFsRegisterArchive = 0x100000 + fsRegisterArchive;
|
||||
romfsRedirPatchArchiveId = archiveId;
|
||||
memcpy(&romfsRedirPatchUpdateRomFsMount, updateRomFsMount, 4);
|
||||
|
||||
@ -734,65 +726,33 @@ 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
|
||||
progId == 0x0004001000028000LL) //TWN MSET
|
||||
&& CONFIG(PATCHVERSTRING))
|
||||
{
|
||||
if (CONFIG(PATCHVERSTRING))
|
||||
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
|
||||
{
|
||||
static const u16 pattern[] = u"Ve";
|
||||
const u16 *patch;
|
||||
u32 patchSize = 0,
|
||||
currentNand = BOOTCFG_NAND;
|
||||
patchSize = 8;
|
||||
|
||||
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;
|
||||
static const u16 *const verStringNandEmu[] = { u" Emu", u"Emu2", u"Emu3", u"Emu4" };
|
||||
patch = currentNand == 0 ? u" Sys" : verStringNandEmu[BOOTCFG_EMUINDEX];
|
||||
}
|
||||
|
||||
// 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
|
||||
//Patch Ver. string
|
||||
if(!patchMemory(code, textSize,
|
||||
pattern,
|
||||
sizeof(pattern) - 2, 0,
|
||||
patch,
|
||||
patchSize, 1
|
||||
)) goto error;
|
||||
}
|
||||
|
||||
else if(progId == 0x0004013000008002LL) //NS
|
||||
@ -1006,7 +966,7 @@ void patchCode(u64 progId, u16 progVer, u8 *code, u32 size, u32 textSize, u32 ro
|
||||
countryId,
|
||||
stateId;
|
||||
|
||||
if(isLumaWithKext && loadTitleLocaleConfig(progId, &mask, ®ionId, &languageId, &countryId, &stateId))
|
||||
if(loadTitleLocaleConfig(progId, &mask, ®ionId, &languageId, &countryId, &stateId))
|
||||
svcKernelSetState(0x10001, ((u32)stateId << 24) | ((u32)countryId << 16) | ((u32)languageId << 8) | ((u32)regionId << 4) | (u32)mask , progId);
|
||||
if(!patchLayeredFs(progId, code, size, textSize, roSize, dataSize, roAddress, dataAddress)) goto error;
|
||||
}
|
||||
|
||||
@ -43,7 +43,7 @@ enum singleOptions
|
||||
};
|
||||
|
||||
extern u32 config, multiConfig, bootConfig;
|
||||
extern bool isN3DS, isSdMode, nextGamePatchDisabled, isLumaWithKext;
|
||||
extern bool isN3DS, isSdMode, nextGamePatchDisabled;
|
||||
|
||||
void patchCode(u64 progId, u16 progVer, u8 *code, u32 size, u32 textSize, u32 roSize, u32 dataSize, u32 roAddress, u32 dataAddress);
|
||||
bool loadTitleCodeSection(u64 progId, u8 *code, u32 size);
|
||||
|
||||
@ -10,7 +10,6 @@ extern u32 romfsRedirPatchSubstituted2, romfsRedirPatchHook2;
|
||||
|
||||
extern u32 romfsRedirPatchArchiveName;
|
||||
extern u32 romfsRedirPatchFsMountArchive;
|
||||
extern u32 romfsRedirPatchFsUnMountArchive;
|
||||
extern u32 romfsRedirPatchFsRegisterArchive;
|
||||
extern u32 romfsRedirPatchArchiveId;
|
||||
extern u32 romfsRedirPatchRomFsMount;
|
||||
|
||||
@ -30,23 +30,17 @@ 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
|
||||
.global romfsRedirPatchFsMountArchive
|
||||
romfsRedirPatchFsMountArchive:
|
||||
.word 0xdead0005
|
||||
ldr r4, romfsRedirPatchFsMountArchive
|
||||
blx r4
|
||||
mov r3, #0
|
||||
mov r2, #0
|
||||
ldr r1, [sp]
|
||||
adr r0, romfsRedirPatchArchiveName
|
||||
.global romfsRedirPatchFsRegisterArchive
|
||||
romfsRedirPatchFsRegisterArchive:
|
||||
.word 0xdead0006
|
||||
ldr r4, romfsRedirPatchFsRegisterArchive
|
||||
blx r4
|
||||
add sp, sp, #4
|
||||
ldmfd sp!, {r0-r4, lr}
|
||||
b romfsRedirPatchSubstituted1
|
||||
@ -114,16 +108,20 @@ 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 0xdead0009
|
||||
romfsRedirPatchCustomPath : .word 0xdead0004
|
||||
|
||||
_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[25];
|
||||
MenuItem items[24];
|
||||
} Menu;
|
||||
|
||||
extern u32 menuCombo;
|
||||
|
||||
@ -36,7 +36,6 @@ 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);
|
||||
|
||||
@ -24,8 +24,7 @@ typedef struct CTR_PACKED
|
||||
u32 eventsSelfManaged : 1;
|
||||
u32 swapNotNeeded : 1;
|
||||
u32 usePrivateMemory : 1;
|
||||
u32 allowHomebrewLoad : 1;
|
||||
u32 unused : 22;
|
||||
u32 unused : 23;
|
||||
};
|
||||
};
|
||||
u32 exeLoadChecksum;
|
||||
|
||||
@ -56,12 +56,10 @@ typedef struct
|
||||
s32* plgldrEvent; ///< Used for synchronization
|
||||
s32* plgldrReply; ///< Used for synchronization
|
||||
u8 notifyHomeEvent;
|
||||
u8 padding[7];
|
||||
u64 waitForReplyTimeout;
|
||||
u32 reserved[20];
|
||||
u8 padding[3];
|
||||
u32 reserved[23];
|
||||
u32 config[32];
|
||||
} PluginHeader;
|
||||
_Static_assert(sizeof(PluginHeader) == 0x100, "Invalid PluginHeader size");
|
||||
} PluginHeader;
|
||||
|
||||
typedef void (*OnPlgLdrEventCb_t)(s32 eventType);
|
||||
|
||||
|
||||
@ -34,7 +34,7 @@ u32 saveSwapFunc(void* startAddr, void* endAddr, void* args);
|
||||
u32 loadSwapFunc(void* startAddr, void* endAddr, void* args);
|
||||
u32 loadExeFunc(void* startAddr, void* endAddr, void* args);
|
||||
|
||||
bool TryToLoadPlugin(Handle process, bool isHomebrew);
|
||||
bool TryToLoadPlugin(Handle process);
|
||||
void PLG__NotifyEvent(PLG_Event event, bool signal);
|
||||
void PLG__SetConfigMemoryStatus(u32 status);
|
||||
u32 PLG__GetConfigMemoryStatus(void);
|
||||
|
||||
@ -30,7 +30,6 @@
|
||||
#include <3ds/srv.h>
|
||||
#include <3ds/result.h>
|
||||
#include <3ds/ipc.h>
|
||||
#include <assert.h>
|
||||
#include "csvc.h"
|
||||
#include "luma_shared_config.h"
|
||||
|
||||
|
||||
@ -268,18 +268,14 @@ 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] = {
|
||||
// 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,
|
||||
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,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00
|
||||
};
|
||||
|
||||
memcpy(dst, bmpHeaderTemplate, 54);
|
||||
memset(dst + 54, 0, 64 - 54);
|
||||
Draw_WriteUnaligned(dst + 2, 64 + 3 * width * heigth, 4);
|
||||
Draw_WriteUnaligned(dst + 2, 54 + 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 + 0x400]; // eyeballed. TODO use #embed
|
||||
char inibuf[0x2000];
|
||||
|
||||
Result res;
|
||||
|
||||
@ -259,12 +259,6 @@ 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,13 +104,8 @@ 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[6];
|
||||
return s_blPwmData.luminanceLevels[s_blPwmData.numLevels - 1];
|
||||
}
|
||||
|
||||
u32 getCurrentLuminance(bool top)
|
||||
@ -119,8 +114,7 @@ u32 getCurrentLuminance(bool top)
|
||||
|
||||
readCalibration();
|
||||
|
||||
bool is3d = (REG32(0x10202000 + 0x000) & 1) != 0;
|
||||
const float *coeffs = s_blPwmData.coeffs[top ? (is3d ? 2 : 1) : 0];
|
||||
const float *coeffs = s_blPwmData.coeffs[top ? (isN3DS ? 2 : 1) : 0];
|
||||
u32 brightness = REG32(regbase + 0x40);
|
||||
float ratio = getPwmRatio(s_blPwmData.brightnessMax, REG32(regbase + 0x44));
|
||||
|
||||
|
||||
@ -55,7 +55,6 @@ 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 },
|
||||
@ -228,7 +227,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-2025 AuroraWright, TuxSH") + SPACING_Y;
|
||||
u32 posY = Draw_DrawString(10, 30, COLOR_WHITE, "Luma3DS (c) 2016-2024 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");
|
||||
@ -274,8 +273,7 @@ static Result RosalinaMenu_WriteScreenshot(IFile *file, u32 width, bool top, boo
|
||||
|
||||
u8 *buf = framebufferCache;
|
||||
Draw_CreateBitmapHeader(framebufferCache, width, numLinesScaled);
|
||||
const u32 headerSize = 0x40;
|
||||
buf += headerSize;
|
||||
buf += 54;
|
||||
|
||||
u32 y = 0;
|
||||
// Our buffer might be smaller than the size of the screenshot...
|
||||
@ -289,7 +287,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 ? headerSize : 0) + lineSize * nlines * scaleFactorY, 0)); // don't forget to write the header
|
||||
TRY(IFile_Write(file, &total, framebufferCache, (y == 0 ? 54 : 0) + lineSize * nlines * scaleFactorY, 0)); // don't forget to write the header
|
||||
timeSpentWritingScreenshot += svcGetSystemTick() - t1;
|
||||
|
||||
y += nlines;
|
||||
@ -302,35 +300,6 @@ 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,6 +367,11 @@ void MiscellaneousMenu_UpdateTimeDateNtp(void)
|
||||
}
|
||||
}
|
||||
|
||||
Draw_Lock();
|
||||
Draw_ClearFramebuffer();
|
||||
Draw_FlushFramebuffer();
|
||||
Draw_Unlock();
|
||||
|
||||
do
|
||||
{
|
||||
Draw_Lock();
|
||||
|
||||
@ -389,78 +389,25 @@ void SysConfigMenu_ToggleCardIfPower(void)
|
||||
|
||||
static Result SysConfigMenu_ApplyVolumeOverride(void)
|
||||
{
|
||||
// This feature repurposes the functionality used for the camera shutter sound.
|
||||
// As such, it interferes with it:
|
||||
// - shutter volume is set to the override instead of its default 100% value
|
||||
// - due to implementation details, having the shutter sound effect play will
|
||||
// make this feature stop working until the volume override is reapplied by
|
||||
// going back to this menu
|
||||
|
||||
// Original credit: profi200
|
||||
|
||||
u8 i2s1Mux;
|
||||
u8 i2s2Mux;
|
||||
// Credit: profi200
|
||||
u8 tmp;
|
||||
Result res = cdcChkInit();
|
||||
|
||||
if (R_SUCCEEDED(res)) res = CDCCHK_ReadRegisters2(0, 116, &i2s1Mux, 1); // used for shutter sound in TWL mode, and all GBA/DSi/3DS application
|
||||
if (R_SUCCEEDED(res)) res = CDCCHK_ReadRegisters2(100, 49, &i2s2Mux, 1); // used for shutter sound in CTR mode and CTR mode library applets
|
||||
|
||||
if (R_SUCCEEDED(res)) res = CDCCHK_ReadRegisters2(0, 116, &tmp, 1); // CDC_REG_VOL_MICDET_PIN_SAR_ADC
|
||||
if (currVolumeSliderOverride >= 0)
|
||||
{
|
||||
i2s1Mux &= ~0x80;
|
||||
i2s2Mux |= 0x20;
|
||||
}
|
||||
tmp &= ~0x80;
|
||||
else
|
||||
{
|
||||
i2s1Mux |= 0x80;
|
||||
i2s2Mux &= ~0x20;
|
||||
tmp |= 0x80;
|
||||
if (R_SUCCEEDED(res)) res = CDCCHK_WriteRegisters2(0, 116, &tmp, 1);
|
||||
|
||||
if (currVolumeSliderOverride >= 0) {
|
||||
s8 calculated = -128 + (((float)currVolumeSliderOverride/100.f) * 108);
|
||||
if (calculated > -20)
|
||||
res = -1; // Just in case
|
||||
s8 volumes[2] = {calculated, calculated}; // Volume in 0.5 dB steps. -128 (muted) to 48. Do not go above -20 (100%).
|
||||
if (R_SUCCEEDED(res)) res = CDCCHK_WriteRegisters2(0, 65, volumes, 2); // CDC_REG_DAC_L_VOLUME_CTRL, CDC_REG_DAC_R_VOLUME_CTRL
|
||||
}
|
||||
|
||||
s8 i2s1Volume;
|
||||
s8 i2s2Volume;
|
||||
if (currVolumeSliderOverride >= 0)
|
||||
{
|
||||
// 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
|
||||
{
|
||||
// Restore shutter sound volumes. This sould be sourced from cfg,
|
||||
// however the values are the same everwhere
|
||||
i2s1Volume = -3; // -1.5 dB (115.7%, only used by TWL applications when taking photos)
|
||||
i2s2Volume = -20; // -10 dB (100%)
|
||||
}
|
||||
|
||||
// Write volume overrides values before writing to the pinmux registers
|
||||
if (R_SUCCEEDED(res)) res = CDCCHK_WriteRegisters2(0, 65, &i2s1Volume, 1); // CDC_REG_DAC_L_VOLUME_CTRL
|
||||
if (R_SUCCEEDED(res)) res = CDCCHK_WriteRegisters2(0, 66, &i2s1Volume, 1); // CDC_REG_DAC_R_VOLUME_CTRL
|
||||
if (R_SUCCEEDED(res)) res = CDCCHK_WriteRegisters2(100, 123, &i2s2Volume, 1);
|
||||
|
||||
if (R_SUCCEEDED(res)) res = CDCCHK_WriteRegisters2(0, 116, &i2s1Mux, 1);
|
||||
if (R_SUCCEEDED(res)) res = CDCCHK_WriteRegisters2(100, 49, &i2s2Mux, 1);
|
||||
|
||||
cdcChkExit();
|
||||
return res;
|
||||
}
|
||||
@ -582,7 +529,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 calibration.\n");
|
||||
posY = Draw_DrawString(10, posY, COLOR_WHITE, " * value will be limited by the presets.\n");
|
||||
posY = Draw_DrawString(10, posY, COLOR_WHITE, " * bottom framebuffer will be restored until\nyou exit.");
|
||||
Draw_FlushFramebuffer();
|
||||
Draw_Unlock();
|
||||
|
||||
@ -142,7 +142,7 @@ static Result CheckPluginCompatibility(_3gx_Header *header, u32 processTitle)
|
||||
return -1;
|
||||
}
|
||||
|
||||
bool TryToLoadPlugin(Handle process, bool isHomebrew)
|
||||
bool TryToLoadPlugin(Handle process)
|
||||
{
|
||||
u64 tid;
|
||||
u64 fileSize;
|
||||
@ -208,14 +208,7 @@ bool TryToLoadPlugin(Handle process, bool isHomebrew)
|
||||
// Check compatibility
|
||||
if (!res && fileHeader.infos.compatibility == PLG_COMPAT_EMULATOR) {
|
||||
ctx->error.message = "Plugin is only compatible with emulators";
|
||||
res = -1;
|
||||
}
|
||||
|
||||
// Check if plugin can load on homebrew
|
||||
if (!res && (isHomebrew && !fileHeader.infos.allowHomebrewLoad)) {
|
||||
// Do not display message as this is a common case
|
||||
ctx->error.message = NULL;
|
||||
res = -1;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Flags
|
||||
@ -270,7 +263,6 @@ 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,6 +136,7 @@ Result PLGLDR__DisplayMenu(PluginMenu *menu)
|
||||
{
|
||||
res = cmdbuf[1];
|
||||
}
|
||||
else Flash(0xFF0000);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
@ -20,6 +20,9 @@ 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;
|
||||
@ -93,7 +96,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;
|
||||
@ -102,7 +105,7 @@ Result PluginLoader__SetMode3AppMode(bool enable)
|
||||
|
||||
if (enable) {
|
||||
ControlApplicationMemoryModeOverrideConfig* mode = (ControlApplicationMemoryModeOverrideConfig*)&cmdbuf[1];
|
||||
|
||||
|
||||
memset(mode, 0, sizeof(ControlApplicationMemoryModeOverrideConfig));
|
||||
mode->query = true;
|
||||
cmdbuf[0] = IPC_MakeHeader(0x101, 1, 0); // ControlApplicationMemoryModeOverride
|
||||
@ -127,12 +130,16 @@ Result PluginLoader__SetMode3AppMode(bool enable)
|
||||
res = cmdbuf[1];
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
@ -144,7 +151,7 @@ void PluginLoader__HandleCommands(void *_ctx)
|
||||
{
|
||||
case 1: // Load plugin
|
||||
{
|
||||
if (cmdbuf[0] != IPC_MakeHeader(1, 2, 0))
|
||||
if (cmdbuf[0] != IPC_MakeHeader(1, 1, 0))
|
||||
{
|
||||
error(cmdbuf, 0xD9001830);
|
||||
break;
|
||||
@ -157,7 +164,7 @@ void PluginLoader__HandleCommands(void *_ctx)
|
||||
TaskRunner_RunTask(j_PluginLoader__SetMode3AppMode, NULL, 0);
|
||||
|
||||
bool flash = !(ctx->useUserLoadParameters && ctx->userLoadParameters.noFlash);
|
||||
if (ctx->isEnabled && TryToLoadPlugin(ctx->target, cmdbuf[2]))
|
||||
if (ctx->isEnabled && TryToLoadPlugin(ctx->target))
|
||||
{
|
||||
if (flash)
|
||||
{
|
||||
@ -169,6 +176,8 @@ void PluginLoader__HandleCommands(void *_ctx)
|
||||
}
|
||||
REG32(0x10202204) = 0;
|
||||
}
|
||||
//if (!ctx->userLoadParameters.noIRPatch)
|
||||
// IR__Patch();
|
||||
PLG__SetConfigMemoryStatus(PLG_CFG_RUNNING);
|
||||
}
|
||||
else
|
||||
@ -231,14 +240,14 @@ void PluginLoader__HandleCommands(void *_ctx)
|
||||
params->pluginMemoryStrategy = (cmdbuf[1] >> 8) & 0xFF;
|
||||
params->persistent = (cmdbuf[1] >> 16) & 0x1;
|
||||
params->lowTitleId = cmdbuf[2];
|
||||
|
||||
|
||||
strncpy(params->path, (const char *)cmdbuf[4], 255);
|
||||
memcpy(params->config, (void *)cmdbuf[6], 32 * sizeof(u32));
|
||||
|
||||
if (params->persistent)
|
||||
{
|
||||
IFile file;
|
||||
if (R_SUCCEEDED(IFile_Open(&file, ARCHIVE_SDMC, fsMakePath(PATH_EMPTY, ""), fsMakePath(PATH_ASCII, "/luma/plugins/user_param.bin"),
|
||||
if (R_SUCCEEDED(IFile_Open(&file, ARCHIVE_SDMC, fsMakePath(PATH_EMPTY, ""), fsMakePath(PATH_ASCII, "/luma/plugins/user_param.bin"),
|
||||
FS_OPEN_CREATE | FS_OPEN_READ | FS_OPEN_WRITE))) {
|
||||
u64 tempWritten;
|
||||
u32 magic = PERS_USER_FILE_MAGIC;
|
||||
@ -367,9 +376,9 @@ void PluginLoader__HandleCommands(void *_ctx)
|
||||
error(cmdbuf, 0xD9001830);
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
g_blockMenuOpen = cmdbuf[1];
|
||||
|
||||
|
||||
cmdbuf[0] = IPC_MakeHeader(11, 1, 0);
|
||||
cmdbuf[1] = 0;
|
||||
break;
|
||||
@ -433,7 +442,7 @@ void PluginLoader__HandleCommands(void *_ctx)
|
||||
Reset_3gx_LoadParams();
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
ctx->isExeLoadFunctionset = true;
|
||||
|
||||
svcInvalidateEntireInstructionCache(); // Could use the range one
|
||||
@ -507,7 +516,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, MemoryBlock__GetMappedPluginHeader()->waitForReplyTimeout);
|
||||
svcArbitrateAddress(PluginLoaderCtx.arbiter, (u32)PluginLoaderCtx.plgReplyPA, ARBITRATION_WAIT_IF_LESS_THAN_TIMEOUT, PLG_OK, 10000000000ULL);
|
||||
}
|
||||
|
||||
void PLG__SetConfigMemoryStatus(u32 status)
|
||||
@ -532,7 +541,7 @@ static void WaitForProcessTerminated(void *arg)
|
||||
|
||||
// Wait until all threads of the process have finished (svcWaitSynchronization == 0) or 2.5 seconds have passed.
|
||||
for (u32 i = 0; svcWaitSynchronization(ctx->target, 0) != 0 && i < 50; i++) svcSleepThread(50000000); // 50ms
|
||||
|
||||
|
||||
// Unmap plugin's memory before closing the process
|
||||
if (!ctx->pluginIsSwapped) {
|
||||
MemoryBlock__UnmountFromProcess();
|
||||
@ -552,6 +561,8 @@ static void WaitForProcessTerminated(void *arg)
|
||||
ctx->isMemPrivate = false;
|
||||
g_blockMenuOpen = 0;
|
||||
MemoryBlock__ResetSwapSettings();
|
||||
//if (!ctx->userLoadParameters.noIRPatch)
|
||||
// IR__Unpatch();
|
||||
}
|
||||
|
||||
void PluginLoader__HandleKernelEvent(u32 notifId)
|
||||
@ -632,11 +643,12 @@ void PluginLoader__HandleKernelEvent(u32 notifId)
|
||||
PLG__NotifyEvent(PLG_HOME_ENTER, false);
|
||||
// Wait for plugin reply
|
||||
PLG__WaitForReply();
|
||||
}
|
||||
}
|
||||
PLG__SetConfigMemoryStatus(PLG_CFG_INHOME);
|
||||
}
|
||||
ctx->pluginIsHome = !ctx->pluginIsHome;
|
||||
}
|
||||
|
||||
}
|
||||
srvPublishToSubscriber(0x1002, 0);
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user