From bfc720152a389c8110c814f600125ccf480075ac Mon Sep 17 00:00:00 2001 From: lizzie Date: Sat, 21 Feb 2026 18:38:32 +0100 Subject: [PATCH] [vfs] remove usage of 'dynamic_cast', use ankerl::map for files (#3594) Do I need to writeout everything wrong with `dynamic_cast`? Also the usual std::map -> `ankerl::unordered_dense::map` conversion, seems to work just fine - but of course test that updates/DLCs/mods/cheats/etc are still being applied :) Signed-off-by: lizzie Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3594 Reviewed-by: DraVee Reviewed-by: MaranBr Co-authored-by: lizzie Co-committed-by: lizzie --- src/core/core.cpp | 4 - src/core/core.h | 7 +- src/core/file_sys/nca_metadata.h | 4 + src/core/file_sys/patch_manager.cpp | 14 +- src/core/file_sys/registered_cache.cpp | 219 ++++++++----------------- src/core/file_sys/registered_cache.h | 33 ++-- src/yuzu/bootmanager.cpp | 2 +- 7 files changed, 101 insertions(+), 182 deletions(-) diff --git a/src/core/core.cpp b/src/core/core.cpp index 50572ea449..9db4589ceb 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -806,10 +806,6 @@ void System::RegisterContentProvider(FileSys::ContentProviderUnionSlot slot, impl->content_provider->SetSlot(slot, provider); } -void System::ClearContentProvider(FileSys::ContentProviderUnionSlot slot) { - impl->content_provider->ClearSlot(slot); -} - const Reporter& System::GetReporter() const { return impl->reporter; } diff --git a/src/core/core.h b/src/core/core.h index 702c5cc81b..012533c1fa 100644 --- a/src/core/core.h +++ b/src/core/core.h @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: 2014 Citra Emulator Project @@ -358,10 +358,7 @@ public: [[nodiscard]] Service::FileSystem::FileSystemController& GetFileSystemController(); [[nodiscard]] const Service::FileSystem::FileSystemController& GetFileSystemController() const; - void RegisterContentProvider(FileSys::ContentProviderUnionSlot slot, - FileSys::ContentProvider* provider); - - void ClearContentProvider(FileSys::ContentProviderUnionSlot slot); + void RegisterContentProvider(FileSys::ContentProviderUnionSlot slot, FileSys::ContentProvider* provider); [[nodiscard]] const Reporter& GetReporter() const; diff --git a/src/core/file_sys/nca_metadata.h b/src/core/file_sys/nca_metadata.h index 0606b8aad7..43437d0dd7 100644 --- a/src/core/file_sys/nca_metadata.h +++ b/src/core/file_sys/nca_metadata.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -37,6 +40,7 @@ enum class ContentRecordType : u8 { HtmlDocument = 4, LegalInformation = 5, DeltaFragment = 6, + Count, }; struct ContentRecord { diff --git a/src/core/file_sys/patch_manager.cpp b/src/core/file_sys/patch_manager.cpp index 86c2076380..44aafb6b34 100644 --- a/src/core/file_sys/patch_manager.cpp +++ b/src/core/file_sys/patch_manager.cpp @@ -143,7 +143,7 @@ VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const { bool checked_external = false; bool checked_manual = false; - const auto* content_union = dynamic_cast(&content_provider); + const auto* content_union = static_cast(&content_provider); const auto update_tid = GetUpdateTitleID(title_id); if (content_union) { @@ -167,7 +167,7 @@ VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const { // Also check ManualContentProvider (for Android) if (!checked_external) { - const auto* manual_provider = dynamic_cast( + const auto* manual_provider = static_cast( content_union->GetSlotProvider(ContentProviderUnionSlot::FrontendManual)); if (manual_provider) { const auto manual_update_versions = manual_provider->ListUpdateVersions(update_tid); @@ -243,7 +243,7 @@ VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const { // Also try ManualContentProvider if (update == nullptr) { - const auto* manual_provider = dynamic_cast( + const auto* manual_provider = static_cast( content_union->GetSlotProvider(ContentProviderUnionSlot::FrontendManual)); if (manual_provider) { auto file = manual_provider->GetEntryForVersion(update_tid, ContentRecordType::Program, *enabled_version); @@ -570,7 +570,7 @@ VirtualFile PatchManager::PatchRomFS(const NCA* base_nca, VirtualFile base_romfs bool checked_external = false; bool checked_manual = false; - const auto* content_union = dynamic_cast(&content_provider); + const auto* content_union = static_cast(&content_provider); if (content_union) { // First, check ExternalContentProvider const auto* external_provider = content_union->GetExternalProvider(); @@ -592,7 +592,7 @@ VirtualFile PatchManager::PatchRomFS(const NCA* base_nca, VirtualFile base_romfs } if (!checked_external) { - const auto* manual_provider = dynamic_cast( + const auto* manual_provider = static_cast( content_union->GetSlotProvider(ContentProviderUnionSlot::FrontendManual)); if (manual_provider) { const auto manual_update_versions = manual_provider->ListUpdateVersions(update_tid); @@ -690,7 +690,7 @@ std::vector PatchManager::GetPatches(VirtualFile update_raw) const { std::vector external_update_patches; - const auto* content_union = dynamic_cast(&content_provider); + const auto* content_union = static_cast(&content_provider); if (content_union) { // First, check ExternalContentProvider for updates @@ -721,7 +721,7 @@ std::vector PatchManager::GetPatches(VirtualFile update_raw) const { } } - const auto* manual_provider = dynamic_cast( + const auto* manual_provider = static_cast( content_union->GetSlotProvider(ContentProviderUnionSlot::FrontendManual)); if (manual_provider && external_update_patches.empty()) { const auto manual_update_versions = manual_provider->ListUpdateVersions(update_tid); diff --git a/src/core/file_sys/registered_cache.cpp b/src/core/file_sys/registered_cache.cpp index 42ec878436..1b2c45176b 100644 --- a/src/core/file_sys/registered_cache.cpp +++ b/src/core/file_sys/registered_cache.cpp @@ -353,8 +353,7 @@ VirtualFile RegisteredCache::GetFileAtID(NcaID id) const { return file; } -static std::optional CheckMapForContentRecord(const std::map& map, u64 title_id, - ContentRecordType type) { +static std::optional CheckMapForContentRecord(const ankerl::unordered_dense::map& map, u64 title_id, ContentRecordType type) { const auto cmnt_iter = map.find(title_id); if (cmnt_iter == map.cend()) { return std::nullopt; @@ -829,133 +828,96 @@ bool RegisteredCache::RawInstallYuzuMeta(const CNMT& cnmt) { } } Refresh(); - return std::find_if(yuzu_meta.begin(), yuzu_meta.end(), - [&cnmt](const std::pair& kv) { - return kv.second.GetType() == cnmt.GetType() && - kv.second.GetTitleID() == cnmt.GetTitleID(); - }) != yuzu_meta.end(); + return std::find_if(yuzu_meta.begin(), yuzu_meta.end(), [&cnmt](const std::pair& kv) { + return kv.second.GetType() == cnmt.GetType() && kv.second.GetTitleID() == cnmt.GetTitleID(); + }) != yuzu_meta.end(); } ContentProviderUnion::~ContentProviderUnion() = default; void ContentProviderUnion::SetSlot(ContentProviderUnionSlot slot, ContentProvider* provider) { - providers[slot] = provider; -} - -void ContentProviderUnion::ClearSlot(ContentProviderUnionSlot slot) { - providers[slot] = nullptr; + providers[size_t(slot)] = provider; } void ContentProviderUnion::Refresh() { - for (auto& provider : providers) { - if (provider.second == nullptr) - continue; - - provider.second->Refresh(); - } + for (auto e : providers) + if (e != nullptr) + e->Refresh(); } bool ContentProviderUnion::HasEntry(u64 title_id, ContentRecordType type) const { - for (const auto& provider : providers) { - if (provider.second == nullptr) - continue; - - if (provider.second->HasEntry(title_id, type)) + for (auto const e : providers) + if (e && e->HasEntry(title_id, type)) return true; - } - return false; } std::optional ContentProviderUnion::GetEntryVersion(u64 title_id) const { - for (const auto& provider : providers) { - if (provider.second == nullptr) + for (auto const e : providers) { + if (e == nullptr) continue; - - const auto res = provider.second->GetEntryVersion(title_id); - if (res != std::nullopt) + if (auto const res = e->GetEntryVersion(title_id); res != std::nullopt) return res; } - return std::nullopt; } VirtualFile ContentProviderUnion::GetEntryUnparsed(u64 title_id, ContentRecordType type) const { - for (const auto& provider : providers) { - if (provider.second == nullptr) + for (auto const e : providers) { + if (e == nullptr) continue; - - const auto res = provider.second->GetEntryUnparsed(title_id, type); - if (res != nullptr) + if (auto const res = e->GetEntryUnparsed(title_id, type); res != nullptr) return res; } - return nullptr; } VirtualFile ContentProviderUnion::GetEntryRaw(u64 title_id, ContentRecordType type) const { - for (const auto& provider : providers) { - if (provider.second == nullptr) + for (auto const e : providers) { + if (e == nullptr) continue; - - const auto res = provider.second->GetEntryRaw(title_id, type); - if (res != nullptr) + if (auto const res = e->GetEntryRaw(title_id, type); res != nullptr) return res; } - return nullptr; } std::unique_ptr ContentProviderUnion::GetEntry(u64 title_id, ContentRecordType type) const { - for (const auto& provider : providers) { - if (provider.second == nullptr) + for (auto const e : providers) { + if (e == nullptr) continue; - - auto res = provider.second->GetEntry(title_id, type); - if (res != nullptr) + if (auto res = e->GetEntry(title_id, type); res != nullptr) return res; } - return nullptr; } -std::vector ContentProviderUnion::ListEntriesFilter( - std::optional title_type, std::optional record_type, - std::optional title_id) const { +std::vector ContentProviderUnion::ListEntriesFilter(std::optional title_type, std::optional record_type, std::optional title_id) const { std::vector out; - - for (const auto& provider : providers) { - if (provider.second == nullptr) - continue; - - const auto vec = provider.second->ListEntriesFilter(title_type, record_type, title_id); - std::copy(vec.begin(), vec.end(), std::back_inserter(out)); + for (auto const& e : providers) { + if (e != nullptr) { + auto const vec = e->ListEntriesFilter(title_type, record_type, title_id); + std::copy(vec.begin(), vec.end(), std::back_inserter(out)); + } } - std::sort(out.begin(), out.end()); out.erase(std::unique(out.begin(), out.end()), out.end()); return out; } -std::vector> -ContentProviderUnion::ListEntriesFilterOrigin(std::optional origin, - std::optional title_type, - std::optional record_type, - std::optional title_id) const { +std::vector> ContentProviderUnion::ListEntriesFilterOrigin(std::optional origin, std::optional title_type, std::optional record_type, std::optional title_id) const { std::vector> out; - for (const auto& provider : providers) { - if (provider.second == nullptr) + for (size_t i = 0; i < providers.size(); ++i) { + auto const& e = providers[i]; + if (e == nullptr) continue; - - if (origin.has_value() && *origin != provider.first) + if (origin.has_value() && *origin != ContentProviderUnionSlot(i)) continue; - - const auto vec = provider.second->ListEntriesFilter(title_type, record_type, title_id); - std::transform(vec.begin(), vec.end(), std::back_inserter(out), - [&provider](const ContentProviderEntry& entry) { - return std::make_pair(provider.first, entry); - }); + auto const vec = e->ListEntriesFilter(title_type, record_type, title_id); + std::transform(vec.begin(), vec.end(), std::back_inserter(out), [i](const ContentProviderEntry& entry) { + return std::make_pair(ContentProviderUnionSlot(i), entry); + }); } std::sort(out.begin(), out.end()); @@ -963,40 +925,22 @@ ContentProviderUnion::ListEntriesFilterOrigin(std::optional ContentProviderUnion::GetSlotForEntry( - u64 title_id, ContentRecordType type) const { - const auto iter = - std::find_if(providers.begin(), providers.end(), [title_id, type](const auto& provider) { - return provider.second != nullptr && provider.second->HasEntry(title_id, type); - }); - - if (iter == providers.end()) { - return std::nullopt; +std::optional ContentProviderUnion::GetSlotForEntry(u64 title_id, ContentRecordType type) const { + for (size_t i = 0; i < providers.size(); ++i) { + auto const& e = providers[i]; + if (e != nullptr && e->HasEntry(title_id, type)) + return {ContentProviderUnionSlot(i)}; } - - return iter->first; + return std::nullopt; } const ExternalContentProvider* ContentProviderUnion::GetExternalProvider() const { - auto it = providers.find(ContentProviderUnionSlot::External); - if (it != providers.end() && it->second != nullptr) { - return dynamic_cast(it->second); - } - return nullptr; -} - -const ContentProvider* ContentProviderUnion::GetSlotProvider(ContentProviderUnionSlot slot) const { - auto it = providers.find(slot); - if (it != providers.end()) { - return it->second; - } - return nullptr; + return static_cast(providers[size_t(ContentProviderUnionSlot::External)]); } ManualContentProvider::~ManualContentProvider() = default; -void ManualContentProvider::AddEntry(TitleType title_type, ContentRecordType content_type, - u64 title_id, VirtualFile file) { +void ManualContentProvider::AddEntry(TitleType title_type, ContentRecordType content_type, u64 title_id, VirtualFile file) { entries.insert_or_assign({title_type, content_type, title_id}, file); } @@ -1004,14 +948,13 @@ void ManualContentProvider::AddEntryWithVersion(TitleType title_type, ContentRec u64 title_id, u32 version, const std::string& version_string, VirtualFile file) { if (title_type == TitleType::Update) { - auto it = std::find_if(multi_version_entries.begin(), multi_version_entries.end(), - [title_id, version](const ExternalUpdateEntry& entry) { - return entry.title_id == title_id && entry.version == version; - }); + auto it = std::find_if(multi_version_entries.begin(), multi_version_entries.end(), [title_id, version](const ExternalUpdateEntry& entry) { + return entry.title_id == title_id && entry.version == version; + }); if (it != multi_version_entries.end()) { // Update existing entry - it->files[content_type] = file; + it->files[size_t(content_type)] = file; if (!version_string.empty()) { it->version_string = version_string; } @@ -1021,7 +964,7 @@ void ManualContentProvider::AddEntryWithVersion(TitleType title_type, ContentRec new_entry.title_id = title_id; new_entry.version = version; new_entry.version_string = version_string; - new_entry.files[content_type] = file; + new_entry.files[size_t(content_type)] = file; multi_version_entries.push_back(new_entry); } @@ -1118,26 +1061,19 @@ std::vector ManualContentProvider::ListUpdateVersions(u64 t VirtualFile ManualContentProvider::GetEntryForVersion(u64 title_id, ContentRecordType type, u32 version) const { for (const auto& entry : multi_version_entries) { if (entry.title_id == title_id && entry.version == version) { - auto it = entry.files.find(type); - if (it != entry.files.end()) { - return it->second; - } + if (auto const p = entry.files[size_t(type)]) + return p; } } return nullptr; } bool ManualContentProvider::HasMultipleVersions(u64 title_id, ContentRecordType type) const { - int count = 0; - for (const auto& entry : multi_version_entries) { - if (entry.title_id == title_id && entry.files.count(type) > 0) { - count++; - if (count > 1) { - return true; - } - } - } - return false; + size_t count = 0; + for (const auto& entry : multi_version_entries) + if (entry.title_id == title_id && entry.files[size_t(type)]) + ++count; + return count > 0; } ExternalContentProvider::ExternalContentProvider(std::vector load_directories) @@ -1256,7 +1192,7 @@ void ExternalContentProvider::ProcessNSP(const VirtualFile& file) { } } - std::map, std::map> version_files; + std::map, std::array> version_files; for (const auto& [title_id, nca_map] : ncas) { for (const auto& [type_pair, nca] : nca_map) { @@ -1277,7 +1213,7 @@ void ExternalContentProvider::ProcessNSP(const VirtualFile& file) { version = ver_it->second; } - version_files[{title_id, version}][content_type] = nca_file; + version_files[{title_id, version}][size_t(content_type)] = nca_file; } LOG_DEBUG(Service_FS, "Added entry - Title ID: {:016X}, Type: {}, Content: {}", @@ -1298,9 +1234,7 @@ void ExternalContentProvider::ProcessNSP(const VirtualFile& file) { bool version_exists = false; for (auto& existing : multi_version_entries) { if (existing.title_id == title_id && existing.version == version) { - for (const auto& [content_type, _file] : files_map) { - existing.files[content_type] = _file; - } + existing.files = files_map; if (existing.version_string.empty() && !ver_str.empty()) { existing.version_string = ver_str; } @@ -1383,7 +1317,7 @@ void ExternalContentProvider::ProcessXCI(const VirtualFile& file) { } } - std::map, std::map> version_files; + std::map, std::array> version_files; for (const auto& [title_id, nca_map] : ncas) { for (const auto& [type_pair, nca] : nca_map) { @@ -1404,7 +1338,7 @@ void ExternalContentProvider::ProcessXCI(const VirtualFile& file) { version = ver_it->second; } - version_files[{title_id, version}][content_type] = nca_file; + version_files[{title_id, version}][size_t(content_type)] = nca_file; } } } @@ -1422,9 +1356,7 @@ void ExternalContentProvider::ProcessXCI(const VirtualFile& file) { bool version_exists = false; for (auto& existing : multi_version_entries) { if (existing.title_id == title_id && existing.version == version) { - for (const auto& [content_type, _file] : files_map) { - existing.files[content_type] = _file; - } + existing.files = files_map; if (existing.version_string.empty() && !ver_str.empty()) { existing.version_string = ver_str; } @@ -1529,28 +1461,19 @@ std::vector ExternalContentProvider::ListUpdateVersions(u64 } VirtualFile ExternalContentProvider::GetEntryForVersion(u64 title_id, ContentRecordType type, u32 version) const { - for (const auto& entry : multi_version_entries) { - if (entry.title_id == title_id && entry.version == version) { - auto it = entry.files.find(type); - if (it != entry.files.end()) { - return it->second; - } - } - } + for (const auto& entry : multi_version_entries) + if (entry.title_id == title_id && entry.version == version) + if (auto const p = entry.files[size_t(type)]) + return p; return nullptr; } bool ExternalContentProvider::HasMultipleVersions(u64 title_id, ContentRecordType type) const { size_t count = 0; - for (const auto& entry : multi_version_entries) { - if (entry.title_id == title_id && entry.files.count(type) > 0) { - count++; - if (count > 1) { - return true; - } - } - } - return false; + for (const auto& entry : multi_version_entries) + if (entry.title_id == title_id && entry.files[size_t(type)]) + ++count; + return count > 1; } } // namespace FileSys diff --git a/src/core/file_sys/registered_cache.h b/src/core/file_sys/registered_cache.h index 04e231f453..2e39f74db8 100644 --- a/src/core/file_sys/registered_cache.h +++ b/src/core/file_sys/registered_cache.h @@ -11,19 +11,19 @@ #include #include #include +#include #include #include "common/common_types.h" #include "core/crypto/key_manager.h" #include "core/file_sys/vfs/vfs.h" +#include "core/file_sys/nca_metadata.h" namespace FileSys { - class ExternalContentProvider; - class CNMT; +class ExternalContentProvider; +class CNMT; class NCA; class NSP; class XCI; - -enum class ContentRecordType : u8; enum class NCAContentType : u8; enum class TitleType : u8; @@ -56,7 +56,7 @@ struct ExternalUpdateEntry { u64 title_id; u32 version; std::string version_string; - std::map files; + std::array files; }; constexpr u64 GetUpdateTitleID(u64 base_title_id) { @@ -207,11 +207,11 @@ private: ContentProviderParsingFunction parser; // maps tid -> NcaID of meta - std::map meta_id; + ankerl::unordered_dense::map meta_id; // maps tid -> meta - std::map meta; + ankerl::unordered_dense::map meta; // maps tid -> meta for CNMT in yuzu_meta - std::map yuzu_meta; + ankerl::unordered_dense::map yuzu_meta; }; enum class ContentProviderUnionSlot { @@ -220,6 +220,7 @@ enum class ContentProviderUnionSlot { SDMC, ///< SD Card FrontendManual, ///< Frontend-defined game list or similar External, ///< External content from NSP/XCI files in configured directories + Count, }; // Combines multiple ContentProvider(s) (i.e. SysNAND, UserNAND, SDMC) into one interface. @@ -228,8 +229,6 @@ public: ~ContentProviderUnion() override; void SetSlot(ContentProviderUnionSlot slot, ContentProvider* provider); - void ClearSlot(ContentProviderUnionSlot slot); - void Refresh() override; bool HasEntry(u64 title_id, ContentRecordType type) const override; std::optional GetEntryVersion(u64 title_id) const override; @@ -241,18 +240,18 @@ public: std::optional title_id) const override; const ExternalContentProvider* GetExternalProvider() const; - const ContentProvider* GetSlotProvider(ContentProviderUnionSlot slot) const; + [[nodiscard]] inline const ContentProvider* GetSlotProvider(ContentProviderUnionSlot slot) const { + return providers[size_t(slot)]; + } std::vector> ListEntriesFilterOrigin( std::optional origin = {}, std::optional title_type = {}, std::optional record_type = {}, std::optional title_id = {}) const; - std::optional GetSlotForEntry(u64 title_id, - ContentRecordType type) const; - + std::optional GetSlotForEntry(u64 title_id, ContentRecordType type) const; private: - std::map providers; + std::array providers; }; class ManualContentProvider : public ContentProvider { @@ -312,8 +311,8 @@ private: void ProcessXCI(const VirtualFile& file); std::vector load_dirs; - std::map, VirtualFile> entries; - std::map versions; + ankerl::unordered_dense::map, VirtualFile> entries; + ankerl::unordered_dense::map versions; std::vector multi_version_entries; }; diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp index 520cd39712..1b88f4133f 100644 --- a/src/yuzu/bootmanager.cpp +++ b/src/yuzu/bootmanager.cpp @@ -193,7 +193,7 @@ public: } void SwapBuffers() override { - if (auto window = dynamic_cast(surface)) { + if (auto window = static_cast(surface)) { if (!window->isExposed()) { LOG_DEBUG(Frontend, "SwapBuffers ignored: window not exposed"); return;