From b90008aadb116d1d35bf13cf866dc66292b21273 Mon Sep 17 00:00:00 2001 From: JosJuice Date: Thu, 5 Aug 2021 19:05:12 +0200 Subject: [PATCH] Split out code for serializing/deserializing cheat lines --- Source/Core/Core/ActionReplay.cpp | 76 ++++++++------- Source/Core/Core/ActionReplay.h | 7 ++ Source/Core/Core/GeckoCodeConfig.cpp | 26 ++++- Source/Core/Core/GeckoCodeConfig.h | 4 + Source/Core/Core/PatchEngine.cpp | 96 ++++++++++--------- Source/Core/Core/PatchEngine.h | 5 + .../Core/DolphinQt/Config/CheatCodeEditor.cpp | 84 ++++------------ .../Core/DolphinQt/Config/GeckoCodeWidget.cpp | 6 +- 8 files changed, 149 insertions(+), 155 deletions(-) diff --git a/Source/Core/Core/ActionReplay.cpp b/Source/Core/Core/ActionReplay.cpp index 260f3fe232..a3ab233fa3 100644 --- a/Source/Core/Core/ActionReplay.cpp +++ b/Source/Core/Core/ActionReplay.cpp @@ -26,6 +26,7 @@ #include #include #include +#include #include #include @@ -215,42 +216,14 @@ std::vector LoadCodes(const IniFile& global_ini, const IniFile& local_in } else { - std::vector pieces = SplitString(line, ' '); + const auto parse_result = DeserializeLine(line); - // Check if the AR code is decrypted - if (pieces.size() == 2 && pieces[0].size() == 8 && pieces[1].size() == 8) - { - AREntry op; - bool success_addr = TryParse(pieces[0], &op.cmd_addr, 16); - bool success_val = TryParse(pieces[1], &op.value, 16); - - if (success_addr && success_val) - { - current_code.ops.push_back(op); - } - else - { - PanicAlertFmtT("Action Replay Error: invalid AR code line: {0}", line); - - if (!success_addr) - PanicAlertFmtT("The address is invalid"); - - if (!success_val) - PanicAlertFmtT("The value is invalid"); - } - } + if (std::holds_alternative(parse_result)) + current_code.ops.push_back(std::get(parse_result)); + else if (std::holds_alternative(parse_result)) + encrypted_lines.emplace_back(std::get(parse_result)); else - { - pieces = SplitString(line, '-'); - if (pieces.size() == 3 && pieces[0].size() == 4 && pieces[1].size() == 4 && - pieces[2].size() == 5) - { - // Encrypted AR code - // Decryption is done in "blocks", so we must push blocks into a vector, - // then send to decrypt when a new block is encountered, or if it's the last block. - encrypted_lines.emplace_back(pieces[0] + pieces[1] + pieces[2]); - } - } + PanicAlertFmtT("Action Replay Error: invalid AR code line: {0}", line); } } @@ -293,7 +266,7 @@ void SaveCodes(IniFile* local_ini, const std::vector& codes) lines.emplace_back('$' + code.name); for (const ActionReplay::AREntry& op : code.ops) { - lines.emplace_back(fmt::format("{:08X} {:08X}", op.cmd_addr, op.value)); + lines.emplace_back(SerializeLine(op)); } } } @@ -303,6 +276,39 @@ void SaveCodes(IniFile* local_ini, const std::vector& codes) local_ini->SetLines("ActionReplay", lines); } +std::variant DeserializeLine(const std::string& line) +{ + std::vector pieces = SplitString(line, ' '); + + // Decrypted AR code + if (pieces.size() == 2 && pieces[0].size() == 8 && pieces[1].size() == 8) + { + AREntry op; + bool success_addr = TryParse(pieces[0], &op.cmd_addr, 16); + bool success_val = TryParse(pieces[1], &op.value, 16); + + if (success_addr && success_val) + return op; + } + + // Encrypted AR code + pieces = SplitString(line, '-'); + if (pieces.size() == 3 && pieces[0].size() == 4 && pieces[1].size() == 4 && pieces[2].size() == 5) + { + // Decryption is done in "blocks", so we can't decrypt right away. Instead we push blocks into + // a vector, then send to decrypt when a new block is encountered, or if it's the last block. + return pieces[0] + pieces[1] + pieces[2]; + } + + // Parsing failed + return std::monostate{}; +} + +std::string SerializeLine(const AREntry& op) +{ + return fmt::format("{:08X} {:08X}", op.cmd_addr, op.value); +} + static void VLogInfo(std::string_view format, fmt::format_args args) { if (s_disable_logging) diff --git a/Source/Core/Core/ActionReplay.h b/Source/Core/Core/ActionReplay.h index 41c1d591df..96c4127d10 100644 --- a/Source/Core/Core/ActionReplay.h +++ b/Source/Core/Core/ActionReplay.h @@ -4,7 +4,10 @@ #pragma once #include +#include +#include #include + #include "Common/CommonTypes.h" class IniFile; @@ -44,6 +47,10 @@ void LoadAndApplyCodes(const IniFile& global_ini, const IniFile& local_ini); std::vector LoadCodes(const IniFile& global_ini, const IniFile& local_ini); void SaveCodes(IniFile* local_ini, const std::vector& codes); +using EncryptedLine = std::string; +std::variant DeserializeLine(const std::string& line); +std::string SerializeLine(const AREntry& op); + void EnableSelfLogging(bool enable); std::vector GetSelfLog(); void ClearSelfLog(); diff --git a/Source/Core/Core/GeckoCodeConfig.cpp b/Source/Core/Core/GeckoCodeConfig.cpp index 27b7c5f507..fd5d416f2e 100644 --- a/Source/Core/Core/GeckoCodeConfig.cpp +++ b/Source/Core/Core/GeckoCodeConfig.cpp @@ -4,6 +4,7 @@ #include "Core/GeckoCodeConfig.h" #include +#include #include #include #include @@ -176,8 +177,10 @@ std::vector LoadCodes(const IniFile& globalIni, const IniFile& localI { GeckoCode::Code new_code; // TODO: support options - new_code.original_line = line; - ss >> std::hex >> new_code.address >> new_code.data; + if (std::optional code = DeserializeLine(line)) + new_code = *code; + else + new_code.original_line = line; gcode.codes.push_back(new_code); } break; @@ -251,4 +254,23 @@ void SaveCodes(IniFile& inifile, const std::vector& gcodes) inifile.SetLines("Gecko_Enabled", enabled_lines); inifile.SetLines("Gecko_Disabled", disabled_lines); } + +std::optional DeserializeLine(const std::string& line) +{ + std::vector items = SplitString(line, ' '); + + GeckoCode::Code code; + code.original_line = line; + + if (items.size() < 2) + return std::nullopt; + + if (!TryParse(items[0], &code.address, 16)) + return std::nullopt; + if (!TryParse(items[1], &code.data, 16)) + return std::nullopt; + + return code; +} + } // namespace Gecko diff --git a/Source/Core/Core/GeckoCodeConfig.h b/Source/Core/Core/GeckoCodeConfig.h index a90b91dd70..1b43539c0c 100644 --- a/Source/Core/Core/GeckoCodeConfig.h +++ b/Source/Core/Core/GeckoCodeConfig.h @@ -3,8 +3,10 @@ #pragma once +#include #include #include + #include "Core/GeckoCode.h" class IniFile; @@ -14,4 +16,6 @@ namespace Gecko std::vector LoadCodes(const IniFile& globalIni, const IniFile& localIni); std::vector DownloadCodes(std::string gametdb_id, bool* succeeded); void SaveCodes(IniFile& inifile, const std::vector& gcodes); + +std::optional DeserializeLine(const std::string& line); } // namespace Gecko diff --git a/Source/Core/Core/PatchEngine.cpp b/Source/Core/Core/PatchEngine.cpp index afd20df61c..f5fe19b7ef 100644 --- a/Source/Core/Core/PatchEngine.cpp +++ b/Source/Core/Core/PatchEngine.cpp @@ -11,6 +11,7 @@ #include #include #include +#include #include #include @@ -45,6 +46,52 @@ const char* PatchTypeAsString(PatchType type) return s_patch_type_strings.at(static_cast(type)); } +std::optional DeserializeLine(std::string line) +{ + std::string::size_type loc = line.find('='); + if (loc != std::string::npos) + line[loc] = ':'; + + const std::vector items = SplitString(line, ':'); + PatchEntry entry; + + if (items.size() < 3) + return std::nullopt; + + if (!TryParse(items[0], &entry.address)) + return std::nullopt; + if (!TryParse(items[2], &entry.value)) + return std::nullopt; + + if (items.size() >= 4) + { + if (!TryParse(items[3], &entry.comparand)) + return std::nullopt; + entry.conditional = true; + } + + const auto iter = std::find(s_patch_type_strings.begin(), s_patch_type_strings.end(), items[1]); + if (iter == s_patch_type_strings.end()) + return std::nullopt; + entry.type = static_cast(std::distance(s_patch_type_strings.begin(), iter)); + + return entry; +} + +std::string SerializeLine(const PatchEntry& entry) +{ + if (entry.conditional) + { + return fmt::format("0x{:08X}:{}:0x{:08X}:0x{:08X}", entry.address, + PatchEngine::PatchTypeAsString(entry.type), entry.value, entry.comparand); + } + else + { + return fmt::format("0x{:08X}:{}:0x{:08X}", entry.address, + PatchEngine::PatchTypeAsString(entry.type), entry.value); + } +} + void LoadPatchSection(const std::string& section, std::vector* patches, const IniFile& globalIni, const IniFile& localIni) { @@ -76,37 +123,8 @@ void LoadPatchSection(const std::string& section, std::vector* patches, } else { - std::string::size_type loc = line.find('='); - - if (loc != std::string::npos) - { - line[loc] = ':'; - } - - const std::vector items = SplitString(line, ':'); - - if (items.size() >= 3) - { - PatchEntry pE; - bool success = true; - success &= TryParse(items[0], &pE.address); - success &= TryParse(items[2], &pE.value); - if (items.size() >= 4) - { - success &= TryParse(items[3], &pE.comparand); - pE.conditional = true; - } - - const auto iter = - std::find(s_patch_type_strings.begin(), s_patch_type_strings.end(), items[1]); - pE.type = PatchType(std::distance(s_patch_type_strings.begin(), iter)); - - success &= (pE.type != (PatchType)3); - if (success) - { - currentPatch.entries.push_back(pE); - } - } + if (std::optional entry = DeserializeLine(line)) + currentPatch.entries.push_back(*entry); } } @@ -141,20 +159,8 @@ void SavePatchSection(IniFile* local_ini, const std::vector& patches) lines.emplace_back('$' + patch.name); - for (const auto& entry : patch.entries) - { - if (!entry.conditional) - { - lines.emplace_back(fmt::format("0x{:08X}:{}:0x{:08X}", entry.address, - PatchEngine::PatchTypeAsString(entry.type), entry.value)); - } - else - { - lines.emplace_back(fmt::format("0x{:08X}:{}:0x{:08X}:0x{:08X}", entry.address, - PatchEngine::PatchTypeAsString(entry.type), entry.value, - entry.comparand)); - } - } + for (const PatchEntry& entry : patch.entries) + lines.emplace_back(SerializeLine(entry)); } local_ini->SetLines("OnFrame_Enabled", lines_enabled); diff --git a/Source/Core/Core/PatchEngine.h b/Source/Core/Core/PatchEngine.h index f3c4d3d6ac..9c0a7ed9ee 100644 --- a/Source/Core/Core/PatchEngine.h +++ b/Source/Core/Core/PatchEngine.h @@ -3,6 +3,7 @@ #pragma once +#include #include #include @@ -42,10 +43,14 @@ struct Patch const char* PatchTypeAsString(PatchType type); int GetSpeedhackCycles(const u32 addr); + +std::optional DeserializeLine(std::string line); +std::string SerializeLine(const PatchEntry& entry); void LoadPatchSection(const std::string& section, std::vector* patches, const IniFile& globalIni, const IniFile& localIni); void SavePatchSection(IniFile* local_ini, const std::vector& patches); void LoadPatches(); + bool ApplyFramePatches(); void Shutdown(); void Reload(); diff --git a/Source/Core/DolphinQt/Config/CheatCodeEditor.cpp b/Source/Core/DolphinQt/Config/CheatCodeEditor.cpp index 65a5cc78c9..b8a62c36b6 100644 --- a/Source/Core/DolphinQt/Config/CheatCodeEditor.cpp +++ b/Source/Core/DolphinQt/Config/CheatCodeEditor.cpp @@ -32,16 +32,10 @@ void CheatCodeEditor::SetARCode(ActionReplay::ARCode* code) { m_name_edit->setText(QString::fromStdString(code->name)); - QString s; + m_code_edit->clear(); for (ActionReplay::AREntry& e : code->ops) - { - s += QStringLiteral("%1 %2\n") - .arg(e.cmd_addr, 8, 16, QLatin1Char('0')) - .arg(e.value, 8, 16, QLatin1Char('0')); - } - - m_code_edit->setText(s); + m_code_edit->append(QString::fromStdString(ActionReplay::SerializeLine(e))); m_creator_label->setHidden(true); m_creator_edit->setHidden(true); @@ -57,14 +51,10 @@ void CheatCodeEditor::SetGeckoCode(Gecko::GeckoCode* code) m_name_edit->setText(QString::fromStdString(code->name)); m_creator_edit->setText(QString::fromStdString(code->creator)); - QString code_string; + m_code_edit->clear(); for (const auto& c : code->codes) - code_string += QStringLiteral("%1 %2\n") - .arg(c.address, 8, 16, QLatin1Char('0')) - .arg(c.data, 8, 16, QLatin1Char('0')); - - m_code_edit->setText(code_string); + m_code_edit->append(QString::fromStdString(c.original_line)); QString notes_string; for (const auto& line : code->notes) @@ -135,7 +125,6 @@ bool CheatCodeEditor::AcceptAR() if (line.isEmpty()) continue; - if (i == 0 && line[0] == u'$') { if (name.isEmpty()) @@ -144,40 +133,17 @@ bool CheatCodeEditor::AcceptAR() continue; } - QStringList values = line.split(QLatin1Char{' '}); + const auto parse_result = ActionReplay::DeserializeLine(line.toStdString()); - bool good = true; - - u32 addr = 0; - u32 value = 0; - - if (values.size() == 2) + if (std::holds_alternative(parse_result)) { - addr = values[0].toUInt(&good, 16); - - if (good) - value = values[1].toUInt(&good, 16); - - if (good) - entries.push_back(ActionReplay::AREntry(addr, value)); + entries.push_back(std::get(parse_result)); + } + else if (std::holds_alternative(parse_result)) + { + encrypted_lines.emplace_back(std::get(parse_result)); } else - { - QStringList blocks = line.split(QLatin1Char{'-'}); - - if (blocks.size() == 3 && blocks[0].size() == 4 && blocks[1].size() == 4 && - blocks[2].size() == 5) - { - encrypted_lines.emplace_back(blocks[0].toStdString() + blocks[1].toStdString() + - blocks[2].toStdString()); - } - else - { - good = false; - } - } - - if (!good) { auto result = ModalMessageBox::warning( this, tr("Parsing Error"), @@ -260,20 +226,11 @@ bool CheatCodeEditor::AcceptGecko() continue; } - QStringList values = line.split(QLatin1Char{' '}); - - bool good = values.size() == 2; - - u32 addr = 0; - u32 value = 0; - - if (good) - addr = values[0].toUInt(&good, 16); - - if (good) - value = values[1].toUInt(&good, 16); - - if (!good) + if (std::optional c = Gecko::DeserializeLine(line.toStdString())) + { + entries.push_back(*c); + } + else { auto result = ModalMessageBox::warning( this, tr("Parsing Error"), @@ -286,15 +243,6 @@ bool CheatCodeEditor::AcceptGecko() if (result == QMessageBox::Abort) return false; } - else - { - Gecko::GeckoCode::Code c; - c.address = addr; - c.data = value; - c.original_line = line.toStdString(); - - entries.push_back(c); - } } if (entries.empty()) diff --git a/Source/Core/DolphinQt/Config/GeckoCodeWidget.cpp b/Source/Core/DolphinQt/Config/GeckoCodeWidget.cpp index 4fc99a0dc4..99110d3b37 100644 --- a/Source/Core/DolphinQt/Config/GeckoCodeWidget.cpp +++ b/Source/Core/DolphinQt/Config/GeckoCodeWidget.cpp @@ -167,11 +167,7 @@ void GeckoCodeWidget::OnSelectionChanged() m_code_view->clear(); for (const auto& c : code.codes) - { - m_code_view->append(QStringLiteral("%1 %2") - .arg(c.address, 8, 16, QLatin1Char('0')) - .arg(c.data, 8, 16, QLatin1Char('0'))); - } + m_code_view->append(QString::fromStdString(c.original_line)); } void GeckoCodeWidget::OnItemChanged(QListWidgetItem* item)