static inline 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; u32 archive = openLumaFile(&file, path); if(!archive) return true; 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; //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 //Place the hooks *(u32 *)(code + fsOpenFileDirectly) = MAKE_BRANCH(fsOpenFileDirectly, throwFatalError); 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; }
static int 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; Result ret = fileOpen(&file, ARCHIVE_SDMC, path, FS_OPEN_READ); if(R_SUCCEEDED(ret)) { u64 fileSize, total; ret = IFile_GetSize(&file, &fileSize); if(!R_SUCCEEDED(ret) || fileSize > size) return -1; ret = IFile_Read(&file, &total, code, fileSize); IFile_Close(&file); if(!R_SUCCEEDED(ret)) return -1; else if(total < fileSize) return -2; //Shouldn't happen } return ret; }
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(!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 void loadCustomVerString(u16 *out, u32 *verStringSize, u32 currentNand) { static const char *paths[] = { "/puma/customversion_sys.txt", "/puma/customversion_emu.txt", "/puma/customversion_emu2.txt", "/puma/customversion_emu3.txt", "/puma/customversion_emu4.txt" }; IFile file; if(R_SUCCEEDED(fileOpen(&file, ARCHIVE_SDMC, paths[currentNand], FS_OPEN_READ))) { u64 fileSize; if(R_SUCCEEDED(IFile_GetSize(&file, &fileSize)) && fileSize <= 62) { u8 buf[fileSize]; u64 total; if(R_SUCCEEDED(IFile_Read(&file, &total, buf, fileSize))) { static const u8 bom[] = {0xEF, 0xBB, 0xBF}; u32 finalSize = 0; //Convert from UTF-8 to UTF-16 (Nintendo doesn't support 4-byte UTF-16, so 4-byte UTF-8 is unsupported) for(u32 increase, fileSizeTmp = (u32)fileSize, i = (fileSizeTmp > 2 && memcmp(buf, bom, 3) == 0) ? 3 : 0; i < fileSizeTmp && finalSize < 19; i += increase, finalSize++) { if((buf[i] & 0x80) == 0 && !(buf[i] == 0xA || buf[i] == 0xD)) { increase = 1; out[finalSize] = (u16)buf[i]; } else if((buf[i] & 0xE0) == 0xC0 && i + 1 < fileSizeTmp && (buf[i + 1] & 0xC0) == 0x80) { increase = 2; out[finalSize] = (u16)(((buf[i] & 0x1F) << 6) | (buf[i + 1] & 0x3F)); } else if((buf[i] & 0xF0) == 0xE0 && i + 2 < fileSizeTmp && (buf[i + 1] & 0xC0) == 0x80 && (buf[i + 2] & 0xC0) == 0x80) { increase = 3; out[finalSize] = (u16)(((buf[i] & 0xF) << 12) | ((buf[i + 1] & 0x3F) << 6) | (buf[i + 2] & 0x3F)); } else break; } if(finalSize > 0) { if(finalSize > 5 && finalSize < 19) out[finalSize++] = 0; *verStringSize = finalSize * 2; } } } IFile_Close(&file); } }
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(!openLumaFile(&file, path)) return true; bool ret = false; u64 fileSize; if(R_FAILED(IFile_GetSize(&file, &fileSize)) || fileSize < 6 || fileSize > 8) goto exit; char buf[8]; u64 total; if(R_FAILED(IFile_Read(&file, &total, buf, fileSize))) 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 int 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"; u32 i = 29; while(progId) { 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; for(u32 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(u32 i = 0; i < 12; ++i) { static const char *languages[] = {"JP", "EN", "FR", "DE", "IT", "ES", "ZH", "KO", "NL", "PT", "RU", "TW"}; if(memcmp(buf + 4, languages[i], 2) == 0) { *languageId = (u8)i; break; } } } return ret; }
static inline void loadCFWInfo(void) { static bool infoLoaded = false; if(infoLoaded) return; svcGetCFWInfo(&info); IFile file; if(LOADERFLAG(ISSAFEMODE) && R_SUCCEEDED(fileOpen(&file, ARCHIVE_SDMC, "/", FS_OPEN_READ))) //Init SD card if SAFE_MODE is being booted IFile_Close(&file); infoLoaded = true; }
static inline bool secureInfoExists(void) { static bool exists = false; if(exists) return true; IFile file; if(R_SUCCEEDED(fileOpen(&file, ARCHIVE_NAND_RW, "/sys/SecureInfo_C", FS_OPEN_READ))) { exists = true; IFile_Close(&file); } return exists; }
static void loadCFWInfo(void) { static bool infoLoaded = false; if(!infoLoaded) { svcGetCFWInfo(&info); IFile file; if(BOOTCFG_SAFEMODE != 0 && R_SUCCEEDED(fileOpen(&file, ARCHIVE_SDMC, "/", FS_OPEN_READ))) //Init SD card if SAFE_MODE is being booted IFile_Close(&file); infoLoaded = true; } }
static void loadTitleLocaleConfig(u64 progId, u8 *regionId, u8 *languageId) { /* Here we look for "/puma/locales/[u64 titleID in hex, uppercase].txt" If it exists it should contain, for example, "EUR IT" */ char path[] = "/puma/locales/0000000000000000.txt"; progIdToStr(path + 29, progId); IFile file; if(R_SUCCEEDED(fileOpen(&file, ARCHIVE_SDMC, path, FS_OPEN_READ))) { u64 fileSize; if(R_SUCCEEDED(IFile_GetSize(&file, &fileSize)) && fileSize > 5 && fileSize < 9) { char buf[fileSize]; u64 total; if(R_SUCCEEDED(IFile_Read(&file, &total, buf, fileSize))) { for(u32 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(u32 i = 0; i < 12; i++) { static const char *languages[] = {"JP", "EN", "FR", "DE", "IT", "ES", "ZH", "KO", "NL", "PT", "RU", "TW"}; if(memcmp(buf + 4, languages[i], 2) == 0) { *languageId = (u8)i; break; } } } } IFile_Close(&file); } }
static u32 secureInfoExists(void) { static u32 secureInfoExists = 0; if(!secureInfoExists) { IFile file; if(R_SUCCEEDED(fileOpen(&file, ARCHIVE_NAND_RW, "/sys/SecureInfo_C", FS_OPEN_READ))) { secureInfoExists = 1; IFile_Close(&file); } } return secureInfoExists; }
static u32 loadConfig(void) { static u32 config = 0; if(!config) { IFile file; if(R_SUCCEEDED(fileOpen(&file, ARCHIVE_SDMC, "/luma/config.bin", FS_OPEN_READ))) { u64 total; if(R_SUCCEEDED(IFile_Read(&file, &total, &config, 4))) config |= 1 << 4; IFile_Close(&file); } } return config; }
static int loadCountryConfig(char *countryString) { /* Here we look for "/puma/locales/country.txt" If it exists it should contain, for example, "GB" */ char path[] = "/puma/locales/country.txt"; IFile file; Result ret = fileOpen(&file, ARCHIVE_SDMC, path, FS_OPEN_READ); if(R_SUCCEEDED(ret)) { u64 total; ret = IFile_Read(&file, &total, countryString, 2); IFile_Close(&file); if(!R_SUCCEEDED(ret) || total < 2) return -1; return 0; } return ret; }
static void loadTitleCodeSection(u64 progId, u8 *code, u32 size) { /* Here we look for "/puma/code_sections/[u64 titleID in hex, uppercase].bin" If it exists it should be a decompressed binary code file */ char path[] = "/puma/code_sections/0000000000000000.bin"; progIdToStr(path + 35, progId); IFile file; if(R_SUCCEEDED(fileOpen(&file, ARCHIVE_SDMC, path, FS_OPEN_READ))) { u64 fileSize; if(R_SUCCEEDED(IFile_GetSize(&file, &fileSize)) && fileSize <= size) { u64 total; IFile_Read(&file, &total, code, fileSize); } IFile_Close(&file); } }