mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2025-06-09 16:57:57 +00:00
Compare commits
6 Commits
507cd17a04
...
f08402b81b
Author | SHA1 | Date | |
---|---|---|---|
![]() |
f08402b81b | ||
![]() |
81980a2807 | ||
![]() |
9fa74ac631 | ||
![]() |
e0c6b4506d | ||
![]() |
6ae12a7e02 | ||
![]() |
32f1dd359d |
@ -51,6 +51,7 @@ void SymbolDB::Clear(const char* prefix)
|
||||
{
|
||||
// TODO: honor prefix
|
||||
m_functions.clear();
|
||||
m_notes.clear();
|
||||
m_checksum_to_function.clear();
|
||||
}
|
||||
|
||||
|
@ -29,6 +29,16 @@ struct SCall
|
||||
u32 call_address;
|
||||
};
|
||||
|
||||
struct Note
|
||||
{
|
||||
std::string name;
|
||||
u32 address = 0;
|
||||
u32 size = 0;
|
||||
int layer = 0;
|
||||
|
||||
Note() = default;
|
||||
};
|
||||
|
||||
struct Symbol
|
||||
{
|
||||
enum class Type
|
||||
@ -71,6 +81,7 @@ class SymbolDB
|
||||
{
|
||||
public:
|
||||
using XFuncMap = std::map<u32, Symbol>;
|
||||
using XNoteMap = std::map<u32, Note>;
|
||||
using XFuncPtrMap = std::map<u32, std::set<Symbol*>>;
|
||||
|
||||
SymbolDB();
|
||||
@ -86,6 +97,7 @@ public:
|
||||
std::vector<Symbol*> GetSymbolsFromHash(u32 hash);
|
||||
|
||||
const XFuncMap& Symbols() const { return m_functions; }
|
||||
const XNoteMap& Notes() const { return m_notes; }
|
||||
XFuncMap& AccessSymbols() { return m_functions; }
|
||||
bool IsEmpty() const;
|
||||
void Clear(const char* prefix = "");
|
||||
@ -94,6 +106,7 @@ public:
|
||||
|
||||
protected:
|
||||
XFuncMap m_functions;
|
||||
XNoteMap m_notes;
|
||||
XFuncPtrMap m_checksum_to_function;
|
||||
};
|
||||
} // namespace Common
|
||||
|
@ -104,6 +104,10 @@ public:
|
||||
{
|
||||
return 0xFFFFFFFF;
|
||||
}
|
||||
virtual u32 GetNoteColor(const CPUThreadGuard* /*guard*/, u32 /*address*/) const
|
||||
{
|
||||
return 0xFFFFFFFF;
|
||||
}
|
||||
virtual std::string_view GetDescription(u32 /*address*/) const = 0;
|
||||
virtual void Clear(const CPUThreadGuard& guard) = 0;
|
||||
};
|
||||
|
@ -437,6 +437,21 @@ u32 PPCDebugInterface::GetColor(const Core::CPUThreadGuard* guard, u32 address)
|
||||
};
|
||||
return colors[symbol->index % colors.size()];
|
||||
}
|
||||
|
||||
u32 PPCDebugInterface::GetNoteColor(const Core::CPUThreadGuard* guard, u32 address) const
|
||||
{
|
||||
if (!IsAlive())
|
||||
return 0xFFFFFF;
|
||||
if (!PowerPC::MMU::HostIsRAMAddress(*guard, address))
|
||||
return 0xeeeeee;
|
||||
static const int colors[3] = {
|
||||
0xcedeee, // light blue
|
||||
0xcceecc, // light green
|
||||
0xeeeece, // light yellow
|
||||
};
|
||||
const Common::Note* note = m_ppc_symbol_db.GetNoteFromAddr(address);
|
||||
return colors[note->layer % 3];
|
||||
}
|
||||
// =============
|
||||
|
||||
std::string_view PPCDebugInterface::GetDescription(u32 address) const
|
||||
|
@ -102,6 +102,7 @@ public:
|
||||
void Step() override {}
|
||||
void RunTo(u32 address) override;
|
||||
u32 GetColor(const Core::CPUThreadGuard* guard, u32 address) const override;
|
||||
u32 GetNoteColor(const Core::CPUThreadGuard* guard, u32 address) const override;
|
||||
std::string_view GetDescription(u32 address) const override;
|
||||
|
||||
std::shared_ptr<Core::NetworkCaptureLogger> NetworkLogger();
|
||||
|
@ -91,6 +91,46 @@ void PPCSymbolDB::AddKnownSymbol(const Core::CPUThreadGuard& guard, u32 startAdd
|
||||
}
|
||||
}
|
||||
|
||||
void PPCSymbolDB::AddKnownNote(u32 start_addr, u32 size, const std::string& name)
|
||||
{
|
||||
auto iter = m_notes.find(start_addr);
|
||||
|
||||
if (iter != m_notes.end())
|
||||
{
|
||||
// already got it, let's just update name, checksum & size to be sure.
|
||||
Common::Note* tempfunc = &iter->second;
|
||||
tempfunc->name = name;
|
||||
tempfunc->size = size;
|
||||
}
|
||||
else
|
||||
{
|
||||
Common::Note tf;
|
||||
tf.name = name;
|
||||
tf.address = start_addr;
|
||||
tf.size = size;
|
||||
|
||||
m_notes[start_addr] = tf;
|
||||
}
|
||||
}
|
||||
|
||||
void PPCSymbolDB::DetermineNoteLayers()
|
||||
{
|
||||
if (m_notes.empty())
|
||||
return;
|
||||
|
||||
for (auto& note : m_notes)
|
||||
note.second.layer = 0;
|
||||
|
||||
for (auto iter = m_notes.begin(); iter != m_notes.end(); ++iter)
|
||||
{
|
||||
const u32 range = iter->second.address + iter->second.size;
|
||||
auto search = m_notes.upper_bound(range);
|
||||
|
||||
while (--search != iter)
|
||||
search->second.layer += 1;
|
||||
}
|
||||
}
|
||||
|
||||
Common::Symbol* PPCSymbolDB::GetSymbolFromAddr(u32 addr)
|
||||
{
|
||||
auto it = m_functions.lower_bound(addr);
|
||||
@ -112,6 +152,43 @@ Common::Symbol* PPCSymbolDB::GetSymbolFromAddr(u32 addr)
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Common::Note* PPCSymbolDB::GetNoteFromAddr(u32 addr)
|
||||
{
|
||||
if (m_notes.empty())
|
||||
return nullptr;
|
||||
|
||||
auto itn = m_notes.lower_bound(addr);
|
||||
|
||||
// If the address is exactly the start address of a symbol, we're done.
|
||||
if (itn != m_notes.end() && itn->second.address == addr)
|
||||
return &itn->second;
|
||||
|
||||
// Otherwise, check whether the address is within the bounds of a symbol.
|
||||
if (itn == m_notes.begin())
|
||||
return nullptr;
|
||||
|
||||
do
|
||||
{
|
||||
--itn;
|
||||
|
||||
if (addr >= itn->second.address && addr < itn->second.address + itn->second.size)
|
||||
return &itn->second;
|
||||
|
||||
} while (itn != m_notes.begin() && itn->second.layer > 0);
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void PPCSymbolDB::DeleteFunction(u32 start_address)
|
||||
{
|
||||
m_functions.erase(start_address);
|
||||
}
|
||||
|
||||
void PPCSymbolDB::DeleteNote(u32 start_address)
|
||||
{
|
||||
m_notes.erase(start_address);
|
||||
}
|
||||
|
||||
std::string_view PPCSymbolDB::GetDescription(u32 addr)
|
||||
{
|
||||
if (const Common::Symbol* const symbol = GetSymbolFromAddr(addr))
|
||||
@ -406,6 +483,7 @@ bool PPCSymbolDB::LoadMap(const Core::CPUThreadGuard& guard, const std::string&
|
||||
if (strlen(name) > 0)
|
||||
{
|
||||
bool good;
|
||||
// Notes will be treated the same as Data.
|
||||
const Common::Symbol::Type type = section_name == ".text" || section_name == ".init" ?
|
||||
Common::Symbol::Type::Function :
|
||||
Common::Symbol::Type::Data;
|
||||
@ -438,7 +516,11 @@ bool PPCSymbolDB::LoadMap(const Core::CPUThreadGuard& guard, const std::string&
|
||||
if (good)
|
||||
{
|
||||
++good_count;
|
||||
AddKnownSymbol(guard, vaddress, size, name_string, object_filename_string, type);
|
||||
|
||||
if (section_name == ".note")
|
||||
AddKnownNote(vaddress, size, name_string);
|
||||
else
|
||||
AddKnownSymbol(guard, vaddress, size, name_string, object_filename_string, type);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -448,6 +530,7 @@ bool PPCSymbolDB::LoadMap(const Core::CPUThreadGuard& guard, const std::string&
|
||||
}
|
||||
|
||||
Index();
|
||||
DetermineNoteLayers();
|
||||
NOTICE_LOG_FMT(SYMBOLS, "{} symbols loaded, {} symbols ignored.", good_count, bad_count);
|
||||
return true;
|
||||
}
|
||||
@ -495,6 +578,17 @@ bool PPCSymbolDB::SaveSymbolMap(const std::string& filename) const
|
||||
file.WriteString(line);
|
||||
}
|
||||
|
||||
// Write .note section
|
||||
auto note_symbols = m_notes | std::views::transform([](auto f) { return f.second; });
|
||||
file.WriteString("\n.note section layout\n");
|
||||
for (const auto& symbol : note_symbols)
|
||||
{
|
||||
// Write symbol address, size, virtual address, alignment, name
|
||||
const std::string line = fmt::format("{:08x} {:06x} {:08x} {} {}\n", symbol.address,
|
||||
symbol.size, symbol.address, 0, symbol.name);
|
||||
file.WriteString(line);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -25,8 +25,13 @@ public:
|
||||
void AddKnownSymbol(const Core::CPUThreadGuard& guard, u32 startAddr, u32 size,
|
||||
const std::string& name, const std::string& object_name,
|
||||
Common::Symbol::Type type = Common::Symbol::Type::Function);
|
||||
void AddKnownNote(u32 startAddr, u32 size, const std::string& name);
|
||||
|
||||
Common::Symbol* GetSymbolFromAddr(u32 addr) override;
|
||||
Common::Note* GetNoteFromAddr(u32 addr);
|
||||
void DetermineNoteLayers();
|
||||
void DeleteFunction(u32 start_address);
|
||||
void DeleteNote(u32 start_address);
|
||||
|
||||
std::string_view GetDescription(u32 addr);
|
||||
|
||||
|
@ -221,6 +221,8 @@ add_executable(dolphin-emu
|
||||
Debugger/CodeViewWidget.h
|
||||
Debugger/CodeWidget.cpp
|
||||
Debugger/CodeWidget.h
|
||||
Debugger/EditSymbolDialog.cpp
|
||||
Debugger/EditSymbolDialog.h
|
||||
Debugger/GekkoSyntaxHighlight.cpp
|
||||
Debugger/GekkoSyntaxHighlight.h
|
||||
Debugger/JitBlockTableModel.cpp
|
||||
|
@ -37,6 +37,7 @@
|
||||
#include "Core/PowerPC/PowerPC.h"
|
||||
#include "Core/System.h"
|
||||
#include "DolphinQt/Debugger/AssembleInstructionDialog.h"
|
||||
#include "DolphinQt/Debugger/EditSymbolDialog.h"
|
||||
#include "DolphinQt/Debugger/PatchInstructionDialog.h"
|
||||
#include "DolphinQt/Host.h"
|
||||
#include "DolphinQt/QtUtils/FromStdString.h"
|
||||
@ -322,7 +323,6 @@ void CodeViewWidget::Update(const Core::CPUThreadGuard* guard)
|
||||
for (int i = 0; i < rowCount(); i++)
|
||||
{
|
||||
const u32 addr = AddressForRow(i);
|
||||
const u32 color = debug_interface.GetColor(guard, addr);
|
||||
auto* bp_item = new QTableWidgetItem;
|
||||
auto* addr_item = new QTableWidgetItem(QStringLiteral("%1").arg(addr, 8, 16, QLatin1Char('0')));
|
||||
|
||||
@ -331,7 +331,19 @@ void CodeViewWidget::Update(const Core::CPUThreadGuard* guard)
|
||||
|
||||
std::string ins = (split == std::string::npos ? disas : disas.substr(0, split));
|
||||
std::string param = (split == std::string::npos ? "" : disas.substr(split + 1));
|
||||
const std::string_view desc = debug_interface.GetDescription(addr);
|
||||
std::string desc;
|
||||
int color = 0xFFFFFF;
|
||||
const Common::Note* note = m_ppc_symbol_db.GetNoteFromAddr(addr);
|
||||
if (note == nullptr)
|
||||
{
|
||||
desc = debug_interface.GetDescription(addr);
|
||||
color = debug_interface.GetColor(guard, addr);
|
||||
}
|
||||
else
|
||||
{
|
||||
desc = note->name;
|
||||
color = debug_interface.GetNoteColor(guard, addr);
|
||||
}
|
||||
|
||||
// Adds whitespace and a minimum size to ins and param. Helps to prevent frequent resizing while
|
||||
// scrolling.
|
||||
@ -566,8 +578,6 @@ void CodeViewWidget::OnContextMenu()
|
||||
|
||||
const u32 addr = GetContextAddress();
|
||||
|
||||
const bool has_symbol = m_ppc_symbol_db.GetSymbolFromAddr(addr);
|
||||
|
||||
auto* follow_branch_action =
|
||||
menu->addAction(tr("Follow &Branch"), this, &CodeViewWidget::OnFollowBranch);
|
||||
|
||||
@ -587,17 +597,15 @@ void CodeViewWidget::OnContextMenu()
|
||||
menu->addAction(tr("Copy Tar&get Address"), this, &CodeViewWidget::OnCopyTargetAddress);
|
||||
menu->addSeparator();
|
||||
|
||||
auto* symbol_rename_action =
|
||||
menu->addAction(tr("&Rename Symbol"), this, &CodeViewWidget::OnRenameSymbol);
|
||||
auto* symbol_size_action =
|
||||
menu->addAction(tr("Set Symbol &Size"), this, &CodeViewWidget::OnSetSymbolSize);
|
||||
auto* symbol_end_action =
|
||||
menu->addAction(tr("Set Symbol &End Address"), this, &CodeViewWidget::OnSetSymbolEndAddress);
|
||||
auto* symbol_edit_action =
|
||||
menu->addAction(tr("&Edit function symbol"), this, &CodeViewWidget::OnEditSymbol);
|
||||
|
||||
auto* note_add_action = menu->addAction(tr("Add Note"), this, &CodeViewWidget::OnAddNote);
|
||||
auto* note_edit_action = menu->addAction(tr("Edit Note"), this, &CodeViewWidget::OnEditNote);
|
||||
|
||||
menu->addSeparator();
|
||||
|
||||
auto* run_to_action = menu->addAction(tr("Run &to Here"), this, &CodeViewWidget::OnRunToHere);
|
||||
auto* function_action =
|
||||
menu->addAction(tr("&Add Function"), this, &CodeViewWidget::OnAddFunction);
|
||||
auto* ppc_action = menu->addAction(tr("PPC vs Host"), this, &CodeViewWidget::OnPPCComparison);
|
||||
auto* insert_blr_action = menu->addAction(tr("&Insert BLR"), this, &CodeViewWidget::OnInsertBLR);
|
||||
auto* insert_nop_action = menu->addAction(tr("Insert &NOP"), this, &CodeViewWidget::OnInsertNOP);
|
||||
@ -645,21 +653,24 @@ void CodeViewWidget::OnContextMenu()
|
||||
run_until_menu->setEnabled(!target.isEmpty());
|
||||
follow_branch_action->setEnabled(follow_branch_enabled);
|
||||
|
||||
for (auto* action :
|
||||
{copy_address_action, copy_line_action, copy_hex_action, function_action, run_to_action,
|
||||
ppc_action, insert_blr_action, insert_nop_action, replace_action, assemble_action})
|
||||
for (auto* action : {copy_address_action, copy_line_action, copy_hex_action, symbol_edit_action,
|
||||
note_add_action, note_edit_action, run_to_action, ppc_action,
|
||||
insert_blr_action, insert_nop_action, replace_action, assemble_action})
|
||||
{
|
||||
action->setEnabled(running);
|
||||
}
|
||||
|
||||
for (auto* action : {symbol_rename_action, symbol_size_action, symbol_end_action})
|
||||
action->setEnabled(has_symbol);
|
||||
|
||||
for (auto* action : {copy_target_memory, show_target_memory})
|
||||
{
|
||||
action->setEnabled(valid_load_store);
|
||||
}
|
||||
|
||||
auto* note = m_ppc_symbol_db.GetNoteFromAddr(addr);
|
||||
note_edit_action->setEnabled(note != nullptr);
|
||||
// A note cannot be added ontop of the starting address of another note.
|
||||
if (note != nullptr && note->address == addr)
|
||||
note_add_action->setEnabled(false);
|
||||
|
||||
restore_action->setEnabled(running &&
|
||||
m_system.GetPowerPC().GetDebugInterface().HasEnabledPatch(addr));
|
||||
|
||||
@ -883,6 +894,13 @@ void CodeViewWidget::OnPPCComparison()
|
||||
void CodeViewWidget::OnAddFunction()
|
||||
{
|
||||
const u32 addr = GetContextAddress();
|
||||
int confirm =
|
||||
QMessageBox::warning(this, tr("Add Function Symbol"),
|
||||
tr("Force new function symbol to be made at %1?").arg(addr, 0, 16),
|
||||
QMessageBox::Ok | QMessageBox::Cancel);
|
||||
|
||||
if (confirm != QMessageBox::Ok)
|
||||
return;
|
||||
|
||||
Core::CPUThreadGuard guard(m_system);
|
||||
|
||||
@ -919,25 +937,79 @@ void CodeViewWidget::OnFollowBranch()
|
||||
SetAddress(branch_addr, SetAddressUpdate::WithDetailedUpdate);
|
||||
}
|
||||
|
||||
void CodeViewWidget::OnRenameSymbol()
|
||||
void CodeViewWidget::OnEditSymbol()
|
||||
{
|
||||
const u32 addr = GetContextAddress();
|
||||
|
||||
Common::Symbol* const symbol = m_ppc_symbol_db.GetSymbolFromAddr(addr);
|
||||
|
||||
if (!symbol)
|
||||
if (symbol == nullptr)
|
||||
{
|
||||
OnAddFunction();
|
||||
return;
|
||||
}
|
||||
|
||||
std::string name = symbol->name;
|
||||
u32 size = symbol->size;
|
||||
const u32 symbol_address = symbol->address;
|
||||
|
||||
EditSymbolDialog* dialog = new EditSymbolDialog(this, symbol_address, &size, &name);
|
||||
|
||||
if (dialog->exec() != QDialog::Accepted)
|
||||
return;
|
||||
|
||||
bool good;
|
||||
const QString name =
|
||||
QInputDialog::getText(this, tr("Rename Symbol"), tr("Symbol Name:"), QLineEdit::Normal,
|
||||
QString::fromStdString(symbol->name), &good, Qt::WindowCloseButtonHint);
|
||||
|
||||
if (good && !name.isEmpty())
|
||||
if (name.empty())
|
||||
{
|
||||
symbol->Rename(name.toStdString());
|
||||
emit Host::GetInstance()->PPCSymbolsChanged();
|
||||
OnDeleteSymbol();
|
||||
return;
|
||||
}
|
||||
|
||||
if (symbol->name != name)
|
||||
symbol->Rename(name);
|
||||
|
||||
if (symbol->size != size)
|
||||
{
|
||||
Core::CPUThreadGuard guard(m_system);
|
||||
PPCAnalyst::ReanalyzeFunction(guard, symbol->address, *symbol, size);
|
||||
}
|
||||
emit Host::GetInstance()->PPCSymbolsChanged();
|
||||
}
|
||||
|
||||
void CodeViewWidget::OnDeleteSymbol()
|
||||
{
|
||||
const u32 addr = GetContextAddress();
|
||||
Common::Symbol* const symbol = m_ppc_symbol_db.GetSymbolFromAddr(addr);
|
||||
|
||||
if (symbol == nullptr)
|
||||
return;
|
||||
|
||||
int confirm = QMessageBox::warning(this, tr("Delete Function Symbol"),
|
||||
tr("Delete function symbol: %1\nat %2?")
|
||||
.arg(QString::fromStdString(symbol->name))
|
||||
.arg(addr, 0, 16),
|
||||
QMessageBox::Ok | QMessageBox::Cancel);
|
||||
|
||||
if (confirm != QMessageBox::Ok)
|
||||
return;
|
||||
|
||||
m_ppc_symbol_db.DeleteFunction(symbol->address);
|
||||
|
||||
emit Host::GetInstance()->PPCSymbolsChanged();
|
||||
}
|
||||
|
||||
void CodeViewWidget::OnAddNote()
|
||||
{
|
||||
const u32 note_address = GetContextAddress();
|
||||
std::string name = "";
|
||||
u32 size = 4;
|
||||
|
||||
EditSymbolDialog* dialog = new EditSymbolDialog(this, note_address, &size, &name);
|
||||
|
||||
if (dialog->exec() != QDialog::Accepted || name.empty())
|
||||
return;
|
||||
|
||||
m_ppc_symbol_db.AddKnownNote(note_address, size, name);
|
||||
m_ppc_symbol_db.DetermineNoteLayers();
|
||||
emit Host::GetInstance()->PPCSymbolsChanged();
|
||||
}
|
||||
|
||||
void CodeViewWidget::OnSelectionChanged()
|
||||
@ -953,53 +1025,57 @@ void CodeViewWidget::OnSelectionChanged()
|
||||
}
|
||||
}
|
||||
|
||||
void CodeViewWidget::OnSetSymbolSize()
|
||||
void CodeViewWidget::OnEditNote()
|
||||
{
|
||||
const u32 addr = GetContextAddress();
|
||||
const u32 context_address = GetContextAddress();
|
||||
Common::Note* const note = m_ppc_symbol_db.GetNoteFromAddr(context_address);
|
||||
|
||||
Common::Symbol* const symbol = m_ppc_symbol_db.GetSymbolFromAddr(addr);
|
||||
|
||||
if (!symbol)
|
||||
if (note == nullptr)
|
||||
return;
|
||||
|
||||
bool good;
|
||||
const int size = QInputDialog::getInt(
|
||||
this, tr("Rename Symbol"), tr("Symbol Size (%1):").arg(QString::fromStdString(symbol->name)),
|
||||
symbol->size, 1, 0xFFFF, 1, &good, Qt::WindowCloseButtonHint);
|
||||
std::string name = note->name;
|
||||
u32 size = note->size;
|
||||
const u32 note_address = note->address;
|
||||
|
||||
if (!good)
|
||||
EditSymbolDialog* dialog = new EditSymbolDialog(this, note_address, &size, &name);
|
||||
|
||||
if (dialog->exec() != QDialog::Accepted)
|
||||
return;
|
||||
|
||||
Core::CPUThreadGuard guard(m_system);
|
||||
if (name.empty())
|
||||
{
|
||||
OnDeleteNote();
|
||||
return;
|
||||
}
|
||||
|
||||
if (note->name != name || note->size != size)
|
||||
{
|
||||
m_ppc_symbol_db.AddKnownNote(note_address, size, name);
|
||||
m_ppc_symbol_db.DetermineNoteLayers();
|
||||
}
|
||||
|
||||
PPCAnalyst::ReanalyzeFunction(guard, symbol->address, *symbol, size);
|
||||
emit Host::GetInstance()->PPCSymbolsChanged();
|
||||
}
|
||||
|
||||
void CodeViewWidget::OnSetSymbolEndAddress()
|
||||
void CodeViewWidget::OnDeleteNote()
|
||||
{
|
||||
const u32 addr = GetContextAddress();
|
||||
const u32 context_address = GetContextAddress();
|
||||
Common::Note* const note = m_ppc_symbol_db.GetNoteFromAddr(context_address);
|
||||
|
||||
Common::Symbol* const symbol = m_ppc_symbol_db.GetSymbolFromAddr(addr);
|
||||
|
||||
if (!symbol)
|
||||
if (note == nullptr)
|
||||
return;
|
||||
|
||||
bool good;
|
||||
const QString name = QInputDialog::getText(
|
||||
this, tr("Set Symbol End Address"),
|
||||
tr("Symbol End Address (%1):").arg(QString::fromStdString(symbol->name)), QLineEdit::Normal,
|
||||
QStringLiteral("%1").arg(addr + symbol->size, 8, 16, QLatin1Char('0')), &good,
|
||||
Qt::WindowCloseButtonHint);
|
||||
int confirm = QMessageBox::warning(this, tr("Delete Note"),
|
||||
tr("Delete Note: %1\nat %2?")
|
||||
.arg(QString::fromStdString(note->name))
|
||||
.arg(context_address, 0, 16),
|
||||
QMessageBox::Ok | QMessageBox::Cancel);
|
||||
|
||||
const u32 address = name.toUInt(&good, 16);
|
||||
|
||||
if (!good)
|
||||
if (confirm != QMessageBox::Ok)
|
||||
return;
|
||||
|
||||
Core::CPUThreadGuard guard(m_system);
|
||||
m_ppc_symbol_db.DeleteNote(note->address);
|
||||
|
||||
PPCAnalyst::ReanalyzeFunction(guard, symbol->address, *symbol, address - symbol->address);
|
||||
emit Host::GetInstance()->PPCSymbolsChanged();
|
||||
}
|
||||
|
||||
|
@ -87,13 +87,15 @@ private:
|
||||
void OnCopyFunction();
|
||||
void OnCopyCode();
|
||||
void OnCopyHex();
|
||||
void OnRenameSymbol();
|
||||
void OnSelectionChanged();
|
||||
void OnSetSymbolSize();
|
||||
void OnSetSymbolEndAddress();
|
||||
void OnRunToHere();
|
||||
void OnAddFunction();
|
||||
void OnEditSymbol();
|
||||
void OnDeleteSymbol();
|
||||
void OnAddNote();
|
||||
void OnPPCComparison();
|
||||
void OnEditNote();
|
||||
void OnDeleteNote();
|
||||
void OnInsertBLR();
|
||||
void OnInsertNOP();
|
||||
void OnReplaceInstruction();
|
||||
|
@ -16,7 +16,9 @@
|
||||
#include <QPushButton>
|
||||
#include <QSplitter>
|
||||
#include <QStyleHints>
|
||||
#include <QTabWidget>
|
||||
#include <QTableWidget>
|
||||
#include <QVBoxLayout>
|
||||
#include <QWidget>
|
||||
|
||||
#include "Common/Event.h"
|
||||
@ -120,7 +122,7 @@ void CodeWidget::CreateWidgets()
|
||||
m_box_splitter = new QSplitter(Qt::Vertical);
|
||||
m_box_splitter->setStyleSheet(BOX_SPLITTER_STYLESHEET);
|
||||
|
||||
auto add_search_line_edit = [this](const QString& name, QListWidget* list_widget) {
|
||||
auto add_search_line_edit = [this](const QString& name, QWidget* list_widget) {
|
||||
auto* widget = new QWidget;
|
||||
auto* line_layout = new QGridLayout;
|
||||
auto* label = new QLabel(name);
|
||||
@ -139,8 +141,12 @@ void CodeWidget::CreateWidgets()
|
||||
m_search_callstack = add_search_line_edit(tr("Callstack"), m_callstack_list);
|
||||
|
||||
// Symbols
|
||||
auto* symbols_tab = new QTabWidget;
|
||||
m_symbols_list = new QListWidget;
|
||||
m_search_symbols = add_search_line_edit(tr("Symbols"), m_symbols_list);
|
||||
m_note_list = new QListWidget;
|
||||
symbols_tab->addTab(m_symbols_list, tr("Symbols"));
|
||||
symbols_tab->addTab(m_note_list, tr("Notes"));
|
||||
m_search_symbols = add_search_line_edit(tr("Symbols"), symbols_tab);
|
||||
|
||||
// Function calls
|
||||
m_function_calls_list = new QListWidget;
|
||||
@ -198,7 +204,7 @@ void CodeWidget::ConnectWidgets()
|
||||
connect(m_search_callstack, &QLineEdit::textChanged, this, &CodeWidget::UpdateCallstack);
|
||||
|
||||
connect(m_branch_watch, &QPushButton::clicked, this, &CodeWidget::OnBranchWatchDialog);
|
||||
|
||||
connect(m_note_list, &QListWidget::itemPressed, this, &CodeWidget::OnSelectNote);
|
||||
connect(m_symbols_list, &QListWidget::itemPressed, this, &CodeWidget::OnSelectSymbol);
|
||||
connect(m_callstack_list, &QListWidget::itemPressed, this, &CodeWidget::OnSelectCallstack);
|
||||
connect(m_function_calls_list, &QListWidget::itemPressed, this,
|
||||
@ -236,6 +242,7 @@ void CodeWidget::OnSetCodeAddress(u32 address)
|
||||
void CodeWidget::OnPPCSymbolsChanged()
|
||||
{
|
||||
UpdateSymbols();
|
||||
UpdateNotes();
|
||||
UpdateCallstack();
|
||||
if (const Common::Symbol* symbol = m_ppc_symbol_db.GetSymbolFromAddr(m_code_view->GetAddress()))
|
||||
{
|
||||
@ -279,6 +286,7 @@ void CodeWidget::OnSearchSymbols()
|
||||
{
|
||||
m_symbol_filter = m_search_symbols->text();
|
||||
UpdateSymbols();
|
||||
UpdateNotes();
|
||||
}
|
||||
|
||||
void CodeWidget::OnSelectSymbol()
|
||||
@ -298,6 +306,17 @@ void CodeWidget::OnSelectSymbol()
|
||||
m_code_view->setFocus();
|
||||
}
|
||||
|
||||
void CodeWidget::OnSelectNote()
|
||||
{
|
||||
const auto items = m_note_list->selectedItems();
|
||||
if (items.isEmpty())
|
||||
return;
|
||||
|
||||
const u32 address = items[0]->data(Qt::UserRole).toUInt();
|
||||
|
||||
m_code_view->SetAddress(address, CodeViewWidget::SetAddressUpdate::WithUpdate);
|
||||
}
|
||||
|
||||
void CodeWidget::OnSelectCallstack()
|
||||
{
|
||||
const auto items = m_callstack_list->selectedItems();
|
||||
@ -426,6 +445,30 @@ void CodeWidget::UpdateSymbols()
|
||||
m_symbols_list->sortItems();
|
||||
}
|
||||
|
||||
void CodeWidget::UpdateNotes()
|
||||
{
|
||||
const QString selection = m_note_list->selectedItems().isEmpty() ?
|
||||
QStringLiteral("") :
|
||||
m_note_list->selectedItems()[0]->text();
|
||||
m_note_list->clear();
|
||||
|
||||
for (const auto& note : m_ppc_symbol_db.Notes())
|
||||
{
|
||||
const QString name = QString::fromStdString(note.second.name);
|
||||
|
||||
auto* item = new QListWidgetItem(name);
|
||||
if (name == selection)
|
||||
item->setSelected(true);
|
||||
|
||||
item->setData(Qt::UserRole, note.second.address);
|
||||
|
||||
if (name.toUpper().indexOf(m_symbol_filter.toUpper()) != -1)
|
||||
m_note_list->addItem(item);
|
||||
}
|
||||
|
||||
m_note_list->sortItems();
|
||||
}
|
||||
|
||||
void CodeWidget::UpdateFunctionCalls(const Common::Symbol* symbol)
|
||||
{
|
||||
m_function_calls_list->clear();
|
||||
|
@ -61,11 +61,13 @@ private:
|
||||
void UpdateCallstack();
|
||||
void UpdateFunctionCalls(const Common::Symbol* symbol);
|
||||
void UpdateFunctionCallers(const Common::Symbol* symbol);
|
||||
void UpdateNotes();
|
||||
|
||||
void OnPPCSymbolsChanged();
|
||||
void OnSearchAddress();
|
||||
void OnSearchSymbols();
|
||||
void OnSelectSymbol();
|
||||
void OnSelectNote();
|
||||
void OnSelectCallstack();
|
||||
void OnSelectFunctionCallers();
|
||||
void OnSelectFunctionCalls();
|
||||
@ -84,6 +86,7 @@ private:
|
||||
QListWidget* m_callstack_list;
|
||||
QLineEdit* m_search_symbols;
|
||||
QListWidget* m_symbols_list;
|
||||
QListWidget* m_note_list;
|
||||
QLineEdit* m_search_calls;
|
||||
QListWidget* m_function_calls_list;
|
||||
QLineEdit* m_search_callers;
|
||||
|
152
Source/Core/DolphinQt/Debugger/EditSymbolDialog.cpp
Normal file
152
Source/Core/DolphinQt/Debugger/EditSymbolDialog.cpp
Normal file
@ -0,0 +1,152 @@
|
||||
// Copyright 2025 Dolphin Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "DolphinQt/Debugger/EditSymbolDialog.h"
|
||||
|
||||
#include <QDialogButtonBox>
|
||||
#include <QHBoxLayout>
|
||||
#include <QLabel>
|
||||
#include <QLineEdit>
|
||||
#include <QPushButton>
|
||||
#include <QRegularExpression>
|
||||
#include <QSpinBox>
|
||||
#include <QVBoxLayout>
|
||||
|
||||
EditSymbolDialog::EditSymbolDialog(QWidget* parent, const u32 symbol_address, u32* symbol_size,
|
||||
std::string* symbol_name)
|
||||
: QDialog(parent), m_symbol_name(symbol_name), m_symbol_size(symbol_size),
|
||||
m_symbol_address(symbol_address)
|
||||
{
|
||||
setWindowTitle(tr("Edit Symbol"));
|
||||
CreateWidgets();
|
||||
ConnectWidgets();
|
||||
}
|
||||
|
||||
void EditSymbolDialog::CreateWidgets()
|
||||
{
|
||||
m_reset_button = new QPushButton(tr("Reset"));
|
||||
m_delete_button = new QPushButton(tr("Delete"));
|
||||
m_buttons = new QDialogButtonBox(QDialogButtonBox::Cancel | QDialogButtonBox::Ok);
|
||||
m_buttons->addButton(m_reset_button, QDialogButtonBox::ResetRole);
|
||||
m_buttons->addButton(m_delete_button, QDialogButtonBox::DestructiveRole);
|
||||
|
||||
QLabel* info_label =
|
||||
new QLabel(tr("Editing symbol starting at: ") + QString::number(m_symbol_address, 16));
|
||||
m_name_edit = new QLineEdit();
|
||||
m_name_edit->setPlaceholderText(tr("Symbol name"));
|
||||
|
||||
auto* size_layout = new QHBoxLayout;
|
||||
QLabel* address_end_label = new QLabel(tr("End Address"));
|
||||
QLabel* size_lines_label = new QLabel(tr("Lines"));
|
||||
QLabel* size_hex_label = new QLabel(tr("Size: 0x"));
|
||||
m_address_end_edit = new QLineEdit();
|
||||
m_size_lines_spin = new QSpinBox();
|
||||
m_size_hex_edit = new QLineEdit();
|
||||
|
||||
size_hex_label->setAlignment(Qt::AlignCenter | Qt::AlignRight);
|
||||
size_lines_label->setAlignment(Qt::AlignCenter | Qt::AlignRight);
|
||||
|
||||
// Get system font and use to size boxes.
|
||||
QFont font;
|
||||
QFontMetrics fm(font);
|
||||
const int width = fm.horizontalAdvance(QLatin1Char('0')) * 2;
|
||||
m_address_end_edit->setFixedWidth(width * 6);
|
||||
m_size_hex_edit->setFixedWidth(width * 5);
|
||||
m_size_lines_spin->setFixedWidth(width * 5);
|
||||
m_size_hex_edit->setMaxLength(7);
|
||||
m_size_lines_spin->setRange(0, 99999);
|
||||
|
||||
// Accept hex input only
|
||||
QRegularExpression rx(QStringLiteral("[0-9a-fA-F]{0,8}"));
|
||||
QValidator* validator = new QRegularExpressionValidator(rx, this);
|
||||
m_address_end_edit->setValidator(validator);
|
||||
m_size_hex_edit->setValidator(validator);
|
||||
|
||||
size_layout->addWidget(address_end_label);
|
||||
size_layout->addWidget(m_address_end_edit);
|
||||
size_layout->addWidget(size_hex_label);
|
||||
size_layout->addWidget(m_size_hex_edit);
|
||||
size_layout->addWidget(size_lines_label);
|
||||
size_layout->addWidget(m_size_lines_spin);
|
||||
|
||||
auto* layout = new QVBoxLayout();
|
||||
layout->addWidget(info_label);
|
||||
layout->addWidget(m_name_edit);
|
||||
layout->addLayout(size_layout);
|
||||
layout->addWidget(m_buttons);
|
||||
|
||||
setLayout(layout);
|
||||
|
||||
FillFunctionData();
|
||||
}
|
||||
|
||||
void EditSymbolDialog::FillFunctionData()
|
||||
{
|
||||
m_name_edit->setText(QString::fromStdString(*m_symbol_name));
|
||||
m_size_lines_spin->setValue(*m_symbol_size / 4);
|
||||
m_size_hex_edit->setText(QString::number(*m_symbol_size, 16));
|
||||
m_address_end_edit->setText(
|
||||
QStringLiteral("%1").arg(m_symbol_address + *m_symbol_size, 8, 16, QLatin1Char('0')));
|
||||
}
|
||||
|
||||
void EditSymbolDialog::UpdateAddressData(u32 size)
|
||||
{
|
||||
// Not sure what the max size should be. Definitely not a full 8, so set to 7.
|
||||
size = size & 0xFFFFFFC;
|
||||
if (size < 4)
|
||||
size = 4;
|
||||
|
||||
m_size_lines_spin->setValue(size / 4);
|
||||
m_size_hex_edit->setText(QString::number(size, 16)); //("%1").arg(size, 4, 16));
|
||||
m_address_end_edit->setText(
|
||||
QStringLiteral("%1").arg(m_symbol_address + size, 8, 16, QLatin1Char('0')));
|
||||
}
|
||||
|
||||
void EditSymbolDialog::ConnectWidgets()
|
||||
{
|
||||
connect(m_size_lines_spin, QOverload<int>::of(&QSpinBox::valueChanged), this,
|
||||
[this](int value) { UpdateAddressData(value * 4); });
|
||||
|
||||
connect(m_size_hex_edit, &QLineEdit::editingFinished, this, [this] {
|
||||
bool good;
|
||||
const u32 size = m_size_hex_edit->text().toUInt(&good, 16);
|
||||
if (good)
|
||||
UpdateAddressData(size);
|
||||
});
|
||||
|
||||
connect(m_address_end_edit, &QLineEdit::textEdited, this, [this] {
|
||||
bool good;
|
||||
const u32 end = m_address_end_edit->text().toUInt(&good, 16);
|
||||
if (good && end > m_symbol_address)
|
||||
UpdateAddressData(end - m_symbol_address);
|
||||
});
|
||||
|
||||
connect(m_buttons, &QDialogButtonBox::accepted, this, &EditSymbolDialog::Accepted);
|
||||
connect(m_buttons, &QDialogButtonBox::rejected, this, &EditSymbolDialog::reject);
|
||||
connect(m_reset_button, &QPushButton::pressed, this, &EditSymbolDialog::FillFunctionData);
|
||||
connect(m_delete_button, &QPushButton::pressed, this, &EditSymbolDialog::NotifyDelete);
|
||||
}
|
||||
|
||||
void EditSymbolDialog::Accepted()
|
||||
{
|
||||
const std::string name = m_name_edit->text().toStdString();
|
||||
|
||||
if (*m_symbol_name != name)
|
||||
*m_symbol_name = name;
|
||||
|
||||
bool good;
|
||||
const u32 size = m_size_hex_edit->text().toUInt(&good, 16);
|
||||
|
||||
if (good && *m_symbol_size != size)
|
||||
*m_symbol_size = size;
|
||||
|
||||
QDialog::accept();
|
||||
}
|
||||
|
||||
void EditSymbolDialog::NotifyDelete()
|
||||
{
|
||||
// Returning an empty name will ask the user if they want to delete the symbol. Also applies to
|
||||
// Accepted().
|
||||
*m_symbol_name = "";
|
||||
QDialog::accept();
|
||||
}
|
42
Source/Core/DolphinQt/Debugger/EditSymbolDialog.h
Normal file
42
Source/Core/DolphinQt/Debugger/EditSymbolDialog.h
Normal file
@ -0,0 +1,42 @@
|
||||
// Copyright 2025 Dolphin Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QDialog>
|
||||
#include "Common/CommonTypes.h"
|
||||
|
||||
// class CodeWidget;
|
||||
class QLineEdit;
|
||||
class QDialogButtonBox;
|
||||
class QSpinBox;
|
||||
|
||||
class EditSymbolDialog : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit EditSymbolDialog(QWidget* parent, const u32 symbol_address, u32* symbol_size,
|
||||
std::string* symbol_name);
|
||||
|
||||
private:
|
||||
void CreateWidgets();
|
||||
void ConnectWidgets();
|
||||
void FillFunctionData();
|
||||
void UpdateAddressData(u32 size);
|
||||
void Accepted();
|
||||
void NotifyDelete();
|
||||
|
||||
QLineEdit* m_name_edit;
|
||||
QSpinBox* m_size_lines_spin;
|
||||
QLineEdit* m_size_hex_edit;
|
||||
QLineEdit* m_address_end_edit;
|
||||
|
||||
QPushButton* m_reset_button;
|
||||
QPushButton* m_delete_button;
|
||||
QDialogButtonBox* m_buttons;
|
||||
|
||||
std::string* m_symbol_name;
|
||||
u32* m_symbol_size;
|
||||
const u32 m_symbol_address;
|
||||
};
|
@ -144,6 +144,7 @@
|
||||
<ClCompile Include="Debugger\BreakpointWidget.cpp" />
|
||||
<ClCompile Include="Debugger\CodeViewWidget.cpp" />
|
||||
<ClCompile Include="Debugger\CodeWidget.cpp" />
|
||||
<ClCompile Include="Debugger\EditSymbolDialog.cpp" />
|
||||
<ClCompile Include="Debugger\GekkoSyntaxHighlight.cpp" />
|
||||
<ClCompile Include="Debugger\JitBlockTableModel.cpp" />
|
||||
<ClCompile Include="Debugger\JITWidget.cpp" />
|
||||
@ -364,6 +365,7 @@
|
||||
<QtMoc Include="Debugger\BreakpointWidget.h" />
|
||||
<QtMoc Include="Debugger\CodeViewWidget.h" />
|
||||
<QtMoc Include="Debugger\CodeWidget.h" />
|
||||
<QtMoc Include="Debugger\EditSymbolDialog.h" />
|
||||
<QtMoc Include="Debugger\GekkoSyntaxHighlight.h" />
|
||||
<QtMoc Include="Debugger\JitBlockTableModel.h" />
|
||||
<QtMoc Include="Debugger\JITWidget.h" />
|
||||
|
Loading…
Reference in New Issue
Block a user