diff --git a/Makefile b/Makefile index 08036c1..7c18946 100644 --- a/Makefile +++ b/Makefile @@ -33,7 +33,7 @@ include $(DEVKITPRO)/libnx/switch_rules VERSION_MAJOR := 1 VERSION_MINOR := 1 -VERSION_MICRO := 11 +VERSION_MICRO := 12 APP_TITLE := nxdumptool APP_AUTHOR := DarkMatterCore diff --git a/README.md b/README.md index c74f9cc..e478e6b 100644 --- a/README.md +++ b/README.md @@ -71,6 +71,12 @@ Thanks to Changelog -------------- +**v1.1.12:** + +* Fixed RomFS dumping/browsing support for games with base Program NCAs without a RomFS section (e.g. Fortnite, World of Tanks Blitz, etc.). Big thanks to [bigkahuna666](https://github.com/bigkahuna666) for reporting the issue and providing with testing. + +This is only a bugfix release. I don't expect to release any new versions until the rewrite is finished - the only exception being fixing some kind of feature-breaking bug. Please understand. + **v1.1.11:** * Built using libnx `f01fb21`. diff --git a/source/dumper.c b/source/dumper.c index 37f0c70..d5bbc68 100644 --- a/source/dumper.c +++ b/source/dumper.c @@ -4641,7 +4641,7 @@ bool dumpRomFsSectionData(u32 titleIndex, selectedRomFsType curRomFsType, ncaFsO } // Retrieve RomFS from Program NCA - if (!readNcaRomFsSection(titleIndex, curRomFsType, -1)) + if (readNcaRomFsSection(titleIndex, curRomFsType, -1) != 0) { free(dumpName); breaks += 2; diff --git a/source/nca.c b/source/nca.c index 36a3696..ad4db15 100644 --- a/source/nca.c +++ b/source/nca.c @@ -596,7 +596,7 @@ bool bktrSectionPhysicalRead(void *outBuf, size_t bufSize) bool readBktrSectionBlock(u64 offset, void *outBuf, size_t bufSize) { - if (!bktrContext.section_offset || !bktrContext.section_size || !bktrContext.relocation_block || !bktrContext.subsection_block || !romFsContext.section_offset || !romFsContext.section_size || !outBuf || !bufSize) + if (!bktrContext.section_offset || !bktrContext.section_size || !bktrContext.relocation_block || (bktrContext.use_base_romfs && (!bktrContext.subsection_block || !romFsContext.section_offset || !romFsContext.section_size || !outBuf || !bufSize))) { uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "%s: invalid parameters to read block from NCA BKTR section!", __func__); return false; @@ -622,6 +622,13 @@ bool readBktrSectionBlock(u64 offset, void *outBuf, size_t bufSize) { if (!bktrSectionPhysicalRead(outBuf, bufSize)) return false; } else { + if (!bktrContext.use_base_romfs) + { + breaks++; + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "%s: BKTR references unexistant base RomFS block(s)!", __func__); + return false; + } + // Nice and easy read from the base RomFS if (!processNcaCtrSectionBlock(&(romFsContext.ncmStorage), &(romFsContext.ncaId), &(romFsContext.aes_ctx), romFsContext.section_offset + bktrContext.base_seek, outBuf, bufSize, false)) return false; } @@ -1686,12 +1693,12 @@ bool parseExeFsEntryFromNca(NcmContentStorage *ncmStorage, const NcmContentId *n return true; } -bool parseRomFsEntryFromNca(NcmContentStorage *ncmStorage, const NcmContentId *ncaId, nca_header_t *dec_nca_header, u8 *decrypted_nca_keys) +int parseRomFsEntryFromNca(NcmContentStorage *ncmStorage, const NcmContentId *ncaId, nca_header_t *dec_nca_header, u8 *decrypted_nca_keys) { if (!ncmStorage || !ncaId || !dec_nca_header || !decrypted_nca_keys) { uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "%s: invalid parameters to read RomFS section from NCA!", __func__); - return false; + return -1; } u8 romfs_index; @@ -1733,7 +1740,7 @@ bool parseRomFsEntryFromNca(NcmContentStorage *ncmStorage, const NcmContentId *n if (!found_romfs) { uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "%s: NCA doesn't hold a RomFS section!", __func__); - return false; + return -2; } section_offset = ((u64)dec_nca_header->section_entries[romfs_index].media_start_offset * (u64)MEDIA_UNIT_SIZE); @@ -1742,7 +1749,7 @@ bool parseRomFsEntryFromNca(NcmContentStorage *ncmStorage, const NcmContentId *n if (!section_offset || section_offset < NCA_FULL_HEADER_LENGTH || !section_size) { uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "%s: invalid offset/size for NCA RomFS section! (#%u)", __func__, romfs_index); - return false; + return -1; } // Generate initial CTR @@ -1763,13 +1770,13 @@ bool parseRomFsEntryFromNca(NcmContentStorage *ncmStorage, const NcmContentId *n if (__builtin_bswap32(dec_nca_header->fs_headers[romfs_index].romfs_superblock.ivfc_header.magic) != IVFC_MAGIC) { uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "%s: invalid IVFC magic word for NCA RomFS section! Wrong KAEK? (0x%08X)\nTry running Lockpick_RCM to generate the keys file from scratch.", __func__, __builtin_bswap32(dec_nca_header->fs_headers[romfs_index].romfs_superblock.ivfc_header.magic)); - return false; + return -1; } if (dec_nca_header->fs_headers[romfs_index].crypt_type != NCA_FS_HEADER_CRYPT_CTR) { uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "%s: invalid AES crypt type for NCA RomFS section! (0x%02X)", __func__, dec_nca_header->fs_headers[romfs_index].crypt_type); - return false; + return -1; } romfs_offset = (section_offset + dec_nca_header->fs_headers[romfs_index].romfs_superblock.ivfc_header.level_headers[IVFC_MAX_LEVEL - 1].logical_offset); @@ -1778,7 +1785,7 @@ bool parseRomFsEntryFromNca(NcmContentStorage *ncmStorage, const NcmContentId *n if (romfs_offset < section_offset || romfs_offset >= (section_offset + section_size) || romfs_size < ROMFS_HEADER_SIZE || (romfs_offset + romfs_size) > (section_offset + section_size)) { uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "%s: invalid offset/size for NCA RomFS section!", __func__); - return false; + return -1; } // First read the RomFS header @@ -1786,13 +1793,13 @@ bool parseRomFsEntryFromNca(NcmContentStorage *ncmStorage, const NcmContentId *n { breaks++; uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "%s: failed to read NCA RomFS section header!", __func__); - return false; + return -1; } if (romFsHeader.headerSize != ROMFS_HEADER_SIZE) { uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "%s: invalid header size for NCA RomFS section! (0x%016lX at 0x%016lX)", __func__, romFsHeader.headerSize, romfs_offset); - return false; + return -1; } romfs_dirtable_offset = (romfs_offset + romFsHeader.dirTableOff); @@ -1804,7 +1811,7 @@ bool parseRomFsEntryFromNca(NcmContentStorage *ncmStorage, const NcmContentId *n if (romfs_dirtable_offset >= (section_offset + section_size) || !romfs_dirtable_size || (romfs_dirtable_offset + romfs_dirtable_size) > (section_offset + section_size) || romfs_filetable_offset >= (section_offset + section_size) || !romfs_filetable_size || (romfs_filetable_offset + romfs_filetable_size) > (section_offset + section_size)) { uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "%s: invalid directory/file table for NCA RomFS section!", __func__); - return false; + return -1; } romfs_filedata_offset = (romfs_offset + romFsHeader.fileDataOff); @@ -1812,14 +1819,14 @@ bool parseRomFsEntryFromNca(NcmContentStorage *ncmStorage, const NcmContentId *n if (romfs_filedata_offset >= (section_offset + section_size)) { uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "%s: invalid file data block offset for NCA RomFS section!", __func__); - return false; + return -1; } romfs_dir_entries = calloc(1, romfs_dirtable_size); if (!romfs_dir_entries) { uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "%s: unable to allocate memory for NCA RomFS section directory entries!", __func__); - return false; + return -1; } if (!processNcaCtrSectionBlock(ncmStorage, ncaId, &aes_ctx, romfs_dirtable_offset, romfs_dir_entries, romfs_dirtable_size, false)) @@ -1827,7 +1834,7 @@ bool parseRomFsEntryFromNca(NcmContentStorage *ncmStorage, const NcmContentId *n breaks++; uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "%s: failed to read NCA RomFS section directory entries!", __func__); free(romfs_dir_entries); - return false; + return -1; } romfs_file_entries = calloc(1, romfs_filetable_size); @@ -1835,7 +1842,7 @@ bool parseRomFsEntryFromNca(NcmContentStorage *ncmStorage, const NcmContentId *n { uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "%s: unable to allocate memory for NCA RomFS section file entries!", __func__); free(romfs_dir_entries); - return false; + return -1; } if (!processNcaCtrSectionBlock(ncmStorage, ncaId, &aes_ctx, romfs_filetable_offset, romfs_file_entries, romfs_filetable_size, false)) @@ -1844,7 +1851,7 @@ bool parseRomFsEntryFromNca(NcmContentStorage *ncmStorage, const NcmContentId *n uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "%s: failed to read NCA RomFS section file entries!", __func__); free(romfs_file_entries); free(romfs_dir_entries); - return false; + return -1; } // Save data to output struct @@ -1864,12 +1871,12 @@ bool parseRomFsEntryFromNca(NcmContentStorage *ncmStorage, const NcmContentId *n romFsContext.romfs_file_entries = romfs_file_entries; romFsContext.romfs_filedata_offset = romfs_filedata_offset; - return true; + return 0; } bool parseBktrEntryFromNca(NcmContentStorage *ncmStorage, const NcmContentId *ncaId, nca_header_t *dec_nca_header, u8 *decrypted_nca_keys) { - if (!ncmStorage || !ncaId || !dec_nca_header || !decrypted_nca_keys || !romFsContext.section_offset || !romFsContext.section_size || !romFsContext.romfs_dir_entries || !romFsContext.romfs_file_entries) + if (!ncmStorage || !ncaId || !dec_nca_header || !decrypted_nca_keys || (bktrContext.use_base_romfs && (!romFsContext.section_offset || !romFsContext.section_size || !romFsContext.romfs_dir_entries || !romFsContext.romfs_file_entries))) { uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "%s: invalid parameters to read BKTR section from NCA!", __func__); return false; @@ -2941,7 +2948,7 @@ bool retrieveNacpDataFromNca(NcmContentStorage *ncmStorage, const NcmContentId * bool availableSGC = false, availableRGC = false; - if (!parseRomFsEntryFromNca(ncmStorage, ncaId, dec_nca_header, decrypted_nca_keys)) return false; + if (parseRomFsEntryFromNca(ncmStorage, ncaId, dec_nca_header, decrypted_nca_keys) != 0) return false; // Look for the control.nacp file while(entryOffset < romFsContext.romfs_filetable_size) @@ -3413,7 +3420,7 @@ bool retrieveLegalInfoXmlFromNca(NcmContentStorage *ncmStorage, const NcmContent u64 legalInfoXmlSize = 0; char *legalInfoXml = NULL; - if (!parseRomFsEntryFromNca(ncmStorage, ncaId, dec_nca_header, decrypted_nca_keys)) return false; + if (parseRomFsEntryFromNca(ncmStorage, ncaId, dec_nca_header, decrypted_nca_keys) != 0) return false; // Look for the legalinfo.xml file while(entryOffset < romFsContext.romfs_filetable_size) diff --git a/source/nca.h b/source/nca.h index 6e43f0b..a633724 100644 --- a/source/nca.h +++ b/source/nca.h @@ -448,6 +448,7 @@ typedef struct { u64 romfs_filetable_size; romfs_file *romfs_file_entries; u64 romfs_filedata_offset; // Relative to section start + bool use_base_romfs; } bktr_ctx_t; // Used in HFS0 / ExeFS / RomFS browsers @@ -748,7 +749,7 @@ bool patchCnmtNca(u8 *ncaBuf, u64 ncaBufSize, cnmt_xml_program_info *xml_program bool parseExeFsEntryFromNca(NcmContentStorage *ncmStorage, const NcmContentId *ncaId, nca_header_t *dec_nca_header, u8 *decrypted_nca_keys); -bool parseRomFsEntryFromNca(NcmContentStorage *ncmStorage, const NcmContentId *ncaId, nca_header_t *dec_nca_header, u8 *decrypted_nca_keys); +int parseRomFsEntryFromNca(NcmContentStorage *ncmStorage, const NcmContentId *ncaId, nca_header_t *dec_nca_header, u8 *decrypted_nca_keys); bool parseBktrEntryFromNca(NcmContentStorage *ncmStorage, const NcmContentId *ncaId, nca_header_t *dec_nca_header, u8 *decrypted_nca_keys); diff --git a/source/ui.c b/source/ui.c index f5c1260..4adb780 100644 --- a/source/ui.c +++ b/source/ui.c @@ -5035,7 +5035,7 @@ UIResult uiProcess() bool romfs_fail = false; - if (readNcaRomFsSection(curIndex, curRomFsType, -1)) + if (readNcaRomFsSection(curIndex, curRomFsType, -1) == 0) { if (getRomFsFileList(0, (curRomFsType == ROMFS_TYPE_PATCH))) { diff --git a/source/util.c b/source/util.c index 06e0787..ccd1605 100644 --- a/source/util.c +++ b/source/util.c @@ -669,6 +669,8 @@ void freeBktrContext() free(bktrContext.romfs_file_entries); bktrContext.romfs_file_entries = NULL; } + + bktrContext.use_base_romfs = false; } static void freeGameCardInfo() @@ -2727,7 +2729,7 @@ out: return success; } -bool readNcaRomFsSection(u32 titleIndex, selectedRomFsType curRomFsType, int desiredIdOffset) +int readNcaRomFsSection(u32 titleIndex, selectedRomFsType curRomFsType, int desiredIdOffset) { u32 i = 0; Result result; @@ -2755,7 +2757,7 @@ bool readNcaRomFsSection(u32 titleIndex, selectedRomFsType curRomFsType, int des u8 decrypted_nca_keys[NCA_KEY_AREA_SIZE]; - bool success = false; + int ret = -1; if (curRomFsType != ROMFS_TYPE_APP && curRomFsType != ROMFS_TYPE_PATCH && curRomFsType != ROMFS_TYPE_ADDON) { @@ -2879,8 +2881,8 @@ bool readNcaRomFsSection(u32 titleIndex, selectedRomFsType curRomFsType, int des if (curRomFsType != ROMFS_TYPE_PATCH) { // Read directory and file tables from the RomFS section - success = parseRomFsEntryFromNca(&ncmStorage, &ncaId, &dec_nca_header, decrypted_nca_keys); - if (success) + ret = parseRomFsEntryFromNca(&ncmStorage, &ncaId, &dec_nca_header, decrypted_nca_keys); + if (ret == 0) { romFsContext.storageId = curStorageId; romFsContext.idOffset = titleContentInfos[contentIndex].id_offset; @@ -2905,11 +2907,19 @@ bool readNcaRomFsSection(u32 titleIndex, selectedRomFsType curRomFsType, int des } // Read directory and file tables from the RomFS section in the Program NCA from the base application - if (!readNcaRomFsSection(appIndex, ROMFS_TYPE_APP, (int)titleContentInfos[contentIndex].id_offset)) goto out; + // We'll proceed even if the Program NCA from the base application doesn't hold a RomFS section (ret == -2) + ret = readNcaRomFsSection(appIndex, ROMFS_TYPE_APP, (int)titleContentInfos[contentIndex].id_offset); + if (ret == -1) goto out; + + // Remove missing base RomFS error message if needed + if (ret == -2) uiFill(0, STRING_Y_POS(breaks), FB_WIDTH, FB_HEIGHT - STRING_Y_POS(breaks), BG_COLOR_RGB); + + // Update BKTR context to use the base RomFS if available + bktrContext.use_base_romfs = (ret == 0); // Read BKTR entry data in the Program NCA from the update - success = parseBktrEntryFromNca(&ncmStorage, &ncaId, &dec_nca_header, decrypted_nca_keys); - if (success) + ret = (parseBktrEntryFromNca(&ncmStorage, &ncaId, &dec_nca_header, decrypted_nca_keys) ? 0 : -1); + if (ret == 0) { bktrContext.storageId = curStorageId; bktrContext.idOffset = titleContentInfos[contentIndex].id_offset; @@ -2917,7 +2927,7 @@ bool readNcaRomFsSection(u32 titleIndex, selectedRomFsType curRomFsType, int des } out: - if (!success) + if (ret != 0) { ncmContentStorageClose(&ncmStorage); if (curStorageId == NcmStorageId_GameCard) closeGameCardStoragePartition(); @@ -2925,7 +2935,7 @@ out: if (titleContentInfos) free(titleContentInfos); - return success; + return ret; } bool getExeFsFileList() diff --git a/source/util.h b/source/util.h index 61b6059..b869c2e 100644 --- a/source/util.h +++ b/source/util.h @@ -388,7 +388,7 @@ void removeConsoleDataFromTicket(title_rights_ctx *rights_info); bool readNcaExeFsSection(u32 titleIndex, bool usePatch); -bool readNcaRomFsSection(u32 titleIndex, selectedRomFsType curRomFsType, int desiredIdOffset); +int readNcaRomFsSection(u32 titleIndex, selectedRomFsType curRomFsType, int desiredIdOffset); bool getExeFsFileList();