diff --git a/Core/CwCheat.cpp b/Core/CwCheat.cpp index dc53115218..6b04c46b82 100644 --- a/Core/CwCheat.cpp +++ b/Core/CwCheat.cpp @@ -43,75 +43,37 @@ using namespace SceCtrl; void hleCheat(u64 userdata, int cyclesLate); -static inline std::string TrimString(const std::string &s) { +static inline std::string TrimString(std::string_view s) { auto wsfront = std::find_if_not(s.begin(), s.end(), [](int c) { // isspace() expects 0 - 255, so convert any sign-extended value. - return std::isspace(c & 0xFF); + return std::isspace((u8)c); }); auto wsback = std::find_if_not(s.rbegin(), s.rend(), [](int c){ - return std::isspace(c & 0xFF); + return std::isspace((u8)c); }).base(); return wsback > wsfront ? std::string(wsfront, wsback) : std::string(); } -class CheatFileParser { -public: - CheatFileParser(const Path &filename, const std::string &gameID = "") { - fp_ = File::OpenCFile(filename, "rt"); - validGameID_ = ReplaceAll(gameID, "-", ""); - } - ~CheatFileParser() { - if (fp_) - fclose(fp_); - } +CheatFileParser::CheatFileParser(const Path &filename, std::string_view gameID) { + fp_ = File::OpenCFile(filename, "rt"); + validGameID_ = ReplaceAll(gameID, "-", ""); +} - bool Parse(); - - std::vector GetErrors() const { - return errors_; - } - - std::vector GetCheats() const { - return cheats_; - } - - std::vector GetFileInfo() const { - return cheatInfo_; - } - -protected: - void Flush(); - void FlushCheatInfo(); - void AddError(const std::string &msg); - void ParseLine(const std::string &line); - void ParseDataLine(const std::string &line); - bool ValidateGameID(const std::string &gameID); - - FILE *fp_ = nullptr; - std::string validGameID_; - - int line_ = 0; - int games_ = 0; - std::vector errors_; - std::vector cheatInfo_; - std::vector cheats_; - std::vector pendingLines_; - CheatFileInfo lastCheatInfo_; - bool gameEnabled_ = true; - bool gameRiskyEnabled_ = false; - bool cheatEnabled_ = false; -}; +CheatFileParser::~CheatFileParser() { + if (fp_) + fclose(fp_); +} bool CheatFileParser::Parse() { // Ugh, using a member variable as loop counter is bad. - for (line_ = 1; fp_ && !feof(fp_); ++line_) { + for (int lineNumber = 1; fp_ && !feof(fp_); ++lineNumber) { char temp[2048]; char *tempLine = fgets(temp, sizeof(temp), fp_); if (!tempLine) continue; // Detect UTF-8 BOM sequence, and ignore it. - if (line_ == 1 && memcmp(tempLine, "\xEF\xBB\xBF", 3) == 0) + if (lineNumber == 1 && memcmp(tempLine, "\xEF\xBB\xBF", 3) == 0) tempLine += 3; std::string line = TrimString(tempLine); @@ -119,13 +81,13 @@ bool CheatFileParser::Parse() { // and a minimum of 1 displayable character in cheat name string "_C0 1" // which both equal to 5 characters. if (line.length() >= 5 && line[0] == '_') { - ParseLine(line); + ParseLine(line, lineNumber); } else if (line.length() >= 2 && line[0] == '/' && line[1] == '/') { // Comment, ignore. } else if (line.length() >= 1 && line[0] == '#') { // Comment, ignore. } else if (line.length() > 0) { - errors_.push_back(StringFromFormat("Unrecognized content on line %d: expecting _", line_)); + errors_.push_back(StringFromFormat("Unrecognized content on line %d: expecting _", lineNumber)); } } @@ -149,11 +111,11 @@ void CheatFileParser::FlushCheatInfo() { } } -void CheatFileParser::AddError(const std::string &err) { - errors_.push_back(StringFromFormat("Error on line %d: %s", line_, err.c_str())); +void CheatFileParser::AddError(const std::string &err, int lineNumber) { + errors_.push_back(StringFromFormat("Error on line %d: %s", lineNumber, err.c_str())); } -void CheatFileParser::ParseLine(const std::string &line) { +void CheatFileParser::ParseLine(const std::string &line, int lineNumber) { switch (line[1]) { case 'S': // Disc ID, validate (for multi-disc cheat files)? @@ -193,13 +155,13 @@ void CheatFileParser::ParseLine(const std::string &line) { // Cheat name and activation status. if (line.length() >= 3 && line[2] >= '1' && line[2] <= '9') { - lastCheatInfo_ = { line_, line.length() >= 5 ? line.substr(4) : "", true }; + lastCheatInfo_ = { lineNumber, line.length() >= 5 ? line.substr(4) : "", true }; cheatEnabled_ = true; } else if (line.length() >= 3 && line[2] == '0') { - lastCheatInfo_ = { line_, line.length() >= 5 ? line.substr(4) : "", false }; + lastCheatInfo_ = { lineNumber, line.length() >= 5 ? line.substr(4) : "", false }; cheatEnabled_ = false; } else { - AddError("could not parse cheat name line"); + AddError("could not parse cheat name line", lineNumber); cheatEnabled_ = false; return; } @@ -207,24 +169,25 @@ void CheatFileParser::ParseLine(const std::string &line) { case 'L': // CwCheat data line. - ParseDataLine(line.substr(2)); + ParseDataLine(line.substr(2), lineNumber); return; case 'M': // TempAR data line. - AddError("TempAR codes not supported"); + AddError("TempAR codes not supported", lineNumber); return; default: - AddError("unknown line type"); + AddError("unknown line type", lineNumber); return; } } -void CheatFileParser::ParseDataLine(const std::string &line) { +void CheatFileParser::ParseDataLine(const std::string &line, int lineNumber) { if (!gameEnabled_) { return; } + if (!cheatEnabled_) { FlushCheatInfo(); return; @@ -234,15 +197,15 @@ void CheatFileParser::ParseDataLine(const std::string &line) { int len = 0; if (sscanf(line.c_str(), "%x %x %n", &cheatLine.part1, &cheatLine.part2, &len) == 2) { if ((size_t)len < line.length()) { - AddError("junk after line data"); + AddError("junk after line data", lineNumber); } pendingLines_.push_back(cheatLine); } else { - AddError("expecting two values"); + AddError("expecting two values", lineNumber); } } -bool CheatFileParser::ValidateGameID(const std::string &gameID) { +bool CheatFileParser::ValidateGameID(std::string_view gameID) { return validGameID_.empty() || ReplaceAll(TrimString(gameID), "-", "") == validGameID_; } @@ -359,14 +322,14 @@ void hleCheat(u64 userdata, int cyclesLate) { if (!cheatEngine || !cheatsEnabled) return; - if (g_Config.bReloadCheats) { //Checks if the "reload cheats" button has been pressed. + if (g_Config.bReloadCheats) { // Checks if the "reload cheats" button has been pressed. cheatEngine->ParseCheats(); g_Config.bReloadCheats = false; } cheatEngine->Run(); } -CWCheatEngine::CWCheatEngine(const std::string &gameID) : gameID_(gameID) { +CWCheatEngine::CWCheatEngine(std::string_view gameID) : gameID_(gameID) { filename_ = GetSysDirectory(DIRECTORY_CHEATS) / (gameID_ + ".ini"); } @@ -390,7 +353,10 @@ void CWCheatEngine::ParseCheats() { CheatFileParser parser(filename_, gameID_); parser.Parse(); - // TODO: Report errors. + // TODO: Report errors in a user-visible way. + for (auto &error : parser.GetErrors()) { + ERROR_LOG(Log::System, "CwCheat error: %s", error.c_str()); + } cheats_ = parser.GetCheats(); } diff --git a/Core/CwCheat.h b/Core/CwCheat.h index 024f473e9a..e697177f17 100644 --- a/Core/CwCheat.h +++ b/Core/CwCheat.h @@ -3,6 +3,7 @@ #pragma once #include +#include #include #include #include @@ -39,7 +40,7 @@ struct CheatOperation; class CWCheatEngine { public: - CWCheatEngine(const std::string &gameID); + CWCheatEngine(std::string_view gameID); std::vector FileInfo() const; void ParseCheats(); void CreateCheatFile(); @@ -69,3 +70,36 @@ private: std::string gameID_; Path filename_; }; + +class CheatFileParser { +public: + CheatFileParser(const Path &filename, std::string_view gameID = ""); + ~CheatFileParser(); + + bool Parse(); + + const std::vector &GetErrors() const { return errors_; } + const std::vector &GetCheats() const { return cheats_; } + const std::vector &GetFileInfo() const { return cheatInfo_; } + +protected: + void Flush(); + void FlushCheatInfo(); + void AddError(const std::string &msg, int lineNumber); + void ParseLine(const std::string &line, int lineNumber); + void ParseDataLine(const std::string &line, int lineNumber); + bool ValidateGameID(std::string_view gameID); + + FILE *fp_ = nullptr; + std::string validGameID_; + + int games_ = 0; + std::vector errors_; + std::vector cheatInfo_; + std::vector cheats_; + std::vector pendingLines_; + CheatFileInfo lastCheatInfo_; + bool gameEnabled_ = true; + bool gameRiskyEnabled_ = false; + bool cheatEnabled_ = false; +};