From e79fc867c5e690f639c88a68e968f372c44bd095 Mon Sep 17 00:00:00 2001 From: Eladash Date: Mon, 25 Sep 2023 18:32:50 +0300 Subject: [PATCH] Patches: Add savable breakpoints patch type --- Utilities/bin_patch.cpp | 14 ++++++++++++++ Utilities/bin_patch.h | 1 + rpcs3/Emu/System.h | 1 + rpcs3/headless_application.cpp | 1 + rpcs3/rpcs3qt/breakpoint_list.cpp | 7 +++++-- rpcs3/rpcs3qt/breakpoint_list.h | 2 +- rpcs3/rpcs3qt/debugger_frame.cpp | 5 +++++ rpcs3/rpcs3qt/debugger_frame.h | 1 + rpcs3/rpcs3qt/debugger_list.h | 2 +- rpcs3/rpcs3qt/gui_application.cpp | 8 ++++++++ rpcs3/rpcs3qt/main_window.cpp | 8 ++++++++ rpcs3/rpcs3qt/main_window.h | 1 + 12 files changed, 47 insertions(+), 4 deletions(-) diff --git a/Utilities/bin_patch.cpp b/Utilities/bin_patch.cpp index 5d89beb79d..3162e0687a 100644 --- a/Utilities/bin_patch.cpp +++ b/Utilities/bin_patch.cpp @@ -76,6 +76,7 @@ void fmt_class_string::format(std::string& out, u64 arg) case patch_type::bd64: return "bd64"; case patch_type::lef32: return "lef32"; case patch_type::lef64: return "lef64"; + case patch_type::bp_exec: return "bpex"; case patch_type::utf8: return "utf8"; case patch_type::c_utf8: return "cutf8"; case patch_type::move_file: return "move_file"; @@ -755,6 +756,7 @@ bool patch_engine::add_patch_data(YAML::Node node, patch_info& info, u32 modifie switch (p_data.type) { + case patch_type::bp_exec: case patch_type::utf8: case patch_type::jump_func: case patch_type::move_file: @@ -1008,6 +1010,7 @@ static usz apply_modification(std::basic_string& applied, patch_engine::pat case patch_type::jump: case patch_type::jump_link: case patch_type::jump_func: + case patch_type::bp_exec: case patch_type::le32: case patch_type::lef32: case patch_type::bd32: @@ -1316,6 +1319,17 @@ static usz apply_modification(std::basic_string& applied, patch_engine::pat std::memcpy(ptr, &val, sizeof(val)); break; } + case patch_type::bp_exec: + { + const u32 exec_addr = vm::try_get_addr(relocate_instructions_at ? vm::get_super_ptr(offset & -4) : mem_translate(offset & -4, 4)).first; + + if (exec_addr) + { + Emu.GetCallbacks().add_breakpoint(exec_addr); + } + + break; + } case patch_type::utf8: { std::memcpy(ptr, p.original_value.data(), p.original_value.size()); diff --git a/Utilities/bin_patch.h b/Utilities/bin_patch.h index 41f675c3f7..336685c97f 100644 --- a/Utilities/bin_patch.h +++ b/Utilities/bin_patch.h @@ -52,6 +52,7 @@ enum class patch_type bd64, // be64 with data hint (non-code) bef32, bef64, + bp_exec, // Execution Breakpoint utf8, // Text of string (not null-terminated automatically) c_utf8, // Text of string (null-terminated automatically) move_file, // Move file diff --git a/rpcs3/Emu/System.h b/rpcs3/Emu/System.h index 7512b54047..6a8c9595ce 100644 --- a/rpcs3/Emu/System.h +++ b/rpcs3/Emu/System.h @@ -91,6 +91,7 @@ struct EmuCallbacks std::string(*resolve_path)(std::string_view) = [](std::string_view arg){ return std::string{arg}; }; // Resolve path using Qt std::function()> get_font_dirs; std::function&)> on_install_pkgs; + std::function add_breakpoint; }; namespace utils diff --git a/rpcs3/headless_application.cpp b/rpcs3/headless_application.cpp index 763987be3a..b10586daee 100644 --- a/rpcs3/headless_application.cpp +++ b/rpcs3/headless_application.cpp @@ -159,6 +159,7 @@ void headless_application::InitializeCallbacks() callbacks.get_localized_u32string = [](localized_string_id, const char*) -> std::u32string { return {}; }; callbacks.play_sound = [](const std::string&){}; + callbacks.add_breakpoint = [](u32 /*addr*/){}; Emu.SetCallbacks(std::move(callbacks)); } diff --git a/rpcs3/rpcs3qt/breakpoint_list.cpp b/rpcs3/rpcs3qt/breakpoint_list.cpp index ef6f39e362..008ecc4df1 100644 --- a/rpcs3/rpcs3qt/breakpoint_list.cpp +++ b/rpcs3/rpcs3qt/breakpoint_list.cpp @@ -100,7 +100,7 @@ bool breakpoint_list::AddBreakpoint(u32 pc) /** * If breakpoint exists, we remove it, else add new one. Yeah, it'd be nicer from a code logic to have it be set/reset. But, that logic has to happen somewhere anyhow. */ -void breakpoint_list::HandleBreakpointRequest(u32 loc) +void breakpoint_list::HandleBreakpointRequest(u32 loc, bool only_add) { if (!m_cpu || m_cpu->state & cpu_flag::exit) { @@ -167,7 +167,10 @@ void breakpoint_list::HandleBreakpointRequest(u32 loc) if (m_ppu_breakpoint_handler->HasBreakpoint(loc)) { - RemoveBreakpoint(loc); + if (!only_add) + { + RemoveBreakpoint(loc); + } } else { diff --git a/rpcs3/rpcs3qt/breakpoint_list.h b/rpcs3/rpcs3qt/breakpoint_list.h index 6ea591377e..ff156c5655 100644 --- a/rpcs3/rpcs3qt/breakpoint_list.h +++ b/rpcs3/rpcs3qt/breakpoint_list.h @@ -24,7 +24,7 @@ public: Q_SIGNALS: void RequestShowAddress(u32 addr, bool select_addr = true, bool force = false); public Q_SLOTS: - void HandleBreakpointRequest(u32 loc); + void HandleBreakpointRequest(u32 loc, bool add_only); private Q_SLOTS: void OnBreakpointListDoubleClicked(); void OnBreakpointListRightClicked(const QPoint &pos); diff --git a/rpcs3/rpcs3qt/debugger_frame.cpp b/rpcs3/rpcs3qt/debugger_frame.cpp index 19bbe6fa2c..60bdd0b9f3 100644 --- a/rpcs3/rpcs3qt/debugger_frame.cpp +++ b/rpcs3/rpcs3qt/debugger_frame.cpp @@ -1250,6 +1250,11 @@ void debugger_frame::PerformGoToRequest(const QString& text_argument) } } +void debugger_frame::PerformAddBreakpointRequest(u32 addr) +{ + m_debugger_list->BreakpointRequested(addr, true); +} + u64 debugger_frame::EvaluateExpression(const QString& expression) { bool ok = false; diff --git a/rpcs3/rpcs3qt/debugger_frame.h b/rpcs3/rpcs3qt/debugger_frame.h index a0381f9898..d8af2f166a 100644 --- a/rpcs3/rpcs3qt/debugger_frame.h +++ b/rpcs3/rpcs3qt/debugger_frame.h @@ -95,6 +95,7 @@ public: void EnableButtons(bool enable); void ShowGotoAddressDialog(); void PerformGoToRequest(const QString& text_argument); + void PerformAddBreakpointRequest(u32 addr); u64 EvaluateExpression(const QString& expression); void ClearBreakpoints() const; // Fallthrough method into breakpoint_list. void ClearCallStack(); diff --git a/rpcs3/rpcs3qt/debugger_list.h b/rpcs3/rpcs3qt/debugger_list.h index b95fa49c3b..c4b93f5799 100644 --- a/rpcs3/rpcs3qt/debugger_list.h +++ b/rpcs3/rpcs3qt/debugger_list.h @@ -30,7 +30,7 @@ public: QColor m_text_color_pc; Q_SIGNALS: - void BreakpointRequested(u32 loc); + void BreakpointRequested(u32 loc, bool only_add = false); public: debugger_list(QWidget* parent, std::shared_ptr settings, breakpoint_handler* handler); void UpdateCPUData(cpu_thread* cpu, CPUDisAsm* disasm); diff --git a/rpcs3/rpcs3qt/gui_application.cpp b/rpcs3/rpcs3qt/gui_application.cpp index b3746d4389..6604f1ad99 100644 --- a/rpcs3/rpcs3qt/gui_application.cpp +++ b/rpcs3/rpcs3qt/gui_application.cpp @@ -638,6 +638,14 @@ void gui_application::InitializeCallbacks() }); }; + callbacks.add_breakpoint = [this](u32 addr) + { + Emu.BlockingCallFromMainThread([this, addr]() + { + m_main_window->OnAddBreakpoint(addr); + }); + }; + Emu.SetCallbacks(std::move(callbacks)); } diff --git a/rpcs3/rpcs3qt/main_window.cpp b/rpcs3/rpcs3qt/main_window.cpp index d0624259c7..ca6541e454 100644 --- a/rpcs3/rpcs3qt/main_window.cpp +++ b/rpcs3/rpcs3qt/main_window.cpp @@ -2007,6 +2007,14 @@ void main_window::EnableMenus(bool enabled) const ui->actionCreate_Savestate->setEnabled(enabled); } +void main_window::OnAddBreakpoint(u32 addr) const +{ + if (m_debugger_frame) + { + m_debugger_frame->PerformAddBreakpointRequest(addr); + } +} + void main_window::OnEnableDiscEject(bool enabled) const { ui->ejectDiscAct->setEnabled(enabled); diff --git a/rpcs3/rpcs3qt/main_window.h b/rpcs3/rpcs3qt/main_window.h index a776e60412..668d47e94d 100644 --- a/rpcs3/rpcs3qt/main_window.h +++ b/rpcs3/rpcs3qt/main_window.h @@ -106,6 +106,7 @@ public Q_SLOTS: void OnEmuReady() const; void OnEnableDiscEject(bool enabled) const; void OnEnableDiscInsert(bool enabled) const; + void OnAddBreakpoint(u32 addr) const; void RepaintGui(); void RetranslateUI(const QStringList& language_codes, const QString& language);