diff --git a/Makefile b/Makefile index fca9c8a..cbe4fbb 100644 --- a/Makefile +++ b/Makefile @@ -26,11 +26,12 @@ dir_out := out ASFLAGS := -mlittle-endian -mcpu=arm946e-s -march=armv5te CFLAGS := -Wall -Wextra -MMD -MP -marm $(ASFLAGS) -fno-builtin -fshort-wchar -std=c11 -Wno-main -O2 -flto -ffast-math +LDFLAGS := -nostartfiles FLAGS := name=$(name).dat dir_out=$(abspath $(dir_out)) ICON=$(abspath icon.png) APP_DESCRIPTION="Noob-friendly 3DS CFW." APP_AUTHOR="Reisyukaku/Aurora Wright" --no-print-directory -objects_cfw = $(patsubst $(dir_source)/%.s, $(dir_build)/%.o, \ - $(patsubst $(dir_source)/%.c, $(dir_build)/%.o, \ - $(call rwildcard, $(dir_source), *.s *.c))) +objects = $(patsubst $(dir_source)/%.s, $(dir_build)/%.o, \ + $(patsubst $(dir_source)/%.c, $(dir_build)/%.o, \ + $(call rwildcard, $(dir_source), *.s *.c))) .PHONY: all @@ -55,10 +56,10 @@ pathchanger: $(dir_out)/pathchanger clean: @$(MAKE) $(FLAGS) -C $(dir_mset) clean @$(MAKE) $(FLAGS) -C $(dir_ninjhax) clean - @rm -rf $(dir_out) $(dir_build) @$(MAKE) -C $(dir_loader) clean @$(MAKE) -C $(dir_screeninit) clean @$(MAKE) -C $(dir_injector) clean + @rm -rf $(dir_out) $(dir_build) $(dir_out): @mkdir -p "$(dir_out)/aurei/payloads" @@ -74,38 +75,36 @@ $(dir_out)/arm9loaderhax.bin: $(dir_build)/main.bin $(dir_out) @cp -a $(dir_build)/main.bin $@ $(dir_out)/3ds/$(name): $(dir_out) - @mkdir -p "$(dir_out)/3ds/$(name)" + @mkdir -p "$@" @$(MAKE) $(FLAGS) -C $(dir_ninjhax) - @mv $(dir_out)/$(name).3dsx $@ - @mv $(dir_out)/$(name).smdh $@ + @mv $(dir_out)/$(name).3dsx $(dir_out)/$(name).smdh $@ $(dir_out)/$(name).zip: launcher a9lh ninjhax - @cd $(dir_out) && zip -9 -r $(name) * + @cd "$(@D)" && zip -9 -r $(name) * $(dir_build)/patches.h: $(dir_patches)/emunand.s $(dir_patches)/reboot.s $(dir_injector)/Makefile - @mkdir -p "$(dir_build)" + @mkdir -p "$(@D)" @armips $< @armips $(word 2,$^) @$(MAKE) -C $(dir_injector) - @mv emunand.bin reboot.bin $(dir_injector)/injector.cxi $(dir_build) - @bin2c -o $@ -n emunand $(dir_build)/emunand.bin -n reboot $(dir_build)/reboot.bin -n injector $(dir_build)/injector.cxi + @mv emunand.bin reboot.bin $(dir_injector)/injector.cxi $(@D) + @bin2c -o $@ -n emunand $(@D)/emunand.bin -n reboot $(@D)/reboot.bin -n injector $(@D)/injector.cxi $(dir_build)/loader.h: $(dir_loader)/Makefile @$(MAKE) -C $(dir_loader) - @mv $(dir_loader)/loader.bin $(dir_build) - @bin2c -o $@ -n loader $(dir_build)/loader.bin + @mv $(dir_loader)/loader.bin $(@D) + @bin2c -o $@ -n loader $(@D)/loader.bin $(dir_build)/screeninit.h: $(dir_screeninit)/Makefile @$(MAKE) -C $(dir_screeninit) - @mv $(dir_screeninit)/screeninit.bin $(dir_build) - @bin2c -o $@ -n screeninit $(dir_build)/screeninit.bin + @mv $(dir_screeninit)/screeninit.bin $(@D) + @bin2c -o $@ -n screeninit $(@D)/screeninit.bin $(dir_build)/main.bin: $(dir_build)/main.elf $(OC) -S -O binary $< $@ -$(dir_build)/main.elf: $(objects_cfw) - # FatFs requires libgcc for __aeabi_uidiv - $(CC) -nostartfiles $(LDFLAGS) -T linker.ld $(OUTPUT_OPTION) $^ +$(dir_build)/main.elf: $(objects) + $(CC) $(LDFLAGS) -T linker.ld $(OUTPUT_OPTION) $^ $(dir_build)/memory.o : CFLAGS += -O3 $(dir_build)/config.o : CFLAGS += -DCONFIG_TITLE="\"$(name) $(version) configuration\"" diff --git a/injector/source/patcher.c b/injector/source/patcher.c index 5162d1e..b42e431 100644 --- a/injector/source/patcher.c +++ b/injector/source/patcher.c @@ -11,7 +11,7 @@ #endif static u32 config = 0; -static u8 secureinfo[0x111] = {0}; +static u8 secureInfo[0x111] = {0}; //Quick Search algorithm, adapted from http://igm.univ-mlv.fr/~lecroq/string/node19.html#SECTION00190 static u8 *memsearch(u8 *startPos, const void *pattern, u32 size, u32 patternSize) @@ -79,40 +79,38 @@ static int fileOpen(IFile *file, FS_ArchiveID id, const char *path, int flags) return IFile_Open(file, archive, ppath, flags); } -static int loadSecureinfo() +static int loadSecureInfo(void) { - IFile file; - Result ret; - u64 total; - - if(secureinfo[0] == 0xFF) + if(secureInfo[0] == 0xFF) return 0; - ret = fileOpen(&file, ARCHIVE_NAND_RW, "/sys/SecureInfo_C", FS_OPEN_READ); + IFile file; + Result ret = fileOpen(&file, ARCHIVE_NAND_RW, "/sys/SecureInfo_C", FS_OPEN_READ); if(R_SUCCEEDED(ret)) { - ret = IFile_Read(&file, &total, secureinfo, sizeof(secureinfo)); + u64 total; + + ret = IFile_Read(&file, &total, secureInfo, 0x111); IFile_Close(&file); - if(R_SUCCEEDED(ret) && total == sizeof(secureinfo)) - secureinfo[0] = 0xFF; + if(R_SUCCEEDED(ret) && total == 0x111) + secureInfo[0] = 0xFF; } return ret; } -static int loadConfig() +static int loadConfig(void) { - IFile file; - Result ret; - u64 total; - if(config) return 0; - ret = fileOpen(&file, ARCHIVE_SDMC, "/aurei/config.bin", FS_OPEN_READ); + IFile file; + Result ret = fileOpen(&file, ARCHIVE_SDMC, "/aurei/config.bin", FS_OPEN_READ); if(R_SUCCEEDED(ret)) { - ret = IFile_Read(&file, &total, &config, 3); + u64 total; + + ret = IFile_Read(&file, &total, &config, 4); IFile_Close(&file); if(R_SUCCEEDED(ret)) config |= 1 << 4; } @@ -120,6 +118,147 @@ static int loadConfig() return ret; } +static int loadTitleLocaleConfig(u64 progId, u8 *regionId, u8 *languageId) +{ + /* Here we look for "/aurei/locales/[u64 titleID in hex, uppercase].txt" + If it exists it should contain, for example, "EUR IT" */ + + char path[] = "/aurei/locales/0000000000000000.txt"; + + u32 i = 30; + + while(progId > 0) + { + static const char hexDigits[] = "0123456789ABCDEF"; + path[i--] = hexDigits[(u32)(progId & 0xF)]; + progId >>= 4; + } + + IFile file; + Result ret = fileOpen(&file, ARCHIVE_SDMC, path, FS_OPEN_READ); + if(R_SUCCEEDED(ret)) + { + char buf[6]; + u64 total; + + ret = IFile_Read(&file, &total, buf, 6); + IFile_Close(&file); + + if(!R_SUCCEEDED(ret) || total < 6) return -1; + + static const char *regions[] = {"JPN", "USA", "EUR", "AUS", "CHN", "KOR", "TWN"}; + static const char *languages[] = {"JP", "EN", "FR", "DE", "IT", "ES", "ZH", "KO", "NL", "PT", "RU", "TW"}; + + for(u32 i = 0; i < 7; ++i) + if(memcmp(buf, regions[i], 3) == 0) + { + *regionId = (u8)i; + break; + } + + for(u32 i = 0; i < 12; ++i) + if(memcmp(buf + 4, languages[i], 2) == 0) + { + *languageId = (u8)i; + break; + } + } + + return ret; +} + +static u8 *getCfgOffsets(u8* code, u32 size, u32 *CFGUHandleOffset) +{ + static const u8 CFGU_GetConfigInfoBlk2_endPattern[] = { + 0x10, 0x80, 0xBD, 0xE8, 0x82, 0x00, 0x01, 0x00 + }; + + u8 *CFGU_GetConfigInfoBlk2_endPos = code; + + while((CFGU_GetConfigInfoBlk2_endPos + sizeof(CFGU_GetConfigInfoBlk2_endPattern)) < (code + size) + && (*CFGUHandleOffset > 0x10000000UL || ((u32)CFGU_GetConfigInfoBlk2_endPos % 4) != 0)) + { + //There might be multiple implementations of GetConfigInfoBlk2 but let's search for the one we want + CFGU_GetConfigInfoBlk2_endPos += sizeof(CFGU_GetConfigInfoBlk2_endPattern); + CFGU_GetConfigInfoBlk2_endPos = memsearch(CFGU_GetConfigInfoBlk2_endPos, CFGU_GetConfigInfoBlk2_endPattern, + size - (u32)(CFGU_GetConfigInfoBlk2_endPos - code), sizeof(CFGU_GetConfigInfoBlk2_endPattern)); + + if(CFGU_GetConfigInfoBlk2_endPos == NULL) break; + + *CFGUHandleOffset = *(u32 *)(CFGU_GetConfigInfoBlk2_endPos + 8); + } + + return CFGU_GetConfigInfoBlk2_endPos; +} + +static void patchCfgGetLanguage(u8* code, u32 size, u8 languageId, u8 *CFGU_GetConfigInfoBlk2_endPos) +{ + u8 *CFGU_GetConfigInfoBlk2_startPos = CFGU_GetConfigInfoBlk2_endPos; //Let's find STMFD SP (there might be a NOP before, but nevermind) + while(*(u16 *)CFGU_GetConfigInfoBlk2_startPos != 0xE92D) CFGU_GetConfigInfoBlk2_startPos -= 2; + CFGU_GetConfigInfoBlk2_startPos -= 2; + + u8 *languageBlkIdPos = code; + u32 patched = 0; + + while((languageBlkIdPos + 4) < (code + size) && !patched) + { + static const u32 languageBlkId = 0xA0002; + + languageBlkIdPos += 4; + languageBlkIdPos = memsearch(languageBlkIdPos, &languageBlkId, size - (u32)(languageBlkIdPos - code), 4); + + if(languageBlkIdPos == NULL) break; + if(((u32)languageBlkIdPos % 4) != 0) continue; + + for(u8 *instr = languageBlkIdPos - 8; instr >= languageBlkIdPos - 0x1008; instr -= 4) //Should be enough + { + if(*(instr + 3) != 0xEB) continue; //We're looking for BL + u32 low24 = (*(u32 *)instr & 0x00FFFFFF) << 2; + s32 offset = (((low24 >> 25) != 0) ? -low24 : low24) + 8; //Branch offset + 8 for prefetch + + if((instr + offset) >= (CFGU_GetConfigInfoBlk2_startPos - 4) && (instr + offset) <= CFGU_GetConfigInfoBlk2_endPos) + { + *(u32 *)(instr - 4) = 0xE3A00000 | languageId; // mov r0, sp => mov r0, =languageID + *(u32 *)instr = 0xE5CD0000; // bl CFGU_GetConfigInfoBlk2 => strb r0, [sp] + *(u32 *)(instr + 4) = 0xE3A00000; // mov r1, pc => mov r0, 0 (result code) + + //We're done + patched = 1; + break; + } + } + } +} + +static void patchCfgGetRegion(u8* code, u32 size, u8 regionID, u32 *CFGUHandleOffset) +{ + static const u8 cfgSecureInfoGetRegionCmdPattern[] = { + 0x70, 0x4F, 0x1D, 0xEE, 0x02, 0x08, 0xA0, 0xE3, 0x80, 0x00, 0xA4, 0xE5 + }; + + u8 *cmdPos = code; + + while((cmdPos + sizeof(cfgSecureInfoGetRegionCmdPattern)) < (code + size)) + { + cmdPos += sizeof(cfgSecureInfoGetRegionCmdPattern); + cmdPos = memsearch(cmdPos, cfgSecureInfoGetRegionCmdPattern, size - (u32)(cmdPos - code), sizeof(cfgSecureInfoGetRegionCmdPattern)); + + if(cmdPos == NULL) break; + if(*(u16 *)(cmdPos + 12 + 2) != 0xE59F) continue; // ldr r0, [pc, X] + + if(*(u32 *)(cmdPos + 12 + 8 + *(u16 *)(cmdPos + 12)) == *CFGUHandleOffset) + { + *(u32 *)(cmdPos + 16) = 0xE3A00000 | regionID; // mov r0, =regionID + *(u32 *)(cmdPos + 20) = 0xE5C40008; // strb r0, [r4, 8] + *(u32 *)(cmdPos + 24) = 0xE3A00000; // mov r0, 0 (result code) + *(u32 *)(cmdPos + 28) = 0xE5840004; // str r0, [r4, 4] + + //The remaining, not patched, function code will do the rest for us + break; + } + } +} + void patchCode(u64 progId, u8 *code, u32 size) { switch(progId) @@ -257,7 +396,7 @@ void patchCode(u64 progId, u8 *code, u32 size) if(cfgN3dsCpuLoc != NULL) { *(u32 *)(cfgN3dsCpuLoc + 3) = 0xE1A00000; - *(u32 *)(cfgN3dsCpuLoc + 0x1F) = 0xE3A00000 + MULTICONFIG(1); + *(u32 *)(cfgN3dsCpuLoc + 0x1F) = 0xE3A00000 | MULTICONFIG(1); } } @@ -281,7 +420,7 @@ void patchCode(u64 progId, u8 *code, u32 size) sizeof(secureinfoSigCheckPatch), 1 ); - if(R_SUCCEEDED(loadSecureinfo())) + if(R_SUCCEEDED(loadSecureInfo())) { static const u16 secureinfoFilenamePattern[] = u"SecureInfo_"; static const u16 secureinfoFilenamePatch[] = u"C"; @@ -298,5 +437,29 @@ void patchCode(u64 progId, u8 *code, u32 size) break; } + + default: + if((progId & 0xFFFFFFFF00000000LL) == 0x0004000000000000LL && R_SUCCEEDED(loadConfig()) && CONFIG(4)) + { + //Language emulation + u8 regionId = 0xFF, + languageId = 0xFF; + + int ret = loadTitleLocaleConfig(progId, ®ionId, &languageId); + + if(R_SUCCEEDED(ret)) + { + u32 CFGUHandleOffset = 0xFFFFFFFF; + u8 *CFGU_GetConfigInfoBlk2_endPos = getCfgOffsets(code, size, &CFGUHandleOffset); + + if(CFGU_GetConfigInfoBlk2_endPos != NULL) + { + if(languageId != 0xFF) patchCfgGetLanguage(code, size, languageId, CFGU_GetConfigInfoBlk2_endPos); + if(regionId != 0xFF) patchCfgGetRegion(code, size, regionId, &CFGUHandleOffset); + } + } + } + + break; } } \ No newline at end of file diff --git a/loader/Makefile b/loader/Makefile index c8b41bc..55a5c71 100644 --- a/loader/Makefile +++ b/loader/Makefile @@ -17,11 +17,12 @@ dir_source := source dir_build := build ASFLAGS := -mlittle-endian -mcpu=arm946e-s -march=armv5te -CFLAGS := -Wall -Wextra -MMD -MP -marm $(ASFLAGS) -fno-builtin -fshort-wchar -std=c11 -Wno-main -O2 -flto -ffast-math -mthumb -mthumb-interwork +CFLAGS := -Wall -Wextra -MMD -MP -marm $(ASFLAGS) -fno-builtin -std=c11 -Wno-main -O2 -flto -ffast-math -mthumb -mthumb-interwork +LDFLAGS := -nostartfiles objects = $(patsubst $(dir_source)/%.s, $(dir_build)/%.o, \ - $(patsubst $(dir_source)/%.c, $(dir_build)/%.o, \ - $(call rwildcard, $(dir_source), *.s *.c))) + $(patsubst $(dir_source)/%.c, $(dir_build)/%.o, \ + $(call rwildcard, $(dir_source), *.s *.c))) .PHONY: all all: $(name).bin @@ -34,8 +35,7 @@ $(name).bin: $(dir_build)/$(name).elf $(OC) -S -O binary $< $@ $(dir_build)/$(name).elf: $(objects) - # FatFs requires libgcc for __aeabi_uidiv - $(CC) -nostartfiles -T linker.ld $(OUTPUT_OPTION) $^ + $(CC) $(LDFLAGS) -T linker.ld $(OUTPUT_OPTION) $^ $(dir_build)/%.o: $(dir_source)/%.c @mkdir -p "$(@D)" diff --git a/screeninit/Makefile b/screeninit/Makefile index a6e3e42..c20af74 100755 --- a/screeninit/Makefile +++ b/screeninit/Makefile @@ -17,11 +17,12 @@ dir_source := source dir_build := build ASFLAGS := -mlittle-endian -mcpu=mpcore -CFLAGS := -Wall -Wextra -MMD -MP -marm $(ASFLAGS) -fno-builtin -fshort-wchar -std=c11 -Wno-main -O2 -flto -ffast-math -mthumb -mthumb-interwork +CFLAGS := -Wall -Wextra -MMD -MP -marm $(ASFLAGS) -fno-builtin -std=c11 -Wno-main -O2 -flto -ffast-math -mthumb -mthumb-interwork +LDFLAGS := -nostdlib objects = $(patsubst $(dir_source)/%.s, $(dir_build)/%.o, \ - $(patsubst $(dir_source)/%.c, $(dir_build)/%.o, \ - $(call rwildcard, $(dir_source), *.s *.c))) + $(patsubst $(dir_source)/%.c, $(dir_build)/%.o, \ + $(call rwildcard, $(dir_source), *.s *.c))) .PHONY: all all: $(name).bin @@ -34,7 +35,7 @@ $(name).bin: $(dir_build)/$(name).elf $(OC) -S -O binary $< $@ $(dir_build)/$(name).elf: $(objects) - $(CC) -nostartfiles -T linker.ld $(OUTPUT_OPTION) $^ + $(CC) $(LDFLAGS) -T linker.ld $(OUTPUT_OPTION) $^ $(dir_build)/%.o: $(dir_source)/%.c @mkdir -p "$(@D)" diff --git a/source/config.c b/source/config.c index 32fdfd8..2eb1d7b 100644 --- a/source/config.c +++ b/source/config.c @@ -26,6 +26,7 @@ void configureCFW(const char *configPath) "( ) Updated SysNAND mode (A9LH-only)", "( ) Force A9LH detection", "( ) Use second EmuNAND as default", + "( ) Enable region/language emulation", "( ) Use developer UNITINFO", "( ) Show current NAND in System Settings", "( ) Show GBA boot screen in patched AGB_FIRM", diff --git a/source/firm.c b/source/firm.c index 4571cdf..ad5e40f 100755 --- a/source/firm.c +++ b/source/firm.c @@ -142,7 +142,7 @@ void main(void) configureCFW(configPath); //If screens are inited or the corresponding option is set, load splash screen - if(PDN_GPU_CNT != 1 || CONFIG(7)) loadSplash(); + if(PDN_GPU_CNT != 1 || CONFIG(8)) loadSplash(); //Determine if we need to boot an emuNAND or sysNAND nandType = (pressed & BUTTON_L1) ? autoBootSys : ((pressed & BUTTON_R1) ? updatedSys : !autoBootSys); @@ -271,7 +271,7 @@ static inline void patchNativeFirm(u32 nandType, u32 emuHeader, u32 a9lhInstalle *(u16 *)sigOffset2 = sigPatch[0]; *((u16 *)sigOffset2 + 1) = sigPatch[1]; - if(CONFIG(4)) + if(CONFIG(5)) { //Apply UNITINFO patch u8 *unitInfoOffset = getUnitInfoValueSet(arm9Section, section[2].size); @@ -381,7 +381,7 @@ static inline void patchTwlAgbFirm(u32 firmType) /* Calculate the amount of patches to apply. Only count the boot screen patch for AGB_FIRM if the matching option was enabled (keep it as last) */ - u32 numPatches = firmType == 1 ? (sizeof(twlPatches) / sizeof(patchData)) : (sizeof(agbPatches) / sizeof(patchData) - !CONFIG(6)); + u32 numPatches = firmType == 1 ? (sizeof(twlPatches) / sizeof(patchData)) : (sizeof(agbPatches) / sizeof(patchData) - !CONFIG(7)); const patchData *patches = firmType == 1 ? twlPatches : agbPatches; //Patch