diff --git a/pcsx2/CMakeLists.txt b/pcsx2/CMakeLists.txt
index 351d56c136..8209021c2d 100644
--- a/pcsx2/CMakeLists.txt
+++ b/pcsx2/CMakeLists.txt
@@ -962,6 +962,7 @@ set(pcsx2GuiSources
gui/Debugger/CtrlDisassemblyView.cpp
gui/Debugger/CtrlRegisterList.cpp
gui/Debugger/CtrlMemView.cpp
+ gui/Debugger/CtrlMemSearch.cpp
gui/Debugger/DebuggerLists.cpp
gui/Debugger/DisassemblyDialog.cpp
gui/Debugger/DebugEvents.cpp
@@ -1021,6 +1022,7 @@ set(pcsx2GuiHeaders
gui/Debugger/BreakpointWindow.h
gui/Debugger/CtrlDisassemblyView.h
gui/Debugger/CtrlMemView.h
+ gui/Debugger/CtrlMemSearch.h
gui/Debugger/CtrlRegisterList.h
gui/Debugger/DebugEvents.h
gui/Debugger/DebuggerLists.h
@@ -1230,7 +1232,8 @@ set(pcsx2UtilitiesSources
# Utilities headers
set(pcsx2UtilitiesHeaders
- Utilities/AsciiFile.h)
+ Utilities/AsciiFile.h
+ Utilities/BitCast.h)
# Windows sources
diff --git a/pcsx2/Utilities/BitCast.h b/pcsx2/Utilities/BitCast.h
new file mode 100644
index 0000000000..6d612ec5b9
--- /dev/null
+++ b/pcsx2/Utilities/BitCast.h
@@ -0,0 +1,27 @@
+/* PCSX2 - PS2 Emulator for PCs
+ * Copyright (C) 2002-2021 PCSX2 Dev Team
+ *
+ * PCSX2 is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU Lesser General Public License as published by the Free Software Found-
+ * ation, either version 3 of the License, or (at your option) any later version.
+ *
+ * PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+ * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE. See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with PCSX2.
+ * If not, see .
+ */
+
+// An implementation of bit_cast until we get c++20
+
+template
+T2 bit_cast(const T1& src)
+{
+ static_assert(sizeof(T2) == sizeof(T1), "bit_cast: types must be equal size");
+ static_assert(std::is_pod::value, "bit_cast: source must be POD");
+ static_assert(std::is_pod::value, "bit_cast: destination must be POD");
+ T2 dst;
+ memcpy(&dst, &src, sizeof(T2));
+ return dst;
+}
\ No newline at end of file
diff --git a/pcsx2/gui/Debugger/CtrlMemSearch.cpp b/pcsx2/gui/Debugger/CtrlMemSearch.cpp
new file mode 100644
index 0000000000..9446f2332c
--- /dev/null
+++ b/pcsx2/gui/Debugger/CtrlMemSearch.cpp
@@ -0,0 +1,501 @@
+/* PCSX2 - PS2 Emulator for PCs
+ * Copyright (C) 2002-2021 PCSX2 Dev Team
+ *
+ * PCSX2 is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU Lesser General Public License as published by the Free Software Found-
+ * ation, either version 3 of the License, or (at your option) any later version.
+ *
+ * PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+ * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE. See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with PCSX2.
+ * If not, see .
+ */
+
+#include "PrecompiledHeader.h"
+#include "CtrlMemSearch.h"
+#include "DebugTools/Debug.h"
+#include "gui/AppConfig.h"
+#include "Utilities/BitCast.h"
+#include "../Patch.h" // Required for SwapEndian :(
+
+#include "BreakpointWindow.h"
+#include "DebugEvents.h"
+#include "DisassemblyDialog.h"
+#include
+#include
+
+wxDEFINE_EVENT(pxEvt_SearchFinished, wxCommandEvent);
+
+wxBEGIN_EVENT_TABLE(CtrlMemSearch, wxWindow)
+ EVT_SET_FOCUS(CtrlMemSearch::focusEvent)
+ EVT_KILL_FOCUS(CtrlMemSearch::focusEvent)
+wxEND_EVENT_TABLE()
+
+enum MemoryViewMenuIdentifiers {
+ ID_MEMVIEW_GOTOINDISASM = 1,
+ ID_MEMVIEW_GOTOADDRESS,
+ ID_MEMVIEW_COPYADDRESS,
+ ID_MEMVIEW_FOLLOWADDRESS,
+ ID_MEMVIEW_DISPLAYVALUE_8,
+ ID_MEMVIEW_DISPLAYVALUE_16,
+ ID_MEMVIEW_DISPLAYVALUE_32,
+ ID_MEMVIEW_DISPLAYVALUE_64,
+ ID_MEMVIEW_DISPLAYVALUE_128,
+ ID_MEMVIEW_COPYVALUE_8,
+ ID_MEMVIEW_COPYVALUE_16,
+ ID_MEMVIEW_COPYVALUE_32,
+ ID_MEMVIEW_COPYVALUE_64,
+ ID_MEMVIEW_COPYVALUE_128,
+ ID_MEMVIEW_ALIGNWINDOW,
+};
+
+// Kind of lame, but it works.
+const wxString wxStringTypes[] =
+ {
+ "Byte",
+ "2 Bytes",
+ "4 Bytes",
+ "8 Bytes",
+ "Float",
+ "Double",
+ "String"};
+const wxArrayString SearchTypes = wxArrayString(7, wxStringTypes);
+
+CtrlMemSearch::CtrlMemSearch(wxWindow* parent, DebugInterface* _cpu)
+ : wxWindow(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize)
+ , cpu(_cpu)
+{
+
+ m_SearchThread.reset(new SearchThread(this, _cpu));
+ m_SearchThread->SetName("Debugger Memory Search");
+
+ Bind(pxEvt_SearchFinished, &CtrlMemSearch::onSearchFinished, this);
+
+ wxBoxSizer* mainSizer = new wxBoxSizer(wxVERTICAL);
+
+ txtSearch = new wxTextCtrl(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(250, -1));
+ btnSearch = new wxButton(this, wxID_ANY, L"Search", wxDefaultPosition);
+
+ Bind(wxEVT_BUTTON, &CtrlMemSearch::Search, this, btnSearch->GetId());
+
+ cmbSearchType = new wxComboBox(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(250, -1), 0, NULL, wxCB_READONLY);
+ cmbSearchType->Set(SearchTypes);
+ cmbSearchType->SetSelection(0);
+
+ Bind(wxEVT_COMBOBOX, &CtrlMemSearch::onSearchTypeChanged, this, cmbSearchType->GetId());
+
+ chkHexadecimal = new wxCheckBox(this, wxID_ANY, L"Hex");
+ chkHexadecimal->SetValue(true);
+
+ radBigEndian = new wxRadioButton(this, wxID_ANY, L"Big Endian", wxDefaultPosition, wxDefaultSize, wxRB_GROUP);
+ radLittleEndian = new wxRadioButton(this, wxID_ANY, L"Little Endian");
+ radLittleEndian->SetValue(true);
+
+ txtMemoryStart = new wxTextCtrl(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(250, -1));
+ txtMemoryStart->SetValue(L"0x0");
+ txtMemoryEnd = new wxTextCtrl(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(250, -1));
+ if (_cpu->getCpuType() == BREAKPOINT_EE)
+ txtMemoryEnd->SetValue(L"0x200000");
+ else
+ txtMemoryEnd->SetValue(L"0x20000");
+
+ lblSearchHits = new wxStaticText(this, wxID_ANY, L"Hits: 0");
+ lblSearchHitSelected = new wxStaticText(this, wxID_ANY, L"Selected: 0");
+
+ btnNext = new wxButton(this, wxID_ANY, L"Next", wxDefaultPosition);
+ btnNext->Enable(false);
+ Bind(wxEVT_BUTTON, &CtrlMemSearch::onSearchNext, this, btnNext->GetId());
+ btnPrev = new wxButton(this, wxID_ANY, L"Prev", wxDefaultPosition);
+ btnPrev->Enable(false);
+ Bind(wxEVT_BUTTON, &CtrlMemSearch::onSearchPrev, this, btnPrev->GetId());
+
+ // GUI design
+
+ // Search box and search button
+ wxBoxSizer* searchSizer = new wxBoxSizer(wxHORIZONTAL);
+ searchSizer->Add(txtSearch, 0);
+ searchSizer->Add(btnSearch, 0, wxLEFT, 5);
+
+ mainSizer->Add(searchSizer, 0, wxUP | wxLEFT | wxRIGHT, 5);
+
+ // Search type combo box and hexadecimal checkbox
+ wxBoxSizer* searchTypeSizer = new wxBoxSizer(wxHORIZONTAL);
+ searchTypeSizer->Add(cmbSearchType, 0, wxEXPAND);
+ searchTypeSizer->Add(chkHexadecimal, 0, wxLEFT | wxALIGN_CENTER_VERTICAL, 5);
+
+ mainSizer->Add(searchTypeSizer, 0, wxUP | wxLEFT | wxRIGHT, 5);
+
+ // Big / Little Endianess radio button
+ wxBoxSizer* endianessSizer = new wxBoxSizer(wxHORIZONTAL);
+ endianessSizer->Add(radLittleEndian, 0, wxALIGN_CENTER_VERTICAL);
+ endianessSizer->Add(radBigEndian, 0, wxLEFT | wxALIGN_CENTER_VERTICAL, 5);
+
+ mainSizer->Add(endianessSizer, 0, wxUP | wxLEFT | wxRIGHT, 5);
+
+ // Address range type text boxes
+ wxFlexGridSizer* addrRangeSizer = new wxFlexGridSizer(2, 2, 5);
+
+ addrRangeSizer->Add(new wxStaticText(this, wxID_ANY, L"Start Address"), 0, wxALIGN_RIGHT | wxALIGN_CENTER_VERTICAL);
+ addrRangeSizer->Add(txtMemoryStart, 0, wxEXPAND);
+
+ addrRangeSizer->Add(new wxStaticText(this, wxID_ANY, L"End Address"), 0, wxALIGN_RIGHT | wxALIGN_CENTER_VERTICAL);
+ addrRangeSizer->Add(txtMemoryEnd, 0, wxEXPAND);
+
+ mainSizer->Add(addrRangeSizer, 0, wxUP | wxLEFT | wxRIGHT, 5);
+
+ wxBoxSizer* resultSizer = new wxBoxSizer(wxHORIZONTAL);
+ resultSizer->Add(lblSearchHits, 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, 5);
+ resultSizer->Add(lblSearchHitSelected, 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, 5);
+ resultSizer->Add(btnPrev, 0, wxRIGHT, 5);
+ resultSizer->Add(btnNext, 0, wxRIGHT, 5);
+
+ mainSizer->Add(resultSizer, 0, wxUP | wxLEFT | wxRIGHT, 5);
+ this->SetSizer(mainSizer);
+ SetDoubleBuffered(true);
+}
+
+void CtrlMemSearch::postEvent(wxEventType type, wxString text)
+{
+ wxCommandEvent event(type, GetId());
+ event.SetEventObject(this);
+ event.SetClientData(cpu);
+ event.SetString(text);
+ wxPostEvent(this, event);
+}
+
+void CtrlMemSearch::postEvent(wxEventType type, int value)
+{
+ wxCommandEvent event(type, GetId());
+ event.SetEventObject(this);
+ event.SetClientData(cpu);
+ event.SetInt(value);
+ wxPostEvent(this, event);
+}
+
+// Set up the thread with out UI params
+// Then execute the thread
+void CtrlMemSearch::Search(wxCommandEvent& evt)
+{
+ if (!m_searchResultsMutex.try_lock())
+ {
+ // Uh, if it doesn't lock then means we somehow tried to search while
+ // the search was still running.
+ return;
+ }
+ m_searchResults.clear();
+ m_searchResultsMutex.unlock();
+
+ lblSearchHits->SetLabelText(L"Hits: 0");
+ btnNext->Enable(false);
+ btnPrev->Enable(false);
+
+ u64 startAddress = 0;
+ u64 endAddress = 0;
+
+ if (!txtMemoryStart->GetValue().ToULong(&startAddress, 16))
+ {
+ wxMessageBox(L"Invalid start address", L"Error", wxOK | wxICON_ERROR);
+ return;
+ }
+
+ if (!txtMemoryEnd->GetValue().ToULong(&endAddress, 16))
+ {
+ wxMessageBox(L"Invalid end address", L"Error", wxOK | wxICON_ERROR);
+ return;
+ }
+ m_SearchThread->m_start = startAddress;
+ m_SearchThread->m_end = endAddress;
+ m_SearchThread->m_type = static_cast(cmbSearchType->GetSelection());
+
+ // Prepare the search value for the thread
+
+ wxString searchString = txtSearch->GetValue();
+ if (cmbSearchType->GetSelection() == SEARCHTYPE::STRING)
+ {
+ m_SearchThread->m_value_string = searchString;
+ }
+ else if (chkHexadecimal->IsChecked())
+ {
+ u64 searchValue = 0;
+ if (!searchString.ToULong(&searchValue, 16))
+ {
+ wxMessageBox(L"Invalid hexadecimal value", L"Error", wxOK | wxICON_ERROR);
+ return;
+ }
+ m_SearchThread->m_value = radBigEndian->GetValue() ? SwapEndian(searchValue, SEARCHTYPEBITS[m_SearchThread->m_type]) : searchValue;
+ }
+ else
+ {
+ // According to my logic, we end up here if we aren't searching for a string
+ // Aren't in hexadecimal mode.
+
+ // Let's check if we're searching for a signed or unsigned value
+ // The thread will handle the interpretation for us.
+ m_SearchThread->m_signed = txtSearch->GetValue().First('-');
+
+ // Now we can convert the string to a number
+
+ if (cmbSearchType->GetSelection() == SEARCHTYPE::FLOAT || cmbSearchType->GetSelection() == SEARCHTYPE::DOUBLE)
+ {
+ // We're searching for a float or double, so we need to convert the string to a double
+ double searchValue = 0;
+ if (!searchString.ToDouble(&searchValue))
+ {
+ wxMessageBox(L"Invalid floating point value", L"Error", wxOK | wxICON_ERROR);
+ return;
+ }
+
+ if (m_SearchThread->m_type == SEARCHTYPE::FLOAT)
+ {
+ const u32 u32SearchValue = bit_cast(searchValue);
+ m_SearchThread->m_value = u32SearchValue;
+ }
+ else
+ {
+ const u64 u64SearchValue = bit_cast(searchValue);
+ m_SearchThread->m_value = u64SearchValue;
+ }
+ }
+ else
+ {
+ // We're searching for either a BYTE, WORD, DWORD or QWORD
+ u64 searchValue = 0;
+ if (!searchString.ToULong(&searchValue, 10))
+ {
+ wxMessageBox(L"Invalid decimal search value", L"Error", wxOK | wxICON_ERROR);
+ return;
+ }
+
+ // We don't need to bitcast here, because the thread already has the correct value type of u64
+ m_SearchThread->m_value = radBigEndian->GetValue() ? SwapEndian(searchValue, SEARCHTYPEBITS[m_SearchThread->m_type]) : searchValue;
+ }
+ }
+
+ m_SearchThread->Start();
+};
+
+void CtrlMemSearch::onSearchFinished(wxCommandEvent& evt)
+{
+ // We're done searching, so let's update the UI
+
+ m_searchResultsMutex.lock();
+
+ lblSearchHits->SetLabelText(wxString::Format(L"Hits: %d", m_searchResults.size()));
+
+ // Enable the buttons only if we have results
+ // -1 indicates we haven't jumped to a result yet
+ m_searchIter = -1;
+ lblSearchHitSelected->SetLabelText(L"Cur: 0");
+ btnNext->Enable(m_searchResults.size() > 0);
+ btnPrev->Enable(m_searchResults.size() > 0);
+
+ m_searchResultsMutex.unlock();
+
+ this->Layout(); // This makes sure the search hit count label doesn't get cut off
+}
+
+void CtrlMemSearch::onSearchNext(wxCommandEvent& evt)
+{
+ m_searchIter++;
+ if (m_searchIter == m_searchResults.size())
+ {
+ m_searchIter = 0;
+ }
+
+ lblSearchHitSelected->SetLabelText(wxString::Format(L"Cur: %d",m_searchIter + 1));
+ this->Layout();
+ postEvent(debEVT_GOTOINMEMORYVIEW, m_searchResults[m_searchIter]);
+}
+
+void CtrlMemSearch::onSearchPrev(wxCommandEvent& evt)
+{
+ m_searchIter--;
+ // Unsigned underflow
+ if (m_searchIter > m_searchResults.size())
+ {
+ m_searchIter = m_searchResults.size() - 1;
+ }
+
+ lblSearchHitSelected->SetLabelText(wxString::Format(L"Cur: %d",m_searchIter + 1));
+ this->Layout();
+ postEvent(debEVT_GOTOINMEMORYVIEW, m_searchResults[m_searchIter]);
+}
+
+void CtrlMemSearch::onSearchTypeChanged(wxCommandEvent& evt)
+{
+ // Update the search value text box to match the search type
+ switch (cmbSearchType->GetSelection())
+ {
+ case SEARCHTYPE::BYTE:
+ case SEARCHTYPE::WORD:
+ case SEARCHTYPE::DWORD:
+ case SEARCHTYPE::QWORD:
+ radBigEndian->Enable(true);
+ radLittleEndian->Enable(true);
+ chkHexadecimal->Enable(false);
+ break;
+ case SEARCHTYPE::FLOAT:
+ case SEARCHTYPE::DOUBLE:
+ radBigEndian->Enable(false);
+ radLittleEndian->Enable(false);
+ chkHexadecimal->Enable(false);
+ chkHexadecimal->SetValue(false);
+ break;
+
+ case SEARCHTYPE::STRING:
+ radBigEndian->Enable(true);
+ radLittleEndian->Enable(true);
+ chkHexadecimal->Enable(false);
+ chkHexadecimal->SetValue(false);
+ break;
+ }
+}
+
+// -- SearchThread
+
+// There might be a prettier way to read from the vtlb
+template
+void memSearch(std::vector& out, u32 start, u32 end, T value, DebugInterface* cpu)
+{
+ for (u32 addr = start; addr < end; addr += sizeof(T))
+ {
+ if (!cpu->isValidAddress(addr))
+ {
+ continue;
+ }
+
+ T val = 0;
+ if (sizeof(T) == 1)
+ {
+ val = cpu->read8(addr);
+ }
+ else if (sizeof(T) == 2)
+ {
+ val = cpu->read16(addr);
+ }
+ else if (sizeof(T) == 4)
+ {
+ if (std::is_same::value)
+ {
+ float fpVal = bit_cast(value);
+ // Love floating point
+ if (bit_cast(cpu->read32(addr)) < fpVal + 0.001f && bit_cast(cpu->read32(addr)) > fpVal - 0.001f)
+ {
+ out.push_back(addr);
+ }
+ continue;
+ }
+
+ val = cpu->read32(addr);
+ }
+ else if (sizeof(T) == 8)
+ {
+ if (std::is_same::value)
+ {
+ double dbVal = bit_cast(value);
+ if (bit_cast(cpu->read64(addr)) < dbVal + 0.001 && bit_cast(cpu->read64(addr)) > dbVal - 0.001)
+ {
+ out.push_back(addr);
+ }
+ continue;
+ }
+
+ val = cpu->read64(addr);
+ }
+
+ if (val == value)
+ {
+ out.push_back(addr);
+ }
+ }
+}
+
+void SearchThread::ExecuteTaskInThread()
+{
+ // First, make sure we can lock the mutex
+ if (!m_parent->m_searchResultsMutex.try_lock())
+ {
+ // We couldn't lock the mutex, this thread either didn't unlock, or we've somehow raced
+ // with the main UI thread ?
+ assert("Failed to lock our memory search mutex?");
+ return;
+ }
+
+ switch (m_type)
+ {
+ case SEARCHTYPE::BYTE:
+ if (m_signed)
+ {
+ memSearch(m_parent->m_searchResults, m_start, m_end, m_value, m_cpu);
+ }
+ else
+ {
+ memSearch(m_parent->m_searchResults, m_start, m_end, m_value, m_cpu);
+ }
+ break;
+ case SEARCHTYPE::WORD:
+ if (m_signed)
+ {
+ memSearch(m_parent->m_searchResults, m_start, m_end, m_value, m_cpu);
+ }
+ else
+ {
+ memSearch(m_parent->m_searchResults, m_start, m_end, m_value, m_cpu);
+ }
+ break;
+ case SEARCHTYPE::DWORD:
+ if (m_signed)
+ {
+ memSearch(m_parent->m_searchResults, m_start, m_end, m_value, m_cpu);
+ }
+ else
+ {
+ memSearch(m_parent->m_searchResults, m_start, m_end, m_value, m_cpu);
+ }
+ break;
+ case SEARCHTYPE::QWORD:
+ if (m_signed)
+ {
+ memSearch(m_parent->m_searchResults, m_start, m_end, m_value, m_cpu);
+ }
+ else
+ {
+ memSearch(m_parent->m_searchResults, m_start, m_end, m_value, m_cpu);
+ }
+ break;
+ case SEARCHTYPE::FLOAT:
+ memSearch(m_parent->m_searchResults, m_start, m_end, m_value, m_cpu);
+ break;
+ case SEARCHTYPE::DOUBLE:
+ memSearch(m_parent->m_searchResults, m_start, m_end, m_value, m_cpu);
+ break;
+ case SEARCHTYPE::STRING:
+ for (u32 addr = m_start; addr < m_end; addr++)
+ {
+ // There is no easy way to do this with the vtable.
+ bool match = true;
+ for (u32 stringIndex = 0; stringIndex < m_value_string.size(); stringIndex++)
+ {
+ if ((char)m_cpu->read8(addr + stringIndex) != m_value_string[stringIndex])
+ {
+ match = false;
+ break;
+ }
+ }
+
+ if (match)
+ {
+ m_parent->m_searchResults.push_back(addr);
+ addr += m_value_string.size() - 1;
+ }
+ }
+ break;
+ }
+
+ m_parent->m_searchResultsMutex.unlock();
+
+ wxCommandEvent done(pxEvt_SearchFinished);
+ m_parent->GetEventHandler()->AddPendingEvent(done);
+}
\ No newline at end of file
diff --git a/pcsx2/gui/Debugger/CtrlMemSearch.h b/pcsx2/gui/Debugger/CtrlMemSearch.h
new file mode 100644
index 0000000000..ef5a1a735a
--- /dev/null
+++ b/pcsx2/gui/Debugger/CtrlMemSearch.h
@@ -0,0 +1,102 @@
+/* PCSX2 - PS2 Emulator for PCs
+ * Copyright (C) 2002-2021 PCSX2 Dev Team
+ *
+ * PCSX2 is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU Lesser General Public License as published by the Free Software Found-
+ * ation, either version 3 of the License, or (at your option) any later version.
+ *
+ * PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+ * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE. See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with PCSX2.
+ * If not, see .
+ */
+
+#pragma once
+#include
+#include "common/PersistentThread.h"
+#include "DebugTools/DebugInterface.h"
+#include "DebugTools/DisassemblyManager.h"
+
+enum SEARCHTYPE
+{
+ BYTE,
+ WORD,
+ DWORD,
+ QWORD,
+ FLOAT,
+ DOUBLE,
+ STRING,
+};
+
+class CtrlMemSearch;
+
+class SearchThread : public Threading::pxThread
+{
+ CtrlMemSearch* m_parent;
+ DebugInterface* m_cpu;
+public:
+ virtual ~SearchThread()
+ {
+ try
+ {
+ pxThread::Cancel();
+ }
+ DESTRUCTOR_CATCHALL
+ }
+ SearchThread(CtrlMemSearch* parent, DebugInterface* cpu) { m_parent = parent; m_cpu = cpu; };
+
+ SEARCHTYPE m_type;
+ // Only guaranteed to be valid if m_type == STRING
+ std::string m_value_string;
+ u64 m_value;
+ bool m_signed;
+ u64 m_start,m_end;
+protected:
+ void ExecuteTaskInThread();
+};
+
+class CtrlMemSearch : public wxWindow
+{
+public:
+ CtrlMemSearch(wxWindow* parent, DebugInterface* _cpu);
+
+ std::vector m_searchResults;
+ std::mutex m_searchResultsMutex;
+ wxDECLARE_EVENT_TABLE();
+
+
+private:
+ void setRowSize(int bytesInRow);
+ void postEvent(wxEventType type, wxString text);
+ void postEvent(wxEventType type, int value);
+ void onPopupClick(wxCommandEvent& evt);
+ void focusEvent(wxFocusEvent& evt) { Refresh(); };
+ void onSearchFinished(wxCommandEvent& evt);
+ void onSearchNext(wxCommandEvent& evt);
+ void onSearchPrev(wxCommandEvent& evt);
+ void onSearchTypeChanged(wxCommandEvent& evt);
+
+ void Search(wxCommandEvent& evt);
+ const u8 SEARCHTYPEBITS[8] = {8,16,32,64,32,64,0};
+
+ DebugInterface* cpu;
+ wxFont font, underlineFont;
+ wxTextCtrl* txtSearch;
+ wxButton* btnSearch;
+ wxComboBox* cmbSearchType;
+ wxCheckBox* chkHexadecimal;
+ wxTextCtrl* txtMemoryStart;
+ wxTextCtrl* txtMemoryEnd;
+ wxRadioButton* radBigEndian;
+ wxRadioButton* radLittleEndian;
+ wxStaticText* lblSearchHits;
+ wxStaticText* lblSearchHitSelected;
+ wxButton* btnNext;
+ wxButton* btnPrev;
+
+ u64 m_searchIter = -1;
+ std::unique_ptr m_SearchThread;
+ wxMenu menu;
+};
diff --git a/pcsx2/gui/Debugger/DisassemblyDialog.cpp b/pcsx2/gui/Debugger/DisassemblyDialog.cpp
index d7e411dea3..78c0b5c433 100644
--- a/pcsx2/gui/Debugger/DisassemblyDialog.cpp
+++ b/pcsx2/gui/Debugger/DisassemblyDialog.cpp
@@ -90,7 +90,10 @@ CpuTabPage::CpuTabPage(wxWindow* parent, DebugInterface* _cpu)
disassembly = new CtrlDisassemblyView(this, cpu);
registerList = new CtrlRegisterList(leftTabs, cpu);
functionList = new wxListBox(leftTabs, wxID_ANY, wxDefaultPosition, wxDefaultSize, 0, NULL, wxBORDER_NONE | wxLB_SORT);
- memory = new CtrlMemView(bottomTabs, cpu);
+
+ wxPanel* memoryPanel = new wxPanel(bottomTabs);
+ memory = new CtrlMemView(memoryPanel, cpu);
+ memorySearch = new CtrlMemSearch(memoryPanel, cpu);
// create register list and disassembly section
wxBoxSizer* middleSizer = new wxBoxSizer(wxHORIZONTAL);
@@ -110,8 +113,17 @@ CpuTabPage::CpuTabPage(wxWindow* parent, DebugInterface* _cpu)
middleSizer->Add(disassembly, 2, wxEXPAND);
mainSizer->Add(middleSizer, 3, wxEXPAND | wxBOTTOM, 3);
+ wxBoxSizer *memorySizer = new wxBoxSizer(wxHORIZONTAL);
+ memorySizer->Add(memory, 1, wxEXPAND);
+
+ // Unfortuneately hacky, probably cause I'm bad at wxWidgets
+ // A max width of 360 ensures that the memory view will never be blocked by the memory search
+ memorySearch->SetMaxSize(wxSize(360, -1));
+ memorySizer->Add(memorySearch, 1, wxEXPAND);
+ memoryPanel->SetSizer(memorySizer);
+
// create bottom section
- bottomTabs->AddPage(memory, L"Memory");
+ bottomTabs->AddPage(memoryPanel, L"Memory");
breakpointList = new BreakpointList(bottomTabs, cpu, disassembly);
bottomTabs->AddPage(breakpointList, L"Breakpoints");
diff --git a/pcsx2/gui/Debugger/DisassemblyDialog.h b/pcsx2/gui/Debugger/DisassemblyDialog.h
index b6806e72da..2ed63bd284 100644
--- a/pcsx2/gui/Debugger/DisassemblyDialog.h
+++ b/pcsx2/gui/Debugger/DisassemblyDialog.h
@@ -21,6 +21,7 @@
#include "CtrlDisassemblyView.h"
#include "CtrlRegisterList.h"
#include "CtrlMemView.h"
+#include "CtrlMemSearch.h"
#include "DebugEvents.h"
#include "DebuggerLists.h"
#include "gui/MSWstuff.h"
@@ -49,6 +50,7 @@ public:
CtrlDisassemblyView* getDisassembly() { return disassembly; };
CtrlRegisterList* getRegisterList() { return registerList; };
CtrlMemView* getMemoryView() { return memory; };
+ CtrlMemSearch* getMemorySearch() { return memorySearch; };
wxNotebook* getBottomTabs() { return bottomTabs; };
void update();
void showMemoryView() { setBottomTabPage(memory); };
@@ -68,6 +70,7 @@ private:
CtrlRegisterList* registerList;
wxListBox* functionList;
CtrlMemView* memory;
+ CtrlMemSearch* memorySearch;
wxNotebook* bottomTabs;
wxNotebook* leftTabs;
BreakpointList* breakpointList;
diff --git a/pcsx2/pcsx2.vcxproj b/pcsx2/pcsx2.vcxproj
index c64a2b565e..9060d4caef 100644
--- a/pcsx2/pcsx2.vcxproj
+++ b/pcsx2/pcsx2.vcxproj
@@ -310,6 +310,7 @@
+
@@ -753,6 +754,7 @@
+
diff --git a/pcsx2/pcsx2.vcxproj.filters b/pcsx2/pcsx2.vcxproj.filters
index c2fa268ccb..a6efc7b40d 100644
--- a/pcsx2/pcsx2.vcxproj.filters
+++ b/pcsx2/pcsx2.vcxproj.filters
@@ -941,6 +941,9 @@
AppHost\Debugger
+
+ AppHost\Debugger
+
System\Ps2\Debug
@@ -2037,6 +2040,9 @@
AppHost\Debugger
+
+ AppHost\Debugger
+
System\Ps2\Debug