From 0b697b7fb06b9957fefe296346b1f9dad37e13c7 Mon Sep 17 00:00:00 2001 From: Anton Kochkov Date: Mon, 3 Jul 2023 22:12:21 +0800 Subject: [PATCH] Add global variables support - Add Globals widget - Add global variable add/modify dialog - Add "Add at" context submenu in Disassembly widget --- src/CMakeLists.txt | 4 + src/core/Cutter.cpp | 126 ++++++++++++++++++++++- src/core/Cutter.h | 10 ++ src/core/CutterDescriptions.h | 8 ++ src/core/MainWindow.cpp | 3 + src/core/MainWindow.h | 2 + src/dialogs/GlobalVariableDialog.cpp | 64 ++++++++++++ src/dialogs/GlobalVariableDialog.h | 32 ++++++ src/dialogs/GlobalVariableDialog.ui | 100 ++++++++++++++++++ src/menus/DisassemblyContextMenu.cpp | 43 +++++++- src/menus/DisassemblyContextMenu.h | 4 + src/widgets/GlobalsWidget.cpp | 147 +++++++++++++++++++++++++++ src/widgets/GlobalsWidget.h | 71 +++++++++++++ 13 files changed, 607 insertions(+), 7 deletions(-) create mode 100644 src/dialogs/GlobalVariableDialog.cpp create mode 100644 src/dialogs/GlobalVariableDialog.h create mode 100644 src/dialogs/GlobalVariableDialog.ui create mode 100644 src/widgets/GlobalsWidget.cpp create mode 100644 src/widgets/GlobalsWidget.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 0d433df09..1dfd6bafb 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -18,6 +18,7 @@ set(SOURCES dialogs/CommentsDialog.cpp dialogs/EditInstructionDialog.cpp dialogs/FlagDialog.cpp + dialogs/GlobalVariableDialog.cpp dialogs/RemoteDebugDialog.cpp dialogs/NativeDebugDialog.cpp dialogs/XrefsDialog.cpp @@ -36,6 +37,7 @@ set(SOURCES widgets/ExportsWidget.cpp widgets/FlagsWidget.cpp widgets/FunctionsWidget.cpp + widgets/GlobalsWidget.cpp widgets/ImportsWidget.cpp widgets/Omnibar.cpp widgets/RelocsWidget.cpp @@ -172,6 +174,7 @@ set(HEADER_FILES dialogs/CommentsDialog.h dialogs/EditInstructionDialog.h dialogs/FlagDialog.h + dialogs/GlobalVariableDialog.h dialogs/RemoteDebugDialog.h dialogs/NativeDebugDialog.h dialogs/XrefsDialog.h @@ -327,6 +330,7 @@ set(UI_FILES dialogs/CommentsDialog.ui dialogs/EditInstructionDialog.ui dialogs/FlagDialog.ui + dialogs/GlobalVariableDialog.ui dialogs/RemoteDebugDialog.ui dialogs/NativeDebugDialog.ui dialogs/XrefsDialog.ui diff --git a/src/core/Cutter.cpp b/src/core/Cutter.cpp index 93f15ec67..0530df550 100644 --- a/src/core/Cutter.cpp +++ b/src/core/Cutter.cpp @@ -1815,6 +1815,32 @@ QList CutterCore::getVariables(RVA at) return ret; } +QList CutterCore::getAllGlobals() +{ + CORE_LOCK(); + RzListIter *it; + + QList ret; + + RzAnalysisVarGlobal *glob; + if (core && core->analysis && core->analysis->typedb) { + const RzList *globals = rz_analysis_var_global_get_all(core->analysis); + CutterRzListForeach (globals, it, RzAnalysisVarGlobal, glob) { + const char *gtype = rz_type_as_string(core->analysis->typedb, glob->type); + if (!gtype) { + continue; + } + GlobalDescription global; + global.addr = glob->addr; + global.name = QString(glob->name); + global.type = QString(gtype); + ret << global; + } + } + + return ret; +} + QVector CutterCore::getRegisterRefValues() { QVector result; @@ -3141,7 +3167,7 @@ QList CutterCore::getAllExports() } RzBinSymNames sn = {}; - rz_core_sym_name_init(&sn, symbol); + rz_core_sym_name_init(&sn, symbol, true); ExportDescription exportDescription; exportDescription.vaddr = rva(bf->o, symbol->paddr, symbol->vaddr, va); @@ -4021,6 +4047,100 @@ QList CutterCore::getXRefs(RVA addr, bool to, bool whole_functi return xrefList; } +void CutterCore::addGlobalVariable(RVA offset, QString name, QString typ) +{ + name = sanitizeStringForCommand(name); + typ = sanitizeStringForCommand(typ); + CORE_LOCK(); + char *errmsg = NULL; + RzType *globType = rz_type_parse_string_single(core->analysis->typedb->parser, + typ.toStdString().c_str(), &errmsg); + if (errmsg) { + qWarning() << tr("Error parsing type: \"") << typ << tr("\" message: ") << errmsg; + free(errmsg); + return; + } + if (!rz_analysis_var_global_create(core->analysis, name.toStdString().c_str(), + globType, offset)) { + return; + } + + emit globalVarsChanged(); +} + +void CutterCore::modifyGlobalVariable(RVA offset, QString name, QString typ) +{ + name = sanitizeStringForCommand(name); + typ = sanitizeStringForCommand(typ); + CORE_LOCK(); + RzAnalysisVarGlobal *glob = rz_analysis_var_global_get_byaddr_at(core->analysis, offset); + if (!glob) { + return; + } + // Compare if the name is not the same - also rename it + if (name.compare(glob->name)) { + rz_analysis_var_global_rename(core->analysis, glob->name, name.toStdString().c_str()); + } + char *errmsg = NULL; + RzType *globType = rz_type_parse_string_single(core->analysis->typedb->parser, + typ.toStdString().c_str(), &errmsg); + if (errmsg) { + qWarning() << tr("Error parsing type: \"") << typ << tr("\" message: ") << errmsg; + free(errmsg); + return; + } + rz_analysis_var_global_set_type(glob, globType); + + emit globalVarsChanged(); +} + +void CutterCore::delGlobalVariable(QString name) +{ + name = sanitizeStringForCommand(name); + CORE_LOCK(); + rz_analysis_var_global_delete_byname(core->analysis, name.toStdString().c_str()); + + emit globalVarsChanged(); +} + +void CutterCore::delGlobalVariable(RVA offset) +{ + CORE_LOCK(); + rz_analysis_var_global_delete_byaddr_at(core->analysis, offset); + + emit globalVarsChanged(); +} + +QString CutterCore::getGlobalVariableType(QString name) +{ + name = sanitizeStringForCommand(name); + CORE_LOCK(); + RzAnalysisVarGlobal *glob = rz_analysis_var_global_get_byname(core->analysis, + name.toStdString().c_str()); + if (!glob) { + return QString(""); + } + const char *gtype = rz_type_as_string(core->analysis->typedb, glob->type); + if (!gtype) { + return QString(""); + } + return QString(gtype); +} + +QString CutterCore::getGlobalVariableType(RVA offset) +{ + CORE_LOCK(); + RzAnalysisVarGlobal *glob = rz_analysis_var_global_get_byaddr_at(core->analysis, offset); + if (!glob) { + return QString(""); + } + const char *gtype = rz_type_as_string(core->analysis->typedb, glob->type); + if (!gtype) { + return QString(""); + } + return QString(gtype); +} + void CutterCore::addFlag(RVA offset, QString name, RVA size) { name = sanitizeStringForCommand(name); @@ -4526,9 +4646,9 @@ char *CutterCore::getTextualGraphAt(RzCoreGraphType type, RzCoreGraphFormat form RzGraph *graph = rz_core_graph(core, type, address); if (!graph) { if (address == RVA_INVALID) { - qWarning() << "Cannot get global graph"; + qWarning() << tr("Cannot get global graph"); } else { - qWarning() << "Cannot get graph at " << RzAddressString(address); + qWarning() << tr("Cannot get graph at ") << RzAddressString(address); } return nullptr; } diff --git a/src/core/Cutter.h b/src/core/Cutter.h index 14fbfde7c..398ed4c26 100644 --- a/src/core/Cutter.h +++ b/src/core/Cutter.h @@ -239,6 +239,14 @@ class CUTTER_EXPORT CutterCore : public QObject QString nearestFlag(RVA offset, RVA *flagOffsetOut); void triggerFlagsChanged(); + /* Global Variables */ + void addGlobalVariable(RVA offset, QString name, QString typ); + void delGlobalVariable(QString name); + void delGlobalVariable(RVA offset); + void modifyGlobalVariable(RVA offset, QString name, QString typ); + QString getGlobalVariableType(QString name); + QString getGlobalVariableType(RVA offset); + /* Edition functions */ PRzAnalysisBytes getRzAnalysisBytesSingle(RVA addr); QString getInstructionBytes(RVA addr); @@ -584,6 +592,7 @@ class CUTTER_EXPORT CutterCore : public QObject QList getAllExports(); QList getAllSymbols(); QList getAllHeaders(); + QList getAllGlobals(); QList getSignaturesDB(); QList getAllComments(const QString &filterType); QList getAllRelocs(); @@ -750,6 +759,7 @@ class CUTTER_EXPORT CutterCore : public QObject void functionRenamed(const RVA offset, const QString &new_name); void varsChanged(); + void globalVarsChanged(); void functionsChanged(); void flagsChanged(); void commentsChanged(RVA addr); diff --git a/src/core/CutterDescriptions.h b/src/core/CutterDescriptions.h index 31e40261f..bb04e12fa 100644 --- a/src/core/CutterDescriptions.h +++ b/src/core/CutterDescriptions.h @@ -360,6 +360,13 @@ struct VariableDescription QString value; }; +struct GlobalDescription +{ + RVA addr; + QString type; + QString name; +}; + struct RegisterRefValueDescription { QString name; @@ -407,6 +414,7 @@ Q_DECLARE_METATYPE(RelocDescription) Q_DECLARE_METATYPE(StringDescription) Q_DECLARE_METATYPE(FlagspaceDescription) Q_DECLARE_METATYPE(FlagDescription) +Q_DECLARE_METATYPE(GlobalDescription) Q_DECLARE_METATYPE(XrefDescription) Q_DECLARE_METATYPE(EntrypointDescription) Q_DECLARE_METATYPE(RzBinPluginDescription) diff --git a/src/core/MainWindow.cpp b/src/core/MainWindow.cpp index 3e259734c..e7364d438 100644 --- a/src/core/MainWindow.cpp +++ b/src/core/MainWindow.cpp @@ -31,6 +31,7 @@ #include "widgets/DisassemblerGraphView.h" #include "widgets/GraphView.h" #include "widgets/GraphWidget.h" +#include "widgets/GlobalsWidget.h" #include "widgets/OverviewWidget.h" #include "widgets/OverviewView.h" #include "widgets/FunctionsWidget.h" @@ -401,6 +402,7 @@ void MainWindow::initDocks() sectionsDock = new SectionsWidget(this), segmentsDock = new SegmentsWidget(this), symbolsDock = new SymbolsWidget(this), + globalsDock = new GlobalsWidget(this), vTablesDock = new VTablesWidget(this), flirtDock = new FlirtWidget(this), rzGraphDock = new RizinGraphWidget(this), @@ -905,6 +907,7 @@ void MainWindow::restoreDocks() tabifyDockWidget(dashboardDock, headersDock); tabifyDockWidget(dashboardDock, flirtDock); tabifyDockWidget(dashboardDock, symbolsDock); + tabifyDockWidget(dashboardDock, globalsDock); tabifyDockWidget(dashboardDock, classesDock); tabifyDockWidget(dashboardDock, resourcesDock); tabifyDockWidget(dashboardDock, vTablesDock); diff --git a/src/core/MainWindow.h b/src/core/MainWindow.h index 733e7ea5e..72a265b8d 100644 --- a/src/core/MainWindow.h +++ b/src/core/MainWindow.h @@ -26,6 +26,7 @@ class FunctionsWidget; class ImportsWidget; class ExportsWidget; class SymbolsWidget; +class GlobalsWidget; class RelocsWidget; class CommentsWidget; class StringsWidget; @@ -240,6 +241,7 @@ private slots: TypesWidget *typesDock = nullptr; SearchWidget *searchDock = nullptr; SymbolsWidget *symbolsDock = nullptr; + GlobalsWidget *globalsDock = nullptr; RelocsWidget *relocsDock = nullptr; CommentsWidget *commentsDock = nullptr; StringsWidget *stringsDock = nullptr; diff --git a/src/dialogs/GlobalVariableDialog.cpp b/src/dialogs/GlobalVariableDialog.cpp new file mode 100644 index 000000000..56ee3d89b --- /dev/null +++ b/src/dialogs/GlobalVariableDialog.cpp @@ -0,0 +1,64 @@ +#include "GlobalVariableDialog.h" +#include "ui_GlobalVariableDialog.h" + +#include +#include "core/Cutter.h" + +GlobalVariableDialog::GlobalVariableDialog(RVA offset, QWidget *parent) + : QDialog(parent), ui(new Ui::GlobalVariableDialog), offset(offset), globalVariableName("") +{ + // Setup UI + ui->setupUi(this); + setWindowFlags(windowFlags() & (~Qt::WindowContextHelpButtonHint)); + RzAnalysisVarGlobal *globalVariable = rz_analysis_var_global_get_byaddr_at(Core()->core()->analysis, offset); + if (globalVariable) { + globalVariableName = QString(globalVariable->name); + globalVariableOffset = globalVariable->addr; + } + + if (globalVariable) { + ui->nameEdit->setText(globalVariable->name); + QString globalVarType = Core()->getGlobalVariableType(globalVariable->name); + ui->typeEdit->setText(globalVarType); + ui->labelAction->setText(tr("Edit global variable at %1").arg(RzAddressString(offset))); + } else { + ui->labelAction->setText(tr("Add global variable at %1").arg(RzAddressString(offset))); + } + + // Connect slots + connect(ui->buttonBox, &QDialogButtonBox::accepted, this, &GlobalVariableDialog::buttonBoxAccepted); + connect(ui->buttonBox, &QDialogButtonBox::rejected, this, &GlobalVariableDialog::buttonBoxRejected); +} + +GlobalVariableDialog::~GlobalVariableDialog() {} + +void GlobalVariableDialog::buttonBoxAccepted() +{ + QString name = ui->nameEdit->text(); + QString typ = ui->typeEdit->text(); + + if (name.isEmpty()) { + if (globalVariableOffset != RVA_INVALID) { + // Empty name and global variable exists -> delete the global variable + Core()->delGlobalVariable(globalVariableOffset); + } else { + // GlobalVariable was not existing and we gave an empty name, do nothing + } + } else { + if (globalVariableOffset != RVA_INVALID) { + // Name provided and global variable exists -> rename the global variable + Core()->modifyGlobalVariable(globalVariableOffset, name, typ); + } else { + // Name provided and global variable does not exist -> create the global variable + Core()->addGlobalVariable(globalVariableOffset, name, typ); + } + } + close(); + this->setResult(QDialog::Accepted); +} + +void GlobalVariableDialog::buttonBoxRejected() +{ + close(); + this->setResult(QDialog::Rejected); +} diff --git a/src/dialogs/GlobalVariableDialog.h b/src/dialogs/GlobalVariableDialog.h new file mode 100644 index 000000000..b7e40e81f --- /dev/null +++ b/src/dialogs/GlobalVariableDialog.h @@ -0,0 +1,32 @@ +#ifndef GLOBALVARIABLEDIALOG_H +#define GLOBALVARIABLEDIALOG_H + +#include +#include +#include "core/CutterCommon.h" + +namespace Ui { +class GlobalVariableDialog; +} + +class GlobalVariableDialog : public QDialog +{ + Q_OBJECT + +public: + explicit GlobalVariableDialog(RVA offset, QWidget *parent = nullptr); + ~GlobalVariableDialog(); + +private slots: + void buttonBoxAccepted(); + void buttonBoxRejected(); + +private: + std::unique_ptr ui; + RVA offset; + QString globalVariableName; + ut64 globalVariableOffset; + QString typ; +}; + +#endif // GLOBALVARIABLEDIALOG_H diff --git a/src/dialogs/GlobalVariableDialog.ui b/src/dialogs/GlobalVariableDialog.ui new file mode 100644 index 000000000..c45ea8a51 --- /dev/null +++ b/src/dialogs/GlobalVariableDialog.ui @@ -0,0 +1,100 @@ + + + GlobalVariableDialog + + + + 0 + 0 + 452 + 121 + + + + Add Global Variable + + + + + + Add global variable at + + + + + + + 12 + + + + + + 75 + true + + + + Name: + + + + + + + false + + + + + + + + + + + 100 + 16777215 + + + + int + + + false + + + + + + + + + + + 75 + true + + + + Type: + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + diff --git a/src/menus/DisassemblyContextMenu.cpp b/src/menus/DisassemblyContextMenu.cpp index 300283ccc..df2f96c7c 100644 --- a/src/menus/DisassemblyContextMenu.cpp +++ b/src/menus/DisassemblyContextMenu.cpp @@ -3,6 +3,7 @@ #include "dialogs/EditInstructionDialog.h" #include "dialogs/CommentsDialog.h" #include "dialogs/FlagDialog.h" +#include "dialogs/GlobalVariableDialog.h" #include "dialogs/XrefsDialog.h" #include "dialogs/EditVariablesDialog.h" #include "dialogs/SetToDataDialog.h" @@ -34,6 +35,7 @@ DisassemblyContextMenu::DisassemblyContextMenu(QWidget *parent, MainWindow *main actionAnalyzeFunction(this), actionEditFunction(this), actionRename(this), + actionGlobalVar(this), actionSetFunctionVarTypes(this), actionXRefs(this), actionXRefsForVariables(this), @@ -83,10 +85,6 @@ DisassemblyContextMenu::DisassemblyContextMenu(QWidget *parent, MainWindow *main getCommentSequence()); addAction(&actionAddComment); - initAction(&actionRename, tr("Rename or add flag"), SLOT(on_actionRename_triggered()), - getRenameSequence()); - addAction(&actionRename); - initAction(&actionSetFunctionVarTypes, tr("Re-type Local Variables"), SLOT(on_actionSetFunctionVarTypes_triggered()), getRetypeSequence()); addAction(&actionSetFunctionVarTypes); @@ -112,6 +110,8 @@ DisassemblyContextMenu::DisassemblyContextMenu(QWidget *parent, MainWindow *main addSeparator(); + addAddAtMenu(); + addSetBaseMenu(); addSetBitsMenu(); @@ -166,6 +166,19 @@ QWidget *DisassemblyContextMenu::parentForDialog() return parentWidget(); } +void DisassemblyContextMenu::addAddAtMenu() +{ + setAsMenu = addMenu(tr("Add at...")); + + initAction(&actionRename, tr("Rename or add flag"), SLOT(on_actionRename_triggered()), + getRenameSequence()); + setAsMenu->addAction(&actionRename); + + initAction(&actionGlobalVar, tr("Change or add global variable"), SLOT(on_actionGlobalVar_triggered()), + getGlobalVarSequence()); + setAsMenu->addAction(&actionGlobalVar); +} + void DisassemblyContextMenu::addSetBaseMenu() { setBaseMenu = addMenu(tr("Set base of immediate value to..")); @@ -479,7 +492,12 @@ void DisassemblyContextMenu::setupRenaming() // Now, build the renaming menu and show it buildRenameMenu(tuh); + + auto name = RzAddressString(tuh->offset); + actionGlobalVar.setText(tr("Add or change global variable at %1 (used here)").arg(name)); + actionRename.setVisible(true); + actionGlobalVar.setVisible(true); } void DisassemblyContextMenu::aboutToShowSlot() @@ -655,6 +673,11 @@ QKeySequence DisassemblyContextMenu::getRenameSequence() const return { Qt::Key_N }; } +QKeySequence DisassemblyContextMenu::getGlobalVarSequence() const +{ + return { Qt::Key_G }; +} + QKeySequence DisassemblyContextMenu::getRetypeSequence() const { return { Qt::Key_Y }; @@ -868,6 +891,18 @@ void DisassemblyContextMenu::on_actionRename_triggered() } } +void DisassemblyContextMenu::on_actionGlobalVar_triggered() +{ + bool ok = false; + GlobalVariableDialog dialog(doRenameInfo.addr, parentForDialog()); + ok = dialog.exec(); + + if (ok) { + // Rebuild menu in case the user presses the rename shortcut directly before clicking + setupRenaming(); + } +} + void DisassemblyContextMenu::on_actionSetFunctionVarTypes_triggered() { RzAnalysisFunction *fcn = Core()->functionIn(offset); diff --git a/src/menus/DisassemblyContextMenu.h b/src/menus/DisassemblyContextMenu.h index 669990a69..66ddcd766 100644 --- a/src/menus/DisassemblyContextMenu.h +++ b/src/menus/DisassemblyContextMenu.h @@ -45,6 +45,7 @@ private slots: void on_actionAddComment_triggered(); void on_actionAnalyzeFunction_triggered(); void on_actionRename_triggered(); + void on_actionGlobalVar_triggered(); void on_actionSetFunctionVarTypes_triggered(); void on_actionXRefs_triggered(); void on_actionXRefsForVariables_triggered(); @@ -78,6 +79,7 @@ private slots: QKeySequence getCopySequence() const; QKeySequence getCommentSequence() const; QKeySequence getCopyAddressSequence() const; + QKeySequence getGlobalVarSequence() const; QKeySequence getSetToCodeSequence() const; QKeySequence getSetAsStringSequence() const; QKeySequence getSetAsStringAdvanced() const; @@ -118,6 +120,7 @@ private slots: QAction actionXRefs; QAction actionXRefsForVariables; QAction actionDisplayOptions; + QAction actionGlobalVar; QAction actionDeleteComment; QAction actionDeleteFlag; @@ -190,6 +193,7 @@ private slots: void addSetAsMenu(); void addSetToDataMenu(); void addEditMenu(); + void addAddAtMenu(); void addBreakpointMenu(); void addDebugMenu(); diff --git a/src/widgets/GlobalsWidget.cpp b/src/widgets/GlobalsWidget.cpp new file mode 100644 index 000000000..d11da0072 --- /dev/null +++ b/src/widgets/GlobalsWidget.cpp @@ -0,0 +1,147 @@ +#include "GlobalsWidget.h" +#include "ui_ListDockWidget.h" +#include "core/MainWindow.h" +#include "common/Helpers.h" + +#include + +GlobalsModel::GlobalsModel(QList *globals, QObject *parent) + : AddressableItemModel(parent), globals(globals) +{ +} + +int GlobalsModel::rowCount(const QModelIndex &) const +{ + return globals->count(); +} + +int GlobalsModel::columnCount(const QModelIndex &) const +{ + return GlobalsModel::ColumnCount; +} + +QVariant GlobalsModel::data(const QModelIndex &index, int role) const +{ + if (index.row() >= globals->count()) { + return QVariant(); + } + + const GlobalDescription &global = globals->at(index.row()); + + switch (role) { + case Qt::DisplayRole: + switch (index.column()) { + case GlobalsModel::AddressColumn: + return RzAddressString(global.addr); + case GlobalsModel::TypeColumn: + return QString(global.type).trimmed(); + case GlobalsModel::NameColumn: + return global.name; + case GlobalsModel::CommentColumn: + return Core()->getCommentAt(global.addr); + default: + return QVariant(); + } + case GlobalsModel::GlobalDescriptionRole: + return QVariant::fromValue(global); + default: + return QVariant(); + } +} + +QVariant GlobalsModel::headerData(int section, Qt::Orientation, int role) const +{ + switch (role) { + case Qt::DisplayRole: + switch (section) { + case GlobalsModel::AddressColumn: + return tr("Address"); + case GlobalsModel::TypeColumn: + return tr("Type"); + case GlobalsModel::NameColumn: + return tr("Name"); + case GlobalsModel::CommentColumn: + return tr("Comment"); + default: + return QVariant(); + } + default: + return QVariant(); + } +} + +RVA GlobalsModel::address(const QModelIndex &index) const +{ + const GlobalDescription &global = globals->at(index.row()); + return global.addr; +} + +QString GlobalsModel::name(const QModelIndex &index) const +{ + const GlobalDescription &global = globals->at(index.row()); + return global.name; +} + +GlobalsProxyModel::GlobalsProxyModel(GlobalsModel *sourceModel, QObject *parent) + : AddressableFilterProxyModel(sourceModel, parent) +{ + setFilterCaseSensitivity(Qt::CaseInsensitive); + setSortCaseSensitivity(Qt::CaseInsensitive); +} + +bool GlobalsProxyModel::filterAcceptsRow(int row, const QModelIndex &parent) const +{ + QModelIndex index = sourceModel()->index(row, 0, parent); + auto global = index.data(GlobalsModel::GlobalDescriptionRole).value(); + + return qhelpers::filterStringContains(global.name, this); +} + +bool GlobalsProxyModel::lessThan(const QModelIndex &left, const QModelIndex &right) const +{ + auto leftGlobal = left.data(GlobalsModel::GlobalDescriptionRole).value(); + auto rightGlobal = right.data(GlobalsModel::GlobalDescriptionRole).value(); + + switch (left.column()) { + case GlobalsModel::AddressColumn: + return leftGlobal.addr < rightGlobal.addr; + case GlobalsModel::TypeColumn: + return leftGlobal.type < rightGlobal.type; + case GlobalsModel::NameColumn: + return leftGlobal.name < rightGlobal.name; + case GlobalsModel::CommentColumn: + return Core()->getCommentAt(leftGlobal.addr) < Core()->getCommentAt(rightGlobal.addr); + default: + break; + } + + return false; +} + +GlobalsWidget::GlobalsWidget(MainWindow *main) : ListDockWidget(main) +{ + setWindowTitle(tr("Globals")); + setObjectName("GlobalsWidget"); + + globalsModel = new GlobalsModel(&globals, this); + globalsProxyModel = new GlobalsProxyModel(globalsModel, this); + setModels(globalsProxyModel); + ui->treeView->sortByColumn(GlobalsModel::AddressColumn, Qt::AscendingOrder); + + connect(Core(), &CutterCore::globalVarsChanged, this, &GlobalsWidget::refreshGlobals); + connect(Core(), &CutterCore::codeRebased, this, &GlobalsWidget::refreshGlobals); + connect(Core(), &CutterCore::refreshAll, this, &GlobalsWidget::refreshGlobals); + connect(Core(), &CutterCore::commentsChanged, this, + [this]() { qhelpers::emitColumnChanged(globalsModel, GlobalsModel::CommentColumn); }); +} + +GlobalsWidget::~GlobalsWidget() {} + +void GlobalsWidget::refreshGlobals() +{ + globalsModel->beginResetModel(); + globals = Core()->getAllGlobals(); + globalsModel->endResetModel(); + + qhelpers::adjustColumns(ui->treeView, GlobalsModel::ColumnCount, 0); +} diff --git a/src/widgets/GlobalsWidget.h b/src/widgets/GlobalsWidget.h new file mode 100644 index 000000000..7c0c61f7f --- /dev/null +++ b/src/widgets/GlobalsWidget.h @@ -0,0 +1,71 @@ +#ifndef GLOBALSWIDGET_H +#define GLOBALSWIDGET_H + +#include +#include +#include + +#include "core/Cutter.h" +#include "CutterDockWidget.h" +#include "widgets/ListDockWidget.h" + +class MainWindow; +class QTreeWidgetItem; +class GlobalsWidget; + +class GlobalsModel : public AddressableItemModel +{ + Q_OBJECT + + friend GlobalsWidget; + +private: + QList *globals; + +public: + enum Column { AddressColumn = 0, TypeColumn, NameColumn, CommentColumn, ColumnCount }; + enum Role { GlobalDescriptionRole = Qt::UserRole }; + + GlobalsModel(QList *exports, QObject *parent = nullptr); + + int rowCount(const QModelIndex &parent = QModelIndex()) const override; + int columnCount(const QModelIndex &parent = QModelIndex()) const override; + + QVariant data(const QModelIndex &index, int role) const override; + QVariant headerData(int section, Qt::Orientation orientation, + int role = Qt::DisplayRole) const override; + + RVA address(const QModelIndex &index) const override; + QString name(const QModelIndex &index) const override; +}; + +class GlobalsProxyModel : public AddressableFilterProxyModel +{ + Q_OBJECT + +public: + GlobalsProxyModel(GlobalsModel *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 GlobalsWidget : public ListDockWidget +{ + Q_OBJECT + +public: + explicit GlobalsWidget(MainWindow *main); + ~GlobalsWidget(); + +private slots: + void refreshGlobals(); + +private: + QList globals; + GlobalsModel *globalsModel; + GlobalsProxyModel *globalsProxyModel; +}; + +#endif // GLOBALSWIDGET_H