From 4783ad54de0d7f1add443914e304fe08409568ff Mon Sep 17 00:00:00 2001 From: Lioncash Date: Tue, 16 Oct 2018 12:08:15 -0400 Subject: [PATCH 1/5] content_archive: Pass and take NCASectionHeader instance by reference Each header is 512 bytes in size, which is kind of an excessive amount to copy all the time when it's possible to avoid doing so. --- src/core/file_sys/content_archive.cpp | 4 ++-- src/core/file_sys/content_archive.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/core/file_sys/content_archive.cpp b/src/core/file_sys/content_archive.cpp index 6dcec7816..0872a378b 100644 --- a/src/core/file_sys/content_archive.cpp +++ b/src/core/file_sys/content_archive.cpp @@ -167,7 +167,7 @@ boost::optional NCA::GetTitlekey() { return titlekey; } -VirtualFile NCA::Decrypt(NCASectionHeader s_header, VirtualFile in, u64 starting_offset) { +VirtualFile NCA::Decrypt(const NCASectionHeader& s_header, VirtualFile in, u64 starting_offset) { if (!encrypted) return in; @@ -295,7 +295,7 @@ NCA::NCA(VirtualFile file_, VirtualFile bktr_base_romfs_, u64 bktr_base_ivfc_off ivfc_offset = 0; for (std::ptrdiff_t i = 0; i < number_sections; ++i) { - auto section = sections[i]; + const auto& section = sections[i]; if (section.raw.header.filesystem_type == NCASectionFilesystemType::ROMFS) { const std::size_t base_offset = diff --git a/src/core/file_sys/content_archive.h b/src/core/file_sys/content_archive.h index f9f66cae9..d02ea4f4b 100644 --- a/src/core/file_sys/content_archive.h +++ b/src/core/file_sys/content_archive.h @@ -109,7 +109,7 @@ private: u8 GetCryptoRevision() const; boost::optional GetKeyAreaKey(NCASectionCryptoType type) const; boost::optional GetTitlekey(); - VirtualFile Decrypt(NCASectionHeader header, VirtualFile in, u64 starting_offset); + VirtualFile Decrypt(const NCASectionHeader& header, VirtualFile in, u64 starting_offset); std::vector dirs; std::vector files; From d6604fa765890fd7583440362d4e0bdbb82a8215 Mon Sep 17 00:00:00 2001 From: Lioncash Date: Tue, 16 Oct 2018 12:12:50 -0400 Subject: [PATCH 2/5] content_archive: Split loading into separate functions The constructor alone is pretty large, the reading code should be split into its consistuent parts to make it easier to understand it without having to build a mental model of a 300+ line function. --- src/core/file_sys/content_archive.cpp | 528 ++++++++++++++------------ src/core/file_sys/content_archive.h | 15 +- 2 files changed, 290 insertions(+), 253 deletions(-) diff --git a/src/core/file_sys/content_archive.cpp b/src/core/file_sys/content_archive.cpp index 0872a378b..0f7cc3fbf 100644 --- a/src/core/file_sys/content_archive.cpp +++ b/src/core/file_sys/content_archive.cpp @@ -102,6 +102,284 @@ bool IsValidNCA(const NCAHeader& header) { return header.magic == Common::MakeMagic('N', 'C', 'A', '3'); } +NCA::NCA(VirtualFile file_, VirtualFile bktr_base_romfs_, u64 bktr_base_ivfc_offset) + : file(std::move(file_)), + bktr_base_romfs(bktr_base_romfs_ ? std::move(bktr_base_romfs_) : nullptr) { + if (file == nullptr) { + status = Loader::ResultStatus::ErrorNullFile; + return; + } + + if (sizeof(NCAHeader) != file->ReadObject(&header)) { + LOG_ERROR(Loader, "File reader errored out during header read."); + status = Loader::ResultStatus::ErrorBadNCAHeader; + return; + } + + if (!HandlePotentialHeaderDecryption()) { + return; + } + + has_rights_id = std::find_if_not(header.rights_id.begin(), header.rights_id.end(), + [](char c) { return c == '\0'; }) != header.rights_id.end(); + + const std::vector sections = ReadSectionHeaders(); + is_update = std::any_of(sections.begin(), sections.end(), [](const NCASectionHeader& header) { + return header.raw.header.crypto_type == NCASectionCryptoType::BKTR; + }); + + if (!ReadSections(sections, bktr_base_ivfc_offset)) { + return; + } + + status = Loader::ResultStatus::Success; +} + +NCA::~NCA() = default; + +bool NCA::CheckSupportedNCA(const NCAHeader& nca_header) { + if (nca_header.magic == Common::MakeMagic('N', 'C', 'A', '2')) { + status = Loader::ResultStatus::ErrorNCA2; + return false; + } + + if (nca_header.magic == Common::MakeMagic('N', 'C', 'A', '0')) { + status = Loader::ResultStatus::ErrorNCA0; + return false; + } + + return true; +} + +bool NCA::HandlePotentialHeaderDecryption() { + if (IsValidNCA(header)) { + return true; + } + + if (!CheckSupportedNCA(header)) { + return false; + } + + NCAHeader dec_header{}; + Core::Crypto::AESCipher cipher( + keys.GetKey(Core::Crypto::S256KeyType::Header), Core::Crypto::Mode::XTS); + cipher.XTSTranscode(&header, sizeof(NCAHeader), &dec_header, 0, 0x200, + Core::Crypto::Op::Decrypt); + if (IsValidNCA(dec_header)) { + header = dec_header; + encrypted = true; + } else { + if (!CheckSupportedNCA(dec_header)) { + return false; + } + + if (keys.HasKey(Core::Crypto::S256KeyType::Header)) { + status = Loader::ResultStatus::ErrorIncorrectHeaderKey; + } else { + status = Loader::ResultStatus::ErrorMissingHeaderKey; + } + return false; + } + + return true; +} + +std::vector NCA::ReadSectionHeaders() const { + const std::ptrdiff_t number_sections = + std::count_if(std::begin(header.section_tables), std::end(header.section_tables), + [](NCASectionTableEntry entry) { return entry.media_offset > 0; }); + + std::vector sections(number_sections); + const auto length_sections = SECTION_HEADER_SIZE * number_sections; + + if (encrypted) { + auto raw = file->ReadBytes(length_sections, SECTION_HEADER_OFFSET); + Core::Crypto::AESCipher cipher( + keys.GetKey(Core::Crypto::S256KeyType::Header), Core::Crypto::Mode::XTS); + cipher.XTSTranscode(raw.data(), length_sections, sections.data(), 2, SECTION_HEADER_SIZE, + Core::Crypto::Op::Decrypt); + } else { + file->ReadBytes(sections.data(), length_sections, SECTION_HEADER_OFFSET); + } + + return sections; +} + +bool NCA::ReadSections(const std::vector& sections, u64 bktr_base_ivfc_offset) { + for (std::size_t i = 0; i < sections.size(); ++i) { + const auto& section = sections[i]; + + if (section.raw.header.filesystem_type == NCASectionFilesystemType::ROMFS) { + if (!ReadRomFSSection(section, header.section_tables[i], bktr_base_ivfc_offset)) { + return false; + } + } else if (section.raw.header.filesystem_type == NCASectionFilesystemType::PFS0) { + if (!ReadPFS0Section(section, header.section_tables[i])) { + return false; + } + } + } + + return true; +} + +bool NCA::ReadRomFSSection(const NCASectionHeader& section, const NCASectionTableEntry& entry, + u64 bktr_base_ivfc_offset) { + const std::size_t base_offset = entry.media_offset * MEDIA_OFFSET_MULTIPLIER; + ivfc_offset = section.romfs.ivfc.levels[IVFC_MAX_LEVEL - 1].offset; + const std::size_t romfs_offset = base_offset + ivfc_offset; + const std::size_t romfs_size = section.romfs.ivfc.levels[IVFC_MAX_LEVEL - 1].size; + auto raw = std::make_shared(file, romfs_size, romfs_offset); + auto dec = Decrypt(section, raw, romfs_offset); + + if (dec == nullptr) { + if (status != Loader::ResultStatus::Success) + return false; + if (has_rights_id) + status = Loader::ResultStatus::ErrorIncorrectTitlekeyOrTitlekek; + else + status = Loader::ResultStatus::ErrorIncorrectKeyAreaKey; + return false; + } + + if (section.raw.header.crypto_type == NCASectionCryptoType::BKTR) { + if (section.bktr.relocation.magic != Common::MakeMagic('B', 'K', 'T', 'R') || + section.bktr.subsection.magic != Common::MakeMagic('B', 'K', 'T', 'R')) { + status = Loader::ResultStatus::ErrorBadBKTRHeader; + return false; + } + + if (section.bktr.relocation.offset + section.bktr.relocation.size != + section.bktr.subsection.offset) { + status = Loader::ResultStatus::ErrorBKTRSubsectionNotAfterRelocation; + return false; + } + + const u64 size = MEDIA_OFFSET_MULTIPLIER * (entry.media_end_offset - entry.media_offset); + if (section.bktr.subsection.offset + section.bktr.subsection.size != size) { + status = Loader::ResultStatus::ErrorBKTRSubsectionNotAtEnd; + return false; + } + + const u64 offset = section.romfs.ivfc.levels[IVFC_MAX_LEVEL - 1].offset; + RelocationBlock relocation_block{}; + if (dec->ReadObject(&relocation_block, section.bktr.relocation.offset - offset) != + sizeof(RelocationBlock)) { + status = Loader::ResultStatus::ErrorBadRelocationBlock; + return false; + } + SubsectionBlock subsection_block{}; + if (dec->ReadObject(&subsection_block, section.bktr.subsection.offset - offset) != + sizeof(RelocationBlock)) { + status = Loader::ResultStatus::ErrorBadSubsectionBlock; + return false; + } + + std::vector relocation_buckets_raw( + (section.bktr.relocation.size - sizeof(RelocationBlock)) / sizeof(RelocationBucketRaw)); + if (dec->ReadBytes(relocation_buckets_raw.data(), + section.bktr.relocation.size - sizeof(RelocationBlock), + section.bktr.relocation.offset + sizeof(RelocationBlock) - offset) != + section.bktr.relocation.size - sizeof(RelocationBlock)) { + status = Loader::ResultStatus::ErrorBadRelocationBuckets; + return false; + } + + std::vector subsection_buckets_raw( + (section.bktr.subsection.size - sizeof(SubsectionBlock)) / sizeof(SubsectionBucketRaw)); + if (dec->ReadBytes(subsection_buckets_raw.data(), + section.bktr.subsection.size - sizeof(SubsectionBlock), + section.bktr.subsection.offset + sizeof(SubsectionBlock) - offset) != + section.bktr.subsection.size - sizeof(SubsectionBlock)) { + status = Loader::ResultStatus::ErrorBadSubsectionBuckets; + return false; + } + + std::vector relocation_buckets(relocation_buckets_raw.size()); + std::transform(relocation_buckets_raw.begin(), relocation_buckets_raw.end(), + relocation_buckets.begin(), &ConvertRelocationBucketRaw); + std::vector subsection_buckets(subsection_buckets_raw.size()); + std::transform(subsection_buckets_raw.begin(), subsection_buckets_raw.end(), + subsection_buckets.begin(), &ConvertSubsectionBucketRaw); + + u32 ctr_low; + std::memcpy(&ctr_low, section.raw.section_ctr.data(), sizeof(ctr_low)); + subsection_buckets.back().entries.push_back({section.bktr.relocation.offset, {0}, ctr_low}); + subsection_buckets.back().entries.push_back({size, {0}, 0}); + + boost::optional key = boost::none; + if (encrypted) { + if (has_rights_id) { + status = Loader::ResultStatus::Success; + key = GetTitlekey(); + if (key == boost::none) { + status = Loader::ResultStatus::ErrorMissingTitlekey; + return false; + } + } else { + key = GetKeyAreaKey(NCASectionCryptoType::BKTR); + if (key == boost::none) { + status = Loader::ResultStatus::ErrorMissingKeyAreaKey; + return false; + } + } + } + + if (bktr_base_romfs == nullptr) { + status = Loader::ResultStatus::ErrorMissingBKTRBaseRomFS; + return false; + } + + auto bktr = std::make_shared( + bktr_base_romfs, std::make_shared(file, romfs_size, base_offset), + relocation_block, relocation_buckets, subsection_block, subsection_buckets, encrypted, + encrypted ? key.get() : Core::Crypto::Key128{}, base_offset, bktr_base_ivfc_offset, + section.raw.section_ctr); + + // BKTR applies to entire IVFC, so make an offset version to level 6 + files.push_back(std::make_shared( + bktr, romfs_size, section.romfs.ivfc.levels[IVFC_MAX_LEVEL - 1].offset)); + } else { + files.push_back(std::move(dec)); + } + + romfs = files.back(); + return true; +} + +bool NCA::ReadPFS0Section(const NCASectionHeader& section, const NCASectionTableEntry& entry) { + const u64 offset = (static_cast(entry.media_offset) * MEDIA_OFFSET_MULTIPLIER) + + section.pfs0.pfs0_header_offset; + const u64 size = MEDIA_OFFSET_MULTIPLIER * (entry.media_end_offset - entry.media_offset); + + auto dec = Decrypt(section, std::make_shared(file, size, offset), offset); + if (dec != nullptr) { + auto npfs = std::make_shared(std::move(dec)); + + if (npfs->GetStatus() == Loader::ResultStatus::Success) { + dirs.push_back(std::move(npfs)); + if (IsDirectoryExeFS(dirs.back())) + exefs = dirs.back(); + } else { + if (has_rights_id) + status = Loader::ResultStatus::ErrorIncorrectTitlekeyOrTitlekek; + else + status = Loader::ResultStatus::ErrorIncorrectKeyAreaKey; + return false; + } + } else { + if (status != Loader::ResultStatus::Success) + return false; + if (has_rights_id) + status = Loader::ResultStatus::ErrorIncorrectTitlekeyOrTitlekek; + else + status = Loader::ResultStatus::ErrorIncorrectKeyAreaKey; + return false; + } + + return true; +} + u8 NCA::GetCryptoRevision() const { u8 master_key_id = header.crypto_type; if (header.crypto_type_2 > master_key_id) @@ -215,256 +493,6 @@ VirtualFile NCA::Decrypt(const NCASectionHeader& s_header, VirtualFile in, u64 s } } -NCA::NCA(VirtualFile file_, VirtualFile bktr_base_romfs_, u64 bktr_base_ivfc_offset) - : file(std::move(file_)), - bktr_base_romfs(bktr_base_romfs_ ? std::move(bktr_base_romfs_) : nullptr) { - status = Loader::ResultStatus::Success; - - if (file == nullptr) { - status = Loader::ResultStatus::ErrorNullFile; - return; - } - - if (sizeof(NCAHeader) != file->ReadObject(&header)) { - LOG_ERROR(Loader, "File reader errored out during header read."); - status = Loader::ResultStatus::ErrorBadNCAHeader; - return; - } - - encrypted = false; - - if (!IsValidNCA(header)) { - if (header.magic == Common::MakeMagic('N', 'C', 'A', '2')) { - status = Loader::ResultStatus::ErrorNCA2; - return; - } - if (header.magic == Common::MakeMagic('N', 'C', 'A', '0')) { - status = Loader::ResultStatus::ErrorNCA0; - return; - } - - NCAHeader dec_header{}; - Core::Crypto::AESCipher cipher( - keys.GetKey(Core::Crypto::S256KeyType::Header), Core::Crypto::Mode::XTS); - cipher.XTSTranscode(&header, sizeof(NCAHeader), &dec_header, 0, 0x200, - Core::Crypto::Op::Decrypt); - if (IsValidNCA(dec_header)) { - header = dec_header; - encrypted = true; - } else { - if (dec_header.magic == Common::MakeMagic('N', 'C', 'A', '2')) { - status = Loader::ResultStatus::ErrorNCA2; - return; - } - if (dec_header.magic == Common::MakeMagic('N', 'C', 'A', '0')) { - status = Loader::ResultStatus::ErrorNCA0; - return; - } - - if (!keys.HasKey(Core::Crypto::S256KeyType::Header)) - status = Loader::ResultStatus::ErrorMissingHeaderKey; - else - status = Loader::ResultStatus::ErrorIncorrectHeaderKey; - return; - } - } - - has_rights_id = std::find_if_not(header.rights_id.begin(), header.rights_id.end(), - [](char c) { return c == '\0'; }) != header.rights_id.end(); - - const std::ptrdiff_t number_sections = - std::count_if(std::begin(header.section_tables), std::end(header.section_tables), - [](NCASectionTableEntry entry) { return entry.media_offset > 0; }); - - std::vector sections(number_sections); - const auto length_sections = SECTION_HEADER_SIZE * number_sections; - - if (encrypted) { - auto raw = file->ReadBytes(length_sections, SECTION_HEADER_OFFSET); - Core::Crypto::AESCipher cipher( - keys.GetKey(Core::Crypto::S256KeyType::Header), Core::Crypto::Mode::XTS); - cipher.XTSTranscode(raw.data(), length_sections, sections.data(), 2, SECTION_HEADER_SIZE, - Core::Crypto::Op::Decrypt); - } else { - file->ReadBytes(sections.data(), length_sections, SECTION_HEADER_OFFSET); - } - - is_update = std::find_if(sections.begin(), sections.end(), [](const NCASectionHeader& header) { - return header.raw.header.crypto_type == NCASectionCryptoType::BKTR; - }) != sections.end(); - ivfc_offset = 0; - - for (std::ptrdiff_t i = 0; i < number_sections; ++i) { - const auto& section = sections[i]; - - if (section.raw.header.filesystem_type == NCASectionFilesystemType::ROMFS) { - const std::size_t base_offset = - header.section_tables[i].media_offset * MEDIA_OFFSET_MULTIPLIER; - ivfc_offset = section.romfs.ivfc.levels[IVFC_MAX_LEVEL - 1].offset; - const std::size_t romfs_offset = base_offset + ivfc_offset; - const std::size_t romfs_size = section.romfs.ivfc.levels[IVFC_MAX_LEVEL - 1].size; - auto raw = std::make_shared(file, romfs_size, romfs_offset); - auto dec = Decrypt(section, raw, romfs_offset); - - if (dec == nullptr) { - if (status != Loader::ResultStatus::Success) - return; - if (has_rights_id) - status = Loader::ResultStatus::ErrorIncorrectTitlekeyOrTitlekek; - else - status = Loader::ResultStatus::ErrorIncorrectKeyAreaKey; - return; - } - - if (section.raw.header.crypto_type == NCASectionCryptoType::BKTR) { - if (section.bktr.relocation.magic != Common::MakeMagic('B', 'K', 'T', 'R') || - section.bktr.subsection.magic != Common::MakeMagic('B', 'K', 'T', 'R')) { - status = Loader::ResultStatus::ErrorBadBKTRHeader; - return; - } - - if (section.bktr.relocation.offset + section.bktr.relocation.size != - section.bktr.subsection.offset) { - status = Loader::ResultStatus::ErrorBKTRSubsectionNotAfterRelocation; - return; - } - - const u64 size = - MEDIA_OFFSET_MULTIPLIER * (header.section_tables[i].media_end_offset - - header.section_tables[i].media_offset); - if (section.bktr.subsection.offset + section.bktr.subsection.size != size) { - status = Loader::ResultStatus::ErrorBKTRSubsectionNotAtEnd; - return; - } - - const u64 offset = section.romfs.ivfc.levels[IVFC_MAX_LEVEL - 1].offset; - RelocationBlock relocation_block{}; - if (dec->ReadObject(&relocation_block, section.bktr.relocation.offset - offset) != - sizeof(RelocationBlock)) { - status = Loader::ResultStatus::ErrorBadRelocationBlock; - return; - } - SubsectionBlock subsection_block{}; - if (dec->ReadObject(&subsection_block, section.bktr.subsection.offset - offset) != - sizeof(RelocationBlock)) { - status = Loader::ResultStatus::ErrorBadSubsectionBlock; - return; - } - - std::vector relocation_buckets_raw( - (section.bktr.relocation.size - sizeof(RelocationBlock)) / - sizeof(RelocationBucketRaw)); - if (dec->ReadBytes(relocation_buckets_raw.data(), - section.bktr.relocation.size - sizeof(RelocationBlock), - section.bktr.relocation.offset + sizeof(RelocationBlock) - - offset) != - section.bktr.relocation.size - sizeof(RelocationBlock)) { - status = Loader::ResultStatus::ErrorBadRelocationBuckets; - return; - } - - std::vector subsection_buckets_raw( - (section.bktr.subsection.size - sizeof(SubsectionBlock)) / - sizeof(SubsectionBucketRaw)); - if (dec->ReadBytes(subsection_buckets_raw.data(), - section.bktr.subsection.size - sizeof(SubsectionBlock), - section.bktr.subsection.offset + sizeof(SubsectionBlock) - - offset) != - section.bktr.subsection.size - sizeof(SubsectionBlock)) { - status = Loader::ResultStatus::ErrorBadSubsectionBuckets; - return; - } - - std::vector relocation_buckets(relocation_buckets_raw.size()); - std::transform(relocation_buckets_raw.begin(), relocation_buckets_raw.end(), - relocation_buckets.begin(), &ConvertRelocationBucketRaw); - std::vector subsection_buckets(subsection_buckets_raw.size()); - std::transform(subsection_buckets_raw.begin(), subsection_buckets_raw.end(), - subsection_buckets.begin(), &ConvertSubsectionBucketRaw); - - u32 ctr_low; - std::memcpy(&ctr_low, section.raw.section_ctr.data(), sizeof(ctr_low)); - subsection_buckets.back().entries.push_back( - {section.bktr.relocation.offset, {0}, ctr_low}); - subsection_buckets.back().entries.push_back({size, {0}, 0}); - - boost::optional key = boost::none; - if (encrypted) { - if (has_rights_id) { - status = Loader::ResultStatus::Success; - key = GetTitlekey(); - if (key == boost::none) { - status = Loader::ResultStatus::ErrorMissingTitlekey; - return; - } - } else { - key = GetKeyAreaKey(NCASectionCryptoType::BKTR); - if (key == boost::none) { - status = Loader::ResultStatus::ErrorMissingKeyAreaKey; - return; - } - } - } - - if (bktr_base_romfs == nullptr) { - status = Loader::ResultStatus::ErrorMissingBKTRBaseRomFS; - return; - } - - auto bktr = std::make_shared( - bktr_base_romfs, std::make_shared(file, romfs_size, base_offset), - relocation_block, relocation_buckets, subsection_block, subsection_buckets, - encrypted, encrypted ? key.get() : Core::Crypto::Key128{}, base_offset, - bktr_base_ivfc_offset, section.raw.section_ctr); - - // BKTR applies to entire IVFC, so make an offset version to level 6 - - files.push_back(std::make_shared( - bktr, romfs_size, section.romfs.ivfc.levels[IVFC_MAX_LEVEL - 1].offset)); - romfs = files.back(); - } else { - files.push_back(std::move(dec)); - romfs = files.back(); - } - } else if (section.raw.header.filesystem_type == NCASectionFilesystemType::PFS0) { - u64 offset = (static_cast(header.section_tables[i].media_offset) * - MEDIA_OFFSET_MULTIPLIER) + - section.pfs0.pfs0_header_offset; - u64 size = MEDIA_OFFSET_MULTIPLIER * (header.section_tables[i].media_end_offset - - header.section_tables[i].media_offset); - auto dec = - Decrypt(section, std::make_shared(file, size, offset), offset); - if (dec != nullptr) { - auto npfs = std::make_shared(std::move(dec)); - - if (npfs->GetStatus() == Loader::ResultStatus::Success) { - dirs.push_back(std::move(npfs)); - if (IsDirectoryExeFS(dirs.back())) - exefs = dirs.back(); - } else { - if (has_rights_id) - status = Loader::ResultStatus::ErrorIncorrectTitlekeyOrTitlekek; - else - status = Loader::ResultStatus::ErrorIncorrectKeyAreaKey; - return; - } - } else { - if (status != Loader::ResultStatus::Success) - return; - if (has_rights_id) - status = Loader::ResultStatus::ErrorIncorrectTitlekeyOrTitlekek; - else - status = Loader::ResultStatus::ErrorIncorrectKeyAreaKey; - return; - } - } - } - - status = Loader::ResultStatus::Success; -} - -NCA::~NCA() = default; - Loader::ResultStatus NCA::GetStatus() const { return status; } diff --git a/src/core/file_sys/content_archive.h b/src/core/file_sys/content_archive.h index d02ea4f4b..e5d3d3c6a 100644 --- a/src/core/file_sys/content_archive.h +++ b/src/core/file_sys/content_archive.h @@ -106,6 +106,15 @@ protected: bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override; private: + bool CheckSupportedNCA(const NCAHeader& header); + bool HandlePotentialHeaderDecryption(); + + std::vector ReadSectionHeaders() const; + bool ReadSections(const std::vector& sections, u64 bktr_base_ivfc_offset); + bool ReadRomFSSection(const NCASectionHeader& section, const NCASectionTableEntry& entry, + u64 bktr_base_ivfc_offset); + bool ReadPFS0Section(const NCASectionHeader& section, const NCASectionTableEntry& entry); + u8 GetCryptoRevision() const; boost::optional GetKeyAreaKey(NCASectionCryptoType type) const; boost::optional GetTitlekey(); @@ -118,15 +127,15 @@ private: VirtualDir exefs = nullptr; VirtualFile file; VirtualFile bktr_base_romfs; - u64 ivfc_offset; + u64 ivfc_offset = 0; NCAHeader header{}; bool has_rights_id{}; Loader::ResultStatus status{}; - bool encrypted; - bool is_update; + bool encrypted = false; + bool is_update = false; Core::Crypto::KeyManager keys; }; From 53e77ffbfe5da01ae4aa9bdac540674f98cd1c6a Mon Sep 17 00:00:00 2001 From: Lioncash Date: Tue, 16 Oct 2018 13:08:42 -0400 Subject: [PATCH 3/5] content_archive: Simplify rights ID check This is the same as using std::any_of with an inverted predicate. --- src/core/file_sys/content_archive.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core/file_sys/content_archive.cpp b/src/core/file_sys/content_archive.cpp index 0f7cc3fbf..a6bd3369c 100644 --- a/src/core/file_sys/content_archive.cpp +++ b/src/core/file_sys/content_archive.cpp @@ -120,8 +120,8 @@ NCA::NCA(VirtualFile file_, VirtualFile bktr_base_romfs_, u64 bktr_base_ivfc_off return; } - has_rights_id = std::find_if_not(header.rights_id.begin(), header.rights_id.end(), - [](char c) { return c == '\0'; }) != header.rights_id.end(); + has_rights_id = std::any_of(header.rights_id.begin(), header.rights_id.end(), + [](char c) { return c != '\0'; }); const std::vector sections = ReadSectionHeaders(); is_update = std::any_of(sections.begin(), sections.end(), [](const NCASectionHeader& header) { From 441b5b97bd4f0c01b33785ef4d32b3ef3d7ea4fa Mon Sep 17 00:00:00 2001 From: Lioncash Date: Tue, 16 Oct 2018 13:13:40 -0400 Subject: [PATCH 4/5] content_archive: Make IsValidNCA() an internally linked function This is only ever used within the cpp file, so it can just be an internal function. --- src/core/file_sys/content_archive.cpp | 2 +- src/core/file_sys/content_archive.h | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/src/core/file_sys/content_archive.cpp b/src/core/file_sys/content_archive.cpp index a6bd3369c..6ce1cb0ae 100644 --- a/src/core/file_sys/content_archive.cpp +++ b/src/core/file_sys/content_archive.cpp @@ -97,7 +97,7 @@ union NCASectionHeader { }; static_assert(sizeof(NCASectionHeader) == 0x200, "NCASectionHeader has incorrect size."); -bool IsValidNCA(const NCAHeader& header) { +static bool IsValidNCA(const NCAHeader& header) { // TODO(DarkLordZach): Add NCA2/NCA0 support. return header.magic == Common::MakeMagic('N', 'C', 'A', '3'); } diff --git a/src/core/file_sys/content_archive.h b/src/core/file_sys/content_archive.h index e5d3d3c6a..1c903cd3f 100644 --- a/src/core/file_sys/content_archive.h +++ b/src/core/file_sys/content_archive.h @@ -73,8 +73,6 @@ inline bool IsDirectoryExeFS(const std::shared_ptr& pfs) { return pfs->GetFile("main") != nullptr && pfs->GetFile("main.npdm") != nullptr; } -bool IsValidNCA(const NCAHeader& header); - // An implementation of VfsDirectory that represents a Nintendo Content Archive (NCA) conatiner. // After construction, use GetStatus to determine if the file is valid and ready to be used. class NCA : public ReadOnlyVfsDirectory { From 871350ae357de050a9588ff84b7b1b7539467234 Mon Sep 17 00:00:00 2001 From: Lioncash Date: Tue, 16 Oct 2018 13:16:02 -0400 Subject: [PATCH 5/5] content_archive: Simpify assignment of bktr_base_romfs in the constructor std::move doesn't actually dereference the data, so it doesn't matter whether or not the type is null. --- src/core/file_sys/content_archive.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/core/file_sys/content_archive.cpp b/src/core/file_sys/content_archive.cpp index 6ce1cb0ae..6c356d85d 100644 --- a/src/core/file_sys/content_archive.cpp +++ b/src/core/file_sys/content_archive.cpp @@ -103,8 +103,7 @@ static bool IsValidNCA(const NCAHeader& header) { } NCA::NCA(VirtualFile file_, VirtualFile bktr_base_romfs_, u64 bktr_base_ivfc_offset) - : file(std::move(file_)), - bktr_base_romfs(bktr_base_romfs_ ? std::move(bktr_base_romfs_) : nullptr) { + : file(std::move(file_)), bktr_base_romfs(std::move(bktr_base_romfs_)) { if (file == nullptr) { status = Loader::ResultStatus::ErrorNullFile; return;