GameList: Cache invalid entries

Saves repeatedly scanning them every time the application starts.
This commit is contained in:
Stenzek 2025-03-15 15:02:54 +10:00
parent 8a0400ad2c
commit dd885cfe2f
No known key found for this signature in database
2 changed files with 57 additions and 25 deletions

View File

@ -92,6 +92,7 @@ static bool ShouldLoadAchievementsProgress();
static bool GetExeListEntry(const std::string& path, Entry* entry); static bool GetExeListEntry(const std::string& path, Entry* entry);
static bool GetPsfListEntry(const std::string& path, Entry* entry); static bool GetPsfListEntry(const std::string& path, Entry* entry);
static bool GetDiscListEntry(const std::string& path, Entry* entry); static bool GetDiscListEntry(const std::string& path, Entry* entry);
static void MakeInvalidEntry(Entry* entry);
static void ApplyCustomAttributes(const std::string& path, Entry* entry, static void ApplyCustomAttributes(const std::string& path, Entry* entry,
const INISettingsInterface& custom_attributes_ini); const INISettingsInterface& custom_attributes_ini);
@ -109,7 +110,7 @@ static void ScanDirectory(const char* path, bool recursive, bool only_cache,
static bool AddFileFromCache(const std::string& path, std::time_t timestamp, const PlayedTimeMap& played_time_map, static bool AddFileFromCache(const std::string& path, std::time_t timestamp, const PlayedTimeMap& played_time_map,
const INISettingsInterface& custom_attributes_ini, const INISettingsInterface& custom_attributes_ini,
const Achievements::ProgressDatabase& achievements_progress); const Achievements::ProgressDatabase& achievements_progress);
static bool ScanFile(std::string path, std::time_t timestamp, std::unique_lock<std::recursive_mutex>& lock, static void ScanFile(std::string path, std::time_t timestamp, std::unique_lock<std::recursive_mutex>& lock,
const PlayedTimeMap& played_time_map, const INISettingsInterface& custom_attributes_ini, const PlayedTimeMap& played_time_map, const INISettingsInterface& custom_attributes_ini,
const Achievements::ProgressDatabase& achievements_progress, BinaryFileWriter& cache_writer); const Achievements::ProgressDatabase& achievements_progress, BinaryFileWriter& cache_writer);
@ -369,6 +370,32 @@ bool GameList::GetDiscListEntry(const std::string& path, Entry* entry)
return true; return true;
} }
void GameList::MakeInvalidEntry(Entry* entry)
{
entry->type = EntryType::Count;
entry->region = DiscRegion::Other;
entry->disc_set_index = -1;
entry->disc_set_member = false;
entry->has_custom_title = false;
entry->has_custom_region = false;
entry->custom_language = GameDatabase::Language::MaxCount;
entry->path = {};
entry->serial = {};
entry->title = {};
entry->disc_set_name = {};
entry->dbentry = nullptr;
entry->hash = 0;
entry->file_size = 0;
entry->uncompressed_size = 0;
entry->last_modified_time = 0;
entry->last_played_time = 0;
entry->achievements_hash = {};
entry->achievements_game_id = 0;
entry->num_achievements = 0;
entry->unlocked_achievements = 0;
entry->unlocked_achievements_hc = 0;
}
bool GameList::PopulateEntryFromPath(const std::string& path, Entry* entry) bool GameList::PopulateEntryFromPath(const std::string& path, Entry* entry)
{ {
if (System::IsExePath(path)) if (System::IsExePath(path))
@ -420,7 +447,7 @@ bool GameList::LoadEntriesFromCache(BinaryFileReader& reader)
!reader.ReadS64(&ge.file_size) || !reader.ReadU64(&ge.uncompressed_size) || !reader.ReadS64(&ge.file_size) || !reader.ReadU64(&ge.uncompressed_size) ||
!reader.ReadU64(reinterpret_cast<u64*>(&ge.last_modified_time)) || !reader.ReadS8(&ge.disc_set_index) || !reader.ReadU64(reinterpret_cast<u64*>(&ge.last_modified_time)) || !reader.ReadS8(&ge.disc_set_index) ||
!reader.Read(ge.achievements_hash.data(), ge.achievements_hash.size()) || !reader.Read(ge.achievements_hash.data(), ge.achievements_hash.size()) ||
region >= static_cast<u8>(DiscRegion::Count) || type >= static_cast<u8>(EntryType::Count)) region >= static_cast<u8>(DiscRegion::Count) || type > static_cast<u8>(EntryType::Count))
{ {
WARNING_LOG("Game list cache entry is corrupted"); WARNING_LOG("Game list cache entry is corrupted");
return false; return false;
@ -522,11 +549,8 @@ void GameList::ScanDirectory(const char* path, bool recursive, bool only_cache,
{ {
files_scanned++; files_scanned++;
if (progress->IsCancelled() || !GameList::IsScannableFilename(ffd.FileName) || if (progress->IsCancelled() || !IsScannableFilename(ffd.FileName) || IsPathExcluded(excluded_paths, ffd.FileName))
IsPathExcluded(excluded_paths, ffd.FileName))
{
continue; continue;
}
std::unique_lock lock(s_mutex); std::unique_lock lock(s_mutex);
if (GetEntryForPath(ffd.FileName) || if (GetEntryForPath(ffd.FileName) ||
@ -559,6 +583,10 @@ bool GameList::AddFileFromCache(const std::string& path, std::time_t timestamp,
return false; return false;
} }
// don't add invalid entries to the list, but don't scan them either
if (!entry.IsValid())
return true;
auto iter = played_time_map.find(entry.serial); auto iter = played_time_map.find(entry.serial);
if (iter != played_time_map.end()) if (iter != played_time_map.end())
{ {
@ -570,7 +598,7 @@ bool GameList::AddFileFromCache(const std::string& path, std::time_t timestamp,
return true; return true;
} }
bool GameList::ScanFile(std::string path, std::time_t timestamp, std::unique_lock<std::recursive_mutex>& lock, void GameList::ScanFile(std::string path, std::time_t timestamp, std::unique_lock<std::recursive_mutex>& lock,
const PlayedTimeMap& played_time_map, const INISettingsInterface& custom_attributes_ini, const PlayedTimeMap& played_time_map, const INISettingsInterface& custom_attributes_ini,
const Achievements::ProgressDatabase& achievements_progress, BinaryFileWriter& cache_writer) const Achievements::ProgressDatabase& achievements_progress, BinaryFileWriter& cache_writer)
{ {
@ -580,18 +608,8 @@ bool GameList::ScanFile(std::string path, std::time_t timestamp, std::unique_loc
VERBOSE_LOG("Scanning '{}'...", path); VERBOSE_LOG("Scanning '{}'...", path);
Entry entry; Entry entry;
if (!PopulateEntryFromPath(path, &entry)) if (PopulateEntryFromPath(path, &entry))
{ {
lock.lock();
return false;
}
entry.path = std::move(path);
entry.last_modified_time = timestamp;
if (cache_writer.IsOpen() && !WriteEntryToCache(&entry, cache_writer)) [[unlikely]]
WARNING_LOG("Failed to write entry '{}' to cache", entry.path);
const auto iter = played_time_map.find(entry.serial); const auto iter = played_time_map.find(entry.serial);
if (iter != played_time_map.end()) if (iter != played_time_map.end())
{ {
@ -603,9 +621,24 @@ bool GameList::ScanFile(std::string path, std::time_t timestamp, std::unique_loc
if (entry.IsDisc()) if (entry.IsDisc())
PopulateEntryAchievements(&entry, achievements_progress); PopulateEntryAchievements(&entry, achievements_progress);
}
else
{
MakeInvalidEntry(&entry);
}
entry.path = std::move(path);
entry.last_modified_time = timestamp;
if (cache_writer.IsOpen() && !WriteEntryToCache(&entry, cache_writer)) [[unlikely]]
WARNING_LOG("Failed to write entry '{}' to cache", entry.path);
lock.lock(); lock.lock();
// don't add invalid entries to the list
if (!entry.IsValid())
return;
// replace if present // replace if present
auto it = std::find_if(s_entries.begin(), s_entries.end(), auto it = std::find_if(s_entries.begin(), s_entries.end(),
[&entry](const Entry& existing_entry) { return (existing_entry.path == entry.path); }); [&entry](const Entry& existing_entry) { return (existing_entry.path == entry.path); });
@ -613,8 +646,6 @@ bool GameList::ScanFile(std::string path, std::time_t timestamp, std::unique_loc
*it = std::move(entry); *it = std::move(entry);
else else
s_entries.push_back(std::move(entry)); s_entries.push_back(std::move(entry));
return true;
} }
bool GameList::RescanCustomAttributesForPath(const std::string& path, const INISettingsInterface& custom_attributes_ini) bool GameList::RescanCustomAttributesForPath(const std::string& path, const INISettingsInterface& custom_attributes_ini)

View File

@ -33,7 +33,7 @@ enum class EntryType : u8
struct Entry struct Entry
{ {
EntryType type = EntryType::Disc; EntryType type = EntryType::Count;
DiscRegion region = DiscRegion::Other; DiscRegion region = DiscRegion::Other;
s8 disc_set_index = -1; s8 disc_set_index = -1;
@ -69,6 +69,7 @@ struct Entry
TinyString GetReleaseDateString() const; TinyString GetReleaseDateString() const;
ALWAYS_INLINE bool IsValid() const { return (type < EntryType::Count); }
ALWAYS_INLINE bool IsDisc() const { return (type == EntryType::Disc); } ALWAYS_INLINE bool IsDisc() const { return (type == EntryType::Disc); }
ALWAYS_INLINE bool IsDiscSet() const { return (type == EntryType::DiscSet); } ALWAYS_INLINE bool IsDiscSet() const { return (type == EntryType::DiscSet); }
ALWAYS_INLINE bool HasCustomLanguage() const { return (custom_language != GameDatabase::Language::MaxCount); } ALWAYS_INLINE bool HasCustomLanguage() const { return (custom_language != GameDatabase::Language::MaxCount); }