mirror of
https://github.com/DarkMatterCore/nxdumptool.git
synced 2024-11-22 18:26:39 +00: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:
parent
0dfdd81422
commit
c474435ea8
4 changed files with 60 additions and 70 deletions
|
@ -49,12 +49,11 @@ typedef struct {
|
||||||
BucketTreeContext *compressed_storage; ///< Compressed storage context.
|
BucketTreeContext *compressed_storage; ///< Compressed storage context.
|
||||||
} NcaStorageContext;
|
} NcaStorageContext;
|
||||||
|
|
||||||
/// Initializes a NCA storage context using a NCA FS section context.
|
/// Initializes a NCA storage context using a NCA FS section context, optionally providing a pointer to a base NcaStorageContext.
|
||||||
bool ncaStorageInitializeContext(NcaStorageContext *out, NcaFsSectionContext *nca_fs_ctx);
|
/// '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.
|
||||||
/// Sets a storage from the provided Base NcaStorageContext as the original substorage for the provided Patch NcaStorageContext's Indirect Storage.
|
/// 'base_ctx' shall be NULL if dealing with a base NCA.
|
||||||
/// Needed to perform combined reads between a base NCA and a patch NCA.
|
bool ncaStorageInitializeContext(NcaStorageContext *out, NcaFsSectionContext *nca_fs_ctx, NcaStorageContext *base_ctx);
|
||||||
bool ncaStorageSetPatchOriginalSubStorage(NcaStorageContext *patch_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.
|
/// 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.
|
/// Output offset is relative to the start of the NCA FS section.
|
||||||
|
|
|
@ -26,11 +26,12 @@
|
||||||
|
|
||||||
static bool ncaStorageInitializeBucketTreeContext(BucketTreeContext **out, NcaFsSectionContext *nca_fs_ctx, u8 storage_type);
|
static bool ncaStorageInitializeBucketTreeContext(BucketTreeContext **out, NcaFsSectionContext *nca_fs_ctx, u8 storage_type);
|
||||||
static bool ncaStorageInitializeCompressedStorageBucketTreeContext(NcaStorageContext *out, NcaFsSectionContext *nca_fs_ctx);
|
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 && \
|
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!");
|
LOG_MSG_ERROR("Invalid parameters!");
|
||||||
return false;
|
return false;
|
||||||
|
@ -64,11 +65,11 @@ bool ncaStorageInitializeContext(NcaStorageContext *out, NcaFsSectionContext *nc
|
||||||
if (!ncaStorageInitializeBucketTreeContext(&(out->aes_ctr_ex_storage), nca_fs_ctx, BucketTreeStorageType_AesCtrEx) || \
|
if (!ncaStorageInitializeBucketTreeContext(&(out->aes_ctr_ex_storage), nca_fs_ctx, BucketTreeStorageType_AesCtrEx) || \
|
||||||
!ncaStorageInitializeBucketTreeContext(&(out->indirect_storage), nca_fs_ctx, BucketTreeStorageType_Indirect)) goto end;
|
!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;
|
if (!bktrSetRegularSubStorage(out->aes_ctr_ex_storage, nca_fs_ctx)) goto end;
|
||||||
|
|
||||||
/* Set Indirect layer's AesCtrEx substorage. */
|
/* Set Indirect layer's substorages (Base + AesCtrEx). */
|
||||||
/* Original substorage (index 0) must be manually set at a later time using ncaStorageSetPatchOriginalSubStorage(). */
|
if (!ncaStorageSetPatchOriginalSubStorage(out, nca_fs_ctx, base_ctx)) goto end;
|
||||||
if (!bktrSetBucketTreeSubStorage(out->indirect_storage, out->aes_ctr_ex_storage, 1)) goto end;
|
if (!bktrSetBucketTreeSubStorage(out->indirect_storage, out->aes_ctr_ex_storage, 1)) goto end;
|
||||||
|
|
||||||
/* Update base storage type. */
|
/* Update base storage type. */
|
||||||
|
@ -90,55 +91,6 @@ end:
|
||||||
return success;
|
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)
|
bool ncaStorageGetHashTargetExtents(NcaStorageContext *ctx, u64 *out_offset, u64 *out_size)
|
||||||
{
|
{
|
||||||
if (!ncaStorageIsValidContext(ctx) || (!out_offset && !out_size))
|
if (!ncaStorageIsValidContext(ctx) || (!out_offset && !out_size))
|
||||||
|
@ -370,3 +322,49 @@ end:
|
||||||
|
|
||||||
return success;
|
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;
|
||||||
|
}
|
||||||
|
|
|
@ -47,7 +47,7 @@ bool pfsInitializeContext(PartitionFileSystemContext *out, NcaFsSectionContext *
|
||||||
|
|
||||||
/* Initialize NCA storage context. */
|
/* Initialize NCA storage context. */
|
||||||
NcaStorageContext *storage_ctx = &(out->storage_ctx);
|
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!");
|
LOG_MSG_ERROR("Failed to initialize NCA storage context!");
|
||||||
goto end;
|
goto end;
|
||||||
|
|
|
@ -57,7 +57,7 @@ bool romfsInitializeContext(RomFileSystemContext *out, NcaFsSectionContext *base
|
||||||
bool is_nca0_romfs = (base_nca_fs_ctx->section_type == NcaFsSectionType_Nca0RomFs);
|
bool is_nca0_romfs = (base_nca_fs_ctx->section_type == NcaFsSectionType_Nca0RomFs);
|
||||||
|
|
||||||
/* Initialize base NCA storage context. */
|
/* 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!");
|
LOG_MSG_ERROR("Failed to initialize base NCA storage context!");
|
||||||
goto end;
|
goto end;
|
||||||
|
@ -66,19 +66,12 @@ bool romfsInitializeContext(RomFileSystemContext *out, NcaFsSectionContext *base
|
||||||
if (patch_nca_fs_ctx)
|
if (patch_nca_fs_ctx)
|
||||||
{
|
{
|
||||||
/* Initialize base NCA storage context. */
|
/* 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!");
|
LOG_MSG_ERROR("Failed to initialize patch NCA storage context!");
|
||||||
goto end;
|
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. */
|
/* Set default NCA FS storage context. */
|
||||||
out->is_patch = true;
|
out->is_patch = true;
|
||||||
out->default_storage_ctx = patch_storage_ctx;
|
out->default_storage_ctx = patch_storage_ctx;
|
||||||
|
|
Loading…
Reference in a new issue