#include "gui/wxgui.h" #include "gui/debugger/BreakpointWindow.h" #include #include "gui/debugger/DebuggerWindow2.h" #include "gui/guiWrapper.h" #include "Cafe/HW/Espresso/Debugger/Debugger.h" #include "Cemu/ExpressionParser/ExpressionParser.h" enum { MENU_ID_CREATE_MEM_BP_READ = 1, MENU_ID_CREATE_MEM_BP_WRITE, }; enum ItemColumns { ColumnEnabled = 0, ColumnAddress, ColumnType, ColumnComment, }; BreakpointWindow::BreakpointWindow(DebuggerWindow2& parent, const wxPoint& main_position, const wxSize& main_size) : wxFrame(&parent, wxID_ANY, "Breakpoints", wxDefaultPosition, wxSize(420, 250), wxSYSTEM_MENU | wxCAPTION | wxCLIP_CHILDREN | wxRESIZE_BORDER | wxFRAME_FLOAT_ON_PARENT) { this->SetSizeHints(wxDefaultSize, wxDefaultSize); this->wxWindowBase::SetBackgroundColour(*wxWHITE); wxBoxSizer* main_sizer = new wxBoxSizer(wxVERTICAL); m_breakpoints = new wxCheckedListCtrl(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLC_REPORT); wxListItem col0; col0.SetId(ColumnEnabled); col0.SetText(_("On")); col0.SetWidth(32); m_breakpoints->InsertColumn(ColumnEnabled, col0); wxListItem col1; col1.SetId(ColumnAddress); col1.SetText(_("Address")); col1.SetWidth(75); m_breakpoints->InsertColumn(ColumnAddress, col1); wxListItem col2; col2.SetId(ColumnType); col2.SetText(_("Type")); col2.SetWidth(42); m_breakpoints->InsertColumn(ColumnType, col2); wxListItem col3; col3.SetId(ColumnComment); col3.SetText(_("Comment")); col3.SetWidth(250); m_breakpoints->InsertColumn(ColumnComment, col3); main_sizer->Add(m_breakpoints, 1, wxEXPAND); this->SetSizer(main_sizer); this->wxWindowBase::Layout(); this->Centre(wxBOTH); if (parent.GetConfig().data().pin_to_main) OnMainMove(main_position, main_size); m_breakpoints->Bind(wxEVT_COMMAND_LIST_ITEM_CHECKED, (wxObjectEventFunction)&BreakpointWindow::OnBreakpointToggled, this); m_breakpoints->Bind(wxEVT_COMMAND_LIST_ITEM_UNCHECKED, (wxObjectEventFunction)&BreakpointWindow::OnBreakpointToggled, this); m_breakpoints->Bind(wxEVT_LEFT_DCLICK, &BreakpointWindow::OnLeftDClick, this); m_breakpoints->Bind(wxEVT_RIGHT_DOWN, &BreakpointWindow::OnRightDown, this); OnUpdateView(); } BreakpointWindow::~BreakpointWindow() { m_breakpoints->Unbind(wxEVT_COMMAND_LIST_ITEM_CHECKED, (wxObjectEventFunction)&BreakpointWindow::OnBreakpointToggled, this); m_breakpoints->Unbind(wxEVT_COMMAND_LIST_ITEM_UNCHECKED, (wxObjectEventFunction)&BreakpointWindow::OnBreakpointToggled, this); } void BreakpointWindow::OnMainMove(const wxPoint& main_position, const wxSize& main_size) { wxSize size(420, 250); this->SetSize(size); wxPoint position = main_position; position.x -= 420; position.y += main_size.y - 250; this->SetPosition(position); } void BreakpointWindow::OnUpdateView() { Freeze(); m_breakpoints->DeleteAllItems(); if (!debuggerState.breakpoints.empty()) { uint32_t i = 0; for (const auto bpBase : debuggerState.breakpoints) { DebuggerBreakpoint* bp = bpBase; while (bp) { wxListItem item; item.SetId(i++); const auto index = m_breakpoints->InsertItem(item); m_breakpoints->SetItem(index, ColumnAddress, wxString::Format("%08x", bp->address)); const char* typeName = "UKN"; if (bp->bpType == DEBUGGER_BP_T_NORMAL) typeName = "X"; else if (bp->bpType == DEBUGGER_BP_T_ONE_SHOT) typeName = "XS"; else if (bp->bpType == DEBUGGER_BP_T_MEMORY_READ) typeName = "R"; else if (bp->bpType == DEBUGGER_BP_T_MEMORY_WRITE) typeName = "W"; m_breakpoints->SetItem(index, ColumnType, typeName); m_breakpoints->SetItem(index, ColumnComment, bp->comment); m_breakpoints->SetItemPtrData(item, (wxUIntPtr)bp); m_breakpoints->Check(index, bp->enabled); bp = bp->next; } } } Thaw(); } void BreakpointWindow::OnGameLoaded() { OnUpdateView(); } void BreakpointWindow::OnBreakpointToggled(wxListEvent& event) { const int32_t index = event.GetIndex(); if (0 <= index && index < m_breakpoints->GetItemCount()) { const bool state = m_breakpoints->IsChecked(index); wxString line = m_breakpoints->GetItemText(index, ColumnAddress); DebuggerBreakpoint* bp = (DebuggerBreakpoint*)m_breakpoints->GetItemData(index); const uint32 address = std::stoul(line.c_str().AsChar(), nullptr, 16); debugger_toggleBreakpoint(address, state, bp); } } void BreakpointWindow::OnLeftDClick(wxMouseEvent& event) { const auto position = event.GetPosition(); const sint32 index = (position.y / m_breakpoints->GetCharHeight()) - 2; if (index < 0 || index >= m_breakpoints->GetItemCount()) return; sint32 x = position.x; const auto enabled_width = m_breakpoints->GetColumnWidth(ColumnEnabled); if (x <= enabled_width) return; x -= enabled_width; const auto address_width = m_breakpoints->GetColumnWidth(ColumnAddress); if(x <= address_width) { const auto item = m_breakpoints->GetItemText(index, ColumnAddress); const auto address = std::stoul(item.ToStdString(), nullptr, 16); debuggerState.debugSession.instructionPointer = address; debuggerWindow_moveIP(); return; } x -= address_width; const auto type_width = m_breakpoints->GetColumnWidth(ColumnType); if (x <= type_width) return; x -= type_width; const auto comment_width = m_breakpoints->GetColumnWidth(ColumnComment); if(x <= comment_width) { if (index >= debuggerState.breakpoints.size()) return; const auto item = m_breakpoints->GetItemText(index, ColumnAddress); const auto address = std::stoul(item.ToStdString(), nullptr, 16); auto it = debuggerState.breakpoints.begin(); std::advance(it, index); wxTextEntryDialog set_value_dialog(this, _("Enter a new comment."), wxString::Format(_("Set comment for breakpoint at address %08x"), address), (*it)->comment); if (set_value_dialog.ShowModal() == wxID_OK) { (*it)->comment = set_value_dialog.GetValue().ToStdWstring(); m_breakpoints->SetItem(index, ColumnComment, set_value_dialog.GetValue()); } return; } } void BreakpointWindow::OnRightDown(wxMouseEvent& event) { wxMenu menu; menu.Append(MENU_ID_CREATE_MEM_BP_READ, _("Create memory breakpoint (read)")); menu.Append(MENU_ID_CREATE_MEM_BP_WRITE, _("Create memory breakpoint (write)")); menu.Connect(wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(BreakpointWindow::OnContextMenuClick), nullptr, this); PopupMenu(&menu); } void BreakpointWindow::OnContextMenuClick(wxCommandEvent& evt) { switch (evt.GetId()) { case MENU_ID_CREATE_MEM_BP_READ: MemoryBreakpointDialog(false); return; case MENU_ID_CREATE_MEM_BP_WRITE: MemoryBreakpointDialog(true); return; } } void BreakpointWindow::MemoryBreakpointDialog(bool isWrite) { wxTextEntryDialog goto_dialog(this, _("Enter a memory address"), _("Memory breakpoint"), wxEmptyString); if (goto_dialog.ShowModal() == wxID_OK) { ExpressionParser parser; auto value = goto_dialog.GetValue().ToStdString(); std::transform(value.begin(), value.end(), value.begin(), tolower); try { debugger_addParserSymbols(parser); const auto result = (uint32)parser.Evaluate(value); debug_printf("goto eval result: %x\n", result); debugger_createMemoryBreakpoint(result, isWrite == false, isWrite == true); this->OnUpdateView(); } catch (const std::exception& e) { //ctx.errorHandler.printError(nullptr, -1, fmt::format("Unexpected error in expression \"{}\"", expressionString)); //return EXPRESSION_RESOLVE_RESULT::EXPRESSION_ERROR; wxMessageBox(e.what(), "Invalid expression"); } } }