diff --git a/rpcs3/rpcs3qt/cheat_manager.cpp b/rpcs3/rpcs3qt/cheat_manager.cpp index 5724999771..8f4d82d9c9 100644 --- a/rpcs3/rpcs3qt/cheat_manager.cpp +++ b/rpcs3/rpcs3qt/cheat_manager.cpp @@ -7,6 +7,7 @@ #include #include "cheat_manager.h" +#include "memory_viewer_panel.h" #include "Emu/System.h" #include "Emu/Memory/vm.h" @@ -829,6 +830,7 @@ cheat_manager_dialog::cheat_manager_dialog(QWidget* parent) QMenu* menu = new QMenu(); QAction* add_to_cheat_list = new QAction(tr("Add to cheat list"), menu); + QAction* show_in_mem_viewer = new QAction(tr("Show in Memory Viewer"), menu); const u32 offset = offsets_found[current_row]; const cheat_type type = static_cast(cbx_cheat_search_type->currentIndex()); @@ -849,7 +851,13 @@ cheat_manager_dialog::cheat_manager_dialog(QWidget* parent) update_cheat_list(); }); + connect(show_in_mem_viewer, &QAction::triggered, this, [offset]() + { + memory_viewer_panel::ShowAtPC(offset); + }); + menu->addAction(add_to_cheat_list); + menu->addAction(show_in_mem_viewer); menu->exec(globalPos); }); diff --git a/rpcs3/rpcs3qt/debugger_frame.cpp b/rpcs3/rpcs3qt/debugger_frame.cpp index 2136c6c9bd..502a01dc09 100644 --- a/rpcs3/rpcs3qt/debugger_frame.cpp +++ b/rpcs3/rpcs3qt/debugger_frame.cpp @@ -32,6 +32,8 @@ #include #include #include +#include +#include #include #include @@ -125,7 +127,8 @@ debugger_frame::debugger_frame(std::shared_ptr gui_settings, QWidg m_regs = new QPlainTextEdit(this); m_regs->setLineWrapMode(QPlainTextEdit::NoWrap); m_regs->setTextInteractionFlags(Qt::TextSelectableByMouse | Qt::TextSelectableByKeyboard); - + m_regs->setContextMenuPolicy(Qt::CustomContextMenu); + m_debugger_list->setFont(m_mono); m_misc_state->setFont(m_mono); m_regs->setFont(m_mono); @@ -158,6 +161,8 @@ debugger_frame::debugger_frame(std::shared_ptr gui_settings, QWidg body->setLayout(vbox_p_main); setWidget(body); + connect(m_regs, &QPlainTextEdit::customContextMenuRequested, this, &debugger_frame::OnRegsContextMenu); + connect(m_go_to_addr, &QAbstractButton::clicked, this, &debugger_frame::ShowGotoAddressDialog); connect(m_go_to_pc, &QAbstractButton::clicked, this, [this]() { ShowPC(true); }); @@ -1702,3 +1707,36 @@ void debugger_frame::EnableButtons(bool enable) m_btn_step_over->setEnabled(step); m_btn_run->setEnabled(enable); } + +void debugger_frame::OnRegsContextMenu(const QPoint& pos) +{ + QMenu* menu = m_regs->createStandardContextMenu(); + QAction* memory_viewer_action = new QAction(tr("Show in Memory Viewer"), menu); + + connect(memory_viewer_action, &QAction::triggered, this, &debugger_frame::RegsShowMemoryViewerAction); + + menu->addSeparator(); + menu->addAction(memory_viewer_action); + menu->exec(m_regs->mapToGlobal(pos)); +} + +void debugger_frame::RegsShowMemoryViewerAction() +{ + const QTextCursor cursor = m_regs->textCursor(); + if (!cursor.hasSelection()) + { + QMessageBox::warning(this, tr("No Selection"), tr("Please select a hex value first.")); + return; + } + + const QTextDocumentFragment frag(cursor); + const QString selected = frag.toPlainText().trimmed(); + u64 pc = 0; + if (!parse_hex_qstring(selected, &pc)) + { + QMessageBox::critical(this, tr("Invalid Hex"), tr("“%0” is not a valid 32-bit hex value.").arg(selected)); + return; + } + + memory_viewer_panel::ShowAtPC(static_cast(pc), make_check_cpu(get_cpu())); +} diff --git a/rpcs3/rpcs3qt/debugger_frame.h b/rpcs3/rpcs3qt/debugger_frame.h index 46df22e3c8..6837347c3f 100644 --- a/rpcs3/rpcs3qt/debugger_frame.h +++ b/rpcs3/rpcs3qt/debugger_frame.h @@ -137,9 +137,11 @@ public Q_SLOTS: private Q_SLOTS: void OnSelectUnit(); void OnSelectSPUDisassembler(); + void OnRegsContextMenu(const QPoint& pos); void ShowPC(bool user_requested = false); void EnableUpdateTimer(bool enable) const; void RunBtnPress(); + void RegsShowMemoryViewerAction(); }; Q_DECLARE_METATYPE(u32) diff --git a/rpcs3/rpcs3qt/hex_validator.h b/rpcs3/rpcs3qt/hex_validator.h index 68af698bfb..e11624483d 100644 --- a/rpcs3/rpcs3qt/hex_validator.h +++ b/rpcs3/rpcs3qt/hex_validator.h @@ -69,3 +69,23 @@ inline QString normalize_hex_qstring(const QString& input) s.chop(1); return s; } + +inline bool parse_hex_qstring(const QString& input, u64* result, int max_bits = 32) +{ + QString s = input; + int pos = 0; + const HexValidator validator(nullptr, max_bits); + const QValidator::State st = validator.validate(s, pos); + + if (st != QValidator::Acceptable) + return false; + + const QString norm = normalize_hex_qstring(input); + bool ok = false; + const quint64 value = norm.toULongLong(&ok, 16); + + if (ok && result) + *result = static_cast(value); + + return ok; +} diff --git a/rpcs3/rpcs3qt/log_frame.cpp b/rpcs3/rpcs3qt/log_frame.cpp index 50ddb13f1c..fd294553ff 100644 --- a/rpcs3/rpcs3qt/log_frame.cpp +++ b/rpcs3/rpcs3qt/log_frame.cpp @@ -1,6 +1,8 @@ #include "log_frame.h" #include "qt_utils.h" #include "gui_settings.h" +#include "hex_validator.h" +#include "memory_viewer_panel.h" #include "Utilities/lockless.h" #include "util/asm.hpp" @@ -250,6 +252,21 @@ void log_frame::CreateAndConnectActions() Q_EMIT PerformGoToOnDebugger(pte->textCursor().selectedText(), true); }); + m_perform_show_in_mem_viewer = new QAction(tr("Show in Memory Viewer"), this); + connect(m_perform_show_in_mem_viewer, &QAction::triggered, this, [this]() + { + const QPlainTextEdit* pte = (m_tabWidget->currentIndex() == 1 ? m_tty : m_log); + const QString selected = pte->textCursor().selectedText(); + u64 pc = 0; + if (!parse_hex_qstring(selected, &pc)) + { + QMessageBox::critical(this, tr("Invalid Hex"), tr("“%0” is not a valid 32-bit hex value.").arg(selected)); + return; + } + + memory_viewer_panel::ShowAtPC(static_cast(pc)); + }); + m_perform_goto_thread_on_debugger = new QAction(tr("Show Thread On The Debugger"), this); connect(m_perform_goto_thread_on_debugger, &QAction::triggered, [this]() { @@ -354,13 +371,16 @@ void log_frame::CreateAndConnectActions() menu->addAction(m_clear_act); menu->addAction(m_perform_goto_on_debugger); menu->addAction(m_perform_goto_thread_on_debugger); + menu->addAction(m_perform_show_in_mem_viewer); std::shared_ptr goto_signal_accepted = std::make_shared(false); Q_EMIT PerformGoToOnDebugger("", true, true, goto_signal_accepted); m_perform_goto_on_debugger->setEnabled(m_log->textCursor().hasSelection() && *goto_signal_accepted); m_perform_goto_thread_on_debugger->setEnabled(m_log->textCursor().hasSelection() && *goto_signal_accepted); + m_perform_show_in_mem_viewer->setEnabled(m_log->textCursor().hasSelection() && *goto_signal_accepted); m_perform_goto_on_debugger->setToolTip(tr("Jump to the selected hexadecimal address from the log text on the debugger.")); m_perform_goto_thread_on_debugger->setToolTip(tr("Show the thread that corresponds to the thread ID from the log text on the debugger.")); + m_perform_show_in_mem_viewer->setToolTip(tr("Jump to the selected hexadecimal address from the log text on the memory viewer.")); menu->addSeparator(); menu->addActions(m_log_level_acts->actions()); @@ -376,11 +396,14 @@ void log_frame::CreateAndConnectActions() QMenu* menu = m_tty->createStandardContextMenu(); menu->addAction(m_clear_tty_act); menu->addAction(m_perform_goto_on_debugger); + menu->addAction(m_perform_show_in_mem_viewer); std::shared_ptr goto_signal_accepted = std::make_shared(false); Q_EMIT PerformGoToOnDebugger("", false, true, goto_signal_accepted); m_perform_goto_on_debugger->setEnabled(m_tty->textCursor().hasSelection() && *goto_signal_accepted); + m_perform_show_in_mem_viewer->setEnabled(m_tty->textCursor().hasSelection() && *goto_signal_accepted); m_perform_goto_on_debugger->setToolTip(tr("Jump to the selected hexadecimal address from the TTY text on the debugger.")); + m_perform_show_in_mem_viewer->setToolTip(tr("Jump to the selected hexadecimal address from the TTY text on the memory viewer.")); menu->addSeparator(); menu->addAction(m_tty_act); diff --git a/rpcs3/rpcs3qt/log_frame.h b/rpcs3/rpcs3qt/log_frame.h index 7909990c27..0de081863c 100644 --- a/rpcs3/rpcs3qt/log_frame.h +++ b/rpcs3/rpcs3qt/log_frame.h @@ -74,6 +74,7 @@ private: QAction* m_clear_tty_act = nullptr; QAction* m_perform_goto_on_debugger = nullptr; QAction* m_perform_goto_thread_on_debugger = nullptr; + QAction* m_perform_show_in_mem_viewer = nullptr; QActionGroup* m_log_level_acts = nullptr; QAction* m_nothing_act = nullptr; diff --git a/rpcs3/rpcs3qt/memory_viewer_panel.cpp b/rpcs3/rpcs3qt/memory_viewer_panel.cpp index 8905744e77..392b3e0385 100644 --- a/rpcs3/rpcs3qt/memory_viewer_panel.cpp +++ b/rpcs3/rpcs3qt/memory_viewer_panel.cpp @@ -9,6 +9,7 @@ #include "Emu/RSX/RSXThread.h" #include "Emu/RSX/rsx_utils.h" #include "Emu/IdManager.h" +#include "Emu/System.h" #include #include #include @@ -25,6 +26,7 @@ #include "util/logs.hpp" #include "util/asm.hpp" +#include "debugger_frame.h" LOG_CHANNEL(gui_log, "GUI"); @@ -589,6 +591,24 @@ memory_viewer_panel::memory_viewer_panel(QWidget* parent, std::shared_ptr(id, handle_ptr); }); + + if (!g_fxo->try_get()) + { + g_fxo->init(); + } + + auto& fxo = g_fxo->get(); + fxo.last_opened[m_type] = this; + + connect(this, &memory_viewer_panel::destroyed, this, [this]() + { + if (auto fxo = g_fxo->try_get()) + { + auto it = fxo->last_opened.find(m_type); + if (it != fxo->last_opened.end() && it->second == this) + fxo->last_opened.erase(it); + } + }); } memory_viewer_panel::~memory_viewer_panel() @@ -1244,3 +1264,43 @@ void memory_viewer_panel::ShowImage(QWidget* parent, u32 addr, color_format form f_image_viewer->setFixedSize(f_image_viewer->sizeHint()); }); } + +void memory_viewer_panel::ShowAtPC(u32 pc, std::function func) +{ + if (Emu.IsStopped()) + return; + + cpu_thread* cpu = func ? func() : nullptr; + thread_class type = cpu ? cpu->get_class() : thread_class::ppu; + + if (type == thread_class::spu) + { + idm::make(nullptr, nullptr, pc, std::move(func)); + return; + } + + if (const auto* fxo = g_fxo->try_get()) + { + auto it = fxo->last_opened.find(type); + + if (it != fxo->last_opened.end()) + { + memory_viewer_panel* panel = it->second; + + if (panel) + { + panel->SetPC(pc); + panel->scroll(0); + + if (!panel->isVisible()) + panel->show(); + + panel->raise(); + + return; + } + } + } + + idm::make(nullptr, nullptr, pc, std::move(func)); +} diff --git a/rpcs3/rpcs3qt/memory_viewer_panel.h b/rpcs3/rpcs3qt/memory_viewer_panel.h index dc59247991..756323be97 100644 --- a/rpcs3/rpcs3qt/memory_viewer_panel.h +++ b/rpcs3/rpcs3qt/memory_viewer_panel.h @@ -10,6 +10,7 @@ #include #include +#include class QLineEdit; class QCheckBox; @@ -55,6 +56,8 @@ public: memory_viewer_panel(QWidget* parent, std::shared_ptr disasm, u32 addr = 0, std::function func = []() -> cpu_thread* { return {}; }); ~memory_viewer_panel(); + static void ShowAtPC(u32 pc, std::function func = nullptr); + enum class color_format : int { RGB, @@ -137,3 +140,12 @@ struct memory_viewer_handle private: const std::add_pointer_t m_mvp; }; + +struct memory_viewer_fxo +{ + std::map last_opened; + + memory_viewer_fxo() = default; + memory_viewer_fxo(const memory_viewer_fxo&) = delete; + memory_viewer_fxo& operator=(const memory_viewer_fxo&) = delete; +};