UI: Update cheat file more safely.

This uses the common parsing logic (which supports multiple games in a
single cheat file), and prevents reverting the file if edited outside.
This commit is contained in:
Unknown W. Brackets 2020-04-11 12:43:55 -07:00
parent 4c061e9212
commit 29808ae53b
4 changed files with 143 additions and 161 deletions

View file

@ -61,8 +61,13 @@ public:
return cheats_;
}
std::vector<CheatFileInfo> 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, CheatCodeFormat format);
@ -74,9 +79,11 @@ protected:
int line_ = 0;
int games_ = 0;
std::vector<std::string> errors_;
std::vector<CheatFileInfo> cheatInfo_;
std::vector<CheatCode> cheats_;
std::vector<CheatLine> pendingLines_;
CheatCodeFormat codeFormat_ = CheatCodeFormat::UNDEFINED;
CheatFileInfo lastCheatInfo_;
bool gameEnabled_ = true;
bool gameRiskyEnabled_ = false;
bool cheatEnabled_ = false;
@ -88,9 +95,7 @@ bool CheatFileParser::Parse() {
getline(file_, line, '\n');
line = TrimString(line);
// Minimum length is set to 5 just to match GetCodesList() function
// which discards anything shorter when called anyway.
// It's decided from shortest possible _ lines name of the game "_G N+"
// Minimum length 5 is shortest possible _ lines name of the game "_G N+"
// 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] == '_') {
@ -111,12 +116,20 @@ bool CheatFileParser::Parse() {
void CheatFileParser::Flush() {
if (!pendingLines_.empty()) {
FlushCheatInfo();
cheats_.push_back({ codeFormat_, pendingLines_ });
pendingLines_.clear();
}
codeFormat_ = CheatCodeFormat::UNDEFINED;
}
void CheatFileParser::FlushCheatInfo() {
if (lastCheatInfo_.lineNum != 0) {
cheatInfo_.push_back(lastCheatInfo_);
lastCheatInfo_ = { 0 };
}
}
void CheatFileParser::AddError(const std::string &err) {
errors_.push_back(StringFromFormat("Error on line %d: %s", line_, err.c_str()));
}
@ -132,6 +145,7 @@ void CheatFileParser::ParseLine(const std::string &line) {
if (gameRiskyEnabled_) {
// We found the right one, so let's not use this risky stuff.
cheats_.clear();
cheatInfo_.clear();
gameRiskyEnabled_ = false;
}
gameEnabled_ = true;
@ -144,6 +158,7 @@ void CheatFileParser::ParseLine(const std::string &line) {
if (gameRiskyEnabled_) {
// There are multiple games here, kill the risky stuff.
cheats_.clear();
cheatInfo_.clear();
gameRiskyEnabled_ = false;
}
gameEnabled_ = false;
@ -155,18 +170,20 @@ void CheatFileParser::ParseLine(const std::string &line) {
return;
case 'C':
Flush();
// Cheat name and activation status.
if (line.length() >= 3 && line[2] >= '1' && line[2] <= '9') {
lastCheatInfo_ = { line_, 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 };
cheatEnabled_ = false;
} else {
AddError("could not parse cheat name line");
cheatEnabled_ = false;
return;
}
Flush();
return;
case 'L':
@ -190,11 +207,16 @@ void CheatFileParser::ParseDataLine(const std::string &line, CheatCodeFormat for
codeFormat_ = format;
} else if (codeFormat_ != format) {
AddError("mixed code format (cwcheat/tempar)");
lastCheatInfo_ = { 0 };
pendingLines_.clear();
cheatEnabled_ = false;
}
if (!cheatEnabled_ || !gameEnabled_) {
if (!gameEnabled_) {
return;
}
if (!cheatEnabled_) {
FlushCheatInfo();
return;
}
@ -361,37 +383,11 @@ u32 CWCheatEngine::GetAddress(u32 value) {
return address;
}
std::vector<std::string> CWCheatEngine::GetCodesList() {
// Reads the entire cheat list from the appropriate .ini.
std::vector<std::string> codesList;
#if defined(_WIN32) && !defined(__MINGW32__)
std::ifstream list(ConvertUTF8ToWString(activeCheatFile));
#else
std::ifstream list(activeCheatFile.c_str());
#endif
while (list && !list.eof()) {
std::string line;
getline(list, line, '\n');
std::vector<CheatFileInfo> CWCheatEngine::FileInfo() {
CheatFileParser parser(activeCheatFile, gameTitle);
bool validCheatLine = false;
// This function is called by cheat menu(UI) which doesn't support empty names
// minimum 1 non space character is required starting from 5 position.
// It also goes through other "_" lines, but they all have to meet this requirement anyway
// so we don't have to specify any syntax checks here that are made by cheat engine.
if (line.length() >= 5 && line[0] == '_') {
for (size_t i = 4; i < line.length(); i++) {
if (line[i] != ' ') {
validCheatLine = true;
break;
}
}
}
// Any lines not passing this check are discarded when we save changes to the cheat ini file
if (validCheatLine || (line.length() >= 2 && line[0] == '/' && line[1] == '/') || (line.length() >= 1 && line[0] == '#')) {
codesList.push_back(TrimString(line));
}
}
return codesList;
parser.Parse();
return parser.GetFileInfo();
}
void CWCheatEngine::InvalidateICache(u32 addr, int size) {