1
0
Fork 0
x64dbg/src/gui/Src/Gui/StructWidget.cpp

261 lines
9.3 KiB
C++

#include "StructWidget.h"
#include "ui_StructWidget.h"
#include "Configuration.h"
#include "MenuBuilder.h"
#include "LineEditDialog.h"
#include "GotoDialog.h"
#include <QFileDialog>
#include "StringUtil.h"
#include "MiscUtil.h"
struct TypeDescriptor
{
TYPEDESCRIPTOR type;
QString name;
};
Q_DECLARE_METATYPE(TypeDescriptor)
StructWidget::StructWidget(QWidget* parent) :
QWidget(parent),
ui(new Ui::StructWidget)
{
ui->setupUi(this);
ui->treeWidget->setStyleSheet("QTreeWidget { color: #000000; background-color: #FFF8F0; alternate-background-color: #DCD9CF; }");
connect(Bridge::getBridge(), SIGNAL(typeAddNode(void*, const TYPEDESCRIPTOR*)), this, SLOT(typeAddNode(void*, const TYPEDESCRIPTOR*)));
connect(Bridge::getBridge(), SIGNAL(typeClear()), this, SLOT(typeClear()));
connect(Bridge::getBridge(), SIGNAL(typeUpdateWidget()), this, SLOT(typeUpdateWidget()));
connect(Bridge::getBridge(), SIGNAL(dbgStateChanged(DBGSTATE)), this, SLOT(dbgStateChangedSlot(DBGSTATE)));
connect(Config(), SIGNAL(colorsUpdated()), this, SLOT(colorsUpdatedSlot()));
connect(Config(), SIGNAL(fontsUpdated()), this, SLOT(fontsUpdatedSlot()));
connect(Config(), SIGNAL(shortcutsUpdated()), this, SLOT(shortcutsUpdatedSlot()));
colorsUpdatedSlot();
fontsUpdatedSlot();
setupContextMenu();
setupColumns();
}
StructWidget::~StructWidget()
{
delete ui;
}
void StructWidget::colorsUpdatedSlot()
{
auto color = ConfigColor("AbstractTableViewTextColor");
auto background = ConfigColor("StructBackgroundColor");
auto altBackground = ConfigColor("StructAlternateBackgroundColor");
auto style = QString("QTreeWidget { color: %1; background-color: %2; alternate-background-color: %3; }").arg(color.name(), background.name(), altBackground.name());
ui->treeWidget->setStyleSheet(style);
}
void StructWidget::fontsUpdatedSlot()
{
auto font = ConfigFont("AbstractTableView");
setFont(font);
ui->treeWidget->setFont(font);
ui->treeWidget->header()->setFont(font);
}
void StructWidget::shortcutsUpdatedSlot()
{
updateShortcuts();
}
void StructWidget::typeAddNode(void* parent, const TYPEDESCRIPTOR* type)
{
TypeDescriptor dtype;
dtype.type = *type;
dtype.name = QString(dtype.type.name);
dtype.type.name = nullptr;
auto text = QStringList() << dtype.name << ToPtrString(dtype.type.addr + dtype.type.offset) << "0x" + ToHexString(dtype.type.size);
QTreeWidgetItem* item = parent ? new QTreeWidgetItem((QTreeWidgetItem*)parent, text) : new QTreeWidgetItem(ui->treeWidget, text);
item->setExpanded(dtype.type.expanded);
QVariant var;
var.setValue(dtype);
item->setData(0, Qt::UserRole, var);
Bridge::getBridge()->setResult(BridgeResult::TypeAddNode, dsint(item));
}
void StructWidget::typeClear()
{
ui->treeWidget->clear();
Bridge::getBridge()->setResult(BridgeResult::TypeClear);
}
void StructWidget::typeUpdateWidget()
{
for(QTreeWidgetItemIterator it(ui->treeWidget); *it; ++it)
{
QTreeWidgetItem* item = *it;
auto type = item->data(0, Qt::UserRole).value<TypeDescriptor>();
auto name = type.name.toUtf8();
type.type.name = name.constData();
auto addr = type.type.addr + type.type.offset;
item->setText(1, ToPtrString(addr));
QString valueStr;
if(type.type.callback) //use the provided callback
{
char value[128] = "";
size_t valueCount = _countof(value);
if(!type.type.callback(&type.type, value, &valueCount) && valueCount && valueCount != _countof(value))
{
auto dest = new char[valueCount];
if(type.type.callback(&type.type, dest, &valueCount))
valueStr = value;
else
valueStr = "???";
delete[] dest;
}
else
valueStr = value;
}
else if(!item->childCount() && type.type.size > 0 && type.type.size <= sizeof(uint64_t)) //attempt to display small, non-parent values
{
uint64_t data;
if(DbgMemRead(addr, &data, type.type.size))
{
if(type.type.reverse)
std::reverse((char*)data, (char*)data + type.type.size);
valueStr = QString().sprintf("0x%llX, %llu", data, data, data);
}
else if(type.type.addr)
valueStr = "???";
}
item->setText(3, valueStr);
}
}
void StructWidget::dbgStateChangedSlot(DBGSTATE state)
{
if(state == stopped)
ui->treeWidget->clear();
}
void StructWidget::setupColumns()
{
ui->treeWidget->setColumnWidth(0, 300); //Name
ui->treeWidget->setColumnWidth(1, 80); //Address
ui->treeWidget->setColumnWidth(2, 80); //Size
//ui->treeWidget->setColumnWidth(3, 80); //Value
}
#define hasSelection !!ui->treeWidget->selectedItems().count()
#define selectedItem ui->treeWidget->selectedItems()[0]
#define selectedType selectedItem->data(0, Qt::UserRole).value<TypeDescriptor>().type
void StructWidget::setupContextMenu()
{
mMenuBuilder = new MenuBuilder(this);
mMenuBuilder->addAction(makeAction(DIcon("dump.png"), tr("&Follow in Dump"), SLOT(followDumpSlot())), [this](QMenu*)
{
return hasSelection && DbgMemIsValidReadPtr(selectedType.addr + selectedType.offset);
});
mMenuBuilder->addAction(makeAction(DIcon("structaddr.png"), tr("Change address"), SLOT(changeAddrSlot())), [this](QMenu*)
{
return hasSelection && !selectedItem->parent() && DbgIsDebugging();
});
mMenuBuilder->addAction(makeAction(DIcon("visitstruct.png"), tr("Visit type"), SLOT(visitSlot())));
mMenuBuilder->addAction(makeAction(DIcon("database-import.png"), tr("Load JSON"), SLOT(loadJsonSlot())));
mMenuBuilder->addAction(makeAction(DIcon("source.png"), tr("Parse header"), SLOT(parseFileSlot())));
mMenuBuilder->addAction(makeAction(DIcon("removestruct.png"), tr("Remove"), SLOT(removeSlot())), [this](QMenu*)
{
return hasSelection && !selectedItem->parent();
});
mMenuBuilder->addAction(makeAction(DIcon("eraser.png"), tr("Clear"), SLOT(clearSlot())));
mMenuBuilder->addAction(makeShortcutAction(DIcon("sync.png"), tr("&Refresh"), SLOT(refreshSlot()), "ActionRefresh"));
mMenuBuilder->loadFromConfig();
}
void StructWidget::on_treeWidget_customContextMenuRequested(const QPoint & pos)
{
QMenu wMenu;
mMenuBuilder->build(&wMenu);
if(wMenu.actions().count())
wMenu.exec(ui->treeWidget->viewport()->mapToGlobal(pos));
}
void StructWidget::followDumpSlot()
{
if(!hasSelection)
return;
DbgCmdExec(QString("dump %1").arg(ToPtrString(selectedType.addr + selectedType.offset)));
}
void StructWidget::clearSlot()
{
ui->treeWidget->clear();
}
void StructWidget::removeSlot()
{
if(!hasSelection)
return;
delete selectedItem;
}
void StructWidget::visitSlot()
{
//TODO: replace with a list to pick from
LineEditDialog mLineEdit(this);
mLineEdit.setWindowTitle(tr("Type to visit"));
if(mLineEdit.exec() != QDialog::Accepted || !mLineEdit.editText.length())
return;
if(!mGotoDialog)
mGotoDialog = new GotoDialog(this);
duint addr = 0;
mGotoDialog->setWindowTitle(tr("Address to visit"));
if(DbgIsDebugging() && mGotoDialog->exec() == QDialog::Accepted)
addr = DbgValFromString(mGotoDialog->expressionText.toUtf8().constData());
DbgCmdExec(QString("VisitType %1, %2").arg(mLineEdit.editText, ToPtrString(addr)));
}
void StructWidget::loadJsonSlot()
{
auto filename = QFileDialog::getOpenFileName(this, tr("Load JSON"), QString(), tr("JSON files (*.json);;All files (*.*)"));
if(!filename.length())
return;
filename = QDir::toNativeSeparators(filename);
DbgCmdExec(QString("LoadTypes \"%1\"").arg(filename));
}
void StructWidget::parseFileSlot()
{
auto filename = QFileDialog::getOpenFileName(this, tr("Parse header"), QString(), tr("Header files (*.h *.hpp);;All files (*.*)"));
if(!filename.length())
return;
filename = QDir::toNativeSeparators(filename);
DbgCmdExec(QString("ParseTypes \"%1\"").arg(filename));
}
static void changeTypeAddr(QTreeWidgetItem* item, duint addr)
{
auto changeAddr = item->data(0, Qt::UserRole).value<TypeDescriptor>().type.addr;
for(QTreeWidgetItemIterator it(item); *it; ++it)
{
QTreeWidgetItem* item = *it;
auto type = item->data(0, Qt::UserRole).value<TypeDescriptor>();
type.type.addr = type.type.addr == changeAddr ? addr : 0; //invalidate pointers (requires revisit)
QVariant var;
var.setValue(type);
item->setData(0, Qt::UserRole, var);
}
}
void StructWidget::changeAddrSlot()
{
if(!hasSelection || !DbgIsDebugging())
return;
if(!mGotoDialog)
mGotoDialog = new GotoDialog(this);
mGotoDialog->setWindowTitle(tr("Change address"));
if(mGotoDialog->exec() != QDialog::Accepted)
return;
changeTypeAddr(selectedItem, DbgValFromString(mGotoDialog->expressionText.toUtf8().constData()));
refreshSlot();
}
void StructWidget::refreshSlot()
{
typeUpdateWidget();
}