diff --git a/code_templates/sd_romfs_dumper.c b/code_templates/sd_romfs_dumper.c index a2d3f86..f008a6d 100644 --- a/code_templates/sd_romfs_dumper.c +++ b/code_templates/sd_romfs_dumper.c @@ -126,6 +126,7 @@ static void read_thread_func(void *arg) { fclose(shared_data->fd); shared_data->fd = NULL; + utilsCommitSdCardFileSystemChanges(); } /* Retrieve RomFS file entry information. */ @@ -141,6 +142,13 @@ static void read_thread_func(void *arg) utilsCreateDirectoryTree(path, false); /* Create file. */ + if (file_entry->size > FAT32_FILESIZE_LIMIT && !utilsCreateConcatenationFile(path)) + { + shared_data->read_error = true; + condvarWakeAll(&g_writeCondvar); + break; + } + shared_data->read_error = ((shared_data->fd = fopen(path, "wb")) == NULL); if (shared_data->read_error) { @@ -201,10 +209,10 @@ static void read_thread_func(void *arg) { fclose(shared_data->fd); shared_data->fd = NULL; + if (shared_data->read_error || shared_data->write_error || shared_data->transfer_cancelled) utilsRemoveConcatenationFile(path); + utilsCommitSdCardFileSystemChanges(); } - if ((shared_data->read_error || shared_data->write_error || shared_data->transfer_cancelled) && *path) remove(path); - free(buf); end: @@ -579,6 +587,12 @@ int main(int argc, char *argv[]) TitleInfo *latest_patch = NULL; if (user_app_data.patch_info) latest_patch = get_latest_patch_info(user_app_data.patch_info); + if (!latest_patch && (!base_nca_ctx->fs_ctx[1].enabled || (base_nca_ctx->fs_ctx[1].section_type != NcaFsSectionType_RomFs && base_nca_ctx->fs_ctx[1].section_type != NcaFsSectionType_Nca0RomFs))) + { + consolePrint("base app has no valid romfs and no updates could be found\n"); + goto out2; + } + if (base_nca_ctx->fs_ctx[1].has_sparse_layer && (!latest_patch || latest_patch->version.value < user_app_data.app_info->version.value)) { consolePrint("base app is a sparse title and no v%u or greater update could be found\n", user_app_data.app_info->version.value); @@ -616,6 +630,20 @@ int main(int argc, char *argv[]) consolePrint("romfs initialize ctx succeeded\n"); + // check free space + u64 free_space = 0; + if (!utilsGetFileSystemStatsByPath("sdmc:/", NULL, &free_space)) + { + consolePrint("failed to retrieve free sd card space\n"); + goto out2; + } + + if (free_space <= shared_data.total_size) + { + consolePrint("extracted romfs size (0x%lX) exceeds free sd card space (0x%lX)\n", shared_data.total_size, free_space); + goto out2; + } + shared_data.fd = NULL; shared_data.data = buf; shared_data.data_size = 0; diff --git a/code_templates/usb_romfs_dumper.c b/code_templates/usb_romfs_dumper.c index 6e38860..126f360 100644 --- a/code_templates/usb_romfs_dumper.c +++ b/code_templates/usb_romfs_dumper.c @@ -552,6 +552,12 @@ int main(int argc, char *argv[]) TitleInfo *latest_patch = NULL; if (user_app_data.patch_info) latest_patch = get_latest_patch_info(user_app_data.patch_info); + if (!latest_patch && (!base_nca_ctx->fs_ctx[1].enabled || (base_nca_ctx->fs_ctx[1].section_type != NcaFsSectionType_RomFs && base_nca_ctx->fs_ctx[1].section_type != NcaFsSectionType_Nca0RomFs))) + { + consolePrint("base app has no valid romfs and no updates could be found\n"); + goto out2; + } + if (base_nca_ctx->fs_ctx[1].has_sparse_layer && (!latest_patch || latest_patch->version.value < user_app_data.app_info->version.value)) { consolePrint("base app is a sparse title and no v%u or greater update could be found\n", user_app_data.app_info->version.value); diff --git a/source/core/bktr.c b/source/core/bktr.c index 7ad6db0..f933972 100644 --- a/source/core/bktr.c +++ b/source/core/bktr.c @@ -363,11 +363,12 @@ static bool bktrReadIndirectStorage(BucketTreeVisitor *visitor, void *out, u64 r { BucketTreeContext *ctx = visitor->bktr_ctx; bool is_sparse = (ctx->storage_type == BucketTreeStorageType_Sparse); + bool missing_original_storage = !bktrIsValidSubstorage(&(ctx->substorages[0])); - if (!out || !bktrIsValidSubstorage(&(ctx->substorages[0])) || (!is_sparse && !bktrIsValidSubstorage(&(ctx->substorages[1]))) || \ - (!is_sparse && ((ctx->substorages[0].type != BucketTreeSubStorageType_Regular && ctx->substorages[0].type != BucketTreeStorageType_Compressed && \ - ctx->substorages[0].type != BucketTreeSubStorageType_Sparse) || ctx->substorages[1].type != BucketTreeSubStorageType_AesCtrEx)) || \ - (is_sparse && ctx->substorages[0].type != BucketTreeSubStorageType_Regular)) + if (!out || (is_sparse && (missing_original_storage || ctx->substorages[0].type != BucketTreeSubStorageType_Regular)) || \ + (!is_sparse && (!bktrIsValidSubstorage(&(ctx->substorages[1])) || ctx->substorages[1].type != BucketTreeSubStorageType_AesCtrEx || \ + (!missing_original_storage && ((ctx->substorages[0].type != BucketTreeSubStorageType_Regular && \ + ctx->substorages[0].type != BucketTreeStorageType_Compressed && ctx->substorages[0].type != BucketTreeSubStorageType_Sparse)))))) { LOG_MSG("Invalid parameters!"); return false; @@ -438,10 +439,15 @@ static bool bktrReadIndirectStorage(BucketTreeVisitor *visitor, void *out, u64 r if (cur_entry.storage_index == BucketTreeIndirectStorageIndex_Original) { - /* Retrieve data from the original data storage. */ - /* This may either be a Regular/Sparse/Compressed storage from the base NCA (Indirect) or a Regular storage from this very same NCA (Sparse). */ - success = bktrReadSubStorage(&(ctx->substorages[0]), ¶ms); - if (!success) LOG_MSG("Failed to read 0x%lX-byte long chunk from offset 0x%lX in original data storage!", read_size, data_offset); + if (!missing_original_storage) + { + /* Retrieve data from the original data storage. */ + /* This may either be a Regular/Sparse/Compressed storage from the base NCA (Indirect) or a Regular storage from this very same NCA (Sparse). */ + success = bktrReadSubStorage(&(ctx->substorages[0]), ¶ms); + if (!success) LOG_MSG("Failed to read 0x%lX-byte long chunk from offset 0x%lX in original data storage!", read_size, data_offset); + } else { + LOG_MSG("Error: attempting to read 0x%lX-byte long chunk from missing original data storage at offset 0x%lX!", read_size, data_offset); + } } else { if (!is_sparse) { diff --git a/source/core/romfs.c b/source/core/romfs.c index 90b28e1..fadc3fa 100644 --- a/source/core/romfs.c +++ b/source/core/romfs.c @@ -33,12 +33,17 @@ bool romfsInitializeContext(RomFileSystemContext *out, NcaFsSectionContext *base NcaContext *base_nca_ctx = NULL, *patch_nca_ctx = NULL; bool dump_fs_header = false, success = false; - if (!out || !base_nca_fs_ctx || !base_nca_fs_ctx->enabled || (base_nca_fs_ctx->has_sparse_layer && !patch_nca_fs_ctx) || !(base_nca_ctx = (NcaContext*)base_nca_fs_ctx->nca_ctx) || \ - (base_nca_ctx->format_version == NcaVersion_Nca0 && (base_nca_fs_ctx->section_type != NcaFsSectionType_Nca0RomFs || \ - base_nca_fs_ctx->hash_type != NcaHashType_HierarchicalSha256)) || (base_nca_ctx->format_version != NcaVersion_Nca0 && \ - (base_nca_fs_ctx->section_type != NcaFsSectionType_RomFs || (base_nca_fs_ctx->hash_type != NcaHashType_HierarchicalIntegrity && \ - base_nca_fs_ctx->hash_type != NcaHashType_HierarchicalIntegritySha3))) || (base_nca_ctx->rights_id_available && !base_nca_ctx->titlekey_retrieved) || \ - (patch_nca_fs_ctx && (!patch_nca_fs_ctx->enabled || !(patch_nca_ctx = (NcaContext*)patch_nca_fs_ctx->nca_ctx) || patch_nca_ctx->format_version != base_nca_ctx->format_version || \ + /* Check if the base RomFS is missing (e.g. Fortnite, World of Tanks Blitz, etc.). */ + bool missing_base_romfs = (base_nca_fs_ctx && (!base_nca_fs_ctx->enabled || (base_nca_fs_ctx->section_type != NcaFsSectionType_RomFs && \ + base_nca_fs_ctx->section_type != NcaFsSectionType_Nca0RomFs))); + + if (!out || !base_nca_fs_ctx || (!patch_nca_fs_ctx && (missing_base_romfs || base_nca_fs_ctx->has_sparse_layer)) || \ + (!missing_base_romfs && (!(base_nca_ctx = (NcaContext*)base_nca_fs_ctx->nca_ctx) || (base_nca_ctx->format_version == NcaVersion_Nca0 && \ + (base_nca_fs_ctx->section_type != NcaFsSectionType_Nca0RomFs || base_nca_fs_ctx->hash_type != NcaHashType_HierarchicalSha256)) || \ + (base_nca_ctx->format_version != NcaVersion_Nca0 && (base_nca_fs_ctx->section_type != NcaFsSectionType_RomFs || \ + (base_nca_fs_ctx->hash_type != NcaHashType_HierarchicalIntegrity && base_nca_fs_ctx->hash_type != NcaHashType_HierarchicalIntegritySha3))) || \ + (base_nca_ctx->rights_id_available && !base_nca_ctx->titlekey_retrieved))) || (patch_nca_fs_ctx && (!patch_nca_fs_ctx->enabled || \ + !(patch_nca_ctx = (NcaContext*)patch_nca_fs_ctx->nca_ctx) || (!missing_base_romfs && patch_nca_ctx->format_version != base_nca_ctx->format_version) || \ patch_nca_fs_ctx->section_type != NcaFsSectionType_PatchRomFs || (patch_nca_ctx->rights_id_available && !patch_nca_ctx->titlekey_retrieved)))) { LOG_MSG("Invalid parameters!"); @@ -52,7 +57,7 @@ bool romfsInitializeContext(RomFileSystemContext *out, NcaFsSectionContext *base bool is_nca0_romfs = (base_nca_fs_ctx->section_type == NcaFsSectionType_Nca0RomFs); /* Initialize base NCA storage context. */ - if (!ncaStorageInitializeContext(base_storage_ctx, base_nca_fs_ctx)) + if (!missing_base_romfs && !ncaStorageInitializeContext(base_storage_ctx, base_nca_fs_ctx)) { LOG_MSG("Failed to initialize base NCA storage context!"); goto end; @@ -67,8 +72,8 @@ bool romfsInitializeContext(RomFileSystemContext *out, NcaFsSectionContext *base goto end; } - /* Set patch NCA storage original substorage. */ - if (!ncaStorageSetPatchOriginalSubStorage(patch_storage_ctx, base_storage_ctx)) + /* Set patch NCA storage original substorage, if available. */ + if (!missing_base_romfs && !ncaStorageSetPatchOriginalSubStorage(patch_storage_ctx, base_storage_ctx)) { LOG_MSG("Failed to set patch NCA storage context's original substorage!"); goto end;