From 4b975efa23b87f4dc0be926bc532713488006de0 Mon Sep 17 00:00:00 2001 From: Tyler Wilding Date: Sun, 22 Nov 2020 17:01:27 -0500 Subject: [PATCH] GameDB: Hopefully resolve possible unicode filepath issues GameDB: Big cleanup as a result of review feedback --- pcsx2/GameDatabase.cpp | 97 +++++++++++++++-------------------- pcsx2/GameDatabase.h | 10 ++-- pcsx2/gui/AppGameDatabase.cpp | 26 +++++++--- pcsx2/gui/AppGameDatabase.h | 3 ++ 4 files changed, 67 insertions(+), 69 deletions(-) diff --git a/pcsx2/GameDatabase.cpp b/pcsx2/GameDatabase.cpp index 627a42c7ee..082f8d5c26 100644 --- a/pcsx2/GameDatabase.cpp +++ b/pcsx2/GameDatabase.cpp @@ -19,6 +19,7 @@ #include "fmt/core.h" #include "yaml-cpp/yaml.h" +#include std::string GameDatabaseSchema::GameEntry::memcardFiltersAsString() { @@ -36,59 +37,35 @@ std::string GameDatabaseSchema::GameEntry::memcardFiltersAsString() return filters; } -std::string YamlGameDatabaseImpl::safeGetString(const YAML::Node& n, std::string key, std::string def) +std::vector YamlGameDatabaseImpl::convertMultiLineStringToVector(const std::string multiLineString) { - if (!n[key]) - return def; - return n[key].as(); -} - -int YamlGameDatabaseImpl::safeGetInt(const YAML::Node& n, std::string key, int def) -{ - if (!n[key]) - return def; - return n[key].as(); -} - -std::vector YamlGameDatabaseImpl::safeGetMultilineString(const YAML::Node& n, std::string key, std::vector def) -{ - if (!n[key]) - return def; - std::vector lines; - - std::istringstream stream(safeGetString(n, key)); + std::istringstream stream(multiLineString); std::string line; - while(std::getline(stream, line)) { + while (std::getline(stream, line)) + { lines.push_back(line); } return lines; } -std::vector YamlGameDatabaseImpl::safeGetStringList(const YAML::Node& n, std::string key, std::vector def) -{ - if (!n[key]) - return def; - return n[key].as>(); -} - GameDatabaseSchema::GameEntry YamlGameDatabaseImpl::entryFromYaml(const std::string serial, const YAML::Node& node) { GameDatabaseSchema::GameEntry gameEntry; try { - gameEntry.name = safeGetString(node, "name"); - gameEntry.region = safeGetString(node, "region"); - gameEntry.compat = static_cast(safeGetInt(node, "compat", enum_cast(gameEntry.compat))); - gameEntry.eeRoundMode = static_cast(safeGetInt(node, "eeRoundMode", enum_cast(gameEntry.eeRoundMode))); - gameEntry.vuRoundMode = static_cast(safeGetInt(node, "vuRoundMode", enum_cast(gameEntry.vuRoundMode))); - gameEntry.eeClampMode = static_cast(safeGetInt(node, "eeClampMode", enum_cast(gameEntry.eeClampMode))); - gameEntry.vuClampMode = static_cast(safeGetInt(node, "vuClampMode", enum_cast(gameEntry.vuClampMode))); + gameEntry.name = node["name"].as(""); + gameEntry.region = node["region"].as(""); + gameEntry.compat = static_cast(node["compat"].as(enum_cast(gameEntry.compat))); + gameEntry.eeRoundMode = static_cast(node["eeRoundMode"].as(enum_cast(gameEntry.eeRoundMode))); + gameEntry.vuRoundMode = static_cast(node["vuRoundMode"].as(enum_cast(gameEntry.vuRoundMode))); + gameEntry.eeClampMode = static_cast(node["eeClampMode"].as(enum_cast(gameEntry.eeClampMode))); + gameEntry.vuClampMode = static_cast(node["vuClampMode"].as(enum_cast(gameEntry.vuClampMode))); // Validate game fixes, invalid ones will be dropped! - for (std::string fix : safeGetStringList(node, "gameFixes")) + for (std::string& fix : node["gameFixes"].as>(std::vector())) { bool fixValidated = false; for (GamefixId id = GamefixId_FIRST; id < pxEnumEnd; ++id) @@ -112,10 +89,10 @@ GameDatabaseSchema::GameEntry YamlGameDatabaseImpl::entryFromYaml(const std::str if (YAML::Node speedHacksNode = node["speedHacks"]) { - for (YAML::const_iterator entry = speedHacksNode.begin(); entry != speedHacksNode.end(); entry++) + for (const auto& entry : speedHacksNode) { // Validate speedhacks, invalid ones will be skipped! - std::string speedHack = entry->first.as(); + std::string speedHack = entry.first.as(); // NOTE - currently only 1 speedhack! if (speedHack != "mvuFlagSpeedHack") @@ -124,32 +101,37 @@ GameDatabaseSchema::GameEntry YamlGameDatabaseImpl::entryFromYaml(const std::str continue; } - gameEntry.speedHacks[speedHack] = entry->second.as(); + gameEntry.speedHacks[speedHack] = entry.second.as(); } } - gameEntry.memcardFilters = safeGetStringList(node, "memcardFilters"); + gameEntry.memcardFilters = node["memcardFilters"].as>(std::vector()); if (YAML::Node patches = node["patches"]) { - for (YAML::const_iterator entry = patches.begin(); entry != patches.end(); entry++) + for (const auto& entry : patches) { - std::string crc = entry->first.as(); - YAML::Node patchNode = entry->second; + std::string crc = entry.first.as(); + YAML::Node patchNode = entry.second; GameDatabaseSchema::Patch patchCol; - patchCol.author = safeGetString(patchNode, "author"); - patchCol.patchLines = safeGetMultilineString(patchNode, "content"); + patchCol.author = patchNode["author"].as(""); + patchCol.patchLines = convertMultiLineStringToVector(patchNode["content"].as("")); gameEntry.patches[crc] = patchCol; } } } - catch (YAML::RepresentationException e) + catch (const YAML::RepresentationException& e) { Console.Error(fmt::format("[GameDB] Invalid GameDB syntax detected on serial: '{}'. Error Details - {}", serial, e.msg)); gameEntry.isValid = false; } + catch (const std::exception& e) + { + Console.Error(fmt::format("[GameDB] Unexpected error occurred when reading serial: '{}'. Error Details - {}", serial, e.what())); + gameEntry.isValid = false; + } return gameEntry; } @@ -168,23 +150,28 @@ int YamlGameDatabaseImpl::numGames() return gameDb.size(); } -bool YamlGameDatabaseImpl::initDatabase(const std::string filePath) +bool YamlGameDatabaseImpl::initDatabase(std::ifstream& stream) { try { + if (!stream) + { + Console.Error("[GameDB] Unable to open GameDB file."); + return false; + } // yaml-cpp has memory leak issues if you persist and modify a YAML::Node // convert to a map and throw it away instead! - YAML::Node data = YAML::LoadFile(filePath); - for (YAML::const_iterator entry = data.begin(); entry != data.end(); entry++) + YAML::Node data = YAML::Load(stream); + for (const auto& entry : data) { - // we don't want to throw away the entire GameDB file if a single entry is made, but we do - // want to yell about it so it can be corrected + // we don't want to throw away the entire GameDB file if a single entry is made incorrectly, + // but we do want to yell about it so it can be corrected try { - std::string serial = entry->first.as(); - gameDb[serial] = entryFromYaml(serial, entry->second); + std::string serial = entry.first.as(); + gameDb[serial] = entryFromYaml(serial, entry.second); } - catch (YAML::RepresentationException e) + catch (const YAML::RepresentationException& e) { Console.Error(fmt::format("[GameDB] Invalid GameDB syntax detected. Error Details - {}", e.msg)); } @@ -192,7 +179,7 @@ bool YamlGameDatabaseImpl::initDatabase(const std::string filePath) } catch (const std::exception& e) { - Console.Error(fmt::format("Error occured when initializing GameDB: {}", e.what())); + Console.Error(fmt::format("[GameDB] Error occured when initializing GameDB: {}", e.what())); return false; } diff --git a/pcsx2/GameDatabase.h b/pcsx2/GameDatabase.h index 0f2e24ef98..de2d1db477 100644 --- a/pcsx2/GameDatabase.h +++ b/pcsx2/GameDatabase.h @@ -84,7 +84,7 @@ public: class IGameDatabase { public: - virtual bool initDatabase(const std::string filePath) = 0; + virtual bool initDatabase(std::ifstream& stream) = 0; virtual GameDatabaseSchema::GameEntry findGame(const std::string serial) = 0; virtual int numGames() = 0; }; @@ -92,7 +92,7 @@ public: class YamlGameDatabaseImpl : public IGameDatabase { public: - bool initDatabase(const std::string filePath) override; + bool initDatabase(std::ifstream& stream) override; GameDatabaseSchema::GameEntry findGame(const std::string serial) override; int numGames() override; @@ -100,11 +100,7 @@ private: std::unordered_map gameDb; GameDatabaseSchema::GameEntry entryFromYaml(const std::string serial, const YAML::Node& node); - // TODO - config - move these into a generic library - std::string safeGetString(const YAML::Node& n, std::string key, std::string def = ""); - int safeGetInt(const YAML::Node& n, std::string key, int def = 0); - std::vector safeGetMultilineString(const YAML::Node& n, std::string key, std::vector def = {}); - std::vector safeGetStringList(const YAML::Node& n, std::string key, std::vector def = {}); + std::vector convertMultiLineStringToVector(const std::string multiLineString); }; extern IGameDatabase* AppHost_GetGameDatabase(); diff --git a/pcsx2/gui/AppGameDatabase.cpp b/pcsx2/gui/AppGameDatabase.cpp index 577726c782..0dd54c6af4 100644 --- a/pcsx2/gui/AppGameDatabase.cpp +++ b/pcsx2/gui/AppGameDatabase.cpp @@ -20,10 +20,21 @@ #include #include "fmt/core.h" +#include + +std::ifstream AppGameDatabase::getFileAsStream(const wxString& file) +{ +// TODO - config - refactor with std::filesystem/ghc::filesystem +#ifdef _WIN32 + return std::ifstream(file.wc_str()); +#else + return std::ifstream(file.c_str()); +#endif +} AppGameDatabase& AppGameDatabase::LoadFromFile(const wxString& _file) { - // TODO - config - kill this with fire with std::filesystem + // TODO - config - refactor with std::filesystem/ghc::filesystem wxString file(_file); if (wxFileName(file).IsRelative()) @@ -45,21 +56,22 @@ AppGameDatabase& AppGameDatabase::LoadFromFile(const wxString& _file) if (!wxFileExists(file)) { - Console.Error(L"(GameDB) Database Not Found! [%s]", WX_STR(file)); + Console.Error(L"[GameDB] Database Not Found! [%s]", WX_STR(file)); return *this; } - u64 qpc_Start = GetCPUTicks(); + const u64 qpc_Start = GetCPUTicks(); - if (!this->initDatabase(std::string(file))) + std::ifstream fileStream = getFileAsStream(file); + if (!this->initDatabase(fileStream)) { - Console.Error(L"(GameDB) Database could not be loaded successfully"); + Console.Error(L"[GameDB] Database could not be loaded successfully"); return *this; } - u64 qpc_end = GetCPUTicks(); + const u64 qpc_end = GetCPUTicks(); - Console.WriteLn(fmt::format("(GameDB) {} games on record (loaded in {}ms)", this->numGames(), + Console.WriteLn(fmt::format("[GameDB] {} games on record (loaded in {}ms)", this->numGames(), (u32)(((qpc_end - qpc_Start) * 1000) / GetTickFrequency()))); return *this; diff --git a/pcsx2/gui/AppGameDatabase.h b/pcsx2/gui/AppGameDatabase.h index fe304f606e..78b37d0a0c 100644 --- a/pcsx2/gui/AppGameDatabase.h +++ b/pcsx2/gui/AppGameDatabase.h @@ -33,6 +33,9 @@ public: } AppGameDatabase& LoadFromFile(const wxString& file = Path::Combine(PathDefs::GetProgramDataDir(), wxFileName(L"GameIndex.yaml"))); + +private: + std::ifstream getFileAsStream(const wxString& file); }; static wxString compatToStringWX(GameDatabaseSchema::Compatibility compat)