LayeredFS
This commit is contained in:
parent
28e6ad3372
commit
5b6bd048a9
@ -1,80 +1,107 @@
|
||||
; Code from delebile
|
||||
|
||||
.arm.little
|
||||
.create "build/romfsredir.bin", 0
|
||||
|
||||
.macro addr, reg, func
|
||||
add reg, pc, #func-.-8
|
||||
.endmacro
|
||||
.macro load, reg, func
|
||||
ldr reg, [pc, #func-.-8]
|
||||
.endmacro
|
||||
.macro svc, svcnum
|
||||
.word 0xef000000 + svcnum
|
||||
.endmacro
|
||||
|
||||
; Patch by delebile
|
||||
|
||||
.arm
|
||||
; fsOpenFileDirectly function will be redirected here.
|
||||
; If the requested archive is not ROMFS, we'll return
|
||||
; to the original function.
|
||||
openFileDirectlyHook:
|
||||
cmp r3, #3
|
||||
beq openRomfs
|
||||
load r12, fsOpenFileDirectly
|
||||
nop ; Will be replaced with the original function opcode
|
||||
bx r12
|
||||
_start:
|
||||
|
||||
; We redirect ROMFS file opening by changing the parameters and call
|
||||
; the fsOpenFileDirectly function recursively. The parameter format:
|
||||
; r0 : fsUserHandle
|
||||
; r1 : Output FileHandle
|
||||
; r2 : Transaction (usually 0)
|
||||
; r3 : Archive ID
|
||||
; [sp, #0x00] : Archive PathType
|
||||
; [sp, #0x04] : Archive DataPointer
|
||||
; [sp, #0x08] : Archive PathSize
|
||||
; [sp, #0x0C] : File PathType
|
||||
; [sp, #0x10] : File DataPointer
|
||||
; [sp, #0x14] : File PathSize
|
||||
; [sp, #0x18] : File OpenFlags
|
||||
; [sp, #0x1C] : Attributes (usually 0)
|
||||
openRomfs:
|
||||
sub sp, sp, #0x50
|
||||
stmfd sp!, {r0, r1, lr}
|
||||
add sp, sp, #0x5C
|
||||
str r3, [sp, #0x0C] ; File PathType (ASCII = 3)
|
||||
load r12, romfsFileName
|
||||
str r12, [sp, #0x10] ; File DataPointer
|
||||
load r12, romfsFileNameSize
|
||||
str r12, [sp, #0x14] ; File PathSize
|
||||
load r3, archive
|
||||
bl openFileDirectlyHook
|
||||
sub sp, sp, #0x5C
|
||||
ldmfd sp!, {r0, r1, lr}
|
||||
add sp, sp, #0x50
|
||||
mov r0, r1 ; Substitute fsUserHandle with the fileHandle
|
||||
; Jumps here before the fsOpenFileDirectly call
|
||||
_mountSd:
|
||||
b mountSd
|
||||
.word 0xdead0000 ; Substituted opcode
|
||||
.word 0xdead0001 ; Branch to hooked function
|
||||
|
||||
; Once we have the sd romfs file opened, we'll open a subfile
|
||||
; in order to skip the useless data.
|
||||
stmfd sp!, {r1, r3-r11}
|
||||
mrc p15, 0, r4, c13, c0, 3
|
||||
add r4, r4, #0x80
|
||||
mov r1, r4
|
||||
add r3, pc, #fsOpenSubFileCmd-.-8
|
||||
ldmia r3!, {r5-r9}
|
||||
stmia r1!, {r5-r9}
|
||||
ldr r0, [r0]
|
||||
swi 0x32
|
||||
ldr r0, [r4, #0x0C]
|
||||
ldmfd sp!, {r1, r3-r11}
|
||||
str r0, [r1]
|
||||
mov r0, #0
|
||||
bx lr
|
||||
; Jumps here before every iFileOpen call
|
||||
_fsRedir:
|
||||
b fsRedir
|
||||
.word 0xdead0002 ; Substituted opcode
|
||||
.word 0xdead0003 ; Branch to hooked function
|
||||
|
||||
; Mounts SDMC and registers the archive as 'sdmc:'
|
||||
mountSd:
|
||||
cmp r3, #3
|
||||
bne _mountSd+4
|
||||
stmfd sp!, {r0-r4, lr}
|
||||
sub sp, sp, #4
|
||||
mov r1, #9
|
||||
mov r0, sp
|
||||
load r4, fsMountArchive
|
||||
blx r4
|
||||
mov r3, #0
|
||||
mov r2, #0
|
||||
ldr r1, [sp]
|
||||
addr r0, sdmcArchiveName
|
||||
load r4, fsRegisterArchive
|
||||
blx r4
|
||||
add sp, sp, #4
|
||||
ldmfd sp!, {r0-r4, lr}
|
||||
b _mountSd+4
|
||||
|
||||
; Check the path passed to iFileOpen.
|
||||
; If it is trying to access a RomFS file, we try to
|
||||
; open it from the title folder on the sdcard.
|
||||
; If the file cannot be opened from the sdcard, we just open
|
||||
; it from its original archive like nothing happened
|
||||
fsRedir:
|
||||
stmfd sp!, {r0-r12, lr}
|
||||
ldrb r12, [r1]
|
||||
cmp r12, #0x72 ; 'r', should include "rom:" and "rom2:"
|
||||
bne endRedir
|
||||
sub sp, sp, #0x400
|
||||
pathRedir:
|
||||
stmfd sp!, {r0-r3}
|
||||
add r0, sp, #0x10
|
||||
addr r3, sdmcCustomPath
|
||||
pathRedir_1:
|
||||
ldrb r2, [r3], #1
|
||||
strh r2, [r0], #2
|
||||
cmp r2, #0
|
||||
bne pathRedir_1
|
||||
sub r0, r0, #2
|
||||
pathRedir_2:
|
||||
ldrh r2, [r1], #2
|
||||
cmp r2, #0x3A ; ':'
|
||||
bne pathRedir_2
|
||||
pathRedir_3:
|
||||
ldrh r2, [r1], #2
|
||||
strh r2, [r0], #2
|
||||
cmp r2, #0
|
||||
bne pathRedir_3
|
||||
ldmfd sp!, {r0-r3}
|
||||
mov r1, sp
|
||||
bl _fsRedir+4
|
||||
add sp, sp, #0x400
|
||||
cmp r0, #0
|
||||
|
||||
endRedir:
|
||||
ldmfd sp!, {r0-r12, lr}
|
||||
moveq r0, #0
|
||||
beq panic
|
||||
bxeq lr
|
||||
b _fsRedir+4
|
||||
|
||||
panic:
|
||||
swi 0x3C
|
||||
b panic
|
||||
|
||||
.pool
|
||||
.align 4
|
||||
; Part of these symbols will be set from outside
|
||||
fsOpenFileDirectly : .word 0
|
||||
fsOpenSubFileCmd : .word 0x08010100
|
||||
.word 0 ; File Offset
|
||||
.word 0
|
||||
.word 0 ; File Size
|
||||
.word 0
|
||||
archive : .word 0
|
||||
romfsFileNameSize : .word 0
|
||||
romfsFileName : .word 0 ; File DataPointer
|
||||
sdmcArchiveName : .dcb "sdmc:", 0
|
||||
.align 4
|
||||
fsMountArchive : .word 0xdead0005
|
||||
fsRegisterArchive : .word 0xdead0006
|
||||
sdmcCustomPath : .word 0xdead0004
|
||||
|
||||
.close
|
||||
|
||||
|
@ -107,3 +107,75 @@ Result FSLDR_OpenFileDirectly(Handle* out, FS_ArchiveID archiveId, FS_Path archi
|
||||
|
||||
return cmdbuf[1];
|
||||
}
|
||||
|
||||
Result FSLDR_OpenArchive(FS_Archive* archive, FS_ArchiveID id, FS_Path path)
|
||||
{
|
||||
if(!archive) return -2;
|
||||
|
||||
u32 *cmdbuf = getThreadCommandBuffer();
|
||||
|
||||
cmdbuf[0] = IPC_MakeHeader(0x80C,3,2); // 0x80C00C2
|
||||
cmdbuf[1] = id;
|
||||
cmdbuf[2] = path.type;
|
||||
cmdbuf[3] = path.size;
|
||||
cmdbuf[4] = IPC_Desc_StaticBuffer(path.size, 0);
|
||||
cmdbuf[5] = (u32) path.data;
|
||||
|
||||
Result ret = 0;
|
||||
if(R_FAILED(ret = svcSendSyncRequest(fsldrHandle))) return ret;
|
||||
|
||||
if(archive) *archive = cmdbuf[2] | ((u64) cmdbuf[3] << 32);
|
||||
|
||||
return cmdbuf[1];
|
||||
}
|
||||
|
||||
Result FSLDR_CloseArchive(FS_Archive archive)
|
||||
{
|
||||
if(!archive) return -2;
|
||||
|
||||
u32 *cmdbuf = getThreadCommandBuffer();
|
||||
|
||||
cmdbuf[0] = IPC_MakeHeader(0x80E,2,0); // 0x80E0080
|
||||
cmdbuf[1] = (u32) archive;
|
||||
cmdbuf[2] = (u32) (archive >> 32);
|
||||
|
||||
Result ret = 0;
|
||||
if(R_FAILED(ret = svcSendSyncRequest(fsldrHandle))) return ret;
|
||||
|
||||
return cmdbuf[1];
|
||||
}
|
||||
|
||||
Result FSLDR_OpenDirectory(Handle* out, FS_Archive archive, FS_Path path)
|
||||
{
|
||||
u32 *cmdbuf = getThreadCommandBuffer();
|
||||
|
||||
cmdbuf[0] = IPC_MakeHeader(0x80B,4,2); // 0x80B0102
|
||||
cmdbuf[1] = (u32) archive;
|
||||
cmdbuf[2] = (u32) (archive >> 32);
|
||||
cmdbuf[3] = path.type;
|
||||
cmdbuf[4] = path.size;
|
||||
cmdbuf[5] = IPC_Desc_StaticBuffer(path.size, 0);
|
||||
cmdbuf[6] = (u32) path.data;
|
||||
|
||||
Result ret = 0;
|
||||
if(R_FAILED(ret = svcSendSyncRequest(fsldrHandle))) return ret;
|
||||
|
||||
if(out) *out = cmdbuf[3];
|
||||
|
||||
return cmdbuf[1];
|
||||
}
|
||||
|
||||
Result FSDIRLDR_Close(Handle handle)
|
||||
{
|
||||
u32 *cmdbuf = getThreadCommandBuffer();
|
||||
|
||||
cmdbuf[0] = IPC_MakeHeader(0x802,0,0); // 0x8020000
|
||||
|
||||
Result ret = 0;
|
||||
if(R_FAILED(ret = svcSendSyncRequest(handle))) return ret;
|
||||
|
||||
ret = cmdbuf[1];
|
||||
if(R_SUCCEEDED(ret)) ret = svcCloseHandle(handle);
|
||||
|
||||
return ret;
|
||||
}
|
@ -7,3 +7,7 @@ void fsldrExit(void);
|
||||
Result FSLDR_InitializeWithSdkVersion(Handle session, u32 version);
|
||||
Result FSLDR_SetPriority(u32 priority);
|
||||
Result FSLDR_OpenFileDirectly(Handle* out, FS_ArchiveID archiveId, FS_Path archivePath, FS_Path filePath, u32 openFlags, u32 attributes);
|
||||
Result FSLDR_OpenArchive(FS_Archive* archive, FS_ArchiveID id, FS_Path path);
|
||||
Result FSLDR_CloseArchive(FS_Archive archive);
|
||||
Result FSLDR_OpenDirectory(Handle* out, FS_Archive archive, FS_Path path);
|
||||
Result FSDIRLDR_Close(Handle handle);
|
@ -2,6 +2,7 @@
|
||||
#include "patcher.h"
|
||||
#include "memory.h"
|
||||
#include "strings.h"
|
||||
#include "fsldr.h"
|
||||
#include "ifile.h"
|
||||
#include "CFWInfo.h"
|
||||
#include "../build/bundled.h"
|
||||
@ -39,6 +40,25 @@ static Result fileOpen(IFile *file, FS_ArchiveID archiveId, const char *path, in
|
||||
return IFile_Open(file, archiveId, archivePath, filePath, flags);
|
||||
}
|
||||
|
||||
static bool dirCheck(FS_ArchiveID archiveId, const char *path)
|
||||
{
|
||||
bool ret;
|
||||
Handle handle;
|
||||
FS_Archive archive;
|
||||
FS_Path dirPath = {PATH_ASCII, strnlen(path, 255) + 1, path},
|
||||
archivePath = {PATH_EMPTY, 1, (u8 *)""};
|
||||
|
||||
if(R_FAILED(FSLDR_OpenArchive(&archive, archiveId, archivePath))) ret = false;
|
||||
else
|
||||
{
|
||||
ret = R_SUCCEEDED(FSLDR_OpenDirectory(&handle, archive, dirPath));
|
||||
if(ret) FSDIRLDR_Close(handle);
|
||||
FSLDR_CloseArchive(archive);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static u32 openLumaFile(IFile *file, const char *path)
|
||||
{
|
||||
Result res = fileOpen(file, ARCHIVE_SDMC, path, FS_OPEN_READ);
|
||||
@ -49,6 +69,11 @@ static u32 openLumaFile(IFile *file, const char *path)
|
||||
return (u32)res == 0xC88044AB && R_SUCCEEDED(fileOpen(file, ARCHIVE_NAND_RW, path, FS_OPEN_READ)) ? ARCHIVE_NAND_RW : 0;
|
||||
}
|
||||
|
||||
static bool checkLumaDir(const char *path)
|
||||
{
|
||||
return dirCheck(ARCHIVE_SDMC, path);
|
||||
}
|
||||
|
||||
static inline void loadCFWInfo(void)
|
||||
{
|
||||
static bool infoLoaded = false;
|
||||
@ -249,7 +274,7 @@ static inline void patchCfgGetRegion(u8 *code, u32 size, u8 regionId, u32 CFGUHa
|
||||
}
|
||||
}
|
||||
|
||||
static u32 findNearestStmfd(u8* code, u32 pos)
|
||||
static u32 findStart(u8* code, u32 pos)
|
||||
{
|
||||
while(pos >= 4)
|
||||
{
|
||||
@ -260,36 +285,82 @@ static u32 findNearestStmfd(u8* code, u32 pos)
|
||||
return 0xFFFFFFFF;
|
||||
}
|
||||
|
||||
static u32 findFunctionCommand(u8* code, u32 size, u32 command)
|
||||
static bool findSymbols(u8* code, u32 size, u32 *fsMountArchive, u32 *fsRegisterArchive, u32 *fsTryOpenFile, u32 *fsOpenFileDirectly, u32 *throwFatalError)
|
||||
{
|
||||
u32 func;
|
||||
u32 svcConnectToPort = 0xFFFFFFFF;
|
||||
|
||||
for(func = 4; *(u32 *)(code + func) != command; func += 4)
|
||||
if(func > size - 8) return 0xFFFFFFFF;
|
||||
|
||||
return findNearestStmfd(code, func);
|
||||
}
|
||||
|
||||
static inline u32 findThrowFatalError(u8* code, u32 size)
|
||||
{
|
||||
u32 connectToPort;
|
||||
|
||||
for(connectToPort = 0; *(u32 *)(code + connectToPort + 4) != 0xEF00002D; connectToPort += 4)
|
||||
if(connectToPort > size - 12) return 0xFFFFFFFF;
|
||||
|
||||
u32 func = 0xFFFFFFFF;
|
||||
|
||||
for(u32 i = 4; func == 0xFFFFFFFF && i <= size - 4; i += 4)
|
||||
for(u32 addr = 0; addr <= size - 4; addr += 4)
|
||||
{
|
||||
if(*(u32 *)(code + i) != MAKE_BRANCH_LINK(i, connectToPort)) continue;
|
||||
if(*fsMountArchive == 0xFFFFFFFF)
|
||||
{
|
||||
if(addr <= size - 12 && *(u32 *)(code + addr) == 0xE5970010)
|
||||
{
|
||||
if((*(u32 *)(code + addr + 4) == 0xE1CD20D8) && ((*(u32 *)(code + addr + 8) & 0xFFFFFF) == 0x008D0000))
|
||||
*fsMountArchive = findStart(code, addr);
|
||||
}
|
||||
else if(addr <= size - 16 && *(u32 *)(code + addr) == 0xE24DD028)
|
||||
{
|
||||
if((*(u32 *)(code + addr + 4) == 0xE1A04000) && (*(u32 *)(code + addr + 8) == 0xE59F60A8) && (*(u32 *)(code + addr + 0xC) == 0xE3A0C001))
|
||||
*fsMountArchive = findStart(code, addr);
|
||||
}
|
||||
}
|
||||
|
||||
func = findNearestStmfd(code, i);
|
||||
if(*fsRegisterArchive == 0xFFFFFFFF && addr <= size - 8)
|
||||
{
|
||||
if(*(u32 *)(code + addr) == 0xC82044B4)
|
||||
{
|
||||
if(*(u32 *)(code + addr + 4) == 0xD8604659)
|
||||
*fsRegisterArchive = findStart(code, addr);
|
||||
}
|
||||
}
|
||||
|
||||
for(u32 pos = func + 4; func != 0xFFFFFFFF && pos <= size - 4 && *(u16 *)(code + pos + 2) != 0xE92D; pos += 4)
|
||||
if(*(u32 *)(code + pos) == 0xE200167E) func = 0xFFFFFFFF;
|
||||
if(*fsTryOpenFile == 0xFFFFFFFF && addr <= size - 12)
|
||||
{
|
||||
if(*(u32 *)(code + addr + 0xC) == 0xE12FFF3C)
|
||||
{
|
||||
if(((*(u32 *)(code + addr) == 0xE1A0100D) || (*(u32 *)(code + addr) == 0xE28D1010)) &&
|
||||
(*(u32 *)(code + addr + 4) == 0xE590C000) && ((*(u32 *)(code + addr + 8) == 0xE1A00004) || (*(u32 *)(code + addr + 8) == 0xE1A00005)))
|
||||
{
|
||||
*fsTryOpenFile = findStart(code, addr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(*fsOpenFileDirectly == 0xFFFFFFFF)
|
||||
{
|
||||
if(*(u32 *)(code + addr) == 0x08030204)
|
||||
{
|
||||
*fsOpenFileDirectly = findStart(code, addr);
|
||||
}
|
||||
}
|
||||
|
||||
if(svcConnectToPort == 0xFFFFFFFF && addr >= 4)
|
||||
{
|
||||
if(*(u32 *)(code + addr) == 0xEF00002D)
|
||||
svcConnectToPort = addr - 4;
|
||||
}
|
||||
}
|
||||
|
||||
return func;
|
||||
if(svcConnectToPort != 0xFFFFFFFF && *fsMountArchive != 0xFFFFFFFF && *fsRegisterArchive != 0xFFFFFFFF && *fsTryOpenFile != 0xFFFFFFFF && *fsOpenFileDirectly != 0xFFFFFFFF)
|
||||
{
|
||||
u32 func = 0xFFFFFFFF;
|
||||
|
||||
for(u32 i = 4; func == 0xFFFFFFFF && i <= size - 4; i += 4)
|
||||
{
|
||||
if(*(u32 *)(code + i) != MAKE_BRANCH_LINK(i, svcConnectToPort)) continue;
|
||||
|
||||
func = findStart(code, i);
|
||||
|
||||
for(u32 pos = func + 4; func != 0xFFFFFFFF && pos <= size - 4 && *(u16 *)(code + pos + 2) != 0xE92D; pos += 4)
|
||||
if(*(u32 *)(code + pos) == 0xE200167E) func = 0xFFFFFFFF;
|
||||
}
|
||||
|
||||
*throwFatalError = func;
|
||||
|
||||
if(func != 0xFFFFFFFF) return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline bool applyCodeIpsPatch(u64 progId, u8 *code, u32 size)
|
||||
@ -442,57 +513,56 @@ static inline bool patchRomfsRedirection(u64 progId, u8* code, u32 size)
|
||||
char path[] = "/luma/titles/0000000000000000/romfs";
|
||||
progIdToStr(path + 28, progId);
|
||||
|
||||
IFile file;
|
||||
u32 archive = openLumaFile(&file, path);
|
||||
if(!checkLumaDir(path)) return true;
|
||||
|
||||
if(!archive) return true;
|
||||
u32 fsMountArchive = 0xFFFFFFFF,
|
||||
fsRegisterArchive = 0xFFFFFFFF,
|
||||
fsTryOpenFile = 0xFFFFFFFF,
|
||||
fsOpenFileDirectly = 0xFFFFFFFF,
|
||||
throwFatalError;
|
||||
|
||||
bool ret = false;
|
||||
u64 romfsSize;
|
||||
|
||||
if(R_FAILED(IFile_GetSize(&file, &romfsSize))) goto exit;
|
||||
|
||||
u64 total;
|
||||
u32 magic;
|
||||
|
||||
if(R_FAILED(IFile_Read(&file, &total, &magic, 4)) || total != 4 || magic != 0x43465649) goto exit;
|
||||
|
||||
u32 fsOpenFileDirectly = findFunctionCommand(code, size, 0x08030204),
|
||||
throwFatalError = findThrowFatalError(code, size);
|
||||
|
||||
if(fsOpenFileDirectly == 0xFFFFFFFF || throwFatalError == 0xFFFFFFFF) goto exit;
|
||||
if(!findSymbols(code, size, &fsMountArchive, &fsRegisterArchive, &fsTryOpenFile, &fsOpenFileDirectly, &throwFatalError)) return false;
|
||||
|
||||
//Setup the payload
|
||||
u8 *payload = code + throwFatalError;
|
||||
memcpy(payload, romfsredir_bin, romfsredir_bin_size);
|
||||
memcpy(payload + romfsredir_bin_size, path, sizeof(path));
|
||||
*(u32 *)(payload + 0xC) = *(u32 *)(code + fsOpenFileDirectly);
|
||||
|
||||
u32 *payloadSymbols = (u32 *)(payload + romfsredir_bin_size - 0x24);
|
||||
payloadSymbols[0] = 0x100000 + fsOpenFileDirectly + 4;
|
||||
*(u64 *)(payloadSymbols + 2) = 0x1000ULL;
|
||||
*(u64 *)(payloadSymbols + 4) = romfsSize - 0x1000ULL;
|
||||
payloadSymbols[6] = archive;
|
||||
payloadSymbols[7] = sizeof(path);
|
||||
payloadSymbols[8] = 0x100000 + throwFatalError + romfsredir_bin_size; //String pointer
|
||||
//Insert symbols in the payload
|
||||
u32 *payload32 = (u32 *)payload;
|
||||
for(u32 i = 0; i < romfsredir_bin_size / 4; i++)
|
||||
{
|
||||
switch (payload32[i])
|
||||
{
|
||||
case 0xdead0000:
|
||||
payload32[i] = *(u32 *)(code + fsOpenFileDirectly);
|
||||
break;
|
||||
case 0xdead0001:
|
||||
payload32[i] = MAKE_BRANCH(throwFatalError + i * 4, fsOpenFileDirectly + 4);
|
||||
break;
|
||||
case 0xdead0002:
|
||||
payload32[i] = *(u32 *)(code + fsTryOpenFile);
|
||||
break;
|
||||
case 0xdead0003:
|
||||
payload32[i] = MAKE_BRANCH(throwFatalError + i * 4, fsTryOpenFile + 4);
|
||||
break;
|
||||
case 0xdead0004:
|
||||
memcpy(payload32 + i, "sdmc:", 5);
|
||||
memcpy((u8 *)(payload32 + i) + 5, path, sizeof(path));
|
||||
break;
|
||||
case 0xdead0005:
|
||||
payload32[i] = 0x100000 + fsMountArchive;
|
||||
break;
|
||||
case 0xdead0006:
|
||||
payload32[i] = 0x100000 + fsRegisterArchive;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//Place the hooks
|
||||
*(u32 *)(code + fsOpenFileDirectly) = MAKE_BRANCH(fsOpenFileDirectly, throwFatalError);
|
||||
*(u32 *)(code + fsTryOpenFile) = MAKE_BRANCH(fsTryOpenFile, throwFatalError + 12);
|
||||
|
||||
u32 fsOpenLinkFile = findFunctionCommand(code, size, 0x80C0000);
|
||||
|
||||
if(fsOpenLinkFile != 0xFFFFFFFF)
|
||||
{
|
||||
*(u32 *)(code + fsOpenLinkFile) = 0xE3A03003; //mov r3, #3
|
||||
*(u32 *)(code + fsOpenLinkFile + 4) = MAKE_BRANCH(fsOpenLinkFile + 4, throwFatalError);
|
||||
}
|
||||
|
||||
ret = true;
|
||||
|
||||
exit:
|
||||
IFile_Close(&file);
|
||||
|
||||
return ret;
|
||||
return true;
|
||||
}
|
||||
|
||||
void patchCode(u64 progId, u16 progVer, u8 *code, u32 size)
|
||||
|
Reference in New Issue
Block a user