diff --git a/src/Cutter.cpp b/src/Cutter.cpp index 99c44b3d..881a1be7 100644 --- a/src/Cutter.cpp +++ b/src/Cutter.cpp @@ -754,6 +754,47 @@ QString CutterCore::getRegisterName(QString registerRole) void CutterCore::setRegister(QString regName, QString regValue) { cmd("dr " + regName + "=" + regValue); + emit registersChanged(); +} + +void CutterCore::startDebug() +{ + cmd("ood"); + emit registersChanged(); +} + +void CutterCore::continueDebug() +{ + cmd("dc"); + emit registersChanged(); +} + +void CutterCore::continueUntilDebug(QString offset) +{ + cmd("dcu " + offset); + emit registersChanged(); +} + +void CutterCore::stepDebug() +{ + cmd("ds"); + QString programCounterValue = cmd("dr?`drn pc`").trimmed(); + seek(programCounterValue); + emit registersChanged(); +} + +void CutterCore::stepOverDebug() +{ + cmd("dso"); + QString programCounterValue = cmd("dr?`drn pc`").trimmed(); + seek(programCounterValue); + emit registersChanged(); +} + +void CutterCore::addBreakpoint(RVA addr) +{ + cmd("db " + RAddressString(addr)); + emit instructionChanged(addr); } QJsonDocument CutterCore::getBacktrace() @@ -761,6 +802,29 @@ QJsonDocument CutterCore::getBacktrace() return cmdj("dbtj"); } +QList CutterCore::getMemoryMap() +{ + QList ret; + QJsonArray memoryMapArray = cmdj("dmj").array(); + + for (QJsonValue value : memoryMapArray) { + QJsonObject memMapObject = value.toObject(); + + MemoryMapDescription memMap; + + memMap.name = memMapObject["name"].toString(); + memMap.fileName = memMapObject["file"].toString(); + memMap.addrStart = memMapObject["addr"].toVariant().toULongLong(); + memMap.addrEnd = memMapObject["addr_end"].toVariant().toULongLong(); + memMap.type = memMapObject["type"].toString(); + memMap.permission = memMapObject["perm"].toString(); + + ret << memMap; + } + + return ret; +} + QStringList CutterCore::getStats() { QStringList stats; diff --git a/src/Cutter.h b/src/Cutter.h index 3e93df3f..2e6b0528 100644 --- a/src/Cutter.h +++ b/src/Cutter.h @@ -294,6 +294,15 @@ struct BlockStatistics { QList blocks; }; +struct MemoryMapDescription { + RVA addrStart; + RVA addrEnd; + QString name; + QString fileName; + QString type; + QString permission; +}; + Q_DECLARE_METATYPE(FunctionDescription) Q_DECLARE_METATYPE(ImportDescription) Q_DECLARE_METATYPE(ExportDescription) @@ -322,6 +331,7 @@ Q_DECLARE_METATYPE(HeaderDescription) Q_DECLARE_METATYPE(ZignatureDescription) Q_DECLARE_METATYPE(SearchDescription) Q_DECLARE_METATYPE(SectionDescription) +Q_DECLARE_METATYPE(MemoryMapDescription) class CutterCore: public QObject { @@ -450,12 +460,21 @@ public: ulong get_baddr(); QList> get_exec_sections(); QString getOffsetInfo(QString addr); + + // Debug QJsonDocument getRegistersInfo(); QJsonDocument getRegisterValues(); QString getRegisterName(QString registerRole); void setRegister(QString regName, QString regValue); QJsonDocument getStack(int size = 0x40); QJsonDocument getBacktrace(); + void startDebug(); + void continueDebug(); + void continueUntilDebug(QString offset); + void stepDebug(); + void stepOverDebug(); + void addBreakpoint(RVA offset); + RVA getOffsetJump(RVA addr); QString getDecompiledCode(RVA addr); QString getDecompiledCode(QString addr); @@ -508,6 +527,7 @@ public: QList getAllResources(); QList getAllVTables(); QList getAllTypes(); + QList getMemoryMap(); QList getAllSearch(QString search_for, QString space); BlockStatistics getBlockStatistics(unsigned int blocksCount); @@ -537,6 +557,7 @@ signals: void functionsChanged(); void flagsChanged(); void commentsChanged(); + void registersChanged(); void instructionChanged(RVA offset); void notesChanged(const QString ¬es); diff --git a/src/Cutter.pro b/src/Cutter.pro index 4a1a470b..775a9486 100644 --- a/src/Cutter.pro +++ b/src/Cutter.pro @@ -175,7 +175,9 @@ SOURCES += \ dialogs/OpenFileDialog.cpp \ utils/CommandTask.cpp \ utils/ProgressIndicator.cpp \ - utils/R2Task.cpp + utils/R2Task.cpp \ + widgets/DebugToolbar.cpp \ + widgets/MemoryMapWidget.cpp HEADERS += \ Cutter.h \ @@ -262,7 +264,9 @@ HEADERS += \ utils/CommandTask.h \ utils/ProgressIndicator.h \ plugins/CutterPlugin.h \ - utils/R2Task.h + utils/R2Task.h \ + widgets/DebugToolbar.h \ + widgets/MemoryMapWidget.h FORMS += \ dialogs/AboutDialog.ui \ @@ -310,7 +314,8 @@ FORMS += \ widgets/StackWidget.ui \ widgets/RegistersWidget.ui \ widgets/BacktraceWidget.ui \ - dialogs/OpenFileDialog.ui + dialogs/OpenFileDialog.ui \ + widgets/MemoryMapWidget.ui RESOURCES += \ resources.qrc \ diff --git a/src/MainWindow.cpp b/src/MainWindow.cpp index cc655e20..354095c7 100644 --- a/src/MainWindow.cpp +++ b/src/MainWindow.cpp @@ -73,6 +73,8 @@ #include "widgets/JupyterWidget.h" #include "widgets/HeadersWidget.h" #include "widgets/ZignaturesWidget.h" +#include "widgets/DebugToolbar.h" +#include "widgets/MemoryMapWidget.h" // Graphics #include @@ -124,9 +126,19 @@ void MainWindow::initUI() QWidget *spacer3 = new QWidget(); spacer3->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); spacer3->setMinimumSize(20, 20); - spacer3->setMaximumWidth(300); + spacer3->setMaximumWidth(100); ui->mainToolBar->addWidget(spacer3); + QToolBar *debugToolbar = new DebugToolbar(this); + ui->mainToolBar->addWidget(debugToolbar); + + // Sepparator between undo/redo and goto lineEdit + QWidget *spacer4 = new QWidget(); + spacer4->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + spacer4->setMinimumSize(20, 20); + spacer4->setMaximumWidth(100); + ui->mainToolBar->addWidget(spacer4); + // Omnibar LineEdit this->omnibar = new Omnibar(this); ui->mainToolBar->addWidget(this->omnibar); @@ -193,6 +205,7 @@ void MainWindow::initUI() stackDock = new StackWidget(this, ui->actionStack); backtraceDock = new BacktraceWidget(this, ui->actionBacktrace); registersDock = new RegistersWidget(this, ui->actionRegisters); + memoryMapDock = new MemoryMapWidget(this, ui->actionMemoryMap); #ifdef CUTTER_ENABLE_JUPYTER jupyterDock = new JupyterWidget(this, ui->actionJupyter); #else @@ -531,6 +544,7 @@ void MainWindow::restoreDocks() addExtraWidget(stackDock); splitDockWidget(stackDock, registersDock, Qt::Vertical); splitDockWidget(stackDock, backtraceDock, Qt::Vertical); + splitDockWidget(stackDock, memoryMapDock, Qt::Vertical); #ifdef CUTTER_ENABLE_JUPYTER tabifyDockWidget(dashboardDock, jupyterDock); #endif diff --git a/src/MainWindow.h b/src/MainWindow.h index 0bbc4704..4bd09244 100644 --- a/src/MainWindow.h +++ b/src/MainWindow.h @@ -222,6 +222,7 @@ private: QDockWidget *stackDock = nullptr; QDockWidget *registersDock = nullptr; QDockWidget *backtraceDock = nullptr; + QDockWidget *memoryMapDock = nullptr; NewFileDialog *newFileDialog = nullptr; #ifdef CUTTER_ENABLE_JUPYTER JupyterWidget *jupyterDock = nullptr; diff --git a/src/MainWindow.ui b/src/MainWindow.ui index e5c1f250..96d49168 100644 --- a/src/MainWindow.ui +++ b/src/MainWindow.ui @@ -230,6 +230,7 @@ border-top: 0px; + @@ -294,23 +295,6 @@ border-top: 0px; Main toolbar - - -QToolButton { -margin: 1px; -margin-left: 2px; -margin-right: 2px; -padding: 2px; -padding-left: 7px; -padding-right: 7px; -border-radius: 6px; -background-color: #ffffff; -} -QToolButton:pressed { -background-color: palette(dark); -} - - false @@ -457,7 +441,7 @@ background-color: palette(dark); - :/img/icons/arrow_left.svg:/img/icons/arrow_left.svg + :/img/icons/arrow_left_light.svg:/img/icons/arrow_left_light.svg Back @@ -469,7 +453,7 @@ background-color: palette(dark); - :/img/icons/arrow_right.svg:/img/icons/arrow_right.svg + :/img/icons/arrow_right_light.svg:/img/icons/arrow_right_light.svg Forward @@ -1054,6 +1038,14 @@ background-color: palette(dark); Backtrace + + + true + + + Memory map + + true diff --git a/src/img/icons/arrow_left_light.svg b/src/img/icons/arrow_left_light.svg new file mode 100644 index 00000000..ffaaa4b7 --- /dev/null +++ b/src/img/icons/arrow_left_light.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/img/icons/arrow_right_light.svg b/src/img/icons/arrow_right_light.svg new file mode 100644 index 00000000..55f91f65 --- /dev/null +++ b/src/img/icons/arrow_right_light.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/img/icons/continue_until_main.svg b/src/img/icons/continue_until_main.svg new file mode 100644 index 00000000..4583ed0e --- /dev/null +++ b/src/img/icons/continue_until_main.svg @@ -0,0 +1,4 @@ + + + M + \ No newline at end of file diff --git a/src/img/icons/media-skip-forward_light.svg b/src/img/icons/media-skip-forward_light.svg new file mode 100644 index 00000000..8d96b9fc --- /dev/null +++ b/src/img/icons/media-skip-forward_light.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/src/img/icons/play_light.svg b/src/img/icons/play_light.svg new file mode 100644 index 00000000..988aab5b --- /dev/null +++ b/src/img/icons/play_light.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/img/icons/step_light.svg b/src/img/icons/step_light.svg new file mode 100644 index 00000000..fe587a99 --- /dev/null +++ b/src/img/icons/step_light.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/img/icons/step_over_light.svg b/src/img/icons/step_over_light.svg new file mode 100644 index 00000000..6f45806f --- /dev/null +++ b/src/img/icons/step_over_light.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/menus/DisassemblyContextMenu.cpp b/src/menus/DisassemblyContextMenu.cpp index 0a83d560..76aec277 100644 --- a/src/menus/DisassemblyContextMenu.cpp +++ b/src/menus/DisassemblyContextMenu.cpp @@ -41,7 +41,10 @@ DisassemblyContextMenu::DisassemblyContextMenu(QWidget *parent) actionSetBaseString(this), actionSetBits16(this), actionSetBits32(this), - actionSetBits64(this) + actionSetBits64(this), + actionContinueUntil(this), + actionAddBreakpoint(this), + actionSetPC(this) { createAction(&actionCopy, tr("Copy"), getCopySequence(), SLOT(on_actionCopy_triggered())); copySeparator = addSeparator(); @@ -107,6 +110,17 @@ DisassemblyContextMenu::DisassemblyContextMenu(QWidget *parent) actionJmpReverse.setText(tr("Reverse Jump")); editMenu->addAction(&actionJmpReverse); + addSeparator(); + debugMenu = new QMenu(tr("Debug"), this); + debugMenuAction = addMenu(debugMenu); + actionAddBreakpoint.setText(tr("Add breakpoint")); + debugMenu->addAction(&actionAddBreakpoint); + actionContinueUntil.setText(tr("Continue until line")); + debugMenu->addAction(&actionContinueUntil); + QString progCounterName = Core()->getRegisterName("PC"); + actionSetPC.setText("Set " + progCounterName + " here"); + debugMenu->addAction(&actionSetPC); + connect(&actionEditInstruction, SIGNAL(triggered(bool)), this, SLOT(on_actionEditInstruction_triggered())); connect(&actionNopInstruction, SIGNAL(triggered(bool)), this, @@ -134,6 +148,13 @@ DisassemblyContextMenu::DisassemblyContextMenu(QWidget *parent) connect(&actionSetBits32, SIGNAL(triggered(bool)), this, SLOT(on_actionSetBits32_triggered())); connect(&actionSetBits64, SIGNAL(triggered(bool)), this, SLOT(on_actionSetBits64_triggered())); + connect(&actionAddBreakpoint, &QAction::triggered, + this, &DisassemblyContextMenu::on_actionAddBreakpoint_triggered); + connect(&actionContinueUntil, &QAction::triggered, + this, &DisassemblyContextMenu::on_actionContinueUntil_triggered); + connect(&actionSetPC, &QAction::triggered, + this, &DisassemblyContextMenu::on_actionSetPC_triggered); + connect(this, SIGNAL(aboutToShow()), this, SLOT(aboutToShowSlot())); } @@ -216,6 +237,10 @@ void DisassemblyContextMenu::aboutToShowSlot() // decide to show Reverse jmp option showReverseJmpQuery(); + // show debug options + // @TODO determine if we are being debugged and only show the menu in those cases + // maybe using dpt command + debugMenuAction->setVisible(true); } QKeySequence DisassemblyContextMenu::getCopySequence() const @@ -325,6 +350,22 @@ void DisassemblyContextMenu::on_actionCopyAddr_triggered() clipboard->setText(RAddressString(offset)); } +void DisassemblyContextMenu::on_actionAddBreakpoint_triggered() +{ + Core()->addBreakpoint(offset); +} + +void DisassemblyContextMenu::on_actionContinueUntil_triggered() +{ + Core()->continueUntilDebug(RAddressString(offset)); +} + +void DisassemblyContextMenu::on_actionSetPC_triggered() +{ + QString progCounterName = Core()->getRegisterName("PC"); + Core()->setRegister(progCounterName, RAddressString(offset)); +} + void DisassemblyContextMenu::on_actionAddComment_triggered() { QString oldComment = Core()->cmd("CC." + RAddressString(offset)); diff --git a/src/menus/DisassemblyContextMenu.h b/src/menus/DisassemblyContextMenu.h index 5947f75d..8436513e 100644 --- a/src/menus/DisassemblyContextMenu.h +++ b/src/menus/DisassemblyContextMenu.h @@ -55,6 +55,9 @@ private slots: void on_actionSetBits16_triggered(); void on_actionSetBits32_triggered(); void on_actionSetBits64_triggered(); + void on_actionAddBreakpoint_triggered(); + void on_actionContinueUntil_triggered(); + void on_actionSetPC_triggered(); private: QKeySequence getCopySequence() const; @@ -111,6 +114,12 @@ private: QAction actionSetBits32; QAction actionSetBits64; + QMenu *debugMenu; + QAction *debugMenuAction; + QAction actionContinueUntil; + QAction actionAddBreakpoint; + QAction actionSetPC; + // For creating anonymous entries (that are always visible) void createAction(QString name, QKeySequence keySequence, const char *slot); void createAction(QAction *action, QString name, QKeySequence keySequence, const char *slot); diff --git a/src/resources.qrc b/src/resources.qrc index a8de2513..afb799da 100644 --- a/src/resources.qrc +++ b/src/resources.qrc @@ -1,7 +1,7 @@ - img/icons/arrow_left.svg - img/icons/arrow_right.svg + img/icons/arrow_left_light.svg + img/icons/arrow_right_light.svg img/icons/sidebar.svg img/icons/undo.svg img/icons/redo.svg @@ -17,6 +17,11 @@ img/icons/spin.svg img/icons/plus.svg img/icons/play.svg + img/icons/play_light.svg + img/icons/media-skip-forward_light.svg + img/icons/continue_until_main.svg + img/icons/step_light.svg + img/icons/step_over_light.svg img/icons/cloud.svg img/icons/down.svg img/icons/down_white.svg diff --git a/src/widgets/DebugToolbar.cpp b/src/widgets/DebugToolbar.cpp new file mode 100644 index 00000000..65a4f051 --- /dev/null +++ b/src/widgets/DebugToolbar.cpp @@ -0,0 +1,39 @@ +#include "DebugToolbar.h" +#include "MainWindow.h" + +#include +#include + +DebugToolbar::DebugToolbar(MainWindow *main, QWidget *parent) : + QToolBar(parent), + main(main) +{ + setObjectName("debugToolbar"); + QIcon startIcon = QIcon(":/img/icons/play_light.svg"); + QIcon continueIcon = QIcon(":/img/icons/media-skip-forward_light.svg"); + QIcon continueUntilIcon = QIcon(":/img/icons/continue_until_main.svg"); + QIcon stepIcon = QIcon(":/img/icons/step_light.svg"); + QIcon stepOverIcon = QIcon(":/img/icons/step_over_light.svg"); + + QAction *actionStart = new QAction(startIcon, tr("Start debug"), parent); + QAction *actionContinue = new QAction(continueIcon, tr("Continue"), parent); + QAction *actionContinueUntilMain = new QAction(continueUntilIcon, tr("Continue until main"), parent); + QAction *actionStep = new QAction(stepIcon, tr("Step"), parent); + QAction *actionStepOver = new QAction(stepOverIcon, tr("Step over"), parent); + addAction(actionStart); + addAction(actionContinue); + addAction(actionContinueUntilMain); + addAction(actionStep); + addAction(actionStepOver); + + connect(actionStep, &QAction::triggered, Core(), &CutterCore::stepDebug); + connect(actionStart, &QAction::triggered, Core(), &CutterCore::startDebug); + connect(actionStepOver, &QAction::triggered, Core(), &CutterCore::stepOverDebug); + connect(actionContinue, &QAction::triggered, Core(), &CutterCore::continueDebug); + connect(actionContinueUntilMain, &QAction::triggered, this, &DebugToolbar::continueUntilMain); +} + +void DebugToolbar::continueUntilMain() +{ + Core()->continueUntilDebug(tr("main")); +} \ No newline at end of file diff --git a/src/widgets/DebugToolbar.h b/src/widgets/DebugToolbar.h new file mode 100644 index 00000000..6cd33a90 --- /dev/null +++ b/src/widgets/DebugToolbar.h @@ -0,0 +1,21 @@ +#pragma once + +#include +#include "Cutter.h" + +class MainWindow; + +class DebugToolbar : public QToolBar +{ + Q_OBJECT + +public: + explicit DebugToolbar(MainWindow *main, QWidget *parent = nullptr); + +private: + MainWindow *main; + +private slots: + void continueUntilMain(); + +}; \ No newline at end of file diff --git a/src/widgets/MemoryMapWidget.cpp b/src/widgets/MemoryMapWidget.cpp new file mode 100644 index 00000000..f5d3ef06 --- /dev/null +++ b/src/widgets/MemoryMapWidget.cpp @@ -0,0 +1,154 @@ +#include "MemoryMapWidget.h" +#include "ui_MemoryMapWidget.h" +#include "MainWindow.h" +#include "utils/Helpers.h" + +MemoryMapModel::MemoryMapModel(QList *memoryMaps, QObject *parent) + : QAbstractListModel(parent), + memoryMaps(memoryMaps) +{ +} + +int MemoryMapModel::rowCount(const QModelIndex &) const +{ + return memoryMaps->count(); +} + +int MemoryMapModel::columnCount(const QModelIndex &) const +{ + return MemoryMapModel::ColumnCount; +} + +QVariant MemoryMapModel::data(const QModelIndex &index, int role) const +{ + if (index.row() >= memoryMaps->count()) + return QVariant(); + + const MemoryMapDescription &memoryMap = memoryMaps->at(index.row()); + + switch (role) { + case Qt::DisplayRole: + switch (index.column()) { + case AddrStartColumn: + return RAddressString(memoryMap.addrStart); + case AddrEndColumn: + return RAddressString(memoryMap.addrEnd); + case NameColumn: + return memoryMap.name; + case PermColumn: + return memoryMap.permission; + default: + return QVariant(); + } + case MemoryDescriptionRole: + return QVariant::fromValue(memoryMap); + default: + return QVariant(); + } +} + +QVariant MemoryMapModel::headerData(int section, Qt::Orientation, int role) const +{ + switch (role) { + case Qt::DisplayRole: + switch (section) { + case AddrStartColumn: + return tr("Offset start"); + case AddrEndColumn: + return tr("Offset end"); + case NameColumn: + return tr("Name"); + case PermColumn: + return tr("Permissions"); + default: + return QVariant(); + } + default: + return QVariant(); + } +} + +void MemoryMapModel::beginReloadMemoryMap() +{ + beginResetModel(); +} + +void MemoryMapModel::endReloadMemoryMap() +{ + endResetModel(); +} + +MemoryProxyModel::MemoryProxyModel(MemoryMapModel *sourceModel, QObject *parent) + : QSortFilterProxyModel(parent) +{ + setSourceModel(sourceModel); +} + +bool MemoryProxyModel::filterAcceptsRow(int row, const QModelIndex &parent) const +{ + QModelIndex index = sourceModel()->index(row, 0, parent); + MemoryMapDescription item = index.data(MemoryMapModel::MemoryDescriptionRole).value(); + return item.name.contains(filterRegExp()); +} + +bool MemoryProxyModel::lessThan(const QModelIndex &left, const QModelIndex &right) const +{ + MemoryMapDescription leftMemMap = left.data(MemoryMapModel::MemoryDescriptionRole).value(); + MemoryMapDescription rightMemMap = right.data(MemoryMapModel::MemoryDescriptionRole).value(); + + switch (left.column()) { + case MemoryMapModel::AddrStartColumn: + return leftMemMap.addrStart < rightMemMap.addrStart; + case MemoryMapModel::AddrEndColumn: + return leftMemMap.addrEnd < rightMemMap.addrEnd; + case MemoryMapModel::NameColumn: + return leftMemMap.name < rightMemMap.name; + case MemoryMapModel::PermColumn: + return leftMemMap.permission < rightMemMap.permission; + default: + break; + } + + return leftMemMap.addrStart < rightMemMap.addrStart; +} + +MemoryMapWidget::MemoryMapWidget(MainWindow *main, QAction *action) : + CutterDockWidget(main, action), + ui(new Ui::MemoryMapWidget) +{ + ui->setupUi(this); + + memoryModel = new MemoryMapModel(&memoryMaps, this); + memoryProxyModel = new MemoryProxyModel(memoryModel, this); + ui->memoryTreeView->setModel(memoryProxyModel); + ui->memoryTreeView->sortByColumn(MemoryMapModel::AddrStartColumn, Qt::AscendingOrder); + + setScrollMode(); + + connect(Core(), &CutterCore::refreshAll, this, &MemoryMapWidget::refreshMemoryMap); + connect(Core(), &CutterCore::registersChanged, this, &MemoryMapWidget::refreshMemoryMap); +} + +MemoryMapWidget::~MemoryMapWidget() {} + +void MemoryMapWidget::refreshMemoryMap() +{ + memoryModel->beginReloadMemoryMap(); + memoryMaps = Core()->getMemoryMap(); + memoryModel->endReloadMemoryMap(); + + ui->memoryTreeView->resizeColumnToContents(0); + ui->memoryTreeView->resizeColumnToContents(1); + ui->memoryTreeView->resizeColumnToContents(2); +} + +void MemoryMapWidget::setScrollMode() +{ + qhelpers::setVerticalScrollMode(ui->memoryTreeView); +} + +void MemoryMapWidget::on_memoryTreeView_doubleClicked(const QModelIndex &index) +{ + MemoryMapDescription item = index.data(MemoryMapModel::MemoryDescriptionRole).value(); + Core()->seek(item.addrStart); +} diff --git a/src/widgets/MemoryMapWidget.h b/src/widgets/MemoryMapWidget.h new file mode 100644 index 00000000..79f920b2 --- /dev/null +++ b/src/widgets/MemoryMapWidget.h @@ -0,0 +1,84 @@ +#pragma once + +#include + +#include "Cutter.h" +#include "CutterDockWidget.h" + +#include +#include + +class MainWindow; +class QTreeWidget; + +namespace Ui +{ + class MemoryMapWidget; +} + + +class MainWindow; +class QTreeWidgetItem; + + +class MemoryMapModel: public QAbstractListModel +{ + Q_OBJECT + +private: + QList *memoryMaps; + +public: + enum Column { AddrStartColumn = 0, AddrEndColumn, NameColumn, PermColumn, ColumnCount }; + enum Role { MemoryDescriptionRole = Qt::UserRole }; + + MemoryMapModel(QList *memoryMaps, QObject *parent = 0); + + int rowCount(const QModelIndex &parent = QModelIndex()) const; + int columnCount(const QModelIndex &parent = QModelIndex()) const; + + QVariant data(const QModelIndex &index, int role) const; + QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; + + void beginReloadMemoryMap(); + void endReloadMemoryMap(); +}; + + + +class MemoryProxyModel : public QSortFilterProxyModel +{ + Q_OBJECT + +public: + MemoryProxyModel(MemoryMapModel *sourceModel, QObject *parent = nullptr); + +protected: + bool filterAcceptsRow(int row, const QModelIndex &parent) const override; + bool lessThan(const QModelIndex &left, const QModelIndex &right) const override; +}; + + + +class MemoryMapWidget : public CutterDockWidget +{ + Q_OBJECT + +public: + explicit MemoryMapWidget(MainWindow *main, QAction *action = nullptr); + ~MemoryMapWidget(); + +private slots: + void on_memoryTreeView_doubleClicked(const QModelIndex &index); + + void refreshMemoryMap(); + +private: + std::unique_ptr ui; + + MemoryMapModel *memoryModel; + MemoryProxyModel *memoryProxyModel; + QList memoryMaps; + + void setScrollMode(); +}; diff --git a/src/widgets/MemoryMapWidget.ui b/src/widgets/MemoryMapWidget.ui new file mode 100644 index 00000000..1349b3b3 --- /dev/null +++ b/src/widgets/MemoryMapWidget.ui @@ -0,0 +1,58 @@ + + + MemoryMapWidget + + + + 0 + 0 + 400 + 300 + + + + Memory Map + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + QTreeView::item +{ + padding-top: 1px; + padding-bottom: 1px; +} + + + QFrame::NoFrame + + + 0 + + + 8 + + + true + + + + + + + + + diff --git a/src/widgets/RegistersWidget.cpp b/src/widgets/RegistersWidget.cpp index 7d13633a..44d1892a 100644 --- a/src/widgets/RegistersWidget.cpp +++ b/src/widgets/RegistersWidget.cpp @@ -19,7 +19,7 @@ RegistersWidget::RegistersWidget(MainWindow *main, QAction *action) : ui->verticalLayout->addWidget(buttonSetRegisters); connect(Core(), &CutterCore::refreshAll, this, &RegistersWidget::updateContents); - connect(Core(), &CutterCore::seekChanged, this, &RegistersWidget::updateContents); + connect(Core(), &CutterCore::registersChanged, this, &RegistersWidget::updateContents); } RegistersWidget::~RegistersWidget() {}