mirror of
https://github.com/RPCS3/rpcs3.git
synced 2026-05-07 13:37:46 +00:00
game_list: Fix ISO cache bypass in is_from_yml branch for multi-game ISOs (#18683)
Fixes regression from #18546 and #18679. ## Problem The is_from_yml ISO branch constructed iso_archive unconditionally, bypassing the cache check inside add_game, making the cache write-only for yml-sourced ISOs. ## Fix Added a lightweight index cache entry (iso_path + "//index") storing the subdir list + mtime. On hit, skips archive construction entirely. On miss, walks as before and writes the index
This commit is contained in:
parent
4f23f5505a
commit
d93d9b2c5a
3 changed files with 115 additions and 10 deletions
|
|
@ -19,17 +19,23 @@ namespace
|
|||
return dir;
|
||||
}
|
||||
|
||||
// FNV-64 hash of the ISO path used as the cache filename stem.
|
||||
std::string get_cache_stem(const std::string& iso_path)
|
||||
// FNV-64 hash of the given key used as the cache filename stem.
|
||||
std::string get_cache_stem(std::string_view key)
|
||||
{
|
||||
usz hash = rpcs3::fnv_seed;
|
||||
for (const char c : iso_path)
|
||||
for (const char c : key)
|
||||
{
|
||||
hash ^= static_cast<u8>(c);
|
||||
hash *= rpcs3::fnv_prime;
|
||||
}
|
||||
return fmt::format("%016llx", hash);
|
||||
}
|
||||
|
||||
// Separate stem for the per-ISO subdir index entry.
|
||||
std::string get_index_stem(const std::string& iso_path)
|
||||
{
|
||||
return get_cache_stem(iso_path + "//index");
|
||||
}
|
||||
}
|
||||
|
||||
namespace iso_cache
|
||||
|
|
@ -134,15 +140,96 @@ namespace iso_cache
|
|||
}
|
||||
}
|
||||
|
||||
bool load_index(const std::string& iso_path, std::vector<std::string>& out_subdirs)
|
||||
{
|
||||
fs::stat_t iso_stat{};
|
||||
if (!fs::get_stat(iso_path, iso_stat) || iso_stat.is_directory)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const std::string dir = get_cache_dir();
|
||||
const std::string yml_path = dir + get_index_stem(iso_path) + ".yml";
|
||||
|
||||
const fs::file yml_file(yml_path);
|
||||
if (!yml_file)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto [node, error] = yaml_load(yml_file.to_string());
|
||||
if (!error.empty())
|
||||
{
|
||||
iso_cache_log.warning("Failed to parse index YAML for '%s': %s", iso_path, error);
|
||||
return false;
|
||||
}
|
||||
|
||||
const s64 cached_mtime = node["mtime"].as<s64>(0);
|
||||
if (cached_mtime != iso_stat.mtime)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const YAML::Node subdirs_node = node["subdirs"];
|
||||
if (!subdirs_node || !subdirs_node.IsSequence())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
for (const auto& entry : subdirs_node)
|
||||
{
|
||||
std::string name = entry.as<std::string>("");
|
||||
if (!name.empty())
|
||||
{
|
||||
out_subdirs.push_back(std::move(name));
|
||||
}
|
||||
}
|
||||
|
||||
return !out_subdirs.empty();
|
||||
}
|
||||
|
||||
void save_index(const std::string& iso_path, const std::vector<std::string>& subdirs)
|
||||
{
|
||||
fs::stat_t iso_stat{};
|
||||
if (!fs::get_stat(iso_path, iso_stat))
|
||||
{
|
||||
return;
|
||||
}
|
||||
const std::string dir = get_cache_dir();
|
||||
const std::string yml_path = dir + get_index_stem(iso_path) + ".yml";
|
||||
|
||||
YAML::Emitter out;
|
||||
out << YAML::BeginMap;
|
||||
out << YAML::Key << "mtime" << YAML::Value << static_cast<long long>(iso_stat.mtime);
|
||||
out << YAML::Key << "subdirs" << YAML::Value << YAML::BeginSeq;
|
||||
for (const std::string& s : subdirs)
|
||||
{
|
||||
out << s;
|
||||
}
|
||||
out << YAML::EndSeq;
|
||||
out << YAML::EndMap;
|
||||
|
||||
if (fs::pending_file yml_file(yml_path); yml_file.file)
|
||||
{
|
||||
yml_file.file.write(out.c_str(), out.size());
|
||||
yml_file.commit();
|
||||
}
|
||||
else
|
||||
{
|
||||
iso_cache_log.warning("Failed to write index YAML for '%s'", iso_path);
|
||||
}
|
||||
}
|
||||
|
||||
void cleanup(const std::unordered_set<std::string>& valid_iso_paths)
|
||||
{
|
||||
const std::string dir = get_cache_dir();
|
||||
|
||||
// Build a set of stems that should exist.
|
||||
// Build a set of stems that should exist, including index entries.
|
||||
std::unordered_set<std::string> valid_stems;
|
||||
for (const std::string& path : valid_iso_paths)
|
||||
{
|
||||
valid_stems.insert(get_cache_stem(path));
|
||||
valid_stems.insert(get_index_stem(path));
|
||||
}
|
||||
|
||||
// Delete any cache files whose stem is not in the valid set.
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ struct iso_metadata_cache_entry
|
|||
std::vector<u8> icon_data{};
|
||||
std::string movie_path{};
|
||||
std::string audio_path{};
|
||||
std::vector<std::string> subdirs{};
|
||||
};
|
||||
|
||||
namespace iso_cache
|
||||
|
|
@ -27,6 +28,9 @@ namespace iso_cache
|
|||
// Persists a populated cache entry to disk.
|
||||
void save(const std::string& iso_path, const std::string& cache_key, const iso_metadata_cache_entry& entry);
|
||||
|
||||
bool load_index(const std::string& iso_path, std::vector<std::string>& out_subdirs);
|
||||
void save_index(const std::string& iso_path, const std::vector<std::string>& subdirs);
|
||||
|
||||
// Remove cache entries for ISOs that are no longer in the scanned set.
|
||||
void cleanup(const std::unordered_set<std::string>& valid_iso_paths);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -861,10 +861,22 @@ void game_list_frame::OnParsingFinished()
|
|||
{
|
||||
if (is_iso_file(entry.path))
|
||||
{
|
||||
std::vector<std::string> subdirs;
|
||||
|
||||
if (iso_cache::load_index(entry.path, subdirs))
|
||||
{
|
||||
for (const std::string& name : subdirs)
|
||||
{
|
||||
if (m_refresh_watcher.isCanceled()) break;
|
||||
add_game(entry.path, name);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
iso_archive archive(entry.path);
|
||||
const iso_fs_node& root = archive.root();
|
||||
const std::regex ps3_gm_regex("^PS3_GM[[:digit:]]{2}$");
|
||||
bool found = false;
|
||||
|
||||
for (const auto& child : root.children)
|
||||
{
|
||||
|
|
@ -872,24 +884,26 @@ void game_list_frame::OnParsingFinished()
|
|||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if (!child->metadata.is_directory)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
const std::string& name = child->metadata.name;
|
||||
|
||||
if (name == "PS3_GAME" || std::regex_match(name, ps3_gm_regex))
|
||||
{
|
||||
subdirs.push_back(name);
|
||||
add_game(entry.path, name);
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found)
|
||||
if (subdirs.empty())
|
||||
{
|
||||
add_game(entry.path);
|
||||
subdirs.push_back("PS3_GAME");
|
||||
}
|
||||
if (!m_refresh_watcher.isCanceled())
|
||||
{
|
||||
iso_cache::save_index(entry.path, subdirs);
|
||||
}
|
||||
|
||||
return;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue