diff --git a/rpcs3/rpcs3.vcxproj b/rpcs3/rpcs3.vcxproj
index b32bc2d2b1..6e18b0d78d 100644
--- a/rpcs3/rpcs3.vcxproj
+++ b/rpcs3/rpcs3.vcxproj
@@ -284,9 +284,6 @@
true
-
- true
-
true
@@ -524,9 +521,6 @@
true
-
- true
-
true
@@ -881,16 +875,6 @@
$(QTDIR)\bin\moc.exe;%(FullPath);$(QTDIR)\bin\moc.exe;%(FullPath)
$(QTDIR)\bin\moc.exe;%(FullPath);$(QTDIR)\bin\moc.exe;%(FullPath)
-
- Moc%27ing memory_string_searcher.h...
- .\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp
- "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -D_WINDOWS -DUNICODE -DWIN32 -DWIN64 -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_CORE_LIB -DQT_WINEXTRAS_LIB -DQT_CONCURRENT_LIB -D%(PreprocessorDefinitions) "-I.\..\3rdparty\wolfssl\wolfssl" "-I.\..\3rdparty\curl\curl\include" "-I.\..\3rdparty\libusb\libusb\libusb" "-I$(VULKAN_SDK)\Include" "-I.\..\3rdparty\XAudio2Redist\include" "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtWidgets" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtANGLE" "-I$(QTDIR)\include\QtCore" "-I.\debug" "-I$(QTDIR)\mkspecs\win32-msvc2015" "-I.\QTGeneratedFiles\$(ConfigurationName)" "-I.\QTGeneratedFiles" "-I$(QTDIR)\include\QtWinExtras" "-I$(QTDIR)\include\QtConcurrent"
- Moc%27ing memory_string_searcher.h...
- .\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp
- "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -D_WINDOWS -DUNICODE -DWIN32 -DWIN64 -DWITH_DISCORD_RPC -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_CORE_LIB -DNDEBUG -DQT_WINEXTRAS_LIB -DQT_CONCURRENT_LIB -D%(PreprocessorDefinitions) "-I.\..\3rdparty\wolfssl\wolfssl" "-I.\..\3rdparty\curl\curl\include" "-I.\..\3rdparty\libusb\libusb\libusb" "-I$(VULKAN_SDK)\Include" "-I.\..\3rdparty\XAudio2Redist\include" "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtWidgets" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtANGLE" "-I$(QTDIR)\include\QtCore" "-I.\release" "-I$(QTDIR)\mkspecs\win32-msvc2015" "-I.\QTGeneratedFiles\$(ConfigurationName)" "-I.\QTGeneratedFiles" "-I$(QTDIR)\include\QtWinExtras" "-I$(QTDIR)\include\QtConcurrent"
- $(QTDIR)\bin\moc.exe;%(FullPath);$(QTDIR)\bin\moc.exe;%(FullPath)
- $(QTDIR)\bin\moc.exe;%(FullPath);$(QTDIR)\bin\moc.exe;%(FullPath)
-
Moc%27ing gui_settings.h...
.\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp
diff --git a/rpcs3/rpcs3.vcxproj.filters b/rpcs3/rpcs3.vcxproj.filters
index 8c712658ba..22945286f5 100644
--- a/rpcs3/rpcs3.vcxproj.filters
+++ b/rpcs3/rpcs3.vcxproj.filters
@@ -1222,9 +1222,6 @@
Gui\game list
-
- Gui\dev tools
-
Gui\settings
diff --git a/rpcs3/rpcs3qt/debugger_frame.cpp b/rpcs3/rpcs3qt/debugger_frame.cpp
index 36f2a36688..59d3aec7ed 100644
--- a/rpcs3/rpcs3qt/debugger_frame.cpp
+++ b/rpcs3/rpcs3qt/debugger_frame.cpp
@@ -2,7 +2,6 @@
#include "register_editor_dialog.h"
#include "instruction_editor_dialog.h"
#include "memory_viewer_panel.h"
-#include "memory_string_searcher.h"
#include "gui_settings.h"
#include "debugger_list.h"
#include "breakpoint_list.h"
@@ -290,7 +289,6 @@ void debugger_frame::keyPressEvent(QKeyEvent* event)
QLabel* l = new QLabel(tr(
"Keys Ctrl+G: Go to typed address."
"\nKeys Ctrl+B: Open breakpoints settings."
- "\nKeys Ctrl+S: Search memory string utility."
"\nKeys Alt+S: Capture SPU images of selected SPU."
"\nKeys Alt+R: Load last saved SPU state capture."
"\nKey D: SPU MFC commands logger, MFC debug setting must be enabled."
@@ -351,20 +349,6 @@ void debugger_frame::keyPressEvent(QKeyEvent* event)
open_breakpoints_settings();
return;
}
- case Qt::Key_S:
- {
- if (m_disasm && (cpu->id_type() == 2 || cpu->id_type() == 1))
- {
- if (cpu->id_type() == 2)
- {
- // Save shared pointer to shared memory handle, ensure the destructor will not be called until the SPUDisAsm is destroyed
- static_cast(m_disasm.get())->set_shm(static_cast(cpu)->shm);
- }
-
- idm::make(this, m_disasm, cpu->id_type() == 2 ? cpu->get_name() : "");
- }
- return;
- }
default: break;
}
}
@@ -638,8 +622,14 @@ void debugger_frame::keyPressEvent(QKeyEvent* event)
if (event->isAutoRepeat())
return;
+ if (m_disasm && cpu->id_type() == 2)
+ {
+ // Save shared pointer to shared memory handle, ensure the destructor will not be called until the SPUDisAsm is destroyed
+ static_cast(m_disasm.get())->set_shm(static_cast(cpu)->shm);
+ }
+
// Memory viewer
- idm::make(this, pc, make_check_cpu(cpu));
+ idm::make(this, m_disasm, pc, make_check_cpu(cpu));
return;
}
case Qt::Key_F10:
diff --git a/rpcs3/rpcs3qt/main_window.cpp b/rpcs3/rpcs3qt/main_window.cpp
index 771861924f..2e346fa868 100644
--- a/rpcs3/rpcs3qt/main_window.cpp
+++ b/rpcs3/rpcs3qt/main_window.cpp
@@ -14,7 +14,6 @@
#include "auto_pause_settings_dialog.h"
#include "cg_disasm_window.h"
#include "log_viewer.h"
-#include "memory_string_searcher.h"
#include "memory_viewer_panel.h"
#include "rsx_debugger.h"
#include "about_dialog.h"
@@ -1904,7 +1903,6 @@ void main_window::EnableMenus(bool enabled) const
ui->toolskernel_explorerAct->setEnabled(enabled);
ui->toolsmemory_viewerAct->setEnabled(enabled);
ui->toolsRsxDebuggerAct->setEnabled(enabled);
- ui->toolsStringSearchAct->setEnabled(enabled);
ui->toolsSystemCommandsAct->setEnabled(enabled);
ui->actionCreate_RSX_Capture->setEnabled(enabled);
ui->actionCreate_Savestate->setEnabled(enabled);
@@ -2519,7 +2517,7 @@ void main_window::CreateConnects()
connect(ui->toolsmemory_viewerAct, &QAction::triggered, this, [this]
{
if (!Emu.IsStopped())
- idm::make(this);
+ idm::make(this, make_basic_ppu_disasm());
});
connect(ui->toolsRsxDebuggerAct, &QAction::triggered, this, [this]
@@ -2528,12 +2526,6 @@ void main_window::CreateConnects()
rsx->show();
});
- connect(ui->toolsStringSearchAct, &QAction::triggered, this, [this]
- {
- if (!Emu.IsStopped())
- idm::make(this, make_basic_ppu_disasm());
- });
-
connect(ui->toolsSystemCommandsAct, &QAction::triggered, this, [this]
{
if (Emu.IsStopped()) return;
diff --git a/rpcs3/rpcs3qt/main_window.ui b/rpcs3/rpcs3qt/main_window.ui
index 99d33c2a85..a98bb750bc 100644
--- a/rpcs3/rpcs3qt/main_window.ui
+++ b/rpcs3/rpcs3qt/main_window.ui
@@ -274,7 +274,6 @@
-
@@ -646,14 +645,6 @@
RSX Debugger
-
-
- false
-
-
- Memory Searcher
-
-
Decrypt PS3 Binaries
diff --git a/rpcs3/rpcs3qt/memory_string_searcher.cpp b/rpcs3/rpcs3qt/memory_string_searcher.cpp
index 0ea3ff2bfe..0136987de0 100644
--- a/rpcs3/rpcs3qt/memory_string_searcher.cpp
+++ b/rpcs3/rpcs3qt/memory_string_searcher.cpp
@@ -1,4 +1,4 @@
-#include "memory_string_searcher.h"
+#include "memory_viewer_panel.h"
#include "Emu/Memory/vm.h"
#include "Emu/Memory/vm_reservation.h"
#include "Emu/CPU/CPUDisAsm.h"
@@ -26,17 +26,6 @@ LOG_CHANNEL(gui_log, "GUI");
constexpr auto qstr = QString::fromStdString;
-enum search_mode : int
-{
- no_mode = 1,
- as_string = 2,
- as_hex = 4,
- as_f64 = 8,
- as_f32 = 16,
- as_inst = 32,
- as_fake_spu_inst = 64,
-};
-
template <>
void fmt_class_string::format(std::string& out, u64 arg)
{
@@ -45,9 +34,9 @@ void fmt_class_string::format(std::string& out, u64 arg)
out += "No search modes have been selected";
}
- for (int modes = static_cast(arg); modes; modes &= modes - 1)
+ for (u32 modes = static_cast(arg); modes; modes &= modes - 1)
{
- const int mode = modes & ~(modes - 1);
+ const u32 mode = modes & ~(modes - 1);
auto mode_s = [&]() -> std::string_view
{
@@ -68,7 +57,7 @@ void fmt_class_string::format(std::string& out, u64 arg)
break;
}
- if (modes != static_cast(arg))
+ if (modes != static_cast(arg))
{
out += ", ";
}
@@ -77,125 +66,13 @@ void fmt_class_string::format(std::string& out, u64 arg)
}
}
-memory_string_searcher::memory_string_searcher(QWidget* parent, std::shared_ptr disasm, std::string_view title)
- : QDialog(parent)
- , m_disasm(std::move(disasm))
+u64 memory_viewer_panel::OnSearch(std::string wstr, u32 mode)
{
- if (title.empty())
+ if (m_rsx)
{
- setWindowTitle(tr("Memory Searcher"));
- }
- else
- {
- setWindowTitle(tr("Memory Searcher Of %1").arg(title.data()));
+ return 0;
}
- setObjectName("memory_string_searcher");
- setAttribute(Qt::WA_DeleteOnClose);
-
- // Extract memory view from the disassembler
- std::tie(m_ptr, m_size) = m_disasm->get_memory_span();
-
- m_addr_line = new QLineEdit(this);
- m_addr_line->setFixedWidth(QLabel("This is the very length of the lineedit due to hidpi reasons.").sizeHint().width());
- m_addr_line->setPlaceholderText(tr("Search..."));
- m_addr_line->setMaxLength(4096);
-
- QPushButton* button_search = new QPushButton(tr("&Search"), this);
-
- m_chkbox_case_insensitive = new QCheckBox(tr("Case Insensitive"), this);
- m_chkbox_case_insensitive->setCheckable(true);
- m_chkbox_case_insensitive->setToolTip(tr("When using string mode, the characters' case will not matter both in string and in memory."
- "\nWarning: this may reduce performance of the search."));
-
- m_cbox_input_mode = new QComboBox(this);
- m_cbox_input_mode->addItem(tr("Select search mode(s).."), QVariant::fromValue(+no_mode));
- m_cbox_input_mode->addItem(tr("String"), QVariant::fromValue(+as_string));
- m_cbox_input_mode->addItem(tr("HEX bytes/integer"), QVariant::fromValue(+as_hex));
- m_cbox_input_mode->addItem(tr("Double"), QVariant::fromValue(+as_f64));
- m_cbox_input_mode->addItem(tr("Float"), QVariant::fromValue(+as_f32));
- m_cbox_input_mode->addItem(tr("Instruction"), QVariant::fromValue(+as_inst));
-
- QString tooltip = tr("String: search the memory for the specified string."
- "\nHEX bytes/integer: search the memory for hexadecimal values. Spaces, commas, \"0x\", \"0X\", \"\\x\", \"h\", \"H\" ensure separation of bytes but they are not mandatory."
- "\nDouble: reinterpret the string as 64-bit precision floating point value. Values are searched for exact representation, meaning -0 != 0."
- "\nFloat: reinterpret the string as 32-bit precision floating point value. Values are searched for exact representation, meaning -0 != 0."
- "\nInstruction: search an instruction contains the text of the string.");
-
- if (m_size != 0x40000/*SPU_LS_SIZE*/)
- {
- m_cbox_input_mode->addItem("SPU Instruction", QVariant::fromValue(+as_fake_spu_inst));
- tooltip.append(tr("\nSPU Instruction: Search an SPU instruction contains the text of the string. For searching instructions within embedded SPU images.\nTip: SPU floats are commented along forming instructions."));
- }
-
- connect(m_cbox_input_mode, QOverload::of(&QComboBox::currentIndexChanged), this, [this](int index)
- {
- if (index < 0) return;
-
- if ((1 << index) == no_mode)
- {
- m_modes = {};
- }
- else
- {
- m_modes = search_mode{m_modes | (1 << index)};
- }
-
- m_modes_label->setText(qstr(fmt::format("%s.", m_modes)));
- });
-
- m_cbox_input_mode->setToolTip(tooltip);
-
- m_modes_label = new QLabel(qstr(fmt::format("%s.", m_modes)));
-
- QHBoxLayout* hbox_panel = new QHBoxLayout();
- hbox_panel->addWidget(m_addr_line);
- hbox_panel->addWidget(m_cbox_input_mode);
- hbox_panel->addWidget(m_chkbox_case_insensitive);
- hbox_panel->addWidget(button_search);
-
- QVBoxLayout* vbox_panel = new QVBoxLayout();
- vbox_panel->addLayout(hbox_panel);
- vbox_panel->addWidget(m_modes_label);
-
- setLayout(vbox_panel);
-
- connect(button_search, &QAbstractButton::clicked, this, [this]()
- {
- std::string wstr = m_addr_line->text().toStdString();
-
- if (wstr.empty() || wstr.size() >= 4096u)
- {
- gui_log.error("String is empty or too long (size=%u)", wstr.size());
- return;
- }
-
- gui_log.notice("Searching for %s (mode: %s)", wstr, m_modes);
-
- u64 found = 0;
-
- for (int modes = m_modes; modes; modes &= modes - 1)
- {
- found += OnSearch(wstr, modes & ~(modes - 1));
- }
-
- gui_log.success("Search completed (found %u matches)", +found);
- });
-
- layout()->setSizeConstraint(QLayout::SetFixedSize);
-
- // Show by default
- show();
-
- // Expected to be created by IDM, emulation stop will close it
- connect(this, &memory_string_searcher::finished, [id = idm::last_id()](int)
- {
- idm::remove(id);
- });
-}
-
-u64 memory_string_searcher::OnSearch(std::string wstr, int mode)
-{
bool case_insensitive = false;
// First characters for case insensitive search
@@ -250,7 +127,7 @@ u64 memory_string_searcher::OnSearch(std::string wstr, int mode)
if (const usz pos = wstr.find_first_not_of(hex_chars); pos != umax)
{
gui_log.error("String '%s' cannot be interpreted as hexadecimal byte string due to unknown character '%c'.",
- m_addr_line->text().toStdString(), wstr[pos]);
+ m_search_line->text().toStdString(), wstr[pos]);
return 0;
}
diff --git a/rpcs3/rpcs3qt/memory_string_searcher.h b/rpcs3/rpcs3qt/memory_string_searcher.h
deleted file mode 100644
index c30d6d6572..0000000000
--- a/rpcs3/rpcs3qt/memory_string_searcher.h
+++ /dev/null
@@ -1,57 +0,0 @@
-#pragma once
-
-#include "util/types.hpp"
-
-#include
-
-class QLineEdit;
-class QCheckBox;
-class QComboBox;
-class QLabel;
-
-class CPUDisAsm;
-
-enum search_mode : int;
-
-class memory_string_searcher : public QDialog
-{
- Q_OBJECT
-
- QLineEdit* m_addr_line = nullptr;
- QCheckBox* m_chkbox_case_insensitive = nullptr;
- QComboBox* m_cbox_input_mode = nullptr;
- QLabel* m_modes_label = nullptr;
-
- std::shared_ptr m_disasm;
-
- const void* m_ptr;
- usz m_size;
-
- search_mode m_modes{};
-
-public:
- explicit memory_string_searcher(QWidget* parent, std::shared_ptr disasm, std::string_view title = {});
-
-private:
- u64 OnSearch(std::string wstr, int mode);
-};
-
-// Lifetime management with IDM
-struct memory_searcher_handle
-{
- static constexpr u32 id_base = 1;
- static constexpr u32 id_step = 1;
- static constexpr u32 id_count = 2048;
- SAVESTATE_INIT_POS(32); // Of course not really used
-
- template requires (std::is_constructible_v)
- memory_searcher_handle(Args&&... args)
- : m_mss(new memory_string_searcher(std::forward(args)...))
- {
- }
-
- ~memory_searcher_handle() { m_mss->close(); m_mss->deleteLater(); }
-
-private:
- const std::add_pointer_t m_mss;
-};
diff --git a/rpcs3/rpcs3qt/memory_viewer_panel.cpp b/rpcs3/rpcs3qt/memory_viewer_panel.cpp
index 62df12ae8a..4128849327 100644
--- a/rpcs3/rpcs3qt/memory_viewer_panel.cpp
+++ b/rpcs3/rpcs3qt/memory_viewer_panel.cpp
@@ -5,6 +5,8 @@
#include "memory_viewer_panel.h"
#include "Emu/Cell/SPUThread.h"
+#include "Emu/CPU/CPUDisAsm.h"
+#include "Emu/Cell/SPUDisAsm.h"
#include "Emu/RSX/RSXThread.h"
#include "Emu/RSX/rsx_utils.h"
#include "Emu/IdManager.h"
@@ -14,14 +16,20 @@
#include
#include
#include
+#include
#include
+#include
+#include
+#include "util/logs.hpp"
#include "util/asm.hpp"
#include "util/vm.hpp"
+LOG_CHANNEL(gui_log, "GUI");
+
constexpr auto qstr = QString::fromStdString;
-memory_viewer_panel::memory_viewer_panel(QWidget* parent, u32 addr, std::function func)
+memory_viewer_panel::memory_viewer_panel(QWidget* parent, std::shared_ptr disasm, u32 addr, std::function func)
: QDialog(parent)
, m_addr(addr)
, m_get_cpu(std::move(func))
@@ -43,6 +51,7 @@ memory_viewer_panel::memory_viewer_panel(QWidget* parent, u32 addr, std::functio
return cpu && m_type == thread_type::spu ? static_cast(cpu)->shm : nullptr;
}())
, m_addr_mask(m_type == thread_type::spu ? SPU_LS_SIZE - 1 : ~0)
+ , m_disasm(std::move(disasm))
{
const auto cpu = m_get_cpu();
@@ -53,7 +62,7 @@ memory_viewer_panel::memory_viewer_panel(QWidget* parent, u32 addr, std::functio
setObjectName("memory_viewer");
m_colcount = 4;
- m_rowcount = 16;
+ m_rowcount = 1;
const int pSize = 10;
// Font
@@ -62,18 +71,18 @@ memory_viewer_panel::memory_viewer_panel(QWidget* parent, u32 addr, std::functio
m_fontMetrics = new QFontMetrics(mono);
// Layout:
- QVBoxLayout* vbox_panel = new QVBoxLayout();
+ QVBoxLayout* vbox_panel = new QVBoxLayout(this);
// Tools
- QHBoxLayout* hbox_tools = new QHBoxLayout();
+ QHBoxLayout* hbox_tools = new QHBoxLayout(this);
// Tools: Memory Viewer Options
- QGroupBox* tools_mem = new QGroupBox(tr("Memory Viewer Options"));
- QHBoxLayout* hbox_tools_mem = new QHBoxLayout();
+ QGroupBox* tools_mem = new QGroupBox(tr("Memory Viewer Options"), this);
+ QHBoxLayout* hbox_tools_mem = new QHBoxLayout(this);
// Tools: Memory Viewer Options: Address
- QGroupBox* tools_mem_addr = new QGroupBox(tr("Address"));
- QHBoxLayout* hbox_tools_mem_addr = new QHBoxLayout();
+ QGroupBox* tools_mem_addr = new QGroupBox(tr("Address"), this);
+ QHBoxLayout* hbox_tools_mem_addr = new QHBoxLayout(this);
m_addr_line = new QLineEdit(this);
m_addr_line->setPlaceholderText("00000000");
m_addr_line->setFont(mono);
@@ -85,7 +94,7 @@ memory_viewer_panel::memory_viewer_panel(QWidget* parent, u32 addr, std::functio
tools_mem_addr->setLayout(hbox_tools_mem_addr);
// Tools: Memory Viewer Options: Words
- QGroupBox* tools_mem_words = new QGroupBox(tr("Words"));
+ QGroupBox* tools_mem_words = new QGroupBox(tr("Words"), this);
QHBoxLayout* hbox_tools_mem_words = new QHBoxLayout();
class words_spin_box : public QSpinBox
@@ -114,7 +123,7 @@ memory_viewer_panel::memory_viewer_panel(QWidget* parent, u32 addr, std::functio
// Tools: Memory Viewer Options: Control
QGroupBox* tools_mem_buttons = new QGroupBox(tr("Control"));
- QHBoxLayout* hbox_tools_mem_buttons = new QHBoxLayout();
+ QHBoxLayout* hbox_tools_mem_buttons = new QHBoxLayout(this);
QPushButton* b_fprev = new QPushButton("<<", this);
QPushButton* b_prev = new QPushButton("<", this);
QPushButton* b_next = new QPushButton(">", this);
@@ -140,12 +149,12 @@ memory_viewer_panel::memory_viewer_panel(QWidget* parent, u32 addr, std::functio
tools_mem->setLayout(hbox_tools_mem);
// Tools: Raw Image Preview Options
- QGroupBox* tools_img = new QGroupBox(tr("Raw Image Preview Options"));
- QHBoxLayout* hbox_tools_img = new QHBoxLayout();
+ QGroupBox* tools_img = new QGroupBox(tr("Raw Image Preview Options"), this);
+ QHBoxLayout* hbox_tools_img = new QHBoxLayout(this);
// Tools: Raw Image Preview Options : Size
- QGroupBox* tools_img_size = new QGroupBox(tr("Size"));
- QHBoxLayout* hbox_tools_img_size = new QHBoxLayout();
+ QGroupBox* tools_img_size = new QGroupBox(tr("Size"), this);
+ QHBoxLayout* hbox_tools_img_size = new QHBoxLayout(this);
QLabel* l_x = new QLabel(" x ");
QSpinBox* sb_img_size_x = new QSpinBox(this);
QSpinBox* sb_img_size_y = new QSpinBox(this);
@@ -159,8 +168,8 @@ memory_viewer_panel::memory_viewer_panel(QWidget* parent, u32 addr, std::functio
tools_img_size->setLayout(hbox_tools_img_size);
// Tools: Raw Image Preview Options: Mode
- QGroupBox* tools_img_mode = new QGroupBox(tr("Mode"));
- QHBoxLayout* hbox_tools_img_mode = new QHBoxLayout();
+ QGroupBox* tools_img_mode = new QGroupBox(tr("Mode"), this);
+ QHBoxLayout* hbox_tools_img_mode = new QHBoxLayout(this);
QComboBox* cbox_img_mode = new QComboBox(this);
cbox_img_mode->addItem("RGB", QVariant::fromValue(color_format::RGB));
cbox_img_mode->addItem("ARGB", QVariant::fromValue(color_format::ARGB));
@@ -176,7 +185,7 @@ memory_viewer_panel::memory_viewer_panel(QWidget* parent, u32 addr, std::functio
tools_img->setLayout(hbox_tools_img);
// Tools: Tool Buttons
- QGroupBox* tools_buttons = new QGroupBox(tr("Tools"));
+ QGroupBox* tools_buttons = new QGroupBox(tr("Tools"), this);
QVBoxLayout* hbox_tools_buttons = new QVBoxLayout(this);
QPushButton* b_img = new QPushButton(tr("View\nimage"), this);
b_img->setAutoDefault(false);
@@ -184,17 +193,22 @@ memory_viewer_panel::memory_viewer_panel(QWidget* parent, u32 addr, std::functio
tools_buttons->setLayout(hbox_tools_buttons);
// Merge Tools = Memory Viewer Options + Raw Image Preview Options + Tool Buttons
- hbox_tools->addSpacing(10);
+ hbox_tools->addSpacing(20);
hbox_tools->addWidget(tools_mem);
hbox_tools->addWidget(tools_img);
hbox_tools->addWidget(tools_buttons);
- hbox_tools->addSpacing(10);
+ hbox_tools->addSpacing(20);
// Memory Panel:
- QHBoxLayout* hbox_mem_panel = new QHBoxLayout();
+ m_hbox_mem_panel = new QHBoxLayout(this);
// Memory Panel: Address Panel
m_mem_addr = new QLabel("");
+
+ QSizePolicy sp_retain = m_mem_addr->sizePolicy();
+ sp_retain.setRetainSizeWhenHidden(false);
+
+ m_mem_addr->setSizePolicy(sp_retain);
m_mem_addr->setObjectName("memory_viewer_address_panel");
m_mem_addr->setFont(mono);
m_mem_addr->setAutoFillBackground(true);
@@ -203,6 +217,7 @@ memory_viewer_panel::memory_viewer_panel(QWidget* parent, u32 addr, std::functio
// Memory Panel: Hex Panel
m_mem_hex = new QLabel("");
+ m_mem_hex->setSizePolicy(sp_retain);
m_mem_hex->setObjectName("memory_viewer_hex_panel");
m_mem_hex->setFont(mono);
m_mem_hex->setAutoFillBackground(true);
@@ -211,6 +226,7 @@ memory_viewer_panel::memory_viewer_panel(QWidget* parent, u32 addr, std::functio
// Memory Panel: ASCII Panel
m_mem_ascii = new QLabel("");
+ m_mem_ascii->setSizePolicy(sp_retain);
m_mem_ascii->setObjectName("memory_viewer_ascii_panel");
m_mem_ascii->setFont(mono);
m_mem_ascii->setAutoFillBackground(true);
@@ -218,40 +234,170 @@ memory_viewer_panel::memory_viewer_panel(QWidget* parent, u32 addr, std::functio
m_mem_ascii->ensurePolished();
// Merge Memory Panel:
- hbox_mem_panel->setAlignment(Qt::AlignTop | Qt::AlignHCenter);
- hbox_mem_panel->addSpacing(20);
- hbox_mem_panel->addWidget(m_mem_addr);
- hbox_mem_panel->addSpacing(10);
- hbox_mem_panel->addWidget(m_mem_hex);
- hbox_mem_panel->addSpacing(10);
- hbox_mem_panel->addWidget(m_mem_ascii);
- hbox_mem_panel->addSpacing(10);
+ m_hbox_mem_panel->setAlignment(Qt::AlignTop | Qt::AlignHCenter);
+ m_hbox_mem_panel->addSpacing(20);
+ m_hbox_mem_panel->addWidget(m_mem_addr);
+ m_hbox_mem_panel->addSpacing(10);
+ m_hbox_mem_panel->addWidget(m_mem_hex);
+ m_hbox_mem_panel->addSpacing(10);
+ m_hbox_mem_panel->addWidget(m_mem_ascii);
+ m_hbox_mem_panel->addSpacing(20);
+
+ QHBoxLayout* hbox_memory_search = new QHBoxLayout(this);
// Set Margins to adjust WindowSize
vbox_panel->setContentsMargins(0, 0, 0, 0);
hbox_tools->setContentsMargins(0, 0, 0, 0);
- tools_mem_addr->setContentsMargins(0, 10, 0, 0);
- tools_mem_words->setContentsMargins(0, 10, 0, 0);
- tools_mem_buttons->setContentsMargins(0, 10, 0, 0);
- tools_img_mode->setContentsMargins(0, 10, 0, 0);
- tools_img_size->setContentsMargins(0, 10, 0, 0);
- tools_mem->setContentsMargins(0, 10, 0, 0);
- tools_img->setContentsMargins(0, 10, 0, 0);
- tools_buttons->setContentsMargins(0, 10, 0, 0);
- hbox_mem_panel->setContentsMargins(0, 0, 0, 0);
+ tools_mem_addr->setContentsMargins(0, 5, 0, 0);
+ tools_mem_words->setContentsMargins(0, 5, 0, 0);
+ tools_mem_buttons->setContentsMargins(0, 5, 0, 0);
+ tools_img_mode->setContentsMargins(0, 5, 0, 0);
+ tools_img_size->setContentsMargins(0, 5, 0, 0);
+ tools_mem->setContentsMargins(0, 5, 0, 0);
+ tools_img->setContentsMargins(0, 5, 0, 0);
+ tools_buttons->setContentsMargins(0, 5, 0, 0);
+ m_hbox_mem_panel->setContentsMargins(0, 0, 0, 0);
+ hbox_memory_search->setContentsMargins(0, 0, 0, 0);
+
+ if (m_disasm)
+ {
+ // Extract memory view from the disassembler
+ std::tie(m_ptr, m_size) = m_disasm->get_memory_span();
+ }
+
+ QGroupBox* group_search = new QGroupBox(tr("Memory Search"), this);
+ QPushButton* button_collapse_viewer = new QPushButton(reinterpret_cast(u8"Ʌ"), group_search);
+ button_collapse_viewer->setFixedWidth(QLabel(button_collapse_viewer->text()).sizeHint().width() * 3);
+
+ m_search_line = new QLineEdit(group_search);
+ m_search_line->setFixedWidth(QLabel(QString("This is the very length of the lineedit due to hidpi reasons.").chopped(4)).sizeHint().width());
+ m_search_line->setPlaceholderText(tr("Search..."));
+ m_search_line->setMaxLength(4096);
+
+ QPushButton* button_search = new QPushButton(tr("Search"), group_search);
+ button_search->setEnabled(false);
+
+ m_chkbox_case_insensitive = new QCheckBox(tr("Case Insensitive"), group_search);
+ m_chkbox_case_insensitive->setCheckable(true);
+ m_chkbox_case_insensitive->setToolTip(tr("When using string mode, the characters' case will not matter both in string and in memory."
+ "\nWarning: this may reduce performance of the search."));
+
+ m_cbox_input_mode = new QComboBox(group_search);
+ m_cbox_input_mode->addItem(tr("Select search mode(s).."), QVariant::fromValue(+no_mode));
+ m_cbox_input_mode->addItem(tr("Deselect All Modes"), QVariant::fromValue(+clear_modes));
+ m_cbox_input_mode->addItem(tr("String"), QVariant::fromValue(+as_string));
+ m_cbox_input_mode->addItem(tr("HEX bytes/integer"), QVariant::fromValue(+as_hex));
+ m_cbox_input_mode->addItem(tr("Double"), QVariant::fromValue(+as_f64));
+ m_cbox_input_mode->addItem(tr("Float"), QVariant::fromValue(+as_f32));
+ m_cbox_input_mode->addItem(tr("Instruction"), QVariant::fromValue(+as_inst));
+
+ QString tooltip = tr("String: search the memory for the specified string."
+ "\nHEX bytes/integer: search the memory for hexadecimal values. Spaces, commas, \"0x\", \"0X\", \"\\x\", \"h\", \"H\" ensure separation of bytes but they are not mandatory."
+ "\nDouble: reinterpret the string as 64-bit precision floating point value. Values are searched for exact representation, meaning -0 != 0."
+ "\nFloat: reinterpret the string as 32-bit precision floating point value. Values are searched for exact representation, meaning -0 != 0."
+ "\nInstruction: search an instruction contains the text of the string.");
+
+ if (m_size != 0x40000/*SPU_LS_SIZE*/)
+ {
+ m_cbox_input_mode->addItem("SPU Instruction", QVariant::fromValue(+as_fake_spu_inst));
+ tooltip.append(tr("\nSPU Instruction: Search an SPU instruction contains the text of the string. For searching instructions within embedded SPU images.\nTip: SPU floats are commented along forming instructions."));
+ }
+
+ connect(m_cbox_input_mode, QOverload::of(&QComboBox::currentIndexChanged), group_search, [this, button_search](int index)
+ {
+ if (index < 1 || m_rsx)
+ {
+ return;
+ }
+
+ if ((1u << index) == clear_modes)
+ {
+ m_modes = {};
+ }
+ else
+ {
+ m_modes = search_mode{m_modes | (1 << index)};
+ }
+
+ const s32 count = std::popcount(+m_modes);
+
+ if (count == 0)
+ {
+ button_search->setEnabled(false);
+ m_cbox_input_mode->setItemText(0, tr("Select search mode(s).."));
+ }
+ else
+ {
+ button_search->setEnabled(true);
+ m_cbox_input_mode->setItemText(0, tr("%0 mode(s) selected").arg(count));
+ }
+
+ for (u32 i = search_mode_last / 2; i > clear_modes; i /= 2)
+ {
+ if (i & m_modes && count > 1)
+ {
+ m_cbox_input_mode->setItemText(std::countr_zero(i), qstr(fmt::format("* %s", search_mode{i})));
+ }
+ else
+ {
+ m_cbox_input_mode->setItemText(std::countr_zero(i), qstr(fmt::format("%s", search_mode{i})));
+ }
+ }
+
+ if (count != 1)
+ {
+ m_cbox_input_mode->setCurrentIndex(0);
+ }
+ });
+
+ m_cbox_input_mode->setToolTip(tooltip);
+
+ QVBoxLayout* vbox_search_layout = new QVBoxLayout(group_search);
+
+ QHBoxLayout* hbox_search_panel = new QHBoxLayout(group_search);
+ QHBoxLayout* hbox_search_modes = new QHBoxLayout(group_search);
+
+ hbox_search_panel->addWidget(button_collapse_viewer);
+ hbox_search_panel->addWidget(m_search_line);
+ hbox_search_panel->addWidget(m_cbox_input_mode);
+ hbox_search_panel->addWidget(m_chkbox_case_insensitive);
+ hbox_search_panel->addWidget(button_search);
+
+ vbox_search_layout->addLayout(hbox_search_panel);
+ vbox_search_layout->addLayout(hbox_search_modes);
+ group_search->setLayout(vbox_search_layout);
+
+ hbox_memory_search->setAlignment(Qt::AlignHCenter | Qt::AlignBottom);
+ hbox_memory_search->addSpacing(20);
+ hbox_memory_search->addWidget(group_search);
+ hbox_memory_search->addSpacing(20);
// Merge and display everything
vbox_panel->addSpacing(10);
- vbox_panel->addLayout(hbox_tools, 0);
- vbox_panel->addSpacing(10);
- vbox_panel->addLayout(hbox_mem_panel, 1);
- vbox_panel->addSpacing(10);
+
+ auto get_row = [row = 0]() mutable
+ {
+ return row++;
+ };
+
+ vbox_panel->addLayout(hbox_tools, get_row());
+ vbox_panel->addSpacing(5);
+ vbox_panel->addLayout(m_hbox_mem_panel, get_row());
+
+ // TODO: RSX memory searcher
+ if (!m_rsx)
+ {
+ vbox_panel->addLayout(hbox_memory_search, get_row());
+ vbox_panel->addSpacing(15);
+ }
+ else
+ {
+ group_search->deleteLater();
+ }
+
vbox_panel->setSizeConstraint(QLayout::SetNoConstraint);
setLayout(vbox_panel);
- // Fill the QTextEdits
- scroll(0);
-
// Events
connect(m_addr_line, &QLineEdit::returnPressed, [this]()
{
@@ -280,15 +426,117 @@ memory_viewer_panel::memory_viewer_panel(QWidget* parent, u32 addr, std::functio
ShowImage(this, m_addr, format, sizex, sizey, false);
});
+ if (!m_rsx)
+ {
+ connect(button_search, &QAbstractButton::clicked, this, [this]()
+ {
+ if (m_search_thread && m_search_thread->isRunning())
+ {
+ // Prevent spamming (search is costly on performance)
+ return;
+ }
+
+ if (m_search_thread)
+ {
+ m_search_thread->deleteLater();
+ m_search_thread = nullptr;
+ }
+
+ std::string wstr = m_search_line->text().toStdString();
+
+ if (wstr.empty() || wstr.size() >= 4096u)
+ {
+ gui_log.error("String is empty or too long (size=%u)", wstr.size());
+ return;
+ }
+
+ m_search_thread = QThread::create([this, wstr, m_modes = m_modes]()
+ {
+ gui_log.notice("Searching for %s (mode: %s)", wstr, m_modes);
+
+ u64 found = 0;
+
+ for (int modes = m_modes; modes; modes &= modes - 1)
+ {
+ found += OnSearch(wstr, modes & ~(modes - 1));
+ }
+
+ gui_log.success("Search completed (found %u matches)", +found);
+ });
+
+ m_search_thread->start();
+ });
+
+ connect(button_collapse_viewer, &QAbstractButton::clicked, this, [this, button_collapse_viewer, m_previous_row_count = -1]() mutable
+ {
+ const bool is_collapsing = button_collapse_viewer->text() == reinterpret_cast(u8"Ʌ");
+ button_collapse_viewer->setText(is_collapsing ? "V" : reinterpret_cast(u8"Ʌ"));
+
+ if (is_collapsing)
+ {
+ m_previous_row_count = std::exchange(m_rowcount, 0);
+ setMinimumHeight(0);
+ }
+ else
+ {
+ m_rowcount = std::exchange(m_previous_row_count, 0);
+ setMaximumHeight(16777215); // Default Qt value
+ }
+
+ ShowMemory();
+
+ QTimer::singleShot(0, this, [this, button_collapse_viewer]()
+ {
+ const bool is_collapsing = button_collapse_viewer->text() != reinterpret_cast(u8"Ʌ");
+
+ // singleShot to evaluate properly after the event
+ const int height_hint = sizeHint().height();
+ resize(size().width(), height_hint);
+
+ if (is_collapsing)
+ {
+ setMinimumHeight(height_hint);
+ setMaximumHeight(height_hint + 1);
+ }
+ else
+ {
+ setMinimumHeight(m_min_height);
+ }
+ });
+ });
+ }
+
+ // Set the minimum height of one row
+ m_rowcount = 1;
+ ShowMemory();
+ m_min_height = sizeHint().height();
+ setMinimumHeight(m_min_height);
+
+ m_rowcount = 16;
+ ShowMemory();
+
setFixedWidth(sizeHint().width());
+ // Fill the QTextEdits
+ scroll(0);
+
// Show by default
show();
// Expected to be created by IDM, emulation stop will close it
- connect(this, &memory_viewer_panel::finished, [id = idm::last_id()](int)
+ const u32 id = idm::last_id();
+ auto handle_ptr = idm::get_unlocked(id);
+
+ connect(this, &memory_viewer_panel::finished, [handle_ptr = std::move(handle_ptr), id, this](int)
{
- idm::remove(id);
+ if (m_search_thread)
+ {
+ m_search_thread->wait();
+ m_search_thread->deleteLater();
+ m_search_thread = nullptr;
+ }
+
+ idm::remove_verify(id, handle_ptr);
});
}
@@ -315,6 +563,7 @@ void memory_viewer_panel::scroll(s32 steps)
m_addr -= m_addr % (m_colcount * 4); // Align by amount of bytes in a row
m_addr_line->setText(qstr(fmt::format("%08x", m_addr)));
+
ShowMemory();
}
@@ -326,21 +575,26 @@ void memory_viewer_panel::resizeEvent(QResizeEvent *event)
const QMargins margins = layout()->contentsMargins();
int free_height = event->size().height()
- - (layout()->count() * (margins.top() + margins.bottom()));
+ - (layout()->count() * (margins.top() + margins.bottom())) - c_pad_memory_labels;
for (int i = 0; i < layout()->count(); i++)
{
- if (i != 3) // Index of our memory layout
- free_height -= layout()->itemAt(i)->sizeHint().height();
+ const auto it = layout()->itemAt(i);
+ if (it != m_hbox_mem_panel) // Do not take our memory layout into account
+ free_height -= it->sizeHint().height();
}
- setMinimumHeight(event->size().height() - free_height + font_height);
const u32 new_row_count = std::max(0, free_height) / font_height;
if (m_rowcount != new_row_count)
{
m_rowcount = new_row_count;
- ShowMemory();
+
+ QTimer::singleShot(0, [this]()
+ {
+ // Prevent recursion of events
+ ShowMemory();
+ });
}
}
@@ -539,7 +793,7 @@ void memory_viewer_panel::ShowMemory()
if (const auto ptr = this->to_ptr(addr))
{
- const be_t rmem = *static_cast*>(ptr);
+ const be_t rmem = read_from_ptr>(static_cast(ptr));
t_mem_hex_str += qstr(fmt::format("%02x %02x %02x %02x",
static_cast(rmem >> 24),
static_cast(rmem >> 16),
@@ -563,19 +817,28 @@ void memory_viewer_panel::ShowMemory()
}
}
+ m_mem_addr->setVisible(m_rowcount != 0);
+ m_mem_hex->setVisible(m_rowcount != 0);
+ m_mem_ascii->setVisible(m_rowcount != 0);
+
m_mem_addr->setText(t_mem_addr_str);
m_mem_hex->setText(t_mem_hex_str);
m_mem_ascii->setText(t_mem_ascii_str);
+ auto mask_height = [&](int height)
+ {
+ return m_rowcount != 0 ? height + c_pad_memory_labels : 0;
+ };
+
// Adjust Text Boxes (also helps with window resize)
QSize textSize = m_fontMetrics->size(0, m_mem_addr->text());
- m_mem_addr->setFixedSize(textSize.width() + 10, textSize.height());
+ m_mem_addr->setFixedSize(textSize.width() + 10, mask_height(textSize.height()));
textSize = m_fontMetrics->size(0, m_mem_hex->text());
- m_mem_hex->setFixedSize(textSize.width() + 10, textSize.height());
+ m_mem_hex->setFixedSize(textSize.width() + 10, mask_height(textSize.height()));
textSize = m_fontMetrics->size(0, m_mem_ascii->text());
- m_mem_ascii->setFixedSize(textSize.width() + 10, textSize.height());
+ m_mem_ascii->setFixedSize(textSize.width() + 10, mask_height(textSize.height()));
}
void memory_viewer_panel::SetPC(const uint pc)
diff --git a/rpcs3/rpcs3qt/memory_viewer_panel.h b/rpcs3/rpcs3qt/memory_viewer_panel.h
index c9eef2f452..c3aebf2743 100644
--- a/rpcs3/rpcs3qt/memory_viewer_panel.h
+++ b/rpcs3/rpcs3qt/memory_viewer_panel.h
@@ -10,7 +10,15 @@
#include
+class QLineEdit;
+class QCheckBox;
+class QComboBox;
+class QLabel;
+class QThread;
+class QHBoxLayout;
+
class cpu_thread;
+class CPUDisAsm;
namespace utils
{
@@ -22,12 +30,25 @@ namespace rsx
class thread;
}
+enum search_mode : unsigned
+{
+ no_mode = 1,
+ clear_modes = 2,
+ as_string = 4,
+ as_hex = 8,
+ as_f64 = 16,
+ as_f32 = 32,
+ as_inst = 64,
+ as_fake_spu_inst = 128,
+ search_mode_last = 256,
+};
+
class memory_viewer_panel : public QDialog
{
Q_OBJECT
public:
- memory_viewer_panel(QWidget* parent, u32 addr = 0, std::function func = []() -> cpu_thread* { return {}; });
+ memory_viewer_panel(QWidget* parent, std::shared_ptr disasm, u32 addr = 0, std::function func = []() -> cpu_thread* { return {}; });
~memory_viewer_panel();
enum class color_format : int
@@ -44,17 +65,26 @@ protected:
void resizeEvent(QResizeEvent* event) override;
private:
- u32 m_addr;
- u32 m_colcount;
- u32 m_rowcount;
+ u32 m_addr = 0;
+ u32 m_colcount = 0;
+ u32 m_rowcount = 0;
+ u32 m_min_height = 0;
- QLineEdit* m_addr_line;
+ QLineEdit* m_addr_line = nullptr;
- QLabel* m_mem_addr;
- QLabel* m_mem_hex;
- QLabel* m_mem_ascii;
+ QLabel* m_mem_addr = nullptr;
+ QLabel* m_mem_hex = nullptr;
+ QLabel* m_mem_ascii = nullptr;
- QFontMetrics* m_fontMetrics;
+ QFontMetrics* m_fontMetrics = nullptr;
+
+ static constexpr int c_pad_memory_labels = 15;
+
+ QLineEdit* m_search_line = nullptr;
+ QCheckBox* m_chkbox_case_insensitive = nullptr;
+ QComboBox* m_cbox_input_mode = nullptr;
+ QHBoxLayout* m_hbox_mem_panel = nullptr;
+ QThread* m_search_thread = nullptr;
enum class thread_type
{
@@ -70,6 +100,13 @@ private:
const std::shared_ptr m_spu_shm;
const u32 m_addr_mask;
+ std::shared_ptr m_disasm;
+
+ const void* m_ptr = nullptr;
+ usz m_size = 0;
+
+ search_mode m_modes{};
+
std::string getHeaderAtAddr(u32 addr) const;
void scroll(s32 steps);
void* to_ptr(u32 addr, u32 size = 1) const;
@@ -78,6 +115,7 @@ private:
virtual void ShowMemory();
void ShowImage(QWidget* parent, u32 addr, color_format format, u32 width, u32 height, bool flipv) const;
+ u64 OnSearch(std::string wstr, u32 mode);
};
// Lifetime management with IDM