diff options
| author | PastaLaPate <pastalapate@proton.me> | 2026-11-02 20:34:15 +0100 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2026-11-02 20:34:15 +0100 |
| commit | 6f002badce45fa2fdb414043544c829e834a7e27 (patch) | |
| tree | 51c21a851fbc8218ebe7febb69fb710a891cc4fc /src/core/loader/deconstructed_rom_directory.cpp | |
| parent | 396f011ea875dfe4fdb1b0f489651ab74a505b74 (diff) | |
| parent | 046646e3514ca4950622d75609e68498f889989e (diff) | |
Merge branch 'nx2' into ci/qt-6.12.0
Diffstat (limited to 'src/core/loader/deconstructed_rom_directory.cpp')
| -rw-r--r-- | src/core/loader/deconstructed_rom_directory.cpp | 87 |
1 files changed, 65 insertions, 22 deletions
diff --git a/src/core/loader/deconstructed_rom_directory.cpp b/src/core/loader/deconstructed_rom_directory.cpp index cda1645548..4b1168a59b 100644 --- a/src/core/loader/deconstructed_rom_directory.cpp +++ b/src/core/loader/deconstructed_rom_directory.cpp @@ -6,7 +6,11 @@ #include <algorithm> #include <cctype> +#include <chrono> #include <cstring> +#include <map> +#include <memory> +#include <mutex> #include <string_view> #include "common/logging.h" #include "common/random.h" @@ -90,34 +94,42 @@ FileSys::VirtualFile FindLooseRomFS(const FileSys::VirtualDir& exefs_dir) { } if (const auto parent = exefs_dir->GetParentDirectory()) { if (const auto romfs_dir = parent->GetSubdirectory("romfs")) { + // Building it walks every file and keeps an object for each (title 1: 206k files, ~150 + // MiB), so loaders of the same directory share one build while any of them holds it. + static std::mutex cache_mutex; + static std::map<std::string, std::weak_ptr<FileSys::VfsFile>> cache; + std::scoped_lock lk{cache_mutex}; + auto& cached = cache[romfs_dir->GetFullPath()]; + if (auto built = cached.lock()) { + return built; + } LOG_INFO(Loader, "Using RomFS directory next to the ExeFS"); - return FileSys::CreateRomFS(romfs_dir); + const auto started = std::chrono::steady_clock::now(); + auto built = FileSys::CreateRomFS(romfs_dir); + LOG_INFO(Loader, "Loose RomFS image ready: elapsed_ms={}", + std::chrono::duration_cast<std::chrono::milliseconds>( + std::chrono::steady_clock::now() - started) + .count()); + cached = built; + return built; } } return nullptr; } -// The display version of a dump without control data, from its RomFS: Nintendo EPD titles name -// their resource size tables after it (System/Resource/ResourceSizeTable.Product.180.* in Mario -// Kart World 1.8.0). A hotfix can keep its minor version's tables (Product.110 in a 1.1.1). -std::string RomFSProductVersion(const FileSys::VirtualDir& romfs_dir) { - if (romfs_dir == nullptr) { - return {}; - } - const auto system_dir = romfs_dir->GetSubdirectory("System"); - const auto resource_dir = system_dir != nullptr ? system_dir->GetSubdirectory("Resource") : nullptr; - if (resource_dir == nullptr) { - return {}; - } - constexpr std::string_view Prefix = "ResourceSizeTable.Product."; +// The newest three-digit tag in a directory's file names of the form <prefix>NNN.<anything>. +std::string NewestProductTag(const FileSys::VirtualDir& dir, std::string_view prefix) { std::string best; - for (const auto& file : resource_dir->GetFiles()) { + if (dir == nullptr) { + return best; + } + for (const auto& file : dir->GetFiles()) { const std::string file_name = file->GetName(); - if (!file_name.starts_with(Prefix) || file_name.size() < Prefix.size() + 4) { + if (!file_name.starts_with(prefix) || file_name.size() < prefix.size() + 4) { continue; } - const std::string_view digits = std::string_view{file_name}.substr(Prefix.size(), 3); - if (file_name[Prefix.size() + 3] != '.' || + const std::string_view digits = std::string_view{file_name}.substr(prefix.size(), 3); + if (file_name[prefix.size() + 3] != '.' || !std::all_of(digits.begin(), digits.end(), [](char c) { return c >= '0' && c <= '9'; })) { continue; } @@ -125,6 +137,27 @@ std::string RomFSProductVersion(const FileSys::VirtualDir& romfs_dir) { best = version; } } + return best; +} + +// The display version of a dump without control data, from its RomFS: Nintendo EPD titles name +// their resource size tables after it (System/Resource/ResourceSizeTable.Product.180.* in Mario +// Kart World 1.8.0), and some name their game data list instead (GameData/GameDataList.Product.320.* +// in Donkey Kong Bananza 3.2.0). A hotfix can keep its minor version's tables (Product.110 in a +// 1.1.1). +std::string RomFSProductVersion(const FileSys::VirtualDir& romfs_dir) { + if (romfs_dir == nullptr) { + return {}; + } + const auto system_dir = romfs_dir->GetSubdirectory("System"); + std::string best = NewestProductTag( + system_dir != nullptr ? system_dir->GetSubdirectory("Resource") : nullptr, + "ResourceSizeTable.Product."); + if (const auto game_data = NewestProductTag(romfs_dir->GetSubdirectory("GameData"), + "GameDataList.Product."); + game_data > best) { + best = game_data; + } if (best.empty()) { return {}; } @@ -233,7 +266,7 @@ AppLoader_DeconstructedRomDirectory::AppLoader_DeconstructedRomDirectory(FileSys name = control->GetApplicationName(); } - romfs = FindLooseRomFS(file_dir); + romfs_exefs_dir = file_dir; if (const auto title_dir = file_dir->GetParentDirectory()) { romfs_version = RomFSProductVersion(title_dir->GetSubdirectory("romfs")); } @@ -245,7 +278,17 @@ AppLoader_DeconstructedRomDirectory::AppLoader_DeconstructedRomDirectory( , dir(std::move(directory)) , override_update(override_update_) { - romfs = FindLooseRomFS(dir); + romfs_exefs_dir = dir; +} + +// Loaders made only for metadata (the game list, title lookups) never need the RomFS, and several +// of them were alive with a whole copy each. +const FileSys::VirtualFile& AppLoader_DeconstructedRomDirectory::LooseRomFS() { + if (!romfs_resolved) { + romfs_resolved = true; + romfs = FindLooseRomFS(romfs_exefs_dir); + } + return romfs; } FileType AppLoader_DeconstructedRomDirectory::IdentifyType(const FileSys::VirtualFile& dir_file) { @@ -304,7 +347,7 @@ AppLoader_DeconstructedRomDirectory::LoadResult AppLoader_DeconstructedRomDirect // Loose-ExeFS loads bypass the NCA/NSP loaders, which are the ones that normally apply // LayeredFS to the RomFS; apply it here so mods in user/load/<titleid>/ work too. - if (romfs != nullptr) { + if (LooseRomFS() != nullptr) { const FileSys::PatchManager patch_manager(metadata.GetTitleID(), system.GetFileSystemController(), system.GetContentProvider()); @@ -414,7 +457,7 @@ AppLoader_DeconstructedRomDirectory::LoadResult AppLoader_DeconstructedRomDirect } ResultStatus AppLoader_DeconstructedRomDirectory::ReadRomFS(FileSys::VirtualFile& out_dir) { - if (romfs == nullptr) { + if (LooseRomFS() == nullptr) { return ResultStatus::ErrorNoRomFS; } |
