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)
|
||||
{
|
||||
FATFS fs;
|
||||
|
||||
f_mount(&fs, "0:", 1);
|
||||
|
||||
//Get pressed buttons
|
||||
|
413
patches/reboot.s
413
patches/reboot.s
@ -1,221 +1,232 @@
|
||||
.arm.little
|
||||
|
||||
byteswritten equ 0x2000E000
|
||||
kernelCode equ 0x080F0000
|
||||
buffer equ 0x24000000
|
||||
fileOpen equ 0x4E45504F ;dummy
|
||||
firm_addr equ 0x24000000 ; Temporary location where we'll load the FIRM to
|
||||
firm_maxsize equ 0x200000 ; Random value that's bigger than any of the currently known firm's sizes.
|
||||
kernel_code equ 0x080F0000
|
||||
|
||||
.create "reboot.bin", 0
|
||||
.arm
|
||||
//Code jumps here right after the sprintf call
|
||||
process9Reboot:
|
||||
doPxi:
|
||||
ldr r4, =0x44846
|
||||
ldr r0, =0x10008000
|
||||
readPxiLoop1:
|
||||
ldrh r1, [r0,#4]
|
||||
.word 0xE1B01B81 //lsls r1, r1, #0x17
|
||||
bmi readPxiLoop1
|
||||
ldr r0, [r0,#0xC]
|
||||
cmp r0, r4
|
||||
bne doPxi
|
||||
|
||||
GetFirmPath:
|
||||
add r0, sp, #0x3A8-0x70+0x24
|
||||
ldr r1, [r0], #4
|
||||
ldr r2, =0x00300030
|
||||
cmp r1, r2
|
||||
ldreq r1, [r0], #4
|
||||
ldreq r2, =0x002F0032
|
||||
cmpeq r1, r2
|
||||
|
||||
OpenFirm:
|
||||
ldreq r1, =(FileName - OpenFirm - 12)
|
||||
addeq r1, pc
|
||||
addne r1, sp, #0x3A8-0x70
|
||||
moveq r2, #1
|
||||
movne r2, #0
|
||||
str r2, [externalFirm]
|
||||
mov r2, #1
|
||||
add r0, r7, #8
|
||||
ldr r6, =fileOpen
|
||||
blx r6
|
||||
|
||||
SeekFirm:
|
||||
ldr r0, [externalFirm]
|
||||
cmp r0, #1
|
||||
moveq r0, r7
|
||||
ldreq r1, =byteswritten
|
||||
ldreq r2, =buffer
|
||||
ldreq r3, =0x0
|
||||
ldreq r6, [sp,#0x3A8-0x198]
|
||||
ldreq r6, [r6,#0x28] //fread function stored here
|
||||
blxeq r6
|
||||
|
||||
ReadFirm:
|
||||
; Interesting registers and locations to keep in mind, set before this code is ran:
|
||||
; - sp + 0x3A8 - 0x70: FIRM path in exefs.
|
||||
; - r7 (which is sp + 0x3A8 - 0x198): Reserved space for file handle
|
||||
; - *(sp + 0x3A8 - 0x198) + 0x28: fread function.
|
||||
|
||||
pxi_wait_recv:
|
||||
ldr r2, =0x44846
|
||||
ldr r0, =0x10008000
|
||||
readPxiLoop1:
|
||||
ldrh r1, [r0, #4]
|
||||
lsls r1, #0x17
|
||||
bmi readPxiLoop1
|
||||
ldr r0, [r0, #0xC]
|
||||
cmp r0, r2
|
||||
bne pxi_wait_recv
|
||||
|
||||
; Convert 2 bytes of the path string
|
||||
; This will be the method of getting the lower 2 bytes of the title ID
|
||||
; until someone bothers figuring out where the value is derived from.
|
||||
mov r0, #0 ; Result
|
||||
add r1, sp, #0x3A8 - 0x70
|
||||
add r1, #0x22 ; The significant bytes
|
||||
mov r2, #4 ; Maximum loops (amount of bytes * 2)
|
||||
|
||||
hex_string_to_int_loop:
|
||||
ldr r3, [r1], #2 ; 2 because it's a utf-16 string.
|
||||
and r3, #0xFF
|
||||
|
||||
; Check if it"s a number
|
||||
cmp r3, #'0'
|
||||
blo hex_string_to_int_end
|
||||
sub r3, #'0'
|
||||
cmp r3, #9
|
||||
bls hex_string_to_int_calc
|
||||
|
||||
; Check if it"s a capital letter
|
||||
cmp r3, #'A' - '0'
|
||||
blo hex_string_to_int_end
|
||||
sub r3, #'A' - '0' - 0xA ; Make the correct value: 0xF >= al >= 0xA
|
||||
cmp r3, #0xF
|
||||
bls hex_string_to_int_calc
|
||||
|
||||
; Incorrect value: x > "A"
|
||||
bhi hex_string_to_int_end
|
||||
|
||||
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
|
||||
ldr r1, =byteswritten
|
||||
ldr r2, =buffer
|
||||
ldr r3, =0x200000
|
||||
ldr r6, [sp,#0x3A8-0x198]
|
||||
ldr r6, [r6,#0x28] //fread function stored here
|
||||
adr r1, bytes_read
|
||||
mov r2, firm_addr
|
||||
mov r3, firm_maxsize
|
||||
ldr r6, [sp, #0x3A8-0x198]
|
||||
ldr r6, [r6, #0x28]
|
||||
blx r6
|
||||
|
||||
KernelSetState:
|
||||
mov r2, #0
|
||||
mov r3, r2
|
||||
mov r1, r2
|
||||
mov r0, r2
|
||||
.word 0xEF00007C //SVC 0x7C
|
||||
; Set kernel state
|
||||
mov r0, #0
|
||||
mov r1, #0
|
||||
mov r2, #0
|
||||
mov r3, #0
|
||||
swi 0x7C
|
||||
|
||||
GoToReboot:
|
||||
ldr r0, =(KernelCodeStart - GoToReboot - 12)
|
||||
add r0, pc
|
||||
ldr r1, =kernelCode
|
||||
ldr r2, =0x300
|
||||
bl Memcpy
|
||||
|
||||
ldr r0, =kernelCode
|
||||
.word 0xEF00007B //SVC 0x7B
|
||||
goto_reboot:
|
||||
; Jump to reboot code
|
||||
ldr r0, =(kernelcode_start - goto_reboot - 12)
|
||||
add r0, pc
|
||||
ldr r1, =kernel_code
|
||||
ldr r2, =0x300
|
||||
bl memcpy32
|
||||
ldr r0, =kernel_code
|
||||
swi 0x7B
|
||||
|
||||
InfiniteLoop:
|
||||
b InfiniteLoop
|
||||
die:
|
||||
b die
|
||||
|
||||
Memcpy:
|
||||
MOV R12, LR
|
||||
STMFD SP!, {R0-R4}
|
||||
ADD R2, R2, R0
|
||||
|
||||
memcpyLoop:
|
||||
LDR R3, [R0],#4
|
||||
STR R3, [R1],#4
|
||||
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
|
||||
memcpy32: ; memcpy32(void *src, void *dst, unsigned int size)
|
||||
add r2, r0
|
||||
memcpy32_loop:
|
||||
ldmia r0!, {r3}
|
||||
stmia r1!, {r3}
|
||||
cmp r0, r2
|
||||
blo memcpy32_loop
|
||||
bx lr
|
||||
|
||||
bytes_read: .word 0
|
||||
fopen: .ascii "OPEN"
|
||||
.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
|
||||
KernelCodeStart:
|
||||
memorySetting:
|
||||
MRC p15, 0, R0,c2,c0, 0
|
||||
MRC p15, 0, R12,c2,c0, 1
|
||||
MRC p15, 0, R1,c3,c0, 0
|
||||
MRC p15, 0, R2,c5,c0, 2
|
||||
MRC p15, 0, R3,c5,c0, 3
|
||||
LDR R4, =0x18000035
|
||||
BIC R2, R2, #0xF0000
|
||||
BIC R3, R3, #0xF0000
|
||||
ORR R0, R0, #0x10
|
||||
ORR R2, R2, #0x30000
|
||||
ORR R3, R3, #0x30000
|
||||
ORR R12, R12, #0x10
|
||||
ORR R1, R1, #0x10
|
||||
MCR p15, 0, R0,c2,c0, 0
|
||||
MCR p15, 0, R12,c2,c0, 1
|
||||
MCR p15, 0, R1,c3,c0, 0
|
||||
MCR p15, 0, R2,c5,c0, 2
|
||||
MCR p15, 0, R3,c5,c0, 3
|
||||
MCR p15, 0, R4,c6,c4, 0
|
||||
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
|
||||
kernelcode_start:
|
||||
; Set MPU settings
|
||||
mrc p15, 0, r0, c2, c0, 0 ; dcacheable
|
||||
mrc p15, 0, r12, c2, c0, 1 ; icacheable
|
||||
mrc p15, 0, r1, c3, c0, 0 ; write bufferable
|
||||
mrc p15, 0, r2, c5, c0, 2 ; daccess
|
||||
mrc p15, 0, r3, c5, c0, 3 ; iaccess
|
||||
ldr r4, =0x18000035 ; 0x18000000 128M
|
||||
bic r2, r2, #0xF0000 ; unprotect region 4
|
||||
bic r3, r3, #0xF0000 ; unprotect region 4
|
||||
orr r0, r0, #0x10 ; dcacheable region 4
|
||||
orr r2, r2, #0x30000 ; region 4 r/w
|
||||
orr r3, r3, #0x30000 ; region 4 r/w
|
||||
orr r12, r12, #0x10 ; icacheable region 4
|
||||
orr r1, r1, #0x10 ; write bufferable region 4
|
||||
mcr p15, 0, r0, c2, c0, 0
|
||||
mcr p15, 0, r12, c2, c0, 1
|
||||
mcr p15, 0, r1, c3, c0, 0 ; write bufferable
|
||||
mcr p15, 0, r2, c5, c0, 2 ; daccess
|
||||
mcr p15, 0, r3, c5, c0, 3 ; iaccess
|
||||
mcr p15, 0, r4, c6, c4, 0 ; region 4 (hmmm)
|
||||
|
||||
copyFirmPartitions:
|
||||
LDR R4, =buffer
|
||||
ADD R3, R4, #0x40
|
||||
LDR R0, [R3]
|
||||
ADD R0, R0, R4
|
||||
LDR R1, [R3,#4]
|
||||
LDR R2, [R3,#8]
|
||||
bl KernelMemcpy
|
||||
|
||||
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
|
||||
mrc p15, 0, r0, c2, c0, 0 ; dcacheable
|
||||
mrc p15, 0, r1, c2, c0, 1 ; icacheable
|
||||
mrc p15, 0, r2, c3, c0, 0 ; write bufferable
|
||||
orr r0, r0, #0x20 ; dcacheable region 5
|
||||
orr r1, r1, #0x20 ; icacheable region 5
|
||||
orr r2, r2, #0x20 ; write bufferable region 5
|
||||
mcr p15, 0, r0, c2, c0, 0 ; dcacheable
|
||||
mcr p15, 0, r1, c2, c0, 1 ; icacheable
|
||||
mcr p15, 0, r2, c3, c0, 0 ; write bufferable
|
||||
|
||||
jumpToEntrypoint:
|
||||
MCR p15, 0, R1,c7,c10, 4
|
||||
LDR R0, =0x42078
|
||||
MCR p15, 0, R0,c1,c0, 0
|
||||
MCR p15, 0, R1,c7,c5, 0
|
||||
MCR p15, 0, R1,c7,c6, 0
|
||||
MCR p15, 0, R1,c7,c10, 4
|
||||
LDR R4, =buffer
|
||||
MOV R1, #0x1FFFFFFC
|
||||
LDR R2, [R4,#8]
|
||||
STR R2, [R1]
|
||||
LDR R0, [R4,#0xC]
|
||||
BX R0
|
||||
; Copy the firmware
|
||||
mov r4, firm_addr
|
||||
add r5, r4, #0x40 ; Start of loop
|
||||
add r6, r5, #0x30 * 3 ; End of loop (scan 4 entries)
|
||||
|
||||
copy_firm_loop:
|
||||
ldr r0, [r5]
|
||||
cmp r0, #0
|
||||
addne r0, r4 ; src
|
||||
ldrne r1, [r5, #4] ; dest
|
||||
ldrne r2, [r5, #8] ; size
|
||||
blne kernelmemcpy32
|
||||
|
||||
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
|
||||
|
||||
KernelMemcpy:
|
||||
MOV R12, LR
|
||||
STMFD SP!, {R0-R4}
|
||||
ADD R2, R2, R0
|
||||
|
||||
kmemcpyLoop:
|
||||
LDR R3, [R0],#4
|
||||
STR R3, [R1],#4
|
||||
CMP R0, R2
|
||||
BLT kmemcpyLoop
|
||||
LDMFD SP!, {R0-R4}
|
||||
MOV LR, R12
|
||||
BX LR
|
||||
.pool
|
||||
|
||||
KernelCodeEnd:
|
||||
|
||||
kernelmemcpy32: ; memcpy32(void *src, void *dst, unsigned int size)
|
||||
add r2, r0
|
||||
kmemcpy32_loop:
|
||||
ldmia r0!, {r3}
|
||||
stmia r1!, {r3}
|
||||
cmp r0, r2
|
||||
blo kmemcpy32_loop
|
||||
bx lr
|
||||
.close
|
||||
|
@ -26,7 +26,9 @@ static u8 *arm9Section;
|
||||
static const char *patchedFirms[] = { "/aurei/patched_firmware_sys.bin",
|
||||
"/aurei/patched_firmware_emu.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,
|
||||
console,
|
||||
@ -212,6 +214,80 @@ void loadFirm(void)
|
||||
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
|
||||
static inline void patchEmuNAND(u8 *proc9Offset)
|
||||
{
|
||||
@ -297,6 +373,10 @@ static inline void injectLoader(void)
|
||||
//Patches
|
||||
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
|
||||
if(usePatchedFirm) return;
|
||||
|
||||
|
Reference in New Issue
Block a user