mirror of
https://github.com/DarkMatterCore/nxdumptool.git
synced 2024-11-22 18:26:39 +00:00
nca: parse CompressionInfo struct.
This commit is contained in:
parent
8d81528619
commit
e372b97131
3 changed files with 94 additions and 49 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -18,4 +18,4 @@ host/nxdumptool
|
||||||
*.exe
|
*.exe
|
||||||
main.cpp
|
main.cpp
|
||||||
/code_templates/tmp/*
|
/code_templates/tmp/*
|
||||||
/nmh3
|
/galgun
|
||||||
|
|
|
@ -389,6 +389,15 @@ typedef struct {
|
||||||
Aes128CtrContext sparse_ctr_ctx; ///< AES-128-CTR context used for sparse table decryption.
|
Aes128CtrContext sparse_ctr_ctx; ///< AES-128-CTR context used for sparse table decryption.
|
||||||
u64 cur_sparse_virtual_offset; ///< Current sparse layer virtual offset. Used for content decryption if a sparse layer is available.
|
u64 cur_sparse_virtual_offset; ///< Current sparse layer virtual offset. Used for content decryption if a sparse layer is available.
|
||||||
|
|
||||||
|
///< CompressionInfo-related fields.
|
||||||
|
bool has_compression_layer; ///< Set to true if this NCA FS section has a compression layer.
|
||||||
|
u64 compression_table_offset; ///< section_offset + hash_target_offset + header.compression_info.bucket.offset. Relative to the start of the NCA content file. Placed here for convenience.
|
||||||
|
u64 compression_table_size; ///< header.compression_info.bucket.size. Placed here for convenience.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
///< NSP-related fields.
|
///< NSP-related fields.
|
||||||
bool header_written; ///< Set to true after this FS section header has been written to an output dump.
|
bool header_written; ///< Set to true after this FS section header has been written to an output dump.
|
||||||
} NcaFsSectionContext;
|
} NcaFsSectionContext;
|
||||||
|
@ -481,6 +490,11 @@ bool ncaInitializeContext(NcaContext *out, u8 storage_id, u8 hfs_partition_type,
|
||||||
/// Input offset must be relative to the start of the NCA content file.
|
/// Input offset must be relative to the start of the NCA content file.
|
||||||
bool ncaReadContentFile(NcaContext *ctx, void *out, u64 read_size, u64 offset);
|
bool ncaReadContentFile(NcaContext *ctx, void *out, u64 read_size, u64 offset);
|
||||||
|
|
||||||
|
/// Retrieves the offset and/or size from the FS section hierarchical hash target layer.
|
||||||
|
/// Output offset is relative to the start of the FS section.
|
||||||
|
/// Either 'out_offset' or 'out_size' can be NULL, but at least one of them must be a valid pointer.
|
||||||
|
bool ncaGetFsSectionHashTargetProperties(NcaFsSectionContext *ctx, u64 *out_offset, u64 *out_size);
|
||||||
|
|
||||||
/// Reads decrypted data from a NCA FS section using an input context.
|
/// Reads decrypted data from a NCA FS section using an input context.
|
||||||
/// Input offset must be relative to the start of the NCA FS section.
|
/// Input offset must be relative to the start of the NCA FS section.
|
||||||
/// If dealing with Patch RomFS sections, this function should only be used when *not* reading BKTR AesCtrEx storage data. Use ncaReadAesCtrExStorageFromBktrSection() for that.
|
/// If dealing with Patch RomFS sections, this function should only be used when *not* reading BKTR AesCtrEx storage data. Use ncaReadAesCtrExStorageFromBktrSection() for that.
|
||||||
|
@ -546,43 +560,6 @@ NX_INLINE bool ncaIsHeaderDirty(NcaContext *ctx)
|
||||||
return (memcmp(tmp_hash, ctx->header_hash, SHA256_HASH_SIZE) != 0);
|
return (memcmp(tmp_hash, ctx->header_hash, SHA256_HASH_SIZE) != 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
NX_INLINE bool ncaGetFsSectionHashTargetProperties(NcaFsSectionContext *ctx, u64 *out_offset, u64 *out_size)
|
|
||||||
{
|
|
||||||
if (!ctx || (!out_offset && !out_size)) return false;
|
|
||||||
|
|
||||||
bool success = true;
|
|
||||||
|
|
||||||
switch(ctx->hash_type)
|
|
||||||
{
|
|
||||||
case NcaHashType_None:
|
|
||||||
if (out_offset) *out_offset = 0;
|
|
||||||
if (out_size) *out_size = ctx->section_size;
|
|
||||||
break;
|
|
||||||
case NcaHashType_HierarchicalSha256:
|
|
||||||
case NcaHashType_HierarchicalSha3256:
|
|
||||||
{
|
|
||||||
u32 layer_count = ctx->header.hash_data.hierarchical_sha256_data.hash_region_count;
|
|
||||||
NcaRegion *hash_region = &(ctx->header.hash_data.hierarchical_sha256_data.hash_region[layer_count - 1]);
|
|
||||||
if (out_offset) *out_offset = hash_region->offset;
|
|
||||||
if (out_size) *out_size = hash_region->size;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case NcaHashType_HierarchicalIntegrity:
|
|
||||||
case NcaHashType_HierarchicalIntegritySha3:
|
|
||||||
{
|
|
||||||
NcaHierarchicalIntegrityVerificationLevelInformation *lvl_info = &(ctx->header.hash_data.integrity_meta_info.info_level_hash.level_information[NCA_IVFC_LEVEL_COUNT - 1]);
|
|
||||||
if (out_offset) *out_offset = lvl_info->offset;
|
|
||||||
if (out_size) *out_size = lvl_info->size;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
success = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return success;
|
|
||||||
}
|
|
||||||
|
|
||||||
NX_INLINE void ncaFreeHierarchicalSha256Patch(NcaHierarchicalSha256Patch *patch)
|
NX_INLINE void ncaFreeHierarchicalSha256Patch(NcaHierarchicalSha256Patch *patch)
|
||||||
{
|
{
|
||||||
if (!patch) return;
|
if (!patch) return;
|
||||||
|
|
|
@ -209,6 +209,47 @@ bool ncaReadContentFile(NcaContext *ctx, void *out, u64 read_size, u64 offset)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool ncaGetFsSectionHashTargetProperties(NcaFsSectionContext *ctx, u64 *out_offset, u64 *out_size)
|
||||||
|
{
|
||||||
|
if (!ctx || (!out_offset && !out_size))
|
||||||
|
{
|
||||||
|
LOG_MSG("Invalid parameters!");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool success = true;
|
||||||
|
|
||||||
|
switch(ctx->hash_type)
|
||||||
|
{
|
||||||
|
case NcaHashType_None:
|
||||||
|
if (out_offset) *out_offset = 0;
|
||||||
|
if (out_size) *out_size = ctx->section_size;
|
||||||
|
break;
|
||||||
|
case NcaHashType_HierarchicalSha256:
|
||||||
|
case NcaHashType_HierarchicalSha3256:
|
||||||
|
{
|
||||||
|
u32 layer_count = ctx->header.hash_data.hierarchical_sha256_data.hash_region_count;
|
||||||
|
NcaRegion *hash_region = &(ctx->header.hash_data.hierarchical_sha256_data.hash_region[layer_count - 1]);
|
||||||
|
if (out_offset) *out_offset = hash_region->offset;
|
||||||
|
if (out_size) *out_size = hash_region->size;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case NcaHashType_HierarchicalIntegrity:
|
||||||
|
case NcaHashType_HierarchicalIntegritySha3:
|
||||||
|
{
|
||||||
|
NcaHierarchicalIntegrityVerificationLevelInformation *lvl_info = &(ctx->header.hash_data.integrity_meta_info.info_level_hash.level_information[NCA_IVFC_LEVEL_COUNT - 1]);
|
||||||
|
if (out_offset) *out_offset = lvl_info->offset;
|
||||||
|
if (out_size) *out_size = lvl_info->size;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
success = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
bool ncaReadFsSection(NcaFsSectionContext *ctx, void *out, u64 read_size, u64 offset)
|
bool ncaReadFsSection(NcaFsSectionContext *ctx, void *out, u64 read_size, u64 offset)
|
||||||
{
|
{
|
||||||
bool ret = false;
|
bool ret = false;
|
||||||
|
@ -700,14 +741,18 @@ static bool ncaInitializeFsSectionContext(NcaContext *nca_ctx, u32 section_idx)
|
||||||
NcaSparseInfo *sparse_info = &(fs_ctx->header.sparse_info);
|
NcaSparseInfo *sparse_info = &(fs_ctx->header.sparse_info);
|
||||||
NcaBucketInfo *sparse_bucket = &(sparse_info->bucket);
|
NcaBucketInfo *sparse_bucket = &(sparse_info->bucket);
|
||||||
|
|
||||||
|
NcaBucketInfo *compression_bucket = &(fs_ctx->header.compression_info.bucket);
|
||||||
|
|
||||||
bool success = false;
|
bool success = false;
|
||||||
|
|
||||||
/* Fill section context. */
|
/* Fill section context. */
|
||||||
|
fs_ctx->enabled = false;
|
||||||
fs_ctx->nca_ctx = nca_ctx;
|
fs_ctx->nca_ctx = nca_ctx;
|
||||||
fs_ctx->section_idx = section_idx;
|
fs_ctx->section_idx = section_idx;
|
||||||
fs_ctx->section_type = NcaFsSectionType_Invalid; /* Placeholder. */
|
fs_ctx->section_type = NcaFsSectionType_Invalid; /* Placeholder. */
|
||||||
fs_ctx->has_sparse_layer = (sparse_info->generation != 0);
|
fs_ctx->has_sparse_layer = (sparse_info->generation != 0);
|
||||||
fs_ctx->enabled = false;
|
fs_ctx->has_compression_layer = (compression_bucket->offset != 0 && compression_bucket->size != 0);
|
||||||
|
fs_ctx->cur_sparse_virtual_offset = 0;
|
||||||
|
|
||||||
/* Don't proceed if this NCA FS section isn't populated. */
|
/* Don't proceed if this NCA FS section isn't populated. */
|
||||||
if (!ncaIsFsInfoEntryValid(fs_info))
|
if (!ncaIsFsInfoEntryValid(fs_info))
|
||||||
|
@ -784,7 +829,7 @@ static bool ncaInitializeFsSectionContext(NcaContext *nca_ctx, u32 section_idx)
|
||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Check if we're dealing with a sparse storage. */
|
/* Check if we're dealing with a sparse layer. */
|
||||||
if (fs_ctx->has_sparse_layer)
|
if (fs_ctx->has_sparse_layer)
|
||||||
{
|
{
|
||||||
/* Check if the sparse bucket is valid. */
|
/* Check if the sparse bucket is valid. */
|
||||||
|
@ -792,7 +837,7 @@ static bool ncaInitializeFsSectionContext(NcaContext *nca_ctx, u32 section_idx)
|
||||||
u64 raw_storage_size = (sparse_bucket->offset + sparse_bucket->size);
|
u64 raw_storage_size = (sparse_bucket->offset + sparse_bucket->size);
|
||||||
|
|
||||||
if (__builtin_bswap32(sparse_bucket->header.magic) != NCA_BKTR_MAGIC || sparse_bucket->header.version != NCA_BKTR_VERSION || raw_storage_offset < sizeof(NcaHeader) || \
|
if (__builtin_bswap32(sparse_bucket->header.magic) != NCA_BKTR_MAGIC || sparse_bucket->header.version != NCA_BKTR_VERSION || raw_storage_offset < sizeof(NcaHeader) || \
|
||||||
((raw_storage_offset + raw_storage_size) > nca_ctx->content_size))
|
(raw_storage_offset + raw_storage_size) > nca_ctx->content_size)
|
||||||
{
|
{
|
||||||
LOG_DATA(sparse_info, sizeof(NcaSparseInfo), "Invalid SparseInfo data for FS section #%u in \"%s\" (0x%lX). Skipping FS section. SparseInfo dump:", section_idx, \
|
LOG_DATA(sparse_info, sizeof(NcaSparseInfo), "Invalid SparseInfo data for FS section #%u in \"%s\" (0x%lX). Skipping FS section. SparseInfo dump:", section_idx, \
|
||||||
nca_ctx->content_id_str, nca_ctx->content_size);
|
nca_ctx->content_id_str, nca_ctx->content_size);
|
||||||
|
@ -811,16 +856,39 @@ static bool ncaInitializeFsSectionContext(NcaContext *nca_ctx, u32 section_idx)
|
||||||
fs_ctx->sparse_table_offset = (sparse_info->physical_offset + sparse_bucket->offset);
|
fs_ctx->sparse_table_offset = (sparse_info->physical_offset + sparse_bucket->offset);
|
||||||
fs_ctx->sparse_table_size = sparse_bucket->size;
|
fs_ctx->sparse_table_size = sparse_bucket->size;
|
||||||
|
|
||||||
/* Check if we're within boundaries. */
|
/* Update section size. */
|
||||||
if ((fs_ctx->sparse_table_offset + fs_ctx->sparse_table_size) > nca_ctx->content_size)
|
fs_ctx->section_size = raw_storage_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check if we're dealing with a compression layer. */
|
||||||
|
if (fs_ctx->has_compression_layer)
|
||||||
{
|
{
|
||||||
LOG_DATA(sparse_info, sizeof(NcaSparseInfo), "SparseInfo table for FS section #%u in \"%s\" is out of NCA boundaries (0x%lX). Skipping FS section. SparseInfo dump:", \
|
u64 raw_storage_offset = 0;
|
||||||
section_idx, nca_ctx->content_id_str, nca_ctx->content_size);
|
u64 raw_storage_size = compression_bucket->size;
|
||||||
|
|
||||||
|
/* Get target hash layer offset. */
|
||||||
|
if (!ncaGetFsSectionHashTargetProperties(fs_ctx, &raw_storage_offset, NULL))
|
||||||
|
{
|
||||||
|
LOG_MSG("Invalid hash type for FS section #%u in \"%s\" (0x%02X). Skipping FS section.", fs_ctx->section_idx, nca_ctx->content_id_str, fs_ctx->hash_type);
|
||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Update section size. */
|
/* Update compression layer offset. */
|
||||||
fs_ctx->section_size = raw_storage_size;
|
raw_storage_offset += compression_bucket->offset;
|
||||||
|
|
||||||
|
/* Check if the compression bucket is valid. */
|
||||||
|
if (__builtin_bswap32(compression_bucket->header.magic) != NCA_BKTR_MAGIC || compression_bucket->header.version != NCA_BKTR_VERSION || !sparse_bucket->header.entry_count || \
|
||||||
|
raw_storage_offset < sizeof(NcaHeader) || (raw_storage_offset + raw_storage_size) > fs_ctx->section_size || \
|
||||||
|
(fs_ctx->section_offset + raw_storage_offset + raw_storage_size) > nca_ctx->content_size)
|
||||||
|
{
|
||||||
|
LOG_DATA(sparse_info, sizeof(NcaSparseInfo), "Invalid CompressionInfo data for FS section #%u in \"%s\" (0x%lX). Skipping FS section. CompressionInfo dump:", section_idx, \
|
||||||
|
nca_ctx->content_id_str, nca_ctx->content_size);
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set sparse table properties. */
|
||||||
|
fs_ctx->compression_table_offset = (fs_ctx->section_offset + raw_storage_offset);
|
||||||
|
fs_ctx->compression_table_size = raw_storage_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Check if we're within boundaries. */
|
/* Check if we're within boundaries. */
|
||||||
|
@ -1223,8 +1291,8 @@ static bool ncaFsSectionCheckHashRegionAccess(NcaFsSectionContext *ctx, u64 offs
|
||||||
static bool _ncaReadAesCtrExStorageFromBktrSection(NcaFsSectionContext *ctx, void *out, u64 read_size, u64 offset, u32 ctr_val)
|
static bool _ncaReadAesCtrExStorageFromBktrSection(NcaFsSectionContext *ctx, void *out, u64 read_size, u64 offset, u32 ctr_val)
|
||||||
{
|
{
|
||||||
if (!g_ncaCryptoBuffer || !ctx || !ctx->enabled || !ctx->nca_ctx || ctx->section_idx >= NCA_FS_HEADER_COUNT || ctx->section_offset < sizeof(NcaHeader) || \
|
if (!g_ncaCryptoBuffer || !ctx || !ctx->enabled || !ctx->nca_ctx || ctx->section_idx >= NCA_FS_HEADER_COUNT || ctx->section_offset < sizeof(NcaHeader) || \
|
||||||
ctx->section_type != NcaFsSectionType_PatchRomFs || (ctx->encryption_type != NcaEncryptionType_AesCtrEx && ctx->encryption_type != NcaEncryptionType_AesCtrExSkipLayerHash) || \
|
ctx->section_type != NcaFsSectionType_PatchRomFs || (ctx->encryption_type != NcaEncryptionType_None && ctx->encryption_type != NcaEncryptionType_AesCtrEx && \
|
||||||
!out || !read_size || (offset + read_size) > ctx->section_size)
|
ctx->encryption_type != NcaEncryptionType_AesCtrExSkipLayerHash) || !out || !read_size || (offset + read_size) > ctx->section_size)
|
||||||
{
|
{
|
||||||
LOG_MSG("Invalid NCA FS section header parameters!");
|
LOG_MSG("Invalid NCA FS section header parameters!");
|
||||||
return false;
|
return false;
|
||||||
|
|
Loading…
Reference in a new issue