From 7cf6ea497fff9a6c0f391e57e50e2b5ecc91de43 Mon Sep 17 00:00:00 2001 From: Sepalani Date: Sat, 27 Feb 2021 21:32:05 +0400 Subject: [PATCH] DebugInterface: Add save/load methods for patches --- Source/Core/Common/Debug/MemoryPatches.cpp | 83 +++++++++++++++++++ Source/Core/Common/Debug/MemoryPatches.h | 2 + Source/Core/Common/DebugInterface.h | 2 + .../Core/Core/Debugger/PPCDebugInterface.cpp | 10 +++ Source/Core/Core/Debugger/PPCDebugInterface.h | 2 + .../DolphinQt/Debugger/MemoryPatchWidget.cpp | 57 +++++++++++++ .../DolphinQt/Debugger/MemoryPatchWidget.h | 4 + 7 files changed, 160 insertions(+) diff --git a/Source/Core/Common/Debug/MemoryPatches.cpp b/Source/Core/Common/Debug/MemoryPatches.cpp index 6104647f0a53..74ebb2b0f2bc 100644 --- a/Source/Core/Common/Debug/MemoryPatches.cpp +++ b/Source/Core/Common/Debug/MemoryPatches.cpp @@ -5,6 +5,8 @@ #include "Common/Debug/MemoryPatches.h" #include +#include +#include #include #include @@ -96,6 +98,87 @@ void MemoryPatches::RemovePatch(std::size_t index) m_patches.erase(m_patches.begin() + index); } +void MemoryPatches::LoadFromStrings(const std::vector& patches) +{ + for (const std::string& patch : patches) + { + std::stringstream ss; + ss.imbue(std::locale::classic()); + + // Get the patch state (on, off) + std::string state; + ss << patch; + ss >> state; + const bool is_enabled = state == "on"; + if (!ss) + continue; + + // Get the patch address + u32 address; + ss >> std::hex >> address; + ss >> std::ws; + if (!ss) + continue; + + // Get the patch value + std::string hexstring; + ss >> hexstring; + if (!ss) + continue; + + // Check the end of line + std::string is_not_eol; + if (ss >> std::ws >> is_not_eol) + continue; + + const bool is_hex_valid = + hexstring.find_first_not_of("0123456789abcdefABCDEF") == hexstring.npos && + (hexstring.size() % 2) == 0; + if (!is_hex_valid) + continue; + + // Convert the patch value to bytes + std::vector value; + const std::size_t len = hexstring.length(); + for (std::size_t i = 0; i < len; i += 2) + { + std::size_t size; + u32 hex = std::stoi(hexstring.substr(i, 2), &size, 16); + if (size != 2) + { + value.clear(); + break; + } + value.push_back(static_cast(hex)); + } + + if (value.empty()) + continue; + + const std::size_t index = m_patches.size(); + SetPatch(address, value); + if (!is_enabled) + DisablePatch(index); + } +} + +std::vector MemoryPatches::SaveToStrings() const +{ + std::vector patches; + for (const auto& patch : m_patches) + { + std::ostringstream oss; + oss.imbue(std::locale::classic()); + const bool is_enabled = patch.is_enabled == MemoryPatch::State::Enabled; + oss << std::hex << std::setfill('0') << (is_enabled ? "on" : "off") << " "; + oss << std::setw(8) << patch.address << " "; + for (u8 b : patch.patch_value) + oss << std::setw(2) << static_cast(b); + patches.push_back(oss.str()); + } + return patches; +} + void MemoryPatches::ClearPatches() { const std::size_t size = m_patches.size(); diff --git a/Source/Core/Common/Debug/MemoryPatches.h b/Source/Core/Common/Debug/MemoryPatches.h index b338c5371848..6ad541c7b971 100644 --- a/Source/Core/Common/Debug/MemoryPatches.h +++ b/Source/Core/Common/Debug/MemoryPatches.h @@ -44,6 +44,8 @@ class MemoryPatches void DisablePatch(std::size_t index); bool HasEnabledPatch(u32 address) const; void RemovePatch(std::size_t index); + void LoadFromStrings(const std::vector& patches); + std::vector SaveToStrings() const; void ClearPatches(); protected: diff --git a/Source/Core/Common/DebugInterface.h b/Source/Core/Common/DebugInterface.h index 7b377ca53060..e537f966f7a9 100644 --- a/Source/Core/Common/DebugInterface.h +++ b/Source/Core/Common/DebugInterface.h @@ -51,6 +51,8 @@ class DebugInterface virtual void DisablePatch(std::size_t index) = 0; virtual bool HasEnabledPatch(u32 address) const = 0; virtual void RemovePatch(std::size_t index) = 0; + virtual void LoadPatchesFromStrings(const std::vector& watches) = 0; + virtual std::vector SavePatchesToStrings() const = 0; virtual void ClearPatches() = 0; // Threads diff --git a/Source/Core/Core/Debugger/PPCDebugInterface.cpp b/Source/Core/Core/Debugger/PPCDebugInterface.cpp index 1ed6871207c1..06909e5dc30f 100644 --- a/Source/Core/Core/Debugger/PPCDebugInterface.cpp +++ b/Source/Core/Core/Debugger/PPCDebugInterface.cpp @@ -174,6 +174,16 @@ void PPCDebugInterface::RemovePatch(std::size_t index) m_patches.RemovePatch(index); } +void PPCDebugInterface::LoadPatchesFromStrings(const std::vector& patches) +{ + m_patches.LoadFromStrings(patches); +} + +std::vector PPCDebugInterface::SavePatchesToStrings() const +{ + return m_patches.SaveToStrings(); +} + void PPCDebugInterface::ClearPatches() { m_patches.ClearPatches(); diff --git a/Source/Core/Core/Debugger/PPCDebugInterface.h b/Source/Core/Core/Debugger/PPCDebugInterface.h index 12b498274ef0..6b23796a864b 100644 --- a/Source/Core/Core/Debugger/PPCDebugInterface.h +++ b/Source/Core/Core/Debugger/PPCDebugInterface.h @@ -53,6 +53,8 @@ class PPCDebugInterface final : public Common::DebugInterface void DisablePatch(std::size_t index) override; bool HasEnabledPatch(u32 address) const override; void RemovePatch(std::size_t index) override; + void LoadPatchesFromStrings(const std::vector& patches) override; + std::vector SavePatchesToStrings() const override; void ClearPatches() override; // Threads diff --git a/Source/Core/DolphinQt/Debugger/MemoryPatchWidget.cpp b/Source/Core/DolphinQt/Debugger/MemoryPatchWidget.cpp index d004a2660796..5679116950c2 100644 --- a/Source/Core/DolphinQt/Debugger/MemoryPatchWidget.cpp +++ b/Source/Core/DolphinQt/Debugger/MemoryPatchWidget.cpp @@ -7,15 +7,18 @@ #include #include +#include #include #include #include #include +#include "Common/FileUtil.h" #include "Core/ConfigManager.h" #include "Core/Core.h" #include "Core/PowerPC/PowerPC.h" +#include "DolphinQt/QtUtils/ModalMessageBox.h" #include "DolphinQt/Resources.h" #include "DolphinQt/Settings.h" @@ -91,6 +94,8 @@ void MemoryPatchWidget::CreateWidgets() m_toggle_on_off = m_toolbar->addAction(tr("On/Off"), this, &MemoryPatchWidget::OnToggleOnOff); m_delete = m_toolbar->addAction(tr("Delete"), this, &MemoryPatchWidget::OnDelete); m_clear = m_toolbar->addAction(tr("Clear"), this, &MemoryPatchWidget::OnClear); + m_load = m_toolbar->addAction(tr("Load from file"), this, &MemoryPatchWidget::OnLoadFromFile); + m_save = m_toolbar->addAction(tr("Save to file"), this, &MemoryPatchWidget::OnSaveToFile); QWidget* widget = new QWidget; widget->setLayout(layout); @@ -104,6 +109,8 @@ void MemoryPatchWidget::UpdateIcons() m_toggle_on_off->setIcon(Resources::GetScaledThemeIcon("debugger_breakpoint")); m_delete->setIcon(Resources::GetScaledThemeIcon("debugger_delete")); m_clear->setIcon(Resources::GetScaledThemeIcon("debugger_clear")); + m_load->setIcon(Resources::GetScaledThemeIcon("debugger_load")); + m_save->setIcon(Resources::GetScaledThemeIcon("debugger_save")); } void MemoryPatchWidget::closeEvent(QCloseEvent*) @@ -194,3 +201,53 @@ void MemoryPatchWidget::OnToggleOnOff() emit MemoryPatchesChanged(); Update(); } + +void MemoryPatchWidget::OnLoadFromFile() +{ + const QString path = QFileDialog::getOpenFileName(this, tr("Load patch file"), QString(), + tr("Dolphin Patch File (*.patch)")); + + if (path.isEmpty()) + return; + + std::ifstream ifs; + File::OpenFStream(ifs, path.toStdString(), std::ios_base::in); + if (!ifs) + { + ModalMessageBox::warning(this, tr("Error"), tr("Failed to load patch file '%1'").arg(path)); + return; + } + + std::string line; + std::vector patches; + while (std::getline(ifs, line)) + patches.push_back(line); + ifs.close(); + + PowerPC::debug_interface.ClearPatches(); + PowerPC::debug_interface.LoadPatchesFromStrings(patches); + + emit MemoryPatchesChanged(); + Update(); +} + +void MemoryPatchWidget::OnSaveToFile() +{ + const QString path = QFileDialog::getSaveFileName(this, tr("Save patch file"), QString(), + tr("Dolphin Patch File (*.patch)")); + + if (path.isEmpty()) + return; + + std::ofstream out; + File::OpenFStream(out, path.toStdString(), std::ios::out); + if (!out) + { + ModalMessageBox::warning(this, tr("Error"), + tr("Failed to save patch file to path '%1'").arg(path)); + } + + for (auto& patch : PowerPC::debug_interface.SavePatchesToStrings()) + out << patch << std::endl; + out.close(); +} diff --git a/Source/Core/DolphinQt/Debugger/MemoryPatchWidget.h b/Source/Core/DolphinQt/Debugger/MemoryPatchWidget.h index b12667667818..25121fb3c55d 100644 --- a/Source/Core/DolphinQt/Debugger/MemoryPatchWidget.h +++ b/Source/Core/DolphinQt/Debugger/MemoryPatchWidget.h @@ -32,6 +32,8 @@ class MemoryPatchWidget : public QDockWidget void OnDelete(); void OnClear(); void OnToggleOnOff(); + void OnLoadFromFile(); + void OnSaveToFile(); void UpdateIcons(); @@ -40,4 +42,6 @@ class MemoryPatchWidget : public QDockWidget QAction* m_delete; QAction* m_clear; QAction* m_toggle_on_off; + QAction* m_load; + QAction* m_save; };