From d4d0d3d9334032726aa48caab001552da40e3673 Mon Sep 17 00:00:00 2001 From: Zach Hilman Date: Sat, 25 Aug 2018 19:03:03 -0400 Subject: file_sys: Add BKTR patching mechanism --- src/core/file_sys/nca_patch.cpp | 208 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 208 insertions(+) create mode 100644 src/core/file_sys/nca_patch.cpp (limited to 'src/core/file_sys/nca_patch.cpp') diff --git a/src/core/file_sys/nca_patch.cpp b/src/core/file_sys/nca_patch.cpp new file mode 100644 index 0000000000..dd684c38e1 --- /dev/null +++ b/src/core/file_sys/nca_patch.cpp @@ -0,0 +1,208 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "common/assert.h" +#include "core/crypto/aes_util.h" +#include "core/file_sys/nca_patch.h" + +namespace FileSys { + +BKTR::BKTR(VirtualFile base_romfs_, VirtualFile bktr_romfs_, RelocationBlock relocation_, + std::vector relocation_buckets_, SubsectionBlock subsection_, + std::vector subsection_buckets_, bool is_encrypted_, + Core::Crypto::Key128 key_, u64 base_offset_, u64 ivfc_offset_, + std::array section_ctr_) + : base_romfs(std::move(base_romfs_)), bktr_romfs(std::move(bktr_romfs_)), + relocation(relocation_), relocation_buckets(std::move(relocation_buckets_)), + subsection(subsection_), subsection_buckets(std::move(subsection_buckets_)), + encrypted(is_encrypted_), key(key_), base_offset(base_offset_), ivfc_offset(ivfc_offset_), + section_ctr(std::move(section_ctr_)) { + for (size_t i = 0; i < relocation.number_buckets - 1; ++i) { + relocation_buckets[i].entries.push_back({relocation.base_offsets[i + 1], 0, 0}); + } + + for (size_t i = 0; i < subsection.number_buckets - 1; ++i) { + subsection_buckets[i].entries.push_back({subsection_buckets[i + 1].entries[0].address_patch, + {0}, + subsection_buckets[i + 1].entries[0].ctr}); + } + + relocation_buckets.back().entries.push_back({relocation.size, 0, 0}); +} + +size_t BKTR::Read(u8* data, size_t length, size_t offset) const { + // Read out of bounds. + if (offset >= relocation.size) + return 0; + const auto relocation = GetRelocationEntry(offset); + const auto section_offset = offset - relocation.address_patch + relocation.address_source; + const auto bktr_read = relocation.from_patch; + + const auto next_relocation = GetNextRelocationEntry(offset); + + if (offset + length <= next_relocation.address_patch) { + if (bktr_read) { + if (!encrypted) { + return bktr_romfs->Read(data, length, section_offset); + } + + const auto subsection = GetSubsectionEntry(section_offset); + Core::Crypto::AESCipher cipher(key, Core::Crypto::Mode::CTR); + + // Calculate AES IV + std::vector iv(16); + auto subsection_ctr = subsection.ctr; + auto offset_iv = section_offset + base_offset; + for (u8 i = 0; i < 8; ++i) + iv[i] = section_ctr[0x8 - i - 1]; + offset_iv >>= 4; + for (size_t i = 0; i < 8; ++i) { + iv[0xF - i] = static_cast(offset_iv & 0xFF); + offset_iv >>= 8; + } + for (size_t i = 0; i < 4; ++i) { + iv[0x7 - i] = static_cast(subsection_ctr & 0xFF); + subsection_ctr >>= 8; + } + cipher.SetIV(iv); + + const auto next_subsection = GetNextSubsectionEntry(section_offset); + + if (section_offset + length <= next_subsection.address_patch) { + const auto block_offset = section_offset & 0xF; + if (block_offset != 0) { + auto block = bktr_romfs->ReadBytes(0x10, section_offset & ~0xF); + cipher.Transcode(block.data(), block.size(), block.data(), + Core::Crypto::Op::Decrypt); + if (length + block_offset < 0x10) { + std::memcpy(data, block.data() + block_offset, + std::min(length, block.size())); + return std::min(length, block.size()); + } + + const auto read = 0x10 - block_offset; + std::memcpy(data, block.data() + block_offset, read); + return read + Read(data + read, length - read, offset + read); + } + + const auto raw_read = bktr_romfs->Read(data, length, section_offset); + cipher.Transcode(data, raw_read, data, Core::Crypto::Op::Decrypt); + return raw_read; + } else { + const u64 partition = next_subsection.address_patch - section_offset; + return Read(data, partition, offset) + + Read(data + partition, length - partition, offset + partition); + } + } else { + ASSERT(section_offset > ivfc_offset, "Offset calculation negative."); + return base_romfs->Read(data, length, section_offset); + } + } else { + const u64 partition = next_relocation.address_patch - offset; + return Read(data, partition, offset) + + Read(data + partition, length - partition, offset + partition); + } +} + +template +std::pair BKTR::SearchBucketEntry(u64 offset, BlockType block, + BucketType buckets) const { + if constexpr (Subsection) { + const auto last_bucket = buckets[block.number_buckets - 1]; + if (offset >= last_bucket.entries[last_bucket.number_entries].address_patch) + return {block.number_buckets - 1, last_bucket.number_entries}; + } else { + ASSERT_MSG(offset <= block.size, "Offset is out of bounds in BKTR relocation block."); + } + + size_t bucket_id = 0; + for (size_t i = 1; i < block.number_buckets; ++i) { + if (block.base_offsets[i] <= offset) + ++bucket_id; + } + + const auto bucket = buckets[bucket_id]; + + if (bucket.number_entries == 1) + return {bucket_id, 0}; + + size_t low = 0; + size_t mid = 0; + size_t high = bucket.number_entries - 1; + while (low <= high) { + mid = (low + high) / 2; + if (bucket.entries[mid].address_patch > offset) { + high = mid - 1; + } else { + if (mid == bucket.number_entries - 1 || + bucket.entries[mid + 1].address_patch > offset) { + return {bucket_id, mid}; + } + + low = mid + 1; + } + } + + UNREACHABLE_MSG("Offset could not be found in BKTR block."); +} + +RelocationEntry BKTR::GetRelocationEntry(u64 offset) const { + const auto res = SearchBucketEntry(offset, relocation, relocation_buckets); + return relocation_buckets[res.first].entries[res.second]; +} + +RelocationEntry BKTR::GetNextRelocationEntry(u64 offset) const { + const auto res = SearchBucketEntry(offset, relocation, relocation_buckets); + const auto bucket = relocation_buckets[res.first]; + if (res.second + 1 < bucket.entries.size()) + return bucket.entries[res.second + 1]; + return relocation_buckets[res.first + 1].entries[0]; +} + +SubsectionEntry BKTR::GetSubsectionEntry(u64 offset) const { + const auto res = SearchBucketEntry(offset, subsection, subsection_buckets); + return subsection_buckets[res.first].entries[res.second]; +} + +SubsectionEntry BKTR::GetNextSubsectionEntry(u64 offset) const { + const auto res = SearchBucketEntry(offset, subsection, subsection_buckets); + const auto bucket = subsection_buckets[res.first]; + if (res.second + 1 < bucket.entries.size()) + return bucket.entries[res.second + 1]; + return subsection_buckets[res.first + 1].entries[0]; +} + +std::string BKTR::GetName() const { + return base_romfs->GetName(); +} + +size_t BKTR::GetSize() const { + return relocation.size; +} + +bool BKTR::Resize(size_t new_size) { + return false; +} + +std::shared_ptr BKTR::GetContainingDirectory() const { + return base_romfs->GetContainingDirectory(); +} + +bool BKTR::IsWritable() const { + return false; +} + +bool BKTR::IsReadable() const { + return true; +} + +size_t BKTR::Write(const u8* data, size_t length, size_t offset) { + return 0; +} + +bool BKTR::Rename(std::string_view name) { + return base_romfs->Rename(name); +} + +} // namespace FileSys -- cgit v1.2.3 From e88835cd40028271ef8de2c3f6eea9ef34e2be7a Mon Sep 17 00:00:00 2001 From: Zach Hilman Date: Sun, 26 Aug 2018 10:53:31 -0400 Subject: bktr: Fix missing includes and optimize style --- src/core/file_sys/nca_patch.cpp | 126 ++++++++++++------------ src/core/file_sys/nca_patch.h | 5 +- src/core/file_sys/patch_manager.cpp | 35 +++---- src/core/file_sys/patch_manager.h | 9 +- src/core/file_sys/registered_cache.cpp | 21 ++-- src/core/file_sys/registered_cache.h | 2 +- src/core/loader/deconstructed_rom_directory.cpp | 2 +- src/core/loader/deconstructed_rom_directory.h | 2 +- src/core/loader/loader.h | 2 +- src/core/loader/nro.cpp | 2 +- src/core/loader/nro.h | 2 +- src/yuzu/main.cpp | 4 +- 12 files changed, 109 insertions(+), 103 deletions(-) (limited to 'src/core/file_sys/nca_patch.cpp') diff --git a/src/core/file_sys/nca_patch.cpp b/src/core/file_sys/nca_patch.cpp index dd684c38e1..22fbba5732 100644 --- a/src/core/file_sys/nca_patch.cpp +++ b/src/core/file_sys/nca_patch.cpp @@ -17,7 +17,7 @@ BKTR::BKTR(VirtualFile base_romfs_, VirtualFile bktr_romfs_, RelocationBlock rel relocation(relocation_), relocation_buckets(std::move(relocation_buckets_)), subsection(subsection_), subsection_buckets(std::move(subsection_buckets_)), encrypted(is_encrypted_), key(key_), base_offset(base_offset_), ivfc_offset(ivfc_offset_), - section_ctr(std::move(section_ctr_)) { + section_ctr(section_ctr_) { for (size_t i = 0; i < relocation.number_buckets - 1; ++i) { relocation_buckets[i].entries.push_back({relocation.base_offsets[i + 1], 0, 0}); } @@ -31,6 +31,8 @@ BKTR::BKTR(VirtualFile base_romfs_, VirtualFile bktr_romfs_, RelocationBlock rel relocation_buckets.back().entries.push_back({relocation.size, 0, 0}); } +BKTR::~BKTR() = default; + size_t BKTR::Read(u8* data, size_t length, size_t offset) const { // Read out of bounds. if (offset >= relocation.size) @@ -41,68 +43,66 @@ size_t BKTR::Read(u8* data, size_t length, size_t offset) const { const auto next_relocation = GetNextRelocationEntry(offset); - if (offset + length <= next_relocation.address_patch) { - if (bktr_read) { - if (!encrypted) { - return bktr_romfs->Read(data, length, section_offset); - } - - const auto subsection = GetSubsectionEntry(section_offset); - Core::Crypto::AESCipher cipher(key, Core::Crypto::Mode::CTR); - - // Calculate AES IV - std::vector iv(16); - auto subsection_ctr = subsection.ctr; - auto offset_iv = section_offset + base_offset; - for (u8 i = 0; i < 8; ++i) - iv[i] = section_ctr[0x8 - i - 1]; - offset_iv >>= 4; - for (size_t i = 0; i < 8; ++i) { - iv[0xF - i] = static_cast(offset_iv & 0xFF); - offset_iv >>= 8; - } - for (size_t i = 0; i < 4; ++i) { - iv[0x7 - i] = static_cast(subsection_ctr & 0xFF); - subsection_ctr >>= 8; - } - cipher.SetIV(iv); - - const auto next_subsection = GetNextSubsectionEntry(section_offset); - - if (section_offset + length <= next_subsection.address_patch) { - const auto block_offset = section_offset & 0xF; - if (block_offset != 0) { - auto block = bktr_romfs->ReadBytes(0x10, section_offset & ~0xF); - cipher.Transcode(block.data(), block.size(), block.data(), - Core::Crypto::Op::Decrypt); - if (length + block_offset < 0x10) { - std::memcpy(data, block.data() + block_offset, - std::min(length, block.size())); - return std::min(length, block.size()); - } - - const auto read = 0x10 - block_offset; - std::memcpy(data, block.data() + block_offset, read); - return read + Read(data + read, length - read, offset + read); - } - - const auto raw_read = bktr_romfs->Read(data, length, section_offset); - cipher.Transcode(data, raw_read, data, Core::Crypto::Op::Decrypt); - return raw_read; - } else { - const u64 partition = next_subsection.address_patch - section_offset; - return Read(data, partition, offset) + - Read(data + partition, length - partition, offset + partition); - } - } else { - ASSERT(section_offset > ivfc_offset, "Offset calculation negative."); - return base_romfs->Read(data, length, section_offset); - } - } else { + if (offset + length >= next_relocation.address_patch) { const u64 partition = next_relocation.address_patch - offset; return Read(data, partition, offset) + Read(data + partition, length - partition, offset + partition); } + + if (!bktr_read) { + ASSERT_MSG(section_offset > ivfc_offset, "Offset calculation negative."); + return base_romfs->Read(data, length, section_offset); + } + + if (!encrypted) { + return bktr_romfs->Read(data, length, section_offset); + } + + const auto subsection = GetSubsectionEntry(section_offset); + Core::Crypto::AESCipher cipher(key, Core::Crypto::Mode::CTR); + + // Calculate AES IV + std::vector iv(16); + auto subsection_ctr = subsection.ctr; + auto offset_iv = section_offset + base_offset; + for (size_t i = 0; i < section_ctr.size(); ++i) + iv[i] = section_ctr[0x8 - i - 1]; + offset_iv >>= 4; + for (size_t i = 0; i < sizeof(u64); ++i) { + iv[0xF - i] = static_cast(offset_iv & 0xFF); + offset_iv >>= 8; + } + for (size_t i = 0; i < sizeof(u32); ++i) { + iv[0x7 - i] = static_cast(subsection_ctr & 0xFF); + subsection_ctr >>= 8; + } + cipher.SetIV(iv); + + const auto next_subsection = GetNextSubsectionEntry(section_offset); + + if (section_offset + length > next_subsection.address_patch) { + const u64 partition = next_subsection.address_patch - section_offset; + return Read(data, partition, offset) + + Read(data + partition, length - partition, offset + partition); + } + + const auto block_offset = section_offset & 0xF; + if (block_offset != 0) { + auto block = bktr_romfs->ReadBytes(0x10, section_offset & ~0xF); + cipher.Transcode(block.data(), block.size(), block.data(), Core::Crypto::Op::Decrypt); + if (length + block_offset < 0x10) { + std::memcpy(data, block.data() + block_offset, std::min(length, block.size())); + return std::min(length, block.size()); + } + + const auto read = 0x10 - block_offset; + std::memcpy(data, block.data() + block_offset, read); + return read + Read(data + read, length - read, offset + read); + } + + const auto raw_read = bktr_romfs->Read(data, length, section_offset); + cipher.Transcode(data, raw_read, data, Core::Crypto::Op::Decrypt); + return raw_read; } template @@ -116,11 +116,9 @@ std::pair BKTR::SearchBucketEntry(u64 offset, BlockType block, ASSERT_MSG(offset <= block.size, "Offset is out of bounds in BKTR relocation block."); } - size_t bucket_id = 0; - for (size_t i = 1; i < block.number_buckets; ++i) { - if (block.base_offsets[i] <= offset) - ++bucket_id; - } + size_t bucket_id = std::count_if(block.base_offsets.begin() + 1, + block.base_offsets.begin() + block.number_buckets, + [&offset](u64 base_offset) { return base_offset < offset; }); const auto bucket = buckets[bucket_id]; diff --git a/src/core/file_sys/nca_patch.h b/src/core/file_sys/nca_patch.h index 8b8d0a4f5f..0d9ad95f53 100644 --- a/src/core/file_sys/nca_patch.h +++ b/src/core/file_sys/nca_patch.h @@ -4,9 +4,11 @@ #pragma once +#include +#include +#include #include "core/crypto/key_manager.h" #include "core/file_sys/romfs.h" -#include "core/loader/loader.h" namespace FileSys { @@ -91,6 +93,7 @@ public: std::vector relocation_buckets, SubsectionBlock subsection, std::vector subsection_buckets, bool is_encrypted, Core::Crypto::Key128 key, u64 base_offset, u64 ivfc_offset, std::array section_ctr); + ~BKTR() override; size_t Read(u8* data, size_t length, size_t offset) const override; diff --git a/src/core/file_sys/patch_manager.cpp b/src/core/file_sys/patch_manager.cpp index 697b8a4c92..5e853c2c07 100644 --- a/src/core/file_sys/patch_manager.cpp +++ b/src/core/file_sys/patch_manager.cpp @@ -8,24 +8,19 @@ namespace FileSys { -union TitleVersion { - u32 version; - - struct { - u8 v_revision; - u8 v_micro; - u8 v_minor; - u8 v_major; - }; -}; - -std::string FormatTitleVersion(u32 version_, bool full) { - TitleVersion ver{}; - ver.version = version_; +constexpr u64 SINGLE_BYTE_MODULUS = 0x100; + +std::string FormatTitleVersion(u32 version, TitleVersionFormat format) { + std::array bytes{}; + bytes[0] = version % SINGLE_BYTE_MODULUS; + for (size_t i = 1; i < bytes.size(); ++i) { + version /= SINGLE_BYTE_MODULUS; + bytes[i] = version % SINGLE_BYTE_MODULUS; + } - if (full) - return fmt::format("v{}.{}.{}.{}", ver.v_major, ver.v_minor, ver.v_minor, ver.v_revision); - return fmt::format("v{}.{}.{}", ver.v_major, ver.v_minor, ver.v_micro); + if (format == TitleVersionFormat::FourElements) + return fmt::format("v{}.{}.{}.{}", bytes[3], bytes[2], bytes[1], bytes[0]); + return fmt::format("v{}.{}.{}", bytes[3], bytes[2], bytes[1]); } constexpr std::array PATCH_TYPE_NAMES{ @@ -49,8 +44,9 @@ VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const { const auto update = installed->GetEntry(update_tid, ContentRecordType::Program); if (update != nullptr) { if (update->GetStatus() == Loader::ResultStatus::ErrorMissingBKTRBaseRomFS && - update->GetExeFS() != nullptr) + update->GetExeFS() != nullptr) { exefs = update->GetExeFS(); + } } return exefs; @@ -81,8 +77,9 @@ std::map PatchManager::GetPatchVersionNames() const { const auto update_tid = GetUpdateTitleID(title_id); const auto update_version = installed->GetEntryVersion(update_tid); if (update_version != boost::none && - installed->HasEntry(update_tid, ContentRecordType::Program)) + installed->HasEntry(update_tid, ContentRecordType::Program)) { out[PatchType::Update] = update_version.get(); + } return out; } diff --git a/src/core/file_sys/patch_manager.h b/src/core/file_sys/patch_manager.h index 2a39c473a3..803bcb2a2c 100644 --- a/src/core/file_sys/patch_manager.h +++ b/src/core/file_sys/patch_manager.h @@ -5,12 +5,19 @@ #pragma once #include +#include #include "common/common_types.h" #include "core/file_sys/vfs.h" namespace FileSys { -std::string FormatTitleVersion(u32 version, bool full = false); +enum class TitleVersionFormat : u8 { + ThreeElements, ///< vX.Y.Z + FourElements, ///< vX.Y.Z.W +}; + +std::string FormatTitleVersion(u32 version, + TitleVersionFormat format = TitleVersionFormat::ThreeElements); enum class PatchType { Update, diff --git a/src/core/file_sys/registered_cache.cpp b/src/core/file_sys/registered_cache.cpp index 39c0710e1b..7361a67be6 100644 --- a/src/core/file_sys/registered_cache.cpp +++ b/src/core/file_sys/registered_cache.cpp @@ -281,10 +281,14 @@ VirtualFile RegisteredCache::GetEntryUnparsed(RegisteredCacheEntry entry) const } boost::optional RegisteredCache::GetEntryVersion(u64 title_id) const { - if (meta.find(title_id) != meta.end()) - return meta.at(title_id).GetTitleVersion(); - if (yuzu_meta.find(title_id) != yuzu_meta.end()) - return yuzu_meta.at(title_id).GetTitleVersion(); + const auto meta_iter = meta.find(title_id); + if (meta_iter != meta.end()) + return meta_iter->second.GetTitleVersion(); + + const auto yuzu_meta_iter = yuzu_meta.find(title_id); + if (yuzu_meta_iter != yuzu_meta.end()) + return yuzu_meta_iter->second.GetTitleVersion(); + return boost::none; } @@ -516,12 +520,9 @@ void RegisteredCacheUnion::Refresh() { } bool RegisteredCacheUnion::HasEntry(u64 title_id, ContentRecordType type) const { - for (const auto& c : caches) { - if (c->HasEntry(title_id, type)) - return true; - } - - return false; + return std::any_of(caches.begin(), caches.end(), [title_id, type](const auto& cache) { + return cache->HasEntry(title_id, type); + }); } bool RegisteredCacheUnion::HasEntry(RegisteredCacheEntry entry) const { diff --git a/src/core/file_sys/registered_cache.h b/src/core/file_sys/registered_cache.h index dcce3fd169..f487b0cf0a 100644 --- a/src/core/file_sys/registered_cache.h +++ b/src/core/file_sys/registered_cache.h @@ -43,7 +43,7 @@ struct RegisteredCacheEntry { std::string DebugInfo() const; }; -constexpr inline u64 GetUpdateTitleID(u64 base_title_id) { +constexpr u64 GetUpdateTitleID(u64 base_title_id) { return base_title_id | 0x800; } diff --git a/src/core/loader/deconstructed_rom_directory.cpp b/src/core/loader/deconstructed_rom_directory.cpp index 6b22302691..2235704315 100644 --- a/src/core/loader/deconstructed_rom_directory.cpp +++ b/src/core/loader/deconstructed_rom_directory.cpp @@ -189,7 +189,7 @@ ResultStatus AppLoader_DeconstructedRomDirectory::ReadTitle(std::string& title) return ResultStatus::Success; } -bool AppLoader_DeconstructedRomDirectory::IsRomFSUpdatable() { +bool AppLoader_DeconstructedRomDirectory::IsRomFSUpdatable() const { return false; } diff --git a/src/core/loader/deconstructed_rom_directory.h b/src/core/loader/deconstructed_rom_directory.h index a8644516b7..8a0dc1b1eb 100644 --- a/src/core/loader/deconstructed_rom_directory.h +++ b/src/core/loader/deconstructed_rom_directory.h @@ -44,7 +44,7 @@ public: ResultStatus ReadIcon(std::vector& buffer) override; ResultStatus ReadProgramId(u64& out_program_id) override; ResultStatus ReadTitle(std::string& title) override; - bool IsRomFSUpdatable() override; + bool IsRomFSUpdatable() const override; private: FileSys::ProgramMetadata metadata; diff --git a/src/core/loader/loader.h b/src/core/loader/loader.h index b4a3a65733..225c05127d 100644 --- a/src/core/loader/loader.h +++ b/src/core/loader/loader.h @@ -210,7 +210,7 @@ public: * the base game it should be set to false. * @return bool whether or not updatable. */ - virtual bool IsRomFSUpdatable() { + virtual bool IsRomFSUpdatable() const { return true; } diff --git a/src/core/loader/nro.cpp b/src/core/loader/nro.cpp index 96f5cd9e5c..bb89a9da33 100644 --- a/src/core/loader/nro.cpp +++ b/src/core/loader/nro.cpp @@ -233,7 +233,7 @@ ResultStatus AppLoader_NRO::ReadTitle(std::string& title) { return ResultStatus::Success; } -bool AppLoader_NRO::IsRomFSUpdatable() { +bool AppLoader_NRO::IsRomFSUpdatable() const { return false; } diff --git a/src/core/loader/nro.h b/src/core/loader/nro.h index c35c99d140..96d2de3050 100644 --- a/src/core/loader/nro.h +++ b/src/core/loader/nro.h @@ -39,7 +39,7 @@ public: ResultStatus ReadProgramId(u64& out_program_id) override; ResultStatus ReadRomFS(FileSys::VirtualFile& dir) override; ResultStatus ReadTitle(std::string& title) override; - bool IsRomFSUpdatable() override; + bool IsRomFSUpdatable() const override; private: bool LoadNro(FileSys::VirtualFile file, VAddr load_base); diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 3d438df47d..b7ce0248b0 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -871,8 +871,8 @@ void GMainWindow::OnMenuInstallToNAND() { const auto id = nca->GetStatus(); // Game updates necessary are missing base RomFS - if (nca->GetStatus() != Loader::ResultStatus::Success && - nca->GetStatus() != Loader::ResultStatus::ErrorMissingBKTRBaseRomFS) { + if (id != Loader::ResultStatus::Success && + id != Loader::ResultStatus::ErrorMissingBKTRBaseRomFS) { failed(); return; } -- cgit v1.2.3 From b8f8b0fa473d4d4e90eef7587387044e911bebfa Mon Sep 17 00:00:00 2001 From: Zach Hilman Date: Tue, 28 Aug 2018 22:37:42 -0400 Subject: bktr: Implement IVFC offset shifting Fixes base game read errors --- src/core/file_sys/content_archive.cpp | 13 +++++++++---- src/core/file_sys/content_archive.h | 7 ++++++- src/core/file_sys/nca_patch.cpp | 2 +- src/core/file_sys/romfs_factory.cpp | 5 +++-- src/core/file_sys/romfs_factory.h | 1 + src/core/loader/loader.h | 9 +++++++++ src/core/loader/nca.cpp | 6 ++++++ src/core/loader/nca.h | 1 + 8 files changed, 36 insertions(+), 8 deletions(-) (limited to 'src/core/file_sys/nca_patch.cpp') diff --git a/src/core/file_sys/content_archive.cpp b/src/core/file_sys/content_archive.cpp index f0d376bf52..26e1daf554 100644 --- a/src/core/file_sys/content_archive.cpp +++ b/src/core/file_sys/content_archive.cpp @@ -215,7 +215,7 @@ VirtualFile NCA::Decrypt(NCASectionHeader s_header, VirtualFile in, u64 starting } } -NCA::NCA(VirtualFile file_, VirtualFile bktr_base_romfs_) +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; @@ -292,6 +292,7 @@ NCA::NCA(VirtualFile file_, VirtualFile bktr_base_romfs_) 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]; @@ -299,8 +300,8 @@ NCA::NCA(VirtualFile file_, VirtualFile bktr_base_romfs_) if (section.raw.header.filesystem_type == NCASectionFilesystemType::ROMFS) { const size_t base_offset = header.section_tables[i].media_offset * MEDIA_OFFSET_MULTIPLIER; - const size_t romfs_offset = - base_offset + section.romfs.ivfc.levels[IVFC_MAX_LEVEL - 1].offset; + ivfc_offset = section.romfs.ivfc.levels[IVFC_MAX_LEVEL - 1].offset; + const size_t romfs_offset = base_offset + ivfc_offset; const 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); @@ -414,7 +415,7 @@ NCA::NCA(VirtualFile file_, VirtualFile bktr_base_romfs_) 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, - romfs_offset - base_offset, section.raw.section_ctr); + bktr_base_ivfc_offset, section.raw.section_ctr); // BKTR applies to entire IVFC, so make an offset version to level 6 @@ -511,6 +512,10 @@ VirtualFile NCA::GetBaseFile() const { return file; } +u64 NCA::GetBaseIVFCOffset() const { + return ivfc_offset; +} + bool NCA::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) { return false; } diff --git a/src/core/file_sys/content_archive.h b/src/core/file_sys/content_archive.h index 104226f3a1..00eca52da5 100644 --- a/src/core/file_sys/content_archive.h +++ b/src/core/file_sys/content_archive.h @@ -79,7 +79,8 @@ bool IsValidNCA(const NCAHeader& header); // After construction, use GetStatus to determine if the file is valid and ready to be used. class NCA : public ReadOnlyVfsDirectory { public: - explicit NCA(VirtualFile file, VirtualFile bktr_base_romfs = nullptr); + explicit NCA(VirtualFile file, VirtualFile bktr_base_romfs = nullptr, + u64 bktr_base_ivfc_offset = 0); Loader::ResultStatus GetStatus() const; std::vector> GetFiles() const override; @@ -96,6 +97,9 @@ public: VirtualFile GetBaseFile() const; + // Returns the base ivfc offset used in BKTR patching. + u64 GetBaseIVFCOffset() const; + protected: bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override; @@ -112,6 +116,7 @@ private: VirtualDir exefs = nullptr; VirtualFile file; VirtualFile bktr_base_romfs; + u64 ivfc_offset; NCAHeader header{}; bool has_rights_id{}; diff --git a/src/core/file_sys/nca_patch.cpp b/src/core/file_sys/nca_patch.cpp index 22fbba5732..e293af4524 100644 --- a/src/core/file_sys/nca_patch.cpp +++ b/src/core/file_sys/nca_patch.cpp @@ -51,7 +51,7 @@ size_t BKTR::Read(u8* data, size_t length, size_t offset) const { if (!bktr_read) { ASSERT_MSG(section_offset > ivfc_offset, "Offset calculation negative."); - return base_romfs->Read(data, length, section_offset); + return base_romfs->Read(data, length, section_offset - ivfc_offset); } if (!encrypted) { diff --git a/src/core/file_sys/romfs_factory.cpp b/src/core/file_sys/romfs_factory.cpp index fc9cf1eca7..33ec62491a 100644 --- a/src/core/file_sys/romfs_factory.cpp +++ b/src/core/file_sys/romfs_factory.cpp @@ -24,14 +24,15 @@ RomFSFactory::RomFSFactory(Loader::AppLoader& app_loader) { } updatable = app_loader.IsRomFSUpdatable(); + ivfc_offset = app_loader.ReadRomFSIVFCOffset(); } ResultVal RomFSFactory::OpenCurrentProcess() { if (!updatable) return MakeResult(file); - const PatchManager patch_manager(Core::CurrentProcess()->process_id); - return MakeResult(patch_manager.PatchRomFS(file)); + const PatchManager patch_manager(Core::CurrentProcess()->program_id); + return MakeResult(patch_manager.PatchRomFS(file, ivfc_offset)); } ResultVal RomFSFactory::Open(u64 title_id, StorageId storage, ContentRecordType type) { diff --git a/src/core/file_sys/romfs_factory.h b/src/core/file_sys/romfs_factory.h index 168db1c465..26b8f46cc2 100644 --- a/src/core/file_sys/romfs_factory.h +++ b/src/core/file_sys/romfs_factory.h @@ -37,6 +37,7 @@ public: private: VirtualFile file; bool updatable; + u64 ivfc_offset; }; } // namespace FileSys diff --git a/src/core/loader/loader.h b/src/core/loader/loader.h index 225c05127d..843c4bb912 100644 --- a/src/core/loader/loader.h +++ b/src/core/loader/loader.h @@ -214,6 +214,15 @@ public: return true; } + /** + * Gets the difference between the start of the IVFC header and the start of level 6 (RomFS) + * data. Needed for bktr patching. + * @return IVFC offset for romfs. + */ + virtual u64 ReadRomFSIVFCOffset() const { + return 0; + } + /** * Get the title of the application * @param title Reference to store the application title into diff --git a/src/core/loader/nca.cpp b/src/core/loader/nca.cpp index 6b1c27b47f..6aaffae595 100644 --- a/src/core/loader/nca.cpp +++ b/src/core/loader/nca.cpp @@ -71,6 +71,12 @@ ResultStatus AppLoader_NCA::ReadRomFS(FileSys::VirtualFile& dir) { return ResultStatus::Success; } +u64 AppLoader_NCA::ReadRomFSIVFCOffset() const { + if (nca == nullptr) + return 0; + return nca->GetBaseIVFCOffset(); +} + ResultStatus AppLoader_NCA::ReadProgramId(u64& out_program_id) { if (nca == nullptr || nca->GetStatus() != ResultStatus::Success) return ResultStatus::ErrorNotInitialized; diff --git a/src/core/loader/nca.h b/src/core/loader/nca.h index 326f848579..10be197c4b 100644 --- a/src/core/loader/nca.h +++ b/src/core/loader/nca.h @@ -37,6 +37,7 @@ public: ResultStatus Load(Kernel::SharedPtr& process) override; ResultStatus ReadRomFS(FileSys::VirtualFile& dir) override; + u64 ReadRomFSIVFCOffset() const override; ResultStatus ReadProgramId(u64& out_program_id) override; private: -- cgit v1.2.3 From b555718bb9d0e6521dff7f427f1609846d021b7f Mon Sep 17 00:00:00 2001 From: Zach Hilman Date: Tue, 28 Aug 2018 22:38:35 -0400 Subject: bktr: Add logging on successful patch --- src/core/file_sys/nca_patch.cpp | 2 +- src/core/file_sys/patch_manager.cpp | 22 +++++++++++++++++----- src/core/file_sys/patch_manager.h | 7 ++++++- 3 files changed, 24 insertions(+), 7 deletions(-) (limited to 'src/core/file_sys/nca_patch.cpp') diff --git a/src/core/file_sys/nca_patch.cpp b/src/core/file_sys/nca_patch.cpp index e293af4524..1e93000d53 100644 --- a/src/core/file_sys/nca_patch.cpp +++ b/src/core/file_sys/nca_patch.cpp @@ -43,7 +43,7 @@ size_t BKTR::Read(u8* data, size_t length, size_t offset) const { const auto next_relocation = GetNextRelocationEntry(offset); - if (offset + length >= next_relocation.address_patch) { + if (offset + length > next_relocation.address_patch) { const u64 partition = next_relocation.address_patch - offset; return Read(data, partition, offset) + Read(data + partition, length - partition, offset + partition); diff --git a/src/core/file_sys/patch_manager.cpp b/src/core/file_sys/patch_manager.cpp index 5e853c2c07..8b7d797734 100644 --- a/src/core/file_sys/patch_manager.cpp +++ b/src/core/file_sys/patch_manager.cpp @@ -34,6 +34,8 @@ std::string FormatPatchTypeName(PatchType type) { PatchManager::PatchManager(u64 title_id) : title_id(title_id) {} VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const { + LOG_INFO(Loader, "Patching ExeFS for title_id={:016X}", title_id); + if (exefs == nullptr) return exefs; @@ -45,6 +47,8 @@ VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const { if (update != nullptr) { if (update->GetStatus() == Loader::ResultStatus::ErrorMissingBKTRBaseRomFS && update->GetExeFS() != nullptr) { + LOG_INFO(Loader, " ExeFS: Update ({}) applied successfully", + FormatTitleVersion(installed->GetEntryVersion(update_tid).get_value_or(0))); exefs = update->GetExeFS(); } } @@ -52,7 +56,11 @@ VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const { return exefs; } -VirtualFile PatchManager::PatchRomFS(VirtualFile romfs) const { +VirtualFile PatchManager::PatchRomFS(VirtualFile romfs, u64 ivfc_offset, + ContentRecordType type) const { + LOG_INFO(Loader, "Patching RomFS for title_id={:016X}, type={:02X}", title_id, + static_cast(type)); + if (romfs == nullptr) return romfs; @@ -60,11 +68,15 @@ VirtualFile PatchManager::PatchRomFS(VirtualFile romfs) const { // Game Updates const auto update_tid = GetUpdateTitleID(title_id); - const auto update = installed->GetEntryRaw(update_tid, ContentRecordType::Program); + const auto update = installed->GetEntryRaw(update_tid, type); if (update != nullptr) { - const auto nca = std::make_shared(update, romfs); - if (nca->GetStatus() == Loader::ResultStatus::Success && nca->GetRomFS() != nullptr) - romfs = nca->GetRomFS(); + const auto new_nca = std::make_shared(update, romfs, ivfc_offset); + if (new_nca->GetStatus() == Loader::ResultStatus::Success && + new_nca->GetRomFS() != nullptr) { + LOG_INFO(Loader, " RomFS: Update ({}) applied successfully", + FormatTitleVersion(installed->GetEntryVersion(update_tid).get_value_or(0))); + romfs = new_nca->GetRomFS(); + } } return romfs; diff --git a/src/core/file_sys/patch_manager.h b/src/core/file_sys/patch_manager.h index 803bcb2a2c..021bc33667 100644 --- a/src/core/file_sys/patch_manager.h +++ b/src/core/file_sys/patch_manager.h @@ -8,9 +8,13 @@ #include #include "common/common_types.h" #include "core/file_sys/vfs.h" +#include "nca_metadata.h" +#include "romfs_factory.h" namespace FileSys { +class NCA; + enum class TitleVersionFormat : u8 { ThreeElements, ///< vX.Y.Z FourElements, ///< vX.Y.Z.W @@ -36,7 +40,8 @@ public: // Currently tracked RomFS patches: // - Game Updates - VirtualFile PatchRomFS(VirtualFile romfs) const; + VirtualFile PatchRomFS(VirtualFile base, u64 ivfc_offset, + ContentRecordType type = ContentRecordType::Program) const; // Returns a vector of pairs between patch names and patch versions. // i.e. Update v80 will return {Update, 80} -- cgit v1.2.3 From 46eb9ec6ba96d8f485d920f1f7b93e7a0ed5d61c Mon Sep 17 00:00:00 2001 From: Zach Hilman Date: Tue, 4 Sep 2018 17:01:40 -0400 Subject: bktr: Fix bucket overlap error --- src/core/file_sys/content_archive.cpp | 3 --- src/core/file_sys/nca_patch.cpp | 4 ++-- src/core/file_sys/patch_manager.cpp | 3 +++ src/core/file_sys/patch_manager.h | 1 - src/core/file_sys/romfs_factory.cpp | 1 + src/core/hle/service/filesystem/filesystem.cpp | 2 +- src/core/loader/loader.cpp | 4 ++-- src/yuzu/main.cpp | 2 ++ 8 files changed, 11 insertions(+), 9 deletions(-) (limited to 'src/core/file_sys/nca_patch.cpp') diff --git a/src/core/file_sys/content_archive.cpp b/src/core/file_sys/content_archive.cpp index 1c093d94f3..79bfb6fecf 100644 --- a/src/core/file_sys/content_archive.cpp +++ b/src/core/file_sys/content_archive.cpp @@ -425,9 +425,6 @@ NCA::NCA(VirtualFile file_, VirtualFile bktr_base_romfs_, u64 bktr_base_ivfc_off } else { files.push_back(std::move(dec)); romfs = files.back(); - const u64 raw_size = - MEDIA_OFFSET_MULTIPLIER * (header.section_tables[i].media_end_offset - - header.section_tables[i].media_offset); } } else if (section.raw.header.filesystem_type == NCASectionFilesystemType::PFS0) { u64 offset = (static_cast(header.section_tables[i].media_offset) * diff --git a/src/core/file_sys/nca_patch.cpp b/src/core/file_sys/nca_patch.cpp index 1e93000d53..e0111bffc4 100644 --- a/src/core/file_sys/nca_patch.cpp +++ b/src/core/file_sys/nca_patch.cpp @@ -50,7 +50,7 @@ size_t BKTR::Read(u8* data, size_t length, size_t offset) const { } if (!bktr_read) { - ASSERT_MSG(section_offset > ivfc_offset, "Offset calculation negative."); + ASSERT_MSG(section_offset >= ivfc_offset, "Offset calculation negative."); return base_romfs->Read(data, length, section_offset - ivfc_offset); } @@ -118,7 +118,7 @@ std::pair BKTR::SearchBucketEntry(u64 offset, BlockType block, size_t bucket_id = std::count_if(block.base_offsets.begin() + 1, block.base_offsets.begin() + block.number_buckets, - [&offset](u64 base_offset) { return base_offset < offset; }); + [&offset](u64 base_offset) { return base_offset <= offset; }); const auto bucket = buckets[bucket_id]; diff --git a/src/core/file_sys/patch_manager.cpp b/src/core/file_sys/patch_manager.cpp index fa2fbe5e1f..40675de35b 100644 --- a/src/core/file_sys/patch_manager.cpp +++ b/src/core/file_sys/patch_manager.cpp @@ -2,10 +2,13 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include "core/file_sys/content_archive.h" #include "core/file_sys/control_metadata.h" #include "core/file_sys/patch_manager.h" #include "core/file_sys/registered_cache.h" +#include "core/file_sys/romfs.h" #include "core/hle/service/filesystem/filesystem.h" +#include "core/loader/loader.h" namespace FileSys { diff --git a/src/core/file_sys/patch_manager.h b/src/core/file_sys/patch_manager.h index c2626bc6cc..28c7ae1363 100644 --- a/src/core/file_sys/patch_manager.h +++ b/src/core/file_sys/patch_manager.h @@ -8,7 +8,6 @@ #include #include "common/common_types.h" #include "core/file_sys/nca_metadata.h" -#include "core/file_sys/romfs_factory.h" #include "core/file_sys/vfs.h" namespace FileSys { diff --git a/src/core/file_sys/romfs_factory.cpp b/src/core/file_sys/romfs_factory.cpp index 33ec62491a..d9d90939eb 100644 --- a/src/core/file_sys/romfs_factory.cpp +++ b/src/core/file_sys/romfs_factory.cpp @@ -12,6 +12,7 @@ #include "core/file_sys/patch_manager.h" #include "core/file_sys/registered_cache.h" #include "core/file_sys/romfs_factory.h" +#include "core/hle/kernel/process.h" #include "core/hle/service/filesystem/filesystem.h" #include "core/loader/loader.h" diff --git a/src/core/hle/service/filesystem/filesystem.cpp b/src/core/hle/service/filesystem/filesystem.cpp index e9d5bd7748..04c9d750fd 100644 --- a/src/core/hle/service/filesystem/filesystem.cpp +++ b/src/core/hle/service/filesystem/filesystem.cpp @@ -10,6 +10,7 @@ #include "core/file_sys/bis_factory.h" #include "core/file_sys/errors.h" #include "core/file_sys/mode.h" +#include "core/file_sys/registered_cache.h" #include "core/file_sys/romfs_factory.h" #include "core/file_sys/savedata_factory.h" #include "core/file_sys/sdmc_factory.h" @@ -19,7 +20,6 @@ #include "core/hle/service/filesystem/fsp_ldr.h" #include "core/hle/service/filesystem/fsp_pr.h" #include "core/hle/service/filesystem/fsp_srv.h" -#include "filesystem.h" namespace Service::FileSystem { diff --git a/src/core/loader/loader.cpp b/src/core/loader/loader.cpp index 729b1ca088..fa43a26506 100644 --- a/src/core/loader/loader.cpp +++ b/src/core/loader/loader.cpp @@ -93,7 +93,7 @@ std::string GetFileTypeString(FileType type) { return "unknown"; } -constexpr std::array RESULT_MESSAGES{ +constexpr std::array RESULT_MESSAGES{ "The operation completed successfully.", "The loader requested to load is already loaded.", "The operation is not implemented.", @@ -143,7 +143,7 @@ constexpr std::array RESULT_MESSAGES{ "The AES Key Generation Source could not be found.", "The SD Save Key Source could not be found.", "The SD NCA Key Source could not be found.", - "The NSP file is missing a Program-type NCA."}; + "The NSP file is missing a Program-type NCA.", "The BKTR-type NCA has a bad BKTR header.", "The BKTR Subsection entry is not located immediately after the Relocation entry.", "The BKTR Subsection entry is not at the end of the media block.", diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 80a2845133..dbe5bd8a4f 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -32,6 +32,8 @@ #include "core/crypto/key_manager.h" #include "core/file_sys/card_image.h" #include "core/file_sys/content_archive.h" +#include "core/file_sys/control_metadata.h" +#include "core/file_sys/patch_manager.h" #include "core/file_sys/registered_cache.h" #include "core/file_sys/savedata_factory.h" #include "core/file_sys/submission_package.h" -- cgit v1.2.3