From 263f7b673be21dc0e87b6ce01659ff216bbeaf20 Mon Sep 17 00:00:00 2001 From: Connor McLaughlin Date: Fri, 27 Nov 2020 17:58:06 +1000 Subject: [PATCH] GameList: Use shipped redump database/compatibility settings --- src/duckstation-qt/gamelistsettingswidget.cpp | 6 +- src/duckstation-qt/gamepropertiesdialog.cpp | 2 +- src/frontend-common/game_list.cpp | 190 +++++++++--------- src/frontend-common/game_list.h | 18 +- src/frontend-common/game_settings.cpp | 10 +- src/frontend-common/game_settings.h | 3 +- 6 files changed, 115 insertions(+), 114 deletions(-) diff --git a/src/duckstation-qt/gamelistsettingswidget.cpp b/src/duckstation-qt/gamelistsettingswidget.cpp index 268e2490f..f29d1ea30 100644 --- a/src/duckstation-qt/gamelistsettingswidget.cpp +++ b/src/duckstation-qt/gamelistsettingswidget.cpp @@ -1,5 +1,6 @@ #include "gamelistsettingswidget.h" #include "common/assert.h" +#include "common/file_system.h" #include "common/minizip_helpers.h" #include "common/string_util.h" #include "frontend-common/game_list.h" @@ -144,8 +145,11 @@ void GameListSettingsWidget::onUpdateRedumpDatabaseButtonClicked() return; } - if (downloadRedumpDatabase(QString::fromStdString(m_host_interface->getGameList()->GetDatabaseFilename()))) + if (downloadRedumpDatabase( + m_host_interface->getUserDirectoryRelativePath("database" FS_OSPATH_SEPARATOR_STR "redump.dat"))) + { m_host_interface->refreshGameList(true, true); + } } static bool ExtractRedumpDatabase(const QByteArray& data, const QString& destination_path) diff --git a/src/duckstation-qt/gamepropertiesdialog.cpp b/src/duckstation-qt/gamepropertiesdialog.cpp index b5c096022..752352f85 100644 --- a/src/duckstation-qt/gamepropertiesdialog.cpp +++ b/src/duckstation-qt/gamepropertiesdialog.cpp @@ -421,7 +421,7 @@ void GamePropertiesDialog::populateGameSettings() void GamePropertiesDialog::saveGameSettings() { - m_host_interface->getGameList()->UpdateGameSettings(m_path, m_game_code, m_game_title, m_game_settings, true, true); + m_host_interface->getGameList()->UpdateGameSettings(m_path, m_game_code, m_game_title, m_game_settings, true); m_host_interface->applySettings(true); } diff --git a/src/frontend-common/game_list.cpp b/src/frontend-common/game_list.cpp index d571e84d6..4adf0bb5a 100644 --- a/src/frontend-common/game_list.cpp +++ b/src/frontend-common/game_list.cpp @@ -671,11 +671,6 @@ void GameList::SetSearchDirectoriesFromSettings(SettingsInterface& si) m_search_directories.push_back({std::move(dir), true}); } -bool GameList::IsDatabasePresent() const -{ - return FileSystem::FileExists(m_database_filename.c_str()); -} - void GameList::Refresh(bool invalidate_cache, bool invalidate_database, ProgressCallback* progress /* = nullptr */) { if (!progress) @@ -740,32 +735,33 @@ void GameList::LoadDatabase() return; m_database_load_tried = true; - if (m_database_filename.empty()) - return; - - auto fp = FileSystem::OpenManagedCFile(m_database_filename.c_str(), "rb"); - if (!fp) - return; tinyxml2::XMLDocument doc; - tinyxml2::XMLError error = doc.LoadFile(fp.get()); - if (error != tinyxml2::XML_SUCCESS) { - Log_ErrorPrintf("Failed to parse redump dat '%s': %s", m_database_filename.c_str(), - tinyxml2::XMLDocument::ErrorIDToName(error)); - return; + std::unique_ptr stream = g_host_interface->OpenPackageFile( + "database" FS_OSPATH_SEPARATOR_STR "redump.dat", BYTESTREAM_OPEN_READ | BYTESTREAM_OPEN_STREAMED); + if (stream) + { + const std::string xml(FileSystem::ReadStreamToString(stream.get())); + tinyxml2::XMLError error = doc.Parse(xml.data(), xml.size()); + if (error != tinyxml2::XML_SUCCESS) + { + Log_ErrorPrintf("Failed to parse redump dat: %s", tinyxml2::XMLDocument::ErrorIDToName(error)); + return; + } + } } const tinyxml2::XMLElement* datafile_elem = doc.FirstChildElement("datafile"); if (!datafile_elem) { - Log_ErrorPrintf("Failed to get datafile element in '%s'", m_database_filename.c_str()); + Log_ErrorPrintf("Failed to get datafile element in redump dat"); return; } RedumpDatVisitor visitor(m_database); datafile_elem->Accept(&visitor); - Log_InfoPrintf("Loaded %zu entries from Redump.org database '%s'", m_database.size(), m_database_filename.c_str()); + Log_InfoPrintf("Loaded %zu entries from Redump.org database", m_database.size()); } void GameList::ClearDatabase() @@ -831,8 +827,8 @@ public: auto iter = m_database.find(code); if (iter != m_database.end()) { - Log_ErrorPrintf("Duplicate game code in compatibility list: '%s'", code.c_str()); - return false; + Log_WarningPrintf("Duplicate game code in compatibility list: '%s'", code.c_str()); + m_database.erase(iter); } GameListCompatibilityEntry entry; @@ -862,33 +858,52 @@ void GameList::LoadCompatibilityList() return; m_compatibility_list_load_tried = true; - if (m_compatibility_list_filename.empty()) - return; - auto fp = FileSystem::OpenManagedCFile(m_compatibility_list_filename.c_str(), "rb"); - if (!fp) - return; + // list we ship with + { + std::unique_ptr file = + g_host_interface->OpenPackageFile("database/compatibility.xml", BYTESTREAM_OPEN_READ | BYTESTREAM_OPEN_STREAMED); + if (file) + { + LoadCompatibilityListFromXML(FileSystem::ReadStreamToString(file.get())); + } + else + { + Log_ErrorPrintf("Failed to load compatibility.xml from package"); + } + } + // user's list + if (!m_user_compatibility_list_filename.empty() && FileSystem::FileExists(m_user_compatibility_list_filename.c_str())) + { + std::unique_ptr file = + FileSystem::OpenFile(m_user_compatibility_list_filename.c_str(), BYTESTREAM_OPEN_READ | BYTESTREAM_OPEN_STREAMED); + if (file) + LoadCompatibilityListFromXML(FileSystem::ReadStreamToString(file.get())); + } +} + +bool GameList::LoadCompatibilityListFromXML(const std::string& xml) +{ tinyxml2::XMLDocument doc; - tinyxml2::XMLError error = doc.LoadFile(fp.get()); + tinyxml2::XMLError error = doc.Parse(xml.c_str(), xml.size()); if (error != tinyxml2::XML_SUCCESS) { - Log_ErrorPrintf("Failed to parse compatibility list '%s': %s", m_compatibility_list_filename.c_str(), - tinyxml2::XMLDocument::ErrorIDToName(error)); - return; + Log_ErrorPrintf("Failed to parse compatibility list: %s", tinyxml2::XMLDocument::ErrorIDToName(error)); + return false; } const tinyxml2::XMLElement* datafile_elem = doc.FirstChildElement("compatibility-list"); if (!datafile_elem) { - Log_ErrorPrintf("Failed to get compatibility-list element in '%s'", m_compatibility_list_filename.c_str()); - return; + Log_ErrorPrintf("Failed to get compatibility-list element"); + return false; } CompatibilityListVisitor visitor(m_compatibility_list); datafile_elem->Accept(&visitor); - Log_InfoPrintf("Loaded %zu entries from compatibility list '%s'", m_compatibility_list.size(), - m_compatibility_list_filename.c_str()); + Log_InfoPrintf("Loaded %zu entries from compatibility list", m_compatibility_list.size()); + return true; } static void InitElementForCompatibilityEntry(tinyxml2::XMLDocument* doc, tinyxml2::XMLElement* entry_elem, @@ -965,59 +980,36 @@ static void InitElementForCompatibilityEntry(tinyxml2::XMLDocument* doc, tinyxml } } -bool GameList::SaveCompatibilityDatabase() -{ - if (m_compatibility_list_filename.empty()) - return false; - - tinyxml2::XMLDocument doc; - tinyxml2::XMLElement* root_elem = doc.NewElement("compatibility-list"); - doc.InsertEndChild(root_elem); - - for (const auto& it : m_compatibility_list) - { - const GameListCompatibilityEntry* entry = &it.second; - tinyxml2::XMLElement* entry_elem = doc.NewElement("entry"); - root_elem->InsertEndChild(entry_elem); - InitElementForCompatibilityEntry(&doc, entry_elem, entry); - } - - tinyxml2::XMLError error = doc.SaveFile(m_compatibility_list_filename.c_str()); - if (error != tinyxml2::XML_SUCCESS) - { - Log_ErrorPrintf("Failed to save compatibility list '%s': %s", m_compatibility_list_filename.c_str(), - tinyxml2::XMLDocument::ErrorIDToName(error)); - return false; - } - - Log_InfoPrintf("Saved %zu entries to compatibility list '%s'", m_compatibility_list.size(), - m_compatibility_list_filename.c_str()); - return true; -} - bool GameList::SaveCompatibilityDatabaseForEntry(const GameListCompatibilityEntry* entry) { - if (m_compatibility_list_filename.empty()) + if (m_user_compatibility_list_filename.empty()) return false; - auto fp = FileSystem::OpenManagedCFile(m_compatibility_list_filename.c_str(), "rb"); - if (!fp) - return SaveCompatibilityDatabase(); - tinyxml2::XMLDocument doc; - tinyxml2::XMLError error = doc.LoadFile(fp.get()); - if (error != tinyxml2::XML_SUCCESS) + tinyxml2::XMLError error; + tinyxml2::XMLElement* root_elem; + auto fp = FileSystem::OpenManagedCFile(m_user_compatibility_list_filename.c_str(), "rb"); + if (fp) { - Log_ErrorPrintf("Failed to parse compatibility list '%s': %s", m_compatibility_list_filename.c_str(), - tinyxml2::XMLDocument::ErrorIDToName(error)); - return false; - } + error = doc.LoadFile(fp.get()); + if (error != tinyxml2::XML_SUCCESS) + { + Log_ErrorPrintf("Failed to parse compatibility list '%s': %s", m_user_compatibility_list_filename.c_str(), + tinyxml2::XMLDocument::ErrorIDToName(error)); + return false; + } - tinyxml2::XMLElement* root_elem = doc.FirstChildElement("compatibility-list"); - if (!root_elem) + root_elem = doc.FirstChildElement("compatibility-list"); + if (!root_elem) + { + Log_ErrorPrintf("Failed to get compatibility-list element in '%s'", m_user_compatibility_list_filename.c_str()); + return false; + } + } + else { - Log_ErrorPrintf("Failed to get compatibility-list element in '%s'", m_compatibility_list_filename.c_str()); - return false; + root_elem = doc.NewElement("compatibility-list"); + doc.InsertEndChild(root_elem); } tinyxml2::XMLElement* current_entry_elem = root_elem->FirstChildElement(); @@ -1043,19 +1035,22 @@ bool GameList::SaveCompatibilityDatabaseForEntry(const GameListCompatibilityEntr } fp.reset(); - fp = FileSystem::OpenManagedCFile(m_compatibility_list_filename.c_str(), "wb"); + fp = FileSystem::OpenManagedCFile(m_user_compatibility_list_filename.c_str(), "wb"); if (!fp) - return SaveCompatibilityDatabase(); + { + Log_ErrorPrintf("Failed to open file '%s'", m_user_compatibility_list_filename.c_str()); + return false; + } error = doc.SaveFile(fp.get()); if (error != tinyxml2::XML_SUCCESS) { - Log_ErrorPrintf("Failed to update compatibility list '%s': %s", m_compatibility_list_filename.c_str(), + Log_ErrorPrintf("Failed to update compatibility list '%s': %s", m_user_compatibility_list_filename.c_str(), tinyxml2::XMLDocument::ErrorIDToName(error)); return false; } - Log_InfoPrintf("Updated compatibility list '%s'", m_compatibility_list_filename.c_str()); + Log_InfoPrintf("Updated compatibility list '%s'", m_user_compatibility_list_filename.c_str()); return true; } @@ -1082,10 +1077,28 @@ void GameList::LoadGameSettings() m_game_settings_load_tried = true; - if (!m_game_settings_filename.empty() && FileSystem::FileExists(m_user_game_settings_filename.c_str())) - m_game_settings.Load(m_game_settings_filename.c_str()); + // list we ship with + { + std::unique_ptr file = + g_host_interface->OpenPackageFile("database/gamesettings.ini", BYTESTREAM_OPEN_READ | BYTESTREAM_OPEN_STREAMED); + if (file) + { + m_game_settings.Load(FileSystem::ReadStreamToString(file.get())); + } + else + { + Log_ErrorPrintf("Failed to load compatibility.xml from package"); + } + } + + // user's list if (!m_user_game_settings_filename.empty() && FileSystem::FileExists(m_user_game_settings_filename.c_str())) - m_game_settings.Load(m_user_game_settings_filename.c_str()); + { + std::unique_ptr file = + FileSystem::OpenFile(m_user_game_settings_filename.c_str(), BYTESTREAM_OPEN_READ | BYTESTREAM_OPEN_STREAMED); + if (file) + m_game_settings.Load(FileSystem::ReadStreamToString(file.get())); + } } const GameSettings::Entry* GameList::GetGameSettings(const std::string& filename, const std::string& game_code) @@ -1102,7 +1115,7 @@ const GameSettings::Entry* GameList::GetGameSettings(const std::string& filename void GameList::UpdateGameSettings(const std::string& filename, const std::string& game_code, const std::string& game_title, const GameSettings::Entry& new_entry, - bool save_to_list /* = true */, bool save_to_user /* = true */) + bool save_to_list /* = true */) { GameListEntry* entry = GetMutableEntryForPath(filename.c_str()); if (entry) @@ -1112,10 +1125,7 @@ void GameList::UpdateGameSettings(const std::string& filename, const std::string } if (save_to_list) - { - m_game_settings.SetEntry(game_code, game_title, new_entry, - save_to_user ? m_user_game_settings_filename.c_str() : m_game_settings_filename.c_str()); - } + m_game_settings.SetEntry(game_code, game_title, new_entry, m_user_game_settings_filename.c_str()); } std::string GameList::GetCoverImagePathForEntry(const GameListEntry* entry) diff --git a/src/frontend-common/game_list.h b/src/frontend-common/game_list.h index 428aa27f5..ce5b48d8a 100644 --- a/src/frontend-common/game_list.h +++ b/src/frontend-common/game_list.h @@ -84,19 +84,11 @@ public: const GameListDatabaseEntry* GetDatabaseEntryForCode(const std::string& code) const; const GameListCompatibilityEntry* GetCompatibilityEntryForCode(const std::string& code) const; - const std::string& GetCacheFilename() const { return m_cache_filename; } - const std::string& GetDatabaseFilename() const { return m_database_filename; } - const std::string& GetCompatibilityFilename() const { return m_database_filename; } - void SetCacheFilename(std::string filename) { m_cache_filename = std::move(filename); } - void SetDatabaseFilename(std::string filename) { m_database_filename = std::move(filename); } - void SetCompatibilityFilename(std::string filename) { m_compatibility_list_filename = std::move(filename); } - void SetGameSettingsFilename(std::string filename) { m_game_settings_filename = std::move(filename); } + void SetUserCompatibilityListFilename(std::string filename) { m_user_compatibility_list_filename = std::move(filename); } void SetUserGameSettingsFilename(std::string filename) { m_user_game_settings_filename = std::move(filename); } void SetSearchDirectoriesFromSettings(SettingsInterface& si); - bool IsDatabasePresent() const; - void AddDirectory(std::string path, bool recursive); void Refresh(bool invalidate_cache, bool invalidate_database, ProgressCallback* progress = nullptr); @@ -106,7 +98,7 @@ public: const GameSettings::Entry* GetGameSettings(const std::string& filename, const std::string& game_code); void UpdateGameSettings(const std::string& filename, const std::string& game_code, const std::string& game_title, - const GameSettings::Entry& new_entry, bool save_to_list = true, bool save_to_user = true); + const GameSettings::Entry& new_entry, bool save_to_list = true); std::string GetCoverImagePathForEntry(const GameListEntry* entry); std::string GetNewCoverImagePathForEntry(const GameListEntry* entry, const char* new_filename); @@ -153,7 +145,7 @@ private: void ClearDatabase(); void LoadCompatibilityList(); - bool SaveCompatibilityDatabase(); + bool LoadCompatibilityListFromXML(const std::string& xml); bool SaveCompatibilityDatabaseForEntry(const GameListCompatibilityEntry* entry); void LoadGameSettings(); @@ -167,9 +159,7 @@ private: std::vector m_search_directories; std::string m_cache_filename; - std::string m_database_filename; - std::string m_compatibility_list_filename; - std::string m_game_settings_filename; + std::string m_user_compatibility_list_filename; std::string m_user_game_settings_filename; bool m_database_load_tried = false; bool m_compatibility_list_load_tried = false; diff --git a/src/frontend-common/game_settings.cpp b/src/frontend-common/game_settings.cpp index 15151e231..30bf82e37 100644 --- a/src/frontend-common/game_settings.cpp +++ b/src/frontend-common/game_settings.cpp @@ -386,14 +386,10 @@ const GameSettings::Entry* Database::GetEntry(const std::string& code) const return (it != m_entries.end()) ? &it->second : nullptr; } -bool Database::Load(const char* path) +bool Database::Load(const std::string_view& ini_data) { - auto fp = FileSystem::OpenManagedCFile(path, "rb"); - if (!fp) - return false; - CSimpleIniA ini; - SI_Error err = ini.LoadFile(fp.get()); + SI_Error err = ini.LoadData(ini_data.data(), ini_data.size()); if (err != SI_OK) { Log_ErrorPrintf("Failed to parse game settings ini: %d", static_cast(err)); @@ -417,7 +413,7 @@ bool Database::Load(const char* path) m_entries.emplace(std::move(code), std::move(entry)); } - Log_InfoPrintf("Loaded settings for %zu games from '%s'", sections.size(), path); + Log_InfoPrintf("Loaded settings for %zu games", sections.size()); return true; } diff --git a/src/frontend-common/game_settings.h b/src/frontend-common/game_settings.h index e2c64b200..dab77367f 100644 --- a/src/frontend-common/game_settings.h +++ b/src/frontend-common/game_settings.h @@ -3,6 +3,7 @@ #include #include #include +#include #include class ByteStream; @@ -93,7 +94,7 @@ public: const Entry* GetEntry(const std::string& code) const; void SetEntry(const std::string& code, const std::string& name, const Entry& entry, const char* save_path); - bool Load(const char* path); + bool Load(const std::string_view& ini_data); private: std::unordered_map m_entries;