Don't store custom names from TitleDatabase in GameFileCache

This saves us from having to update the GameFileCache when the
TitleDatabase changes (for instance when the user changes language).
This commit is contained in:
JosJuice 2018-06-03 12:07:53 +02:00
parent 0f7370a22c
commit 85e94cc510
12 changed files with 75 additions and 94 deletions

View File

@ -19,6 +19,8 @@
namespace Core namespace Core
{ {
static const std::string EMPTY_STRING;
static std::string GetLanguageCode(DiscIO::Language language) static std::string GetLanguageCode(DiscIO::Language language)
{ {
switch (language) switch (language)
@ -157,16 +159,16 @@ TitleDatabase::TitleDatabase()
TitleDatabase::~TitleDatabase() = default; TitleDatabase::~TitleDatabase() = default;
std::string TitleDatabase::GetTitleName(const std::string& game_id, TitleType type) const const std::string& TitleDatabase::GetTitleName(const std::string& game_id, TitleType type) const
{ {
const auto& map = IsWiiTitle(game_id) ? m_wii_title_map : m_gc_title_map; const auto& map = IsWiiTitle(game_id) ? m_wii_title_map : m_gc_title_map;
const std::string key = const std::string key =
type == TitleType::Channel && game_id.length() == 6 ? game_id.substr(0, 4) : game_id; type == TitleType::Channel && game_id.length() == 6 ? game_id.substr(0, 4) : game_id;
const auto iterator = map.find(key); const auto iterator = map.find(key);
return iterator != map.end() ? iterator->second : ""; return iterator != map.end() ? iterator->second : EMPTY_STRING;
} }
std::string TitleDatabase::GetChannelName(u64 title_id) const const std::string& TitleDatabase::GetChannelName(u64 title_id) const
{ {
const std::string id{ const std::string id{
{static_cast<char>((title_id >> 24) & 0xff), static_cast<char>((title_id >> 16) & 0xff), {static_cast<char>((title_id >> 24) & 0xff), static_cast<char>((title_id >> 16) & 0xff),
@ -176,7 +178,7 @@ std::string TitleDatabase::GetChannelName(u64 title_id) const
std::string TitleDatabase::Describe(const std::string& game_id, TitleType type) const std::string TitleDatabase::Describe(const std::string& game_id, TitleType type) const
{ {
const std::string title_name = GetTitleName(game_id, type); const std::string& title_name = GetTitleName(game_id, type);
if (title_name.empty()) if (title_name.empty())
return game_id; return game_id;
return StringFromFormat("%s (%s)", title_name.c_str(), game_id.c_str()); return StringFromFormat("%s (%s)", title_name.c_str(), game_id.c_str());

View File

@ -26,10 +26,10 @@ public:
// Get a user friendly title name for a game ID. // Get a user friendly title name for a game ID.
// This falls back to returning an empty string if none could be found. // This falls back to returning an empty string if none could be found.
std::string GetTitleName(const std::string& game_id, TitleType = TitleType::Other) const; const std::string& GetTitleName(const std::string& game_id, TitleType = TitleType::Other) const;
// Same as above, but takes a title ID instead of a game ID, and can only find names of channels. // Same as above, but takes a title ID instead of a game ID, and can only find names of channels.
std::string GetChannelName(u64 title_id) const; const std::string& GetChannelName(u64 title_id) const;
// Get a description for a game ID (title name if available + game ID). // Get a description for a game ID (title name if available + game ID).
std::string Describe(const std::string& game_id, TitleType = TitleType::Other) const; std::string Describe(const std::string& game_id, TitleType = TitleType::Other) const;

View File

@ -80,7 +80,7 @@ QVariant GameListModel::data(const QModelIndex& index, int role) const
case COL_TITLE: case COL_TITLE:
if (role == Qt::DisplayRole || role == Qt::InitialSortOrderRole) if (role == Qt::DisplayRole || role == Qt::InitialSortOrderRole)
{ {
QString name = QString::fromStdString(game.GetName()); QString name = QString::fromStdString(game.GetName(m_title_database));
const int disc_nr = game.GetDiscNumber() + 1; const int disc_nr = game.GetDiscNumber() + 1;
if (disc_nr > 1) if (disc_nr > 1)
{ {
@ -161,8 +161,10 @@ bool GameListModel::ShouldDisplayGameListItem(int index) const
const UICommon::GameFile& game = *m_games[index]; const UICommon::GameFile& game = *m_games[index];
if (!m_term.isEmpty() && if (!m_term.isEmpty() &&
!QString::fromStdString(game.GetName()).contains(m_term, Qt::CaseInsensitive)) !QString::fromStdString(game.GetName(m_title_database)).contains(m_term, Qt::CaseInsensitive))
{
return false; return false;
}
const bool show_platform = [&game] { const bool show_platform = [&game] {
switch (game.GetPlatform()) switch (game.GetPlatform())

View File

@ -10,6 +10,8 @@
#include <QAbstractTableModel> #include <QAbstractTableModel>
#include <QString> #include <QString>
#include "Core/TitleDatabase.h"
#include "DolphinQt2/GameList/GameTracker.h" #include "DolphinQt2/GameList/GameTracker.h"
#include "UICommon/GameFile.h" #include "UICommon/GameFile.h"
@ -63,5 +65,6 @@ private:
GameTracker m_tracker; GameTracker m_tracker;
QList<std::shared_ptr<const UICommon::GameFile>> m_games; QList<std::shared_ptr<const UICommon::GameFile>> m_games;
Core::TitleDatabase m_title_database;
QString m_term; QString m_term;
}; };

View File

@ -105,7 +105,7 @@ void GameTracker::StartInternal()
m_initial_games_emitted_event.Wait(); m_initial_games_emitted_event.Wait();
bool cache_updated = m_cache.Update(paths, emit_game_loaded, emit_game_removed); bool cache_updated = m_cache.Update(paths, emit_game_loaded, emit_game_removed);
cache_updated |= m_cache.UpdateAdditionalMetadata(m_title_database, emit_game_updated); cache_updated |= m_cache.UpdateAdditionalMetadata(emit_game_updated);
if (cache_updated) if (cache_updated)
m_cache.Save(); m_cache.Save();
} }
@ -256,7 +256,7 @@ void GameTracker::LoadGame(const QString& path)
if (!DiscIO::ShouldHideFromGameList(converted_path)) if (!DiscIO::ShouldHideFromGameList(converted_path))
{ {
bool cache_changed = false; bool cache_changed = false;
auto game = m_cache.AddOrGet(converted_path, &cache_changed, m_title_database); auto game = m_cache.AddOrGet(converted_path, &cache_changed);
if (game) if (game)
emit GameLoaded(std::move(game)); emit GameLoaded(std::move(game));
if (cache_changed) if (cache_changed)

View File

@ -14,7 +14,6 @@
#include "Common/Event.h" #include "Common/Event.h"
#include "Common/WorkQueueThread.h" #include "Common/WorkQueueThread.h"
#include "Core/TitleDatabase.h"
#include "UICommon/GameFile.h" #include "UICommon/GameFile.h"
#include "UICommon/GameFileCache.h" #include "UICommon/GameFileCache.h"
@ -75,7 +74,6 @@ private:
QMap<QString, QSet<QString>> m_tracked_files; QMap<QString, QSet<QString>> m_tracked_files;
Common::WorkQueueThread<Command> m_load_thread; Common::WorkQueueThread<Command> m_load_thread;
UICommon::GameFileCache m_cache; UICommon::GameFileCache m_cache;
Core::TitleDatabase m_title_database;
Common::Event m_cache_loaded_event; Common::Event m_cache_loaded_event;
Common::Event m_initial_games_emitted_event; Common::Event m_initial_games_emitted_event;
bool m_initial_games_emitted = false; bool m_initial_games_emitted = false;

View File

@ -82,9 +82,18 @@ public:
static bool sorted = false; static bool sorted = false;
static int CompareGameListItems(const UICommon::GameFile* iso1, const UICommon::GameFile* iso2, static int CompareGameListItems(size_t item1, size_t item2, long sortData,
long sortData = GameListCtrl::COLUMN_TITLE) const GameListCtrl* caller)
{ {
// return 1 if item1 > item2
// return -1 if item1 < item2
// return 0 for identity
const UICommon::GameFile* iso1 = caller->GetISO(item1);
const UICommon::GameFile* iso2 = caller->GetISO(item2);
if (iso1 == iso2)
return 0;
int t = 1; int t = 1;
if (sortData < 0) if (sortData < 0)
@ -136,7 +145,8 @@ static int CompareGameListItems(const UICommon::GameFile* iso1, const UICommon::
if (sortData != GameListCtrl::COLUMN_TITLE) if (sortData != GameListCtrl::COLUMN_TITLE)
t = 1; t = 1;
int name_cmp = strcasecmp(iso1->GetName().c_str(), iso2->GetName().c_str()) * t; int name_cmp =
strcasecmp(caller->GetShownName(item1).c_str(), caller->GetShownName(item2).c_str()) * t;
if (name_cmp != 0) if (name_cmp != 0)
return name_cmp; return name_cmp;
@ -383,28 +393,34 @@ void GameListCtrl::RefreshList()
if (Core::GetState() != Core::State::Uninitialized) if (Core::GetState() != Core::State::Uninitialized)
return; return;
// Use a newly loaded title database (it might have gotten updated)
const Core::TitleDatabase title_database;
m_shown_names.clear();
m_shown_files.clear(); m_shown_files.clear();
{ {
std::unique_lock<std::mutex> lk(m_cache_mutex); std::unique_lock<std::mutex> lk(m_cache_mutex);
m_cache.ForEach([this](const std::shared_ptr<const UICommon::GameFile>& game_file) { m_cache.ForEach(
if (ShouldDisplayGameListItem(*game_file)) [this, &title_database](const std::shared_ptr<const UICommon::GameFile>& game_file) {
m_shown_files.push_back(game_file); if (ShouldDisplayGameListItem(*game_file))
}); {
m_shown_names.push_back(game_file->GetName(title_database));
m_shown_files.push_back(game_file);
}
});
} }
// Drives are not cached. Not sure if this is required, but better to err on the // Drives are not cached. Not sure if this is required, but better to err on the
// side of caution if cross-platform issues could come into play. // side of caution if cross-platform issues could come into play.
if (SConfig::GetInstance().m_ListDrives) if (SConfig::GetInstance().m_ListDrives)
{ {
std::unique_lock<std::mutex> lk(m_title_database_mutex);
for (const auto& drive : Common::GetCDDevices()) for (const auto& drive : Common::GetCDDevices())
{ {
auto file = std::make_shared<UICommon::GameFile>(drive); auto file = std::make_shared<UICommon::GameFile>(drive);
if (file->IsValid()) if (file->IsValid())
{ {
if (file->CustomNameChanged(m_title_database)) m_shown_names.push_back(file->GetName(title_database));
file->CustomNameCommit(); m_shown_files.push_back(std::move(file));
m_shown_files.push_back(file);
} }
} }
} }
@ -501,7 +517,8 @@ void GameListCtrl::RefreshList()
// Update the column content of the item at index // Update the column content of the item at index
void GameListCtrl::UpdateItemAtColumn(long index, int column) void GameListCtrl::UpdateItemAtColumn(long index, int column)
{ {
const auto& iso_file = *GetISO(GetItemData(index)); const size_t item_data = GetItemData(index);
const auto& iso_file = *GetISO(item_data);
switch (column) switch (column)
{ {
@ -527,7 +544,7 @@ void GameListCtrl::UpdateItemAtColumn(long index, int column)
} }
case COLUMN_TITLE: case COLUMN_TITLE:
{ {
wxString name = StrToWxStr(iso_file.GetName()); wxString name = StrToWxStr(GetShownName(item_data));
int disc_number = iso_file.GetDiscNumber() + 1; int disc_number = iso_file.GetDiscNumber() + 1;
if (disc_number > 1 && if (disc_number > 1 &&
@ -628,12 +645,6 @@ void GameListCtrl::RescanList()
const std::vector<std::string> game_paths = UICommon::FindAllGamePaths( const std::vector<std::string> game_paths = UICommon::FindAllGamePaths(
SConfig::GetInstance().m_ISOFolder, SConfig::GetInstance().m_RecursiveISOFolder); SConfig::GetInstance().m_ISOFolder, SConfig::GetInstance().m_RecursiveISOFolder);
// Reload the TitleDatabase
{
std::unique_lock<std::mutex> lock(m_title_database_mutex);
m_title_database = {};
}
bool cache_changed = false; bool cache_changed = false;
{ {
@ -647,7 +658,7 @@ void GameListCtrl::RescanList()
{ {
std::unique_lock<std::mutex> lk(m_cache_mutex); std::unique_lock<std::mutex> lk(m_cache_mutex);
if (m_cache.UpdateAdditionalMetadata(m_title_database)) if (m_cache.UpdateAdditionalMetadata())
{ {
cache_changed = true; cache_changed = true;
QueueEvent(new wxCommandEvent(DOLPHIN_EVT_REFRESH_GAMELIST)); QueueEvent(new wxCommandEvent(DOLPHIN_EVT_REFRESH_GAMELIST));
@ -701,19 +712,15 @@ const UICommon::GameFile* GameListCtrl::GetISO(size_t index) const
return nullptr; return nullptr;
} }
const std::string& GameListCtrl::GetShownName(size_t index) const
{
return m_shown_names[index];
}
static GameListCtrl* caller; static GameListCtrl* caller;
static int wxCALLBACK wxListCompare(wxIntPtr item1, wxIntPtr item2, wxIntPtr sortData) static int wxCALLBACK wxListCompare(wxIntPtr item1, wxIntPtr item2, wxIntPtr sortData)
{ {
// return 1 if item1 > item2 return CompareGameListItems(item1, item2, sortData, caller);
// return -1 if item1 < item2
// return 0 for identity
const UICommon::GameFile* iso1 = caller->GetISO(item1);
const UICommon::GameFile* iso2 = caller->GetISO(item2);
if (iso1 == iso2)
return 0;
return CompareGameListItems(iso1, iso2, sortData);
} }
void GameListCtrl::OnColumnClick(wxListEvent& event) void GameListCtrl::OnColumnClick(wxListEvent& event)

View File

@ -35,6 +35,7 @@ public:
void BrowseForDirectory(); void BrowseForDirectory();
const UICommon::GameFile* GetISO(size_t index) const; const UICommon::GameFile* GetISO(size_t index) const;
const std::string& GetShownName(size_t index) const;
const UICommon::GameFile* GetSelectedISO() const; const UICommon::GameFile* GetSelectedISO() const;
static bool IsHidingItems(); static bool IsHidingItems();
@ -111,13 +112,12 @@ private:
UICommon::GameFileCache m_cache; UICommon::GameFileCache m_cache;
// Locks the cache object, not the shared_ptr<GameFile>s obtained from it // Locks the cache object, not the shared_ptr<GameFile>s obtained from it
std::mutex m_cache_mutex; std::mutex m_cache_mutex;
Core::TitleDatabase m_title_database;
std::mutex m_title_database_mutex;
std::thread m_scan_thread; std::thread m_scan_thread;
Common::Event m_scan_trigger; Common::Event m_scan_trigger;
Common::Flag m_scan_exiting; Common::Flag m_scan_exiting;
// UI thread's view into the cache // UI thread's view into the cache
std::vector<std::shared_ptr<const UICommon::GameFile>> m_shown_files; std::vector<std::shared_ptr<const UICommon::GameFile>> m_shown_files;
std::vector<std::string> m_shown_names;
int m_last_column; int m_last_column;
int m_last_sort; int m_last_sort;

View File

@ -141,20 +141,6 @@ bool GameFile::IsValid() const
return true; return true;
} }
bool GameFile::CustomNameChanged(const Core::TitleDatabase& title_database)
{
const auto type = m_platform == DiscIO::Platform::WiiWAD ?
Core::TitleDatabase::TitleType::Channel :
Core::TitleDatabase::TitleType::Other;
m_pending.custom_name = title_database.GetTitleName(m_game_id, type);
return m_custom_name != m_pending.custom_name;
}
void GameFile::CustomNameCommit()
{
m_custom_name = std::move(m_pending.custom_name);
}
void GameBanner::DoState(PointerWrap& p) void GameBanner::DoState(PointerWrap& p)
{ {
p.Do(buffer); p.Do(buffer);
@ -191,7 +177,6 @@ void GameFile::DoState(PointerWrap& p)
m_volume_banner.DoState(p); m_volume_banner.DoState(p);
m_custom_banner.DoState(p); m_custom_banner.DoState(p);
p.Do(m_custom_name);
} }
bool GameFile::IsElfOrDol() const bool GameFile::IsElfOrDol() const
@ -280,11 +265,17 @@ void GameFile::CustomBannerCommit()
m_custom_banner = std::move(m_pending.custom_banner); m_custom_banner = std::move(m_pending.custom_banner);
} }
const std::string& GameFile::GetName(const Core::TitleDatabase& title_database) const
{
const auto type = m_platform == DiscIO::Platform::WiiWAD ?
Core::TitleDatabase::TitleType::Channel :
Core::TitleDatabase::TitleType::Other;
const std::string& custom_name = title_database.GetTitleName(m_game_id, type);
return custom_name.empty() ? GetName() : custom_name;
}
const std::string& GameFile::GetName(bool long_name) const const std::string& GameFile::GetName(bool long_name) const
{ {
if (!m_custom_name.empty())
return m_custom_name;
const std::string& name = long_name ? GetLongName() : GetShortName(); const std::string& name = long_name ? GetLongName() : GetShortName();
if (!name.empty()) if (!name.empty())
return name; return name;
@ -323,9 +314,7 @@ std::string GameFile::GetUniqueIdentifier() const
if (GetRevision() != 0) if (GetRevision() != 0)
info.push_back("Revision " + std::to_string(GetRevision())); info.push_back("Revision " + std::to_string(GetRevision()));
std::string name(GetLongName(lang)); const std::string& name = GetName();
if (name.empty())
name = GetName();
int disc_number = GetDiscNumber() + 1; int disc_number = GetDiscNumber() + 1;

View File

@ -44,6 +44,7 @@ public:
bool IsValid() const; bool IsValid() const;
const std::string& GetFilePath() const { return m_file_path; } const std::string& GetFilePath() const { return m_file_path; }
const std::string& GetFileName() const { return m_file_name; } const std::string& GetFileName() const { return m_file_name; }
const std::string& GetName(const Core::TitleDatabase& title_database) const;
const std::string& GetName(bool long_name = true) const; const std::string& GetName(bool long_name = true) const;
const std::string& GetMaker(bool long_maker = true) const; const std::string& GetMaker(bool long_maker = true) const;
const std::string& GetShortName(DiscIO::Language l) const { return Lookup(l, m_short_names); } const std::string& GetShortName(DiscIO::Language l) const { return Lookup(l, m_short_names); }
@ -79,8 +80,6 @@ public:
void WiiBannerCommit(); void WiiBannerCommit();
bool CustomBannerChanged(); bool CustomBannerChanged();
void CustomBannerCommit(); void CustomBannerCommit();
bool CustomNameChanged(const Core::TitleDatabase& title_database);
void CustomNameCommit();
private: private:
static const std::string& Lookup(DiscIO::Language language, static const std::string& Lookup(DiscIO::Language language,
@ -121,8 +120,6 @@ private:
GameBanner m_volume_banner{}; GameBanner m_volume_banner{};
GameBanner m_custom_banner{}; GameBanner m_custom_banner{};
// Overridden name from TitleDatabase
std::string m_custom_name{};
// The following data members allow GameFileCache to construct updated versions // The following data members allow GameFileCache to construct updated versions
// of GameFiles in a threadsafe way. They should not be handled in DoState. // of GameFiles in a threadsafe way. They should not be handled in DoState.
@ -130,7 +127,6 @@ private:
{ {
GameBanner volume_banner; GameBanner volume_banner;
GameBanner custom_banner; GameBanner custom_banner;
std::string custom_name;
} m_pending{}; } m_pending{};
}; };

View File

@ -20,15 +20,13 @@
#include "Common/FileSearch.h" #include "Common/FileSearch.h"
#include "Common/FileUtil.h" #include "Common/FileUtil.h"
#include "Core/TitleDatabase.h"
#include "DiscIO/DirectoryBlob.h" #include "DiscIO/DirectoryBlob.h"
#include "UICommon/GameFile.h" #include "UICommon/GameFile.h"
namespace UICommon namespace UICommon
{ {
static constexpr u32 CACHE_REVISION = 10; // Last changed in PR 6429 static constexpr u32 CACHE_REVISION = 11; // Last changed in PR 7058
std::vector<std::string> FindAllGamePaths(const std::vector<std::string>& directories_to_scan, std::vector<std::string> FindAllGamePaths(const std::vector<std::string>& directories_to_scan,
bool recursive_scan) bool recursive_scan)
@ -52,8 +50,7 @@ void GameFileCache::Clear()
} }
std::shared_ptr<const GameFile> GameFileCache::AddOrGet(const std::string& path, std::shared_ptr<const GameFile> GameFileCache::AddOrGet(const std::string& path,
bool* cache_changed, bool* cache_changed)
const Core::TitleDatabase& title_database)
{ {
auto it = std::find_if( auto it = std::find_if(
m_cached_files.begin(), m_cached_files.end(), m_cached_files.begin(), m_cached_files.end(),
@ -67,7 +64,7 @@ std::shared_ptr<const GameFile> GameFileCache::AddOrGet(const std::string& path,
m_cached_files.emplace_back(std::move(game)); m_cached_files.emplace_back(std::move(game));
} }
std::shared_ptr<GameFile>& result = found ? *it : m_cached_files.back(); std::shared_ptr<GameFile>& result = found ? *it : m_cached_files.back();
if (UpdateAdditionalMetadata(&result, title_database) || !found) if (UpdateAdditionalMetadata(&result) || !found)
*cache_changed = true; *cache_changed = true;
return result; return result;
@ -135,14 +132,13 @@ bool GameFileCache::Update(
} }
bool GameFileCache::UpdateAdditionalMetadata( bool GameFileCache::UpdateAdditionalMetadata(
const Core::TitleDatabase& title_database,
std::function<void(const std::shared_ptr<const GameFile>&)> game_updated) std::function<void(const std::shared_ptr<const GameFile>&)> game_updated)
{ {
bool cache_changed = false; bool cache_changed = false;
for (std::shared_ptr<GameFile>& file : m_cached_files) for (std::shared_ptr<GameFile>& file : m_cached_files)
{ {
const bool updated = UpdateAdditionalMetadata(&file, title_database); const bool updated = UpdateAdditionalMetadata(&file);
cache_changed |= updated; cache_changed |= updated;
if (game_updated && updated) if (game_updated && updated)
game_updated(file); game_updated(file);
@ -151,13 +147,11 @@ bool GameFileCache::UpdateAdditionalMetadata(
return cache_changed; return cache_changed;
} }
bool GameFileCache::UpdateAdditionalMetadata(std::shared_ptr<GameFile>* game_file, bool GameFileCache::UpdateAdditionalMetadata(std::shared_ptr<GameFile>* game_file)
const Core::TitleDatabase& title_database)
{ {
const bool wii_banner_changed = (*game_file)->WiiBannerChanged(); const bool wii_banner_changed = (*game_file)->WiiBannerChanged();
const bool custom_banner_changed = (*game_file)->CustomBannerChanged(); const bool custom_banner_changed = (*game_file)->CustomBannerChanged();
const bool custom_title_changed = (*game_file)->CustomNameChanged(title_database); if (!wii_banner_changed && !custom_banner_changed)
if (!wii_banner_changed && !custom_banner_changed && !custom_title_changed)
return false; return false;
// If a cached file needs an update, apply the updates to a copy and delete the original. // If a cached file needs an update, apply the updates to a copy and delete the original.
@ -168,8 +162,6 @@ bool GameFileCache::UpdateAdditionalMetadata(std::shared_ptr<GameFile>* game_fil
copy->WiiBannerCommit(); copy->WiiBannerCommit();
if (custom_banner_changed) if (custom_banner_changed)
copy->CustomBannerCommit(); copy->CustomBannerCommit();
if (custom_title_changed)
copy->CustomNameCommit();
*game_file = std::move(copy); *game_file = std::move(copy);
return true; return true;

View File

@ -16,11 +16,6 @@
class PointerWrap; class PointerWrap;
namespace Core
{
class TitleDatabase;
}
namespace UICommon namespace UICommon
{ {
class GameFile; class GameFile;
@ -36,23 +31,20 @@ public:
void Clear(); void Clear();
// Returns nullptr if the file is invalid. // Returns nullptr if the file is invalid.
std::shared_ptr<const GameFile> AddOrGet(const std::string& path, bool* cache_changed, std::shared_ptr<const GameFile> AddOrGet(const std::string& path, bool* cache_changed);
const Core::TitleDatabase& title_database);
// These functions return true if the call modified the cache. // These functions return true if the call modified the cache.
bool Update(const std::vector<std::string>& all_game_paths, bool Update(const std::vector<std::string>& all_game_paths,
std::function<void(const std::shared_ptr<const GameFile>&)> game_added_to_cache = {}, std::function<void(const std::shared_ptr<const GameFile>&)> game_added_to_cache = {},
std::function<void(const std::string&)> game_removed_from_cache = {}); std::function<void(const std::string&)> game_removed_from_cache = {});
bool UpdateAdditionalMetadata( bool UpdateAdditionalMetadata(
const Core::TitleDatabase& title_database,
std::function<void(const std::shared_ptr<const GameFile>&)> game_updated = {}); std::function<void(const std::shared_ptr<const GameFile>&)> game_updated = {});
bool Load(); bool Load();
bool Save(); bool Save();
private: private:
bool UpdateAdditionalMetadata(std::shared_ptr<GameFile>* game_file, bool UpdateAdditionalMetadata(std::shared_ptr<GameFile>* game_file);
const Core::TitleDatabase& title_database);
bool SyncCacheFile(bool save); bool SyncCacheFile(bool save);
void DoState(PointerWrap* p, u64 size = 0); void DoState(PointerWrap* p, u64 size = 0);