mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2025-08-25 11:46:27 +00:00
MemoryWidget: Add symbols and Notes.
Add option to hide them. Add box to search. Add ability to edit data symbols and notes in MemoryViewWidget.
This commit is contained in:
parent
b2b2808d01
commit
f7e7b0f6b0
4 changed files with 309 additions and 6 deletions
|
@ -29,8 +29,10 @@
|
||||||
#include "Core/Core.h"
|
#include "Core/Core.h"
|
||||||
#include "Core/HW/AddressSpace.h"
|
#include "Core/HW/AddressSpace.h"
|
||||||
#include "Core/PowerPC/BreakPoints.h"
|
#include "Core/PowerPC/BreakPoints.h"
|
||||||
|
#include "Core/PowerPC/PPCSymbolDB.h"
|
||||||
#include "Core/PowerPC/PowerPC.h"
|
#include "Core/PowerPC/PowerPC.h"
|
||||||
#include "Core/System.h"
|
#include "Core/System.h"
|
||||||
|
#include "DolphinQt/Debugger/EditSymbolDialog.h"
|
||||||
#include "DolphinQt/Host.h"
|
#include "DolphinQt/Host.h"
|
||||||
#include "DolphinQt/Resources.h"
|
#include "DolphinQt/Resources.h"
|
||||||
#include "DolphinQt/Settings.h"
|
#include "DolphinQt/Settings.h"
|
||||||
|
@ -196,7 +198,7 @@ private:
|
||||||
};
|
};
|
||||||
|
|
||||||
MemoryViewWidget::MemoryViewWidget(Core::System& system, QWidget* parent)
|
MemoryViewWidget::MemoryViewWidget(Core::System& system, QWidget* parent)
|
||||||
: QWidget(parent), m_system(system)
|
: QWidget(parent), m_system(system), m_ppc_symbol_db(m_system.GetPPCSymbolDB())
|
||||||
{
|
{
|
||||||
auto* layout = new QHBoxLayout();
|
auto* layout = new QHBoxLayout();
|
||||||
layout->setContentsMargins(0, 0, 0, 0);
|
layout->setContentsMargins(0, 0, 0, 0);
|
||||||
|
@ -220,6 +222,8 @@ MemoryViewWidget::MemoryViewWidget(Core::System& system, QWidget* parent)
|
||||||
this->setLayout(layout);
|
this->setLayout(layout);
|
||||||
|
|
||||||
connect(&Settings::Instance(), &Settings::DebugFontChanged, this, &MemoryViewWidget::UpdateFont);
|
connect(&Settings::Instance(), &Settings::DebugFontChanged, this, &MemoryViewWidget::UpdateFont);
|
||||||
|
connect(Host::GetInstance(), &Host::PPCSymbolsChanged, this,
|
||||||
|
[this] { UpdateDispatcher(UpdateType::Symbols); });
|
||||||
connect(Host::GetInstance(), &Host::PPCBreakpointsChanged, this,
|
connect(Host::GetInstance(), &Host::PPCBreakpointsChanged, this,
|
||||||
&MemoryViewWidget::UpdateBreakpointTags);
|
&MemoryViewWidget::UpdateBreakpointTags);
|
||||||
connect(&Settings::Instance(), &Settings::EmulationStateChanged, this, [this] {
|
connect(&Settings::Instance(), &Settings::EmulationStateChanged, this, [this] {
|
||||||
|
@ -347,6 +351,9 @@ void MemoryViewWidget::UpdateDispatcher(UpdateType type)
|
||||||
if (Core::GetState(m_system) == Core::State::Paused)
|
if (Core::GetState(m_system) == Core::State::Paused)
|
||||||
GetValues();
|
GetValues();
|
||||||
UpdateColumns();
|
UpdateColumns();
|
||||||
|
[[fallthrough]];
|
||||||
|
case UpdateType::Symbols:
|
||||||
|
UpdateSymbols();
|
||||||
break;
|
break;
|
||||||
case UpdateType::Auto:
|
case UpdateType::Auto:
|
||||||
// Values were captured on CPU thread while doing a callback.
|
// Values were captured on CPU thread while doing a callback.
|
||||||
|
@ -371,7 +378,7 @@ void MemoryViewWidget::CreateTable()
|
||||||
// Span is the number of unique memory values covered in one row.
|
// Span is the number of unique memory values covered in one row.
|
||||||
const int data_span = m_bytes_per_row / GetTypeSize(m_type);
|
const int data_span = m_bytes_per_row / GetTypeSize(m_type);
|
||||||
m_data_columns = m_dual_view ? data_span * 2 : data_span;
|
m_data_columns = m_dual_view ? data_span * 2 : data_span;
|
||||||
const int total_columns = MISC_COLUMNS + m_data_columns;
|
const int total_columns = MISC_COLUMNS + m_data_columns + (m_show_symbols ? 1 : 0);
|
||||||
|
|
||||||
const int rows =
|
const int rows =
|
||||||
std::round((m_table->height() / static_cast<float>(m_table->rowHeight(0))) - 0.25);
|
std::round((m_table->height() / static_cast<float>(m_table->rowHeight(0))) - 0.25);
|
||||||
|
@ -440,6 +447,15 @@ void MemoryViewWidget::CreateTable()
|
||||||
|
|
||||||
m_table->setItem(i, c + MISC_COLUMNS, item.clone());
|
m_table->setItem(i, c + MISC_COLUMNS, item.clone());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!m_show_symbols)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// Symbols
|
||||||
|
auto* description_item = new QTableWidgetItem(QStringLiteral("-"));
|
||||||
|
description_item->setFlags(Qt::ItemIsEnabled);
|
||||||
|
|
||||||
|
m_table->setItem(i, m_table->columnCount() - 1, description_item);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update column width
|
// Update column width
|
||||||
|
@ -500,6 +516,9 @@ void MemoryViewWidget::Update()
|
||||||
item->setBackground(Qt::transparent);
|
item->setBackground(Qt::transparent);
|
||||||
item->setData(USER_ROLE_VALID_ADDRESS, false);
|
item->setData(USER_ROLE_VALID_ADDRESS, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (m_show_symbols)
|
||||||
|
m_table->item(i, m_table->columnCount() - 1)->setData(USER_ROLE_CELL_ADDRESS, row_address);
|
||||||
}
|
}
|
||||||
|
|
||||||
UpdateBreakpointTags();
|
UpdateBreakpointTags();
|
||||||
|
@ -576,6 +595,34 @@ void MemoryViewWidget::UpdateColumns()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MemoryViewWidget::UpdateSymbols()
|
||||||
|
{
|
||||||
|
if (!m_show_symbols)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Update symbols
|
||||||
|
for (int i = 0; i < m_table->rowCount(); i++)
|
||||||
|
{
|
||||||
|
auto* item = m_table->item(i, m_table->columnCount() - 1);
|
||||||
|
if (!item)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
const u32 address = item->data(USER_ROLE_CELL_ADDRESS).toUInt();
|
||||||
|
const Common::Note* note = m_ppc_symbol_db.GetNoteFromAddr(address);
|
||||||
|
|
||||||
|
std::string desc;
|
||||||
|
if (note == nullptr)
|
||||||
|
desc = m_ppc_symbol_db.GetDescription(address);
|
||||||
|
else
|
||||||
|
desc = note->name;
|
||||||
|
|
||||||
|
item->setText(QString::fromStdString(" " + desc));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_show_symbols)
|
||||||
|
m_table->resizeColumnToContents(m_table->columnCount() - 1);
|
||||||
|
}
|
||||||
|
|
||||||
// Always runs on CPU thread from a callback.
|
// Always runs on CPU thread from a callback.
|
||||||
void MemoryViewWidget::UpdateOnFrameEnd()
|
void MemoryViewWidget::UpdateOnFrameEnd()
|
||||||
{
|
{
|
||||||
|
@ -1059,6 +1106,78 @@ void MemoryViewWidget::OnCopyHex(u32 addr)
|
||||||
QStringLiteral("%1").arg(value, sizeof(u64) * 2, 16, QLatin1Char('0')).left(length * 2));
|
QStringLiteral("%1").arg(value, sizeof(u64) * 2, 16, QLatin1Char('0')).left(length * 2));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MemoryViewWidget::ShowSymbols(bool enable)
|
||||||
|
{
|
||||||
|
m_show_symbols = enable;
|
||||||
|
UpdateDispatcher(UpdateType::Full);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MemoryViewWidget::OnEditSymbol(EditSymbolType type, u32 addr)
|
||||||
|
{
|
||||||
|
// Add Note and Add Region use these values.
|
||||||
|
std::string name = "";
|
||||||
|
std::string object_name = "";
|
||||||
|
u32 size = GetTypeSize(m_type);
|
||||||
|
u32 address = addr;
|
||||||
|
EditSymbolDialog::Type dialog_type = EditSymbolDialog::Type::Note;
|
||||||
|
|
||||||
|
// Add and edit region are tied to the same context menu action.
|
||||||
|
if (type == EditSymbolType::EditRegion)
|
||||||
|
{
|
||||||
|
// If symbol doesn't exist, it's safe to add a new region.
|
||||||
|
const Common::Symbol* const symbol = m_ppc_symbol_db.GetSymbolFromAddr(addr);
|
||||||
|
dialog_type = EditSymbolDialog::Type::Symbol;
|
||||||
|
|
||||||
|
if (symbol != nullptr)
|
||||||
|
{
|
||||||
|
// Leave the more specialized function editing to code widget.
|
||||||
|
if (symbol->type != Common::Symbol::Type::Data)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Edit data region.
|
||||||
|
name = symbol->name;
|
||||||
|
object_name = symbol->object_name;
|
||||||
|
size = symbol->size;
|
||||||
|
address = symbol->address;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (type == EditSymbolType::EditNote)
|
||||||
|
{
|
||||||
|
const Common::Note* note = m_ppc_symbol_db.GetNoteFromAddr(addr);
|
||||||
|
if (note == nullptr)
|
||||||
|
return;
|
||||||
|
|
||||||
|
name = note->name;
|
||||||
|
size = note->size;
|
||||||
|
address = note->address;
|
||||||
|
}
|
||||||
|
|
||||||
|
EditSymbolDialog dialog(this, address, &size, &name, dialog_type);
|
||||||
|
|
||||||
|
if (dialog.exec() != QDialog::Accepted)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (dialog.DeleteRequested())
|
||||||
|
{
|
||||||
|
if (type == EditSymbolType::EditRegion)
|
||||||
|
m_ppc_symbol_db.DeleteFunction(address);
|
||||||
|
else
|
||||||
|
m_ppc_symbol_db.DeleteNote(address);
|
||||||
|
}
|
||||||
|
else if (type == EditSymbolType::EditRegion)
|
||||||
|
{
|
||||||
|
m_ppc_symbol_db.AddKnownSymbol(Core::CPUThreadGuard{m_system}, address, size, name, object_name,
|
||||||
|
Common::Symbol::Type::Data);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_ppc_symbol_db.AddKnownNote(address, size, name);
|
||||||
|
m_ppc_symbol_db.DetermineNoteLayers();
|
||||||
|
}
|
||||||
|
|
||||||
|
emit Host::GetInstance()->PPCSymbolsChanged();
|
||||||
|
}
|
||||||
|
|
||||||
void MemoryViewWidget::OnContextMenu(const QPoint& pos)
|
void MemoryViewWidget::OnContextMenu(const QPoint& pos)
|
||||||
{
|
{
|
||||||
auto* item_selected = m_table->itemAt(pos);
|
auto* item_selected = m_table->itemAt(pos);
|
||||||
|
@ -1089,6 +1208,21 @@ void MemoryViewWidget::OnContextMenu(const QPoint& pos)
|
||||||
|
|
||||||
menu->addSeparator();
|
menu->addSeparator();
|
||||||
|
|
||||||
|
auto* note_add_action = menu->addAction(
|
||||||
|
tr("Add Note"), this, [this, addr] { OnEditSymbol(EditSymbolType::AddNote, addr); });
|
||||||
|
auto* note_edit_action = menu->addAction(
|
||||||
|
tr("Edit Note"), this, [this, addr] { OnEditSymbol(EditSymbolType::EditNote, addr); });
|
||||||
|
menu->addAction(tr("Add or edit region label"), this,
|
||||||
|
[this, addr] { OnEditSymbol(EditSymbolType::EditRegion, addr); });
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
menu->addSeparator();
|
||||||
|
|
||||||
menu->addAction(tr("Show in code"), this, [this, addr] { emit ShowCode(addr); });
|
menu->addAction(tr("Show in code"), this, [this, addr] { emit ShowCode(addr); });
|
||||||
|
|
||||||
menu->addSeparator();
|
menu->addSeparator();
|
||||||
|
|
|
@ -24,6 +24,8 @@ class CPUThreadGuard;
|
||||||
class System;
|
class System;
|
||||||
} // namespace Core
|
} // namespace Core
|
||||||
|
|
||||||
|
class PPCSymbolDB;
|
||||||
|
|
||||||
// Captures direct editing of the table.
|
// Captures direct editing of the table.
|
||||||
class TableEditDelegate : public QStyledItemDelegate
|
class TableEditDelegate : public QStyledItemDelegate
|
||||||
{
|
{
|
||||||
|
@ -76,14 +78,23 @@ public:
|
||||||
Full,
|
Full,
|
||||||
Addresses,
|
Addresses,
|
||||||
Values,
|
Values,
|
||||||
|
Symbols,
|
||||||
Auto,
|
Auto,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum class EditSymbolType
|
||||||
|
{
|
||||||
|
AddNote,
|
||||||
|
EditNote,
|
||||||
|
EditRegion,
|
||||||
|
};
|
||||||
|
|
||||||
explicit MemoryViewWidget(Core::System& system, QWidget* parent = nullptr);
|
explicit MemoryViewWidget(Core::System& system, QWidget* parent = nullptr);
|
||||||
|
|
||||||
void CreateTable();
|
void CreateTable();
|
||||||
void UpdateDispatcher(UpdateType type = UpdateType::Addresses);
|
void UpdateDispatcher(UpdateType type = UpdateType::Addresses);
|
||||||
void Update();
|
void Update();
|
||||||
|
void UpdateSymbols();
|
||||||
void UpdateOnFrameEnd();
|
void UpdateOnFrameEnd();
|
||||||
void GetValues();
|
void GetValues();
|
||||||
void UpdateFont(const QFont& font);
|
void UpdateFont(const QFont& font);
|
||||||
|
@ -98,6 +109,7 @@ public:
|
||||||
void SetBPType(BPType type);
|
void SetBPType(BPType type);
|
||||||
void SetAddress(u32 address);
|
void SetAddress(u32 address);
|
||||||
void SetFocus() const;
|
void SetFocus() const;
|
||||||
|
void ShowSymbols(bool enable);
|
||||||
|
|
||||||
void SetBPLoggingEnabled(bool enabled);
|
void SetBPLoggingEnabled(bool enabled);
|
||||||
|
|
||||||
|
@ -108,6 +120,7 @@ signals:
|
||||||
void ActivateSearch();
|
void ActivateSearch();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void OnEditSymbol(EditSymbolType type, u32 addr);
|
||||||
void OnContextMenu(const QPoint& pos);
|
void OnContextMenu(const QPoint& pos);
|
||||||
void OnCopyAddress(u32 addr);
|
void OnCopyAddress(u32 addr);
|
||||||
void OnCopyHex(u32 addr);
|
void OnCopyHex(u32 addr);
|
||||||
|
@ -116,9 +129,11 @@ private:
|
||||||
void UpdateColumns();
|
void UpdateColumns();
|
||||||
void ScrollbarActionTriggered(int action);
|
void ScrollbarActionTriggered(int action);
|
||||||
void ScrollbarSliderReleased();
|
void ScrollbarSliderReleased();
|
||||||
|
|
||||||
std::optional<QString> ValueToString(const Core::CPUThreadGuard& guard, u32 address, Type type);
|
std::optional<QString> ValueToString(const Core::CPUThreadGuard& guard, u32 address, Type type);
|
||||||
|
|
||||||
Core::System& m_system;
|
Core::System& m_system;
|
||||||
|
PPCSymbolDB& m_ppc_symbol_db;
|
||||||
|
|
||||||
MemoryViewTable* m_table;
|
MemoryViewTable* m_table;
|
||||||
QScrollBar* m_scrollbar;
|
QScrollBar* m_scrollbar;
|
||||||
|
@ -137,6 +152,7 @@ private:
|
||||||
int m_alignment = 16;
|
int m_alignment = 16;
|
||||||
int m_data_columns;
|
int m_data_columns;
|
||||||
bool m_dual_view = false;
|
bool m_dual_view = false;
|
||||||
|
bool m_show_symbols = true;
|
||||||
std::mutex m_updating;
|
std::mutex m_updating;
|
||||||
QColor m_highlight_color = QColor(120, 255, 255, 100);
|
QColor m_highlight_color = QColor(120, 255, 255, 100);
|
||||||
|
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
#include <QHBoxLayout>
|
#include <QHBoxLayout>
|
||||||
#include <QLabel>
|
#include <QLabel>
|
||||||
#include <QLineEdit>
|
#include <QLineEdit>
|
||||||
|
#include <QListWidget>
|
||||||
#include <QMenuBar>
|
#include <QMenuBar>
|
||||||
#include <QPushButton>
|
#include <QPushButton>
|
||||||
#include <QRegularExpression>
|
#include <QRegularExpression>
|
||||||
|
@ -32,6 +33,7 @@
|
||||||
#include "Core/ConfigManager.h"
|
#include "Core/ConfigManager.h"
|
||||||
#include "Core/Core.h"
|
#include "Core/Core.h"
|
||||||
#include "Core/HW/AddressSpace.h"
|
#include "Core/HW/AddressSpace.h"
|
||||||
|
#include "Core/PowerPC/PPCSymbolDB.h"
|
||||||
#include "Core/System.h"
|
#include "Core/System.h"
|
||||||
#include "DolphinQt/Debugger/MemoryViewWidget.h"
|
#include "DolphinQt/Debugger/MemoryViewWidget.h"
|
||||||
#include "DolphinQt/Host.h"
|
#include "DolphinQt/Host.h"
|
||||||
|
@ -41,7 +43,7 @@
|
||||||
using Type = MemoryViewWidget::Type;
|
using Type = MemoryViewWidget::Type;
|
||||||
|
|
||||||
MemoryWidget::MemoryWidget(Core::System& system, QWidget* parent)
|
MemoryWidget::MemoryWidget(Core::System& system, QWidget* parent)
|
||||||
: QDockWidget(parent), m_system(system)
|
: QDockWidget(parent), m_system(system), m_ppc_symbol_db(system.GetPPCSymbolDB())
|
||||||
{
|
{
|
||||||
setWindowTitle(tr("Memory"));
|
setWindowTitle(tr("Memory"));
|
||||||
setObjectName(QStringLiteral("memory"));
|
setObjectName(QStringLiteral("memory"));
|
||||||
|
@ -247,6 +249,25 @@ void MemoryWidget::CreateWidgets()
|
||||||
bp_layout->addWidget(m_bp_log_check);
|
bp_layout->addWidget(m_bp_log_check);
|
||||||
bp_layout->setSpacing(1);
|
bp_layout->setSpacing(1);
|
||||||
|
|
||||||
|
// Notes
|
||||||
|
m_labels_group = new QGroupBox(tr("Labels"));
|
||||||
|
auto* symbols_box = new QTabWidget;
|
||||||
|
m_note_list = new QListWidget;
|
||||||
|
m_data_list = new QListWidget;
|
||||||
|
m_symbols_list = new QListWidget;
|
||||||
|
symbols_box->addTab(m_note_list, tr("Notes"));
|
||||||
|
symbols_box->addTab(m_data_list, tr("Data"));
|
||||||
|
symbols_box->addTab(m_symbols_list, tr("Symbols"));
|
||||||
|
m_symbols_list->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
||||||
|
|
||||||
|
auto* labels_layout = new QVBoxLayout;
|
||||||
|
m_search_labels = new QLineEdit;
|
||||||
|
m_search_labels->setPlaceholderText(tr("Filter Label List"));
|
||||||
|
|
||||||
|
m_labels_group->setLayout(labels_layout);
|
||||||
|
labels_layout->addWidget(symbols_box);
|
||||||
|
labels_layout->addWidget(m_search_labels);
|
||||||
|
|
||||||
// Sidebar
|
// Sidebar
|
||||||
auto* sidebar = new QWidget;
|
auto* sidebar = new QWidget;
|
||||||
auto* sidebar_layout = new QVBoxLayout;
|
auto* sidebar_layout = new QVBoxLayout;
|
||||||
|
@ -262,8 +283,9 @@ void MemoryWidget::CreateWidgets()
|
||||||
&MemoryWidget::OnSetValueFromFile);
|
&MemoryWidget::OnSetValueFromFile);
|
||||||
menubar->addMenu(menu_import);
|
menubar->addMenu(menu_import);
|
||||||
|
|
||||||
|
// View Menu
|
||||||
auto* auto_update_action =
|
auto* auto_update_action =
|
||||||
menu_views->addAction(tr("Auto update memory values"), this, [this](bool checked) {
|
menu_views->addAction(tr("&Auto update memory values"), this, [this](bool checked) {
|
||||||
m_auto_update_enabled = checked;
|
m_auto_update_enabled = checked;
|
||||||
if (checked)
|
if (checked)
|
||||||
RegisterAfterFrameEventCallback();
|
RegisterAfterFrameEventCallback();
|
||||||
|
@ -274,14 +296,23 @@ void MemoryWidget::CreateWidgets()
|
||||||
auto_update_action->setChecked(true);
|
auto_update_action->setChecked(true);
|
||||||
|
|
||||||
auto* highlight_update_action =
|
auto* highlight_update_action =
|
||||||
menu_views->addAction(tr("Highlight recently changed values"), this,
|
menu_views->addAction(tr("&Highlight recently changed values"), this,
|
||||||
[this](bool checked) { m_memory_view->ToggleHighlights(checked); });
|
[this](bool checked) { m_memory_view->ToggleHighlights(checked); });
|
||||||
highlight_update_action->setCheckable(true);
|
highlight_update_action->setCheckable(true);
|
||||||
highlight_update_action->setChecked(true);
|
highlight_update_action->setChecked(true);
|
||||||
|
|
||||||
menu_views->addAction(tr("Highlight color"), this,
|
menu_views->addAction(tr("Highlight &color"), this,
|
||||||
[this] { m_memory_view->SetHighlightColor(); });
|
[this] { m_memory_view->SetHighlightColor(); });
|
||||||
|
|
||||||
|
auto* show_notes =
|
||||||
|
menu_views->addAction(tr("&Show symbols and notes"), this, [this](bool checked) {
|
||||||
|
m_labels_visible = checked;
|
||||||
|
m_memory_view->ShowSymbols(checked);
|
||||||
|
UpdateNotes();
|
||||||
|
});
|
||||||
|
show_notes->setCheckable(true);
|
||||||
|
show_notes->setChecked(true);
|
||||||
|
|
||||||
QMenu* menu_export = new QMenu(tr("&Export"), menubar);
|
QMenu* menu_export = new QMenu(tr("&Export"), menubar);
|
||||||
menu_export->addAction(tr("Dump &MRAM"), this, &MemoryWidget::OnDumpMRAM);
|
menu_export->addAction(tr("Dump &MRAM"), this, &MemoryWidget::OnDumpMRAM);
|
||||||
menu_export->addAction(tr("Dump &ExRAM"), this, &MemoryWidget::OnDumpExRAM);
|
menu_export->addAction(tr("Dump &ExRAM"), this, &MemoryWidget::OnDumpExRAM);
|
||||||
|
@ -306,6 +337,7 @@ void MemoryWidget::CreateWidgets()
|
||||||
sidebar_layout->addWidget(address_space_group);
|
sidebar_layout->addWidget(address_space_group);
|
||||||
sidebar_layout->addItem(new QSpacerItem(1, 10));
|
sidebar_layout->addItem(new QSpacerItem(1, 10));
|
||||||
sidebar_layout->addWidget(bp_group);
|
sidebar_layout->addWidget(bp_group);
|
||||||
|
sidebar_layout->addWidget(m_labels_group);
|
||||||
sidebar_layout->addItem(new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Expanding));
|
sidebar_layout->addItem(new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Expanding));
|
||||||
|
|
||||||
// Splitter
|
// Splitter
|
||||||
|
@ -327,6 +359,7 @@ void MemoryWidget::CreateWidgets()
|
||||||
auto* widget = new QWidget;
|
auto* widget = new QWidget;
|
||||||
widget->setLayout(layout);
|
widget->setLayout(layout);
|
||||||
setWidget(widget);
|
setWidget(widget);
|
||||||
|
UpdateNotes();
|
||||||
}
|
}
|
||||||
|
|
||||||
void MemoryWidget::ConnectWidgets()
|
void MemoryWidget::ConnectWidgets()
|
||||||
|
@ -359,6 +392,12 @@ void MemoryWidget::ConnectWidgets()
|
||||||
|
|
||||||
connect(m_base_check, &QCheckBox::toggled, this, &MemoryWidget::ValidateAndPreviewInputValue);
|
connect(m_base_check, &QCheckBox::toggled, this, &MemoryWidget::ValidateAndPreviewInputValue);
|
||||||
connect(m_bp_log_check, &QCheckBox::toggled, this, &MemoryWidget::OnBPLogChanged);
|
connect(m_bp_log_check, &QCheckBox::toggled, this, &MemoryWidget::OnBPLogChanged);
|
||||||
|
|
||||||
|
for (auto* list : {m_symbols_list, m_data_list, m_note_list})
|
||||||
|
connect(list, &QListWidget::itemClicked, this, &MemoryWidget::OnSelectLabel);
|
||||||
|
|
||||||
|
connect(Host::GetInstance(), &Host::PPCSymbolsChanged, this, &MemoryWidget::RefreshLabelBox);
|
||||||
|
connect(m_search_labels, &QLineEdit::textChanged, this, &MemoryWidget::RefreshLabelBox);
|
||||||
connect(m_memory_view, &MemoryViewWidget::ShowCode, this, &MemoryWidget::ShowCode);
|
connect(m_memory_view, &MemoryViewWidget::ShowCode, this, &MemoryWidget::ShowCode);
|
||||||
connect(m_memory_view, &MemoryViewWidget::RequestWatch, this, &MemoryWidget::RequestWatch);
|
connect(m_memory_view, &MemoryViewWidget::RequestWatch, this, &MemoryWidget::RequestWatch);
|
||||||
connect(m_memory_view, &MemoryViewWidget::ActivateSearch, this,
|
connect(m_memory_view, &MemoryViewWidget::ActivateSearch, this,
|
||||||
|
@ -795,6 +834,100 @@ void MemoryWidget::OnSetValueFromFile()
|
||||||
Update();
|
Update();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MemoryWidget::RefreshLabelBox()
|
||||||
|
{
|
||||||
|
if (!m_labels_visible || (m_ppc_symbol_db.Notes().empty() && m_ppc_symbol_db.IsEmpty()))
|
||||||
|
{
|
||||||
|
m_labels_group->hide();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_labels_group->show();
|
||||||
|
|
||||||
|
UpdateSymbols();
|
||||||
|
UpdateNotes();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MemoryWidget::OnSelectLabel()
|
||||||
|
{
|
||||||
|
QList<QListWidgetItem*> items;
|
||||||
|
if (m_note_list->isVisible())
|
||||||
|
items = m_note_list->selectedItems();
|
||||||
|
else if (m_symbols_list->isVisible())
|
||||||
|
items = m_symbols_list->selectedItems();
|
||||||
|
else if (m_data_list->isVisible())
|
||||||
|
items = m_data_list->selectedItems();
|
||||||
|
|
||||||
|
if (items.isEmpty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
const u32 address = items[0]->data(Qt::UserRole).toUInt();
|
||||||
|
|
||||||
|
SetAddress(address);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MemoryWidget::UpdateSymbols()
|
||||||
|
{
|
||||||
|
const QString selection = m_symbols_list->selectedItems().isEmpty() ?
|
||||||
|
QString{} :
|
||||||
|
m_symbols_list->selectedItems()[0]->text();
|
||||||
|
m_symbols_list->clear();
|
||||||
|
m_data_list->clear();
|
||||||
|
|
||||||
|
for (const auto& symbol : m_ppc_symbol_db.Symbols())
|
||||||
|
{
|
||||||
|
QString name = QString::fromStdString(symbol.second.name);
|
||||||
|
|
||||||
|
// If the symbol has an object name, add it to the entry name.
|
||||||
|
if (!symbol.second.object_name.empty())
|
||||||
|
{
|
||||||
|
name += QString::fromStdString(fmt::format(" ({})", symbol.second.object_name));
|
||||||
|
}
|
||||||
|
|
||||||
|
auto* item = new QListWidgetItem(name);
|
||||||
|
if (name == selection)
|
||||||
|
item->setSelected(true);
|
||||||
|
|
||||||
|
item->setData(Qt::UserRole, symbol.second.address);
|
||||||
|
|
||||||
|
if (!name.contains(m_search_labels->text(), Qt::CaseInsensitive))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (symbol.second.type != Common::Symbol::Type::Function)
|
||||||
|
m_data_list->addItem(item);
|
||||||
|
else
|
||||||
|
m_symbols_list->addItem(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_symbols_list->sortItems();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MemoryWidget::UpdateNotes()
|
||||||
|
{
|
||||||
|
// Save selection to re-apply.
|
||||||
|
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);
|
||||||
|
|
||||||
|
// Filter notes based on the search text.
|
||||||
|
if (name.contains(m_search_labels->text(), Qt::CaseInsensitive))
|
||||||
|
m_note_list->addItem(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_note_list->sortItems();
|
||||||
|
}
|
||||||
|
|
||||||
static void DumpArray(const std::string& filename, const u8* data, size_t length)
|
static void DumpArray(const std::string& filename, const u8* data, size_t length)
|
||||||
{
|
{
|
||||||
if (!data)
|
if (!data)
|
||||||
|
|
|
@ -14,8 +14,11 @@
|
||||||
class MemoryViewWidget;
|
class MemoryViewWidget;
|
||||||
class QCheckBox;
|
class QCheckBox;
|
||||||
class QComboBox;
|
class QComboBox;
|
||||||
|
class QGroupBox;
|
||||||
|
class QHideEvent;
|
||||||
class QLabel;
|
class QLabel;
|
||||||
class QLineEdit;
|
class QLineEdit;
|
||||||
|
class QListWidget;
|
||||||
class QPushButton;
|
class QPushButton;
|
||||||
class QRadioButton;
|
class QRadioButton;
|
||||||
class QShowEvent;
|
class QShowEvent;
|
||||||
|
@ -27,6 +30,8 @@ class System;
|
||||||
class CPUThreadGuard;
|
class CPUThreadGuard;
|
||||||
} // namespace Core
|
} // namespace Core
|
||||||
|
|
||||||
|
class PPCSymbolDB;
|
||||||
|
|
||||||
class MemoryWidget : public QDockWidget
|
class MemoryWidget : public QDockWidget
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
@ -66,6 +71,11 @@ private:
|
||||||
void OnSetValue();
|
void OnSetValue();
|
||||||
void OnSetValueFromFile();
|
void OnSetValueFromFile();
|
||||||
|
|
||||||
|
void OnSelectLabel();
|
||||||
|
void RefreshLabelBox();
|
||||||
|
void UpdateSymbols();
|
||||||
|
void UpdateNotes();
|
||||||
|
|
||||||
void OnDumpMRAM();
|
void OnDumpMRAM();
|
||||||
void OnDumpExRAM();
|
void OnDumpExRAM();
|
||||||
void OnDumpARAM();
|
void OnDumpARAM();
|
||||||
|
@ -85,6 +95,7 @@ private:
|
||||||
void ActivateSearchAddress();
|
void ActivateSearchAddress();
|
||||||
|
|
||||||
Core::System& m_system;
|
Core::System& m_system;
|
||||||
|
PPCSymbolDB& m_ppc_symbol_db;
|
||||||
|
|
||||||
MemoryViewWidget* m_memory_view;
|
MemoryViewWidget* m_memory_view;
|
||||||
QSplitter* m_splitter;
|
QSplitter* m_splitter;
|
||||||
|
@ -115,6 +126,15 @@ private:
|
||||||
QRadioButton* m_bp_read_only;
|
QRadioButton* m_bp_read_only;
|
||||||
QRadioButton* m_bp_write_only;
|
QRadioButton* m_bp_write_only;
|
||||||
QCheckBox* m_bp_log_check;
|
QCheckBox* m_bp_log_check;
|
||||||
|
|
||||||
|
QGroupBox* m_labels_group;
|
||||||
|
QLineEdit* m_search_labels;
|
||||||
|
QListWidget* m_symbols_list;
|
||||||
|
QListWidget* m_data_list;
|
||||||
|
QListWidget* m_note_list;
|
||||||
|
QString m_note_filter;
|
||||||
|
bool m_labels_visible = true;
|
||||||
|
|
||||||
Common::EventHook m_vi_end_field_event;
|
Common::EventHook m_vi_end_field_event;
|
||||||
|
|
||||||
bool m_auto_update_enabled = true;
|
bool m_auto_update_enabled = true;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue