mirror of
https://github.com/RPCS3/rpcs3.git
synced 2026-05-12 16:19:44 -06:00
163 lines
4.1 KiB
C++
163 lines
4.1 KiB
C++
#include "stdafx.h"
|
|
|
|
#include "iso_cache.h"
|
|
#include "Loader/PSF.h"
|
|
#include "util/yaml.hpp"
|
|
#include "util/fnv_hash.hpp"
|
|
#include "Utilities/File.h"
|
|
|
|
#include <unordered_set>
|
|
|
|
LOG_CHANNEL(iso_cache_log, "ISOCache");
|
|
|
|
namespace
|
|
{
|
|
std::string get_cache_dir()
|
|
{
|
|
const std::string dir = fs::get_cache_dir() + "cache/iso_cache/";
|
|
fs::create_path(dir);
|
|
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)
|
|
{
|
|
usz hash = rpcs3::fnv_seed;
|
|
for (const char c : iso_path)
|
|
{
|
|
hash ^= static_cast<u8>(c);
|
|
hash *= rpcs3::fnv_prime;
|
|
}
|
|
return fmt::format("%016llx", hash);
|
|
}
|
|
}
|
|
|
|
namespace iso_cache
|
|
{
|
|
bool load(const std::string& iso_path, const std::string& cache_key, iso_metadata_cache_entry& out_entry)
|
|
{
|
|
fs::stat_t iso_stat{};
|
|
if (!fs::get_stat(iso_path, iso_stat) || iso_stat.is_directory)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
const std::string stem = get_cache_stem(cache_key);
|
|
const std::string dir = get_cache_dir();
|
|
const std::string yml_path = dir + stem + ".yml";
|
|
const std::string sfo_path = dir + stem + ".sfo";
|
|
const std::string png_path = dir + stem + ".png";
|
|
|
|
const fs::file yml_file(yml_path);
|
|
if (!yml_file)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
auto [node, error] = yaml_load(yml_file.to_string());
|
|
if (!error.empty())
|
|
{
|
|
iso_cache_log.warning("Failed to parse cache YAML for '%s': %s", iso_path, error);
|
|
return false;
|
|
}
|
|
|
|
// Reject stale entries.
|
|
const s64 cached_mtime = node["mtime"].as<s64>(0);
|
|
if (cached_mtime != iso_stat.mtime)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
const fs::file sfo_file(sfo_path);
|
|
if (!sfo_file)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
out_entry.mtime = cached_mtime;
|
|
out_entry.psf_data = sfo_file.to_vector<u8>();
|
|
out_entry.icon_path = node["icon_path"].as<std::string>("");
|
|
out_entry.movie_path = node["movie_path"].as<std::string>("");
|
|
out_entry.audio_path = node["audio_path"].as<std::string>("");
|
|
|
|
// Icon bytes are optional — game may have no icon.
|
|
if (const fs::file png_file(png_path); png_file)
|
|
{
|
|
out_entry.icon_data = png_file.to_vector<u8>();
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void save(const std::string& iso_path, const std::string& cache_key, const iso_metadata_cache_entry& entry)
|
|
{
|
|
const std::string stem = get_cache_stem(cache_key);
|
|
const std::string dir = get_cache_dir();
|
|
const std::string yml_path = dir + stem + ".yml";
|
|
const std::string sfo_path = dir + stem + ".sfo";
|
|
const std::string png_path = dir + stem + ".png";
|
|
|
|
YAML::Emitter out;
|
|
out << YAML::BeginMap;
|
|
out << YAML::Key << "mtime" << YAML::Value << static_cast<long long>(entry.mtime);
|
|
out << YAML::Key << "icon_path" << YAML::Value << entry.icon_path;
|
|
out << YAML::Key << "movie_path" << YAML::Value << entry.movie_path;
|
|
out << YAML::Key << "audio_path" << YAML::Value << entry.audio_path;
|
|
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 cache YAML for '%s'", iso_path);
|
|
}
|
|
|
|
if (!entry.psf_data.empty())
|
|
{
|
|
if (fs::pending_file sfo_file(sfo_path); sfo_file.file)
|
|
{
|
|
sfo_file.file.write(entry.psf_data);
|
|
sfo_file.commit();
|
|
}
|
|
}
|
|
|
|
if (!entry.icon_data.empty())
|
|
{
|
|
if (fs::pending_file png_file(png_path); png_file.file)
|
|
{
|
|
png_file.file.write(entry.icon_data);
|
|
png_file.commit();
|
|
}
|
|
}
|
|
}
|
|
|
|
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.
|
|
std::unordered_set<std::string> valid_stems;
|
|
for (const std::string& path : valid_iso_paths)
|
|
{
|
|
valid_stems.insert(get_cache_stem(path));
|
|
}
|
|
|
|
// Delete any cache files whose stem is not in the valid set.
|
|
fs::dir cache_dir(dir);
|
|
fs::dir_entry entry{};
|
|
while (cache_dir.read(entry))
|
|
{
|
|
if (entry.name == "." || entry.name == "..") continue;
|
|
|
|
const std::string stem = entry.name.substr(0, entry.name.find_last_of('.'));
|
|
if (valid_stems.find(stem) == valid_stems.end())
|
|
{
|
|
fs::remove_file(dir + entry.name);
|
|
}
|
|
}
|
|
}
|
|
}
|