mirror of
https://github.com/cemu-project/Cemu.git
synced 2024-12-02 14:04:18 +01:00
d22901be1c
* Make PPC threads/texture cache info window columns untranslatable * Make several window titles translatable * Make About window text translatable * Fix <profile name> placeholder not being recognized as translatable * Miscellaneous improvements to GUI strings * Add a few missing entries to gitignore * Adjust Italian translation of Linux files
676 lines
21 KiB
C++
676 lines
21 KiB
C++
#include "gui/wxgui.h"
|
|
#include "gui/debugger/DebuggerWindow2.h"
|
|
|
|
#include <filesystem>
|
|
|
|
#include "config/ActiveSettings.h"
|
|
#include "Cafe/OS/RPL/rpl_structs.h"
|
|
#include "Cafe/OS/RPL/rpl_debug_symbols.h"
|
|
|
|
#include "gui/debugger/RegisterWindow.h"
|
|
#include "gui/debugger/DumpWindow.h"
|
|
|
|
#include "Cafe/HW/Espresso/Debugger/Debugger.h"
|
|
|
|
#include "Cafe/OS/RPL/rpl.h"
|
|
#include "gui/debugger/DisasmCtrl.h"
|
|
#include "gui/debugger/SymbolWindow.h"
|
|
#include "gui/debugger/BreakpointWindow.h"
|
|
#include "gui/debugger/ModuleWindow.h"
|
|
#include "util/helpers/helpers.h"
|
|
|
|
#if BOOST_OS_LINUX
|
|
#include "resource/embedded/resources.h"
|
|
#endif
|
|
|
|
enum
|
|
{
|
|
// file
|
|
MENU_ID_FILE_EXIT = wxID_HIGHEST + 8000,
|
|
// settings
|
|
MENU_ID_OPTIONS_PIN_TO_MAINWINDOW,
|
|
MENU_ID_OPTIONS_BREAK_ON_START,
|
|
// window
|
|
MENU_ID_WINDOW_REGISTERS,
|
|
MENU_ID_WINDOW_DUMP,
|
|
MENU_ID_WINDOW_STACK,
|
|
MENU_ID_WINDOW_BREAKPOINTS,
|
|
MENU_ID_WINDOW_MODULE,
|
|
MENU_ID_WINDOW_SYMBOL,
|
|
|
|
// tool
|
|
TOOL_ID_GOTO,
|
|
TOOL_ID_BP,
|
|
TOOL_ID_PAUSE,
|
|
TOOL_ID_STEP_OVER,
|
|
TOOL_ID_STEP_INTO,
|
|
};
|
|
|
|
wxDEFINE_EVENT(wxEVT_DEBUGGER_CLOSE, wxCloseEvent);
|
|
wxDEFINE_EVENT(wxEVT_UPDATE_VIEW, wxCommandEvent);
|
|
wxDEFINE_EVENT(wxEVT_BREAKPOINT_CHANGE, wxCommandEvent);
|
|
wxDEFINE_EVENT(wxEVT_BREAKPOINT_HIT, wxCommandEvent);
|
|
wxDEFINE_EVENT(wxEVT_MOVE_IP, wxCommandEvent);
|
|
wxDEFINE_EVENT(wxEVT_RUN, wxCommandEvent);
|
|
wxDEFINE_EVENT(wxEVT_NOTIFY_MODULE_LOADED, wxCommandEvent);
|
|
wxDEFINE_EVENT(wxEVT_NOTIFY_MODULE_UNLOADED, wxCommandEvent);
|
|
|
|
wxBEGIN_EVENT_TABLE(DebuggerWindow2, wxFrame)
|
|
EVT_SHOW(DebuggerWindow2::OnShow)
|
|
EVT_CLOSE(DebuggerWindow2::OnClose)
|
|
EVT_COMMAND(wxID_ANY, wxEVT_UPDATE_VIEW, DebuggerWindow2::OnUpdateView)
|
|
EVT_COMMAND(wxID_ANY, wxEVT_BREAKPOINT_CHANGE, DebuggerWindow2::OnBreakpointChange)
|
|
EVT_COMMAND(wxID_ANY, wxEVT_MOVE_IP, DebuggerWindow2::OnMoveIP)
|
|
EVT_COMMAND(wxID_ANY, wxEVT_COMMAND_TOOL_CLICKED, DebuggerWindow2::OnToolClicked)
|
|
EVT_COMMAND(wxID_ANY, wxEVT_BREAKPOINT_HIT, DebuggerWindow2::OnBreakpointHit)
|
|
EVT_COMMAND(wxID_ANY, wxEVT_RUN, DebuggerWindow2::OnRunProgram)
|
|
EVT_COMMAND(wxID_ANY, wxEVT_NOTIFY_MODULE_LOADED, DebuggerWindow2::OnNotifyModuleLoaded)
|
|
EVT_COMMAND(wxID_ANY, wxEVT_NOTIFY_MODULE_UNLOADED, DebuggerWindow2::OnNotifyModuleUnloaded)
|
|
// file menu
|
|
EVT_MENU(MENU_ID_FILE_EXIT, DebuggerWindow2::OnExit)
|
|
// setting
|
|
EVT_MENU(MENU_ID_OPTIONS_PIN_TO_MAINWINDOW, DebuggerWindow2::OnOptionsInput)
|
|
// window
|
|
EVT_MENU_RANGE(MENU_ID_WINDOW_REGISTERS, MENU_ID_WINDOW_MODULE, DebuggerWindow2::OnWindowMenu)
|
|
wxEND_EVENT_TABLE()
|
|
|
|
DebuggerWindow2* g_debugger_window;
|
|
|
|
void DebuggerConfig::Load(XMLConfigParser& parser)
|
|
{
|
|
pin_to_main = parser.get("PinToMainWindow", true);
|
|
break_on_start = parser.get("break_on_start", true);
|
|
|
|
auto window_parser = parser.get("Windows");
|
|
show_register = window_parser.get("Registers", true);
|
|
show_dump = window_parser.get("MemoryDump", true);
|
|
show_stack = window_parser.get("Stack", true);
|
|
show_breakpoints = window_parser.get("Breakpoints", true);
|
|
show_modules = window_parser.get("Modules", true);
|
|
show_symbols = window_parser.get("Symbols", true);
|
|
}
|
|
|
|
void DebuggerConfig::Save(XMLConfigParser& parser)
|
|
{
|
|
parser.set("PinToMainWindow", pin_to_main);
|
|
parser.set("break_on_start", break_on_start);
|
|
|
|
auto window_parser = parser.set("Windows");
|
|
window_parser.set("Registers", show_register);
|
|
window_parser.set("MemoryDump", show_dump);
|
|
window_parser.set("Stack", show_stack);
|
|
window_parser.set("Breakpoints", show_breakpoints);
|
|
window_parser.set("Modules", show_modules);
|
|
window_parser.set("Symbols", show_symbols);
|
|
}
|
|
|
|
void DebuggerModuleStorage::Load(XMLConfigParser& parser)
|
|
{
|
|
auto breakpoints_parser = parser.get("Breakpoints");
|
|
for (auto element = breakpoints_parser.get("Entry"); element.valid(); element = breakpoints_parser.get("Entry", element))
|
|
{
|
|
const auto address_string = element.get("Address", "");
|
|
if (*address_string == '\0')
|
|
continue;
|
|
|
|
uint32 relative_address = std::stoul(address_string, nullptr, 16);
|
|
if (relative_address == 0)
|
|
continue;
|
|
|
|
auto type = element.get("Type", 0);
|
|
auto enabled = element.get("Enabled", true);
|
|
const auto comment = element.get("Comment", "");
|
|
|
|
// calculate absolute address
|
|
uint32 module_base_address = (type == DEBUGGER_BP_T_NORMAL ? this->rpl_module->regionMappingBase_text.GetMPTR() : this->rpl_module->regionMappingBase_data);
|
|
uint32 address = module_base_address + relative_address;
|
|
|
|
// don't change anything if there's already a breakpoint
|
|
if (debugger_getFirstBP(address) != nullptr)
|
|
continue;
|
|
|
|
// register breakpoints in debugger
|
|
if (type == DEBUGGER_BP_T_NORMAL)
|
|
debugger_createExecuteBreakpoint(address);
|
|
else if (type == DEBUGGER_BP_T_MEMORY_READ)
|
|
debugger_createMemoryBreakpoint(address, true, false);
|
|
else if (type == DEBUGGER_BP_T_MEMORY_WRITE)
|
|
debugger_createMemoryBreakpoint(address, false, true);
|
|
else
|
|
continue;
|
|
|
|
DebuggerBreakpoint* debugBreakpoint = debugger_getFirstBP(address);
|
|
|
|
if (!enabled)
|
|
debugger_toggleBreakpoint(address, false, debugBreakpoint);
|
|
|
|
debugBreakpoint->comment = boost::nowide::widen(comment);
|
|
}
|
|
|
|
auto comments_parser = parser.get("Comments");
|
|
for (XMLConfigParser element = comments_parser.get("Entry"); element.valid(); element = comments_parser.get("Entry", element))
|
|
{
|
|
const auto address_string = element.get("Address", "");
|
|
if (*address_string == '\0')
|
|
continue;
|
|
|
|
uint32 address = std::stoul(address_string, nullptr, 16);
|
|
if (address == 0)
|
|
continue;
|
|
|
|
const auto comment = element.get("Comment", "");
|
|
if (*comment == '\0')
|
|
continue;
|
|
|
|
rplDebugSymbol_createComment(address, boost::nowide::widen(comment).c_str());
|
|
}
|
|
}
|
|
|
|
void DebuggerModuleStorage::Save(XMLConfigParser& parser)
|
|
{
|
|
auto breakpoints_parser = parser.set("Breakpoints");
|
|
for (const auto& bp : debuggerState.breakpoints)
|
|
{
|
|
// check breakpoint type
|
|
if (bp->dbType != DEBUGGER_BP_T_DEBUGGER)
|
|
continue;
|
|
|
|
// check whether the breakpoint is part of the current module being saved
|
|
RPLModule* address_module;
|
|
if (bp->bpType == DEBUGGER_BP_T_NORMAL) address_module = RPLLoader_FindModuleByCodeAddr(bp->address);
|
|
else if (bp->isMemBP()) address_module = RPLLoader_FindModuleByDataAddr(bp->address);
|
|
else continue;
|
|
|
|
if (!address_module || !(address_module->moduleName2 == this->module_name && address_module->patchCRC == this->crc_hash))
|
|
continue;
|
|
|
|
uint32_t relative_address = bp->address - (bp->isMemBP() ? address_module->regionMappingBase_data : address_module->regionMappingBase_text.GetMPTR());
|
|
auto entry = breakpoints_parser.set("Entry");
|
|
entry.set("Address", fmt::format("{:#10x}", relative_address));
|
|
entry.set("Comment", boost::nowide::narrow(bp->comment).c_str());
|
|
entry.set("Type", bp->bpType);
|
|
entry.set("Enabled", bp->enabled);
|
|
|
|
if (this->delete_breakpoints_after_saving) debugger_deleteBreakpoint(bp);
|
|
this->delete_breakpoints_after_saving = false;
|
|
}
|
|
|
|
auto comments_parser = parser.set("Comments");
|
|
for (const auto& comment_entry : rplDebugSymbol_getSymbols())
|
|
{
|
|
// check comment type
|
|
const auto comment_address = comment_entry.first;
|
|
const auto comment = static_cast<rplDebugSymbolComment*>(comment_entry.second);
|
|
if (!comment || comment->type != RplDebugSymbolComment)
|
|
continue;
|
|
|
|
// check whether it's part of the current module being saved
|
|
RPLModule* address_module = RPLLoader_FindModuleByCodeAddr(comment_entry.first);
|
|
if (!address_module || !(address_module->moduleName2 == module_name && address_module->patchCRC == this->crc_hash))
|
|
continue;
|
|
|
|
uint32_t relative_address = comment_address - address_module->regionMappingBase_text.GetMPTR();
|
|
auto entry = comments_parser.set("Entry");
|
|
entry.set("Address", fmt::format("{:#10x}", relative_address));
|
|
entry.set("Comment", boost::nowide::narrow(comment->comment).c_str());
|
|
}
|
|
}
|
|
|
|
void DebuggerWindow2::CreateToolBar()
|
|
{
|
|
m_toolbar = wxFrame::CreateToolBar(wxTB_HORIZONTAL, wxID_ANY);
|
|
m_toolbar->SetToolBitmapSize(wxSize(1, 1));
|
|
|
|
m_toolbar->AddTool(TOOL_ID_GOTO, wxEmptyString, wxBITMAP_PNG(DEBUGGER_GOTO), wxNullBitmap, wxITEM_NORMAL, _("GoTo (CTRL + G)"), "test", NULL);
|
|
m_toolbar->AddSeparator();
|
|
|
|
m_toolbar->AddTool(TOOL_ID_BP, wxEmptyString, wxBITMAP_PNG(DEBUGGER_BP_RED), wxNullBitmap, wxITEM_NORMAL, _("Toggle Breakpoint (F9)"), wxEmptyString, NULL);
|
|
m_toolbar->AddSeparator();
|
|
|
|
m_pause = wxBITMAP_PNG(DEBUGGER_PAUSE);
|
|
m_run = wxBITMAP_PNG(DEBUGGER_PLAY);
|
|
m_toolbar->AddTool(TOOL_ID_PAUSE, wxEmptyString, m_pause, wxNullBitmap, wxITEM_NORMAL, _("Break (F5)"), wxEmptyString, NULL);
|
|
|
|
m_toolbar->AddTool(TOOL_ID_STEP_INTO, wxEmptyString, wxBITMAP_PNG(DEBUGGER_STEP_INTO), wxNullBitmap, wxITEM_NORMAL, _("Step Into (F11)"), wxEmptyString, NULL);
|
|
m_toolbar->AddTool(TOOL_ID_STEP_OVER, wxEmptyString, wxBITMAP_PNG(DEBUGGER_STEP_OVER), wxNullBitmap, wxITEM_NORMAL, _("Step Over (F10)"), wxEmptyString, NULL);
|
|
m_toolbar->AddSeparator();
|
|
|
|
m_toolbar->Realize();
|
|
|
|
m_toolbar->EnableTool(TOOL_ID_STEP_INTO, false);
|
|
m_toolbar->EnableTool(TOOL_ID_STEP_OVER, false);
|
|
}
|
|
|
|
void DebuggerWindow2::SaveModuleStorage(const RPLModule* module, bool delete_breakpoints)
|
|
{
|
|
auto path = GetModuleStoragePath(module->moduleName2, module->patchCRC);
|
|
for (auto& module_storage : m_modules_storage)
|
|
{
|
|
if (module_storage->data().module_name == module->moduleName2 && module_storage->data().crc_hash == module->patchCRC)
|
|
{
|
|
module_storage->data().delete_breakpoints_after_saving = delete_breakpoints;
|
|
module_storage->Save(path);
|
|
if (delete_breakpoints) m_modules_storage.erase(std::find(m_modules_storage.begin(), m_modules_storage.end(), module_storage));
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
void DebuggerWindow2::LoadModuleStorage(const RPLModule* module)
|
|
{
|
|
auto path = GetModuleStoragePath(module->moduleName2, module->patchCRC);
|
|
bool already_loaded = std::any_of(m_modules_storage.begin(), m_modules_storage.end(), [path](const std::unique_ptr<XMLDebuggerModuleConfig>& debug) { return debug->GetFilename() == path; });
|
|
if (!path.empty() && !already_loaded)
|
|
{
|
|
m_modules_storage.emplace_back(std::move(new XMLDebuggerModuleConfig(path, { module->moduleName2, module->patchCRC, module, false })));
|
|
}
|
|
}
|
|
|
|
DebuggerWindow2::DebuggerWindow2(wxFrame& parent, const wxRect& display_size)
|
|
: wxFrame(&parent, wxID_ANY, _("PPC Debugger"), wxDefaultPosition, wxSize(1280, 300), wxMINIMIZE_BOX | wxMAXIMIZE_BOX | wxSYSTEM_MENU | wxCAPTION | wxCLOSE_BOX | wxCLIP_CHILDREN | wxRESIZE_BORDER | wxFRAME_FLOAT_ON_PARENT),
|
|
m_module_address(0)
|
|
{
|
|
this->wxWindowBase::SetBackgroundColour(*wxWHITE);
|
|
|
|
const auto file = ActiveSettings::GetPath("debugger/config.xml");
|
|
m_config.SetFilename(file.generic_wstring());
|
|
m_config.Load();
|
|
|
|
debuggerState.breakOnEntry = m_config.data().break_on_start;
|
|
|
|
m_main_position = parent.GetPosition();
|
|
m_main_size = parent.GetSize();
|
|
|
|
auto y = std::max<uint32>(300, (display_size.GetHeight() - 500 - 300) * 0.8);
|
|
this->SetSize(1280, y);
|
|
|
|
CreateMenuBar();
|
|
CreateToolBar();
|
|
|
|
wxBoxSizer* main_sizer = new wxBoxSizer(wxVERTICAL);
|
|
|
|
// load configs for already loaded modules
|
|
const auto module_count = RPLLoader_GetModuleCount();
|
|
const auto module_list = RPLLoader_GetModuleList();
|
|
for (sint32 i = 0; i < module_count; i++)
|
|
{
|
|
const auto module = module_list[i];
|
|
LoadModuleStorage(module);
|
|
}
|
|
|
|
wxString label_text = _("> no modules loaded");
|
|
if (module_count != 0)
|
|
{
|
|
RPLModule* current_rpl_module = RPLLoader_FindModuleByCodeAddr(MEMORY_CODEAREA_ADDR);
|
|
if (current_rpl_module)
|
|
label_text = wxString::Format("> %s", current_rpl_module->moduleName2.c_str());
|
|
else
|
|
label_text = _("> unknown module");
|
|
}
|
|
|
|
m_module_label = new wxStaticText(this, wxID_ANY, label_text);
|
|
m_module_label->SetBackgroundColour(*wxWHITE);
|
|
m_module_label->SetForegroundColour(wxColour(0xFFbf52fe));
|
|
main_sizer->Add(m_module_label, 0, wxEXPAND | wxALL, 5);
|
|
|
|
m_disasm_ctrl = new DisasmCtrl(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxScrolledWindowStyle);
|
|
main_sizer->Add(m_disasm_ctrl, 1, wxEXPAND);
|
|
|
|
this->SetSizer(main_sizer);
|
|
this->wxWindowBase::Layout();
|
|
|
|
m_register_window = new RegisterWindow(*this, m_main_position, m_main_size);
|
|
m_dump_window = new DumpWindow(*this, m_main_position, m_main_size);
|
|
m_breakpoint_window = new BreakpointWindow(*this, m_main_position, m_main_size);
|
|
m_module_window = new ModuleWindow(*this, m_main_position, m_main_size);
|
|
m_symbol_window = new SymbolWindow(*this, m_main_position, m_main_size);
|
|
|
|
const bool value = m_config.data().pin_to_main;
|
|
m_config.data().pin_to_main = true;
|
|
OnParentMove(m_main_position, m_main_size);
|
|
m_config.data().pin_to_main = value;
|
|
|
|
g_debugger_window = this;
|
|
}
|
|
|
|
DebuggerWindow2::~DebuggerWindow2()
|
|
{
|
|
debuggerState.breakOnEntry = false;
|
|
g_debugger_window = nullptr;
|
|
|
|
// save configs for all modules that are still loaded
|
|
// doesn't delete breakpoints since that should (in the future) be done by unloading the rpl modules when exiting the current game
|
|
const auto module_count = RPLLoader_GetModuleCount();
|
|
const auto module_list = RPLLoader_GetModuleList();
|
|
for (sint32 i = 0; i < module_count; i++)
|
|
{
|
|
const auto module = module_list[i];
|
|
if (module)
|
|
SaveModuleStorage(module, false);
|
|
}
|
|
|
|
if (m_register_window && m_register_window->IsShown())
|
|
m_register_window->Close(true);
|
|
|
|
if (m_dump_window && m_dump_window->IsShown())
|
|
m_dump_window->Close(true);
|
|
|
|
if (m_breakpoint_window && m_breakpoint_window->IsShown())
|
|
m_breakpoint_window->Close(true);
|
|
|
|
if (m_module_window && m_module_window->IsShown())
|
|
m_module_window->Close(true);
|
|
|
|
if (m_symbol_window && m_symbol_window->IsShown())
|
|
m_symbol_window->Close(true);
|
|
|
|
m_config.Save();
|
|
}
|
|
|
|
void DebuggerWindow2::OnClose(wxCloseEvent& event)
|
|
{
|
|
debuggerState.breakOnEntry = false;
|
|
|
|
const wxCloseEvent parentEvent(wxEVT_DEBUGGER_CLOSE);
|
|
wxPostEvent(m_parent, parentEvent);
|
|
|
|
event.Skip();
|
|
}
|
|
|
|
void DebuggerWindow2::OnMoveIP(wxCommandEvent& event)
|
|
{
|
|
const auto ip = debuggerState.debugSession.instructionPointer;
|
|
UpdateModuleLabel(ip);
|
|
m_disasm_ctrl->CenterOffset(ip);
|
|
}
|
|
|
|
void DebuggerWindow2::OnParentMove(const wxPoint& main_position, const wxSize& main_size)
|
|
{
|
|
m_main_position = main_position;
|
|
m_main_size = main_size;
|
|
|
|
if (!m_config.data().pin_to_main)
|
|
return;
|
|
|
|
wxSize size(m_main_size.x, GetSize().GetHeight());
|
|
SetSize(size);
|
|
|
|
wxPoint position = m_main_position;
|
|
position.y -= size.y;
|
|
SetPosition(position);
|
|
|
|
m_register_window->OnMainMove(main_position, main_size);
|
|
m_dump_window->OnMainMove(main_position, main_size);
|
|
m_breakpoint_window->OnMainMove(main_position, main_size);
|
|
m_module_window->OnMainMove(main_position, main_size);
|
|
m_symbol_window->OnMainMove(main_position, main_size);
|
|
}
|
|
|
|
void DebuggerWindow2::OnNotifyModuleLoaded(wxCommandEvent& event)
|
|
{
|
|
RPLModule* module = (RPLModule*)event.GetClientData();
|
|
LoadModuleStorage(module);
|
|
m_module_window->OnGameLoaded();
|
|
m_symbol_window->OnGameLoaded();
|
|
m_disasm_ctrl->Init();
|
|
}
|
|
|
|
void DebuggerWindow2::OnNotifyModuleUnloaded(wxCommandEvent& event)
|
|
{
|
|
RPLModule* module = (RPLModule*)event.GetClientData();
|
|
SaveModuleStorage(module, true);
|
|
m_module_window->OnGameLoaded();
|
|
m_symbol_window->OnGameLoaded();
|
|
m_disasm_ctrl->Init();
|
|
}
|
|
|
|
void DebuggerWindow2::OnGameLoaded()
|
|
{
|
|
m_disasm_ctrl->Init();
|
|
|
|
m_dump_window->OnGameLoaded();
|
|
m_module_window->OnGameLoaded();
|
|
m_breakpoint_window->OnGameLoaded();
|
|
m_symbol_window->OnGameLoaded();
|
|
|
|
RPLModule* current_rpl_module = RPLLoader_FindModuleByCodeAddr(MEMORY_CODEAREA_ADDR);
|
|
if(current_rpl_module)
|
|
m_module_label->SetLabel(wxString::Format("> %s", current_rpl_module->moduleName2.c_str()));
|
|
|
|
this->SendSizeEvent();
|
|
}
|
|
|
|
XMLDebuggerConfig& DebuggerWindow2::GetConfig()
|
|
{
|
|
return m_config;
|
|
}
|
|
|
|
bool DebuggerWindow2::Show(bool show)
|
|
{
|
|
const bool result = wxFrame::Show(show);
|
|
|
|
if (show)
|
|
{
|
|
m_register_window->Show(m_config.data().show_register);
|
|
m_dump_window->Show(m_config.data().show_dump);
|
|
m_breakpoint_window->Show(m_config.data().show_breakpoints);
|
|
m_module_window->Show(m_config.data().show_modules);
|
|
m_symbol_window->Show(m_config.data().show_symbols);
|
|
}
|
|
else
|
|
{
|
|
m_register_window->Show(false);
|
|
m_dump_window->Show(false);
|
|
m_breakpoint_window->Show(false);
|
|
m_module_window->Show(false);
|
|
m_symbol_window->Show(false);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
std::wstring DebuggerWindow2::GetModuleStoragePath(std::string module_name, uint32_t crc_hash) const
|
|
{
|
|
if (module_name.empty() || crc_hash == 0) return std::wstring();
|
|
return ActiveSettings::GetPath("debugger/{}_{:#10x}.xml", module_name, crc_hash).generic_wstring();
|
|
}
|
|
|
|
void DebuggerWindow2::OnBreakpointHit(wxCommandEvent& event)
|
|
{
|
|
const auto ip = debuggerState.debugSession.instructionPointer;
|
|
UpdateModuleLabel(ip);
|
|
|
|
m_toolbar->SetToolShortHelp(TOOL_ID_PAUSE, _("Run (F5)"));
|
|
m_toolbar->SetToolNormalBitmap(TOOL_ID_PAUSE, m_run);
|
|
|
|
m_toolbar->EnableTool(TOOL_ID_STEP_INTO, true);
|
|
m_toolbar->EnableTool(TOOL_ID_STEP_OVER, true);
|
|
|
|
m_disasm_ctrl->CenterOffset(ip);
|
|
}
|
|
|
|
void DebuggerWindow2::OnRunProgram(wxCommandEvent& event)
|
|
{
|
|
m_toolbar->SetToolShortHelp(TOOL_ID_PAUSE, _("Break (F5)"));
|
|
m_toolbar->SetToolNormalBitmap(TOOL_ID_PAUSE, m_pause);
|
|
|
|
m_toolbar->EnableTool(TOOL_ID_STEP_INTO, false);
|
|
m_toolbar->EnableTool(TOOL_ID_STEP_OVER, false);
|
|
}
|
|
|
|
void DebuggerWindow2::OnToolClicked(wxCommandEvent& event)
|
|
{
|
|
switch(event.GetId())
|
|
{
|
|
case TOOL_ID_GOTO:
|
|
m_disasm_ctrl->GoToAddressDialog();
|
|
break;
|
|
case TOOL_ID_PAUSE:
|
|
if (debugger_isTrapped())
|
|
debugger_resume();
|
|
else
|
|
debugger_forceBreak();
|
|
break;
|
|
case TOOL_ID_STEP_INTO:
|
|
if (debugger_isTrapped())
|
|
debuggerState.debugSession.stepInto = true;
|
|
break;
|
|
case TOOL_ID_STEP_OVER:
|
|
if (debugger_isTrapped())
|
|
debuggerState.debugSession.stepOver = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
void DebuggerWindow2::OnBreakpointChange(wxCommandEvent& event)
|
|
{
|
|
m_breakpoint_window->OnUpdateView();
|
|
UpdateModuleLabel();
|
|
}
|
|
|
|
void DebuggerWindow2::OnOptionsInput(wxCommandEvent& event)
|
|
{
|
|
switch(event.GetId())
|
|
{
|
|
case MENU_ID_OPTIONS_PIN_TO_MAINWINDOW:
|
|
{
|
|
const bool value = !m_config.data().pin_to_main;
|
|
m_config.data().pin_to_main = value;
|
|
if(value)
|
|
OnParentMove(m_main_position, m_main_size);
|
|
|
|
break;
|
|
}
|
|
case MENU_ID_OPTIONS_BREAK_ON_START:
|
|
{
|
|
const bool value = !m_config.data().break_on_start;
|
|
m_config.data().break_on_start = value;
|
|
debuggerState.breakOnEntry = value;
|
|
break;
|
|
}
|
|
default:
|
|
return;
|
|
}
|
|
|
|
m_config.Save();
|
|
}
|
|
|
|
void DebuggerWindow2::OnWindowMenu(wxCommandEvent& event)
|
|
{
|
|
switch (event.GetId())
|
|
{
|
|
case MENU_ID_WINDOW_REGISTERS:
|
|
{
|
|
const bool value = !m_config.data().show_register;
|
|
m_config.data().show_register = value;
|
|
m_register_window->Show(value);
|
|
break;
|
|
}
|
|
case MENU_ID_WINDOW_DUMP:
|
|
{
|
|
const bool value = !m_config.data().show_dump;
|
|
m_config.data().show_dump = value;
|
|
m_dump_window->Show(value);
|
|
break;
|
|
}
|
|
case MENU_ID_WINDOW_BREAKPOINTS:
|
|
{
|
|
const bool value = !m_config.data().show_breakpoints;
|
|
m_config.data().show_breakpoints = value;
|
|
m_breakpoint_window->Show(value);
|
|
break;
|
|
}
|
|
case MENU_ID_WINDOW_MODULE:
|
|
{
|
|
const bool value = !m_config.data().show_modules;
|
|
m_config.data().show_modules = value;
|
|
m_module_window->Show(value);
|
|
break;
|
|
}
|
|
case MENU_ID_WINDOW_SYMBOL:
|
|
{
|
|
const bool value = !m_config.data().show_symbols;
|
|
m_config.data().show_symbols = value;
|
|
m_symbol_window->Show(value);
|
|
break;
|
|
}
|
|
default:
|
|
return;
|
|
}
|
|
|
|
m_config.Save();
|
|
}
|
|
|
|
void DebuggerWindow2::OnUpdateView(wxCommandEvent& event)
|
|
{
|
|
UpdateModuleLabel();
|
|
m_disasm_ctrl->OnUpdateView();
|
|
m_register_window->OnUpdateView();
|
|
m_breakpoint_window->OnUpdateView();
|
|
}
|
|
|
|
void DebuggerWindow2::OnExit(wxCommandEvent& event)
|
|
{
|
|
this->Close();
|
|
}
|
|
|
|
void DebuggerWindow2::OnShow(wxShowEvent& event)
|
|
{
|
|
m_register_window->Show();
|
|
m_dump_window->Show();
|
|
m_breakpoint_window->Show();
|
|
m_module_window->Show();
|
|
m_symbol_window->Show();
|
|
event.Skip();
|
|
}
|
|
|
|
void DebuggerWindow2::CreateMenuBar()
|
|
{
|
|
auto menu_bar = new wxMenuBar;
|
|
|
|
// file
|
|
wxMenu* file_menu = new wxMenu;
|
|
file_menu->Append(MENU_ID_FILE_EXIT, _("&Exit"));
|
|
file_menu->Bind(wxEVT_MENU, &DebuggerWindow2::OnExit, this);
|
|
|
|
menu_bar->Append(file_menu, _("&File"));
|
|
|
|
// options
|
|
wxMenu* options_menu = new wxMenu;
|
|
options_menu->Append(MENU_ID_OPTIONS_PIN_TO_MAINWINDOW, _("&Pin to main window"), wxEmptyString, wxITEM_CHECK)->Check(m_config.data().pin_to_main);
|
|
options_menu->Append(MENU_ID_OPTIONS_BREAK_ON_START, _("Break on &entry point"), wxEmptyString, wxITEM_CHECK)->Check(m_config.data().break_on_start);
|
|
menu_bar->Append(options_menu, _("&Options"));
|
|
|
|
// window
|
|
wxMenu* window_menu = new wxMenu;
|
|
window_menu->Append(MENU_ID_WINDOW_REGISTERS, _("&Registers"), wxEmptyString, wxITEM_CHECK)->Check(m_config.data().show_register);
|
|
window_menu->Append(MENU_ID_WINDOW_DUMP, _("&Memory Dump"), wxEmptyString, wxITEM_CHECK)->Check(m_config.data().show_dump);
|
|
window_menu->Append(MENU_ID_WINDOW_BREAKPOINTS, _("&Breakpoints"), wxEmptyString, wxITEM_CHECK)->Check(m_config.data().show_breakpoints);
|
|
window_menu->Append(MENU_ID_WINDOW_MODULE, _("Module&list"), wxEmptyString, wxITEM_CHECK)->Check(m_config.data().show_modules);
|
|
window_menu->Append(MENU_ID_WINDOW_SYMBOL, _("&Symbols"), wxEmptyString, wxITEM_CHECK)->Check(m_config.data().show_symbols);
|
|
|
|
menu_bar->Append(window_menu, _("&Window"));
|
|
|
|
SetMenuBar(menu_bar);
|
|
|
|
options_menu->Bind(wxEVT_MENU, &DebuggerWindow2::OnOptionsInput, this);
|
|
window_menu->Bind(wxEVT_MENU, &DebuggerWindow2::OnWindowMenu, this);
|
|
}
|
|
|
|
void DebuggerWindow2::UpdateModuleLabel(uint32 address)
|
|
{
|
|
if(address == 0)
|
|
address = m_disasm_ctrl->GetViewBaseAddress();
|
|
|
|
RPLModule* module = RPLLoader_FindModuleByCodeAddr(address);
|
|
if (module && m_module_address != module->regionMappingBase_text.GetMPTR())
|
|
{
|
|
m_module_label->SetLabel(wxString::Format("> %s", module->moduleName2.c_str()));
|
|
m_module_address = module->regionMappingBase_text.GetMPTR();
|
|
}
|
|
else if (address >= mmuRange_CODECAVE.getBase() && address < mmuRange_CODECAVE.getEnd())
|
|
{
|
|
m_module_label->SetLabel(wxString::Format("> %s", "Cemu codecave"));
|
|
m_module_address = mmuRange_CODECAVE.getBase();
|
|
}
|
|
} |