2017-12-23 16:42:42 +00:00
|
|
|
|
|
|
|
#include <QList>
|
|
|
|
|
|
|
|
#include "ClassesWidget.h"
|
2018-03-16 21:46:57 +00:00
|
|
|
#include "MainWindow.h"
|
2017-12-23 16:42:42 +00:00
|
|
|
#include "ui_ClassesWidget.h"
|
2018-10-17 07:55:53 +00:00
|
|
|
#include "common/Helpers.h"
|
2018-08-17 11:27:07 +00:00
|
|
|
#include "dialogs/EditMethodDialog.h"
|
2017-12-23 16:42:42 +00:00
|
|
|
|
2018-08-17 11:27:07 +00:00
|
|
|
#include <QMenu>
|
|
|
|
|
|
|
|
|
|
|
|
QVariant ClassesModel::headerData(int section, Qt::Orientation, int role) const
|
|
|
|
{
|
|
|
|
switch (role) {
|
|
|
|
case Qt::DisplayRole:
|
|
|
|
switch (section) {
|
|
|
|
case NAME:
|
|
|
|
return tr("Name");
|
|
|
|
case TYPE:
|
|
|
|
return tr("Type");
|
|
|
|
case OFFSET:
|
|
|
|
return tr("Offset");
|
|
|
|
case VTABLE:
|
|
|
|
return tr("VTable");
|
|
|
|
default:
|
|
|
|
return QVariant();
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
return QVariant();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
BinClassesModel::BinClassesModel(QObject *parent)
|
|
|
|
: ClassesModel(parent)
|
2017-12-23 16:42:42 +00:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2018-08-17 11:27:07 +00:00
|
|
|
void BinClassesModel::setClasses(const QList<BinClassDescription> &classes)
|
|
|
|
{
|
|
|
|
beginResetModel();
|
|
|
|
this->classes = classes;
|
|
|
|
endResetModel();
|
|
|
|
}
|
2017-12-23 16:42:42 +00:00
|
|
|
|
2018-08-17 11:27:07 +00:00
|
|
|
|
|
|
|
QModelIndex BinClassesModel::index(int row, int column, const QModelIndex &parent) const
|
2017-12-23 16:42:42 +00:00
|
|
|
{
|
2019-02-01 14:51:29 +00:00
|
|
|
if (!parent.isValid()) {
|
2017-12-23 16:42:42 +00:00
|
|
|
return createIndex(row, column, (quintptr)0); // root function nodes have id = 0
|
2019-02-01 14:51:29 +00:00
|
|
|
}
|
2017-12-23 16:42:42 +00:00
|
|
|
|
|
|
|
return createIndex(row, column, (quintptr)parent.row() + 1); // sub-nodes have id = class index + 1
|
|
|
|
}
|
|
|
|
|
2018-08-17 11:27:07 +00:00
|
|
|
QModelIndex BinClassesModel::parent(const QModelIndex &index) const
|
2017-12-23 16:42:42 +00:00
|
|
|
{
|
2019-02-01 14:51:29 +00:00
|
|
|
if (!index.isValid()) {
|
|
|
|
return {};
|
|
|
|
}
|
2017-12-23 16:42:42 +00:00
|
|
|
|
2019-02-01 14:51:29 +00:00
|
|
|
if (index.internalId() == 0) { // root function node
|
|
|
|
return {};
|
|
|
|
} else { // sub-node
|
2017-12-23 16:42:42 +00:00
|
|
|
return this->index((int)(index.internalId() - 1), 0);
|
2019-02-01 14:51:29 +00:00
|
|
|
}
|
2017-12-23 16:42:42 +00:00
|
|
|
}
|
|
|
|
|
2018-08-17 11:27:07 +00:00
|
|
|
int BinClassesModel::rowCount(const QModelIndex &parent) const
|
2017-12-23 16:42:42 +00:00
|
|
|
{
|
2018-03-21 20:32:32 +00:00
|
|
|
if (!parent.isValid()) { // root
|
2018-08-17 11:27:07 +00:00
|
|
|
return classes.count();
|
2017-12-23 16:42:42 +00:00
|
|
|
}
|
|
|
|
|
2018-03-21 20:32:32 +00:00
|
|
|
if (parent.internalId() == 0) { // methods/fields
|
2018-08-17 11:27:07 +00:00
|
|
|
const BinClassDescription *cls = &classes.at(parent.row());
|
|
|
|
return cls->baseClasses.length() + cls->methods.length() + cls->fields.length();
|
2017-12-23 16:42:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0; // below methods/fields
|
|
|
|
}
|
|
|
|
|
2018-08-17 11:27:07 +00:00
|
|
|
int BinClassesModel::columnCount(const QModelIndex &) const
|
2017-12-23 16:42:42 +00:00
|
|
|
{
|
|
|
|
return Columns::COUNT;
|
|
|
|
}
|
|
|
|
|
2018-08-17 11:27:07 +00:00
|
|
|
QVariant BinClassesModel::data(const QModelIndex &index, int role) const
|
2017-12-23 16:42:42 +00:00
|
|
|
{
|
2018-08-17 11:27:07 +00:00
|
|
|
const BinClassDescription *cls;
|
2019-01-31 16:45:58 +00:00
|
|
|
const BinClassMethodDescription *meth = nullptr;
|
|
|
|
const BinClassFieldDescription *field = nullptr;
|
|
|
|
const BinClassBaseClassDescription *base = nullptr;
|
2018-03-21 20:32:32 +00:00
|
|
|
if (index.internalId() == 0) { // class row
|
2018-08-17 11:27:07 +00:00
|
|
|
if (index.row() >= classes.count()) {
|
2017-12-23 16:42:42 +00:00
|
|
|
return QVariant();
|
|
|
|
}
|
|
|
|
|
2018-08-17 11:27:07 +00:00
|
|
|
cls = &classes.at(index.row());
|
|
|
|
} else { // method/field/base row
|
|
|
|
cls = &classes.at(static_cast<int>(index.internalId() - 1));
|
2017-12-23 16:42:42 +00:00
|
|
|
|
2018-08-17 11:27:07 +00:00
|
|
|
if (index.row() >= cls->baseClasses.length() + cls->methods.length() + cls->fields.length()) {
|
2017-12-23 16:42:42 +00:00
|
|
|
return QVariant();
|
|
|
|
}
|
|
|
|
|
2018-08-17 11:27:07 +00:00
|
|
|
if (index.row() < cls->baseClasses.length()) {
|
|
|
|
base = &cls->baseClasses[index.row()];
|
|
|
|
} else if (index.row() - cls->baseClasses.length() < cls->methods.length()) {
|
|
|
|
meth = &cls->methods[index.row() - cls->baseClasses.length()];
|
2018-03-21 20:32:32 +00:00
|
|
|
} else {
|
2018-08-17 11:27:07 +00:00
|
|
|
field = &cls->fields[index.row() - cls->baseClasses.length() - cls->methods.length()];
|
2017-12-23 16:42:42 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-03-21 20:32:32 +00:00
|
|
|
if (meth) {
|
|
|
|
switch (role) {
|
|
|
|
case Qt::DisplayRole:
|
|
|
|
switch (index.column()) {
|
|
|
|
case NAME:
|
2017-12-23 16:42:42 +00:00
|
|
|
return meth->name;
|
2018-03-21 20:32:32 +00:00
|
|
|
case TYPE:
|
|
|
|
return tr("method");
|
|
|
|
case OFFSET:
|
2018-08-17 11:27:07 +00:00
|
|
|
return meth->addr == RVA_INVALID ? QString() : RAddressString(meth->addr);
|
|
|
|
case VTABLE:
|
|
|
|
return meth->vtableOffset < 0 ? QString() : QString("+%1").arg(meth->vtableOffset);
|
2017-12-23 16:42:42 +00:00
|
|
|
default:
|
|
|
|
return QVariant();
|
2018-03-21 20:32:32 +00:00
|
|
|
}
|
|
|
|
case OffsetRole:
|
|
|
|
return QVariant::fromValue(meth->addr);
|
2018-08-17 11:27:07 +00:00
|
|
|
case VTableOffsetRole:
|
|
|
|
return QVariant::fromValue(index.parent().data(VTableOffsetRole).toULongLong() + meth->vtableOffset);
|
2018-03-21 20:32:32 +00:00
|
|
|
case NameRole:
|
|
|
|
return meth->name;
|
|
|
|
case TypeRole:
|
2019-02-01 14:51:29 +00:00
|
|
|
return QVariant::fromValue(RowType::Method);
|
2018-08-17 11:27:07 +00:00
|
|
|
case DataRole:
|
|
|
|
return QVariant::fromValue(*meth);
|
2018-03-21 20:32:32 +00:00
|
|
|
default:
|
|
|
|
return QVariant();
|
2017-12-23 16:42:42 +00:00
|
|
|
}
|
2018-03-21 20:32:32 +00:00
|
|
|
} else if (field) {
|
|
|
|
switch (role) {
|
|
|
|
case Qt::DisplayRole:
|
|
|
|
switch (index.column()) {
|
|
|
|
case NAME:
|
2017-12-23 16:42:42 +00:00
|
|
|
return field->name;
|
2018-03-21 20:32:32 +00:00
|
|
|
case TYPE:
|
|
|
|
return tr("field");
|
|
|
|
case OFFSET:
|
2018-08-17 11:27:07 +00:00
|
|
|
return field->addr == RVA_INVALID ? QString() : RAddressString(field->addr);
|
2017-12-23 16:42:42 +00:00
|
|
|
default:
|
|
|
|
return QVariant();
|
2018-03-21 20:32:32 +00:00
|
|
|
}
|
|
|
|
case OffsetRole:
|
|
|
|
return QVariant::fromValue(field->addr);
|
|
|
|
case NameRole:
|
|
|
|
return field->name;
|
|
|
|
case TypeRole:
|
2019-02-01 14:51:29 +00:00
|
|
|
return QVariant::fromValue(RowType::Field);
|
2018-03-21 20:32:32 +00:00
|
|
|
default:
|
|
|
|
return QVariant();
|
2017-12-23 16:42:42 +00:00
|
|
|
}
|
2018-08-17 11:27:07 +00:00
|
|
|
} else if (base) {
|
|
|
|
switch (role) {
|
|
|
|
case Qt::DisplayRole:
|
|
|
|
switch (index.column()) {
|
|
|
|
case NAME:
|
|
|
|
return base->name;
|
|
|
|
case TYPE:
|
|
|
|
return tr("base class");
|
|
|
|
case OFFSET:
|
|
|
|
return QString("+%1").arg(base->offset);
|
|
|
|
default:
|
|
|
|
return QVariant();
|
|
|
|
}
|
|
|
|
case NameRole:
|
|
|
|
return base->name;
|
|
|
|
case TypeRole:
|
2019-02-01 14:51:29 +00:00
|
|
|
return QVariant::fromValue(RowType::Base);
|
2018-08-17 11:27:07 +00:00
|
|
|
default:
|
|
|
|
return QVariant();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
2018-03-21 20:32:32 +00:00
|
|
|
switch (role) {
|
|
|
|
case Qt::DisplayRole:
|
|
|
|
switch (index.column()) {
|
|
|
|
case NAME:
|
2017-12-23 16:42:42 +00:00
|
|
|
return cls->name;
|
2018-03-21 20:32:32 +00:00
|
|
|
case TYPE:
|
|
|
|
return tr("class");
|
|
|
|
case OFFSET:
|
2018-08-17 11:27:07 +00:00
|
|
|
return cls->addr == RVA_INVALID ? QString() : RAddressString(cls->addr);
|
|
|
|
case VTABLE:
|
|
|
|
return cls->vtableAddr == RVA_INVALID ? QString() : RAddressString(cls->vtableAddr);
|
2017-12-23 16:42:42 +00:00
|
|
|
default:
|
|
|
|
return QVariant();
|
2018-03-21 20:32:32 +00:00
|
|
|
}
|
|
|
|
case OffsetRole:
|
|
|
|
return QVariant::fromValue(cls->addr);
|
2018-08-17 11:27:07 +00:00
|
|
|
case VTableOffsetRole:
|
|
|
|
return QVariant::fromValue(cls->vtableAddr);
|
2018-03-21 20:32:32 +00:00
|
|
|
case NameRole:
|
|
|
|
return cls->name;
|
|
|
|
case TypeRole:
|
2019-02-01 14:51:29 +00:00
|
|
|
return QVariant::fromValue(RowType::Class);
|
2018-03-21 20:32:32 +00:00
|
|
|
default:
|
|
|
|
return QVariant();
|
2017-12-23 16:42:42 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-08-17 11:27:07 +00:00
|
|
|
|
|
|
|
AnalClassesModel::AnalClassesModel(QObject *parent)
|
2019-02-01 14:51:29 +00:00
|
|
|
: ClassesModel(parent), attrs(new QMap<QString, QVector<Attribute>>)
|
2017-12-23 16:42:42 +00:00
|
|
|
{
|
2018-08-17 11:27:07 +00:00
|
|
|
}
|
|
|
|
|
2019-01-31 16:45:58 +00:00
|
|
|
void AnalClassesModel::refreshClasses()
|
2018-08-17 11:27:07 +00:00
|
|
|
{
|
|
|
|
beginResetModel();
|
2019-01-31 16:45:58 +00:00
|
|
|
classes = Core()->getAllAnalClasses();
|
2018-08-17 11:27:07 +00:00
|
|
|
endResetModel();
|
|
|
|
}
|
|
|
|
|
2019-02-01 14:51:29 +00:00
|
|
|
const QVector<AnalClassesModel::Attribute> &AnalClassesModel::getAttrs(const QString &cls) const
|
|
|
|
{
|
|
|
|
auto it = attrs->find(cls);
|
|
|
|
if(it != attrs->end()) {
|
|
|
|
return it.value();
|
|
|
|
}
|
|
|
|
|
|
|
|
QVector<AnalClassesModel::Attribute> clsAttrs;
|
|
|
|
QList<AnalBaseClassDescription> bases = Core()->getAnalClassBaseClasses(cls);
|
|
|
|
QList<AnalMethodDescription> meths = Core()->getAnalClassMethods(cls);
|
|
|
|
QList<AnalVTableDescription> vtables = Core()->getAnalClassVTables(cls);
|
|
|
|
clsAttrs.reserve(bases.size() + meths.size() + vtables.size());
|
|
|
|
|
|
|
|
for(const AnalBaseClassDescription &base : bases) {
|
|
|
|
clsAttrs.push_back(Attribute(Attribute::Type::Base, QVariant::fromValue(base)));
|
|
|
|
}
|
|
|
|
|
|
|
|
for(const AnalVTableDescription &vtable : vtables) {
|
|
|
|
clsAttrs.push_back(Attribute(Attribute::Type::VTable, QVariant::fromValue(vtable)));
|
|
|
|
}
|
|
|
|
|
|
|
|
for(const AnalMethodDescription &meth : meths) {
|
|
|
|
clsAttrs.push_back(Attribute(Attribute::Type::Method, QVariant::fromValue(meth)));
|
|
|
|
}
|
|
|
|
|
|
|
|
return attrs->insert(cls, clsAttrs).value();
|
|
|
|
}
|
2018-08-17 11:27:07 +00:00
|
|
|
|
|
|
|
QModelIndex AnalClassesModel::index(int row, int column, const QModelIndex &parent) const
|
|
|
|
{
|
2019-02-01 14:51:29 +00:00
|
|
|
if (!parent.isValid()) {
|
2018-08-17 11:27:07 +00:00
|
|
|
return createIndex(row, column, (quintptr)0); // root function nodes have id = 0
|
2019-02-01 14:51:29 +00:00
|
|
|
}
|
2018-08-17 11:27:07 +00:00
|
|
|
|
|
|
|
return createIndex(row, column, (quintptr)parent.row() + 1); // sub-nodes have id = class index + 1
|
|
|
|
}
|
|
|
|
|
|
|
|
QModelIndex AnalClassesModel::parent(const QModelIndex &index) const
|
|
|
|
{
|
2019-02-01 14:51:29 +00:00
|
|
|
if (!index.isValid()) {
|
|
|
|
return {};
|
|
|
|
}
|
2018-08-17 11:27:07 +00:00
|
|
|
|
2019-02-01 14:51:29 +00:00
|
|
|
if (index.internalId() == 0) { // root function node
|
|
|
|
return {};
|
|
|
|
} else { // sub-node
|
2018-08-17 11:27:07 +00:00
|
|
|
return this->index((int)(index.internalId() - 1), 0);
|
2019-02-01 14:51:29 +00:00
|
|
|
}
|
2018-08-17 11:27:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
int AnalClassesModel::rowCount(const QModelIndex &parent) const
|
|
|
|
{
|
|
|
|
if (!parent.isValid()) { // root
|
|
|
|
return classes.count();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (parent.internalId() == 0) { // methods/fields
|
2019-02-01 14:51:29 +00:00
|
|
|
return getAttrs(classes[parent.row()]).size();
|
2018-08-17 11:27:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0; // below methods/fields
|
|
|
|
}
|
|
|
|
|
|
|
|
bool AnalClassesModel::hasChildren(const QModelIndex &parent) const
|
|
|
|
{
|
2019-02-01 14:51:29 +00:00
|
|
|
return !parent.isValid() || !parent.parent().isValid();
|
2018-08-17 11:27:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
int AnalClassesModel::columnCount(const QModelIndex &) const
|
|
|
|
{
|
|
|
|
return Columns::COUNT;
|
|
|
|
}
|
|
|
|
|
|
|
|
QVariant AnalClassesModel::data(const QModelIndex &index, int role) const
|
|
|
|
{
|
|
|
|
if (index.internalId() == 0) { // class row
|
|
|
|
if (index.row() >= classes.count()) {
|
|
|
|
return QVariant();
|
|
|
|
}
|
|
|
|
|
2019-02-01 14:51:29 +00:00
|
|
|
QString cls = classes.at(index.row());
|
2018-08-17 11:27:07 +00:00
|
|
|
switch (role) {
|
|
|
|
case Qt::DisplayRole:
|
|
|
|
switch (index.column()) {
|
|
|
|
case NAME:
|
2019-02-01 14:51:29 +00:00
|
|
|
return cls;
|
2018-08-17 11:27:07 +00:00
|
|
|
case TYPE:
|
2019-02-01 14:51:29 +00:00
|
|
|
return tr("class");
|
2018-08-17 11:27:07 +00:00
|
|
|
default:
|
|
|
|
return QVariant();
|
|
|
|
}
|
|
|
|
case TypeRole:
|
2019-02-01 14:51:29 +00:00
|
|
|
return QVariant::fromValue(RowType::Class);
|
2018-08-17 11:27:07 +00:00
|
|
|
default:
|
|
|
|
return QVariant();
|
|
|
|
}
|
2019-02-01 14:51:29 +00:00
|
|
|
} else { // method/field/base row
|
|
|
|
QString cls = classes.at(static_cast<int>(index.internalId() - 1));
|
|
|
|
const Attribute &attr = getAttrs(cls)[index.row()];
|
|
|
|
|
|
|
|
switch (attr.type) {
|
|
|
|
case Attribute::Type::Base: {
|
|
|
|
AnalBaseClassDescription base = attr.data.value<AnalBaseClassDescription>();
|
|
|
|
switch (role) {
|
|
|
|
case Qt::DisplayRole:
|
|
|
|
switch (index.column()) {
|
|
|
|
case NAME:
|
|
|
|
return base.className;
|
|
|
|
case TYPE:
|
|
|
|
return tr("base");
|
|
|
|
case OFFSET:
|
|
|
|
return QString("+%1").arg(base.offset);
|
|
|
|
default:
|
|
|
|
return QVariant();
|
|
|
|
}
|
2019-02-01 15:14:11 +00:00
|
|
|
case Qt::DecorationRole:
|
|
|
|
if (index.column() == NAME) {
|
|
|
|
return QIcon(new SvgIconEngine(QString(":/img/icons/home.svg"), QPalette::WindowText));
|
|
|
|
}
|
|
|
|
return QVariant();
|
2019-02-01 14:51:29 +00:00
|
|
|
case NameRole:
|
|
|
|
return base.className;
|
|
|
|
case TypeRole:
|
|
|
|
return QVariant::fromValue(RowType::Base);
|
2018-08-17 11:27:07 +00:00
|
|
|
default:
|
|
|
|
return QVariant();
|
|
|
|
}
|
2019-02-01 14:51:29 +00:00
|
|
|
break;
|
2018-08-17 11:27:07 +00:00
|
|
|
}
|
2019-02-01 14:51:29 +00:00
|
|
|
case Attribute::Type::Method: {
|
|
|
|
AnalMethodDescription meth = attr.data.value<AnalMethodDescription>();
|
|
|
|
switch (role) {
|
|
|
|
case Qt::DisplayRole:
|
|
|
|
switch (index.column()) {
|
|
|
|
case NAME:
|
|
|
|
return meth.name;
|
|
|
|
case TYPE:
|
|
|
|
return tr("method");
|
|
|
|
case OFFSET:
|
|
|
|
return meth.addr == RVA_INVALID ? QString() : RAddressString(meth.addr);
|
|
|
|
case VTABLE:
|
|
|
|
return meth.vtableOffset < 0 ? QString() : QString("+%1").arg(meth.vtableOffset);
|
|
|
|
default:
|
|
|
|
return QVariant();
|
|
|
|
}
|
2019-02-01 15:14:11 +00:00
|
|
|
case Qt::DecorationRole:
|
|
|
|
if (index.column() == NAME) {
|
|
|
|
return QIcon(new SvgIconEngine(QString(":/img/icons/fork.svg"), QPalette::WindowText));
|
|
|
|
}
|
|
|
|
return QVariant();
|
2019-02-01 14:51:29 +00:00
|
|
|
case OffsetRole:
|
|
|
|
return QVariant::fromValue(meth.addr);
|
|
|
|
case VTableOffsetRole:
|
|
|
|
return QVariant::fromValue(index.parent().data(VTableOffsetRole).toULongLong() + meth.vtableOffset);
|
|
|
|
case NameRole:
|
|
|
|
return meth.name;
|
|
|
|
case TypeRole:
|
|
|
|
return QVariant::fromValue(RowType::Method);
|
2018-08-17 11:27:07 +00:00
|
|
|
default:
|
|
|
|
return QVariant();
|
|
|
|
}
|
2019-02-01 14:51:29 +00:00
|
|
|
break;
|
2018-08-17 11:27:07 +00:00
|
|
|
}
|
2019-02-01 14:51:29 +00:00
|
|
|
case Attribute::Type::VTable: {
|
|
|
|
AnalVTableDescription vtable = attr.data.value<AnalVTableDescription>();
|
|
|
|
switch (role) {
|
|
|
|
case Qt::DisplayRole:
|
|
|
|
switch (index.column()) {
|
|
|
|
case NAME:
|
|
|
|
return "vtable";
|
|
|
|
case TYPE:
|
|
|
|
return tr("vtable");
|
|
|
|
case OFFSET:
|
|
|
|
return RAddressString(vtable.addr);
|
|
|
|
default:
|
|
|
|
return QVariant();
|
|
|
|
}
|
2019-02-01 15:14:11 +00:00
|
|
|
case Qt::DecorationRole:
|
|
|
|
if (index.column() == NAME) {
|
|
|
|
return QIcon(new SvgIconEngine(QString(":/img/icons/list.svg"), QPalette::WindowText));
|
|
|
|
}
|
|
|
|
return QVariant();
|
2019-02-01 14:51:29 +00:00
|
|
|
case OffsetRole:
|
|
|
|
return QVariant::fromValue(vtable.addr);
|
|
|
|
case TypeRole:
|
|
|
|
return QVariant::fromValue(RowType::VTable);
|
2018-08-17 11:27:07 +00:00
|
|
|
default:
|
|
|
|
return QVariant();
|
|
|
|
}
|
2019-02-01 14:51:29 +00:00
|
|
|
break;
|
|
|
|
}
|
2017-12-23 16:42:42 +00:00
|
|
|
}
|
|
|
|
}
|
2019-02-01 14:51:29 +00:00
|
|
|
return QVariant();
|
2017-12-23 16:42:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2018-08-17 11:27:07 +00:00
|
|
|
ClassesSortFilterProxyModel::ClassesSortFilterProxyModel(QObject *parent)
|
2017-12-23 16:42:42 +00:00
|
|
|
: QSortFilterProxyModel(parent)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
bool ClassesSortFilterProxyModel::filterAcceptsRow(int row, const QModelIndex &parent) const
|
|
|
|
{
|
|
|
|
QModelIndex index = sourceModel()->index(row, 0, parent);
|
|
|
|
return index.data(ClassesModel::NameRole).toString().contains(filterRegExp());
|
|
|
|
}
|
|
|
|
|
|
|
|
bool ClassesSortFilterProxyModel::lessThan(const QModelIndex &left, const QModelIndex &right) const
|
|
|
|
{
|
2018-03-21 20:32:32 +00:00
|
|
|
switch (left.column()) {
|
|
|
|
case ClassesModel::OFFSET: {
|
2017-12-23 16:42:42 +00:00
|
|
|
RVA left_offset = left.data(ClassesModel::OffsetRole).toULongLong();
|
|
|
|
RVA right_offset = right.data(ClassesModel::OffsetRole).toULongLong();
|
|
|
|
if (left_offset != right_offset)
|
|
|
|
return left_offset < right_offset;
|
|
|
|
}
|
|
|
|
// fallthrough
|
2018-03-21 20:32:32 +00:00
|
|
|
case ClassesModel::TYPE: {
|
2017-12-23 16:42:42 +00:00
|
|
|
auto left_type = left.data(ClassesModel::TypeRole).value<ClassesModel::RowType>();
|
|
|
|
auto right_type = right.data(ClassesModel::TypeRole).value<ClassesModel::RowType>();
|
|
|
|
if (left_type != right_type)
|
|
|
|
return left_type < right_type;
|
|
|
|
}
|
|
|
|
// fallthrough
|
|
|
|
case ClassesModel::NAME:
|
|
|
|
default:
|
|
|
|
QString left_name = left.data(ClassesModel::NameRole).toString();
|
|
|
|
QString right_name = right.data(ClassesModel::NameRole).toString();
|
|
|
|
return left_name < right_name;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2018-03-16 21:46:57 +00:00
|
|
|
ClassesWidget::ClassesWidget(MainWindow *main, QAction *action) :
|
|
|
|
CutterDockWidget(main, action),
|
2018-02-04 14:32:18 +00:00
|
|
|
ui(new Ui::ClassesWidget)
|
2017-12-23 16:42:42 +00:00
|
|
|
{
|
|
|
|
ui->setupUi(this);
|
|
|
|
|
2019-02-01 16:00:09 +00:00
|
|
|
ui->classesTreeView->setIconSize(QSize(10, 10));
|
|
|
|
|
2018-08-17 11:27:07 +00:00
|
|
|
proxy_model = new ClassesSortFilterProxyModel(this);
|
|
|
|
ui->classesTreeView->setModel(nullptr);
|
2017-12-23 16:42:42 +00:00
|
|
|
ui->classesTreeView->sortByColumn(ClassesModel::TYPE, Qt::AscendingOrder);
|
2018-08-17 11:27:07 +00:00
|
|
|
ui->classesTreeView->setContextMenuPolicy(Qt::CustomContextMenu);
|
|
|
|
|
|
|
|
ui->classSourceCombo->setCurrentIndex(1);
|
2017-12-23 16:42:42 +00:00
|
|
|
|
|
|
|
connect(Core(), SIGNAL(refreshAll()), this, SLOT(refreshClasses()));
|
2018-08-17 11:27:07 +00:00
|
|
|
connect(Core(), &CutterCore::classesChanged, this, [this]() {
|
|
|
|
if (getSource() == Source::ANAL) {
|
|
|
|
refreshClasses();
|
|
|
|
}
|
|
|
|
});
|
2018-03-11 15:57:38 +00:00
|
|
|
connect(ui->classSourceCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(refreshClasses()));
|
2018-08-17 11:27:07 +00:00
|
|
|
connect(ui->classesTreeView, &QTreeView::customContextMenuRequested, this, &ClassesWidget::showContextMenu);
|
2017-12-23 16:42:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
ClassesWidget::~ClassesWidget() {}
|
|
|
|
|
2018-03-11 15:57:38 +00:00
|
|
|
ClassesWidget::Source ClassesWidget::getSource()
|
|
|
|
{
|
2018-08-17 11:27:07 +00:00
|
|
|
switch (ui->classSourceCombo->currentIndex()) {
|
|
|
|
case 0:
|
2018-03-11 15:57:38 +00:00
|
|
|
return Source::BIN;
|
2018-08-17 11:27:07 +00:00
|
|
|
default:
|
|
|
|
return Source::ANAL;
|
2018-03-11 15:57:38 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-12-23 16:42:42 +00:00
|
|
|
void ClassesWidget::refreshClasses()
|
|
|
|
{
|
2018-08-17 11:27:07 +00:00
|
|
|
switch (getSource()) {
|
|
|
|
case Source::BIN:
|
|
|
|
if (!bin_model) {
|
|
|
|
proxy_model->setSourceModel(nullptr);
|
|
|
|
delete anal_model;
|
|
|
|
anal_model = nullptr;
|
|
|
|
bin_model = new BinClassesModel(this);
|
|
|
|
ui->classesTreeView->setModel(bin_model);
|
|
|
|
//proxy_model->setSourceModel(bin_model);
|
|
|
|
}
|
|
|
|
bin_model->setClasses(Core()->getAllClassesFromBin());
|
|
|
|
break;
|
|
|
|
case Source::ANAL:
|
|
|
|
if (!anal_model) {
|
|
|
|
proxy_model->setSourceModel(nullptr);
|
|
|
|
delete bin_model;
|
|
|
|
bin_model = nullptr;
|
|
|
|
anal_model = new AnalClassesModel(this);
|
|
|
|
ui->classesTreeView->setModel(anal_model);
|
|
|
|
//proxy_model->setSourceModel(anal_model);
|
|
|
|
}
|
2019-01-31 16:45:58 +00:00
|
|
|
anal_model->refreshClasses();
|
2018-08-17 11:27:07 +00:00
|
|
|
break;
|
|
|
|
}
|
2017-12-23 16:42:42 +00:00
|
|
|
|
2018-04-01 08:25:31 +00:00
|
|
|
qhelpers::adjustColumns(ui->classesTreeView, 3, 0);
|
2017-12-23 16:42:42 +00:00
|
|
|
|
|
|
|
ui->classesTreeView->setColumnWidth(0, 200);
|
|
|
|
}
|
|
|
|
|
|
|
|
void ClassesWidget::on_classesTreeView_doubleClicked(const QModelIndex &index)
|
|
|
|
{
|
2018-04-23 07:54:06 +00:00
|
|
|
if (!index.isValid())
|
|
|
|
return;
|
|
|
|
|
2019-02-01 16:00:09 +00:00
|
|
|
QVariant offsetData = index.data(ClassesModel::OffsetRole);
|
|
|
|
if(!offsetData.isValid()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
RVA offset = offsetData.value<RVA>();
|
2018-04-12 06:33:30 +00:00
|
|
|
Core()->seek(offset);
|
2017-12-23 16:42:42 +00:00
|
|
|
}
|
2018-08-17 11:27:07 +00:00
|
|
|
|
|
|
|
void ClassesWidget::showContextMenu(const QPoint &pt)
|
|
|
|
{
|
|
|
|
QModelIndex index = ui->classesTreeView->selectionModel()->currentIndex();
|
|
|
|
if (!index.isValid()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
QMenu menu(ui->classesTreeView);
|
|
|
|
|
|
|
|
QVariant vtableOffsetVariant = index.data(ClassesModel::VTableOffsetRole);
|
|
|
|
if (vtableOffsetVariant.isValid() && vtableOffsetVariant.toULongLong() != RVA_INVALID) {
|
|
|
|
menu.addAction(ui->seekToVTableAction);
|
|
|
|
}
|
|
|
|
|
|
|
|
menu.addAction(ui->addMethodAction);
|
|
|
|
|
2019-02-01 14:51:29 +00:00
|
|
|
if (index.data(ClassesModel::TypeRole).toInt() == static_cast<int>(ClassesModel::RowType::Method)) {
|
2018-08-17 11:27:07 +00:00
|
|
|
menu.addAction(ui->editMethodAction);
|
|
|
|
}
|
|
|
|
|
|
|
|
menu.exec(ui->classesTreeView->mapToGlobal(pt));
|
|
|
|
}
|
|
|
|
|
|
|
|
void ClassesWidget::on_seekToVTableAction_triggered()
|
|
|
|
{
|
|
|
|
RVA vtableOffset = ui->classesTreeView->selectionModel()->currentIndex()
|
|
|
|
.data(ClassesModel::VTableOffsetRole).value<RVA>();
|
|
|
|
if (vtableOffset != RVA_INVALID) {
|
|
|
|
Core()->seek(vtableOffset);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void ClassesWidget::on_addMethodAction_triggered()
|
|
|
|
{
|
|
|
|
QModelIndex index = ui->classesTreeView->selectionModel()->currentIndex();
|
|
|
|
if (!index.isValid()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
QString className;
|
2019-02-01 14:51:29 +00:00
|
|
|
if (index.data(ClassesModel::TypeRole).toInt() == static_cast<int>(ClassesModel::RowType::Class)) {
|
2018-08-17 11:27:07 +00:00
|
|
|
className = index.data(ClassesModel::NameRole).toString();
|
|
|
|
} else {
|
|
|
|
className = index.parent().data(ClassesModel::NameRole).toString();
|
|
|
|
}
|
|
|
|
|
2019-01-31 16:45:58 +00:00
|
|
|
BinClassMethodDescription meth;
|
2018-08-17 11:27:07 +00:00
|
|
|
meth.addr = Core()->getOffset();
|
|
|
|
|
|
|
|
EditMethodDialog::newMethod(className, meth);
|
|
|
|
}
|
|
|
|
|
|
|
|
void ClassesWidget::on_editMethodAction_triggered()
|
|
|
|
{
|
|
|
|
QModelIndex index = ui->classesTreeView->selectionModel()->currentIndex();
|
2019-02-01 14:51:29 +00:00
|
|
|
if (!index.isValid() || index.data(ClassesModel::TypeRole).toInt() != static_cast<int>(ClassesModel::RowType::Method)) {
|
2018-08-17 11:27:07 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
QString className = index.parent().data(ClassesModel::NameRole).toString();
|
2019-01-31 16:45:58 +00:00
|
|
|
BinClassMethodDescription meth = index.data(ClassesModel::DataRole).value<BinClassMethodDescription>();
|
2018-08-17 11:27:07 +00:00
|
|
|
EditMethodDialog::editMethod(className, meth);
|
|
|
|
}
|