Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

DolphinQt: Patches window added #9531

Draft
wants to merge 4 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
99 changes: 94 additions & 5 deletions Source/Core/Common/Debug/MemoryPatches.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,21 @@
#include "Common/Debug/MemoryPatches.h"

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

namespace Common::Debug
{
MemoryPatch::MemoryPatch(u32 address_, std::vector<u8> value_)
: address(address_), value(std::move(value_))
MemoryPatch::MemoryPatch(u32 address_, std::vector<u8> value)
: address(address_), patch_value(std::move(value)), original_value()
{
}

MemoryPatch::MemoryPatch(u32 address_, u32 value_)
: MemoryPatch(address_, {static_cast<u8>(value_ >> 24), static_cast<u8>(value_ >> 16),
static_cast<u8>(value_ >> 8), static_cast<u8>(value_)})
MemoryPatch::MemoryPatch(u32 address_, u32 value)
: MemoryPatch(address_, {static_cast<u8>(value >> 24), static_cast<u8>(value >> 16),
static_cast<u8>(value >> 8), static_cast<u8>(value)})
{
}

Expand All @@ -37,6 +39,11 @@ void MemoryPatches::SetPatch(u32 address, std::vector<u8> value)
Patch(index);
}

const MemoryPatch& MemoryPatches::GetPatch(std::size_t index) const
{
return m_patches.at(index);
}

const std::vector<MemoryPatch>& MemoryPatches::GetPatches() const
{
return m_patches;
Expand Down Expand Up @@ -74,6 +81,7 @@ void MemoryPatches::DisablePatch(std::size_t index)
return;
m_patches[index].is_enabled = MemoryPatch::State::Disabled;
Patch(index);
m_patches[index].original_value.clear();
}

bool MemoryPatches::HasEnabledPatch(u32 address) const
Expand All @@ -89,6 +97,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;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if it makes sense to make a fmt::formatter for MemoryPatch then you just do: fmt::to_string(patch)...

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another option is to implement std::ostream& operator<< and use fmt's ostream support, if that'd make it easier to be consistent with the parsing code.

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
6 changes: 5 additions & 1 deletion Source/Core/Common/Debug/MemoryPatches.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ struct MemoryPatch
MemoryPatch(u32 address_, u32 value_);

u32 address;
std::vector<u8> value;
std::vector<u8> patch_value;
std::vector<u8> original_value;
State is_enabled = State::Enabled;
};

Expand All @@ -35,12 +36,15 @@ class MemoryPatches

void SetPatch(u32 address, u32 value);
void SetPatch(u32 address, std::vector<u8> value);
const MemoryPatch& GetPatch(std::size_t index) const;
const std::vector<MemoryPatch>& GetPatches() const;
void UnsetPatch(u32 address);
void EnablePatch(std::size_t index);
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
3 changes: 3 additions & 0 deletions Source/Core/Common/DebugInterface.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,12 +43,15 @@ class DebugInterface
// Memory Patches
virtual void SetPatch(u32 address, u32 value) = 0;
virtual void SetPatch(u32 address, std::vector<u8> value) = 0;
virtual const Debug::MemoryPatch& GetPatch(std::size_t index) const = 0;
virtual const std::vector<Debug::MemoryPatch>& GetPatches() const = 0;
virtual void UnsetPatch(u32 address) = 0;
virtual void EnablePatch(std::size_t index) = 0;
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
33 changes: 28 additions & 5 deletions Source/Core/Core/Debugger/PPCDebugInterface.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,19 +26,27 @@
void PPCPatches::Patch(std::size_t index)
{
auto& patch = m_patches[index];
if (patch.value.empty())
if (patch.patch_value.empty())
return;

const u32 address = patch.address;
const std::size_t size = patch.value.size();
const std::size_t size = patch.patch_value.size();
if (!PowerPC::HostIsRAMAddress(address))
return;

const bool is_enabled = patch.is_enabled == Common::Debug::MemoryPatch::State::Enabled;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've seen this a couple times, maybe it makes sense to make a function?

for (u32 offset = 0; offset < size; ++offset)
{
const u8 value = PowerPC::HostRead_U8(address + offset);
PowerPC::HostWrite_U8(patch.value[offset], address + offset);
patch.value[offset] = value;
if (is_enabled)
{
const u8 original_value = PowerPC::HostRead_U8(address + offset);
PowerPC::HostWrite_U8(patch.patch_value[offset], address + offset);
patch.original_value.push_back(original_value);
}
else
{
PowerPC::HostWrite_U8(patch.original_value[offset], address + offset);
}

if (((address + offset) % 4) == 3)
PowerPC::ScheduleInvalidateCacheThreadSafe(Common::AlignDown(address + offset, 4));
Expand Down Expand Up @@ -133,6 +141,11 @@ void PPCDebugInterface::SetPatch(u32 address, std::vector<u8> value)
m_patches.SetPatch(address, std::move(value));
}

const Common::Debug::MemoryPatch& PPCDebugInterface::GetPatch(std::size_t index) const
{
return m_patches.GetPatch(index);
}

const std::vector<Common::Debug::MemoryPatch>& PPCDebugInterface::GetPatches() const
{
return m_patches.GetPatches();
Expand Down Expand Up @@ -163,6 +176,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
3 changes: 3 additions & 0 deletions Source/Core/Core/Debugger/PPCDebugInterface.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,12 +45,15 @@ class PPCDebugInterface final : public Common::DebugInterface
// Memory Patches
void SetPatch(u32 address, u32 value) override;
void SetPatch(u32 address, std::vector<u8> value) override;
const Common::Debug::MemoryPatch& GetPatch(std::size_t index) const override;
const std::vector<Common::Debug::MemoryPatch>& GetPatches() const override;
void UnsetPatch(u32 address) override;
void EnablePatch(std::size_t index) override;
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
2 changes: 2 additions & 0 deletions Source/Core/DolphinQt/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,8 @@ add_executable(dolphin-emu
Debugger/CodeWidget.h
Debugger/JITWidget.cpp
Debugger/JITWidget.h
Debugger/MemoryPatchWidget.cpp
Debugger/MemoryPatchWidget.h
Debugger/MemoryViewWidget.cpp
Debugger/MemoryViewWidget.h
Debugger/MemoryWidget.cpp
Expand Down
6 changes: 6 additions & 0 deletions Source/Core/DolphinQt/Debugger/CodeViewWidget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -513,6 +513,8 @@ void CodeViewWidget::ReplaceAddress(u32 address, ReplaceWith replace)
{
PowerPC::debug_interface.UnsetPatch(address);
PowerPC::debug_interface.SetPatch(address, replace == ReplaceWith::BLR ? 0x4e800020 : 0x60000000);

emit MemoryPatchesChanged();
Update();
}

Expand Down Expand Up @@ -823,6 +825,8 @@ void CodeViewWidget::OnReplaceInstruction()
{
PowerPC::debug_interface.UnsetPatch(addr);
PowerPC::debug_interface.SetPatch(addr, dialog.GetCode());

emit MemoryPatchesChanged();
Update();
}
}
Expand All @@ -832,6 +836,8 @@ void CodeViewWidget::OnRestoreInstruction()
const u32 addr = GetContextAddress();

PowerPC::debug_interface.UnsetPatch(addr);

emit MemoryPatchesChanged();
Update();
}

Expand Down
1 change: 1 addition & 0 deletions Source/Core/DolphinQt/Debugger/CodeViewWidget.h
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ class CodeViewWidget : public QTableWidget
void ShowMemory(u32 address);
void SymbolsChanged();
void BreakpointsChanged();
void MemoryPatchesChanged();
void UpdateCodeWidget();

private:
Expand Down
2 changes: 2 additions & 0 deletions Source/Core/DolphinQt/Debugger/CodeWidget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,8 @@ void CodeWidget::ConnectWidgets()
connect(m_code_view, &CodeViewWidget::SymbolsChanged, this, &CodeWidget::UpdateSymbols);
connect(m_code_view, &CodeViewWidget::BreakpointsChanged, this,
[this] { emit BreakpointsChanged(); });
connect(m_code_view, &CodeViewWidget::MemoryPatchesChanged, this,
[this] { emit MemoryPatchesChanged(); });
connect(m_code_view, &CodeViewWidget::UpdateCodeWidget, this, &CodeWidget::Update);

connect(m_code_view, &CodeViewWidget::RequestPPCComparison, this,
Expand Down
1 change: 1 addition & 0 deletions Source/Core/DolphinQt/Debugger/CodeWidget.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ class CodeWidget : public QDockWidget
void UpdateSymbols();
signals:
void BreakpointsChanged();
void MemoryPatchesChanged();
void RequestPPCComparison(u32 addr);
void ShowMemory(u32 address);

Expand Down
Loading