mirror of
https://github.com/DarkMatterCore/nxdumptool.git
synced 2024-11-26 04:02:11 +00:00
Functions and wrappers to write generated NCA hash layer patches.
This commit is contained in:
parent
90e0f057bc
commit
b8d80bf260
6 changed files with 140 additions and 65 deletions
45
source/nca.c
45
source/nca.c
|
@ -59,6 +59,8 @@ static bool _ncaReadFsSection(NcaFsSectionContext *ctx, void *out, u64 read_size
|
||||||
static bool _ncaReadAesCtrExStorageFromBktrSection(NcaFsSectionContext *ctx, void *out, u64 read_size, u64 offset, u32 ctr_val, bool lock);
|
static bool _ncaReadAesCtrExStorageFromBktrSection(NcaFsSectionContext *ctx, void *out, u64 read_size, u64 offset, u32 ctr_val, bool lock);
|
||||||
|
|
||||||
static bool ncaGenerateHashDataPatch(NcaFsSectionContext *ctx, const void *data, u64 data_size, u64 data_offset, void *out, bool is_integrity_patch);
|
static bool ncaGenerateHashDataPatch(NcaFsSectionContext *ctx, const void *data, u64 data_size, u64 data_offset, void *out, bool is_integrity_patch);
|
||||||
|
static void ncaWriteHashDataPatchToMemoryBuffer(NcaContext *ctx, NcaHashDataPatch *layer_patch, void *buf, u64 buf_size, u64 buf_offset);
|
||||||
|
|
||||||
static void *_ncaGenerateEncryptedFsSectionBlock(NcaFsSectionContext *ctx, const void *data, u64 data_size, u64 data_offset, u64 *out_block_size, u64 *out_block_offset, bool lock);
|
static void *_ncaGenerateEncryptedFsSectionBlock(NcaFsSectionContext *ctx, const void *data, u64 data_size, u64 data_offset, u64 *out_block_size, u64 *out_block_offset, bool lock);
|
||||||
|
|
||||||
bool ncaAllocateCryptoBuffer(void)
|
bool ncaAllocateCryptoBuffer(void)
|
||||||
|
@ -296,11 +298,27 @@ bool ncaGenerateHierarchicalSha256Patch(NcaFsSectionContext *ctx, const void *da
|
||||||
return ncaGenerateHashDataPatch(ctx, data, data_size, data_offset, out, false);
|
return ncaGenerateHashDataPatch(ctx, data, data_size, data_offset, out, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ncaWriteHierarchicalSha256PatchToMemoryBuffer(NcaContext *ctx, NcaHierarchicalSha256Patch *patch, void *buf, u64 buf_size, u64 buf_offset)
|
||||||
|
{
|
||||||
|
if (!ctx || !strlen(ctx->content_id_str) || ctx->content_size < NCA_FULL_HEADER_LENGTH || !patch || memcmp(patch->content_id.c, ctx->content_id.c, 0x10) != 0 || !patch->hash_region_count || \
|
||||||
|
patch->hash_region_count > NCA_HIERARCHICAL_SHA256_MAX_REGION_COUNT || !buf || !buf_size || buf_offset >= ctx->content_size || (buf_offset + buf_size) > ctx->content_size) return;
|
||||||
|
|
||||||
|
for(u32 i = 0; i < patch->hash_region_count; i++) ncaWriteHashDataPatchToMemoryBuffer(ctx, &(patch->hash_region_patch[i]), buf, buf_size, buf_offset);
|
||||||
|
}
|
||||||
|
|
||||||
bool ncaGenerateHierarchicalIntegrityPatch(NcaFsSectionContext *ctx, const void *data, u64 data_size, u64 data_offset, NcaHierarchicalIntegrityPatch *out)
|
bool ncaGenerateHierarchicalIntegrityPatch(NcaFsSectionContext *ctx, const void *data, u64 data_size, u64 data_offset, NcaHierarchicalIntegrityPatch *out)
|
||||||
{
|
{
|
||||||
return ncaGenerateHashDataPatch(ctx, data, data_size, data_offset, out, true);
|
return ncaGenerateHashDataPatch(ctx, data, data_size, data_offset, out, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ncaWriteHierarchicalIntegrityPatchToMemoryBuffer(NcaContext *ctx, NcaHierarchicalIntegrityPatch *patch, void *buf, u64 buf_size, u64 buf_offset)
|
||||||
|
{
|
||||||
|
if (!ctx || !strlen(ctx->content_id_str) || ctx->content_size < NCA_FULL_HEADER_LENGTH || !patch || memcmp(patch->content_id.c, ctx->content_id.c, 0x10) != 0 || !buf || !buf_size || \
|
||||||
|
buf_offset >= ctx->content_size || (buf_offset + buf_size) > ctx->content_size) return;
|
||||||
|
|
||||||
|
for(u32 i = 0; i < NCA_IVFC_LEVEL_COUNT; i++) ncaWriteHashDataPatchToMemoryBuffer(ctx, &(patch->hash_level_patch[i]), buf, buf_size, buf_offset);
|
||||||
|
}
|
||||||
|
|
||||||
void ncaRemoveTitlekeyCrypto(NcaContext *ctx)
|
void ncaRemoveTitlekeyCrypto(NcaContext *ctx)
|
||||||
{
|
{
|
||||||
if (!ctx || !ctx->rights_id_available || !ctx->titlekey_retrieved) return;
|
if (!ctx || !ctx->rights_id_available || !ctx->titlekey_retrieved) return;
|
||||||
|
@ -630,7 +648,7 @@ static bool _ncaReadFsSection(NcaFsSectionContext *ctx, void *out, u64 read_size
|
||||||
|
|
||||||
bool ret = false;
|
bool ret = false;
|
||||||
|
|
||||||
if (!g_ncaCryptoBuffer || !ctx || !ctx->enabled || !ctx->nca_ctx || ctx->section_num >= NCA_FS_HEADER_COUNT || ctx->section_offset < NCA_FULL_HEADER_LENGTH || \
|
if (!g_ncaCryptoBuffer || !ctx || !ctx->enabled || !ctx->nca_ctx || ctx->section_num >= NCA_FS_HEADER_COUNT || ctx->section_offset < sizeof(NcaHeader) || \
|
||||||
ctx->section_type >= NcaFsSectionType_Invalid || ctx->encryption_type == NcaEncryptionType_Auto || ctx->encryption_type > NcaEncryptionType_AesCtrEx || !out || !read_size || \
|
ctx->section_type >= NcaFsSectionType_Invalid || ctx->encryption_type == NcaEncryptionType_Auto || ctx->encryption_type > NcaEncryptionType_AesCtrEx || !out || !read_size || \
|
||||||
offset >= ctx->section_size || (offset + read_size) > ctx->section_size)
|
offset >= ctx->section_size || (offset + read_size) > ctx->section_size)
|
||||||
{
|
{
|
||||||
|
@ -752,7 +770,7 @@ static bool _ncaReadAesCtrExStorageFromBktrSection(NcaFsSectionContext *ctx, voi
|
||||||
|
|
||||||
bool ret = false;
|
bool ret = false;
|
||||||
|
|
||||||
if (!g_ncaCryptoBuffer || !ctx || !ctx->enabled || !ctx->nca_ctx || ctx->section_num >= NCA_FS_HEADER_COUNT || ctx->section_offset < NCA_FULL_HEADER_LENGTH || \
|
if (!g_ncaCryptoBuffer || !ctx || !ctx->enabled || !ctx->nca_ctx || ctx->section_num >= NCA_FS_HEADER_COUNT || ctx->section_offset < sizeof(NcaHeader) || \
|
||||||
ctx->section_type != NcaFsSectionType_PatchRomFs || ctx->encryption_type != NcaEncryptionType_AesCtrEx || !out || !read_size || offset >= ctx->section_size || \
|
ctx->section_type != NcaFsSectionType_PatchRomFs || ctx->encryption_type != NcaEncryptionType_AesCtrEx || !out || !read_size || offset >= ctx->section_size || \
|
||||||
(offset + read_size) > ctx->section_size)
|
(offset + read_size) > ctx->section_size)
|
||||||
{
|
{
|
||||||
|
@ -1014,6 +1032,9 @@ static bool ncaGenerateHashDataPatch(NcaFsSectionContext *ctx, const void *data,
|
||||||
/* Enable the 'dirty_header' flag. */
|
/* Enable the 'dirty_header' flag. */
|
||||||
nca_ctx->dirty_header = true;
|
nca_ctx->dirty_header = true;
|
||||||
|
|
||||||
|
/* Copy content ID. */
|
||||||
|
memcpy(!is_integrity_patch ? &(hierarchical_sha256_patch->content_id) : &(hierarchical_integrity_patch->content_id), &(nca_ctx->content_id), sizeof(NcmContentId));
|
||||||
|
|
||||||
/* Set hash region count (if needed). */
|
/* Set hash region count (if needed). */
|
||||||
if (!is_integrity_patch) hierarchical_sha256_patch->hash_region_count = layer_count;
|
if (!is_integrity_patch) hierarchical_sha256_patch->hash_region_count = layer_count;
|
||||||
|
|
||||||
|
@ -1039,6 +1060,24 @@ end:
|
||||||
return success;
|
return success;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void ncaWriteHashDataPatchToMemoryBuffer(NcaContext *ctx, NcaHashDataPatch *layer_patch, void *buf, u64 buf_size, u64 buf_offset)
|
||||||
|
{
|
||||||
|
/* Return right away if we're dealing with invalid parameters, or if the buffer data is not part of the range covered by the patch (last two conditions). */
|
||||||
|
if (!ctx || !layer_patch || layer_patch->offset < sizeof(NcaHeader) || layer_patch->offset >= ctx->content_size || !layer_patch->size || !layer_patch->data || \
|
||||||
|
(layer_patch->offset + layer_patch->size) > ctx->content_size || !buf || (buf_offset + buf_size) <= layer_patch->offset || (layer_patch->offset + layer_patch->size) <= buf_offset) return;
|
||||||
|
|
||||||
|
/* Overwrite buffer data using patch data. */
|
||||||
|
u64 patch_block_offset = (buf_offset > layer_patch->offset ? (buf_offset - layer_patch->offset) : 0);
|
||||||
|
u64 patch_block_size = (layer_patch->size - patch_block_offset);
|
||||||
|
|
||||||
|
u64 buf_block_offset = (buf_offset > layer_patch->offset ? 0 : (layer_patch->offset - buf_offset));
|
||||||
|
u64 buf_block_size = ((buf_size - buf_block_offset) > patch_block_size ? patch_block_size : (buf_size - buf_block_offset));
|
||||||
|
|
||||||
|
memcpy((u8*)buf + buf_block_offset, layer_patch->data + patch_block_offset, buf_block_size);
|
||||||
|
|
||||||
|
LOGFILE("Overwrote 0x%lX bytes block at offset 0x%lX from raw NCA \"%s\" buffer (size 0x%lX, NCA offset 0x%lX).", buf_block_size, buf_block_offset, ctx->content_id_str, buf_size, buf_offset);
|
||||||
|
}
|
||||||
|
|
||||||
static void *_ncaGenerateEncryptedFsSectionBlock(NcaFsSectionContext *ctx, const void *data, u64 data_size, u64 data_offset, u64 *out_block_size, u64 *out_block_offset, bool lock)
|
static void *_ncaGenerateEncryptedFsSectionBlock(NcaFsSectionContext *ctx, const void *data, u64 data_size, u64 data_offset, u64 *out_block_size, u64 *out_block_offset, bool lock)
|
||||||
{
|
{
|
||||||
if (lock) mutexLock(&g_ncaCryptoBufferMutex);
|
if (lock) mutexLock(&g_ncaCryptoBufferMutex);
|
||||||
|
@ -1046,7 +1085,7 @@ static void *_ncaGenerateEncryptedFsSectionBlock(NcaFsSectionContext *ctx, const
|
||||||
u8 *out = NULL;
|
u8 *out = NULL;
|
||||||
bool success = false;
|
bool success = false;
|
||||||
|
|
||||||
if (!g_ncaCryptoBuffer || !ctx || !ctx->enabled || !ctx->nca_ctx || ctx->section_num >= NCA_FS_HEADER_COUNT || ctx->section_offset < NCA_FULL_HEADER_LENGTH || \
|
if (!g_ncaCryptoBuffer || !ctx || !ctx->enabled || !ctx->nca_ctx || ctx->section_num >= NCA_FS_HEADER_COUNT || ctx->section_offset < sizeof(NcaHeader) || \
|
||||||
ctx->section_type >= NcaFsSectionType_Invalid || ctx->encryption_type == NcaEncryptionType_Auto || ctx->encryption_type >= NcaEncryptionType_AesCtrEx || !data || !data_size || \
|
ctx->section_type >= NcaFsSectionType_Invalid || ctx->encryption_type == NcaEncryptionType_Auto || ctx->encryption_type >= NcaEncryptionType_AesCtrEx || !data || !data_size || \
|
||||||
data_offset >= ctx->section_size || (data_offset + data_size) > ctx->section_size || !out_block_size || !out_block_offset)
|
data_offset >= ctx->section_size || (data_offset + data_size) > ctx->section_size || !out_block_size || !out_block_offset)
|
||||||
{
|
{
|
||||||
|
|
72
source/nca.h
72
source/nca.h
|
@ -332,11 +332,13 @@ typedef struct {
|
||||||
} NcaHashDataPatch;
|
} NcaHashDataPatch;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
NcmContentId content_id;
|
||||||
u32 hash_region_count;
|
u32 hash_region_count;
|
||||||
NcaHashDataPatch hash_region_patch[NCA_HIERARCHICAL_SHA256_MAX_REGION_COUNT];
|
NcaHashDataPatch hash_region_patch[NCA_HIERARCHICAL_SHA256_MAX_REGION_COUNT];
|
||||||
} NcaHierarchicalSha256Patch;
|
} NcaHierarchicalSha256Patch;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
NcmContentId content_id;
|
||||||
NcaHashDataPatch hash_level_patch[NCA_IVFC_LEVEL_COUNT];
|
NcaHashDataPatch hash_level_patch[NCA_IVFC_LEVEL_COUNT];
|
||||||
} NcaHierarchicalIntegrityPatch;
|
} NcaHierarchicalIntegrityPatch;
|
||||||
|
|
||||||
|
@ -378,18 +380,9 @@ void *ncaGenerateEncryptedFsSectionBlock(NcaFsSectionContext *ctx, const void *d
|
||||||
/// As such, this function is not designed to generate more than one patch per HierarchicalSha256 FS section.
|
/// As such, this function is not designed to generate more than one patch per HierarchicalSha256 FS section.
|
||||||
bool ncaGenerateHierarchicalSha256Patch(NcaFsSectionContext *ctx, const void *data, u64 data_size, u64 data_offset, NcaHierarchicalSha256Patch *out);
|
bool ncaGenerateHierarchicalSha256Patch(NcaFsSectionContext *ctx, const void *data, u64 data_size, u64 data_offset, NcaHierarchicalSha256Patch *out);
|
||||||
|
|
||||||
/// Cleanups a previously generated NcaHierarchicalSha256Patch.
|
/// Overwrites block(s) from a buffer holding raw NCA data using previously initialized NcaContext and NcaHierarchicalSha256Patch.
|
||||||
NX_INLINE void ncaFreeHierarchicalSha256Patch(NcaHierarchicalSha256Patch *patch)
|
/// 'buf_offset' must hold the raw NCA offset where the data stored in 'buf' was read from.
|
||||||
{
|
void ncaWriteHierarchicalSha256PatchToMemoryBuffer(NcaContext *ctx, NcaHierarchicalSha256Patch *patch, void *buf, u64 buf_size, u64 buf_offset);
|
||||||
if (!patch || !patch->hash_region_count || patch->hash_region_count > NCA_HIERARCHICAL_SHA256_MAX_REGION_COUNT) return;
|
|
||||||
|
|
||||||
for(u8 i = 0; i < patch->hash_region_count; i++)
|
|
||||||
{
|
|
||||||
if (patch->hash_region_patch[i].data) free(patch->hash_region_patch[i].data);
|
|
||||||
}
|
|
||||||
|
|
||||||
memset(patch, 0, sizeof(NcaHierarchicalSha256Patch));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Generates HierarchicalIntegrity FS section patch data, which can be used to seamlessly replace NCA data.
|
/// Generates HierarchicalIntegrity FS section patch data, which can be used to seamlessly replace NCA data.
|
||||||
/// Input offset must be relative to the start of the last HierarchicalIntegrity hash level (actual underlying FS).
|
/// Input offset must be relative to the start of the last HierarchicalIntegrity hash level (actual underlying FS).
|
||||||
|
@ -397,20 +390,21 @@ NX_INLINE void ncaFreeHierarchicalSha256Patch(NcaHierarchicalSha256Patch *patch)
|
||||||
/// As such, this function is not designed to generate more than one patch per HierarchicalIntegrity FS section.
|
/// As such, this function is not designed to generate more than one patch per HierarchicalIntegrity FS section.
|
||||||
bool ncaGenerateHierarchicalIntegrityPatch(NcaFsSectionContext *ctx, const void *data, u64 data_size, u64 data_offset, NcaHierarchicalIntegrityPatch *out);
|
bool ncaGenerateHierarchicalIntegrityPatch(NcaFsSectionContext *ctx, const void *data, u64 data_size, u64 data_offset, NcaHierarchicalIntegrityPatch *out);
|
||||||
|
|
||||||
/// Cleanups a previously generated NcaHierarchicalIntegrityPatch.
|
/// Overwrites block(s) from a buffer holding raw NCA data using a previously initialized NcaContext and NcaHierarchicalIntegrityPatch.
|
||||||
NX_INLINE void ncaFreeHierarchicalIntegrityPatch(NcaHierarchicalIntegrityPatch *patch)
|
/// 'buf_offset' must hold the raw NCA offset where the data stored in 'buf' was read from.
|
||||||
{
|
void ncaWriteHierarchicalIntegrityPatchToMemoryBuffer(NcaContext *ctx, NcaHierarchicalIntegrityPatch *patch, void *buf, u64 buf_size, u64 buf_offset);
|
||||||
if (!patch) return;
|
|
||||||
|
|
||||||
for(u8 i = 0; i < NCA_IVFC_LEVEL_COUNT; i++)
|
|
||||||
{
|
|
||||||
if (patch->hash_level_patch[i].data) free(patch->hash_level_patch[i].data);
|
|
||||||
}
|
|
||||||
|
|
||||||
memset(patch, 0, sizeof(NcaHierarchicalIntegrityPatch));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Removes titlekey crypto dependency from a NCA context by wiping the Rights ID from the underlying NCA header copy and copying the decrypted titlekey to the NCA key area.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/// Removes titlekey crypto dependency from a NCA context by wiping the Rights ID from the underlying NCA header and copying the decrypted titlekey to the NCA key area.
|
||||||
void ncaRemoveTitlekeyCrypto(NcaContext *ctx);
|
void ncaRemoveTitlekeyCrypto(NcaContext *ctx);
|
||||||
|
|
||||||
|
|
||||||
|
@ -457,7 +451,7 @@ NX_INLINE bool ncaValidateHierarchicalSha256Offsets(NcaHierarchicalSha256Data *h
|
||||||
if (!hierarchical_sha256_data || !section_size || !hierarchical_sha256_data->hash_block_size || !hierarchical_sha256_data->hash_region_count || \
|
if (!hierarchical_sha256_data || !section_size || !hierarchical_sha256_data->hash_block_size || !hierarchical_sha256_data->hash_region_count || \
|
||||||
hierarchical_sha256_data->hash_region_count > NCA_HIERARCHICAL_SHA256_MAX_REGION_COUNT) return false;
|
hierarchical_sha256_data->hash_region_count > NCA_HIERARCHICAL_SHA256_MAX_REGION_COUNT) return false;
|
||||||
|
|
||||||
for(u8 i = 0; i < hierarchical_sha256_data->hash_region_count; i++)
|
for(u32 i = 0; i < hierarchical_sha256_data->hash_region_count; i++)
|
||||||
{
|
{
|
||||||
if (hierarchical_sha256_data->hash_region[i].offset >= section_size || !hierarchical_sha256_data->hash_region[i].size || \
|
if (hierarchical_sha256_data->hash_region[i].offset >= section_size || !hierarchical_sha256_data->hash_region[i].size || \
|
||||||
(hierarchical_sha256_data->hash_region[i].offset + hierarchical_sha256_data->hash_region[i].size) > section_size) return false;
|
(hierarchical_sha256_data->hash_region[i].offset + hierarchical_sha256_data->hash_region[i].size) > section_size) return false;
|
||||||
|
@ -471,7 +465,7 @@ NX_INLINE bool ncaValidateHierarchicalIntegrityOffsets(NcaIntegrityMetaInfo *int
|
||||||
if (!integrity_meta_info || !section_size || __builtin_bswap32(integrity_meta_info->magic) != NCA_IVFC_MAGIC || integrity_meta_info->master_hash_size != SHA256_HASH_SIZE || \
|
if (!integrity_meta_info || !section_size || __builtin_bswap32(integrity_meta_info->magic) != NCA_IVFC_MAGIC || integrity_meta_info->master_hash_size != SHA256_HASH_SIZE || \
|
||||||
integrity_meta_info->info_level_hash.max_level_count != NCA_IVFC_MAX_LEVEL_COUNT) return false;
|
integrity_meta_info->info_level_hash.max_level_count != NCA_IVFC_MAX_LEVEL_COUNT) return false;
|
||||||
|
|
||||||
for(u8 i = 0; i < NCA_IVFC_LEVEL_COUNT; i++)
|
for(u32 i = 0; i < NCA_IVFC_LEVEL_COUNT; i++)
|
||||||
{
|
{
|
||||||
if (integrity_meta_info->info_level_hash.level_information[i].offset >= section_size || !integrity_meta_info->info_level_hash.level_information[i].size || \
|
if (integrity_meta_info->info_level_hash.level_information[i].offset >= section_size || !integrity_meta_info->info_level_hash.level_information[i].size || \
|
||||||
!integrity_meta_info->info_level_hash.level_information[i].block_order || \
|
!integrity_meta_info->info_level_hash.level_information[i].block_order || \
|
||||||
|
@ -481,4 +475,28 @@ NX_INLINE bool ncaValidateHierarchicalIntegrityOffsets(NcaIntegrityMetaInfo *int
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
NX_INLINE void ncaFreeHierarchicalSha256Patch(NcaHierarchicalSha256Patch *patch)
|
||||||
|
{
|
||||||
|
if (!patch) return;
|
||||||
|
|
||||||
|
for(u32 i = 0; i < NCA_HIERARCHICAL_SHA256_MAX_REGION_COUNT; i++)
|
||||||
|
{
|
||||||
|
if (patch->hash_region_patch[i].data) free(patch->hash_region_patch[i].data);
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(patch, 0, sizeof(NcaHierarchicalSha256Patch));
|
||||||
|
}
|
||||||
|
|
||||||
|
NX_INLINE void ncaFreeHierarchicalIntegrityPatch(NcaHierarchicalIntegrityPatch *patch)
|
||||||
|
{
|
||||||
|
if (!patch) return;
|
||||||
|
|
||||||
|
for(u32 i = 0; i < NCA_IVFC_LEVEL_COUNT; i++)
|
||||||
|
{
|
||||||
|
if (patch->hash_level_patch[i].data) free(patch->hash_level_patch[i].data);
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(patch, 0, sizeof(NcaHierarchicalIntegrityPatch));
|
||||||
|
}
|
||||||
|
|
||||||
#endif /* __NCA_H__ */
|
#endif /* __NCA_H__ */
|
||||||
|
|
29
source/pfs.h
29
source/pfs.h
|
@ -53,14 +53,6 @@ typedef struct {
|
||||||
/// Initializes a partition FS context.
|
/// Initializes a partition FS context.
|
||||||
bool pfsInitializeContext(PartitionFileSystemContext *out, NcaFsSectionContext *nca_fs_ctx);
|
bool pfsInitializeContext(PartitionFileSystemContext *out, NcaFsSectionContext *nca_fs_ctx);
|
||||||
|
|
||||||
/// Cleanups a previously initialized partition FS context.
|
|
||||||
NX_INLINE void pfsFreeContext(PartitionFileSystemContext *ctx)
|
|
||||||
{
|
|
||||||
if (!ctx) return;
|
|
||||||
if (ctx->header) free(ctx->header);
|
|
||||||
memset(ctx, 0, sizeof(PartitionFileSystemContext));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Reads raw partition data using a partition FS context.
|
/// Reads raw partition data using a partition FS context.
|
||||||
/// Input offset must be relative to the start of the partition FS.
|
/// Input offset must be relative to the start of the partition FS.
|
||||||
bool pfsReadPartitionData(PartitionFileSystemContext *ctx, void *out, u64 read_size, u64 offset);
|
bool pfsReadPartitionData(PartitionFileSystemContext *ctx, void *out, u64 read_size, u64 offset);
|
||||||
|
@ -75,13 +67,21 @@ bool pfsGetEntryIndexByName(PartitionFileSystemContext *ctx, const char *name, u
|
||||||
/// Calculates the extracted partition FS size.
|
/// Calculates the extracted partition FS size.
|
||||||
bool pfsGetTotalDataSize(PartitionFileSystemContext *ctx, u64 *out_size);
|
bool pfsGetTotalDataSize(PartitionFileSystemContext *ctx, u64 *out_size);
|
||||||
|
|
||||||
/// Generates HierarchicalSha256 FS section patch data using a partition FS context + entry, which can be used to replace NCA data in content dumping operations.
|
/// Generates HierarchicalSha256 FS section patch data using a partition FS context + entry, which can be used to seamlessly replace NCA data.
|
||||||
/// Input offset must be relative to the start of the partition FS entry data.
|
/// Input offset must be relative to the start of the partition FS entry data.
|
||||||
/// This function shares the same limitations as ncaGenerateHierarchicalSha256Patch().
|
/// This function shares the same limitations as ncaGenerateHierarchicalSha256Patch().
|
||||||
|
/// Use the pfsWriteEntryPatchToMemoryBuffer() wrapper to write patch data generated by this function.
|
||||||
bool pfsGenerateEntryPatch(PartitionFileSystemContext *ctx, PartitionFileSystemEntry *fs_entry, const void *data, u64 data_size, u64 data_offset, NcaHierarchicalSha256Patch *out);
|
bool pfsGenerateEntryPatch(PartitionFileSystemContext *ctx, PartitionFileSystemEntry *fs_entry, const void *data, u64 data_size, u64 data_offset, NcaHierarchicalSha256Patch *out);
|
||||||
|
|
||||||
/// Miscellaneous functions.
|
/// Miscellaneous functions.
|
||||||
|
|
||||||
|
NX_INLINE void pfsFreeContext(PartitionFileSystemContext *ctx)
|
||||||
|
{
|
||||||
|
if (!ctx) return;
|
||||||
|
if (ctx->header) free(ctx->header);
|
||||||
|
memset(ctx, 0, sizeof(PartitionFileSystemContext));
|
||||||
|
}
|
||||||
|
|
||||||
NX_INLINE u32 pfsGetEntryCount(PartitionFileSystemContext *ctx)
|
NX_INLINE u32 pfsGetEntryCount(PartitionFileSystemContext *ctx)
|
||||||
{
|
{
|
||||||
if (!ctx || !ctx->header_size || !ctx->header) return 0;
|
if (!ctx || !ctx->header_size || !ctx->header) return 0;
|
||||||
|
@ -116,4 +116,15 @@ NX_INLINE PartitionFileSystemEntry *pfsGetEntryByName(PartitionFileSystemContext
|
||||||
return pfsGetEntryByIndex(ctx, idx);
|
return pfsGetEntryByIndex(ctx, idx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
NX_INLINE void pfsWriteEntryPatchToMemoryBuffer(PartitionFileSystemContext *ctx, NcaHierarchicalSha256Patch *patch, void *buf, u64 buf_size, u64 buf_offset)
|
||||||
|
{
|
||||||
|
if (!ctx || !ctx->nca_fs_ctx) return;
|
||||||
|
ncaWriteHierarchicalSha256PatchToMemoryBuffer((NcaContext*)ctx->nca_fs_ctx->nca_ctx, patch, buf, buf_size, buf_offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
NX_INLINE void pfsFreeEntryPatch(NcaHierarchicalSha256Patch *patch)
|
||||||
|
{
|
||||||
|
ncaFreeHierarchicalSha256Patch(patch);
|
||||||
|
}
|
||||||
|
|
||||||
#endif /* __PFS_H__ */
|
#endif /* __PFS_H__ */
|
||||||
|
|
|
@ -496,20 +496,18 @@ bool romfsGenerateFileEntryPatch(RomFileSystemContext *ctx, RomFileSystemFileEnt
|
||||||
bool success = false;
|
bool success = false;
|
||||||
u64 fs_offset = (ctx->body_offset + file_entry->offset + data_offset);
|
u64 fs_offset = (ctx->body_offset + file_entry->offset + data_offset);
|
||||||
|
|
||||||
memset(&(out->old_format_patch), 0, sizeof(NcaHierarchicalSha256Patch));
|
|
||||||
memset(&(out->cur_format_patch), 0, sizeof(NcaHierarchicalIntegrityPatch));
|
|
||||||
|
|
||||||
if (ctx->nca_fs_ctx->section_type == NcaFsSectionType_Nca0RomFs)
|
if (ctx->nca_fs_ctx->section_type == NcaFsSectionType_Nca0RomFs)
|
||||||
{
|
{
|
||||||
out->use_old_format_patch = true;
|
out->use_old_format_patch = true;
|
||||||
success = ncaGenerateHierarchicalSha256Patch(ctx->nca_fs_ctx, data, data_size, fs_offset, &(out->old_format_patch));
|
success = ncaGenerateHierarchicalSha256Patch(ctx->nca_fs_ctx, data, data_size, fs_offset, &(out->old_format_patch));
|
||||||
if (!success) LOGFILE("Failed to generate 0x%lX bytes HierarchicalSha256 patch at offset 0x%lX for RomFS file entry!", data_size, fs_offset);
|
|
||||||
} else {
|
} else {
|
||||||
out->use_old_format_patch = false;
|
out->use_old_format_patch = false;
|
||||||
success = ncaGenerateHierarchicalIntegrityPatch(ctx->nca_fs_ctx, data, data_size, fs_offset, &(out->cur_format_patch));
|
success = ncaGenerateHierarchicalIntegrityPatch(ctx->nca_fs_ctx, data, data_size, fs_offset, &(out->cur_format_patch));
|
||||||
if (!success) LOGFILE("Failed to generate 0x%lX bytes HierarchicalIntegrity patch at offset 0x%lX for RomFS file entry!", data_size, fs_offset);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!success) LOGFILE("Failed to generate 0x%lX bytes Hierarchical%s patch at offset 0x%lX for RomFS file entry!", data_size, \
|
||||||
|
ctx->nca_fs_ctx->section_type == NcaFsSectionType_Nca0RomFs ? "Sha256" : "Integrity", fs_offset);
|
||||||
|
|
||||||
return success;
|
return success;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -119,15 +119,6 @@ typedef enum {
|
||||||
/// Initializes a RomFS context.
|
/// Initializes a RomFS context.
|
||||||
bool romfsInitializeContext(RomFileSystemContext *out, NcaFsSectionContext *nca_fs_ctx);
|
bool romfsInitializeContext(RomFileSystemContext *out, NcaFsSectionContext *nca_fs_ctx);
|
||||||
|
|
||||||
/// Cleanups a previously initialized RomFS context.
|
|
||||||
NX_INLINE void romfsFreeContext(RomFileSystemContext *ctx)
|
|
||||||
{
|
|
||||||
if (!ctx) return;
|
|
||||||
if (ctx->dir_table) free(ctx->dir_table);
|
|
||||||
if (ctx->file_table) free(ctx->file_table);
|
|
||||||
memset(ctx, 0, sizeof(RomFileSystemContext));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Reads raw filesystem data using a RomFS context.
|
/// Reads raw filesystem data using a RomFS context.
|
||||||
/// Input offset must be relative to the start of the RomFS.
|
/// Input offset must be relative to the start of the RomFS.
|
||||||
bool romfsReadFileSystemData(RomFileSystemContext *ctx, void *out, u64 read_size, u64 offset);
|
bool romfsReadFileSystemData(RomFileSystemContext *ctx, void *out, u64 read_size, u64 offset);
|
||||||
|
@ -159,19 +150,19 @@ bool romfsGeneratePathFromFileEntry(RomFileSystemContext *ctx, RomFileSystemFile
|
||||||
/// Generates HierarchicalSha256 (NCA0) / HierarchicalIntegrity (NCA2/NCA3) FS section patch data using a RomFS context + file entry, which can be used to seamlessly replace NCA data.
|
/// Generates HierarchicalSha256 (NCA0) / HierarchicalIntegrity (NCA2/NCA3) FS section patch data using a RomFS context + file entry, which can be used to seamlessly replace NCA data.
|
||||||
/// Input offset must be relative to the start of the RomFS file entry data.
|
/// Input offset must be relative to the start of the RomFS file entry data.
|
||||||
/// This function shares the same limitations as ncaGenerateHierarchicalSha256Patch() / ncaGenerateHierarchicalIntegrityPatch().
|
/// This function shares the same limitations as ncaGenerateHierarchicalSha256Patch() / ncaGenerateHierarchicalIntegrityPatch().
|
||||||
|
/// Use the romfsWriteFileEntryPatchToMemoryBuffer() wrapper to write patch data generated by this function.
|
||||||
bool romfsGenerateFileEntryPatch(RomFileSystemContext *ctx, RomFileSystemFileEntry *file_entry, const void *data, u64 data_size, u64 data_offset, RomFileSystemFileEntryPatch *out);
|
bool romfsGenerateFileEntryPatch(RomFileSystemContext *ctx, RomFileSystemFileEntry *file_entry, const void *data, u64 data_size, u64 data_offset, RomFileSystemFileEntryPatch *out);
|
||||||
|
|
||||||
/// Cleanups a previously generated RomFS file entry patch.
|
|
||||||
NX_INLINE void romfsFreeFileEntryPatch(RomFileSystemFileEntryPatch *patch)
|
|
||||||
{
|
|
||||||
if (!patch) return;
|
|
||||||
ncaFreeHierarchicalSha256Patch(&(patch->old_format_patch));
|
|
||||||
ncaFreeHierarchicalIntegrityPatch(&(patch->cur_format_patch));
|
|
||||||
memset(patch, 0, sizeof(RomFileSystemFileEntryPatch));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Miscellaneous functions.
|
/// Miscellaneous functions.
|
||||||
|
|
||||||
|
NX_INLINE void romfsFreeContext(RomFileSystemContext *ctx)
|
||||||
|
{
|
||||||
|
if (!ctx) return;
|
||||||
|
if (ctx->dir_table) free(ctx->dir_table);
|
||||||
|
if (ctx->file_table) free(ctx->file_table);
|
||||||
|
memset(ctx, 0, sizeof(RomFileSystemContext));
|
||||||
|
}
|
||||||
|
|
||||||
NX_INLINE RomFileSystemDirectoryEntry *romfsGetDirectoryEntryByOffset(RomFileSystemContext *ctx, u32 dir_entry_offset)
|
NX_INLINE RomFileSystemDirectoryEntry *romfsGetDirectoryEntryByOffset(RomFileSystemContext *ctx, u32 dir_entry_offset)
|
||||||
{
|
{
|
||||||
if (!ctx || !ctx->dir_table || (dir_entry_offset + sizeof(RomFileSystemDirectoryEntry)) > ctx->dir_table_size) return NULL;
|
if (!ctx || !ctx->dir_table || (dir_entry_offset + sizeof(RomFileSystemDirectoryEntry)) > ctx->dir_table_size) return NULL;
|
||||||
|
@ -184,4 +175,25 @@ NX_INLINE RomFileSystemFileEntry *romfsGetFileEntryByOffset(RomFileSystemContext
|
||||||
return (RomFileSystemFileEntry*)((u8*)ctx->file_table + file_entry_offset);
|
return (RomFileSystemFileEntry*)((u8*)ctx->file_table + file_entry_offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
NX_INLINE void romfsWriteFileEntryPatch(RomFileSystemContext *ctx, RomFileSystemFileEntryPatch *patch, void *buf, u64 buf_size, u64 buf_offset)
|
||||||
|
{
|
||||||
|
if (!ctx || !ctx->nca_fs_ctx || !patch || (!patch->use_old_format_patch && ctx->nca_fs_ctx->section_type == NcaFsSectionType_Nca0RomFs) || \
|
||||||
|
(patch->use_old_format_patch && ctx->nca_fs_ctx->section_type != NcaFsSectionType_Nca0RomFs)) return;
|
||||||
|
|
||||||
|
if (patch->use_old_format_patch)
|
||||||
|
{
|
||||||
|
ncaWriteHierarchicalSha256PatchToMemoryBuffer((NcaContext*)ctx->nca_fs_ctx->nca_ctx, &(patch->old_format_patch), buf, buf_size, buf_offset);
|
||||||
|
} else {
|
||||||
|
ncaWriteHierarchicalIntegrityPatchToMemoryBuffer((NcaContext*)ctx->nca_fs_ctx->nca_ctx, &(patch->cur_format_patch), buf, buf_size, buf_offset);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
NX_INLINE void romfsFreeFileEntryPatch(RomFileSystemFileEntryPatch *patch)
|
||||||
|
{
|
||||||
|
if (!patch) return;
|
||||||
|
patch->use_old_format_patch = false;
|
||||||
|
ncaFreeHierarchicalSha256Patch(&(patch->old_format_patch));
|
||||||
|
ncaFreeHierarchicalIntegrityPatch(&(patch->cur_format_patch));
|
||||||
|
}
|
||||||
|
|
||||||
#endif /* __ROMFS_H__ */
|
#endif /* __ROMFS_H__ */
|
||||||
|
|
3
todo.txt
3
todo.txt
|
@ -7,14 +7,11 @@ todo:
|
||||||
tik: use dumped tickets when the original ones can't be found in the ES savefile?
|
tik: use dumped tickets when the original ones can't be found in the ES savefile?
|
||||||
|
|
||||||
nca: function to write encrypted nca headers / nca fs headers (don't forget nca0 please)
|
nca: function to write encrypted nca headers / nca fs headers (don't forget nca0 please)
|
||||||
nca: function to write hashdata patches
|
|
||||||
|
|
||||||
pfs0: filelist generation methods
|
pfs0: filelist generation methods
|
||||||
pfs0: full header aligned to 0x20 (nsp)
|
pfs0: full header aligned to 0x20 (nsp)
|
||||||
pfs0: function to write patches
|
|
||||||
|
|
||||||
romfs: filelist generation methods
|
romfs: filelist generation methods
|
||||||
romfs: function to write patches
|
|
||||||
|
|
||||||
bktr: filelist generation methods (wrappers for romfs functions)
|
bktr: filelist generation methods (wrappers for romfs functions)
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue