Skip to content

Commit

Permalink
DebugInterface: Add save/load methods for patches
Browse files Browse the repository at this point in the history
  • Loading branch information
sepalani committed Feb 28, 2021
1 parent fe7e575 commit 1ac3455
Show file tree
Hide file tree
Showing 7 changed files with 162 additions and 0 deletions.
83 changes: 83 additions & 0 deletions Source/Core/Common/Debug/MemoryPatches.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
#include "Common/Debug/MemoryPatches.h"

#include <algorithm>
#include <iomanip>
#include <locale>
#include <sstream>
#include <utility>

Expand Down Expand Up @@ -96,6 +98,87 @@ void MemoryPatches::RemovePatch(std::size_t index)
m_patches.erase(m_patches.begin() + index);
}

void MemoryPatches::LoadFromStrings(const std::vector<std::string>& 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<u8> 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<u8>(hex));
}

if (value.empty())
continue;

const std::size_t index = m_patches.size();
SetPatch(address, value);
if (!is_enabled)
DisablePatch(index);
}
}

std::vector<std::string> MemoryPatches::SaveToStrings() const
{
std::vector<std::string> 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<u32>(b);
patches.push_back(oss.str());
}
return patches;
}

void MemoryPatches::ClearPatches()
{
const std::size_t size = m_patches.size();
Expand Down
2 changes: 2 additions & 0 deletions Source/Core/Common/Debug/MemoryPatches.h
Original file line number Diff line number Diff line change
Expand Up @@ -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<std::string>& patches);
std::vector<std::string> SaveToStrings() const;
void ClearPatches();

protected:
Expand Down
2 changes: 2 additions & 0 deletions Source/Core/Common/DebugInterface.h
Original file line number Diff line number Diff line change
Expand Up @@ -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<std::string>& watches) = 0;
virtual std::vector<std::string> SavePatchesToStrings() const = 0;
virtual void ClearPatches() = 0;

// Threads
Expand Down
10 changes: 10 additions & 0 deletions Source/Core/Core/Debugger/PPCDebugInterface.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,16 @@ void PPCDebugInterface::RemovePatch(std::size_t index)
m_patches.RemovePatch(index);
}

void PPCDebugInterface::LoadPatchesFromStrings(const std::vector<std::string>& patches)
{
m_patches.LoadFromStrings(patches);
}

std::vector<std::string> PPCDebugInterface::SavePatchesToStrings() const
{
return m_patches.SaveToStrings();
}

void PPCDebugInterface::ClearPatches()
{
m_patches.ClearPatches();
Expand Down
2 changes: 2 additions & 0 deletions Source/Core/Core/Debugger/PPCDebugInterface.h
Original file line number Diff line number Diff line change
Expand Up @@ -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<std::string>& patches) override;
std::vector<std::string> SavePatchesToStrings() const override;
void ClearPatches() override;

// Threads
Expand Down
59 changes: 59 additions & 0 deletions Source/Core/DolphinQt/Debugger/MemoryPatchWidget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,18 @@
#include <iomanip>
#include <sstream>

#include <QFileDialog>
#include <QHeaderView>
#include <QTableWidget>
#include <QToolBar>
#include <QVBoxLayout>

#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"

Expand Down Expand Up @@ -93,6 +96,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);
Expand All @@ -106,6 +111,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::UpdateButtonsEnabled()
Expand All @@ -117,6 +124,8 @@ void MemoryPatchWidget::UpdateButtonsEnabled()
m_toggle_on_off->setEnabled(is_enabled);
m_delete->setEnabled(is_enabled);
m_clear->setEnabled(is_enabled);
m_load->setEnabled(is_enabled);
m_save->setEnabled(is_enabled);
}

void MemoryPatchWidget::closeEvent(QCloseEvent*)
Expand Down Expand Up @@ -212,3 +221,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<std::string> 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();
}
4 changes: 4 additions & 0 deletions Source/Core/DolphinQt/Debugger/MemoryPatchWidget.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ class MemoryPatchWidget : public QDockWidget
void OnDelete();
void OnClear();
void OnToggleOnOff();
void OnLoadFromFile();
void OnSaveToFile();

void UpdateIcons();
void UpdateButtonsEnabled();
Expand All @@ -42,4 +44,6 @@ class MemoryPatchWidget : public QDockWidget
QAction* m_delete;
QAction* m_clear;
QAction* m_toggle_on_off;
QAction* m_load;
QAction* m_save;
};

0 comments on commit 1ac3455

Please sign in to comment.