1
0
Fork 0
mirror of https://github.com/DarkMatterCore/nxdumptool.git synced 2024-09-18 21:13:25 +01:00

nca_storage: set original substorage while initializing a patch NCA storage.

ncaStorageInitializeContext() now takes an extra input argument. ncaStorageSetPatchOriginalSubStorage() is now a static function that's only used internally.

Fixes issues with "AKIBA'S TRIP: Undead & Undressed Director's Cut" compressed storage initialization while using an update. Big thanks to @Arch9SK7 for reporting the issue.
This commit is contained in:
Pablo Curiel 2023-09-24 18:41:39 +02:00
parent 0dfdd81422
commit c474435ea8
4 changed files with 60 additions and 70 deletions

View file

@ -49,12 +49,11 @@ typedef struct {
BucketTreeContext *compressed_storage; ///< Compressed storage context.
} NcaStorageContext;
/// Initializes a NCA storage context using a NCA FS section context.
bool ncaStorageInitializeContext(NcaStorageContext *out, NcaFsSectionContext *nca_fs_ctx);
/// Sets a storage from the provided Base NcaStorageContext as the original substorage for the provided Patch NcaStorageContext's Indirect Storage.
/// Needed to perform combined reads between a base NCA and a patch NCA.
bool ncaStorageSetPatchOriginalSubStorage(NcaStorageContext *patch_ctx, NcaStorageContext *base_ctx);
/// Initializes a NCA storage context using a NCA FS section context, optionally providing a pointer to a base NcaStorageContext.
/// 'base_ctx' must be provided if dealing with a patch NCA. One of its storages will be set as the original substorage for the initialized NcaStorageContext's Indirect Storage.
/// This is needed to perform combined reads between a base NCA and a patch NCA.
/// 'base_ctx' shall be NULL if dealing with a base NCA.
bool ncaStorageInitializeContext(NcaStorageContext *out, NcaFsSectionContext *nca_fs_ctx, NcaStorageContext *base_ctx);
/// Retrieves the underlying NCA FS section's hierarchical hash target layer extents. Virtual extents may be returned, depending on the base storage type.
/// Output offset is relative to the start of the NCA FS section.

View file

@ -26,11 +26,12 @@
static bool ncaStorageInitializeBucketTreeContext(BucketTreeContext **out, NcaFsSectionContext *nca_fs_ctx, u8 storage_type);
static bool ncaStorageInitializeCompressedStorageBucketTreeContext(NcaStorageContext *out, NcaFsSectionContext *nca_fs_ctx);
static bool ncaStorageSetPatchOriginalSubStorage(NcaStorageContext *patch_ctx, NcaFsSectionContext *patch_nca_fs_ctx, NcaStorageContext *base_ctx);
bool ncaStorageInitializeContext(NcaStorageContext *out, NcaFsSectionContext *nca_fs_ctx)
bool ncaStorageInitializeContext(NcaStorageContext *out, NcaFsSectionContext *nca_fs_ctx, NcaStorageContext *base_ctx)
{
if (!out || !nca_fs_ctx || !nca_fs_ctx->enabled || (nca_fs_ctx->section_type == NcaFsSectionType_PatchRomFs && \
(!nca_fs_ctx->has_patch_indirect_layer || !nca_fs_ctx->has_patch_aes_ctr_ex_layer || nca_fs_ctx->has_sparse_layer)))
(!nca_fs_ctx->has_patch_indirect_layer || !nca_fs_ctx->has_patch_aes_ctr_ex_layer || nca_fs_ctx->has_sparse_layer || !base_ctx)))
{
LOG_MSG_ERROR("Invalid parameters!");
return false;
@ -64,11 +65,11 @@ bool ncaStorageInitializeContext(NcaStorageContext *out, NcaFsSectionContext *nc
if (!ncaStorageInitializeBucketTreeContext(&(out->aes_ctr_ex_storage), nca_fs_ctx, BucketTreeStorageType_AesCtrEx) || \
!ncaStorageInitializeBucketTreeContext(&(out->indirect_storage), nca_fs_ctx, BucketTreeStorageType_Indirect)) goto end;
/* Set AesCtrEx layer's substorage. */
/* Set AesCtrEx layer's substorage (plain NCA reads). */
if (!bktrSetRegularSubStorage(out->aes_ctr_ex_storage, nca_fs_ctx)) goto end;
/* Set Indirect layer's AesCtrEx substorage. */
/* Original substorage (index 0) must be manually set at a later time using ncaStorageSetPatchOriginalSubStorage(). */
/* Set Indirect layer's substorages (Base + AesCtrEx). */
if (!ncaStorageSetPatchOriginalSubStorage(out, nca_fs_ctx, base_ctx)) goto end;
if (!bktrSetBucketTreeSubStorage(out->indirect_storage, out->aes_ctr_ex_storage, 1)) goto end;
/* Update base storage type. */
@ -90,55 +91,6 @@ end:
return success;
}
bool ncaStorageSetPatchOriginalSubStorage(NcaStorageContext *patch_ctx, NcaStorageContext *base_ctx)
{
NcaContext *patch_nca_ctx = NULL, *base_nca_ctx = NULL;
if (!ncaStorageIsValidContext(patch_ctx) || !ncaStorageIsValidContext(base_ctx) || patch_ctx->nca_fs_ctx == base_ctx->nca_fs_ctx || \
!(patch_nca_ctx = patch_ctx->nca_fs_ctx->nca_ctx) || !(base_nca_ctx = base_ctx->nca_fs_ctx->nca_ctx) || \
patch_ctx->nca_fs_ctx->section_type != NcaFsSectionType_PatchRomFs || base_ctx->nca_fs_ctx->section_type != NcaFsSectionType_RomFs || \
patch_nca_ctx->header.program_id != base_nca_ctx->header.program_id || patch_nca_ctx->header.content_type != base_nca_ctx->header.content_type || \
patch_nca_ctx->id_offset != base_nca_ctx->id_offset || patch_nca_ctx->title_version.value < base_nca_ctx->title_version.value || \
(patch_ctx->base_storage_type != NcaStorageBaseStorageType_Indirect && patch_ctx->base_storage_type != NcaStorageBaseStorageType_Compressed) || \
!patch_ctx->indirect_storage || !patch_ctx->aes_ctr_ex_storage || (base_ctx->base_storage_type == NcaStorageBaseStorageType_Compressed && \
patch_ctx->base_storage_type != NcaStorageBaseStorageType_Compressed))
{
LOG_MSG_ERROR("Invalid parameters!");
return false;
}
bool success = false;
/* Set original substorage. */
switch(base_ctx->base_storage_type)
{
case NcaStorageBaseStorageType_Regular:
case NcaStorageBaseStorageType_Compressed:
/* Regular: we just make the Patch's Indirect Storage's SubStorage #0 point to the Base NCA FS section as-is and call it a day. */
/* Compressed: if a Compressed Storage is available in the Base NCA FS section, the corresponding Patch NCA FS section *must* also have one. */
/* This is because the Patch's Compressed Storage also takes care of LZ4-compressed chunks within Base NCA FS section areas. */
/* Furthermore, the Patch's Indirect Storage already provides section-relative physical offsets for the Base NCA FS section. */
/* In other words, we don't need to parse the Base NCA's Compressed Storage on every read. */
success = bktrSetRegularSubStorage(patch_ctx->indirect_storage, base_ctx->nca_fs_ctx);
break;
case NcaStorageBaseStorageType_Sparse:
/* Sparse: we should *always* arrive here if a Sparse Storage is available in the Base NCA FS section, regardless if a Compressed Storage is available or not. */
/* This is because compression bucket trees are non-existent in Base NCA FS sections that have both Sparse and Compressed Storages. */
/* Furthermore, in these cases, the compression BucketInfo from the NCA FS section header references the full, patched FS section, so we can't really use it. */
/* We just completely ignore the Base's Compressed Storage and let the Patch's Compressed Storage take care of LZ4-compressed chunks. */
/* Anyway, we just make the Patch's Indirect Storage's SubStorage #0 point to the Base's Sparse Storage and call it a day. */
success = bktrSetBucketTreeSubStorage(patch_ctx->indirect_storage, base_ctx->sparse_storage, 0);
break;
default:
break;
}
if (!success) LOG_MSG_ERROR("Failed to set base storage to patch storage! (0x%02X, 0x%02X).", base_ctx->base_storage_type, patch_ctx->base_storage_type);
return success;
}
bool ncaStorageGetHashTargetExtents(NcaStorageContext *ctx, u64 *out_offset, u64 *out_size)
{
if (!ncaStorageIsValidContext(ctx) || (!out_offset && !out_size))
@ -370,3 +322,49 @@ end:
return success;
}
static bool ncaStorageSetPatchOriginalSubStorage(NcaStorageContext *patch_ctx, NcaFsSectionContext *patch_nca_fs_ctx, NcaStorageContext *base_ctx)
{
NcaContext *patch_nca_ctx = NULL, *base_nca_ctx = NULL;
if (!patch_ctx || !patch_ctx->indirect_storage || !patch_ctx->aes_ctr_ex_storage || !patch_nca_fs_ctx || !ncaStorageIsValidContext(base_ctx) || \
!(patch_nca_ctx = patch_nca_fs_ctx->nca_ctx) || !(base_nca_ctx = base_ctx->nca_fs_ctx->nca_ctx) || \
patch_nca_fs_ctx->section_type != NcaFsSectionType_PatchRomFs || base_ctx->nca_fs_ctx->section_type != NcaFsSectionType_RomFs || \
patch_nca_ctx->header.program_id != base_nca_ctx->header.program_id || patch_nca_ctx->header.content_type != base_nca_ctx->header.content_type || \
patch_nca_ctx->id_offset != base_nca_ctx->id_offset || patch_nca_ctx->title_version.value < base_nca_ctx->title_version.value)
{
LOG_MSG_ERROR("Invalid parameters!");
return false;
}
bool success = false;
/* Set original substorage. */
switch(base_ctx->base_storage_type)
{
case NcaStorageBaseStorageType_Regular:
case NcaStorageBaseStorageType_Compressed:
/* Regular: we just make the Patch's Indirect Storage's SubStorage #0 point to the Base NCA FS section as-is and call it a day. */
/* Compressed: if a Compressed Storage is available in the Base NCA FS section, the corresponding Patch NCA FS section *must* also have one. */
/* This is because the Patch's Compressed Storage also takes care of LZ4-compressed chunks within Base NCA FS section areas. */
/* Furthermore, the Patch's Indirect Storage already provides section-relative physical offsets for the Base NCA FS section. */
/* In other words, we don't need to parse the Base NCA's Compressed Storage on every read. */
success = bktrSetRegularSubStorage(patch_ctx->indirect_storage, base_ctx->nca_fs_ctx);
break;
case NcaStorageBaseStorageType_Sparse:
/* Sparse: we should *always* arrive here if a Sparse Storage is available in the Base NCA FS section, regardless if a Compressed Storage is available or not. */
/* This is because compression bucket trees are non-existent in Base NCA FS sections that have both Sparse and Compressed Storages. */
/* Furthermore, in these cases, the compression BucketInfo from the NCA FS section header references the full, patched FS section, so we can't really use it. */
/* We just completely ignore the Base's Compressed Storage and let the Patch's Compressed Storage take care of LZ4-compressed chunks. */
/* Anyway, we just make the Patch's Indirect Storage's SubStorage #0 point to the Base's Sparse Storage and call it a day. */
success = bktrSetBucketTreeSubStorage(patch_ctx->indirect_storage, base_ctx->sparse_storage, 0);
break;
default:
break;
}
if (!success) LOG_MSG_ERROR("Failed to set base storage to patch storage! (0x%02X, 0x%02X).", base_ctx->base_storage_type, patch_ctx->base_storage_type);
return success;
}

View file

@ -47,7 +47,7 @@ bool pfsInitializeContext(PartitionFileSystemContext *out, NcaFsSectionContext *
/* Initialize NCA storage context. */
NcaStorageContext *storage_ctx = &(out->storage_ctx);
if (!ncaStorageInitializeContext(storage_ctx, nca_fs_ctx))
if (!ncaStorageInitializeContext(storage_ctx, nca_fs_ctx, NULL))
{
LOG_MSG_ERROR("Failed to initialize NCA storage context!");
goto end;

View file

@ -57,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 (!missing_base_romfs && !ncaStorageInitializeContext(base_storage_ctx, base_nca_fs_ctx))
if (!missing_base_romfs && !ncaStorageInitializeContext(base_storage_ctx, base_nca_fs_ctx, NULL))
{
LOG_MSG_ERROR("Failed to initialize base NCA storage context!");
goto end;
@ -66,19 +66,12 @@ bool romfsInitializeContext(RomFileSystemContext *out, NcaFsSectionContext *base
if (patch_nca_fs_ctx)
{
/* Initialize base NCA storage context. */
if (!ncaStorageInitializeContext(patch_storage_ctx, patch_nca_fs_ctx))
if (!ncaStorageInitializeContext(patch_storage_ctx, patch_nca_fs_ctx, base_storage_ctx))
{
LOG_MSG_ERROR("Failed to initialize patch NCA storage context!");
goto end;
}
/* Set patch NCA storage original substorage, if available. */
if (!missing_base_romfs && !ncaStorageSetPatchOriginalSubStorage(patch_storage_ctx, base_storage_ctx))
{
LOG_MSG_ERROR("Failed to set patch NCA storage context's original substorage!");
goto end;
}
/* Set default NCA FS storage context. */
out->is_patch = true;
out->default_storage_ctx = patch_storage_ctx;