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