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:
parent
4c061e9212
commit
29808ae53b
4 changed files with 143 additions and 161 deletions
|
@ -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) {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue