Added TWL/AGB FIRM patching/SD loading for New and Old 3DS (thanks to mid-kid of CakesFW for making it possible!)
This commit is contained in:
parent
993e564fbb
commit
a181bba9f2
@ -23,6 +23,7 @@ static u32 loadPayload(const char *path)
|
|||||||
void main(void)
|
void main(void)
|
||||||
{
|
{
|
||||||
FATFS fs;
|
FATFS fs;
|
||||||
|
|
||||||
f_mount(&fs, "0:", 1);
|
f_mount(&fs, "0:", 1);
|
||||||
|
|
||||||
//Get pressed buttons
|
//Get pressed buttons
|
||||||
|
413
patches/reboot.s
413
patches/reboot.s
@ -1,221 +1,232 @@
|
|||||||
.arm.little
|
.arm.little
|
||||||
|
|
||||||
byteswritten equ 0x2000E000
|
firm_addr equ 0x24000000 ; Temporary location where we'll load the FIRM to
|
||||||
kernelCode equ 0x080F0000
|
firm_maxsize equ 0x200000 ; Random value that's bigger than any of the currently known firm's sizes.
|
||||||
buffer equ 0x24000000
|
kernel_code equ 0x080F0000
|
||||||
fileOpen equ 0x4E45504F ;dummy
|
|
||||||
|
|
||||||
.create "reboot.bin", 0
|
.create "reboot.bin", 0
|
||||||
.arm
|
.arm
|
||||||
//Code jumps here right after the sprintf call
|
; Interesting registers and locations to keep in mind, set before this code is ran:
|
||||||
process9Reboot:
|
; - sp + 0x3A8 - 0x70: FIRM path in exefs.
|
||||||
doPxi:
|
; - r7 (which is sp + 0x3A8 - 0x198): Reserved space for file handle
|
||||||
ldr r4, =0x44846
|
; - *(sp + 0x3A8 - 0x198) + 0x28: fread function.
|
||||||
ldr r0, =0x10008000
|
|
||||||
readPxiLoop1:
|
pxi_wait_recv:
|
||||||
ldrh r1, [r0,#4]
|
ldr r2, =0x44846
|
||||||
.word 0xE1B01B81 //lsls r1, r1, #0x17
|
ldr r0, =0x10008000
|
||||||
bmi readPxiLoop1
|
readPxiLoop1:
|
||||||
ldr r0, [r0,#0xC]
|
ldrh r1, [r0, #4]
|
||||||
cmp r0, r4
|
lsls r1, #0x17
|
||||||
bne doPxi
|
bmi readPxiLoop1
|
||||||
|
ldr r0, [r0, #0xC]
|
||||||
GetFirmPath:
|
cmp r0, r2
|
||||||
add r0, sp, #0x3A8-0x70+0x24
|
bne pxi_wait_recv
|
||||||
ldr r1, [r0], #4
|
|
||||||
ldr r2, =0x00300030
|
; Convert 2 bytes of the path string
|
||||||
cmp r1, r2
|
; This will be the method of getting the lower 2 bytes of the title ID
|
||||||
ldreq r1, [r0], #4
|
; until someone bothers figuring out where the value is derived from.
|
||||||
ldreq r2, =0x002F0032
|
mov r0, #0 ; Result
|
||||||
cmpeq r1, r2
|
add r1, sp, #0x3A8 - 0x70
|
||||||
|
add r1, #0x22 ; The significant bytes
|
||||||
OpenFirm:
|
mov r2, #4 ; Maximum loops (amount of bytes * 2)
|
||||||
ldreq r1, =(FileName - OpenFirm - 12)
|
|
||||||
addeq r1, pc
|
hex_string_to_int_loop:
|
||||||
addne r1, sp, #0x3A8-0x70
|
ldr r3, [r1], #2 ; 2 because it's a utf-16 string.
|
||||||
moveq r2, #1
|
and r3, #0xFF
|
||||||
movne r2, #0
|
|
||||||
str r2, [externalFirm]
|
; Check if it"s a number
|
||||||
mov r2, #1
|
cmp r3, #'0'
|
||||||
add r0, r7, #8
|
blo hex_string_to_int_end
|
||||||
ldr r6, =fileOpen
|
sub r3, #'0'
|
||||||
blx r6
|
cmp r3, #9
|
||||||
|
bls hex_string_to_int_calc
|
||||||
SeekFirm:
|
|
||||||
ldr r0, [externalFirm]
|
; Check if it"s a capital letter
|
||||||
cmp r0, #1
|
cmp r3, #'A' - '0'
|
||||||
moveq r0, r7
|
blo hex_string_to_int_end
|
||||||
ldreq r1, =byteswritten
|
sub r3, #'A' - '0' - 0xA ; Make the correct value: 0xF >= al >= 0xA
|
||||||
ldreq r2, =buffer
|
cmp r3, #0xF
|
||||||
ldreq r3, =0x0
|
bls hex_string_to_int_calc
|
||||||
ldreq r6, [sp,#0x3A8-0x198]
|
|
||||||
ldreq r6, [r6,#0x28] //fread function stored here
|
; Incorrect value: x > "A"
|
||||||
blxeq r6
|
bhi hex_string_to_int_end
|
||||||
|
|
||||||
ReadFirm:
|
hex_string_to_int_calc:
|
||||||
|
orr r0, r3, r0, lsl #4
|
||||||
|
subs r2, #1
|
||||||
|
bne hex_string_to_int_loop
|
||||||
|
hex_string_to_int_end:
|
||||||
|
|
||||||
|
; Get the FIRM path
|
||||||
|
cmp r0, #0x0002 ; NATIVE_FIRM
|
||||||
|
adreq r1, firm_fname
|
||||||
|
beq load_firm
|
||||||
|
ldr r5, =0x0102 ; TWL_FIRM
|
||||||
|
cmp r0, r5
|
||||||
|
adreq r1, twlfirm_fname
|
||||||
|
beq load_firm
|
||||||
|
ldr r5, =0x0202 ; AGB_FIRM
|
||||||
|
cmp r0, r5
|
||||||
|
adreq r1, agbfirm_fname
|
||||||
|
beq load_firm
|
||||||
|
bne fallback ; TODO: Stubbed
|
||||||
|
|
||||||
|
fallback:
|
||||||
|
; Fallback: Load specified FIRM from exefs
|
||||||
|
add r1, sp, #0x3A8-0x70 ; Location of exefs string.
|
||||||
|
b load_firm
|
||||||
|
|
||||||
|
load_firm:
|
||||||
|
; Open file
|
||||||
|
add r0, r7, #8
|
||||||
|
mov r2, #1
|
||||||
|
ldr r6, [fopen]
|
||||||
|
orr r6, 1
|
||||||
|
blx r6
|
||||||
|
|
||||||
|
cmp r0, #0 ; Check if we were able to load the FIRM
|
||||||
|
bne fallback ; Otherwise, try again with the FIRM from exefs.
|
||||||
|
; This will loop indefinitely if the exefs FIRM fails to load, but whatever.
|
||||||
|
|
||||||
|
; Read file
|
||||||
mov r0, r7
|
mov r0, r7
|
||||||
ldr r1, =byteswritten
|
adr r1, bytes_read
|
||||||
ldr r2, =buffer
|
mov r2, firm_addr
|
||||||
ldr r3, =0x200000
|
mov r3, firm_maxsize
|
||||||
ldr r6, [sp,#0x3A8-0x198]
|
ldr r6, [sp, #0x3A8-0x198]
|
||||||
ldr r6, [r6,#0x28] //fread function stored here
|
ldr r6, [r6, #0x28]
|
||||||
blx r6
|
blx r6
|
||||||
|
|
||||||
KernelSetState:
|
; Set kernel state
|
||||||
mov r2, #0
|
mov r0, #0
|
||||||
mov r3, r2
|
mov r1, #0
|
||||||
mov r1, r2
|
mov r2, #0
|
||||||
mov r0, r2
|
mov r3, #0
|
||||||
.word 0xEF00007C //SVC 0x7C
|
swi 0x7C
|
||||||
|
|
||||||
GoToReboot:
|
goto_reboot:
|
||||||
ldr r0, =(KernelCodeStart - GoToReboot - 12)
|
; Jump to reboot code
|
||||||
add r0, pc
|
ldr r0, =(kernelcode_start - goto_reboot - 12)
|
||||||
ldr r1, =kernelCode
|
add r0, pc
|
||||||
ldr r2, =0x300
|
ldr r1, =kernel_code
|
||||||
bl Memcpy
|
ldr r2, =0x300
|
||||||
|
bl memcpy32
|
||||||
ldr r0, =kernelCode
|
ldr r0, =kernel_code
|
||||||
.word 0xEF00007B //SVC 0x7B
|
swi 0x7B
|
||||||
|
|
||||||
InfiniteLoop:
|
die:
|
||||||
b InfiniteLoop
|
b die
|
||||||
|
|
||||||
Memcpy:
|
memcpy32: ; memcpy32(void *src, void *dst, unsigned int size)
|
||||||
MOV R12, LR
|
add r2, r0
|
||||||
STMFD SP!, {R0-R4}
|
memcpy32_loop:
|
||||||
ADD R2, R2, R0
|
ldmia r0!, {r3}
|
||||||
|
stmia r1!, {r3}
|
||||||
memcpyLoop:
|
cmp r0, r2
|
||||||
LDR R3, [R0],#4
|
blo memcpy32_loop
|
||||||
STR R3, [R1],#4
|
bx lr
|
||||||
CMP R0, R2
|
|
||||||
BLT memcpyLoop
|
|
||||||
LDMFD SP!, {R0-R4}
|
|
||||||
MOV LR, R12
|
|
||||||
BX LR
|
|
||||||
|
|
||||||
FileName:
|
|
||||||
.dcw "sdmc:/aurei/patched_firmware_sys.bin"
|
|
||||||
.word 0x0
|
|
||||||
|
|
||||||
externalFirm:
|
|
||||||
.word 0x2000A000
|
|
||||||
|
|
||||||
|
bytes_read: .word 0
|
||||||
|
fopen: .ascii "OPEN"
|
||||||
.pool
|
.pool
|
||||||
|
firm_fname: .dcw "sdmc:/aurei/patched_firmware_sys.bin"
|
||||||
|
.word 0x0
|
||||||
|
.pool
|
||||||
|
twlfirm_fname: .dcw "sdmc:/aurei/patched_firmware_twl.bin"
|
||||||
|
.word 0x0
|
||||||
|
.pool
|
||||||
|
agbfirm_fname: .dcw "sdmc:/aurei/patched_firmware_agb.bin"
|
||||||
|
.word 0x0
|
||||||
|
|
||||||
// Kernel Code
|
|
||||||
.align 4
|
.align 4
|
||||||
KernelCodeStart:
|
kernelcode_start:
|
||||||
memorySetting:
|
; Set MPU settings
|
||||||
MRC p15, 0, R0,c2,c0, 0
|
mrc p15, 0, r0, c2, c0, 0 ; dcacheable
|
||||||
MRC p15, 0, R12,c2,c0, 1
|
mrc p15, 0, r12, c2, c0, 1 ; icacheable
|
||||||
MRC p15, 0, R1,c3,c0, 0
|
mrc p15, 0, r1, c3, c0, 0 ; write bufferable
|
||||||
MRC p15, 0, R2,c5,c0, 2
|
mrc p15, 0, r2, c5, c0, 2 ; daccess
|
||||||
MRC p15, 0, R3,c5,c0, 3
|
mrc p15, 0, r3, c5, c0, 3 ; iaccess
|
||||||
LDR R4, =0x18000035
|
ldr r4, =0x18000035 ; 0x18000000 128M
|
||||||
BIC R2, R2, #0xF0000
|
bic r2, r2, #0xF0000 ; unprotect region 4
|
||||||
BIC R3, R3, #0xF0000
|
bic r3, r3, #0xF0000 ; unprotect region 4
|
||||||
ORR R0, R0, #0x10
|
orr r0, r0, #0x10 ; dcacheable region 4
|
||||||
ORR R2, R2, #0x30000
|
orr r2, r2, #0x30000 ; region 4 r/w
|
||||||
ORR R3, R3, #0x30000
|
orr r3, r3, #0x30000 ; region 4 r/w
|
||||||
ORR R12, R12, #0x10
|
orr r12, r12, #0x10 ; icacheable region 4
|
||||||
ORR R1, R1, #0x10
|
orr r1, r1, #0x10 ; write bufferable region 4
|
||||||
MCR p15, 0, R0,c2,c0, 0
|
mcr p15, 0, r0, c2, c0, 0
|
||||||
MCR p15, 0, R12,c2,c0, 1
|
mcr p15, 0, r12, c2, c0, 1
|
||||||
MCR p15, 0, R1,c3,c0, 0
|
mcr p15, 0, r1, c3, c0, 0 ; write bufferable
|
||||||
MCR p15, 0, R2,c5,c0, 2
|
mcr p15, 0, r2, c5, c0, 2 ; daccess
|
||||||
MCR p15, 0, R3,c5,c0, 3
|
mcr p15, 0, r3, c5, c0, 3 ; iaccess
|
||||||
MCR p15, 0, R4,c6,c4, 0
|
mcr p15, 0, r4, c6, c4, 0 ; region 4 (hmmm)
|
||||||
MRC p15, 0, R0,c2,c0, 0
|
|
||||||
MRC p15, 0, R1,c2,c0, 1
|
|
||||||
MRC p15, 0, R2,c3,c0, 0
|
|
||||||
ORR R0, R0, #0x20
|
|
||||||
ORR R1, R1, #0x20
|
|
||||||
ORR R2, R2, #0x20
|
|
||||||
MCR p15, 0, R0,c2,c0, 0
|
|
||||||
MCR p15, 0, R1,c2,c0, 1
|
|
||||||
MCR p15, 0, R2,c3,c0, 0
|
|
||||||
|
|
||||||
copyFirmPartitions:
|
mrc p15, 0, r0, c2, c0, 0 ; dcacheable
|
||||||
LDR R4, =buffer
|
mrc p15, 0, r1, c2, c0, 1 ; icacheable
|
||||||
ADD R3, R4, #0x40
|
mrc p15, 0, r2, c3, c0, 0 ; write bufferable
|
||||||
LDR R0, [R3]
|
orr r0, r0, #0x20 ; dcacheable region 5
|
||||||
ADD R0, R0, R4
|
orr r1, r1, #0x20 ; icacheable region 5
|
||||||
LDR R1, [R3,#4]
|
orr r2, r2, #0x20 ; write bufferable region 5
|
||||||
LDR R2, [R3,#8]
|
mcr p15, 0, r0, c2, c0, 0 ; dcacheable
|
||||||
bl KernelMemcpy
|
mcr p15, 0, r1, c2, c0, 1 ; icacheable
|
||||||
|
mcr p15, 0, r2, c3, c0, 0 ; write bufferable
|
||||||
ADD R3, R4, #0x70
|
|
||||||
LDR R0, [R3]
|
|
||||||
ADD R0, R0, R4
|
|
||||||
LDR R1, [R3,#4]
|
|
||||||
LDR R2, [R3,#8]
|
|
||||||
bl KernelMemcpy
|
|
||||||
|
|
||||||
ADD R3, R4, #0xA0
|
|
||||||
LDR R0, [R3]
|
|
||||||
ADD R0, R0, R4
|
|
||||||
LDR R1, [R3,#4]
|
|
||||||
LDR R2, [R3,#8]
|
|
||||||
bl KernelMemcpy
|
|
||||||
|
|
||||||
ADD R3, R4, #0xD0
|
|
||||||
LDR R0, [R3]
|
|
||||||
CMP R0, #0
|
|
||||||
BEQ invalidateDataCache
|
|
||||||
ADD R0, R0, R4
|
|
||||||
LDR R1, [R3,#4]
|
|
||||||
LDR R2, [R3,#8]
|
|
||||||
bl KernelMemcpy
|
|
||||||
|
|
||||||
invalidateDataCache:
|
|
||||||
MOV R2, #0
|
|
||||||
MOV R1, R2
|
|
||||||
loc_809460C:
|
|
||||||
MOV R0, #0
|
|
||||||
MOV R3, R2,LSL#30
|
|
||||||
loc_8094614:
|
|
||||||
ORR R12, R3, R0,LSL#5
|
|
||||||
MCR p15, 0, R1,c7,c10, 4
|
|
||||||
MCR p15, 0, R12,c7,c14, 2
|
|
||||||
ADD R0, R0, #1
|
|
||||||
CMP R0, #0x20
|
|
||||||
BCC loc_8094614
|
|
||||||
ADD R2, R2, #1
|
|
||||||
CMP R2, #4
|
|
||||||
BCC loc_809460C
|
|
||||||
|
|
||||||
jumpToEntrypoint:
|
; Copy the firmware
|
||||||
MCR p15, 0, R1,c7,c10, 4
|
mov r4, firm_addr
|
||||||
LDR R0, =0x42078
|
add r5, r4, #0x40 ; Start of loop
|
||||||
MCR p15, 0, R0,c1,c0, 0
|
add r6, r5, #0x30 * 3 ; End of loop (scan 4 entries)
|
||||||
MCR p15, 0, R1,c7,c5, 0
|
|
||||||
MCR p15, 0, R1,c7,c6, 0
|
copy_firm_loop:
|
||||||
MCR p15, 0, R1,c7,c10, 4
|
ldr r0, [r5]
|
||||||
LDR R4, =buffer
|
cmp r0, #0
|
||||||
MOV R1, #0x1FFFFFFC
|
addne r0, r4 ; src
|
||||||
LDR R2, [R4,#8]
|
ldrne r1, [r5, #4] ; dest
|
||||||
STR R2, [R1]
|
ldrne r2, [r5, #8] ; size
|
||||||
LDR R0, [R4,#0xC]
|
blne kernelmemcpy32
|
||||||
BX R0
|
|
||||||
|
cmp r5, r6
|
||||||
|
addlo r5, #0x30
|
||||||
|
blo copy_firm_loop
|
||||||
|
|
||||||
|
; Flush cache
|
||||||
|
mov r2, #0
|
||||||
|
mov r1, r2
|
||||||
|
flush_cache:
|
||||||
|
mov r0, #0
|
||||||
|
mov r3, r2, lsl #30
|
||||||
|
flush_cache_inner_loop:
|
||||||
|
orr r12, r3, r0, lsl#5
|
||||||
|
mcr p15, 0, r1, c7, c10, 4 ; drain write buffer
|
||||||
|
mcr p15, 0, r12, c7, c14, 2 ; clean and flush dcache entry (index and segment)
|
||||||
|
add r0, #1
|
||||||
|
cmp r0, #0x20
|
||||||
|
bcc flush_cache_inner_loop
|
||||||
|
add r2, #1
|
||||||
|
cmp r2, #4
|
||||||
|
bcc flush_cache
|
||||||
|
|
||||||
|
; Enable MPU
|
||||||
|
ldr r0, =0x42078 ; alt vector select, enable itcm
|
||||||
|
mcr p15, 0, r0, c1, c0, 0
|
||||||
|
mcr p15, 0, r1, c7, c5, 0 ; flush dcache
|
||||||
|
mcr p15, 0, r1, c7, c6, 0 ; flush icache
|
||||||
|
mcr p15, 0, r1, c7, c10, 4 ; drain write buffer
|
||||||
|
mov r0, firm_addr
|
||||||
|
|
||||||
|
; Boot FIRM
|
||||||
|
mov r1, #0x1FFFFFFC
|
||||||
|
ldr r2, [r0, #8] ; arm11 entry
|
||||||
|
str r2, [r1]
|
||||||
|
ldr r0, [r0, #0xC] ; arm9 entry
|
||||||
|
bx r0
|
||||||
.pool
|
.pool
|
||||||
|
|
||||||
KernelMemcpy:
|
kernelmemcpy32: ; memcpy32(void *src, void *dst, unsigned int size)
|
||||||
MOV R12, LR
|
add r2, r0
|
||||||
STMFD SP!, {R0-R4}
|
kmemcpy32_loop:
|
||||||
ADD R2, R2, R0
|
ldmia r0!, {r3}
|
||||||
|
stmia r1!, {r3}
|
||||||
kmemcpyLoop:
|
cmp r0, r2
|
||||||
LDR R3, [R0],#4
|
blo kmemcpy32_loop
|
||||||
STR R3, [R1],#4
|
bx lr
|
||||||
CMP R0, R2
|
|
||||||
BLT kmemcpyLoop
|
|
||||||
LDMFD SP!, {R0-R4}
|
|
||||||
MOV LR, R12
|
|
||||||
BX LR
|
|
||||||
.pool
|
|
||||||
|
|
||||||
KernelCodeEnd:
|
|
||||||
|
|
||||||
.close
|
.close
|
||||||
|
@ -26,7 +26,9 @@ static u8 *arm9Section;
|
|||||||
static const char *patchedFirms[] = { "/aurei/patched_firmware_sys.bin",
|
static const char *patchedFirms[] = { "/aurei/patched_firmware_sys.bin",
|
||||||
"/aurei/patched_firmware_emu.bin",
|
"/aurei/patched_firmware_emu.bin",
|
||||||
"/aurei/patched_firmware_em2.bin",
|
"/aurei/patched_firmware_em2.bin",
|
||||||
"/aurei/patched_firmware90.bin" };
|
"/aurei/patched_firmware90.bin",
|
||||||
|
"/aurei/patched_firmware_twl.bin",
|
||||||
|
"/aurei/patched_firmware_agb.bin" };
|
||||||
|
|
||||||
static u32 firmSize,
|
static u32 firmSize,
|
||||||
console,
|
console,
|
||||||
@ -212,6 +214,80 @@ void loadFirm(void)
|
|||||||
if(console && !usePatchedFirm) decryptArm9Bin(arm9Section, mode);
|
if(console && !usePatchedFirm) decryptArm9Bin(arm9Section, mode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline void patchTwlAgb(u32 mode)
|
||||||
|
{
|
||||||
|
static firmHeader *const twlAgbFirm = (firmHeader *)0x25000000;
|
||||||
|
|
||||||
|
const char *path = mode ? "aurei/firmware_agb.bin" : "aurei/firmware_twl.bin";
|
||||||
|
u32 size = fileSize(path);
|
||||||
|
|
||||||
|
//Skip patching
|
||||||
|
if(!size) return;
|
||||||
|
|
||||||
|
fileRead(twlAgbFirm, path, size);
|
||||||
|
|
||||||
|
const firmSectionHeader *twlAgbSection = twlAgbFirm->section;
|
||||||
|
|
||||||
|
//Check that the loaded FIRM matches the console
|
||||||
|
if((((u32)twlAgbSection[3].address >> 8) & 0xFF) != (console ? 0x60 : 0x68))
|
||||||
|
error(mode ? "aurei/firmware_agb.bin doesn't match this\nconsole, or it's encrypted" :
|
||||||
|
"aurei/firmware_twl.bin doesn't match this\nconsole, or it's encrypted");
|
||||||
|
|
||||||
|
if(console) decryptArm9Bin((u8 *)twlAgbFirm + twlAgbSection[3].offset, 0);
|
||||||
|
|
||||||
|
struct patchData {
|
||||||
|
u32 offset[2];
|
||||||
|
union {
|
||||||
|
u8 type0[8];
|
||||||
|
u16 type1;
|
||||||
|
} patch;
|
||||||
|
u32 type;
|
||||||
|
};
|
||||||
|
|
||||||
|
const struct patchData twlPatches[] = {
|
||||||
|
{{0x1650C0, 0x165D64}, {{ 6, 0x00, 0x20, 0x4E, 0xB0, 0x70, 0xBD }}, 0},
|
||||||
|
{{0x173A0E, 0x17474A}, { .type1 = 0x2001 }, 1},
|
||||||
|
{{0x174802, 0x17553E}, { .type1 = 0x2000 }, 2},
|
||||||
|
{{0x174964, 0x1756A0}, { .type1 = 0x2000 }, 2},
|
||||||
|
{{0x174D52, 0x175A8E}, { .type1 = 0x2001 }, 2},
|
||||||
|
{{0x174D5E, 0x175A9A}, { .type1 = 0x2001 }, 2},
|
||||||
|
{{0x174D6A, 0x175AA6}, { .type1 = 0x2001 }, 2},
|
||||||
|
{{0x174E56, 0x175B92}, { .type1 = 0x2001 }, 1},
|
||||||
|
{{0x174E58, 0x175B94}, { .type1 = 0x4770 }, 1}
|
||||||
|
};
|
||||||
|
|
||||||
|
const struct patchData agbPatches[] = {
|
||||||
|
{{0x9D2A8, 0x9DF64}, {{ 6, 0x00, 0x20, 0x4E, 0xB0, 0x70, 0xBD }}, 0},
|
||||||
|
{{0xD7A12, 0xD8B8A}, { .type1 = 0xEF26 }, 1}
|
||||||
|
};
|
||||||
|
|
||||||
|
//Calculate the amount of patches to apply
|
||||||
|
u32 numPatches = mode ? (sizeof(agbPatches) / sizeof(struct patchData)) : (sizeof(twlPatches) / sizeof(struct patchData));
|
||||||
|
const struct patchData *patches = mode ? agbPatches : twlPatches;
|
||||||
|
|
||||||
|
//Patch
|
||||||
|
for(u32 i = 0; i < numPatches; i++)
|
||||||
|
{
|
||||||
|
switch(patches[i].type)
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
memcpy((u8 *)twlAgbFirm + patches[i].offset[console], patches[i].patch.type0 + 1, patches[i].patch.type0[0]);
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
*(u16 *)((u8 *)twlAgbFirm + patches[i].offset[console] + 2) = 0;
|
||||||
|
case 1:
|
||||||
|
*(u16 *)((u8 *)twlAgbFirm + patches[i].offset[console]) = patches[i].patch.type1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//Patch ARM9 entrypoint on N3DS to skip arm9loader
|
||||||
|
if(console)
|
||||||
|
twlAgbFirm->arm9Entry = (u8 *)0x801301C;
|
||||||
|
|
||||||
|
fileWrite(twlAgbFirm, mode ? patchedFirms[5] : patchedFirms[4], size);
|
||||||
|
}
|
||||||
|
|
||||||
//NAND redirection
|
//NAND redirection
|
||||||
static inline void patchEmuNAND(u8 *proc9Offset)
|
static inline void patchEmuNAND(u8 *proc9Offset)
|
||||||
{
|
{
|
||||||
@ -297,6 +373,10 @@ static inline void injectLoader(void)
|
|||||||
//Patches
|
//Patches
|
||||||
void patchFirm(void)
|
void patchFirm(void)
|
||||||
{
|
{
|
||||||
|
//Only patch AGB_FIRM/TWL_FIRM if the patched ones don't already exist
|
||||||
|
if(!fileExists(patchedFirms[4])) patchTwlAgb(0);
|
||||||
|
if(!fileExists(patchedFirms[5])) patchTwlAgb(1);
|
||||||
|
|
||||||
//Skip patching
|
//Skip patching
|
||||||
if(usePatchedFirm) return;
|
if(usePatchedFirm) return;
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user