From da5290189bb0899cc3fcec2ed8ead086e644c685 Mon Sep 17 00:00:00 2001 From: Pablo Curiel Date: Sun, 25 Oct 2020 20:03:02 -0400 Subject: [PATCH] Optimize modified NCA header(s) write code. --- code_templates/nsp_dumper.c | 17 +++++++++++++---- source/nca.c | 18 ++++++++++++------ source/nca.h | 2 ++ 3 files changed, 27 insertions(+), 10 deletions(-) diff --git a/code_templates/nsp_dumper.c b/code_templates/nsp_dumper.c index 8712623..4a4365b 100644 --- a/code_templates/nsp_dumper.c +++ b/code_templates/nsp_dumper.c @@ -115,7 +115,7 @@ static void nspDump(TitleInfo *title_info) pfsInitializeFileContext(&pfs_file_ctx); char entry_name[64] = {0}; - u64 nsp_header_size = 0, nsp_size = 0; + u64 nsp_header_size = 0, nsp_size = 0, nsp_offset = 0; Sha256Context sha256_ctx = {0}; u8 sha256_hash[SHA256_HASH_SIZE] = {0}; @@ -457,13 +457,14 @@ static void nspDump(TitleInfo *title_info) goto end; } - consolePrint("dump process started. please wait... yes, i'm too lazy to print progress here...\n"); + consolePrint("dump process started. please wait...\n"); time_t start = time(NULL); // write placeholder header memset(buf, 0, nsp_header_size); fwrite(buf, 1, nsp_header_size, fd); + nsp_offset += nsp_header_size; // write ncas for(u32 i = 0; i < title_info->content_count; i++) @@ -482,7 +483,7 @@ static void nspDump(TitleInfo *title_info) bool dirty_header = ncaIsHeaderDirty(cur_nca_ctx); - for(u64 offset = 0; offset < cur_nca_ctx->content_size; offset += blksize) + for(u64 offset = 0; offset < cur_nca_ctx->content_size; offset += blksize, nsp_offset += blksize) { if ((cur_nca_ctx->content_size - offset) < blksize) blksize = (cur_nca_ctx->content_size - offset); @@ -496,7 +497,7 @@ static void nspDump(TitleInfo *title_info) if (dirty_header) { // write re-encrypted headers - ncaWriteEncryptedHeaderDataToMemoryBuffer(cur_nca_ctx, buf, blksize, offset); + if (!cur_nca_ctx->header_written) ncaWriteEncryptedHeaderDataToMemoryBuffer(cur_nca_ctx, buf, blksize, offset); if (cur_nca_ctx->content_type_ctx_patch) { @@ -521,6 +522,9 @@ static void nspDump(TitleInfo *title_info) break; } } + + // update flag to avoid entering this code block if it's not needed anymore + dirty_header = (!cur_nca_ctx->header_written || cur_nca_ctx->content_type_ctx_patch); } // update hash calculation @@ -560,6 +564,7 @@ static void nspDump(TitleInfo *title_info) // write cnmt xml fwrite(cnmt_ctx.authoring_tool_xml, 1, cnmt_ctx.authoring_tool_xml_size, fd); + nsp_offset += cnmt_ctx.authoring_tool_xml_size; // update cnmt xml pfs entry name if (!pfsUpdateEntryNameFromFileContext(&pfs_file_ctx, meta_nca_ctx->content_type_ctx_data_idx, meta_nca_ctx->content_id_str)) @@ -600,6 +605,7 @@ static void nspDump(TitleInfo *title_info) // write icon fwrite(icon_ctx->icon_data, 1, icon_ctx->icon_size, fd); + nsp_offset += icon_ctx->icon_size; // update pfs entry name if (!pfsUpdateEntryNameFromFileContext(&pfs_file_ctx, data_idx++, cur_nca_ctx->content_id_str)) @@ -624,6 +630,7 @@ static void nspDump(TitleInfo *title_info) // write xml fwrite(authoring_tool_xml, 1, authoring_tool_xml_size, fd); + nsp_offset += authoring_tool_xml_size; // update pfs entry name if (!pfsUpdateEntryNameFromFileContext(&pfs_file_ctx, data_idx, cur_nca_ctx->content_id_str)) @@ -637,9 +644,11 @@ static void nspDump(TitleInfo *title_info) { // write ticket fwrite(tik.data, 1, tik.size, fd); + nsp_offset += tik.size; // write cert fwrite(raw_cert_chain, 1, raw_cert_chain_size, fd); + nsp_offset += raw_cert_chain_size; } // write new pfs0 header diff --git a/source/nca.c b/source/nca.c index 2850b23..11c5b4a 100644 --- a/source/nca.c +++ b/source/nca.c @@ -436,23 +436,29 @@ bool ncaEncryptHeader(NcaContext *ctx) void ncaWriteEncryptedHeaderDataToMemoryBuffer(NcaContext *ctx, 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 encrypted header data (NCA2/3 optimization, last condition). */ + /* Return right away if we're dealing with invalid parameters. */ /* In order to avoid taking up too much execution time when this function is called (ideally inside a loop), we won't use ncaIsHeaderDirty() here. Let the user take care of it instead. */ - if (!ctx || ctx->content_size < NCA_FULL_HEADER_LENGTH || !buf || !buf_size || (buf_offset + buf_size) > ctx->content_size || \ - (ctx->format_version != NcaVersion_Nca0 && buf_offset >= NCA_FULL_HEADER_LENGTH)) return; + if (!ctx || ctx->header_written || ctx->content_size < NCA_FULL_HEADER_LENGTH || !buf || !buf_size || (buf_offset + buf_size) > ctx->content_size) return; + + ctx->header_written = true; /* Attempt to write the NCA header. */ /* Return right away if the NCA header was only partially written. */ - if (buf_offset < sizeof(NcaHeader) && !ncaWritePatchToMemoryBuffer(ctx, &(ctx->encrypted_header), sizeof(NcaHeader), 0, buf, buf_size, buf_offset)) return; + if (buf_offset < sizeof(NcaHeader) && !ncaWritePatchToMemoryBuffer(ctx, &(ctx->encrypted_header), sizeof(NcaHeader), 0, buf, buf_size, buf_offset)) + { + ctx->header_written = false; + return; + } /* Attempt to write NCA FS section headers. */ for(u8 i = 0; i < NCA_FS_HEADER_COUNT; i++) { NcaFsSectionContext *fs_ctx = &(ctx->fs_ctx[i]); - if (!fs_ctx->enabled) continue; + if (!fs_ctx->enabled || fs_ctx->header_written) continue; u64 fs_header_offset = (ctx->format_version != NcaVersion_Nca0 ? (sizeof(NcaHeader) + (i * sizeof(NcaFsHeader))) : fs_ctx->section_offset); - ncaWritePatchToMemoryBuffer(ctx, &(fs_ctx->encrypted_header), sizeof(NcaFsHeader), fs_header_offset, buf, buf_size, buf_offset); + fs_ctx->header_written = ncaWritePatchToMemoryBuffer(ctx, &(fs_ctx->encrypted_header), sizeof(NcaFsHeader), fs_header_offset, buf, buf_size, buf_offset); + if (!fs_ctx->header_written) ctx->header_written = false; } } diff --git a/source/nca.h b/source/nca.h index 7de69ef..da73d62 100644 --- a/source/nca.h +++ b/source/nca.h @@ -278,6 +278,7 @@ typedef struct { NcaFsHeader header; ///< Plaintext NCA FS section header. NcaFsHeader encrypted_header; ///< Encrypted NCA FS section header. If the plaintext NCA FS section header is modified, this will hold an encrypted copy of it. ///< Otherwise, this holds the unmodified, encrypted NCA FS section header. + bool header_written; ///< Set to true after this FS section header has been written to an output dump. u8 section_num; u64 section_offset; u64 section_size; @@ -322,6 +323,7 @@ typedef struct { u8 header_hash[SHA256_HASH_SIZE]; ///< Plaintext NCA header hash. Used to determine if it's necessary to replace the NCA header while dumping this NCA. NcaHeader encrypted_header; ///< Encrypted NCA header. If the plaintext NCA header is modified, this will hold an encrypted copy of it. ///< Otherwise, this holds the unmodified, encrypted NCA header. + bool header_written; ///< Set to true after the NCA header and the FS section headers have been written to an output dump. NcaFsSectionContext fs_ctx[NCA_FS_HEADER_COUNT]; NcaDecryptedKeyArea decrypted_key_area; void *content_type_ctx; ///< Pointer to a content type context (e.g. ContentMetaContext, ProgramInfoContext, NacpContext, LegalInfoContext). Set to NULL if unused.