mirror of
https://github.com/rizinorg/cutter.git
synced 2024-12-18 10:56:11 +00:00
Add virtual tables panel (#340)
This commit is contained in:
parent
a9f669e27e
commit
c1132aba0c
@ -65,6 +65,7 @@
|
||||
#include "dialogs/SaveProjectDialog.h"
|
||||
#include "widgets/ClassesWidget.h"
|
||||
#include "widgets/ResourcesWidget.h"
|
||||
#include "widgets/VTablesWidget.h"
|
||||
|
||||
// graphics
|
||||
#include <QGraphicsEllipseItem>
|
||||
@ -223,6 +224,7 @@ void MainWindow::initUI()
|
||||
ADD_DOCK(SdbDock, sdbDock, ui->actionSDBBrowser);
|
||||
ADD_DOCK(ClassesWidget, classesDock, ui->actionClasses);
|
||||
ADD_DOCK(ResourcesWidget, resourcesDock, ui->actionResources);
|
||||
ADD_DOCK(VTablesWidget, vTablesDock, ui->actionVTables);
|
||||
|
||||
#undef ADD_DOCK
|
||||
|
||||
@ -545,6 +547,7 @@ void MainWindow::restoreDocks()
|
||||
tabifyDockWidget(dashboardDock, notepadDock);
|
||||
tabifyDockWidget(dashboardDock, classesDock);
|
||||
tabifyDockWidget(dashboardDock, resourcesDock);
|
||||
tabifyDockWidget(dashboardDock, vTablesDock);
|
||||
|
||||
updateDockActionsChecked();
|
||||
}
|
||||
|
@ -39,6 +39,7 @@ class EntrypointWidget;
|
||||
class DisassemblerGraphView;
|
||||
class ClassesWidget;
|
||||
class ResourcesWidget;
|
||||
class VTablesWidget;
|
||||
|
||||
class QDockWidget;
|
||||
|
||||
@ -186,6 +187,7 @@ private:
|
||||
ConsoleWidget *consoleDock = nullptr;
|
||||
ClassesWidget *classesDock = nullptr;
|
||||
ResourcesWidget *resourcesDock = nullptr;
|
||||
VTablesWidget *vTablesDock = nullptr;
|
||||
DisassemblerGraphView *graphView = nullptr;
|
||||
QDockWidget *asmDock = nullptr;
|
||||
QDockWidget *calcDock = nullptr;
|
||||
|
@ -250,6 +250,7 @@ border-top: 0px;
|
||||
<addaction name="actionSymbols"/>
|
||||
<addaction name="actionSDBBrowser"/>
|
||||
<addaction name="actionResources"/>
|
||||
<addaction name="actionVTables"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="actionRelocs"/>
|
||||
<addaction name="actionStrings"/>
|
||||
@ -1050,6 +1051,17 @@ QToolButton:pressed {
|
||||
<string>Resources</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionVTables">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>VTables</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Show/Hide VTables panel</string>
|
||||
</property>
|
||||
</action>
|
||||
</widget>
|
||||
<layoutdefault spacing="6" margin="11"/>
|
||||
<resources>
|
||||
|
@ -1252,6 +1252,36 @@ QList<ResourcesDescription> CutterCore::getAllResources()
|
||||
return ret;
|
||||
}
|
||||
|
||||
QList<VTableDescription> CutterCore::getAllVTables()
|
||||
{
|
||||
CORE_LOCK();
|
||||
QList<VTableDescription> ret;
|
||||
|
||||
QJsonArray vTablesArray = cmdj("avj").array();
|
||||
for(QJsonValueRef vTableValue : vTablesArray)
|
||||
{
|
||||
QJsonObject vTableObject = vTableValue.toObject();
|
||||
|
||||
VTableDescription res;
|
||||
res.addr = vTableObject["offset"].toVariant().toULongLong();
|
||||
QJsonArray methodArray = vTableObject["methods"].toArray();
|
||||
|
||||
for(QJsonValueRef methodValue : methodArray)
|
||||
{
|
||||
QJsonObject methodObject = methodValue.toObject();
|
||||
|
||||
ClassMethodDescription method;
|
||||
method.addr = methodObject["offset"].toVariant().toULongLong();
|
||||
method.name = methodObject["name"].toString();
|
||||
|
||||
res.methods << method;
|
||||
}
|
||||
|
||||
ret << res;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
QList<XrefDescription> CutterCore::getXRefs(RVA addr, bool to, bool whole_function, const QString &filterType)
|
||||
{
|
||||
QList<XrefDescription> ret = QList<XrefDescription>();
|
||||
|
@ -208,6 +208,12 @@ struct ResourcesDescription
|
||||
QString lang;
|
||||
};
|
||||
|
||||
struct VTableDescription
|
||||
{
|
||||
RVA addr;
|
||||
QList<ClassMethodDescription> methods;
|
||||
};
|
||||
|
||||
Q_DECLARE_METATYPE(FunctionDescription)
|
||||
Q_DECLARE_METATYPE(ImportDescription)
|
||||
Q_DECLARE_METATYPE(ExportDescription)
|
||||
@ -227,6 +233,7 @@ Q_DECLARE_METATYPE(const ClassDescription *)
|
||||
Q_DECLARE_METATYPE(const ClassMethodDescription *)
|
||||
Q_DECLARE_METATYPE(const ClassFieldDescription *)
|
||||
Q_DECLARE_METATYPE(ResourcesDescription)
|
||||
Q_DECLARE_METATYPE(VTableDescription)
|
||||
|
||||
class CutterCore: public QObject
|
||||
{
|
||||
@ -360,6 +367,7 @@ public:
|
||||
QList<EntrypointDescription> getAllEntrypoint();
|
||||
QList<ClassDescription> getAllClasses();
|
||||
QList<ResourcesDescription> getAllResources();
|
||||
QList<VTableDescription> getAllVTables();
|
||||
|
||||
QList<XrefDescription> getXRefs(RVA addr, bool to, bool whole_function, const QString &filterType = QString::null);
|
||||
|
||||
|
@ -90,6 +90,7 @@ SOURCES += \
|
||||
widgets/QuickFilterView.cpp \
|
||||
widgets/ClassesWidget.cpp \
|
||||
widgets/ResourcesWidget.cpp \
|
||||
widgets/VTablesWidget.cpp \
|
||||
CutterApplication.cpp
|
||||
|
||||
HEADERS += \
|
||||
@ -150,7 +151,8 @@ HEADERS += \
|
||||
widgets/QuickFilterView.h \
|
||||
widgets/ClassesWidget.h \
|
||||
widgets/ResourcesWidget.h \
|
||||
CutterApplication.h
|
||||
CutterApplication.h \
|
||||
widgets/VTablesWidget.h
|
||||
|
||||
FORMS += \
|
||||
dialogs/AboutDialog.ui \
|
||||
@ -186,7 +188,8 @@ FORMS += \
|
||||
dialogs/preferences/GraphOptionsWidget.ui \
|
||||
widgets/QuickFilterView.ui \
|
||||
widgets/PseudocodeWidget.ui \
|
||||
widgets/ClassesWidget.ui
|
||||
widgets/ClassesWidget.ui \
|
||||
widgets/VTablesWidget.ui
|
||||
|
||||
RESOURCES += \
|
||||
resources.qrc \
|
||||
|
179
src/widgets/VTablesWidget.cpp
Normal file
179
src/widgets/VTablesWidget.cpp
Normal file
@ -0,0 +1,179 @@
|
||||
#include <QShortcut>
|
||||
|
||||
#include "VTablesWidget.h"
|
||||
#include "ui_VTablesWidget.h"
|
||||
|
||||
VTableModel::VTableModel(QList<VTableDescription> *vtables, QObject *parent)
|
||||
: QAbstractItemModel(parent),
|
||||
vtables(vtables)
|
||||
{
|
||||
}
|
||||
|
||||
QModelIndex VTableModel::index(int row, int column, const QModelIndex &parent) const
|
||||
{
|
||||
return createIndex(row, column, (quintptr) parent.isValid()? parent.row() : -1);
|
||||
}
|
||||
|
||||
QModelIndex VTableModel::parent(const QModelIndex &index) const
|
||||
{
|
||||
return index.isValid() && index.internalId() != (quintptr) -1 ?
|
||||
this->index(index.internalId(), index.column()) : QModelIndex();
|
||||
}
|
||||
|
||||
int VTableModel::rowCount(const QModelIndex &parent) const
|
||||
{
|
||||
return parent.isValid()? (parent.parent().isValid()? 0 : vtables->at(parent.row()).methods.count()) : vtables->count();
|
||||
}
|
||||
|
||||
int VTableModel::columnCount(const QModelIndex &) const
|
||||
{
|
||||
return Columns::COUNT;
|
||||
}
|
||||
|
||||
QVariant VTableModel::data(const QModelIndex &index, int role) const
|
||||
{
|
||||
QModelIndex parent = index.parent();
|
||||
if(parent.isValid())
|
||||
{
|
||||
const ClassMethodDescription &res = vtables->at(parent.row()).methods.at(index.row());
|
||||
switch (role)
|
||||
{
|
||||
case Qt::DisplayRole:
|
||||
switch(index.column())
|
||||
{
|
||||
case NAME:
|
||||
return res.name;
|
||||
case ADDRESS:
|
||||
return RAddressString(res.addr);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
switch(role)
|
||||
{
|
||||
case Qt::DisplayRole:
|
||||
switch(index.column())
|
||||
{
|
||||
case NAME:
|
||||
return tr("VTable ") + QString::number(index.row() + 1);
|
||||
case ADDRESS:
|
||||
return RAddressString(vtables->at(index.row()).addr);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
QVariant VTableModel::headerData(int section, Qt::Orientation, int role) const
|
||||
{
|
||||
switch(role)
|
||||
{
|
||||
case Qt::DisplayRole:
|
||||
switch(section)
|
||||
{
|
||||
case NAME:
|
||||
return tr("Name");
|
||||
case ADDRESS:
|
||||
return tr("Address");
|
||||
default:
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
void VTableModel::beginReload()
|
||||
{
|
||||
beginResetModel();
|
||||
}
|
||||
|
||||
void VTableModel::endReload()
|
||||
{
|
||||
endResetModel();
|
||||
}
|
||||
|
||||
VTableSortFilterProxyModel::VTableSortFilterProxyModel(VTableModel *model, QObject* parent)
|
||||
: QSortFilterProxyModel(parent)
|
||||
{
|
||||
setSourceModel(model);
|
||||
setFilterCaseSensitivity(Qt::CaseInsensitive);
|
||||
setSortCaseSensitivity(Qt::CaseInsensitive);
|
||||
setFilterKeyColumn(VTableModel::NAME);
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)
|
||||
setRecursiveFilteringEnabled(true);
|
||||
#endif
|
||||
}
|
||||
|
||||
bool VTableSortFilterProxyModel::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const
|
||||
{
|
||||
if(QSortFilterProxyModel::filterAcceptsRow(source_row, source_parent))
|
||||
return true;
|
||||
if(source_parent.isValid())
|
||||
return QSortFilterProxyModel::filterAcceptsRow(source_parent.row(), QModelIndex());
|
||||
#if QT_VERSION < QT_VERSION_CHECK(5, 10, 0)
|
||||
else
|
||||
{
|
||||
QAbstractItemModel* const model = sourceModel();
|
||||
const QModelIndex source = model->index(source_row, 0, QModelIndex());
|
||||
const int rows = model->rowCount(source);
|
||||
for(int i = 0; i < rows; ++i)
|
||||
if(QSortFilterProxyModel::filterAcceptsRow(i, source))
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
VTablesWidget::VTablesWidget(QWidget *parent) :
|
||||
QDockWidget(parent),
|
||||
ui(new Ui::VTablesWidget)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
|
||||
model = new VTableModel(&vtables, this);
|
||||
proxy = new VTableSortFilterProxyModel(model);
|
||||
|
||||
ui->vTableTreeView->setModel(proxy);
|
||||
ui->vTableTreeView->sortByColumn(VTableModel::ADDRESS, Qt::AscendingOrder);
|
||||
|
||||
// Esc to clear the filter entry
|
||||
QShortcut *clear_shortcut = new QShortcut(QKeySequence(Qt::Key_Escape), this);
|
||||
connect(clear_shortcut, &QShortcut::activated, ui->quickFilterView, &QuickFilterView::clearFilter);
|
||||
|
||||
// Ctrl-F to show/hide the filter entry
|
||||
QShortcut *search_shortcut = new QShortcut(QKeySequence::Find, this);
|
||||
connect(search_shortcut, &QShortcut::activated, ui->quickFilterView, &QuickFilterView::showFilter);
|
||||
search_shortcut->setContext(Qt::WidgetWithChildrenShortcut);
|
||||
|
||||
connect(ui->quickFilterView, SIGNAL(filterTextChanged(const QString&)), proxy, SLOT(setFilterWildcard(const QString &)));
|
||||
connect(ui->quickFilterView, SIGNAL(filterClosed()), ui->vTableTreeView, SLOT(setFocus()));
|
||||
|
||||
connect(Core(), SIGNAL(refreshAll()), this, SLOT(refreshVTables()));
|
||||
}
|
||||
|
||||
VTablesWidget::~VTablesWidget()
|
||||
{
|
||||
}
|
||||
|
||||
void VTablesWidget::refreshVTables()
|
||||
{
|
||||
model->beginReload();
|
||||
vtables = CutterCore::getInstance()->getAllVTables();
|
||||
model->endReload();
|
||||
|
||||
ui->vTableTreeView->resizeColumnToContents(0);
|
||||
ui->vTableTreeView->resizeColumnToContents(1);
|
||||
ui->vTableTreeView->resizeColumnToContents(2);
|
||||
|
||||
ui->vTableTreeView->setColumnWidth(0, 200);
|
||||
}
|
70
src/widgets/VTablesWidget.h
Normal file
70
src/widgets/VTablesWidget.h
Normal file
@ -0,0 +1,70 @@
|
||||
#ifndef VTABLESWIDGET_H
|
||||
#define VTABLESWIDGET_H
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include <QTreeView>
|
||||
#include <QSortFilterProxyModel>
|
||||
#include <QDockWidget>
|
||||
|
||||
#include "cutter.h"
|
||||
|
||||
namespace Ui
|
||||
{
|
||||
class VTablesWidget;
|
||||
}
|
||||
|
||||
class VTableModel : public QAbstractItemModel
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private:
|
||||
QList<VTableDescription> *vtables;
|
||||
|
||||
public:
|
||||
enum Columns { NAME = 0, ADDRESS, COUNT };
|
||||
|
||||
VTableModel(QList<VTableDescription> *vtables, QObject* parent = nullptr);
|
||||
|
||||
QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const;
|
||||
QModelIndex parent(const QModelIndex &index) const;
|
||||
|
||||
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 beginReload();
|
||||
void endReload();
|
||||
};
|
||||
|
||||
class VTableSortFilterProxyModel : public QSortFilterProxyModel
|
||||
{
|
||||
public:
|
||||
VTableSortFilterProxyModel(VTableModel* model, QObject *parent = nullptr);
|
||||
|
||||
protected:
|
||||
bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const;
|
||||
};
|
||||
|
||||
class VTablesWidget : public QDockWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit VTablesWidget(QWidget *parent = 0);
|
||||
~VTablesWidget();
|
||||
|
||||
private slots:
|
||||
void refreshVTables();
|
||||
|
||||
private:
|
||||
std::unique_ptr<Ui::VTablesWidget> ui;
|
||||
|
||||
VTableModel *model;
|
||||
QSortFilterProxyModel *proxy;
|
||||
QList<VTableDescription> vtables;
|
||||
};
|
||||
|
||||
#endif // VTABLESWIDGET_H
|
93
src/widgets/VTablesWidget.ui
Normal file
93
src/widgets/VTablesWidget.ui
Normal file
@ -0,0 +1,93 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>VTablesWidget</class>
|
||||
<widget class="QDockWidget" name="VTablesWidget">
|
||||
<property name="styleSheet">
|
||||
<string notr="true"/>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string notr="true">&VTable</string>
|
||||
</property>
|
||||
<widget class="QWidget" name="dockWidgetContents">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||
<horstretch>1</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>200</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<property name="spacing">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QTreeView" name="vTableTreeView">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Ignored" vsizetype="Expanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="contextMenuPolicy">
|
||||
<enum>Qt::CustomContextMenu</enum>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true">
|
||||
QTreeView::item
|
||||
{
|
||||
padding-top: 1px;
|
||||
padding-bottom: 1px;
|
||||
}
|
||||
</string>
|
||||
</property>
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::NoFrame</enum>
|
||||
</property>
|
||||
<property name="lineWidth">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="sizeAdjustPolicy">
|
||||
<enum>QAbstractScrollArea::AdjustToContents</enum>
|
||||
</property>
|
||||
<property name="indentation">
|
||||
<number>8</number>
|
||||
</property>
|
||||
<property name="sortingEnabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QuickFilterView" name="quickFilterView" native="true"/>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>QuickFilterView</class>
|
||||
<extends>QWidget</extends>
|
||||
<header>widgets/QuickFilterView.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
Loading…
Reference in New Issue
Block a user