summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/core/file_sys/content_archive.cpp531
-rw-r--r--src/core/file_sys/content_archive.h19
2 files changed, 292 insertions, 258 deletions
diff --git a/src/core/file_sys/content_archive.cpp b/src/core/file_sys/content_archive.cpp
index 6dcec7816..6c356d85d 100644
--- a/src/core/file_sys/content_archive.cpp
+++ b/src/core/file_sys/content_archive.cpp
@@ -97,11 +97,288 @@ 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');
}
+NCA::NCA(VirtualFile file_, VirtualFile bktr_base_romfs_, u64 bktr_base_ivfc_offset)
+ : file(std::move(file_)), bktr_base_romfs(std::move(bktr_base_romfs_)) {
+ 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::any_of(header.rights_id.begin(), header.rights_id.end(),
+ [](char c) { return c != '\0'; });
+
+ const std::vector<NCASectionHeader> 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<Core::Crypto::Key256> 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<NCASectionHeader> 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<NCASectionHeader> 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<Core::Crypto::Key256> 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<NCASectionHeader>& 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<OffsetVfsFile>(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<RelocationBucketRaw> 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<SubsectionBucketRaw> 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<RelocationBucket> relocation_buckets(relocation_buckets_raw.size());
+ std::transform(relocation_buckets_raw.begin(), relocation_buckets_raw.end(),
+ relocation_buckets.begin(), &ConvertRelocationBucketRaw);
+ std::vector<SubsectionBucket> 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<Core::Crypto::Key128> 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>(
+ bktr_base_romfs, std::make_shared<OffsetVfsFile>(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<OffsetVfsFile>(
+ 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<u64>(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<OffsetVfsFile>(file, size, offset), offset);
+ if (dec != nullptr) {
+ auto npfs = std::make_shared<PartitionFilesystem>(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)
@@ -167,7 +444,7 @@ boost::optional<Core::Crypto::Key128> 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;
@@ -215,256 +492,6 @@ VirtualFile NCA::Decrypt(NCASectionHeader s_header, VirtualFile in, u64 starting
}
}
-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<Core::Crypto::Key256> 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<NCASectionHeader> 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<Core::Crypto::Key256> 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) {
- 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<OffsetVfsFile>(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<RelocationBucketRaw> 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<SubsectionBucketRaw> 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<RelocationBucket> relocation_buckets(relocation_buckets_raw.size());
- std::transform(relocation_buckets_raw.begin(), relocation_buckets_raw.end(),
- relocation_buckets.begin(), &ConvertRelocationBucketRaw);
- std::vector<SubsectionBucket> 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<Core::Crypto::Key128> 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>(
- bktr_base_romfs, std::make_shared<OffsetVfsFile>(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<OffsetVfsFile>(
- 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<u64>(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<OffsetVfsFile>(file, size, offset), offset);
- if (dec != nullptr) {
- auto npfs = std::make_shared<PartitionFilesystem>(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 f9f66cae9..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<VfsDirectory>& 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 {
@@ -106,10 +104,19 @@ protected:
bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override;
private:
+ bool CheckSupportedNCA(const NCAHeader& header);
+ bool HandlePotentialHeaderDecryption();
+
+ std::vector<NCASectionHeader> ReadSectionHeaders() const;
+ bool ReadSections(const std::vector<NCASectionHeader>& 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<Core::Crypto::Key128> GetKeyAreaKey(NCASectionCryptoType type) const;
boost::optional<Core::Crypto::Key128> GetTitlekey();
- VirtualFile Decrypt(NCASectionHeader header, VirtualFile in, u64 starting_offset);
+ VirtualFile Decrypt(const NCASectionHeader& header, VirtualFile in, u64 starting_offset);
std::vector<VirtualDir> dirs;
std::vector<VirtualFile> files;
@@ -118,15 +125,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;
};