Added RomFS redirection courtesy of @delebile, changed structure for game patches: language emulation txts now go to /luma/titles/<titleid>/locale.txt, code.bins go to /luma/titles/<titleid>/code.bin, RomFSes go to /luma/titles/<titleid>/romfs

This commit is contained in:
Aurora 2016-11-16 03:41:59 +01:00
parent 1fcab825bf
commit b5336c81cc
10 changed files with 346 additions and 111 deletions

View File

@ -14,6 +14,7 @@ OC := arm-none-eabi-objcopy
name := $(shell basename $(CURDIR))
dir_source := source
dir_patches := patches
dir_build := build
dir_out := ../$(dir_build)
@ -31,6 +32,14 @@ LDFLAGS := -Xlinker --defsym="__start__=0x14000000" -specs=3dsx.specs $(ASFLAGS)
objects = $(patsubst $(dir_source)/%.c, $(dir_build)/%.o, \
$(call rwildcard, $(dir_source), *.s *.c))
bundled = $(dir_build)/romfsredir.bin.o
define bin2o
bin2s $< | $(AS) -o $(@)
echo "extern const u8" `(echo $(<F) | sed -e 's/^\([0-9]\)/_\1/' | tr . _)`"[];" >> $(dir_build)/bundled.h
echo "extern const u32" `(echo $(<F) | sed -e 's/^\([0-9]\)/_\1/' | tr . _)`_size";" >> $(dir_build)/bundled.h
endef
.PHONY: all
all: $(dir_out)/$(name).bin
@ -38,12 +47,23 @@ all: $(dir_out)/$(name).bin
clean:
@rm -rf $(dir_build)
.PRECIOUS: $(dir_build)/%.bin
$(dir_build):
@mkdir -p "$@"
$(dir_out)/$(name).bin: $(dir_build)/$(name).elf
@makerom -f ncch -rsf loader.rsf -nocodepadding -o $@ -elf $<
$(dir_build)/$(name).elf: $(objects)
$(dir_build)/$(name).elf: $(bundled) $(objects)
$(LINK.o) $(OUTPUT_OPTION) $^ $(LIBPATHS) $(LIBS)
$(dir_build)/%.bin.o: $(dir_build)/%.bin
@$(bin2o)
$(dir_build)/%.bin: $(dir_patches)/%.s $(dir_build)
@armips $<
$(dir_build)/memory.o $(dir_build)/strings.o: CFLAGS += -O3
$(dir_build)/%.o: $(dir_source)/%.c

View File

@ -0,0 +1,81 @@
; Code from delebile
.arm.little
.create "build/romfsredir.bin", 0
.macro load, reg, func
ldr reg, [pc, #func-.-8]
.endmacro
.arm
; fsOpenFileDirectly function will be redirected here.
; If the requested archive is not ROMFS, we'll return
; to the original fucntion.
openFileDirectlyHook:
cmp r3, #3
beq openRomfs
load r12, fsOpenFileDirectly
add r12, r12, #4
nop ; Will be replaced with the original function opcode
bx r12
; 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
mov r3, #9 ; SDMC Archive ID
bl openFileDirectlyHook
sub sp, sp, #0x5C
ldmfd sp!, {r0, r1, lr}
add sp, sp, #0x50
mov r0, r1 ; Substitute fsUserHandle with the fileHandle
; Once we have the sd romfs file opened, we'll open a subfile
; in order to skip the useless data.
fsOpenSubFile:
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
.pool
.align 4
; Part of these symbols will be set from outside
fsOpenFileDirectly : .word 0x00000000
fsOpenSubFileCmd : .word 0x08010100
.word 0x00000000 ; File Offset
.word 0x00000000
.word 0x00000000 ; File Size
.word 0x00000000
romfsFileNameSize : .word 0x00000000
romfsFileName : .word 0x00000000 ; File DataPointer
.close

View File

@ -4,6 +4,7 @@
#include "strings.h"
#include "ifile.h"
#include "CFWInfo.h"
#include "../build/bundled.h"
static CFWInfo info;
@ -134,97 +135,6 @@ exit:
IFile_Close(&file);
}
static inline bool loadTitleCodeSection(u64 progId, u8 *code, u32 size)
{
/* Here we look for "/luma/code_sections/[u64 titleID in hex, uppercase].bin"
If it exists it should be a decompressed binary code file */
char path[] = "/luma/code_sections/0000000000000000.bin";
progIdToStr(path + 35, progId);
IFile file;
if(R_FAILED(openLumaFile(&file, path))) return true;
bool ret;
u64 fileSize;
if(R_FAILED(IFile_GetSize(&file, &fileSize)) || fileSize > size) ret = false;
else
{
u64 total;
ret = R_SUCCEEDED(IFile_Read(&file, &total, code, fileSize)) && total == fileSize;
}
IFile_Close(&file);
return ret;
}
static inline bool loadTitleLocaleConfig(u64 progId, u8 *regionId, u8 *languageId)
{
/* Here we look for "/luma/locales/[u64 titleID in hex, uppercase].txt"
If it exists it should contain, for example, "EUR IT" */
char path[] = "/luma/locales/0000000000000000.txt";
progIdToStr(path + 29, progId);
IFile file;
if(R_FAILED(openLumaFile(&file, path))) return true;
bool ret;
u64 fileSize;
if(R_FAILED(IFile_GetSize(&file, &fileSize)) || fileSize < 6 || fileSize > 8)
{
ret = false;
goto exit;
}
char buf[8];
u64 total;
if(R_FAILED(IFile_Read(&file, &total, buf, fileSize)))
{
ret = false;
goto exit;
}
u32 i,
j;
for(i = 0; i < 7; i++)
{
static const char *regions[] = {"JPN", "USA", "EUR", "AUS", "CHN", "KOR", "TWN"};
if(memcmp(buf, regions[i], 3) == 0)
{
*regionId = (u8)i;
break;
}
}
for(j = 0; j < 12; j++)
{
static const char *languages[] = {"JP", "EN", "FR", "DE", "IT", "ES", "ZH", "KO", "NL", "PT", "RU", "TW"};
if(memcmp(buf + 4, languages[j], 2) == 0)
{
*languageId = (u8)j;
break;
}
}
ret = i != 7 && j != 12;
exit:
IFile_Close(&file);
return ret;
}
static inline u8 *getCfgOffsets(u8 *code, u32 size, u32 *CFGUHandleOffset)
{
/* HANS:
@ -285,7 +195,6 @@ static inline bool patchCfgGetLanguage(u8 *code, u32 size, u8 languageId, u8 *CF
u8 *calledFunction = instr;
u32 i = 0;
bool found;
do
{
@ -307,7 +216,7 @@ static inline bool patchCfgGetLanguage(u8 *code, u32 size, u8 languageId, u8 *CF
i++;
}
while(i < 2 && !found && calledFunction[3] == 0xEA);
while(i < 2 && calledFunction[3] == 0xEA);
}
}
@ -339,6 +248,214 @@ static inline void patchCfgGetRegion(u8 *code, u32 size, u8 regionId, u32 CFGUHa
}
}
static u32 findNearestStmfd(u8* code, u32 pos)
{
while(pos > 0)
{
if(*(u16 *)(code + pos + 2) == 0xE92D) return pos;
pos -= 4;
}
return 0;
}
static u32 findFunctionCommand(u8* code, u32 size, u32 command)
{
u32 func = 0;
for(u32 i = 0; i < size && !func; i += 4)
if(*(u32 *)(code + i) == command) func = i;
return !func ? 0 : findNearestStmfd(code, func);
}
static inline u32 findThrowFatalError(u8* code, u32 size)
{
u32 connectToPort = 0;
for(u32 i = 0; i < size && !connectToPort; i += 4)
if(*(u32 *)(code + i) == 0xEF00002D) connectToPort = i - 4;
if(!connectToPort) return 0;
u32 func = 0;
for(u32 i = 0; i < size && !func; i += 4)
{
if(*(u32 *)(code + i) != MAKE_BRANCH_LINK(i, connectToPort)) continue;
func = findNearestStmfd(code, i);
for(u32 pos = func + 4; func != 0 && pos <= size - 4 && *(u16 *)(code + pos + 2) != 0xE92D; pos += 4)
if(*(u32 *)(code + pos) == 0xE200167E) func = 0;
}
return func;
}
static inline bool loadTitleCodeSection(u64 progId, u8 *code, u32 size)
{
/* Here we look for "/luma/titles/[u64 titleID in hex, uppercase]/code.bin"
If it exists it should be a decrypted and decompressed binary code file */
char path[] = "/luma/titles/0000000000000000/code.bin";
progIdToStr(path + 28, progId);
IFile file;
if(R_FAILED(openLumaFile(&file, path))) return true;
bool ret;
u64 fileSize;
if(R_FAILED(IFile_GetSize(&file, &fileSize)) || fileSize > size) ret = false;
else
{
u64 total;
ret = R_SUCCEEDED(IFile_Read(&file, &total, code, fileSize)) && total == fileSize;
}
IFile_Close(&file);
return ret;
}
static inline bool loadTitleLocaleConfig(u64 progId, u8 *regionId, u8 *languageId)
{
/* Here we look for "/luma/titles/[u64 titleID in hex, uppercase]/locale.txt"
If it exists it should contain, for example, "EUR IT" */
char path[] = "/luma/titles/0000000000000000/locale.txt";
progIdToStr(path + 28, progId);
IFile file;
if(R_FAILED(openLumaFile(&file, path))) return true;
bool ret;
u64 fileSize;
if(R_FAILED(IFile_GetSize(&file, &fileSize)) || fileSize < 6 || fileSize > 8)
{
ret = false;
goto exit;
}
char buf[8];
u64 total;
if(R_FAILED(IFile_Read(&file, &total, buf, fileSize)))
{
ret = false;
goto exit;
}
u32 i,
j;
for(i = 0; i < 7; i++)
{
static const char *regions[] = {"JPN", "USA", "EUR", "AUS", "CHN", "KOR", "TWN"};
if(memcmp(buf, regions[i], 3) == 0)
{
*regionId = (u8)i;
break;
}
}
for(j = 0; j < 12; j++)
{
static const char *languages[] = {"JP", "EN", "FR", "DE", "IT", "ES", "ZH", "KO", "NL", "PT", "RU", "TW"};
if(memcmp(buf + 4, languages[j], 2) == 0)
{
*languageId = (u8)j;
break;
}
}
ret = i != 7 && j != 12;
exit:
IFile_Close(&file);
return ret;
}
static bool patchRomfsRedirection(u64 progId, u8* code, u32 size)
{
/* Here we look for "/luma/titles/[u64 titleID in hex, uppercase]/romfs"
If it exists it should be a decrypted raw RomFS */
char path[] = "/luma/titles/0000000000000000/romfs";
progIdToStr(path + 28, progId);
IFile file;
if(R_FAILED(openLumaFile(&file, path))) return true;
bool ret;
u64 romfsSize;
if(R_FAILED(IFile_GetSize(&file, &romfsSize)))
{
ret = false;
goto exit;
}
u64 total;
u32 header;
if(R_FAILED(IFile_Read(&file, &total, &header, 4)) || total != 4 || header != 0x43465649)
{
ret = false;
goto exit;
}
u32 fsOpenFileDirectly = findFunctionCommand(code, size, 0x08030204),
fsOpenLinkFile = findFunctionCommand(code, size, 0x80C0000),
throwFatalError = findThrowFatalError(code, size);
if(!fsOpenFileDirectly || !throwFatalError)
{
ret = false;
goto exit;
}
//Setup the payload
memcpy(code + throwFatalError, romfsredir_bin, romfsredir_bin_size);
*((u32 *)(code + throwFatalError + 0x10)) = *(u32 *)(code + fsOpenFileDirectly);
*((u32 *)(code + throwFatalError + romfsredir_bin_size - 0x08)) = sizeof(path);
*((u64 *)(code + throwFatalError + romfsredir_bin_size - 0x10)) = romfsSize - 0x1000ULL;
*((u64 *)(code + throwFatalError + romfsredir_bin_size - 0x18)) = 0x1000ULL;
*((u32 *)(code + throwFatalError + romfsredir_bin_size - 0x20)) = fsOpenFileDirectly + 0x100000;
//Place the hooks
*(u32 *)(code + fsOpenFileDirectly) = MAKE_BRANCH(fsOpenFileDirectly, throwFatalError);
if(fsOpenLinkFile != 0)
{
*(u32 *)(code + fsOpenLinkFile) = 0xE3A03003; //mov r3, #3
*(u32 *)(code + fsOpenLinkFile + 4) = MAKE_BRANCH(fsOpenLinkFile + 4, throwFatalError);
memcpy(code + fsOpenLinkFile + 8, path, sizeof(path));
*(u32 *)(code + throwFatalError + romfsredir_bin_size - 4) = fsOpenLinkFile + 8 + 0x100000; //String pointer
}
else
{
memcpy(code + throwFatalError + romfsredir_bin_size, path, 0x30);
*(u32 *)(code + throwFatalError + romfsredir_bin_size - 4) = throwFatalError + romfsredir_bin_size + 0x100000; //String pointer
}
ret = true;
exit:
IFile_Close(&file);
return ret;
}
void patchCode(u64 progId, u16 progVer, u8 *code, u32 size)
{
loadCFWInfo();
@ -575,12 +692,13 @@ void patchCode(u64 progId, u16 progVer, u8 *code, u32 size)
) != 3) goto error;
}
else if(CONFIG(USELANGEMUANDCODE) && (u32)((progId & 0xFFFFFFF000000000LL) >> 0x24) == 0x0004000)
else if(CONFIG(PATCHGAMES) && (u32)((progId & 0xFFFFFFF000000000LL) >> 0x24) == 0x0004000)
{
u8 regionId = 0xFF,
languageId;
if(!loadTitleLocaleConfig(progId, &regionId, &languageId) ||
!loadTitleCodeSection(progId, code, size)) goto error;
if(!loadTitleCodeSection(progId, code, size) ||
!loadTitleLocaleConfig(progId, &regionId, &languageId)) goto error;
if(regionId != 0xFF)
{
@ -592,6 +710,8 @@ void patchCode(u64 progId, u16 progVer, u8 *code, u32 size)
patchCfgGetRegion(code, size, regionId, CFGUHandleOffset);
}
if(!patchRomfsRedirection(progId, code, size)) goto error;
}
return;

View File

@ -2,6 +2,9 @@
#include <3ds/types.h>
#define MAKE_BRANCH(src,dst) (0xEA000000 | ((u32)((((u8 *)(dst) - (u8 *)(src)) >> 2) - 2) & 0xFFFFFF))
#define MAKE_BRANCH_LINK(src,dst) (0xEB000000 | ((u32)((((u8 *)(dst) - (u8 *)(src)) >> 2) - 2) & 0xFFFFFF))
#define CONFIG(a) (((info.config >> (a + 20)) & 1) != 0)
#define MULTICONFIG(a) ((info.config >> (a * 2 + 8)) & 3)
#define BOOTCONFIG(a, b) ((info.config >> a) & b)
@ -28,7 +31,7 @@ enum singleOptions
USESYSFIRM,
LOADEXTFIRMSANDMODULES,
USECUSTOMPATH,
USELANGEMUANDCODE,
PATCHGAMES,
PATCHVERSTRING,
SHOWGBABOOT,
PATCHACCESS

View File

@ -80,7 +80,7 @@ void configMenu(bool isSdMode, bool oldPinStatus, u32 oldPinMode)
"( ) Use SysNAND FIRM if booting with R",
"( ) Enable loading external FIRMs and modules",
"( ) Use custom path",
"( ) Enable region/language emu. and ext. .code",
"( ) Enable game patching",
"( ) Show NAND or user string in System Settings",
"( ) Show GBA boot screen in patched AGB_FIRM",
"( ) Patch SVC/service/archive/ARM9 access"
@ -160,10 +160,12 @@ void configMenu(bool isSdMode, bool oldPinStatus, u32 oldPinMode)
"Enable overriding the region and\n"
"language configuration and the usage\n"
"of patched code binaries for specific\n"
"games.\n\n"
"of patched code binaries and\n"
"custom RomFS for specific games.\n\n"
"Also makes certain DLCs for\n"
"out-of-region games work.\n\n"
"Enabling this requires the\n"
"archive patch to be applied.\n\n"
"Refer to the wiki for instructions.",
"Enable showing the current NAND/FIRM:\n\n"

View File

@ -53,7 +53,7 @@ enum singleOptions
USESYSFIRM,
LOADEXTFIRMSANDMODULES,
USECUSTOMPATH,
USELANGEMUANDCODE,
PATCHGAMES,
PATCHVERSTRING,
SHOWGBABOOT,
PATCHACCESS

View File

@ -195,11 +195,18 @@ u32 patchNativeFirm(u32 firmVersion, FirmwareSource nandType, u32 emuHeader, boo
ret += patchKernel9Panic(arm9Section, kernel9Size);
}
if(CONFIG(PATCHACCESS))
bool patchAccess = CONFIG(PATCHACCESS),
patchGames = CONFIG(PATCHGAMES);
if(patchAccess || patchGames)
{
ret += patchArm11SvcAccessChecks(arm11SvcHandler, (u32 *)(arm11Section1 + firm->section[1].size));
ret += patchK11ModuleChecks(arm11Section1, firm->section[1].size, &freeK11Space);
ret += patchP9AccessChecks(process9Offset, process9Size);
ret += patchK11ModuleChecks(arm11Section1, firm->section[1].size, &freeK11Space, patchGames);
if(patchAccess)
{
ret += patchArm11SvcAccessChecks(arm11SvcHandler, (u32 *)(arm11Section1 + firm->section[1].size));
ret += patchP9AccessChecks(process9Offset, process9Size);
}
}
return ret;

View File

@ -429,13 +429,13 @@ u32 patchArm11SvcAccessChecks(u32 *arm11SvcHandler, u32 *endPos)
return 0;
}
u32 patchK11ModuleChecks(u8 *pos, u32 size, u8 **freeK11Space)
u32 patchK11ModuleChecks(u8 *pos, u32 size, u8 **freeK11Space, bool patchGames)
{
/* We have to detour a function in the ARM11 kernel because builtin modules
are compressed in memory and are only decompressed at runtime */
//Check that we have enough free space
if(*(u32 *)(*freeK11Space + k11modules_bin_size - 4) != 0xFFFFFFFF) return 0;
if(*(u32 *)(*freeK11Space + k11modules_bin_size - 4) != 0xFFFFFFFF) return patchGames ? 1 : 0;
//Look for the code that decompresses the .code section of the builtin modules
const u8 pattern[] = {0xE5, 0x48, 0x00, 0x9D};

View File

@ -54,7 +54,7 @@ u32 patchKernel9Panic(u8 *pos, u32 size);
u32 patchKernel11Panic(u8 *pos, u32 size);
u32 patchP9AccessChecks(u8 *pos, u32 size);
u32 patchArm11SvcAccessChecks(u32 *arm11SvcHandler, u32 *endPos);
u32 patchK11ModuleChecks(u8 *pos, u32 size, u8 **freeK11Space);
u32 patchK11ModuleChecks(u8 *pos, u32 size, u8 **freeK11Space, bool patchGames);
u32 patchUnitInfoValueSet(u8 *pos, u32 size);
u32 patchLgySignatureChecks(u8 *pos, u32 size);
u32 patchTwlInvalidSignatureChecks(u8 *pos, u32 size);

View File

@ -93,7 +93,8 @@ void newPin(bool allowSkipping, u32 pinMode)
if(!pressed) continue;
enteredPassword[cnt] = (u8)pinKeyToLetter(pressed); //Add character to password
//Add character to password
enteredPassword[cnt] = (u8)pinKeyToLetter(pressed);
//Visualize character on screen
drawCharacter(enteredPassword[cnt], true, 10 + (16 + 2 * cnt) * SPACING_X, 10 + 3 * SPACING_Y, COLOR_WHITE);
@ -200,7 +201,8 @@ bool verifyPin(u32 pinMode)
if(!pressed) continue;
enteredPassword[cnt] = (u8)pinKeyToLetter(pressed); //Add character to password
//Add character to password
enteredPassword[cnt] = (u8)pinKeyToLetter(pressed);
//Visualize character on screen
drawCharacter((char)enteredPassword[cnt], true, 10 + (16 + 2 * cnt) * SPACING_X, 10 + 3 * SPACING_Y, COLOR_WHITE);