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