mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2025-01-17 11:39:13 +01:00
95660a5563
These ID values would clash with the window parent IDs of all the actual debugger panes (they are in the 350 range as well). For example, attempting to show and then close the memory window would cause an assertion, because it would attempt to destroy the text control for searching through memory, rather than destroying the actual parent window it's attached to. These IDs are only used locally, so their value doesn't matter.
451 lines
11 KiB
C++
451 lines
11 KiB
C++
// Copyright 2013 Dolphin Emulator Project
|
|
// Licensed under GPLv2
|
|
// Refer to the license.txt file included.
|
|
|
|
#include <cstddef>
|
|
#include <cstdio>
|
|
#include <cstring>
|
|
#include <string>
|
|
#include <vector>
|
|
#include <wx/button.h>
|
|
#include <wx/chartype.h>
|
|
#include <wx/checkbox.h>
|
|
#include <wx/defs.h>
|
|
#include <wx/event.h>
|
|
#include <wx/gdicmn.h>
|
|
#include <wx/listbox.h>
|
|
#include <wx/msgdlg.h>
|
|
#include <wx/panel.h>
|
|
#include <wx/sizer.h>
|
|
#include <wx/string.h>
|
|
#include <wx/textctrl.h>
|
|
#include <wx/translation.h>
|
|
#include <wx/window.h>
|
|
#include <wx/windowid.h>
|
|
#include <wx/wxcrtvararg.h>
|
|
|
|
#include "Common/CommonTypes.h"
|
|
#include "Common/FileUtil.h"
|
|
#include "Common/IniFile.h"
|
|
#include "Common/StringUtil.h"
|
|
#include "Common/SymbolDB.h"
|
|
#include "Core/ConfigManager.h"
|
|
#include "Core/CoreParameter.h"
|
|
#include "Core/Debugger/PPCDebugInterface.h"
|
|
#include "Core/HW/DSP.h"
|
|
#include "Core/HW/Memmap.h"
|
|
#include "Core/PowerPC/PowerPC.h"
|
|
#include "DolphinWX/Globals.h"
|
|
#include "DolphinWX/WxUtils.h"
|
|
#include "DolphinWX/Debugger/MemoryView.h"
|
|
#include "DolphinWX/Debugger/MemoryWindow.h"
|
|
|
|
class DebugInterface;
|
|
|
|
enum
|
|
{
|
|
IDM_MEM_ADDRBOX,
|
|
IDM_SYMBOLLIST,
|
|
IDM_SETVALBUTTON,
|
|
IDM_DUMP_MEMORY,
|
|
IDM_DUMP_MEM2,
|
|
IDM_DUMP_FAKEVMEM,
|
|
IDM_VALBOX,
|
|
IDM_U8,
|
|
IDM_U16,
|
|
IDM_U32,
|
|
IDM_SEARCH,
|
|
IDM_ASCII,
|
|
IDM_HEX
|
|
};
|
|
|
|
BEGIN_EVENT_TABLE(CMemoryWindow, wxPanel)
|
|
EVT_TEXT(IDM_MEM_ADDRBOX, CMemoryWindow::OnAddrBoxChange)
|
|
EVT_LISTBOX(IDM_SYMBOLLIST, CMemoryWindow::OnSymbolListChange)
|
|
EVT_HOST_COMMAND(wxID_ANY, CMemoryWindow::OnHostMessage)
|
|
EVT_BUTTON(IDM_SETVALBUTTON, CMemoryWindow::SetMemoryValue)
|
|
EVT_BUTTON(IDM_DUMP_MEMORY, CMemoryWindow::OnDumpMemory)
|
|
EVT_BUTTON(IDM_DUMP_MEM2, CMemoryWindow::OnDumpMem2)
|
|
EVT_BUTTON(IDM_DUMP_FAKEVMEM, CMemoryWindow::OnDumpFakeVMEM)
|
|
EVT_CHECKBOX(IDM_U8, CMemoryWindow::U8)
|
|
EVT_CHECKBOX(IDM_U16, CMemoryWindow::U16)
|
|
EVT_CHECKBOX(IDM_U32, CMemoryWindow::U32)
|
|
EVT_BUTTON(IDM_SEARCH, CMemoryWindow::onSearch)
|
|
EVT_CHECKBOX(IDM_ASCII, CMemoryWindow::onAscii)
|
|
EVT_CHECKBOX(IDM_HEX, CMemoryWindow::onHex)
|
|
END_EVENT_TABLE()
|
|
|
|
CMemoryWindow::CMemoryWindow(wxWindow* parent, wxWindowID id,
|
|
const wxPoint& pos, const wxSize& size, long style, const wxString& name)
|
|
: wxPanel(parent, id, pos, size, style, name)
|
|
{
|
|
wxBoxSizer* sizerBig = new wxBoxSizer(wxHORIZONTAL);
|
|
wxBoxSizer* sizerRight = new wxBoxSizer(wxVERTICAL);
|
|
// Didn't see anything useful in the left part
|
|
//wxBoxSizer* sizerLeft = new wxBoxSizer(wxVERTICAL);
|
|
|
|
DebugInterface* di = &PowerPC::debug_interface;
|
|
|
|
//symbols = new wxListBox(this, IDM_SYMBOLLIST, wxDefaultPosition,
|
|
// wxSize(20, 100), 0, nullptr, wxLB_SORT);
|
|
//sizerLeft->Add(symbols, 1, wxEXPAND);
|
|
memview = new CMemoryView(di, this);
|
|
memview->dataType = 0;
|
|
//sizerBig->Add(sizerLeft, 1, wxEXPAND);
|
|
sizerBig->Add(memview, 20, wxEXPAND);
|
|
sizerBig->Add(sizerRight, 0, wxEXPAND | wxALL, 3);
|
|
sizerRight->Add(addrbox = new wxTextCtrl(this, IDM_MEM_ADDRBOX, ""));
|
|
sizerRight->Add(valbox = new wxTextCtrl(this, IDM_VALBOX, ""));
|
|
sizerRight->Add(new wxButton(this, IDM_SETVALBUTTON, _("Set &Value")));
|
|
|
|
sizerRight->AddSpacer(5);
|
|
sizerRight->Add(new wxButton(this, IDM_DUMP_MEMORY, _("&Dump MRAM")));
|
|
sizerRight->Add(new wxButton(this, IDM_DUMP_MEM2, _("&Dump EXRAM")));
|
|
|
|
if (SConfig::GetInstance().m_LocalCoreStartupParameter.bTLBHack == true)
|
|
sizerRight->Add(new wxButton(this, IDM_DUMP_FAKEVMEM, _("&Dump FakeVMEM")));
|
|
|
|
wxStaticBoxSizer* sizerSearchType = new wxStaticBoxSizer(wxVERTICAL, this, _("Search"));
|
|
|
|
sizerSearchType->Add(btnSearch = new wxButton(this, IDM_SEARCH, _("Search")));
|
|
sizerSearchType->Add(chkAscii = new wxCheckBox(this, IDM_ASCII, "&Ascii "));
|
|
sizerSearchType->Add(chkHex = new wxCheckBox(this, IDM_HEX, _("&Hex")));
|
|
sizerRight->Add(sizerSearchType);
|
|
wxStaticBoxSizer* sizerDataTypes = new wxStaticBoxSizer(wxVERTICAL, this, _("Data Type"));
|
|
|
|
sizerDataTypes->SetMinSize(74, 40);
|
|
sizerDataTypes->Add(chk8 = new wxCheckBox(this, IDM_U8, "&U8"));
|
|
sizerDataTypes->Add(chk16 = new wxCheckBox(this, IDM_U16, "&U16"));
|
|
sizerDataTypes->Add(chk32 = new wxCheckBox(this, IDM_U32, "&U32"));
|
|
sizerRight->Add(sizerDataTypes);
|
|
SetSizer(sizerBig);
|
|
chkHex->SetValue(1); //Set defaults
|
|
chk8->SetValue(1);
|
|
|
|
//sizerLeft->Fit(this);
|
|
sizerRight->Fit(this);
|
|
sizerBig->Fit(this);
|
|
}
|
|
|
|
void CMemoryWindow::Save(IniFile& ini) const
|
|
{
|
|
// Prevent these bad values that can happen after a crash or hanging
|
|
if (GetPosition().x != -32000 && GetPosition().y != -32000)
|
|
{
|
|
IniFile::Section* mem_window = ini.GetOrCreateSection("MemoryWindow");
|
|
mem_window->Set("x", GetPosition().x);
|
|
mem_window->Set("y", GetPosition().y);
|
|
mem_window->Set("w", GetSize().GetWidth());
|
|
mem_window->Set("h", GetSize().GetHeight());
|
|
}
|
|
}
|
|
|
|
void CMemoryWindow::Load(IniFile& ini)
|
|
{
|
|
int x, y, w, h;
|
|
|
|
IniFile::Section* mem_window = ini.GetOrCreateSection("MemoryWindow");
|
|
mem_window->Get("x", &x, GetPosition().x);
|
|
mem_window->Get("y", &y, GetPosition().y);
|
|
mem_window->Get("w", &w, GetSize().GetWidth());
|
|
mem_window->Get("h", &h, GetSize().GetHeight());
|
|
|
|
SetSize(x, y, w, h);
|
|
}
|
|
|
|
void CMemoryWindow::JumpToAddress(u32 _Address)
|
|
{
|
|
memview->Center(_Address);
|
|
}
|
|
|
|
void CMemoryWindow::SetMemoryValue(wxCommandEvent& event)
|
|
{
|
|
if (!Memory::IsInitialized())
|
|
{
|
|
WxUtils::ShowErrorDialog(_("Cannot set uninitialized memory."));
|
|
return;
|
|
}
|
|
|
|
std::string str_addr = WxStrToStr(addrbox->GetValue());
|
|
std::string str_val = WxStrToStr(valbox->GetValue());
|
|
u32 addr;
|
|
u32 val;
|
|
|
|
if (!TryParse(std::string("0x") + str_addr, &addr))
|
|
{
|
|
WxUtils::ShowErrorDialog(wxString::Format(_("Invalid address: %s"), str_addr.c_str()));
|
|
return;
|
|
}
|
|
|
|
if (!TryParse(std::string("0x") + str_val, &val))
|
|
{
|
|
WxUtils::ShowErrorDialog(wxString::Format(_("Invalid value: %s"), str_val.c_str()));
|
|
return;
|
|
}
|
|
|
|
Memory::Write_U32(val, addr);
|
|
memview->Refresh();
|
|
}
|
|
|
|
void CMemoryWindow::OnAddrBoxChange(wxCommandEvent& event)
|
|
{
|
|
wxString txt = addrbox->GetValue();
|
|
if (txt.size())
|
|
{
|
|
u32 addr;
|
|
sscanf(WxStrToStr(txt).c_str(), "%08x", &addr);
|
|
memview->Center(addr & ~3);
|
|
}
|
|
|
|
event.Skip();
|
|
}
|
|
|
|
void CMemoryWindow::Update()
|
|
{
|
|
memview->Refresh();
|
|
memview->Center(PC);
|
|
}
|
|
|
|
void CMemoryWindow::NotifyMapLoaded()
|
|
{
|
|
symbols->Show(false); // hide it for faster filling
|
|
symbols->Clear();
|
|
#if 0
|
|
#ifdef _WIN32
|
|
const FunctionDB::XFuncMap &syms = g_symbolDB.Symbols();
|
|
for (FuntionDB::XFuncMap::iterator iter = syms.begin(); iter != syms.end(); ++iter)
|
|
{
|
|
int idx = symbols->Append(iter->second.name.c_str());
|
|
symbols->SetClientData(idx, (void*)&iter->second);
|
|
}
|
|
#endif
|
|
#endif
|
|
symbols->Show(true);
|
|
Update();
|
|
}
|
|
|
|
void CMemoryWindow::OnSymbolListChange(wxCommandEvent& event)
|
|
{
|
|
int index = symbols->GetSelection();
|
|
if (index >= 0)
|
|
{
|
|
Symbol* pSymbol = static_cast<Symbol *>(symbols->GetClientData(index));
|
|
if (pSymbol != nullptr)
|
|
{
|
|
memview->Center(pSymbol->address);
|
|
}
|
|
}
|
|
}
|
|
|
|
void CMemoryWindow::OnHostMessage(wxCommandEvent& event)
|
|
{
|
|
switch (event.GetId())
|
|
{
|
|
case IDM_NOTIFYMAPLOADED:
|
|
NotifyMapLoaded();
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void DumpArray(const std::string& filename, const u8* data, size_t length)
|
|
{
|
|
if (data)
|
|
{
|
|
File::IOFile f(filename, "wb");
|
|
f.WriteBytes(data, length);
|
|
}
|
|
}
|
|
|
|
// Write mram to file
|
|
void CMemoryWindow::OnDumpMemory( wxCommandEvent& event )
|
|
{
|
|
DumpArray(File::GetUserPath(F_RAMDUMP_IDX), Memory::m_pRAM, Memory::REALRAM_SIZE);
|
|
}
|
|
|
|
// Write exram (aram or mem2) to file
|
|
void CMemoryWindow::OnDumpMem2( wxCommandEvent& event )
|
|
{
|
|
if (SConfig::GetInstance().m_LocalCoreStartupParameter.bWii)
|
|
{
|
|
DumpArray(File::GetUserPath(F_ARAMDUMP_IDX), Memory::m_pEXRAM, Memory::EXRAM_SIZE);
|
|
}
|
|
else
|
|
{
|
|
DumpArray(File::GetUserPath(F_ARAMDUMP_IDX), DSP::GetARAMPtr(), DSP::ARAM_SIZE);
|
|
}
|
|
}
|
|
|
|
// Write fake vmem to file
|
|
void CMemoryWindow::OnDumpFakeVMEM( wxCommandEvent& event )
|
|
{
|
|
DumpArray(File::GetUserPath(F_FAKEVMEMDUMP_IDX), Memory::m_pVirtualFakeVMEM, Memory::FAKEVMEM_SIZE);
|
|
}
|
|
|
|
void CMemoryWindow::U8(wxCommandEvent& event)
|
|
{
|
|
chk16->SetValue(0);
|
|
chk32->SetValue(0);
|
|
memview->dataType = 0;
|
|
memview->Refresh();
|
|
}
|
|
|
|
void CMemoryWindow::U16(wxCommandEvent& event)
|
|
{
|
|
chk8->SetValue(0);
|
|
chk32->SetValue(0);
|
|
memview->dataType = 1;
|
|
memview->Refresh();
|
|
}
|
|
|
|
void CMemoryWindow::U32(wxCommandEvent& event)
|
|
{
|
|
chk16->SetValue(0);
|
|
chk8->SetValue(0);
|
|
memview->dataType = 2;
|
|
memview->Refresh();
|
|
}
|
|
|
|
void CMemoryWindow::onSearch(wxCommandEvent& event)
|
|
{
|
|
u8* TheRAM = nullptr;
|
|
u32 szRAM = 0;
|
|
switch (memview->GetMemoryType())
|
|
{
|
|
case 0:
|
|
default:
|
|
if (Memory::m_pRAM)
|
|
{
|
|
TheRAM = Memory::m_pRAM;
|
|
szRAM = Memory::REALRAM_SIZE;
|
|
}
|
|
break;
|
|
case 1:
|
|
{
|
|
u8* aram = DSP::GetARAMPtr();
|
|
if (aram)
|
|
{
|
|
TheRAM = aram;
|
|
szRAM = DSP::ARAM_SIZE;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
//Now we have memory to look in
|
|
//Are we looking for ASCII string, or hex?
|
|
//memview->cu
|
|
wxString rawData = valbox->GetValue();
|
|
std::vector<u8> Dest; //May need a better name
|
|
u32 size = 0;
|
|
int pad = rawData.size()%2; //If it's uneven
|
|
unsigned int i = 0;
|
|
long count = 0;
|
|
char copy[3] = {0};
|
|
long newsize = 0;
|
|
unsigned char *tmp2 = nullptr;
|
|
char* tmpstr = nullptr;
|
|
|
|
if (chkHex->GetValue())
|
|
{
|
|
//We are looking for hex
|
|
//If it's uneven
|
|
size = (rawData.size()/2) + pad;
|
|
Dest.resize(size+32);
|
|
newsize = rawData.size();
|
|
|
|
if (pad)
|
|
{
|
|
tmpstr = new char[newsize + 2];
|
|
memset(tmpstr, 0, newsize + 2);
|
|
tmpstr[0] = '0';
|
|
}
|
|
else
|
|
{
|
|
tmpstr = new char[newsize + 1];
|
|
memset(tmpstr, 0, newsize + 1);
|
|
}
|
|
strcat(tmpstr, WxStrToStr(rawData).c_str());
|
|
tmp2 = &Dest.front();
|
|
count = 0;
|
|
for (i = 0; i < strlen(tmpstr); i++)
|
|
{
|
|
copy[0] = tmpstr[i];
|
|
copy[1] = tmpstr[i+1];
|
|
copy[2] = 0;
|
|
int tmpint;
|
|
sscanf(copy, "%02x", &tmpint);
|
|
tmp2[count++] = tmpint;
|
|
// Dest[count] should now be the hex of what the two chars were!
|
|
// Also should add a check to make sure it's A-F only
|
|
//sscanf(copy, "%02x", &tmp2[count++]);
|
|
i += 1;
|
|
}
|
|
delete[] tmpstr;
|
|
}
|
|
else
|
|
{
|
|
//Looking for an ascii string
|
|
size = rawData.size();
|
|
Dest.resize(size+1);
|
|
tmpstr = new char[size+1];
|
|
|
|
tmp2 = &Dest.front();
|
|
sprintf(tmpstr, "%s", WxStrToStr(rawData).c_str());
|
|
|
|
for (i = 0; i < size; i++)
|
|
tmp2[i] = tmpstr[i];
|
|
|
|
delete[] tmpstr;
|
|
}
|
|
|
|
if (size)
|
|
{
|
|
unsigned char* pnt = &Dest.front();
|
|
unsigned int k = 0;
|
|
//grab
|
|
wxString txt = addrbox->GetValue();
|
|
u32 addr = 0;
|
|
if (txt.size())
|
|
{
|
|
sscanf(WxStrToStr(txt).c_str(), "%08x", &addr);
|
|
}
|
|
i = addr+4;
|
|
for ( ; i < szRAM; ++i)
|
|
{
|
|
for (k = 0; k < size; ++k)
|
|
{
|
|
if (i + k > szRAM) break;
|
|
if (k > size) break;
|
|
if (pnt[k] != TheRAM[i+k])
|
|
{
|
|
k = 0;
|
|
break;
|
|
}
|
|
}
|
|
if (k == size)
|
|
{
|
|
//Match was found
|
|
wxMessageBox(_("A match was found. Placing viewer at the offset."));
|
|
wxChar tmpwxstr[128] = {0};
|
|
wxSprintf(tmpwxstr, "%08x", i);
|
|
wxString tmpwx(tmpwxstr);
|
|
addrbox->SetValue(tmpwx);
|
|
//memview->curAddress = i;
|
|
//memview->Refresh();
|
|
OnAddrBoxChange(event);
|
|
return;
|
|
}
|
|
}
|
|
wxMessageBox(_("No match was found."));
|
|
}
|
|
}
|
|
|
|
void CMemoryWindow::onAscii(wxCommandEvent& event)
|
|
{
|
|
chkHex->SetValue(0);
|
|
}
|
|
|
|
void CMemoryWindow::onHex(wxCommandEvent& event)
|
|
{
|
|
chkAscii->SetValue(0);
|
|
}
|