Compare commits

...

6 Commits

Author SHA1 Message Date
TryTwo
f08402b81b
Merge 81980a2807 into b1d114f7f7 2025-06-06 17:54:27 -05:00
TryTwo
81980a2807 Fix loading issue.
Add delete button.
Disable Add or Edit note when they shouldn't be used. Don't allow edit note to have a note nullptr.
2025-05-21 17:44:34 -07:00
TryTwo
9fa74ac631 Fixes to be merged. 2025-05-20 08:54:44 -07:00
TryTwo
e0c6b4506d Debugger CodeViewWidget: Add context options for making and managing Notes. Add popup dialog for editing functions and notes. 2025-05-20 00:24:16 -07:00
TryTwo
6ae12a7e02 Debugger CodeWidget : Add search box for notes. 2025-05-19 23:30:20 -07:00
TryTwo
32f1dd359d Debugger symbols: Add new symbol type: Notes.. Notes are for naming single instructions, or small groups of instructions.
Notes are separate from function symbols, and can be searched separately.
Unlike functions, notes of different length can overlap each other.
In the instruction window, a note will always display over the function symbol.
2025-05-19 23:26:32 -07:00
15 changed files with 519 additions and 64 deletions

View File

@ -51,6 +51,7 @@ void SymbolDB::Clear(const char* prefix)
{
// TODO: honor prefix
m_functions.clear();
m_notes.clear();
m_checksum_to_function.clear();
}

View File

@ -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

View File

@ -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;
};

View File

@ -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

View File

@ -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();

View File

@ -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;
}

View File

@ -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);

View File

@ -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

View File

@ -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();
}

View File

@ -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();

View File

@ -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();

View File

@ -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;

View 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();
}

View 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;
};

View File

@ -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" />