GameDB: Hopefully resolve possible unicode filepath issues

GameDB: Big cleanup as a result of review feedback
This commit is contained in:
Tyler Wilding 2020-11-22 17:01:27 -05:00 committed by refractionpcsx2
parent e90046d8a2
commit 4b975efa23
4 changed files with 67 additions and 69 deletions

View File

@ -19,6 +19,7 @@
#include "fmt/core.h"
#include "yaml-cpp/yaml.h"
#include <fstream>
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<std::string> YamlGameDatabaseImpl::convertMultiLineStringToVector(const std::string multiLineString)
{
if (!n[key])
return def;
return n[key].as<std::string>();
}
int YamlGameDatabaseImpl::safeGetInt(const YAML::Node& n, std::string key, int def)
{
if (!n[key])
return def;
return n[key].as<int>();
}
std::vector<std::string> YamlGameDatabaseImpl::safeGetMultilineString(const YAML::Node& n, std::string key, std::vector<std::string> def)
{
if (!n[key])
return def;
std::vector<std::string> 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<std::string> YamlGameDatabaseImpl::safeGetStringList(const YAML::Node& n, std::string key, std::vector<std::string> def)
{
if (!n[key])
return def;
return n[key].as<std::vector<std::string>>();
}
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<GameDatabaseSchema::Compatibility>(safeGetInt(node, "compat", enum_cast(gameEntry.compat)));
gameEntry.eeRoundMode = static_cast<GameDatabaseSchema::RoundMode>(safeGetInt(node, "eeRoundMode", enum_cast(gameEntry.eeRoundMode)));
gameEntry.vuRoundMode = static_cast<GameDatabaseSchema::RoundMode>(safeGetInt(node, "vuRoundMode", enum_cast(gameEntry.vuRoundMode)));
gameEntry.eeClampMode = static_cast<GameDatabaseSchema::ClampMode>(safeGetInt(node, "eeClampMode", enum_cast(gameEntry.eeClampMode)));
gameEntry.vuClampMode = static_cast<GameDatabaseSchema::ClampMode>(safeGetInt(node, "vuClampMode", enum_cast(gameEntry.vuClampMode)));
gameEntry.name = node["name"].as<std::string>("");
gameEntry.region = node["region"].as<std::string>("");
gameEntry.compat = static_cast<GameDatabaseSchema::Compatibility>(node["compat"].as<int>(enum_cast(gameEntry.compat)));
gameEntry.eeRoundMode = static_cast<GameDatabaseSchema::RoundMode>(node["eeRoundMode"].as<int>(enum_cast(gameEntry.eeRoundMode)));
gameEntry.vuRoundMode = static_cast<GameDatabaseSchema::RoundMode>(node["vuRoundMode"].as<int>(enum_cast(gameEntry.vuRoundMode)));
gameEntry.eeClampMode = static_cast<GameDatabaseSchema::ClampMode>(node["eeClampMode"].as<int>(enum_cast(gameEntry.eeClampMode)));
gameEntry.vuClampMode = static_cast<GameDatabaseSchema::ClampMode>(node["vuClampMode"].as<int>(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<std::string>>(std::vector<std::string>()))
{
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>();
std::string speedHack = entry.first.as<std::string>();
// 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<int>();
gameEntry.speedHacks[speedHack] = entry.second.as<int>();
}
}
gameEntry.memcardFilters = safeGetStringList(node, "memcardFilters");
gameEntry.memcardFilters = node["memcardFilters"].as<std::vector<std::string>>(std::vector<std::string>());
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<std::string>();
YAML::Node patchNode = entry->second;
std::string crc = entry.first.as<std::string>();
YAML::Node patchNode = entry.second;
GameDatabaseSchema::Patch patchCol;
patchCol.author = safeGetString(patchNode, "author");
patchCol.patchLines = safeGetMultilineString(patchNode, "content");
patchCol.author = patchNode["author"].as<std::string>("");
patchCol.patchLines = convertMultiLineStringToVector(patchNode["content"].as<std::string>(""));
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<std::string>();
gameDb[serial] = entryFromYaml(serial, entry->second);
std::string serial = entry.first.as<std::string>();
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;
}

View File

@ -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<std::string, GameDatabaseSchema::GameEntry> 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<std::string> safeGetMultilineString(const YAML::Node& n, std::string key, std::vector<std::string> def = {});
std::vector<std::string> safeGetStringList(const YAML::Node& n, std::string key, std::vector<std::string> def = {});
std::vector<std::string> convertMultiLineStringToVector(const std::string multiLineString);
};
extern IGameDatabase* AppHost_GetGameDatabase();

View File

@ -20,10 +20,21 @@
#include <wx/stdpaths.h>
#include "fmt/core.h"
#include <fstream>
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;

View File

@ -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)