From b8d80bf26002280fb612916095cd5e8ec5127154 Mon Sep 17 00:00:00 2001 From: Pablo Curiel Date: Wed, 22 Jul 2020 16:35:23 -0400 Subject: [PATCH] Functions and wrappers to write generated NCA hash layer patches. --- source/nca.c | 45 ++++++++++++++++++++++++++++--- source/nca.h | 72 +++++++++++++++++++++++++++++++------------------- source/pfs.h | 29 +++++++++++++------- source/romfs.c | 8 +++--- source/romfs.h | 48 ++++++++++++++++++++------------- todo.txt | 3 --- 6 files changed, 140 insertions(+), 65 deletions(-) diff --git a/source/nca.c b/source/nca.c index e364cb7..42fa637 100644 --- a/source/nca.c +++ b/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 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); bool ncaAllocateCryptoBuffer(void) @@ -296,11 +298,27 @@ bool ncaGenerateHierarchicalSha256Patch(NcaFsSectionContext *ctx, const void *da 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) { 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) { 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; - 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 || \ offset >= ctx->section_size || (offset + read_size) > ctx->section_size) { @@ -752,7 +770,7 @@ static bool _ncaReadAesCtrExStorageFromBktrSection(NcaFsSectionContext *ctx, voi 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 || \ (offset + read_size) > ctx->section_size) { @@ -1014,6 +1032,9 @@ static bool ncaGenerateHashDataPatch(NcaFsSectionContext *ctx, const void *data, /* Enable the 'dirty_header' flag. */ 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). */ if (!is_integrity_patch) hierarchical_sha256_patch->hash_region_count = layer_count; @@ -1039,6 +1060,24 @@ end: 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) { if (lock) mutexLock(&g_ncaCryptoBufferMutex); @@ -1046,7 +1085,7 @@ static void *_ncaGenerateEncryptedFsSectionBlock(NcaFsSectionContext *ctx, const u8 *out = NULL; 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 || \ data_offset >= ctx->section_size || (data_offset + data_size) > ctx->section_size || !out_block_size || !out_block_offset) { diff --git a/source/nca.h b/source/nca.h index e76121f..c00917d 100644 --- a/source/nca.h +++ b/source/nca.h @@ -332,11 +332,13 @@ typedef struct { } NcaHashDataPatch; typedef struct { + NcmContentId content_id; u32 hash_region_count; NcaHashDataPatch hash_region_patch[NCA_HIERARCHICAL_SHA256_MAX_REGION_COUNT]; } NcaHierarchicalSha256Patch; typedef struct { + NcmContentId content_id; NcaHashDataPatch hash_level_patch[NCA_IVFC_LEVEL_COUNT]; } 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. bool ncaGenerateHierarchicalSha256Patch(NcaFsSectionContext *ctx, const void *data, u64 data_size, u64 data_offset, NcaHierarchicalSha256Patch *out); -/// Cleanups a previously generated NcaHierarchicalSha256Patch. -NX_INLINE void ncaFreeHierarchicalSha256Patch(NcaHierarchicalSha256Patch *patch) -{ - 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)); -} +/// Overwrites block(s) from a buffer holding raw NCA data using previously initialized NcaContext and NcaHierarchicalSha256Patch. +/// '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); /// 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). @@ -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. bool ncaGenerateHierarchicalIntegrityPatch(NcaFsSectionContext *ctx, const void *data, u64 data_size, u64 data_offset, NcaHierarchicalIntegrityPatch *out); -/// Cleanups a previously generated NcaHierarchicalIntegrityPatch. -NX_INLINE void ncaFreeHierarchicalIntegrityPatch(NcaHierarchicalIntegrityPatch *patch) -{ - 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)); -} +/// Overwrites block(s) from a buffer holding raw NCA data using a previously initialized NcaContext and NcaHierarchicalIntegrityPatch. +/// '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); -/// 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); @@ -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 || \ 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 || \ (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 || \ 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 || \ !integrity_meta_info->info_level_hash.level_information[i].block_order || \ @@ -481,4 +475,28 @@ NX_INLINE bool ncaValidateHierarchicalIntegrityOffsets(NcaIntegrityMetaInfo *int 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__ */ diff --git a/source/pfs.h b/source/pfs.h index c716a72..df2d9ab 100644 --- a/source/pfs.h +++ b/source/pfs.h @@ -53,14 +53,6 @@ typedef struct { /// Initializes a partition FS context. 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. /// Input offset must be relative to the start of the partition FS. 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. 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. /// 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); /// 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) { if (!ctx || !ctx->header_size || !ctx->header) return 0; @@ -116,4 +116,15 @@ NX_INLINE PartitionFileSystemEntry *pfsGetEntryByName(PartitionFileSystemContext 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__ */ diff --git a/source/romfs.c b/source/romfs.c index 83422e6..efd8902 100644 --- a/source/romfs.c +++ b/source/romfs.c @@ -496,20 +496,18 @@ bool romfsGenerateFileEntryPatch(RomFileSystemContext *ctx, RomFileSystemFileEnt bool success = false; 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) { out->use_old_format_patch = true; 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 { out->use_old_format_patch = false; 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; } diff --git a/source/romfs.h b/source/romfs.h index 1b57424..43fe177 100644 --- a/source/romfs.h +++ b/source/romfs.h @@ -119,15 +119,6 @@ typedef enum { /// Initializes a RomFS context. 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. /// Input offset must be relative to the start of the RomFS. 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. /// Input offset must be relative to the start of the RomFS file entry data. /// 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); -/// 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. +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) { 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); } +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__ */ diff --git a/todo.txt b/todo.txt index 8148c5e..ed804ff 100644 --- a/todo.txt +++ b/todo.txt @@ -7,14 +7,11 @@ todo: 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 hashdata patches pfs0: filelist generation methods pfs0: full header aligned to 0x20 (nsp) - pfs0: function to write patches romfs: filelist generation methods - romfs: function to write patches bktr: filelist generation methods (wrappers for romfs functions)