Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Merge pull request #10011 from JosJuice/android-cheats-preparation
Split out code for serializing/deserializing cheat lines
  • Loading branch information
JosJuice committed Sep 8, 2021
2 parents 2a22367 + b90008a commit 52304df
Show file tree
Hide file tree
Showing 9 changed files with 188 additions and 188 deletions.
76 changes: 41 additions & 35 deletions Source/Core/Core/ActionReplay.cpp
Expand Up @@ -26,6 +26,7 @@
#include <mutex>
#include <string>
#include <utility>
#include <variant>
#include <vector>

#include <fmt/format.h>
Expand Down Expand Up @@ -215,42 +216,14 @@ std::vector<ARCode> LoadCodes(const IniFile& global_ini, const IniFile& local_in
}
else
{
std::vector<std::string> 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<AREntry>(parse_result))
current_code.ops.push_back(std::get<AREntry>(parse_result));
else if (std::holds_alternative<EncryptedLine>(parse_result))
encrypted_lines.emplace_back(std::get<EncryptedLine>(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);
}
}

Expand Down Expand Up @@ -293,7 +266,7 @@ void SaveCodes(IniFile* local_ini, const std::vector<ARCode>& 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));
}
}
}
Expand All @@ -303,6 +276,39 @@ void SaveCodes(IniFile* local_ini, const std::vector<ARCode>& codes)
local_ini->SetLines("ActionReplay", lines);
}

std::variant<std::monostate, AREntry, EncryptedLine> DeserializeLine(const std::string& line)
{
std::vector<std::string> 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)
Expand Down
7 changes: 7 additions & 0 deletions Source/Core/Core/ActionReplay.h
Expand Up @@ -4,7 +4,10 @@
#pragma once

#include <string>
#include <utility>
#include <variant>
#include <vector>

#include "Common/CommonTypes.h"

class IniFile;
Expand Down Expand Up @@ -44,6 +47,10 @@ void LoadAndApplyCodes(const IniFile& global_ini, const IniFile& local_ini);
std::vector<ARCode> LoadCodes(const IniFile& global_ini, const IniFile& local_ini);
void SaveCodes(IniFile* local_ini, const std::vector<ARCode>& codes);

using EncryptedLine = std::string;
std::variant<std::monostate, AREntry, EncryptedLine> DeserializeLine(const std::string& line);
std::string SerializeLine(const AREntry& op);

void EnableSelfLogging(bool enable);
std::vector<std::string> GetSelfLog();
void ClearSelfLog();
Expand Down
26 changes: 24 additions & 2 deletions Source/Core/Core/GeckoCodeConfig.cpp
Expand Up @@ -4,6 +4,7 @@
#include "Core/GeckoCodeConfig.h"

#include <algorithm>
#include <optional>
#include <sstream>
#include <string>
#include <vector>
Expand Down Expand Up @@ -176,8 +177,10 @@ std::vector<GeckoCode> 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<GeckoCode::Code> code = DeserializeLine(line))
new_code = *code;
else
new_code.original_line = line;
gcode.codes.push_back(new_code);
}
break;
Expand Down Expand Up @@ -251,4 +254,23 @@ void SaveCodes(IniFile& inifile, const std::vector<GeckoCode>& gcodes)
inifile.SetLines("Gecko_Enabled", enabled_lines);
inifile.SetLines("Gecko_Disabled", disabled_lines);
}

std::optional<GeckoCode::Code> DeserializeLine(const std::string& line)
{
std::vector<std::string> 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
4 changes: 4 additions & 0 deletions Source/Core/Core/GeckoCodeConfig.h
Expand Up @@ -3,8 +3,10 @@

#pragma once

#include <optional>
#include <string>
#include <vector>

#include "Core/GeckoCode.h"

class IniFile;
Expand All @@ -14,4 +16,6 @@ namespace Gecko
std::vector<GeckoCode> LoadCodes(const IniFile& globalIni, const IniFile& localIni);
std::vector<GeckoCode> DownloadCodes(std::string gametdb_id, bool* succeeded);
void SaveCodes(IniFile& inifile, const std::vector<GeckoCode>& gcodes);

std::optional<GeckoCode::Code> DeserializeLine(const std::string& line);
} // namespace Gecko
121 changes: 83 additions & 38 deletions Source/Core/Core/PatchEngine.cpp
Expand Up @@ -11,9 +11,12 @@
#include <array>
#include <iterator>
#include <map>
#include <optional>
#include <string>
#include <vector>

#include <fmt/format.h>

#include "Common/Assert.h"
#include "Common/IniFile.h"
#include "Common/StringUtil.h"
Expand Down Expand Up @@ -43,8 +46,54 @@ const char* PatchTypeAsString(PatchType type)
return s_patch_type_strings.at(static_cast<int>(type));
}

void LoadPatchSection(const std::string& section, std::vector<Patch>& patches, IniFile& globalIni,
IniFile& localIni)
std::optional<PatchEntry> DeserializeLine(std::string line)
{
std::string::size_type loc = line.find('=');
if (loc != std::string::npos)
line[loc] = ':';

const std::vector<std::string> 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<PatchType>(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<Patch>* patches,
const IniFile& globalIni, const IniFile& localIni)
{
const IniFile* inis[2] = {&globalIni, &localIni};

Expand All @@ -64,7 +113,7 @@ void LoadPatchSection(const std::string& section, std::vector<Patch>& patches, I
// Take care of the previous code
if (!currentPatch.name.empty())
{
patches.push_back(currentPatch);
patches->push_back(currentPatch);
}
currentPatch.entries.clear();

Expand All @@ -74,55 +123,51 @@ void LoadPatchSection(const std::string& section, std::vector<Patch>& patches, I
}
else
{
std::string::size_type loc = line.find('=');

if (loc != std::string::npos)
{
line[loc] = ':';
}

const std::vector<std::string> 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<PatchEntry> entry = DeserializeLine(line))
currentPatch.entries.push_back(*entry);
}
}

if (!currentPatch.name.empty() && !currentPatch.entries.empty())
{
patches.push_back(currentPatch);
patches->push_back(currentPatch);
}

ReadEnabledAndDisabled(*ini, section, &patches);
ReadEnabledAndDisabled(*ini, section, patches);

if (ini == &globalIni)
{
for (Patch& patch : patches)
for (Patch& patch : *patches)
patch.default_enabled = patch.enabled;
}
}
}

void SavePatchSection(IniFile* local_ini, const std::vector<Patch>& patches)
{
std::vector<std::string> lines;
std::vector<std::string> lines_enabled;
std::vector<std::string> lines_disabled;

for (const auto& patch : patches)
{
if (patch.enabled != patch.default_enabled)
(patch.enabled ? lines_enabled : lines_disabled).emplace_back('$' + patch.name);

if (!patch.user_defined)
continue;

lines.emplace_back('$' + patch.name);

for (const PatchEntry& entry : patch.entries)
lines.emplace_back(SerializeLine(entry));
}

local_ini->SetLines("OnFrame_Enabled", lines_enabled);
local_ini->SetLines("OnFrame_Disabled", lines_disabled);
local_ini->SetLines("OnFrame", lines);
}

static void LoadSpeedhacks(const std::string& section, IniFile& ini)
{
std::vector<std::string> keys;
Expand Down Expand Up @@ -161,7 +206,7 @@ void LoadPatches()
IniFile globalIni = SConfig::GetInstance().LoadDefaultGameIni();
IniFile localIni = SConfig::GetInstance().LoadLocalGameIni();

LoadPatchSection("OnFrame", s_on_frame, globalIni, localIni);
LoadPatchSection("OnFrame", &s_on_frame, globalIni, localIni);

// Check if I'm syncing Codes
if (Config::Get(Config::SESSION_CODE_SYNC_OVERRIDE))
Expand Down
10 changes: 8 additions & 2 deletions Source/Core/Core/PatchEngine.h
Expand Up @@ -3,6 +3,7 @@

#pragma once

#include <optional>
#include <string>
#include <vector>

Expand Down Expand Up @@ -42,9 +43,14 @@ struct Patch
const char* PatchTypeAsString(PatchType type);

int GetSpeedhackCycles(const u32 addr);
void LoadPatchSection(const std::string& section, std::vector<Patch>& patches, IniFile& globalIni,
IniFile& localIni);

std::optional<PatchEntry> DeserializeLine(std::string line);
std::string SerializeLine(const PatchEntry& entry);
void LoadPatchSection(const std::string& section, std::vector<Patch>* patches,
const IniFile& globalIni, const IniFile& localIni);
void SavePatchSection(IniFile* local_ini, const std::vector<Patch>& patches);
void LoadPatches();

bool ApplyFramePatches();
void Shutdown();
void Reload();
Expand Down

0 comments on commit 52304df

Please sign in to comment.