From 99fbcb3bf2ddeb0e95ee4b7ce891f46f44447af3 Mon Sep 17 00:00:00 2001 From: Zach Hilman Date: Sat, 25 Aug 2018 18:56:25 -0400 Subject: [PATCH 01/20] aes_util: Fix error involving reads of less than 0x10 Issues with block size are fixed by making all reads minimum length of 0x10 --- src/core/crypto/aes_util.cpp | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/core/crypto/aes_util.cpp b/src/core/crypto/aes_util.cpp index 72e4bed67..89ade5000 100644 --- a/src/core/crypto/aes_util.cpp +++ b/src/core/crypto/aes_util.cpp @@ -82,11 +82,25 @@ void AESCipher::Transcode(const u8* src, size_t size, u8* dest, Op } } else { const auto block_size = mbedtls_cipher_get_block_size(context); + if (size < block_size) { + std::vector block(block_size); + std::memcpy(block.data(), src, size); + Transcode(block.data(), block.size(), block.data(), op); + std::memcpy(dest, block.data(), size); + return; + } for (size_t offset = 0; offset < size; offset += block_size) { auto length = std::min(block_size, size - offset); mbedtls_cipher_update(context, src + offset, length, dest + offset, &written); if (written != length) { + if (length < block_size) { + std::vector block(block_size); + std::memcpy(block.data(), src + offset, length); + Transcode(block.data(), block.size(), block.data(), op); + std::memcpy(dest + offset, block.data(), length); + return; + } LOG_WARNING(Crypto, "Not all data was decrypted requested={:016X}, actual={:016X}.", length, written); } From d2caf4af7da36f3947eea17656f9c85a6709707c Mon Sep 17 00:00:00 2001 From: Zach Hilman Date: Sat, 25 Aug 2018 18:59:19 -0400 Subject: [PATCH 02/20] game_list: Use RegisteredCacheUnion for installed Reduces code --- src/core/crypto/ctr_encryption_layer.cpp | 2 +- src/yuzu/game_list.cpp | 4 +--- src/yuzu/game_list_p.h | 2 +- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/src/core/crypto/ctr_encryption_layer.cpp b/src/core/crypto/ctr_encryption_layer.cpp index 3ea60dbd0..296fad419 100644 --- a/src/core/crypto/ctr_encryption_layer.cpp +++ b/src/core/crypto/ctr_encryption_layer.cpp @@ -21,7 +21,7 @@ size_t CTREncryptionLayer::Read(u8* data, size_t length, size_t offset) const { UpdateIV(base_offset + offset); std::vector raw = base->ReadBytes(length, offset); cipher.Transcode(raw.data(), raw.size(), data, Op::Decrypt); - return raw.size(); + return length; } // offset does not fall on block boundary (0x10) diff --git a/src/yuzu/game_list.cpp b/src/yuzu/game_list.cpp index 3e2a5976b..baf78af09 100644 --- a/src/yuzu/game_list.cpp +++ b/src/yuzu/game_list.cpp @@ -621,9 +621,7 @@ void GameListWorker::run() { stop_processing = false; watch_list.append(dir_path); FillControlMap(dir_path.toStdString()); - AddInstalledTitlesToGameList(Service::FileSystem::GetUserNANDContents()); - AddInstalledTitlesToGameList(Service::FileSystem::GetSystemNANDContents()); - AddInstalledTitlesToGameList(Service::FileSystem::GetSDMCContents()); + AddInstalledTitlesToGameList(); AddFstEntriesToGameList(dir_path.toStdString(), deep_scan ? 256 : 0); nca_control_map.clear(); emit Finished(watch_list); diff --git a/src/yuzu/game_list_p.h b/src/yuzu/game_list_p.h index 4ddd8cd88..a70a151c5 100644 --- a/src/yuzu/game_list_p.h +++ b/src/yuzu/game_list_p.h @@ -239,7 +239,7 @@ private: const std::unordered_map>& compatibility_list; std::atomic_bool stop_processing; - void AddInstalledTitlesToGameList(std::shared_ptr cache); + void AddInstalledTitlesToGameList(); void FillControlMap(const std::string& dir_path); void AddFstEntriesToGameList(const std::string& dir_path, unsigned int recursion = 0); }; From 9951f6d0543980e29e6107e0bd4ea35977f1cf29 Mon Sep 17 00:00:00 2001 From: Zach Hilman Date: Sat, 25 Aug 2018 19:00:36 -0400 Subject: [PATCH 03/20] registration: Add RegisteredCacheUnion Aggregates multiple caches into one interface --- src/core/file_sys/registered_cache.cpp | 114 ++++++++++++++++++ src/core/file_sys/registered_cache.h | 40 ++++++ .../hle/service/filesystem/filesystem.cpp | 7 ++ src/core/hle/service/filesystem/filesystem.h | 3 + 4 files changed, 164 insertions(+) diff --git a/src/core/file_sys/registered_cache.cpp b/src/core/file_sys/registered_cache.cpp index cf6f77401..39c0710e1 100644 --- a/src/core/file_sys/registered_cache.cpp +++ b/src/core/file_sys/registered_cache.cpp @@ -280,6 +280,14 @@ VirtualFile RegisteredCache::GetEntryUnparsed(RegisteredCacheEntry entry) const return GetEntryUnparsed(entry.title_id, entry.type); } +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(); + return boost::none; +} + VirtualFile RegisteredCache::GetEntryRaw(u64 title_id, ContentRecordType type) const { const auto id = GetNcaIDFromMetadata(title_id, type); if (id == boost::none) @@ -498,4 +506,110 @@ bool RegisteredCache::RawInstallYuzuMeta(const CNMT& cnmt) { kv.second.GetTitleID() == cnmt.GetTitleID(); }) != yuzu_meta.end(); } + +RegisteredCacheUnion::RegisteredCacheUnion(std::vector> caches) + : caches(std::move(caches)) {} + +void RegisteredCacheUnion::Refresh() { + for (const auto& c : caches) + c->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; +} + +bool RegisteredCacheUnion::HasEntry(RegisteredCacheEntry entry) const { + return HasEntry(entry.title_id, entry.type); +} + +boost::optional RegisteredCacheUnion::GetEntryVersion(u64 title_id) const { + for (const auto& c : caches) { + const auto res = c->GetEntryVersion(title_id); + if (res != boost::none) + return res; + } + + return boost::none; +} + +VirtualFile RegisteredCacheUnion::GetEntryUnparsed(u64 title_id, ContentRecordType type) const { + for (const auto& c : caches) { + const auto res = c->GetEntryUnparsed(title_id, type); + if (res != nullptr) + return res; + } + + return nullptr; +} + +VirtualFile RegisteredCacheUnion::GetEntryUnparsed(RegisteredCacheEntry entry) const { + return GetEntryUnparsed(entry.title_id, entry.type); +} + +VirtualFile RegisteredCacheUnion::GetEntryRaw(u64 title_id, ContentRecordType type) const { + for (const auto& c : caches) { + const auto res = c->GetEntryRaw(title_id, type); + if (res != nullptr) + return res; + } + + return nullptr; +} + +VirtualFile RegisteredCacheUnion::GetEntryRaw(RegisteredCacheEntry entry) const { + return GetEntryRaw(entry.title_id, entry.type); +} + +std::shared_ptr RegisteredCacheUnion::GetEntry(u64 title_id, ContentRecordType type) const { + const auto raw = GetEntryRaw(title_id, type); + if (raw == nullptr) + return nullptr; + return std::make_shared(raw); +} + +std::shared_ptr RegisteredCacheUnion::GetEntry(RegisteredCacheEntry entry) const { + return GetEntry(entry.title_id, entry.type); +} + +std::vector RegisteredCacheUnion::ListEntries() const { + std::vector out; + for (const auto& c : caches) { + c->IterateAllMetadata( + out, + [](const CNMT& c, const ContentRecord& r) { + return RegisteredCacheEntry{c.GetTitleID(), r.type}; + }, + [](const CNMT& c, const ContentRecord& r) { return true; }); + } + return out; +} + +std::vector RegisteredCacheUnion::ListEntriesFilter( + boost::optional title_type, boost::optional record_type, + boost::optional title_id) const { + std::vector out; + for (const auto& c : caches) { + c->IterateAllMetadata( + out, + [](const CNMT& c, const ContentRecord& r) { + return RegisteredCacheEntry{c.GetTitleID(), r.type}; + }, + [&title_type, &record_type, &title_id](const CNMT& c, const ContentRecord& r) { + if (title_type != boost::none && title_type.get() != c.GetType()) + return false; + if (record_type != boost::none && record_type.get() != r.type) + return false; + if (title_id != boost::none && title_id.get() != c.GetTitleID()) + return false; + return true; + }); + } + return out; +} } // namespace FileSys diff --git a/src/core/file_sys/registered_cache.h b/src/core/file_sys/registered_cache.h index 467ceeef1..dcce3fd16 100644 --- a/src/core/file_sys/registered_cache.h +++ b/src/core/file_sys/registered_cache.h @@ -43,6 +43,10 @@ struct RegisteredCacheEntry { std::string DebugInfo() const; }; +constexpr inline u64 GetUpdateTitleID(u64 base_title_id) { + return base_title_id | 0x800; +} + // boost flat_map requires operator< for O(log(n)) lookups. bool operator<(const RegisteredCacheEntry& lhs, const RegisteredCacheEntry& rhs); @@ -60,6 +64,8 @@ bool operator<(const RegisteredCacheEntry& lhs, const RegisteredCacheEntry& rhs) * 4GB splitting can be ignored.) */ class RegisteredCache { + friend class RegisteredCacheUnion; + public: // Parsing function defines the conversion from raw file to NCA. If there are other steps // besides creating the NCA from the file (e.g. NAX0 on SD Card), that should go in a custom @@ -74,6 +80,8 @@ public: bool HasEntry(u64 title_id, ContentRecordType type) const; bool HasEntry(RegisteredCacheEntry entry) const; + boost::optional GetEntryVersion(u64 title_id) const; + VirtualFile GetEntryUnparsed(u64 title_id, ContentRecordType type) const; VirtualFile GetEntryUnparsed(RegisteredCacheEntry entry) const; @@ -131,4 +139,36 @@ private: boost::container::flat_map yuzu_meta; }; +// Combines multiple RegisteredCaches (i.e. SysNAND, UserNAND, SDMC) into one interface. +class RegisteredCacheUnion { +public: + explicit RegisteredCacheUnion(std::vector> caches); + + void Refresh(); + + bool HasEntry(u64 title_id, ContentRecordType type) const; + bool HasEntry(RegisteredCacheEntry entry) const; + + boost::optional GetEntryVersion(u64 title_id) const; + + VirtualFile GetEntryUnparsed(u64 title_id, ContentRecordType type) const; + VirtualFile GetEntryUnparsed(RegisteredCacheEntry entry) const; + + VirtualFile GetEntryRaw(u64 title_id, ContentRecordType type) const; + VirtualFile GetEntryRaw(RegisteredCacheEntry entry) const; + + std::shared_ptr GetEntry(u64 title_id, ContentRecordType type) const; + std::shared_ptr GetEntry(RegisteredCacheEntry entry) const; + + std::vector ListEntries() const; + // If a parameter is not boost::none, it will be filtered for from all entries. + std::vector ListEntriesFilter( + boost::optional title_type = boost::none, + boost::optional record_type = boost::none, + boost::optional title_id = boost::none) const; + +private: + std::vector> caches; +}; + } // namespace FileSys diff --git a/src/core/hle/service/filesystem/filesystem.cpp b/src/core/hle/service/filesystem/filesystem.cpp index a4426af96..e9d5bd774 100644 --- a/src/core/hle/service/filesystem/filesystem.cpp +++ b/src/core/hle/service/filesystem/filesystem.cpp @@ -19,6 +19,7 @@ #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 { @@ -307,6 +308,12 @@ ResultVal OpenSDMC() { return sdmc_factory->Open(); } +std::shared_ptr GetUnionContents() { + return std::make_shared( + std::vector>{ + GetSystemNANDContents(), GetUserNANDContents(), GetSDMCContents()}); +} + std::shared_ptr GetSystemNANDContents() { LOG_TRACE(Service_FS, "Opening System NAND Contents"); diff --git a/src/core/hle/service/filesystem/filesystem.h b/src/core/hle/service/filesystem/filesystem.h index 9ba0e2eab..793a7b06f 100644 --- a/src/core/hle/service/filesystem/filesystem.h +++ b/src/core/hle/service/filesystem/filesystem.h @@ -13,6 +13,7 @@ namespace FileSys { class BISFactory; class RegisteredCache; +class RegisteredCacheUnion; class RomFSFactory; class SaveDataFactory; class SDMCFactory; @@ -45,6 +46,8 @@ ResultVal OpenSaveData(FileSys::SaveDataSpaceId space, FileSys::SaveDataDescriptor save_struct); ResultVal OpenSDMC(); +std::shared_ptr GetUnionContents(); + std::shared_ptr GetSystemNANDContents(); std::shared_ptr GetUserNANDContents(); std::shared_ptr GetSDMCContents(); From 1efe5a76b11a5e7fd8071686acaf1b060e721aaa Mon Sep 17 00:00:00 2001 From: Zach Hilman Date: Sat, 25 Aug 2018 19:01:46 -0400 Subject: [PATCH 04/20] content_archive: Add BKTR header parsing to NCA --- src/core/file_sys/content_archive.cpp | 171 +++++++++++++++++++++++--- src/core/file_sys/content_archive.h | 8 +- 2 files changed, 160 insertions(+), 19 deletions(-) diff --git a/src/core/file_sys/content_archive.cpp b/src/core/file_sys/content_archive.cpp index 7cfb6f36b..f0d376bf5 100644 --- a/src/core/file_sys/content_archive.cpp +++ b/src/core/file_sys/content_archive.cpp @@ -12,6 +12,7 @@ #include "core/crypto/aes_util.h" #include "core/crypto/ctr_encryption_layer.h" #include "core/file_sys/content_archive.h" +#include "core/file_sys/nca_patch.h" #include "core/file_sys/partition_filesystem.h" #include "core/file_sys/romfs.h" #include "core/file_sys/vfs_offset.h" @@ -68,10 +69,31 @@ struct RomFSSuperblock { }; static_assert(sizeof(RomFSSuperblock) == 0x200, "RomFSSuperblock has incorrect size."); +struct BKTRHeader { + u64_le offset; + u64_le size; + u32_le magic; + INSERT_PADDING_BYTES(0x4); + u32_le number_entries; + INSERT_PADDING_BYTES(0x4); +}; +static_assert(sizeof(BKTRHeader) == 0x20, "BKTRHeader has incorrect size."); + +struct BKTRSuperblock { + NCASectionHeaderBlock header_block; + IVFCHeader ivfc; + INSERT_PADDING_BYTES(0x18); + BKTRHeader relocation; + BKTRHeader subsection; + INSERT_PADDING_BYTES(0xC0); +}; +static_assert(sizeof(BKTRSuperblock) == 0x200, "BKTRSuperblock has incorrect size."); + union NCASectionHeader { NCASectionRaw raw; PFS0Superblock pfs0; RomFSSuperblock romfs; + BKTRSuperblock bktr; }; static_assert(sizeof(NCASectionHeader) == 0x200, "NCASectionHeader has incorrect size."); @@ -104,7 +126,7 @@ boost::optional NCA::GetKeyAreaKey(NCASectionCryptoType ty Core::Crypto::Key128 out; if (type == NCASectionCryptoType::XTS) std::copy(key_area.begin(), key_area.begin() + 0x10, out.begin()); - else if (type == NCASectionCryptoType::CTR) + else if (type == NCASectionCryptoType::CTR || type == NCASectionCryptoType::BKTR) std::copy(key_area.begin() + 0x20, key_area.begin() + 0x30, out.begin()); else LOG_CRITICAL(Crypto, "Called GetKeyAreaKey on invalid NCASectionCryptoType type={:02X}", @@ -154,6 +176,9 @@ VirtualFile NCA::Decrypt(NCASectionHeader s_header, VirtualFile in, u64 starting LOG_DEBUG(Crypto, "called with mode=NONE"); return in; case NCASectionCryptoType::CTR: + // During normal BKTR decryption, this entire function is skipped. This is for the metadata, + // which uses the same CTR as usual. + case NCASectionCryptoType::BKTR: LOG_DEBUG(Crypto, "called with mode=CTR, starting_offset={:016X}", starting_offset); { boost::optional key = boost::none; @@ -190,7 +215,9 @@ VirtualFile NCA::Decrypt(NCASectionHeader s_header, VirtualFile in, u64 starting } } -NCA::NCA(VirtualFile file_) : file(std::move(file_)) { +NCA::NCA(VirtualFile file_, VirtualFile bktr_base_romfs_) + : file(std::move(file_)), + bktr_base_romfs(bktr_base_romfs_ ? std::move(bktr_base_romfs_) : nullptr) { status = Loader::ResultStatus::Success; if (file == nullptr) { @@ -270,17 +297,15 @@ NCA::NCA(VirtualFile file_) : file(std::move(file_)) { auto section = sections[i]; 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 = - header.section_tables[i].media_offset * MEDIA_OFFSET_MULTIPLIER + - section.romfs.ivfc.levels[IVFC_MAX_LEVEL - 1].offset; + base_offset + section.romfs.ivfc.levels[IVFC_MAX_LEVEL - 1].offset; const size_t romfs_size = section.romfs.ivfc.levels[IVFC_MAX_LEVEL - 1].size; - auto dec = - Decrypt(section, std::make_shared(file, romfs_size, romfs_offset), - romfs_offset); - if (dec != nullptr) { - files.push_back(std::move(dec)); - romfs = files.back(); - } else { + 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) @@ -289,6 +314,120 @@ NCA::NCA(VirtualFile file_) : file(std::move(file_)) { 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, + romfs_offset - base_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(); + 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) * MEDIA_OFFSET_MULTIPLIER) + @@ -349,11 +488,17 @@ NCAContentType NCA::GetType() const { } u64 NCA::GetTitleId() const { + if (is_update || status == Loader::ResultStatus::ErrorMissingBKTRBaseRomFS) + return header.title_id | 0x800; if (status != Loader::ResultStatus::Success) return {}; return header.title_id; } +bool NCA::IsUpdate() const { + return is_update; +} + VirtualFile NCA::GetRomFS() const { return romfs; } @@ -366,10 +511,6 @@ VirtualFile NCA::GetBaseFile() const { return file; } -bool NCA::IsUpdate() const { - return is_update; -} - 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 0ea666cac..104226f3a 100644 --- a/src/core/file_sys/content_archive.h +++ b/src/core/file_sys/content_archive.h @@ -79,7 +79,7 @@ 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); + explicit NCA(VirtualFile file, VirtualFile bktr_base_romfs = nullptr); Loader::ResultStatus GetStatus() const; std::vector> GetFiles() const override; @@ -89,14 +89,13 @@ public: NCAContentType GetType() const; u64 GetTitleId() const; + bool IsUpdate() const; VirtualFile GetRomFS() const; VirtualDir GetExeFS() const; VirtualFile GetBaseFile() const; - bool IsUpdate() const; - protected: bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override; @@ -112,14 +111,15 @@ private: VirtualFile romfs = nullptr; VirtualDir exefs = nullptr; VirtualFile file; + VirtualFile bktr_base_romfs; NCAHeader header{}; bool has_rights_id{}; - bool is_update{}; Loader::ResultStatus status{}; bool encrypted; + bool is_update; Core::Crypto::KeyManager keys; }; From 54e7ddb93a1a0357c14d03aeb182c7c98bb9cebb Mon Sep 17 00:00:00 2001 From: Zach Hilman Date: Sat, 25 Aug 2018 19:03:03 -0400 Subject: [PATCH 05/20] file_sys: Add BKTR patching mechanism --- src/core/file_sys/nca_patch.cpp | 208 ++++++++++++++++++++++++++++++++ src/core/file_sys/nca_patch.h | 144 ++++++++++++++++++++++ 2 files changed, 352 insertions(+) create mode 100644 src/core/file_sys/nca_patch.cpp create mode 100644 src/core/file_sys/nca_patch.h diff --git a/src/core/file_sys/nca_patch.cpp b/src/core/file_sys/nca_patch.cpp new file mode 100644 index 000000000..dd684c38e --- /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 diff --git a/src/core/file_sys/nca_patch.h b/src/core/file_sys/nca_patch.h new file mode 100644 index 000000000..8b8d0a4f5 --- /dev/null +++ b/src/core/file_sys/nca_patch.h @@ -0,0 +1,144 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "core/crypto/key_manager.h" +#include "core/file_sys/romfs.h" +#include "core/loader/loader.h" + +namespace FileSys { + +#pragma pack(push, 1) +struct RelocationEntry { + u64_le address_patch; + u64_le address_source; + u32 from_patch; +}; +#pragma pack(pop) +static_assert(sizeof(RelocationEntry) == 0x14, "RelocationEntry has incorrect size."); + +struct RelocationBucketRaw { + INSERT_PADDING_BYTES(4); + u32_le number_entries; + u64_le end_offset; + std::array relocation_entries; + INSERT_PADDING_BYTES(8); +}; +static_assert(sizeof(RelocationBucketRaw) == 0x4000, "RelocationBucketRaw has incorrect size."); + +// Vector version of RelocationBucketRaw +struct RelocationBucket { + u32 number_entries; + u64 end_offset; + std::vector entries; +}; + +struct RelocationBlock { + INSERT_PADDING_BYTES(4); + u32_le number_buckets; + u64_le size; + std::array base_offsets; +}; +static_assert(sizeof(RelocationBlock) == 0x4000, "RelocationBlock has incorrect size."); + +struct SubsectionEntry { + u64_le address_patch; + INSERT_PADDING_BYTES(0x4); + u32_le ctr; +}; +static_assert(sizeof(SubsectionEntry) == 0x10, "SubsectionEntry has incorrect size."); + +struct SubsectionBucketRaw { + INSERT_PADDING_BYTES(4); + u32_le number_entries; + u64_le end_offset; + std::array subsection_entries; +}; +static_assert(sizeof(SubsectionBucketRaw) == 0x4000, "SubsectionBucketRaw has incorrect size."); + +// Vector version of SubsectionBucketRaw +struct SubsectionBucket { + u32 number_entries; + u64 end_offset; + std::vector entries; +}; + +struct SubsectionBlock { + INSERT_PADDING_BYTES(4); + u32_le number_buckets; + u64_le size; + std::array base_offsets; +}; +static_assert(sizeof(SubsectionBlock) == 0x4000, "SubsectionBlock has incorrect size."); + +inline RelocationBucket ConvertRelocationBucketRaw(RelocationBucketRaw raw) { + return {raw.number_entries, + raw.end_offset, + {raw.relocation_entries.begin(), raw.relocation_entries.begin() + raw.number_entries}}; +} + +inline SubsectionBucket ConvertSubsectionBucketRaw(SubsectionBucketRaw raw) { + return {raw.number_entries, + raw.end_offset, + {raw.subsection_entries.begin(), raw.subsection_entries.begin() + raw.number_entries}}; +} + +class BKTR : public VfsFile { +public: + 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); + + size_t Read(u8* data, size_t length, size_t offset) const override; + + std::string GetName() const override; + + size_t GetSize() const override; + + bool Resize(size_t new_size) override; + + std::shared_ptr GetContainingDirectory() const override; + + bool IsWritable() const override; + + bool IsReadable() const override; + + size_t Write(const u8* data, size_t length, size_t offset) override; + + bool Rename(std::string_view name) override; + +private: + template + std::pair SearchBucketEntry(u64 offset, BlockType block, + BucketType buckets) const; + + RelocationEntry GetRelocationEntry(u64 offset) const; + RelocationEntry GetNextRelocationEntry(u64 offset) const; + + SubsectionEntry GetSubsectionEntry(u64 offset) const; + SubsectionEntry GetNextSubsectionEntry(u64 offset) const; + + RelocationBlock relocation; + std::vector relocation_buckets; + SubsectionBlock subsection; + std::vector subsection_buckets; + + // Should be the raw base romfs, decrypted. + VirtualFile base_romfs; + // Should be the raw BKTR romfs, (located at media_offset with size media_size). + VirtualFile bktr_romfs; + + bool encrypted; + Core::Crypto::Key128 key; + + // Base offset into NCA, used for IV calculation. + u64 base_offset; + // Distance between IVFC start and RomFS start, used for base reads + u64 ivfc_offset; + std::array section_ctr; +}; + +} // namespace FileSys From 8e900a301a1094d74f68f173c7dec67b12baec25 Mon Sep 17 00:00:00 2001 From: Zach Hilman Date: Sat, 25 Aug 2018 19:03:45 -0400 Subject: [PATCH 06/20] file_sys: Add class to manage game patches Right now only includes Updates, but should eventually contain all of the other patches we need. --- src/core/file_sys/patch_manager.cpp | 90 +++++++++++++++++++++++++++++ src/core/file_sys/patch_manager.h | 42 ++++++++++++++ 2 files changed, 132 insertions(+) create mode 100644 src/core/file_sys/patch_manager.cpp create mode 100644 src/core/file_sys/patch_manager.h diff --git a/src/core/file_sys/patch_manager.cpp b/src/core/file_sys/patch_manager.cpp new file mode 100644 index 000000000..697b8a4c9 --- /dev/null +++ b/src/core/file_sys/patch_manager.cpp @@ -0,0 +1,90 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "core/file_sys/patch_manager.h" +#include "core/file_sys/registered_cache.h" +#include "core/hle/service/filesystem/filesystem.h" + +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_; + + 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); +} + +constexpr std::array PATCH_TYPE_NAMES{ + "Update", +}; + +std::string FormatPatchTypeName(PatchType type) { + return PATCH_TYPE_NAMES.at(static_cast(type)); +} + +PatchManager::PatchManager(u64 title_id) : title_id(title_id) {} + +VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const { + if (exefs == nullptr) + return exefs; + + const auto installed = Service::FileSystem::GetUnionContents(); + + // Game Updates + const auto update_tid = GetUpdateTitleID(title_id); + const auto update = installed->GetEntry(update_tid, ContentRecordType::Program); + if (update != nullptr) { + if (update->GetStatus() == Loader::ResultStatus::ErrorMissingBKTRBaseRomFS && + update->GetExeFS() != nullptr) + exefs = update->GetExeFS(); + } + + return exefs; +} + +VirtualFile PatchManager::PatchRomFS(VirtualFile romfs) const { + if (romfs == nullptr) + return romfs; + + const auto installed = Service::FileSystem::GetUnionContents(); + + // Game Updates + const auto update_tid = GetUpdateTitleID(title_id); + const auto update = installed->GetEntryRaw(update_tid, ContentRecordType::Program); + if (update != nullptr) { + const auto nca = std::make_shared(update, romfs); + if (nca->GetStatus() == Loader::ResultStatus::Success && nca->GetRomFS() != nullptr) + romfs = nca->GetRomFS(); + } + + return romfs; +} + +std::map PatchManager::GetPatchVersionNames() const { + std::map out; + const auto installed = Service::FileSystem::GetUnionContents(); + + 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)) + out[PatchType::Update] = update_version.get(); + + return out; +} + +} // namespace FileSys diff --git a/src/core/file_sys/patch_manager.h b/src/core/file_sys/patch_manager.h new file mode 100644 index 000000000..2a39c473a --- /dev/null +++ b/src/core/file_sys/patch_manager.h @@ -0,0 +1,42 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include +#include "common/common_types.h" +#include "core/file_sys/vfs.h" + +namespace FileSys { + +std::string FormatTitleVersion(u32 version, bool full = false); + +enum class PatchType { + Update, +}; + +std::string FormatPatchTypeName(PatchType type); + +// A centralized class to manage patches to games. +class PatchManager { +public: + explicit PatchManager(u64 title_id); + + // Currently tracked ExeFS patches: + // - Game Updates + VirtualDir PatchExeFS(VirtualDir exefs) const; + + // Currently tracked RomFS patches: + // - Game Updates + VirtualFile PatchRomFS(VirtualFile romfs) const; + + // Returns a vector of pairs between patch names and patch versions. + // i.e. Update v80 will return {Update, 80} + std::map GetPatchVersionNames() const; + +private: + u64 title_id; +}; + +} // namespace FileSys From 97bf83bc56860be244077e9213468466f894c73d Mon Sep 17 00:00:00 2001 From: Zach Hilman Date: Sat, 25 Aug 2018 19:04:48 -0400 Subject: [PATCH 07/20] patch_manager: Add usages of patches to ExeFS --- src/core/file_sys/romfs_factory.cpp | 11 ++++++- src/core/file_sys/romfs_factory.h | 1 + .../loader/deconstructed_rom_directory.cpp | 29 +++++++++++++++---- src/core/loader/deconstructed_rom_directory.h | 7 +++-- src/core/loader/nca.cpp | 2 +- 5 files changed, 41 insertions(+), 9 deletions(-) diff --git a/src/core/file_sys/romfs_factory.cpp b/src/core/file_sys/romfs_factory.cpp index 66f9786e0..fc9cf1eca 100644 --- a/src/core/file_sys/romfs_factory.cpp +++ b/src/core/file_sys/romfs_factory.cpp @@ -6,7 +6,10 @@ #include "common/assert.h" #include "common/common_types.h" #include "common/logging/log.h" +#include "core/core.h" #include "core/file_sys/content_archive.h" +#include "core/file_sys/nca_metadata.h" +#include "core/file_sys/patch_manager.h" #include "core/file_sys/registered_cache.h" #include "core/file_sys/romfs_factory.h" #include "core/hle/service/filesystem/filesystem.h" @@ -19,10 +22,16 @@ RomFSFactory::RomFSFactory(Loader::AppLoader& app_loader) { if (app_loader.ReadRomFS(file) != Loader::ResultStatus::Success) { LOG_ERROR(Service_FS, "Unable to read RomFS!"); } + + updatable = app_loader.IsRomFSUpdatable(); } ResultVal RomFSFactory::OpenCurrentProcess() { - return MakeResult(file); + if (!updatable) + return MakeResult(file); + + const PatchManager patch_manager(Core::CurrentProcess()->process_id); + return MakeResult(patch_manager.PatchRomFS(file)); } 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 f38ddc4f7..168db1c46 100644 --- a/src/core/file_sys/romfs_factory.h +++ b/src/core/file_sys/romfs_factory.h @@ -36,6 +36,7 @@ public: private: VirtualFile file; + bool updatable; }; } // namespace FileSys diff --git a/src/core/loader/deconstructed_rom_directory.cpp b/src/core/loader/deconstructed_rom_directory.cpp index 1ae4bb656..04ef55da1 100644 --- a/src/core/loader/deconstructed_rom_directory.cpp +++ b/src/core/loader/deconstructed_rom_directory.cpp @@ -9,6 +9,7 @@ #include "core/core.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/romfs_factory.h" #include "core/gdbstub/gdbstub.h" #include "core/hle/kernel/kernel.h" @@ -21,8 +22,9 @@ namespace Loader { -AppLoader_DeconstructedRomDirectory::AppLoader_DeconstructedRomDirectory(FileSys::VirtualFile file_) - : AppLoader(std::move(file_)) { +AppLoader_DeconstructedRomDirectory::AppLoader_DeconstructedRomDirectory(FileSys::VirtualFile file_, + bool override_update) + : AppLoader(std::move(file_)), override_update(override_update) { const auto dir = file->GetContainingDirectory(); // Icon @@ -66,8 +68,9 @@ AppLoader_DeconstructedRomDirectory::AppLoader_DeconstructedRomDirectory(FileSys } AppLoader_DeconstructedRomDirectory::AppLoader_DeconstructedRomDirectory( - FileSys::VirtualDir directory) - : AppLoader(directory->GetFile("main")), dir(std::move(directory)) {} + FileSys::VirtualDir directory, bool override_update) + : AppLoader(directory->GetFile("main")), dir(std::move(directory)), + override_update(override_update) {} FileType AppLoader_DeconstructedRomDirectory::IdentifyType(const FileSys::VirtualFile& file) { if (FileSys::IsDirectoryExeFS(file->GetContainingDirectory())) { @@ -89,7 +92,8 @@ ResultStatus AppLoader_DeconstructedRomDirectory::Load( dir = file->GetContainingDirectory(); } - const FileSys::VirtualFile npdm = dir->GetFile("main.npdm"); + // Read meta to determine title ID + FileSys::VirtualFile npdm = dir->GetFile("main.npdm"); if (npdm == nullptr) return ResultStatus::ErrorMissingNPDM; @@ -97,6 +101,21 @@ ResultStatus AppLoader_DeconstructedRomDirectory::Load( if (result != ResultStatus::Success) { return result; } + + if (override_update) { + const FileSys::PatchManager patch_manager(metadata.GetTitleID()); + dir = patch_manager.PatchExeFS(dir); + } + + // Reread in case PatchExeFS affected the main.npdm + npdm = dir->GetFile("main.npdm"); + if (npdm == nullptr) + return ResultStatus::ErrorMissingNPDM; + + ResultStatus result2 = metadata.Load(npdm); + if (result2 != ResultStatus::Success) { + return result2; + } metadata.Print(); const FileSys::ProgramAddressSpaceType arch_bits{metadata.GetAddressSpaceType()}; diff --git a/src/core/loader/deconstructed_rom_directory.h b/src/core/loader/deconstructed_rom_directory.h index b20804f75..cb50f8bff 100644 --- a/src/core/loader/deconstructed_rom_directory.h +++ b/src/core/loader/deconstructed_rom_directory.h @@ -20,10 +20,12 @@ namespace Loader { */ class AppLoader_DeconstructedRomDirectory final : public AppLoader { public: - explicit AppLoader_DeconstructedRomDirectory(FileSys::VirtualFile main_file); + explicit AppLoader_DeconstructedRomDirectory(FileSys::VirtualFile main_file, + bool override_update = false); // Overload to accept exefs directory. Must contain 'main' and 'main.npdm' - explicit AppLoader_DeconstructedRomDirectory(FileSys::VirtualDir directory); + explicit AppLoader_DeconstructedRomDirectory(FileSys::VirtualDir directory, + bool override_update = false); /** * Returns the type of the file @@ -51,6 +53,7 @@ private: std::vector icon_data; std::string name; u64 title_id{}; + bool override_update; }; } // namespace Loader diff --git a/src/core/loader/nca.cpp b/src/core/loader/nca.cpp index c036a8a1c..6b1c27b47 100644 --- a/src/core/loader/nca.cpp +++ b/src/core/loader/nca.cpp @@ -48,7 +48,7 @@ ResultStatus AppLoader_NCA::Load(Kernel::SharedPtr& process) { if (exefs == nullptr) return ResultStatus::ErrorNoExeFS; - directory_loader = std::make_unique(exefs); + directory_loader = std::make_unique(exefs, true); const auto load_result = directory_loader->Load(process); if (load_result != ResultStatus::Success) From 08fcb4694f28fab40d76cc60fcd2fc35fb24917f Mon Sep 17 00:00:00 2001 From: Zach Hilman Date: Sat, 25 Aug 2018 19:05:04 -0400 Subject: [PATCH 08/20] loader: Ignore patches on NRO and DRD --- src/core/loader/deconstructed_rom_directory.cpp | 4 ++++ src/core/loader/deconstructed_rom_directory.h | 1 + src/core/loader/nro.cpp | 5 +++++ src/core/loader/nro.h | 1 + 4 files changed, 11 insertions(+) diff --git a/src/core/loader/deconstructed_rom_directory.cpp b/src/core/loader/deconstructed_rom_directory.cpp index 04ef55da1..6b2230269 100644 --- a/src/core/loader/deconstructed_rom_directory.cpp +++ b/src/core/loader/deconstructed_rom_directory.cpp @@ -189,4 +189,8 @@ ResultStatus AppLoader_DeconstructedRomDirectory::ReadTitle(std::string& title) return ResultStatus::Success; } +bool AppLoader_DeconstructedRomDirectory::IsRomFSUpdatable() { + return false; +} + } // namespace Loader diff --git a/src/core/loader/deconstructed_rom_directory.h b/src/core/loader/deconstructed_rom_directory.h index cb50f8bff..a8644516b 100644 --- a/src/core/loader/deconstructed_rom_directory.h +++ b/src/core/loader/deconstructed_rom_directory.h @@ -44,6 +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; private: FileSys::ProgramMetadata metadata; diff --git a/src/core/loader/nro.cpp b/src/core/loader/nro.cpp index 77026b850..96f5cd9e5 100644 --- a/src/core/loader/nro.cpp +++ b/src/core/loader/nro.cpp @@ -232,4 +232,9 @@ ResultStatus AppLoader_NRO::ReadTitle(std::string& title) { title = nacp->GetApplicationName(); return ResultStatus::Success; } + +bool AppLoader_NRO::IsRomFSUpdatable() { + return false; +} + } // namespace Loader diff --git a/src/core/loader/nro.h b/src/core/loader/nro.h index bb01c9e25..c35c99d14 100644 --- a/src/core/loader/nro.h +++ b/src/core/loader/nro.h @@ -39,6 +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; private: bool LoadNro(FileSys::VirtualFile file, VAddr load_base); From f5e03b9173268d2607b9db379fef93170212328a Mon Sep 17 00:00:00 2001 From: Zach Hilman Date: Sat, 25 Aug 2018 19:05:22 -0400 Subject: [PATCH 09/20] loader: Add BKTR-specific error messages and codes --- src/core/CMakeLists.txt | 4 ++++ src/core/loader/loader.cpp | 11 ++++++++++- src/core/loader/loader.h | 20 ++++++++++++++------ 3 files changed, 28 insertions(+), 7 deletions(-) diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 54afa6a87..7ddc87539 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -35,8 +35,12 @@ add_library(core STATIC file_sys/mode.h file_sys/nca_metadata.cpp file_sys/nca_metadata.h + file_sys/nca_patch.cpp + file_sys/nca_patch.h file_sys/partition_filesystem.cpp file_sys/partition_filesystem.h + file_sys/patch_manager.cpp + file_sys/patch_manager.h file_sys/program_metadata.cpp file_sys/program_metadata.h file_sys/registered_cache.cpp diff --git a/src/core/loader/loader.cpp b/src/core/loader/loader.cpp index 446adf557..729b1ca08 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.", @@ -144,6 +144,15 @@ constexpr std::array RESULT_MESSAGES{ "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 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.", + "The BKTR-type NCA has a bad Relocation block.", + "The BKTR-type NCA has a bad Subsection block.", + "The BKTR-type NCA has a bad Relocation bucket.", + "The BKTR-type NCA has a bad Subsection bucket.", + "The BKTR-type NCA is missing the base RomFS.", +}; std::ostream& operator<<(std::ostream& os, ResultStatus status) { os << RESULT_MESSAGES.at(static_cast(status)); diff --git a/src/core/loader/loader.h b/src/core/loader/loader.h index be66b2257..b4a3a6573 100644 --- a/src/core/loader/loader.h +++ b/src/core/loader/loader.h @@ -107,6 +107,14 @@ enum class ResultStatus : u16 { ErrorMissingSDSaveKeySource, ErrorMissingSDNCAKeySource, ErrorNSPMissingProgramNCA, + ErrorBadBKTRHeader, + ErrorBKTRSubsectionNotAfterRelocation, + ErrorBKTRSubsectionNotAtEnd, + ErrorBadRelocationBlock, + ErrorBadSubsectionBlock, + ErrorBadRelocationBuckets, + ErrorBadSubsectionBuckets, + ErrorMissingBKTRBaseRomFS, }; std::ostream& operator<<(std::ostream& os, ResultStatus status); @@ -197,13 +205,13 @@ public: } /** - * Get the update RomFS of the application - * Since the RomFS can be huge, we return a file reference instead of copying to a buffer - * @param file The file containing the RomFS - * @return ResultStatus result of function + * Get whether or not updates can be applied to the RomFS. + * By default, this is true, however for formats where it cannot be guaranteed that the RomFS is + * the base game it should be set to false. + * @return bool whether or not updatable. */ - virtual ResultStatus ReadUpdateRomFS(FileSys::VirtualFile& file) { - return ResultStatus::ErrorNotImplemented; + virtual bool IsRomFSUpdatable() { + return true; } /** From 8e150c46b9abb29337633df90b115637deaf80a8 Mon Sep 17 00:00:00 2001 From: Zach Hilman Date: Sat, 25 Aug 2018 19:06:16 -0400 Subject: [PATCH 10/20] game_list: Display patch names and versions on list --- src/yuzu/game_list.cpp | 26 ++++++++++++++++++++++++++ src/yuzu/game_list.h | 1 + 2 files changed, 27 insertions(+) diff --git a/src/yuzu/game_list.cpp b/src/yuzu/game_list.cpp index baf78af09..1aec08cdb 100644 --- a/src/yuzu/game_list.cpp +++ b/src/yuzu/game_list.cpp @@ -21,6 +21,7 @@ #include "core/file_sys/content_archive.h" #include "core/file_sys/control_metadata.h" #include "core/file_sys/nca_metadata.h" +#include "core/file_sys/patch_manager.h" #include "core/file_sys/registered_cache.h" #include "core/file_sys/romfs.h" #include "core/file_sys/vfs_real.h" @@ -232,6 +233,7 @@ GameList::GameList(FileSys::VirtualFilesystem vfs, GMainWindow* parent) item_model->insertColumns(0, COLUMN_COUNT); item_model->setHeaderData(COLUMN_NAME, Qt::Horizontal, "Name"); item_model->setHeaderData(COLUMN_COMPATIBILITY, Qt::Horizontal, "Compatibility"); + item_model->setHeaderData(COLUMN_ADD_ONS, Qt::Horizontal, "Add-ons"); item_model->setHeaderData(COLUMN_FILE_TYPE, Qt::Horizontal, "File type"); item_model->setHeaderData(COLUMN_SIZE, Qt::Horizontal, "Size"); @@ -454,6 +456,26 @@ static QString FormatGameName(const std::string& physical_name) { return physical_name_as_qstring; } +static QString FormatPatchNameVersions(u64 title_id, bool updatable = true) { + const FileSys::PatchManager patch_manager(title_id); + QString out; + for (const auto& kv : patch_manager.GetPatchVersionNames()) { + if (!updatable && kv.first == FileSys::PatchType::Update) + continue; + + if (kv.second == 0) { + out.append(fmt::format("{}\n", FileSys::FormatPatchTypeName(kv.first)).c_str()); + } else { + out.append(fmt::format("{} ({})\n", FileSys::FormatPatchTypeName(kv.first), + FileSys::FormatTitleVersion(kv.second)) + .c_str()); + } + } + + out.chop(1); + return out; +} + void GameList::RefreshGameDirectory() { if (!UISettings::values.gamedir.isEmpty() && current_worker != nullptr) { LOG_INFO(Frontend, "Change detected in the games directory. Reloading game list."); @@ -515,6 +537,7 @@ void GameListWorker::AddInstalledTitlesToGameList(std::shared_ptrGetFullPath()), icon, QString::fromStdString(name), QString::fromStdString(Loader::GetFileTypeString(loader->GetFileType())), program_id), + new GameListItem(FormatPatchNameVersions(program_id)), new GameListItem( QString::fromStdString(Loader::GetFileTypeString(loader->GetFileType()))), new GameListItemSize(file->GetSize()), @@ -596,12 +619,15 @@ void GameListWorker::AddFstEntriesToGameList(const std::string& dir_path, unsign if (it != compatibility_list.end()) compatibility = it->second.first; + FileSys::PatchManager patch{program_id}; + emit EntryReady({ new GameListItemPath( FormatGameName(physical_name), icon, QString::fromStdString(name), QString::fromStdString(Loader::GetFileTypeString(loader->GetFileType())), program_id), new GameListItemCompat(compatibility), + new GameListItem(FormatPatchNameVersions(program_id, loader->IsRomFSUpdatable())), new GameListItem( QString::fromStdString(Loader::GetFileTypeString(loader->GetFileType()))), new GameListItemSize(FileUtil::GetSize(physical_name)), diff --git a/src/yuzu/game_list.h b/src/yuzu/game_list.h index 84731464a..3fcb298ed 100644 --- a/src/yuzu/game_list.h +++ b/src/yuzu/game_list.h @@ -38,6 +38,7 @@ public: enum { COLUMN_NAME, COLUMN_COMPATIBILITY, + COLUMN_ADD_ONS, COLUMN_FILE_TYPE, COLUMN_SIZE, COLUMN_COUNT, // Number of columns From f92b3512e0772a959c6a3c292af600149165e180 Mon Sep 17 00:00:00 2001 From: Zach Hilman Date: Sat, 25 Aug 2018 19:06:33 -0400 Subject: [PATCH 11/20] main: Make game updates installable --- src/yuzu/main.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 56bd3ee2e..3d438df47 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -868,7 +868,11 @@ void GMainWindow::OnMenuInstallToNAND() { } else { const auto nca = std::make_shared( vfs->OpenFile(filename.toStdString(), FileSys::Mode::Read)); - if (nca->GetStatus() != Loader::ResultStatus::Success) { + const auto id = nca->GetStatus(); + + // Game updates necessary are missing base RomFS + if (nca->GetStatus() != Loader::ResultStatus::Success && + nca->GetStatus() != Loader::ResultStatus::ErrorMissingBKTRBaseRomFS) { failed(); return; } From 9664ce255db09f4501db642c1e82d8cf8f274a22 Mon Sep 17 00:00:00 2001 From: Zach Hilman Date: Sun, 26 Aug 2018 10:53:31 -0400 Subject: [PATCH 12/20] 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 | 33 +++-- src/core/file_sys/patch_manager.h | 9 +- src/core/file_sys/registered_cache.cpp | 21 +-- src/core/file_sys/registered_cache.h | 2 +- .../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, 108 insertions(+), 102 deletions(-) diff --git a/src/core/file_sys/nca_patch.cpp b/src/core/file_sys/nca_patch.cpp index dd684c38e..22fbba573 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 8b8d0a4f5..0d9ad95f5 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 697b8a4c9..5e853c2c0 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; +constexpr u64 SINGLE_BYTE_MODULUS = 0x100; - struct { - u8 v_revision; - u8 v_micro; - u8 v_minor; - u8 v_major; - }; -}; +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; + } -std::string FormatTitleVersion(u32 version_, bool full) { - TitleVersion ver{}; - ver.version = version_; - - 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 2a39c473a..803bcb2a2 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 39c0710e1..7361a67be 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 dcce3fd16..f487b0cf0 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 6b2230269..223570431 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 a8644516b..8a0dc1b1e 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 b4a3a6573..225c05127 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 96f5cd9e5..bb89a9da3 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 c35c99d14..96d2de305 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 3d438df47..b7ce0248b 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; } From a6e75cd45b75a202eed1a68692e33e7732789dd2 Mon Sep 17 00:00:00 2001 From: Zach Hilman Date: Tue, 28 Aug 2018 22:37:42 -0400 Subject: [PATCH 13/20] 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(-) diff --git a/src/core/file_sys/content_archive.cpp b/src/core/file_sys/content_archive.cpp index f0d376bf5..26e1daf55 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 104226f3a..00eca52da 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 22fbba573..e293af452 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 fc9cf1eca..33ec62491 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 168db1c46..26b8f46cc 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 225c05127..843c4bb91 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 6b1c27b47..6aaffae59 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 326f84857..10be197c4 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: From 2814ca362494c3d3af90abab8a66db5eb2db56f6 Mon Sep 17 00:00:00 2001 From: Zach Hilman Date: Tue, 28 Aug 2018 22:38:19 -0400 Subject: [PATCH 14/20] game_list: Use friendly game versions Mainly, from control.nacp metadata instead of cnmt metadata --- src/yuzu/game_list.cpp | 45 ++++++++++++++++++++++++++++++------------ 1 file changed, 32 insertions(+), 13 deletions(-) diff --git a/src/yuzu/game_list.cpp b/src/yuzu/game_list.cpp index 1aec08cdb..02d8a3882 100644 --- a/src/yuzu/game_list.cpp +++ b/src/yuzu/game_list.cpp @@ -456,8 +456,9 @@ static QString FormatGameName(const std::string& physical_name) { return physical_name_as_qstring; } -static QString FormatPatchNameVersions(u64 title_id, bool updatable = true) { - const FileSys::PatchManager patch_manager(title_id); +static QString FormatPatchNameVersions(const FileSys::PatchManager& patch_manager, + std::string update_version_override = "", + bool updatable = true) { QString out; for (const auto& kv : patch_manager.GetPatchVersionNames()) { if (!updatable && kv.first == FileSys::PatchType::Update) @@ -466,9 +467,13 @@ static QString FormatPatchNameVersions(u64 title_id, bool updatable = true) { if (kv.second == 0) { out.append(fmt::format("{}\n", FileSys::FormatPatchTypeName(kv.first)).c_str()); } else { - out.append(fmt::format("{} ({})\n", FileSys::FormatPatchTypeName(kv.first), - FileSys::FormatTitleVersion(kv.second)) - .c_str()); + auto version_data = FileSys::FormatTitleVersion(kv.second); + if (kv.first == FileSys::PatchType::Update && !update_version_override.empty()) + version_data = update_version_override; + + out.append( + fmt::format("{} ({})\n", FileSys::FormatPatchTypeName(kv.first), version_data) + .c_str()); } } @@ -484,9 +489,16 @@ void GameList::RefreshGameDirectory() { } } -static void GetMetadataFromControlNCA(const std::shared_ptr& nca, - std::vector& icon, std::string& name) { - const auto control_dir = FileSys::ExtractRomFS(nca->GetRomFS()); +static void GetMetadataFromControlNCA(const FileSys::PatchManager& patch_manager, + const std::shared_ptr& nca, + std::vector& icon, std::string& name, + std::string& version) { + const auto romfs = patch_manager.PatchRomFS(nca->GetRomFS(), nca->GetBaseIVFCOffset(), + FileSys::ContentRecordType::Control); + if (romfs == nullptr) + return; + + const auto control_dir = FileSys::ExtractRomFS(romfs); if (control_dir == nullptr) return; @@ -495,6 +507,7 @@ static void GetMetadataFromControlNCA(const std::shared_ptr& nca, return; FileSys::NACP nacp(nacp_file); name = nacp.GetApplicationName(); + version = nacp.GetVersionString(); FileSys::VirtualFile icon_file = nullptr; for (const auto& language : FileSys::LANGUAGE_NAMES) { @@ -526,18 +539,20 @@ void GameListWorker::AddInstalledTitlesToGameList(std::shared_ptr icon; std::string name; + std::string version = ""; u64 program_id = 0; loader->ReadProgramId(program_id); + const FileSys::PatchManager patch{program_id}; const auto& control = cache->GetEntry(game.title_id, FileSys::ContentRecordType::Control); if (control != nullptr) - GetMetadataFromControlNCA(control, icon, name); + GetMetadataFromControlNCA(patch, control, icon, name, version); emit EntryReady({ new GameListItemPath( FormatGameName(file->GetFullPath()), icon, QString::fromStdString(name), QString::fromStdString(Loader::GetFileTypeString(loader->GetFileType())), program_id), - new GameListItem(FormatPatchNameVersions(program_id)), + new GameListItem(FormatPatchNameVersions(patch, version)), new GameListItem( QString::fromStdString(Loader::GetFileTypeString(loader->GetFileType()))), new GameListItemSize(file->GetSize()), @@ -603,12 +618,16 @@ void GameListWorker::AddFstEntriesToGameList(const std::string& dir_path, unsign std::string name = " "; const auto res3 = loader->ReadTitle(name); + const FileSys::PatchManager patch{program_id}; + + std::string version = ""; + if (res1 != Loader::ResultStatus::Success && res3 != Loader::ResultStatus::Success && res2 == Loader::ResultStatus::Success) { // Use from metadata pool. if (nca_control_map.find(program_id) != nca_control_map.end()) { const auto nca = nca_control_map[program_id]; - GetMetadataFromControlNCA(nca, icon, name); + GetMetadataFromControlNCA(patch, nca, icon, name, version); } } @@ -619,8 +638,6 @@ void GameListWorker::AddFstEntriesToGameList(const std::string& dir_path, unsign if (it != compatibility_list.end()) compatibility = it->second.first; - FileSys::PatchManager patch{program_id}; - emit EntryReady({ new GameListItemPath( FormatGameName(physical_name), icon, QString::fromStdString(name), @@ -628,6 +645,8 @@ void GameListWorker::AddFstEntriesToGameList(const std::string& dir_path, unsign program_id), new GameListItemCompat(compatibility), new GameListItem(FormatPatchNameVersions(program_id, loader->IsRomFSUpdatable())), + new GameListItem( + FormatPatchNameVersions(patch, version, loader->IsRomFSUpdatable())), new GameListItem( QString::fromStdString(Loader::GetFileTypeString(loader->GetFileType()))), new GameListItemSize(FileUtil::GetSize(physical_name)), From cbd517d8cc1ba70d149adb57299a62c7a4e5fd72 Mon Sep 17 00:00:00 2001 From: Zach Hilman Date: Tue, 28 Aug 2018 22:38:35 -0400 Subject: [PATCH 15/20] 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(-) diff --git a/src/core/file_sys/nca_patch.cpp b/src/core/file_sys/nca_patch.cpp index e293af452..1e93000d5 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 5e853c2c0..8b7d79773 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 803bcb2a2..021bc3366 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} From c91b60a421a3bd0dc85d80e0a5a2d261370df340 Mon Sep 17 00:00:00 2001 From: Zach Hilman Date: Sat, 1 Sep 2018 13:11:30 -0400 Subject: [PATCH 16/20] game_list: Fix version display on non-NAND titles --- src/core/file_sys/patch_manager.cpp | 28 +++++++++++++++----- src/core/file_sys/patch_manager.h | 2 +- src/core/loader/xci.cpp | 11 +++++++- src/yuzu/game_list.cpp | 41 +++++++++++++---------------- 4 files changed, 52 insertions(+), 30 deletions(-) diff --git a/src/core/file_sys/patch_manager.cpp b/src/core/file_sys/patch_manager.cpp index 8b7d79773..b6e25f7eb 100644 --- a/src/core/file_sys/patch_manager.cpp +++ b/src/core/file_sys/patch_manager.cpp @@ -82,15 +82,31 @@ VirtualFile PatchManager::PatchRomFS(VirtualFile romfs, u64 ivfc_offset, return romfs; } -std::map PatchManager::GetPatchVersionNames() const { - std::map out; +std::map PatchManager::GetPatchVersionNames() const { + std::map out; const auto installed = Service::FileSystem::GetUnionContents(); 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)) { - out[PatchType::Update] = update_version.get(); + const auto update_control = installed->GetEntry(title_id, ContentRecordType::Control); + if (update_control != nullptr) { + do { + const auto romfs = + PatchRomFS(update_control->GetRomFS(), update_control->GetBaseIVFCOffset(), + FileSys::ContentRecordType::Control); + if (romfs == nullptr) + break; + + const auto control_dir = FileSys::ExtractRomFS(romfs); + if (control_dir == nullptr) + break; + + const auto nacp_file = control_dir->GetFile("control.nacp"); + if (nacp_file == nullptr) + break; + + FileSys::NACP nacp(nacp_file); + out[PatchType::Update] = nacp.GetVersionString(); + } while (false); } return out; diff --git a/src/core/file_sys/patch_manager.h b/src/core/file_sys/patch_manager.h index 021bc3366..b6bf86222 100644 --- a/src/core/file_sys/patch_manager.h +++ b/src/core/file_sys/patch_manager.h @@ -45,7 +45,7 @@ public: // Returns a vector of pairs between patch names and patch versions. // i.e. Update v80 will return {Update, 80} - std::map GetPatchVersionNames() const; + std::map GetPatchVersionNames() const; private: u64 title_id; diff --git a/src/core/loader/xci.cpp b/src/core/loader/xci.cpp index 75b998faa..b01d51abb 100644 --- a/src/core/loader/xci.cpp +++ b/src/core/loader/xci.cpp @@ -8,6 +8,7 @@ #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/romfs.h" #include "core/hle/kernel/process.h" #include "core/loader/nca.h" @@ -20,10 +21,18 @@ AppLoader_XCI::AppLoader_XCI(FileSys::VirtualFile file) nca_loader(std::make_unique(xci->GetProgramNCAFile())) { if (xci->GetStatus() != ResultStatus::Success) return; + const auto control_nca = xci->GetNCAByType(FileSys::NCAContentType::Control); + if (control_nca == nullptr || control_nca->GetStatus() != ResultStatus::Success) return; - const auto romfs = FileSys::ExtractRomFS(control_nca->GetRomFS()); + + auto romfs_raw = control_nca->GetRomFS(); + FileSys::PatchManager patch{xci->GetNCAByType(FileSys::NCAContentType::Program)->GetTitleId()}; + romfs_raw = patch.PatchRomFS(romfs_raw, control_nca->GetBaseIVFCOffset(), + FileSys::ContentRecordType::Control); + + const auto romfs = FileSys::ExtractRomFS(romfs_raw); if (romfs == nullptr) return; for (const auto& language : FileSys::LANGUAGE_NAMES) { diff --git a/src/yuzu/game_list.cpp b/src/yuzu/game_list.cpp index 02d8a3882..38c5071e3 100644 --- a/src/yuzu/game_list.cpp +++ b/src/yuzu/game_list.cpp @@ -457,23 +457,17 @@ static QString FormatGameName(const std::string& physical_name) { } static QString FormatPatchNameVersions(const FileSys::PatchManager& patch_manager, - std::string update_version_override = "", bool updatable = true) { QString out; for (const auto& kv : patch_manager.GetPatchVersionNames()) { if (!updatable && kv.first == FileSys::PatchType::Update) continue; - if (kv.second == 0) { + if (kv.second.empty()) { out.append(fmt::format("{}\n", FileSys::FormatPatchTypeName(kv.first)).c_str()); } else { - auto version_data = FileSys::FormatTitleVersion(kv.second); - if (kv.first == FileSys::PatchType::Update && !update_version_override.empty()) - version_data = update_version_override; - - out.append( - fmt::format("{} ({})\n", FileSys::FormatPatchTypeName(kv.first), version_data) - .c_str()); + out.append(fmt::format("{} ({})\n", FileSys::FormatPatchTypeName(kv.first), kv.second) + .c_str()); } } @@ -491,8 +485,7 @@ void GameList::RefreshGameDirectory() { static void GetMetadataFromControlNCA(const FileSys::PatchManager& patch_manager, const std::shared_ptr& nca, - std::vector& icon, std::string& name, - std::string& version) { + std::vector& icon, std::string& name) { const auto romfs = patch_manager.PatchRomFS(nca->GetRomFS(), nca->GetBaseIVFCOffset(), FileSys::ContentRecordType::Control); if (romfs == nullptr) @@ -507,7 +500,6 @@ static void GetMetadataFromControlNCA(const FileSys::PatchManager& patch_manager return; FileSys::NACP nacp(nacp_file); name = nacp.GetApplicationName(); - version = nacp.GetVersionString(); FileSys::VirtualFile icon_file = nullptr; for (const auto& language : FileSys::LANGUAGE_NAMES) { @@ -527,7 +519,8 @@ GameListWorker::GameListWorker( GameListWorker::~GameListWorker() = default; -void GameListWorker::AddInstalledTitlesToGameList(std::shared_ptr cache) { +void GameListWorker::AddInstalledTitlesToGameList() { + const auto cache = Service::FileSystem::GetUnionContents(); const auto installed_games = cache->ListEntriesFilter(FileSys::TitleType::Application, FileSys::ContentRecordType::Program); @@ -539,20 +532,28 @@ void GameListWorker::AddInstalledTitlesToGameList(std::shared_ptr icon; std::string name; - std::string version = ""; u64 program_id = 0; loader->ReadProgramId(program_id); const FileSys::PatchManager patch{program_id}; const auto& control = cache->GetEntry(game.title_id, FileSys::ContentRecordType::Control); if (control != nullptr) - GetMetadataFromControlNCA(patch, control, icon, name, version); + GetMetadataFromControlNCA(patch, control, icon, name); + + auto it = FindMatchingCompatibilityEntry(compatibility_list, program_id); + + // The game list uses this as compatibility number for untested games + QString compatibility("99"); + if (it != compatibility_list.end()) + compatibility = it->second.first; + emit EntryReady({ new GameListItemPath( FormatGameName(file->GetFullPath()), icon, QString::fromStdString(name), QString::fromStdString(Loader::GetFileTypeString(loader->GetFileType())), program_id), - new GameListItem(FormatPatchNameVersions(patch, version)), + new GameListItemCompat(compatibility), + new GameListItem(FormatPatchNameVersions(patch)), new GameListItem( QString::fromStdString(Loader::GetFileTypeString(loader->GetFileType()))), new GameListItemSize(file->GetSize()), @@ -620,14 +621,12 @@ void GameListWorker::AddFstEntriesToGameList(const std::string& dir_path, unsign const FileSys::PatchManager patch{program_id}; - std::string version = ""; - if (res1 != Loader::ResultStatus::Success && res3 != Loader::ResultStatus::Success && res2 == Loader::ResultStatus::Success) { // Use from metadata pool. if (nca_control_map.find(program_id) != nca_control_map.end()) { const auto nca = nca_control_map[program_id]; - GetMetadataFromControlNCA(patch, nca, icon, name, version); + GetMetadataFromControlNCA(patch, nca, icon, name); } } @@ -644,9 +643,7 @@ void GameListWorker::AddFstEntriesToGameList(const std::string& dir_path, unsign QString::fromStdString(Loader::GetFileTypeString(loader->GetFileType())), program_id), new GameListItemCompat(compatibility), - new GameListItem(FormatPatchNameVersions(program_id, loader->IsRomFSUpdatable())), - new GameListItem( - FormatPatchNameVersions(patch, version, loader->IsRomFSUpdatable())), + new GameListItem(FormatPatchNameVersions(patch, loader->IsRomFSUpdatable())), new GameListItem( QString::fromStdString(Loader::GetFileTypeString(loader->GetFileType()))), new GameListItemSize(FileUtil::GetSize(physical_name)), From 92e26df00f12de2e084ceb84d17ca79c5323a315 Mon Sep 17 00:00:00 2001 From: Zach Hilman Date: Mon, 3 Sep 2018 18:46:56 -0400 Subject: [PATCH 17/20] nsp: Fix error masking issue with XCI files Now display correct error instead of catch-all MissingProgramNCA --- src/core/file_sys/card_image.cpp | 6 +++--- src/core/file_sys/content_archive.cpp | 8 ++++++-- src/core/file_sys/submission_package.cpp | 5 ++++- 3 files changed, 13 insertions(+), 6 deletions(-) diff --git a/src/core/file_sys/card_image.cpp b/src/core/file_sys/card_image.cpp index 1bd3353e4..8218893b2 100644 --- a/src/core/file_sys/card_image.cpp +++ b/src/core/file_sys/card_image.cpp @@ -52,11 +52,11 @@ XCI::XCI(VirtualFile file_) : file(std::move(file_)), partitions(0x4) { const auto secure_ncas = secure_partition->GetNCAsCollapsed(); std::copy(secure_ncas.begin(), secure_ncas.end(), std::back_inserter(ncas)); - program_nca_status = Loader::ResultStatus::ErrorXCIMissingProgramNCA; program = secure_partition->GetNCA(secure_partition->GetProgramTitleID(), ContentRecordType::Program); - if (program != nullptr) - program_nca_status = program->GetStatus(); + program_nca_status = secure_partition->GetProgramStatus(secure_partition->GetProgramTitleID()); + if (program_nca_status == Loader::ResultStatus::ErrorNSPMissingProgramNCA) + program_nca_status = Loader::ResultStatus::ErrorXCIMissingProgramNCA; auto result = AddNCAFromPartition(XCIPartition::Update); if (result != Loader::ResultStatus::Success) { diff --git a/src/core/file_sys/content_archive.cpp b/src/core/file_sys/content_archive.cpp index 26e1daf55..1c093d94f 100644 --- a/src/core/file_sys/content_archive.cpp +++ b/src/core/file_sys/content_archive.cpp @@ -444,6 +444,12 @@ NCA::NCA(VirtualFile file_, VirtualFile bktr_base_romfs_, u64 bktr_base_ivfc_off 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) @@ -491,8 +497,6 @@ NCAContentType NCA::GetType() const { u64 NCA::GetTitleId() const { if (is_update || status == Loader::ResultStatus::ErrorMissingBKTRBaseRomFS) return header.title_id | 0x800; - if (status != Loader::ResultStatus::Success) - return {}; return header.title_id; } diff --git a/src/core/file_sys/submission_package.cpp b/src/core/file_sys/submission_package.cpp index bde879861..182b40698 100644 --- a/src/core/file_sys/submission_package.cpp +++ b/src/core/file_sys/submission_package.cpp @@ -60,8 +60,11 @@ NSP::NSP(VirtualFile file_) for (const auto& outer_file : files) { if (outer_file->GetName().substr(outer_file->GetName().size() - 9) == ".cnmt.nca") { const auto nca = std::make_shared(outer_file); - if (nca->GetStatus() != Loader::ResultStatus::Success) + if (nca->GetStatus() != Loader::ResultStatus::Success) { + program_status[nca->GetTitleId()] = nca->GetStatus(); continue; + } + const auto section0 = nca->GetSubdirectories()[0]; for (const auto& inner_file : section0->GetFiles()) { From 23a16c1720ee522f6ac7d1f426a2d4a918ce41c9 Mon Sep 17 00:00:00 2001 From: Zach Hilman Date: Mon, 3 Sep 2018 18:57:52 -0400 Subject: [PATCH 18/20] patch_manager: Centralize Control-type NCA parsing --- src/core/file_sys/patch_manager.cpp | 73 +++++++++++++++++++++-------- src/core/file_sys/patch_manager.h | 13 ++++- src/core/loader/nsp.cpp | 20 ++------ src/core/loader/xci.cpp | 21 ++------- src/yuzu/game_list.cpp | 28 ++--------- src/yuzu/main.cpp | 12 ++++- 6 files changed, 88 insertions(+), 79 deletions(-) diff --git a/src/core/file_sys/patch_manager.cpp b/src/core/file_sys/patch_manager.cpp index b6e25f7eb..fa2fbe5e1 100644 --- a/src/core/file_sys/patch_manager.cpp +++ b/src/core/file_sys/patch_manager.cpp @@ -2,6 +2,7 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include "core/file_sys/control_metadata.h" #include "core/file_sys/patch_manager.h" #include "core/file_sys/registered_cache.h" #include "core/hle/service/filesystem/filesystem.h" @@ -87,29 +88,63 @@ std::map PatchManager::GetPatchVersionNames() const { const auto installed = Service::FileSystem::GetUnionContents(); const auto update_tid = GetUpdateTitleID(title_id); - const auto update_control = installed->GetEntry(title_id, ContentRecordType::Control); - if (update_control != nullptr) { - do { - const auto romfs = - PatchRomFS(update_control->GetRomFS(), update_control->GetBaseIVFCOffset(), - FileSys::ContentRecordType::Control); - if (romfs == nullptr) - break; + PatchManager update{update_tid}; + auto [nacp, discard_icon_file] = update.GetControlMetadata(); - const auto control_dir = FileSys::ExtractRomFS(romfs); - if (control_dir == nullptr) - break; - - const auto nacp_file = control_dir->GetFile("control.nacp"); - if (nacp_file == nullptr) - break; - - FileSys::NACP nacp(nacp_file); - out[PatchType::Update] = nacp.GetVersionString(); - } while (false); + if (nacp != nullptr) { + out[PatchType::Update] = nacp->GetVersionString(); + } else { + if (installed->HasEntry(update_tid, ContentRecordType::Program)) { + const auto meta_ver = installed->GetEntryVersion(update_tid); + if (meta_ver == boost::none || meta_ver.get() == 0) { + out[PatchType::Update] = ""; + } else { + out[PatchType::Update] = + FormatTitleVersion(meta_ver.get(), TitleVersionFormat::ThreeElements); + } + } } return out; } +std::pair, VirtualFile> PatchManager::GetControlMetadata() const { + const auto& installed{Service::FileSystem::GetUnionContents()}; + + const auto base_control_nca = installed->GetEntry(title_id, ContentRecordType::Control); + if (base_control_nca == nullptr) + return {}; + + return ParseControlNCA(base_control_nca); +} + +std::pair, VirtualFile> PatchManager::ParseControlNCA( + const std::shared_ptr& nca) const { + const auto base_romfs = nca->GetRomFS(); + if (base_romfs == nullptr) + return {}; + + const auto romfs = PatchRomFS(base_romfs, nca->GetBaseIVFCOffset(), ContentRecordType::Control); + if (romfs == nullptr) + return {}; + + const auto extracted = ExtractRomFS(romfs); + if (extracted == nullptr) + return {}; + + auto nacp_file = extracted->GetFile("control.nacp"); + if (nacp_file == nullptr) + nacp_file = extracted->GetFile("Control.nacp"); + + const auto nacp = nacp_file == nullptr ? nullptr : std::make_shared(nacp_file); + + VirtualFile icon_file; + for (const auto& language : FileSys::LANGUAGE_NAMES) { + icon_file = extracted->GetFile("icon_" + std::string(language) + ".dat"); + if (icon_file != nullptr) + break; + } + + return {nacp, icon_file}; +} } // namespace FileSys diff --git a/src/core/file_sys/patch_manager.h b/src/core/file_sys/patch_manager.h index b6bf86222..c2626bc6c 100644 --- a/src/core/file_sys/patch_manager.h +++ b/src/core/file_sys/patch_manager.h @@ -7,13 +7,14 @@ #include #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" -#include "nca_metadata.h" -#include "romfs_factory.h" namespace FileSys { class NCA; +class NACP; enum class TitleVersionFormat : u8 { ThreeElements, ///< vX.Y.Z @@ -47,6 +48,14 @@ public: // i.e. Update v80 will return {Update, 80} std::map GetPatchVersionNames() const; + // Given title_id of the program, attempts to get the control data of the update and parse it, + // falling back to the base control data. + std::pair, VirtualFile> GetControlMetadata() const; + + // Version of GetControlMetadata that takes an arbitrary NCA + std::pair, VirtualFile> ParseControlNCA( + const std::shared_ptr& nca) const; + private: u64 title_id; }; diff --git a/src/core/loader/nsp.cpp b/src/core/loader/nsp.cpp index 7c06239f2..291a9876d 100644 --- a/src/core/loader/nsp.cpp +++ b/src/core/loader/nsp.cpp @@ -9,6 +9,8 @@ #include "core/file_sys/content_archive.h" #include "core/file_sys/control_metadata.h" #include "core/file_sys/nca_metadata.h" +#include "core/file_sys/patch_manager.h" +#include "core/file_sys/registered_cache.h" #include "core/file_sys/romfs.h" #include "core/file_sys/submission_package.h" #include "core/hle/kernel/process.h" @@ -28,24 +30,12 @@ AppLoader_NSP::AppLoader_NSP(FileSys::VirtualFile file) return; const auto control_nca = - nsp->GetNCA(nsp->GetFirstTitleID(), FileSys::ContentRecordType::Control); + nsp->GetNCA(nsp->GetProgramTitleID(), FileSys::ContentRecordType::Control); if (control_nca == nullptr || control_nca->GetStatus() != ResultStatus::Success) return; - const auto romfs = FileSys::ExtractRomFS(control_nca->GetRomFS()); - if (romfs == nullptr) - return; - - for (const auto& language : FileSys::LANGUAGE_NAMES) { - icon_file = romfs->GetFile("icon_" + std::string(language) + ".dat"); - if (icon_file != nullptr) - break; - } - - const auto nacp_raw = romfs->GetFile("control.nacp"); - if (nacp_raw == nullptr) - return; - nacp_file = std::make_shared(nacp_raw); + std::tie(nacp_file, icon_file) = + FileSys::PatchManager(nsp->GetProgramTitleID()).ParseControlNCA(control_nca); } AppLoader_NSP::~AppLoader_NSP() = default; diff --git a/src/core/loader/xci.cpp b/src/core/loader/xci.cpp index b01d51abb..16509229f 100644 --- a/src/core/loader/xci.cpp +++ b/src/core/loader/xci.cpp @@ -10,6 +10,7 @@ #include "core/file_sys/control_metadata.h" #include "core/file_sys/patch_manager.h" #include "core/file_sys/romfs.h" +#include "core/file_sys/submission_package.h" #include "core/hle/kernel/process.h" #include "core/loader/nca.h" #include "core/loader/xci.h" @@ -23,27 +24,11 @@ AppLoader_XCI::AppLoader_XCI(FileSys::VirtualFile file) return; const auto control_nca = xci->GetNCAByType(FileSys::NCAContentType::Control); - if (control_nca == nullptr || control_nca->GetStatus() != ResultStatus::Success) return; - auto romfs_raw = control_nca->GetRomFS(); - FileSys::PatchManager patch{xci->GetNCAByType(FileSys::NCAContentType::Program)->GetTitleId()}; - romfs_raw = patch.PatchRomFS(romfs_raw, control_nca->GetBaseIVFCOffset(), - FileSys::ContentRecordType::Control); - - const auto romfs = FileSys::ExtractRomFS(romfs_raw); - if (romfs == nullptr) - return; - for (const auto& language : FileSys::LANGUAGE_NAMES) { - icon_file = romfs->GetFile("icon_" + std::string(language) + ".dat"); - if (icon_file != nullptr) - break; - } - const auto nacp_raw = romfs->GetFile("control.nacp"); - if (nacp_raw == nullptr) - return; - nacp_file = std::make_shared(nacp_raw); + std::tie(nacp_file, icon_file) = + FileSys::PatchManager(xci->GetProgramTitleID()).ParseControlNCA(control_nca); } AppLoader_XCI::~AppLoader_XCI() = default; diff --git a/src/yuzu/game_list.cpp b/src/yuzu/game_list.cpp index 38c5071e3..a3b841684 100644 --- a/src/yuzu/game_list.cpp +++ b/src/yuzu/game_list.cpp @@ -486,29 +486,11 @@ void GameList::RefreshGameDirectory() { static void GetMetadataFromControlNCA(const FileSys::PatchManager& patch_manager, const std::shared_ptr& nca, std::vector& icon, std::string& name) { - const auto romfs = patch_manager.PatchRomFS(nca->GetRomFS(), nca->GetBaseIVFCOffset(), - FileSys::ContentRecordType::Control); - if (romfs == nullptr) - return; - - const auto control_dir = FileSys::ExtractRomFS(romfs); - if (control_dir == nullptr) - return; - - const auto nacp_file = control_dir->GetFile("control.nacp"); - if (nacp_file == nullptr) - return; - FileSys::NACP nacp(nacp_file); - name = nacp.GetApplicationName(); - - FileSys::VirtualFile icon_file = nullptr; - for (const auto& language : FileSys::LANGUAGE_NAMES) { - icon_file = control_dir->GetFile("icon_" + std::string(language) + ".dat"); - if (icon_file != nullptr) { - icon = icon_file->ReadAllBytes(); - break; - } - } + auto [nacp, icon_file] = patch_manager.ParseControlNCA(nca); + if (icon_file != nullptr) + icon = icon_file->ReadAllBytes(); + if (nacp != nullptr) + name = nacp->GetApplicationName(); } GameListWorker::GameListWorker( diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index b7ce0248b..80a284513 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -592,8 +592,16 @@ void GMainWindow::BootGame(const QString& filename) { std::string title_name; const auto res = Core::System::GetInstance().GetGameName(title_name); - if (res != Loader::ResultStatus::Success) - title_name = FileUtil::GetFilename(filename.toStdString()); + if (res != Loader::ResultStatus::Success) { + const u64 program_id = Core::System::GetInstance().CurrentProcess()->program_id; + + const auto [nacp, icon_file] = FileSys::PatchManager(program_id).GetControlMetadata(); + if (nacp != nullptr) + title_name = nacp->GetApplicationName(); + + if (title_name.empty()) + title_name = FileUtil::GetFilename(filename.toStdString()); + } setWindowTitle(QString("yuzu %1| %4 | %2-%3") .arg(Common::g_build_name, Common::g_scm_branch, Common::g_scm_desc, From 7d5d781b20b47aa7e5d538404bbb665759646f95 Mon Sep 17 00:00:00 2001 From: Zach Hilman Date: Mon, 3 Sep 2018 19:00:51 -0400 Subject: [PATCH 19/20] drd: Parse title ID from program metadata --- .../loader/deconstructed_rom_directory.cpp | 9 ++++++- src/core/telemetry_session.cpp | 24 ++++++++++++++++--- 2 files changed, 29 insertions(+), 4 deletions(-) diff --git a/src/core/loader/deconstructed_rom_directory.cpp b/src/core/loader/deconstructed_rom_directory.cpp index 223570431..2b8f78136 100644 --- a/src/core/loader/deconstructed_rom_directory.cpp +++ b/src/core/loader/deconstructed_rom_directory.cpp @@ -27,6 +27,14 @@ AppLoader_DeconstructedRomDirectory::AppLoader_DeconstructedRomDirectory(FileSys : AppLoader(std::move(file_)), override_update(override_update) { const auto dir = file->GetContainingDirectory(); + // Title ID + const auto npdm = dir->GetFile("main.npdm"); + if (npdm != nullptr) { + const auto res = metadata.Load(npdm); + if (res == ResultStatus::Success) + title_id = metadata.GetTitleID(); + } + // Icon FileSys::VirtualFile icon_file = nullptr; for (const auto& language : FileSys::LANGUAGE_NAMES) { @@ -138,7 +146,6 @@ ResultStatus AppLoader_DeconstructedRomDirectory::Load( } auto& kernel = Core::System::GetInstance().Kernel(); - title_id = metadata.GetTitleID(); process->program_id = metadata.GetTitleID(); process->svc_access_mask.set(); process->resource_limit = diff --git a/src/core/telemetry_session.cpp b/src/core/telemetry_session.cpp index 65571b948..3730e85b8 100644 --- a/src/core/telemetry_session.cpp +++ b/src/core/telemetry_session.cpp @@ -7,6 +7,8 @@ #include "common/file_util.h" #include "core/core.h" +#include "core/file_sys/control_metadata.h" +#include "core/file_sys/patch_manager.h" #include "core/loader/loader.h" #include "core/settings.h" #include "core/telemetry_session.h" @@ -88,12 +90,28 @@ TelemetrySession::TelemetrySession() { std::chrono::system_clock::now().time_since_epoch()) .count()}; AddField(Telemetry::FieldType::Session, "Init_Time", init_time); - std::string program_name; - const Loader::ResultStatus res{System::GetInstance().GetAppLoader().ReadTitle(program_name)}; + + u64 program_id{}; + const Loader::ResultStatus res{System::GetInstance().GetAppLoader().ReadProgramId(program_id)}; if (res == Loader::ResultStatus::Success) { - AddField(Telemetry::FieldType::Session, "ProgramName", program_name); + AddField(Telemetry::FieldType::Session, "ProgramId", program_id); + + std::string name; + System::GetInstance().GetAppLoader().ReadTitle(name); + + if (name.empty()) { + auto [nacp, icon_file] = FileSys::PatchManager(program_id).GetControlMetadata(); + if (nacp != nullptr) + name = nacp->GetApplicationName(); + } + + if (!name.empty()) + AddField(Telemetry::FieldType::Session, "ProgramName", name); } + AddField(Telemetry::FieldType::Session, "ProgramFormat", + static_cast(System::GetInstance().GetAppLoader().GetFileType())); + // Log application information Telemetry::AppendBuildInfo(field_collection); From c913136eb215699f9c8d51a8fd56490b9df7657f Mon Sep 17 00:00:00 2001 From: Zach Hilman Date: Tue, 4 Sep 2018 17:01:40 -0400 Subject: [PATCH 20/20] 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(-) diff --git a/src/core/file_sys/content_archive.cpp b/src/core/file_sys/content_archive.cpp index 1c093d94f..79bfb6fec 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 1e93000d5..e0111bffc 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 fa2fbe5e1..40675de35 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 c2626bc6c..28c7ae136 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 33ec62491..d9d90939e 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 e9d5bd774..04c9d750f 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 729b1ca08..fa43a2650 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 80a284513..dbe5bd8a4 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"