diff --git a/code_templates/system_title_dumper.c b/code_templates/system_title_dumper.c index 55217e8..8fc5fa8 100644 --- a/code_templates/system_title_dumper.c +++ b/code_templates/system_title_dumper.c @@ -313,7 +313,7 @@ int main(int argc, char *argv[]) } else if (menu == 2) { - printf("fs section #%u (%s)\n", i + 1, ncaGetFsSectionTypeName(&(nca_ctx->fs_contexts[i]))); + printf("fs section #%u (%s)\n", i + 1, ncaGetFsSectionTypeName(&(nca_ctx->fs_ctx[i]))); } } @@ -367,7 +367,7 @@ int main(int argc, char *argv[]) { consoleClear(); utilsChangeHomeButtonBlockStatus(true); - dumpFsSection(cur_title_info, &(nca_ctx->fs_contexts[selected_idx])); + dumpFsSection(cur_title_info, &(nca_ctx->fs_ctx[selected_idx])); utilsChangeHomeButtonBlockStatus(false); } diff --git a/code_templates/usb_romfs_dumper.c b/code_templates/usb_romfs_dumper.c index 13c19ea..25d69c4 100644 --- a/code_templates/usb_romfs_dumper.c +++ b/code_templates/usb_romfs_dumper.c @@ -501,7 +501,7 @@ int main(int argc, char *argv[]) goto out2; } - if (!bktrInitializeContext(&bktr_ctx, &(base_nca_ctx->fs_contexts[1]), &(update_nca_ctx->fs_contexts[1]))) + if (!bktrInitializeContext(&bktr_ctx, &(base_nca_ctx->fs_ctx[1]), &(update_nca_ctx->fs_ctx[1]))) { consolePrint("bktr initialize ctx failed\n"); goto out2; @@ -512,7 +512,7 @@ int main(int argc, char *argv[]) consolePrint("bktr initialize ctx succeeded\n"); } else { - if (!romfsInitializeContext(&romfs_ctx, &(base_nca_ctx->fs_contexts[1]))) + if (!romfsInitializeContext(&romfs_ctx, &(base_nca_ctx->fs_ctx[1]))) { consolePrint("romfs initialize ctx failed\n"); goto out2; diff --git a/source/cnmt.c b/source/cnmt.c index 0d83815..eca33f8 100644 --- a/source/cnmt.c +++ b/source/cnmt.c @@ -53,7 +53,7 @@ bool cnmtInitializeContext(ContentMetaContext *out, NcaContext *nca_ctx) cnmtFreeContext(out); /* Initialize Partition FS context. */ - if (!pfsInitializeContext(&(out->pfs_ctx), &(nca_ctx->fs_contexts[0]))) + if (!pfsInitializeContext(&(out->pfs_ctx), &(nca_ctx->fs_ctx[0]))) { LOGFILE("Failed to initialize Partition FS context!"); goto end; diff --git a/source/cnmt.h b/source/cnmt.h index 0371a24..7203d9c 100644 --- a/source/cnmt.h +++ b/source/cnmt.h @@ -107,7 +107,7 @@ typedef enum { /// Header for the extended data region in the SystemUpdate title, pointed to by the extended header. /// If version is ContentMetaFirmwareVariationVersion_V1, this is followed by 'variation_count' ContentMetaFirmwareVariationInfoV1 entries. /// Otherwise, if version is ContentMetaFirmwareVariationVersion_V2, this is followed by: -/// * 'variation_count' firmware variation IDs. +/// * 'variation_count' firmware variation IDs (4 bytes each). /// * 'variation_count' ContentMetaFirmwareVariationInfoV2 entries. /// * (Optionally) A variable number of NcmContentMetaInfo entries, which is the sum of all 'meta_count' values from ContentMetaFirmwareVariationInfoV2 entries where 'refer_to_base' is set to false. typedef struct { diff --git a/source/legal_info.c b/source/legal_info.c index df205a4..30893ae 100644 --- a/source/legal_info.c +++ b/source/legal_info.c @@ -41,7 +41,7 @@ bool legalInfoInitializeContext(LegalInfoContext *out, NcaContext *nca_ctx) legalInfoFreeContext(out); /* Initialize RomFS context. */ - if (!romfsInitializeContext(&romfs_ctx, &(nca_ctx->fs_contexts[0]))) + if (!romfsInitializeContext(&romfs_ctx, &(nca_ctx->fs_ctx[0]))) { LOGFILE("Failed to initialize RomFS context!"); goto end; diff --git a/source/nacp.c b/source/nacp.c index c40d58d..40e76de 100644 --- a/source/nacp.c +++ b/source/nacp.c @@ -216,7 +216,7 @@ bool nacpInitializeContext(NacpContext *out, NcaContext *nca_ctx) nacpFreeContext(out); /* Initialize RomFS context. */ - if (!romfsInitializeContext(&(out->romfs_ctx), &(nca_ctx->fs_contexts[0]))) + if (!romfsInitializeContext(&(out->romfs_ctx), &(nca_ctx->fs_ctx[0]))) { LOGFILE("Failed to initialize RomFS context!"); goto end; diff --git a/source/nca.c b/source/nca.c index 879f620..15089db 100644 --- a/source/nca.c +++ b/source/nca.c @@ -42,7 +42,7 @@ static const u8 g_nca0KeyAreaHash[SHA256_HASH_SIZE] = { NX_INLINE bool ncaIsFsInfoEntryValid(NcaFsInfo *fs_info); -static bool ncaDecryptHeader(NcaContext *ctx); +static bool ncaReadDecryptedHeader(NcaContext *ctx); static bool ncaDecryptKeyArea(NcaContext *ctx); static bool ncaEncryptKeyArea(NcaContext *ctx); @@ -129,23 +129,13 @@ bool ncaInitializeContext(NcaContext *out, u8 storage_id, u8 hfs_partition_type, } } - /* Read NCA header. */ - if (!ncaReadContentFile(out, &(out->header), sizeof(NcaHeader), 0)) + /* Read decrypted NCA header and NCA FS section headers. */ + if (!ncaReadDecryptedHeader(out)) { - LOGFILE("Failed to read NCA \"%s\" header!", out->content_id_str); + LOGFILE("Failed to read decrypted NCA \"%s\" header!", out->content_id_str); return false; } - /* Decrypt NCA header. */ - if (!ncaDecryptHeader(out)) - { - LOGFILE("Failed to decrypt NCA \"%s\" header!", out->content_id_str); - return false; - } - - /* Calculate decrypted header hash. */ - sha256CalculateHash(out->header_hash, &(out->header), sizeof(NcaHeader)); - if (out->rights_id_available) { /* Retrieve ticket. */ @@ -163,34 +153,37 @@ bool ncaInitializeContext(NcaContext *out, u8 storage_id, u8 hfs_partition_type, /* Parse sections. */ for(u8 i = 0; i < NCA_FS_HEADER_COUNT; i++) { + NcaFsInfo *fs_info = &(out->header.fs_info[i]); + NcaFsSectionContext *fs_ctx = &(out->fs_ctx[i]); + /* Fill section context. */ - out->fs_contexts[i].nca_ctx = out; - out->fs_contexts[i].section_num = i; - out->fs_contexts[i].section_type = NcaFsSectionType_Invalid; /* Placeholder. */ + fs_ctx->nca_ctx = out; + fs_ctx->section_num = i; + fs_ctx->section_type = NcaFsSectionType_Invalid; /* Placeholder. */ /* Don't proceed if this NCA FS section isn't populated. */ - if (!ncaIsFsInfoEntryValid(&(out->header.fs_info[i]))) continue; + if (!ncaIsFsInfoEntryValid(fs_info)) continue; /* Calculate section offset and size. */ - out->fs_contexts[i].section_offset = NCA_FS_SECTOR_OFFSET(out->header.fs_info[i].start_sector); - out->fs_contexts[i].section_size = (NCA_FS_SECTOR_OFFSET(out->header.fs_info[i].end_sector) - out->fs_contexts[i].section_offset); + fs_ctx->section_offset = NCA_FS_SECTOR_OFFSET(fs_info->start_sector); + fs_ctx->section_size = (NCA_FS_SECTOR_OFFSET(fs_info->end_sector) - fs_ctx->section_offset); /* Check if we're dealing with an invalid offset/size. */ - if (out->fs_contexts[i].section_offset < sizeof(NcaHeader) || !out->fs_contexts[i].section_size || \ - (out->fs_contexts[i].section_offset + out->fs_contexts[i].section_size) > out->content_size) continue; + if (fs_ctx->section_offset < sizeof(NcaHeader) || !fs_ctx->section_size || \ + (fs_ctx->section_offset + fs_ctx->section_size) > out->content_size) continue; /* Determine encryption type. */ - out->fs_contexts[i].encryption_type = (out->format_version == NcaVersion_Nca0 ? NcaEncryptionType_AesXts : out->fs_contexts[i].header.encryption_type); - if (out->fs_contexts[i].encryption_type == NcaEncryptionType_Auto) + fs_ctx->encryption_type = (out->format_version == NcaVersion_Nca0 ? NcaEncryptionType_AesXts : fs_ctx->header.encryption_type); + if (fs_ctx->encryption_type == NcaEncryptionType_Auto) { - switch(out->fs_contexts[i].section_num) + switch(fs_ctx->section_num) { case 0: /* ExeFS Partition FS. */ case 1: /* RomFS. */ - out->fs_contexts[i].encryption_type = NcaEncryptionType_AesCtr; + fs_ctx->encryption_type = NcaEncryptionType_AesCtr; break; case 2: /* Logo Partition FS. */ - out->fs_contexts[i].encryption_type = NcaEncryptionType_None; + fs_ctx->encryption_type = NcaEncryptionType_None; break; default: break; @@ -198,53 +191,53 @@ bool ncaInitializeContext(NcaContext *out, u8 storage_id, u8 hfs_partition_type, } /* Check if we're dealing with an invalid encryption type value. */ - if (out->fs_contexts[i].encryption_type == NcaEncryptionType_Auto || out->fs_contexts[i].encryption_type > NcaEncryptionType_AesCtrEx) continue; + if (fs_ctx->encryption_type == NcaEncryptionType_Auto || fs_ctx->encryption_type > NcaEncryptionType_AesCtrEx) continue; /* Determine FS section type. */ - if (out->fs_contexts[i].header.fs_type == NcaFsType_PartitionFs && out->fs_contexts[i].header.hash_type == NcaHashType_HierarchicalSha256) + if (fs_ctx->header.fs_type == NcaFsType_PartitionFs && fs_ctx->header.hash_type == NcaHashType_HierarchicalSha256) { - out->fs_contexts[i].section_type = NcaFsSectionType_PartitionFs; + fs_ctx->section_type = NcaFsSectionType_PartitionFs; } else - if (out->fs_contexts[i].header.fs_type == NcaFsType_RomFs && out->fs_contexts[i].header.hash_type == NcaHashType_HierarchicalIntegrity) + if (fs_ctx->header.fs_type == NcaFsType_RomFs && fs_ctx->header.hash_type == NcaHashType_HierarchicalIntegrity) { - out->fs_contexts[i].section_type = (out->fs_contexts[i].encryption_type == NcaEncryptionType_AesCtrEx ? NcaFsSectionType_PatchRomFs : NcaFsSectionType_RomFs); + fs_ctx->section_type = (fs_ctx->encryption_type == NcaEncryptionType_AesCtrEx ? NcaFsSectionType_PatchRomFs : NcaFsSectionType_RomFs); } else - if (out->fs_contexts[i].header.fs_type == NcaFsType_RomFs && out->fs_contexts[i].header.hash_type == NcaHashType_HierarchicalSha256 && out->format_version == NcaVersion_Nca0) + if (fs_ctx->header.fs_type == NcaFsType_RomFs && fs_ctx->header.hash_type == NcaHashType_HierarchicalSha256 && out->format_version == NcaVersion_Nca0) { - out->fs_contexts[i].section_type = NcaFsSectionType_Nca0RomFs; + fs_ctx->section_type = NcaFsSectionType_Nca0RomFs; } /* Check if we're dealing with an invalid section type value. */ - if (out->fs_contexts[i].section_type >= NcaFsSectionType_Invalid) continue; + if (fs_ctx->section_type >= NcaFsSectionType_Invalid) continue; /* Initialize crypto data. */ - if ((!out->rights_id_available || (out->rights_id_available && out->titlekey_retrieved)) && out->fs_contexts[i].encryption_type > NcaEncryptionType_None && \ - out->fs_contexts[i].encryption_type <= NcaEncryptionType_AesCtrEx) + if ((!out->rights_id_available || (out->rights_id_available && out->titlekey_retrieved)) && fs_ctx->encryption_type > NcaEncryptionType_None && \ + fs_ctx->encryption_type <= NcaEncryptionType_AesCtrEx) { /* Initialize section CTR. */ - ncaInitializeAesCtrIv(out->fs_contexts[i].ctr, out->fs_contexts[i].header.aes_ctr_upper_iv.value, out->fs_contexts[i].section_offset); + ncaInitializeAesCtrIv(fs_ctx->ctr, fs_ctx->header.aes_ctr_upper_iv.value, fs_ctx->section_offset); /* Initialize AES context. */ if (out->rights_id_available) { /* AES-128-CTR is always used for FS crypto in NCAs with a rights ID. */ - aes128CtrContextCreate(&(out->fs_contexts[i].ctr_ctx), out->titlekey, out->fs_contexts[i].ctr); + aes128CtrContextCreate(&(fs_ctx->ctr_ctx), out->titlekey, fs_ctx->ctr); } else { - if (out->fs_contexts[i].encryption_type == NcaEncryptionType_AesXts) + if (fs_ctx->encryption_type == NcaEncryptionType_AesXts) { /* We need to create two different contexts: one for decryption and another one for encryption. */ - aes128XtsContextCreate(&(out->fs_contexts[i].xts_decrypt_ctx), out->decrypted_key_area.aes_xts_1, out->decrypted_key_area.aes_xts_2, false); - aes128XtsContextCreate(&(out->fs_contexts[i].xts_encrypt_ctx), out->decrypted_key_area.aes_xts_1, out->decrypted_key_area.aes_xts_2, true); + aes128XtsContextCreate(&(fs_ctx->xts_decrypt_ctx), out->decrypted_key_area.aes_xts_1, out->decrypted_key_area.aes_xts_2, false); + aes128XtsContextCreate(&(fs_ctx->xts_encrypt_ctx), out->decrypted_key_area.aes_xts_1, out->decrypted_key_area.aes_xts_2, true); } else - if (out->fs_contexts[i].encryption_type == NcaEncryptionType_AesCtr || out->fs_contexts[i].encryption_type == NcaEncryptionType_AesCtrEx) + if (fs_ctx->encryption_type == NcaEncryptionType_AesCtr || fs_ctx->encryption_type == NcaEncryptionType_AesCtrEx) { - aes128CtrContextCreate(&(out->fs_contexts[i].ctr_ctx), out->decrypted_key_area.aes_ctr, out->fs_contexts[i].ctr); + aes128CtrContextCreate(&(fs_ctx->ctr_ctx), out->decrypted_key_area.aes_ctr, fs_ctx->ctr); } } } /* Enable FS context if we got up to this point. */ - out->fs_contexts[i].enabled = true; + fs_ctx->enabled = true; } return true; @@ -356,8 +349,10 @@ void ncaRemoveTitlekeyCrypto(NcaContext *ctx) for(u8 i = 0; i < NCA_FS_HEADER_COUNT; i++) { /* AES-128-XTS is not used in FS sections from NCAs with titlekey crypto. */ - if (!ctx->fs_contexts[i].enabled || (ctx->fs_contexts[i].encryption_type != NcaEncryptionType_AesCtr && ctx->fs_contexts[i].encryption_type != NcaEncryptionType_AesCtrEx)) continue; - u8 *key_ptr = (ctx->fs_contexts[i].encryption_type == NcaEncryptionType_AesCtr ? ctx->decrypted_key_area.aes_ctr : ctx->decrypted_key_area.aes_ctr_ex); + NcaFsSectionContext *fs_ctx = &(ctx->fs_ctx[i]); + if (!fs_ctx->enabled || (fs_ctx->encryption_type != NcaEncryptionType_AesCtr && fs_ctx->encryption_type != NcaEncryptionType_AesCtrEx)) continue; + + u8 *key_ptr = (fs_ctx->encryption_type == NcaEncryptionType_AesCtr ? ctx->decrypted_key_area.aes_ctr : ctx->decrypted_key_area.aes_ctr_ex); memcpy(key_ptr, ctx->titlekey, AES_128_KEY_SIZE); } @@ -395,7 +390,7 @@ bool ncaEncryptHeader(NcaContext *ctx) if (ctx->format_version == NcaVersion_Nca0) aes128XtsContextCreate(&nca0_fs_header_ctx, ctx->decrypted_key_area.aes_xts_1, ctx->decrypted_key_area.aes_xts_2, true); /* Encrypt NCA header. */ - crypt_res = aes128XtsNintendoCrypt(&hdr_aes_ctx, &(ctx->header), &(ctx->header), sizeof(NcaHeader), 0, NCA_AES_XTS_SECTOR_SIZE, true); + crypt_res = aes128XtsNintendoCrypt(&hdr_aes_ctx, &(ctx->encrypted_header), &(ctx->header), sizeof(NcaHeader), 0, NCA_AES_XTS_SECTOR_SIZE, true); if (crypt_res != sizeof(NcaHeader)) { LOGFILE("Error encrypting NCA \"%s\" header!", ctx->content_id_str); @@ -404,20 +399,22 @@ bool ncaEncryptHeader(NcaContext *ctx) /* Encrypt NCA FS section headers. */ /* Both NCA2 and NCA3 place the NCA FS section headers right after the NCA header. However, NCA0 places them at the start sector from each NCA FS section. */ - /* NCA0 FS section headers will be encrypted in-place, but they need to be written to their proper offsets. */ for(u8 i = 0; i < NCA_FS_HEADER_COUNT; i++) { + NcaFsInfo *fs_info = &(ctx->header.fs_info[i]); + NcaFsSectionContext *fs_ctx = &(ctx->fs_ctx[i]); + /* Don't proceed if this NCA FS section isn't populated. */ - if (ctx->format_version != NcaVersion_Nca3 && !ncaIsFsInfoEntryValid(&(ctx->header.fs_info[i]))) continue; + if (ctx->format_version != NcaVersion_Nca3 && !ncaIsFsInfoEntryValid(fs_info)) continue; /* The AES-XTS sector number for each NCA FS header varies depending on the NCA format version. */ /* NCA3 uses sector number 0 for the NCA header, then increases it with each new sector (e.g. making the first NCA FS section header use sector number 2, and so on). */ /* NCA2 uses sector number 0 for each NCA FS section header. */ /* NCA0 uses sector number 0 for the NCA header, then uses sector number 0 for the rest of the data and increases it with each new sector. */ Aes128XtsContext *aes_xts_ctx = (ctx->format_version != NcaVersion_Nca0 ? &hdr_aes_ctx : &nca0_fs_header_ctx); - u64 sector = (ctx->format_version == NcaVersion_Nca3 ? (2U + i) : (ctx->format_version == NcaVersion_Nca2 ? 0 : (ctx->header.fs_info[i].start_sector - 2))); + u64 sector = (ctx->format_version == NcaVersion_Nca3 ? (2U + i) : (ctx->format_version == NcaVersion_Nca2 ? 0 : (fs_info->start_sector - 2))); - crypt_res = aes128XtsNintendoCrypt(aes_xts_ctx, &(ctx->fs_contexts[i].header), &(ctx->fs_contexts[i].header), sizeof(NcaFsHeader), sector, NCA_AES_XTS_SECTOR_SIZE, true); + crypt_res = aes128XtsNintendoCrypt(aes_xts_ctx, &(fs_ctx->encrypted_header), &(fs_ctx->header), sizeof(NcaFsHeader), sector, NCA_AES_XTS_SECTOR_SIZE, true); if (crypt_res != sizeof(NcaFsHeader)) { LOGFILE("Error encrypting NCA%u \"%s\" FS section header #%u!", ctx->format_version, ctx->content_id_str, i); @@ -446,7 +443,7 @@ NX_INLINE bool ncaIsFsInfoEntryValid(NcaFsInfo *fs_info) return (memcmp(&tmp_fs_info, fs_info, sizeof(NcaFsInfo)) != 0); } -static bool ncaDecryptHeader(NcaContext *ctx) +static bool ncaReadDecryptedHeader(NcaContext *ctx) { if (!ctx || !strlen(ctx->content_id_str) || ctx->content_size < NCA_FULL_HEADER_LENGTH) { @@ -459,11 +456,18 @@ static bool ncaDecryptHeader(NcaContext *ctx) const u8 *header_key = keysGetNcaHeaderKey(); Aes128XtsContext hdr_aes_ctx = {0}, nca0_fs_header_ctx = {0}; + /* Read NCA header. */ + if (!ncaReadContentFile(ctx, &(ctx->encrypted_header), sizeof(NcaHeader), 0)) + { + LOGFILE("Failed to read NCA \"%s\" header!", ctx->content_id_str); + return false; + } + /* Prepare NCA header AES-128-XTS context. */ aes128XtsContextCreate(&hdr_aes_ctx, header_key, header_key + AES_128_KEY_SIZE, false); /* Decrypt NCA header. */ - crypt_res = aes128XtsNintendoCrypt(&hdr_aes_ctx, &(ctx->header), &(ctx->header), sizeof(NcaHeader), 0, NCA_AES_XTS_SECTOR_SIZE, false); + crypt_res = aes128XtsNintendoCrypt(&hdr_aes_ctx, &(ctx->header), &(ctx->encrypted_header), sizeof(NcaHeader), 0, NCA_AES_XTS_SECTOR_SIZE, false); magic = __builtin_bswap32(ctx->header.magic); if (crypt_res != sizeof(NcaHeader) || (magic != NCA_NCA3_MAGIC && magic != NCA_NCA2_MAGIC && magic != NCA_NCA0_MAGIC) || ctx->header.content_size != ctx->content_size) @@ -476,6 +480,7 @@ static bool ncaDecryptHeader(NcaContext *ctx) ctx->format_version = (magic == NCA_NCA3_MAGIC ? NcaVersion_Nca3 : (magic == NCA_NCA2_MAGIC ? NcaVersion_Nca2 : NcaVersion_Nca0)); ctx->key_generation = ncaGetKeyGenerationValue(ctx); ctx->rights_id_available = ncaCheckRightsIdAvailability(ctx); + sha256CalculateHash(ctx->header_hash, &(ctx->header), sizeof(NcaHeader)); /* Decrypt NCA key area (if needed). */ if (!ctx->rights_id_available && !ncaDecryptKeyArea(ctx)) @@ -491,12 +496,15 @@ static bool ncaDecryptHeader(NcaContext *ctx) /* Both NCA2 and NCA3 place the NCA FS section headers right after the NCA header. However, NCA0 places them at the start sector from each NCA FS section. */ for(u8 i = 0; i < NCA_FS_HEADER_COUNT; i++) { + NcaFsInfo *fs_info = &(ctx->header.fs_info[i]); + NcaFsSectionContext *fs_ctx = &(ctx->fs_ctx[i]); + /* Don't proceed if this NCA FS section isn't populated. */ - if (ctx->format_version != NcaVersion_Nca3 && !ncaIsFsInfoEntryValid(&(ctx->header.fs_info[i]))) continue; + if (ctx->format_version != NcaVersion_Nca3 && !ncaIsFsInfoEntryValid(fs_info)) continue; /* Read NCA FS section header. */ - u64 fs_header_offset = (ctx->format_version != NcaVersion_Nca0 ? (sizeof(NcaHeader) + (i * sizeof(NcaFsHeader))) : NCA_FS_SECTOR_OFFSET(ctx->header.fs_info[i].start_sector)); - if (!ncaReadContentFile(ctx, &(ctx->fs_contexts[i].header), sizeof(NcaFsHeader), fs_header_offset)) + u64 fs_header_offset = (ctx->format_version != NcaVersion_Nca0 ? (sizeof(NcaHeader) + (i * sizeof(NcaFsHeader))) : NCA_FS_SECTOR_OFFSET(fs_info->start_sector)); + if (!ncaReadContentFile(ctx, &(fs_ctx->encrypted_header), sizeof(NcaFsHeader), fs_header_offset)) { LOGFILE("Failed to read NCA%u \"%s\" FS section header #%u at offset 0x%lX!", ctx->format_version, ctx->content_id_str, i, fs_header_offset); return false; @@ -507,9 +515,9 @@ static bool ncaDecryptHeader(NcaContext *ctx) /* NCA2 uses sector number 0 for each NCA FS section header. */ /* NCA0 uses sector number 0 for the NCA header, then uses sector number 0 for the rest of the data and increases it with each new sector. */ Aes128XtsContext *aes_xts_ctx = (ctx->format_version != NcaVersion_Nca0 ? &hdr_aes_ctx : &nca0_fs_header_ctx); - u64 sector = (ctx->format_version == NcaVersion_Nca3 ? (2U + i) : (ctx->format_version == NcaVersion_Nca2 ? 0 : (ctx->header.fs_info[i].start_sector - 2))); + u64 sector = (ctx->format_version == NcaVersion_Nca3 ? (2U + i) : (ctx->format_version == NcaVersion_Nca2 ? 0 : (fs_info->start_sector - 2))); - crypt_res = aes128XtsNintendoCrypt(aes_xts_ctx, &(ctx->fs_contexts[i].header), &(ctx->fs_contexts[i].header), sizeof(NcaFsHeader), sector, NCA_AES_XTS_SECTOR_SIZE, false); + crypt_res = aes128XtsNintendoCrypt(aes_xts_ctx, &(fs_ctx->header), &(fs_ctx->encrypted_header), sizeof(NcaFsHeader), sector, NCA_AES_XTS_SECTOR_SIZE, false); if (crypt_res != sizeof(NcaFsHeader)) { LOGFILE("Error decrypting NCA%u \"%s\" FS section header #%u!", ctx->format_version, ctx->content_id_str, i); diff --git a/source/nca.h b/source/nca.h index 8db0478..8b66a6b 100644 --- a/source/nca.h +++ b/source/nca.h @@ -270,10 +270,14 @@ typedef enum { NcaFsSectionType_Invalid = 4 } NcaFsSectionType; +/// Unlike NCA contexts, we don't need to keep a hash for the NCA FS section header in NCA FS section contexts. +/// This is because the functions that modify the NCA FS section header also update the NCA FS section header hash stored in the NCA header. typedef struct { bool enabled; void *nca_ctx; ///< NcaContext. Used to perform NCA reads. - NcaFsHeader header; ///< NCA FS section header. + 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. u8 section_num; u64 section_offset; u64 section_size; @@ -299,26 +303,28 @@ typedef struct { } NcaDecryptedKeyArea; typedef struct { - u8 storage_id; ///< NcmStorageId. - NcmContentStorage *ncm_storage; ///< Pointer to a NcmContentStorage instance. Used to read NCA data from eMMC/SD. - u64 gamecard_offset; ///< Used to read NCA data from a gamecard using a FsStorage instance when storage_id == NcmStorageId_GameCard. - NcmContentId content_id; ///< Also used to read NCA data. + u8 storage_id; ///< NcmStorageId. + NcmContentStorage *ncm_storage; ///< Pointer to a NcmContentStorage instance. Used to read NCA data from eMMC/SD. + u64 gamecard_offset; ///< Used to read NCA data from a gamecard using a FsStorage instance when storage_id == NcmStorageId_GameCard. + NcmContentId content_id; ///< Also used to read NCA data. char content_id_str[0x21]; - u8 hash[SHA256_HASH_SIZE]; ///< Manually calculated (if needed). + u8 hash[SHA256_HASH_SIZE]; ///< Manually calculated (if needed). char hash_str[0x41]; - u8 format_version; ///< NcaVersion. - u8 content_type; ///< NcmContentType. Retrieved from NcmContentInfo. - u64 content_size; ///< Retrieved from NcmContentInfo. - u8 key_generation; ///< NcaKeyGenerationOld / NcaKeyGeneration. Retrieved from the decrypted header. - u8 id_offset; ///< Retrieved from NcmContentInfo. + u8 format_version; ///< NcaVersion. + u8 content_type; ///< NcmContentType. Retrieved from NcmContentInfo. + u64 content_size; ///< Retrieved from NcmContentInfo. + u8 key_generation; ///< NcaKeyGenerationOld / NcaKeyGeneration. Retrieved from the decrypted header. + u8 id_offset; ///< Retrieved from NcmContentInfo. bool rights_id_available; bool titlekey_retrieved; - u8 titlekey[AES_128_KEY_SIZE]; ///< Decrypted titlekey from the ticket. - NcaHeader header; ///< NCA header. - u8 header_hash[SHA256_HASH_SIZE]; ///< NCA header hash. Used to determine if it's necessary to replace the NCA header while dumping this NCA. - NcaFsSectionContext fs_contexts[NCA_FS_HEADER_COUNT]; + u8 titlekey[AES_128_KEY_SIZE]; ///< Decrypted titlekey from the ticket. + NcaHeader header; ///< Plaintext NCA header. + 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. + 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. + void *content_type_ctx; ///< Pointer to a content type context (e.g. ContentMetaContext, ProgramInfoContext, NacpContext, LegalInfoContext). Set to NULL if unused. } NcaContext; typedef struct { @@ -362,11 +368,12 @@ bool ncaReadFsSection(NcaFsSectionContext *ctx, void *out, u64 read_size, u64 of /// Input offset must be relative to the start of the NCA FS section. bool ncaReadAesCtrExStorageFromBktrSection(NcaFsSectionContext *ctx, void *out, u64 read_size, u64 offset, u32 ctr_val); -/// Returns a pointer to a heap-allocated buffer used to encrypt the input plaintext data, based on the encryption type used by the input NCA FS section, as well as its offset and size. +/// Returns a pointer to a dynamically allocated buffer used to encrypt the input plaintext data, based on the encryption type used by the input NCA FS section, as well as its offset and size. /// Input offset must be relative to the start of the NCA FS section. /// Output size and offset are guaranteed to be aligned to the AES sector size used by the encryption type from the FS section. /// Output offset is relative to the start of the NCA content file, making it easier to use the output encrypted block to seamlessly replace data while dumping a NCA. /// This function isn't compatible with Patch RomFS sections. +/// Used internally by both ncaGenerateHierarchicalSha256Patch() and ncaGenerateHierarchicalIntegrityPatch(). void *ncaGenerateEncryptedFsSectionBlock(NcaFsSectionContext *ctx, const void *data, u64 data_size, u64 data_offset, u64 *out_block_size, u64 *out_block_offset); /// Generates HierarchicalSha256 FS section patch data, which can be used to seamlessly replace NCA data. @@ -403,7 +410,8 @@ const char *ncaGetFsSectionTypeName(NcaFsSectionContext *ctx); /// 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); -/// Encrypts NCA header and NCA FS headers from a NCA context. +/// Encrypts NCA header and NCA FS headers. +/// The 'encrypted_header' member from the NCA context and its underlying NCA FS section contexts is updated by this function. bool ncaEncryptHeader(NcaContext *ctx); /// Updates the content ID and hash from a NCA context using a provided SHA-256 checksum. diff --git a/source/npdm.c b/source/npdm.c index 2827b12..0ee1a46 100644 --- a/source/npdm.c +++ b/source/npdm.c @@ -156,14 +156,14 @@ bool npdmInitializeContext(NpdmContext *out, PartitionFileSystemContext *pfs_ctx goto end; } - if (out->acid_header->fs_access_control_offset < sizeof(NpdmAcidHeader) || out->acid_header->fs_access_control_size < sizeof(NpdmAcidFsAccessControlDescriptor) || \ + if (out->acid_header->fs_access_control_offset < sizeof(NpdmAcidHeader) || out->acid_header->fs_access_control_size < sizeof(NpdmFsAccessControlDescriptor) || \ (out->acid_header->fs_access_control_offset + out->acid_header->fs_access_control_size) > out->meta_header->acid_size) { LOGFILE("Invalid ACID FsAccessControl offset/size! (0x%08X, 0x%08X).", out->acid_header->fs_access_control_offset, out->acid_header->fs_access_control_size); goto end; } - out->acid_fac_descriptor = (NpdmAcidFsAccessControlDescriptor*)(out->raw_data + out->meta_header->acid_offset + out->acid_header->fs_access_control_offset); + out->acid_fac_descriptor = (NpdmFsAccessControlDescriptor*)(out->raw_data + out->meta_header->acid_offset + out->acid_header->fs_access_control_offset); if (out->acid_header->srv_access_control_size) { @@ -212,14 +212,14 @@ bool npdmInitializeContext(NpdmContext *out, PartitionFileSystemContext *pfs_ctx goto end; } - if (out->aci_header->fs_access_control_offset < sizeof(NpdmAciHeader) || out->aci_header->fs_access_control_size < sizeof(NpdmAciFsAccessControlDescriptor) || \ + if (out->aci_header->fs_access_control_offset < sizeof(NpdmAciHeader) || out->aci_header->fs_access_control_size < sizeof(NpdmFsAccessControlData) || \ (out->aci_header->fs_access_control_offset + out->aci_header->fs_access_control_size) > out->meta_header->aci_size) { LOGFILE("Invalid ACI0 FsAccessControl offset/size! (0x%08X, 0x%08X).", out->aci_header->fs_access_control_offset, out->aci_header->fs_access_control_size); goto end; } - out->aci_fac_descriptor = (NpdmAciFsAccessControlDescriptor*)(out->raw_data + out->meta_header->aci_offset + out->aci_header->fs_access_control_offset); + out->aci_fac_data = (NpdmFsAccessControlData*)(out->raw_data + out->meta_header->aci_offset + out->aci_header->fs_access_control_offset); if (out->aci_header->srv_access_control_size) { diff --git a/source/npdm.h b/source/npdm.h index a0d2e5a..af6fd2d 100644 --- a/source/npdm.h +++ b/source/npdm.h @@ -51,23 +51,23 @@ typedef struct { /// This is the start of every NPDM file. /// This is followed by ACID and ACI0 sections, both with variable offsets and sizes. typedef struct { - u32 magic; ///< "NPDM". + u32 magic; ///< "NPDM". u8 acid_signature_key_generation; u8 reserved_1[0x7]; NpdmMetaFlags flags; u8 reserved_2; - u8 main_thread_priority; ///< Must not exceed NPDM_MAIN_THREAD_MAX_PRIORITY. - u8 main_thread_core_number; ///< Must not exceed NPDM_MAIN_THREAD_MAX_CORE_NUMBER. + u8 main_thread_priority; ///< Must not exceed NPDM_MAIN_THREAD_MAX_PRIORITY. + u8 main_thread_core_number; ///< Must not exceed NPDM_MAIN_THREAD_MAX_CORE_NUMBER. u8 reserved_3[0x4]; - u32 system_resource_size; ///< Must not exceed NPDM_SYSTEM_RESOURCE_MAX_SIZE. + u32 system_resource_size; ///< Must not exceed NPDM_SYSTEM_RESOURCE_MAX_SIZE. VersionType1 version; - u32 main_thread_stack_size; ///< Must be aligned to NPDM_MAIN_THREAD_STACK_SIZE_ALIGNMENT. - char name[0x10]; ///< Usually set to "Application". - char product_code[0x10]; ///< Usually zeroed out. + u32 main_thread_stack_size; ///< Must be aligned to NPDM_MAIN_THREAD_STACK_SIZE_ALIGNMENT. + char name[0x10]; ///< Usually set to "Application". + char product_code[0x10]; ///< Usually zeroed out. u8 reserved_4[0x30]; - u32 aci_offset; ///< Offset value relative to the start of this header. + u32 aci_offset; ///< Offset value relative to the start of this header. u32 aci_size; - u32 acid_offset; ///< Offset value relative to the start of this header. + u32 acid_offset; ///< Offset value relative to the start of this header. u32 acid_size; } NpdmMetaHeader; @@ -90,27 +90,27 @@ typedef struct { } NpdmAcidFlags; /// This is the start of an ACID section. -/// This is followed by FsAccessControl (ACID), SrvAccessControl and KernelCapability descriptors, each one aligned to a 0x10 byte boundary using zero padding (if needed). +/// This is followed by FsAccessControl, SrvAccessControl and KernelCapability descriptors, each one aligned to a 0x10 byte boundary using zero padding (if needed). typedef struct { - u8 signature[0x100]; ///< RSA-2048-PSS with SHA-256 signature over the rest of the ACID section, using the value from the 'size' member. - u8 public_key[0x100]; ///< RSA public key used to verify the ACID signature from the Program NCA header. - u32 magic; ///< "ACID". - u32 size; ///< Must be equal to ACID section size from the META header minus 0x100 (ACID signature size). + u8 signature[0x100]; ///< RSA-2048-PSS with SHA-256 signature over the rest of the ACID section, using the value from the 'size' member. + u8 public_key[0x100]; ///< RSA public key used to verify the ACID signature from the Program NCA header. + u32 magic; ///< "ACID". + u32 size; ///< Must be equal to ACID section size from the META header minus 0x100 (ACID signature size). u8 reserved_1[0x4]; NpdmAcidFlags flags; u64 program_id_min; u64 program_id_max; - u32 fs_access_control_offset; ///< Offset value relative to the start of this header. + u32 fs_access_control_offset; ///< Offset value relative to the start of this header. u32 fs_access_control_size; - u32 srv_access_control_offset; ///< Offset value relative to the start of this header. + u32 srv_access_control_offset; ///< Offset value relative to the start of this header. u32 srv_access_control_size; - u32 kernel_capability_offset; ///< Offset value relative to the start of this header. + u32 kernel_capability_offset; ///< Offset value relative to the start of this header. u32 kernel_capability_size; u8 reserved_2[0x8]; } NpdmAcidHeader; /// This is the start of an ACI0 section. -/// This is followed by FsAccessControl (ACI0), SrvAccessControl and KernelCapability descriptors, each one aligned to a 0x10 byte boundary using zero padding (if needed). +/// This is followed by a FsAccessControl data block, as well as SrvAccessControl and KernelCapability descriptors, each one aligned to a 0x10 byte boundary using zero padding (if needed). typedef struct { u32 magic; u8 reserved_1[0xC]; @@ -167,13 +167,13 @@ typedef enum { NpdmFsAccessControlFlags_FullPermission = BIT_LONG(63) } NpdmFsAccessControlFlags; -/// AcidFsAccessControl descriptor. Part of the ACID section body. +/// FsAccessControl descriptor. Part of the ACID section body. /// This is followed by: /// * 'content_owner_id_count' content owner IDs. /// * 'save_data_owner_id_count' save data owner IDs. #pragma pack(push, 1) typedef struct { - u8 version; ///< Always non-zero. Usually set to 1. + u8 version; ///< Always non-zero. Usually set to 1. u8 content_owner_id_count; u8 save_data_owner_id_count; u8 reserved; @@ -182,13 +182,13 @@ typedef struct { u64 content_owner_id_max; u64 save_data_owner_id_min; u64 save_data_owner_id_max; -} NpdmAcidFsAccessControlDescriptor; +} NpdmFsAccessControlDescriptor; #pragma pack(pop) -/// AciFsAccessControl descriptor. Part of the ACI0 section body. +/// FsAccessControl data. Part of the ACI0 section body. /// This is followed by: -/// * A NpdmAciFsAccessControlDescriptorContentOwnerBlock if 'content_owner_info_size' is greater than zero. -/// * A NpdmAciFsAccessControlDescriptorSaveDataOwnerBlock if 'save_data_owner_info_size' is greater than zero. +/// * A NpdmFsAccessControlDataContentOwnerBlock if 'content_owner_info_size' is greater than zero. +/// * A NpdmFsAccessControlDataSaveDataOwnerBlock if 'save_data_owner_info_size' is greater than zero. /// * If available, this block is padded to a 0x4-byte boundary and followed by 'save_data_owner_id_count' save data owner IDs. #pragma pack(push, 1) typedef struct { @@ -199,26 +199,26 @@ typedef struct { u32 content_owner_info_size; u32 save_data_owner_info_offset; ///< Relative to the start of this block. Only valid if 'save_data_owner_info_size' is greater than 0. u32 save_data_owner_info_size; -} NpdmAciFsAccessControlDescriptor; +} NpdmFsAccessControlData; #pragma pack(pop) -/// Placed after NpdmAciFsAccessControlDescriptor if its 'content_owner_info_size' member is greater than zero. +/// Placed after NpdmFsAccessControlData if its 'content_owner_info_size' member is greater than zero. typedef struct { u32 content_owner_id_count; u64 content_owner_id[]; ///< 'content_owner_id_count' content owned IDs. -} NpdmAciFsAccessControlDescriptorContentOwnerBlock; +} NpdmFsAccessControlDataContentOwnerBlock; typedef enum { NpdmAccessibility_Read = BIT(0), NpdmAccessibility_Write = BIT(1) } NpdmAccessibility; -/// Placed after NpdmAciFsAccessControlDescriptor / NpdmAciFsAccessControlDescriptorContentOwnerBlock if the 'content_owner_info_size' member from NpdmAciFsAccessControlDescriptor is greater than zero. +/// Placed after NpdmFsAccessControlData / NpdmFsAccessControlDataContentOwnerBlock if the 'content_owner_info_size' member from NpdmFsAccessControlData is greater than zero. /// If available, this block is padded to a 0x4-byte boundary and followed by 'save_data_owner_id_count' save data owner IDs. typedef struct { u32 save_data_owner_id_count; u8 accessibility[]; ///< 'save_data_owner_id_count' NpdmAccessibility fields. -} NpdmAciFsAccessControlDescriptorSaveDataOwnerBlock; +} NpdmFsAccessControlDataSaveDataOwnerBlock; /// SrvAccessControl descriptor. Part of the ACID and ACI0 section bodies. /// This descriptor is composed of a variable number of NpdmSrvAccessControlDescriptorEntry elements, each one with a variable size. @@ -543,11 +543,11 @@ typedef struct { u8 raw_data_hash[SHA256_HASH_SIZE]; ///< SHA-256 checksum calculated over the whole raw NPDM. Used to determine if NcaHierarchicalSha256Patch generation is truly needed. NpdmMetaHeader *meta_header; ///< Pointer to the NpdmMetaHeader within 'raw_data'. NpdmAcidHeader *acid_header; ///< Pointer to the NpdmAcidHeader within 'raw_data'. - NpdmAcidFsAccessControlDescriptor *acid_fac_descriptor; ///< Pointer to the NpdmAcidFsAccessControlDescriptor within the NPDM ACID section. + NpdmFsAccessControlDescriptor *acid_fac_descriptor; ///< Pointer to the NpdmFsAccessControlDescriptor within the NPDM ACID section. NpdmSrvAccessControlDescriptorEntry *acid_sac_descriptor; ///< Pointer to the first NpdmSrvAccessControlDescriptorEntry within the NPDM ACID section, if available. NpdmKernelCapabilityDescriptorEntry *acid_kc_descriptor; ///< Pointer to the first NpdmKernelCapabilityDescriptorEntry within the NPDM ACID section, if available. NpdmAciHeader *aci_header; ///< Pointer to the NpdmAciHeader within 'raw_data'. - NpdmAciFsAccessControlDescriptor *aci_fac_descriptor; ///< Pointer to the NpdmAciFsAccessControlDescriptor within the NPDM ACI0 section. + NpdmFsAccessControlData *aci_fac_data; ///< Pointer to the NpdmFsAccessControlData within the NPDM ACI0 section. NpdmSrvAccessControlDescriptorEntry *aci_sac_descriptor; ///< Pointer to the first NpdmSrvAccessControlDescriptorEntry within the NPDM ACI0 section, if available. NpdmKernelCapabilityDescriptorEntry *aci_kc_descriptor; ///< Pointer to the first NpdmKernelCapabilityDescriptorEntry within the NPDM ACI0 section, if available. } NpdmContext; @@ -573,7 +573,7 @@ NX_INLINE bool npdmIsValidContext(NpdmContext *npdm_ctx) return (npdm_ctx && npdm_ctx->pfs_ctx && npdm_ctx->pfs_entry && npdm_ctx->raw_data && npdm_ctx->raw_data_size && npdm_ctx->meta_header && npdm_ctx->acid_header && npdm_ctx->acid_fac_descriptor && \ ((npdm_ctx->acid_header->srv_access_control_size && npdm_ctx->acid_sac_descriptor) || (!npdm_ctx->acid_header->srv_access_control_size && !npdm_ctx->acid_sac_descriptor)) && \ ((npdm_ctx->acid_header->kernel_capability_size && npdm_ctx->acid_kc_descriptor) || (!npdm_ctx->acid_header->kernel_capability_size && !npdm_ctx->acid_kc_descriptor)) && \ - npdm_ctx->aci_header && npdm_ctx->aci_fac_descriptor && \ + npdm_ctx->aci_header && npdm_ctx->aci_fac_data && \ ((npdm_ctx->aci_header->srv_access_control_size && npdm_ctx->aci_sac_descriptor) || (!npdm_ctx->aci_header->srv_access_control_size && !npdm_ctx->aci_sac_descriptor)) && \ ((npdm_ctx->aci_header->kernel_capability_size && npdm_ctx->aci_kc_descriptor) || (!npdm_ctx->aci_header->kernel_capability_size && !npdm_ctx->aci_kc_descriptor))); } diff --git a/source/program_info.c b/source/program_info.c index 3398f6c..16bada1 100644 --- a/source/program_info.c +++ b/source/program_info.c @@ -71,7 +71,7 @@ bool programInfoInitializeContext(ProgramInfoContext *out, NcaContext *nca_ctx) programInfoFreeContext(out); /* Initialize Partition FS context. */ - if (!pfsInitializeContext(&(out->pfs_ctx), &(nca_ctx->fs_contexts[0]))) + if (!pfsInitializeContext(&(out->pfs_ctx), &(nca_ctx->fs_ctx[0]))) { LOGFILE("Failed to initialize Partition FS context!"); goto end; @@ -552,32 +552,32 @@ static bool programInfoIsElfSymbolValid(u8 *dynsym_ptr, char *dynstr_base_ptr, u static bool programInfoAddFsAccessControlDataToAuthoringToolXml(char **xml_buf, u64 *xml_buf_size, ProgramInfoContext *program_info_ctx) { - NpdmAciFsAccessControlDescriptor *aci_fac_descriptor = NULL; - NpdmAciFsAccessControlDescriptorSaveDataOwnerBlock *save_data_owner_block = NULL; + NpdmFsAccessControlData *aci_fac_data = NULL; + NpdmFsAccessControlDataSaveDataOwnerBlock *save_data_owner_block = NULL; u64 *save_data_owner_ids = NULL; - bool success = false, fac_data_available = false; + bool success = false, sdo_data_available = false; - if (!xml_buf || !xml_buf_size || !program_info_ctx || !(aci_fac_descriptor = program_info_ctx->npdm_ctx.aci_fac_descriptor)) + if (!xml_buf || !xml_buf_size || !program_info_ctx || !(aci_fac_data = program_info_ctx->npdm_ctx.aci_fac_data)) { LOGFILE("Invalid parameters!"); return false; } - /* Check if there's save data owner data available in the FS access control data descriptor from the ACI0 section in the NPDM. */ - fac_data_available = (aci_fac_descriptor->save_data_owner_info_offset >= sizeof(NpdmAciFsAccessControlDescriptor) && aci_fac_descriptor->save_data_owner_info_size); - if (!fac_data_available) goto end; + /* Check if there's save data owner data available in the FS access control data region from the ACI0 section in the NPDM. */ + sdo_data_available = (aci_fac_data->save_data_owner_info_offset >= sizeof(NpdmFsAccessControlData) && aci_fac_data->save_data_owner_info_size); + if (!sdo_data_available) goto end; /* Get save data owner block and check the ID count. */ - save_data_owner_block = (NpdmAciFsAccessControlDescriptorSaveDataOwnerBlock*)((u8*)aci_fac_descriptor + aci_fac_descriptor->save_data_owner_info_offset); + save_data_owner_block = (NpdmFsAccessControlDataSaveDataOwnerBlock*)((u8*)aci_fac_data + aci_fac_data->save_data_owner_info_offset); if (!save_data_owner_block->save_data_owner_id_count) { - fac_data_available = false; + sdo_data_available = false; goto end; } /* Get save data owner IDs. */ /* Padding to a 0x4-byte boundary is needed. Each accessibility field takes up a single byte, so we can get away with it by aligning the ID count. */ - save_data_owner_ids = (u64*)((u8*)save_data_owner_block + sizeof(NpdmAciFsAccessControlDescriptorSaveDataOwnerBlock) + ALIGN_UP(save_data_owner_block->save_data_owner_id_count, 0x4)); + save_data_owner_ids = (u64*)((u8*)save_data_owner_block + sizeof(NpdmFsAccessControlDataSaveDataOwnerBlock) + ALIGN_UP(save_data_owner_block->save_data_owner_id_count, 0x4)); if (!utilsAppendFormattedStringToBuffer(xml_buf, xml_buf_size, " \n")) goto end; @@ -597,7 +597,7 @@ static bool programInfoAddFsAccessControlDataToAuthoringToolXml(char **xml_buf, end: /* Append an empty XML element if no FS access control data exists. */ - if (!success && !fac_data_available) success = utilsAppendFormattedStringToBuffer(xml_buf, xml_buf_size, " \n"); + if (!success && !sdo_data_available) success = utilsAppendFormattedStringToBuffer(xml_buf, xml_buf_size, " \n"); return success; } diff --git a/todo.txt b/todo.txt index 5e09291..8288a0c 100644 --- a/todo.txt +++ b/todo.txt @@ -1,7 +1,6 @@ todo: nca: functions for fs section lookup? (could just let the user choose...) - nca: add encrypted headers to nca context, avoid re-encrypting plaintext headers in place nca: function to write re-encrypted nca headers / nca fs headers (don't forget nca0 please) tik: option to wipe elicense property mask