Merge pull request #10011 from JosJuice/android-cheats-preparation
Split out code for serializing/deserializing cheat lines
This commit is contained in:
commit
52304df943
|
@ -26,6 +26,7 @@
|
|||
#include <mutex>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <variant>
|
||||
#include <vector>
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
|
|
|
@ -4,7 +4,10 @@
|
|||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <variant>
|
||||
#include <vector>
|
||||
|
||||
#include "Common/CommonTypes.h"
|
||||
|
||||
class IniFile;
|
||||
|
@ -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();
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#include "Core/GeckoCodeConfig.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <optional>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
|
|
@ -3,8 +3,10 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "Core/GeckoCode.h"
|
||||
|
||||
class IniFile;
|
||||
|
@ -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
|
||||
|
|
|
@ -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"
|
||||
|
@ -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};
|
||||
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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))
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
|
@ -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();
|
||||
|
|
|
@ -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<ActionReplay::AREntry>(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<ActionReplay::AREntry>(parse_result));
|
||||
}
|
||||
else if (std::holds_alternative<ActionReplay::EncryptedLine>(parse_result))
|
||||
{
|
||||
encrypted_lines.emplace_back(std::get<ActionReplay::EncryptedLine>(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<Gecko::GeckoCode::Code> 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())
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -7,8 +7,6 @@
|
|||
#include <QListWidget>
|
||||
#include <QPushButton>
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include "Common/FileUtil.h"
|
||||
#include "Common/IniFile.h"
|
||||
#include "Common/StringUtil.h"
|
||||
|
@ -28,7 +26,7 @@ PatchesWidget::PatchesWidget(const UICommon::GameFile& game)
|
|||
|
||||
IniFile game_ini_default = SConfig::GetInstance().LoadDefaultGameIni(m_game_id, m_game_revision);
|
||||
|
||||
PatchEngine::LoadPatchSection("OnFrame", m_patches, game_ini_default, game_ini_local);
|
||||
PatchEngine::LoadPatchSection("OnFrame", &m_patches, game_ini_default, game_ini_local);
|
||||
|
||||
CreateWidgets();
|
||||
ConnectWidgets();
|
||||
|
@ -128,44 +126,12 @@ void PatchesWidget::OnRemove()
|
|||
|
||||
void PatchesWidget::SavePatches()
|
||||
{
|
||||
std::vector<std::string> lines;
|
||||
std::vector<std::string> lines_enabled;
|
||||
std::vector<std::string> lines_disabled;
|
||||
|
||||
for (const auto& patch : m_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 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));
|
||||
}
|
||||
}
|
||||
}
|
||||
const std::string ini_path = File::GetUserPath(D_GAMESETTINGS_IDX) + m_game_id + ".ini";
|
||||
|
||||
IniFile game_ini_local;
|
||||
game_ini_local.Load(File::GetUserPath(D_GAMESETTINGS_IDX) + m_game_id + ".ini");
|
||||
|
||||
game_ini_local.SetLines("OnFrame_Enabled", lines_enabled);
|
||||
game_ini_local.SetLines("OnFrame_Disabled", lines_disabled);
|
||||
game_ini_local.SetLines("OnFrame", lines);
|
||||
|
||||
game_ini_local.Save(File::GetUserPath(D_GAMESETTINGS_IDX) + m_game_id + ".ini");
|
||||
game_ini_local.Load(ini_path);
|
||||
PatchEngine::SavePatchSection(&game_ini_local, m_patches);
|
||||
game_ini_local.Save(ini_path);
|
||||
}
|
||||
|
||||
void PatchesWidget::Update()
|
||||
|
|
Loading…
Reference in New Issue