DBG+BRIDGE+GUI: initial version of struct viewer
This commit is contained in:
parent
59f944a090
commit
de1951e9ca
|
@ -1029,6 +1029,7 @@ typedef struct _TYPEDESCRIPTOR
|
|||
bool expanded; //is the type node expanded?
|
||||
const char* name; //type name (int b)
|
||||
duint addr; //virtual address
|
||||
duint offset; //offset to addr for the actual location
|
||||
int id; //type id
|
||||
int size; //sizeof(type)
|
||||
TYPETOSTRING callback; //convert to string
|
||||
|
|
|
@ -331,108 +331,134 @@ struct PrintVisitor : TypeManager::Visitor
|
|||
return StringUtils::sprintf(format, *(T*)data);
|
||||
}
|
||||
|
||||
static bool cbPrintPrimitive(const TYPEDESCRIPTOR* type, char* dest, size_t* destCount)
|
||||
{
|
||||
if(!type->addr)
|
||||
{
|
||||
*dest = '\0';
|
||||
return true;
|
||||
}
|
||||
String valueStr;
|
||||
Memory<unsigned char*> data(type->size);
|
||||
if(MemRead(type->addr + type->offset, data(), data.size()))
|
||||
{
|
||||
switch(Primitive(type->id))
|
||||
{
|
||||
case Void:
|
||||
valueStr.clear();
|
||||
break;
|
||||
case Int8:
|
||||
valueStr += basicPrint<char>(data(), "%c");
|
||||
break;
|
||||
case Uint8:
|
||||
valueStr += basicPrint<unsigned char>(data(), "0x%02X");
|
||||
break;
|
||||
case Int16:
|
||||
valueStr += basicPrint<short>(data(), "%d");
|
||||
break;
|
||||
case Uint16:
|
||||
valueStr += basicPrint<short>(data(), "%u");
|
||||
break;
|
||||
case Int32:
|
||||
valueStr += basicPrint<int>(data(), "%d");
|
||||
break;
|
||||
case Uint32:
|
||||
valueStr += basicPrint<unsigned int>(data(), "%u");
|
||||
break;
|
||||
case Int64:
|
||||
valueStr += basicPrint<long long>(data(), "%lld");
|
||||
break;
|
||||
case Uint64:
|
||||
valueStr += basicPrint<unsigned long long>(data(), "%llu");
|
||||
break;
|
||||
case Dsint:
|
||||
#ifdef _WIN64
|
||||
valueStr += basicPrint<dsint>(data(), "%lld");
|
||||
#else
|
||||
valueStr += basicPrint<dsint>(data(), "%d");
|
||||
#endif //_WIN64
|
||||
break;
|
||||
case Duint:
|
||||
#ifdef _WIN64
|
||||
valueStr += basicPrint<duint>(data(), "%llu");
|
||||
#else
|
||||
valueStr += basicPrint<duint>(data(), "%u");
|
||||
#endif //_WIN64
|
||||
break;
|
||||
case Float:
|
||||
valueStr += basicPrint<float>(data(), "%f");
|
||||
break;
|
||||
case Double:
|
||||
valueStr += basicPrint<double>(data(), "%f");
|
||||
break;
|
||||
case Pointer:
|
||||
valueStr += basicPrint<void*>(data(), "0x%p");
|
||||
break;
|
||||
case PtrString:
|
||||
{
|
||||
valueStr += basicPrint<char*>(data(), "0x%p");
|
||||
Memory<char*> strdata(MAX_STRING_SIZE + 1);
|
||||
if(MemRead(*(duint*)data(), strdata(), strdata.size() - 1))
|
||||
{
|
||||
valueStr += "\"";
|
||||
valueStr += strdata();
|
||||
valueStr.push_back('\"');
|
||||
}
|
||||
else
|
||||
valueStr += "???";
|
||||
}
|
||||
break;
|
||||
|
||||
case PtrWString:
|
||||
{
|
||||
valueStr += basicPrint<wchar_t*>(data(), "0x%p");
|
||||
Memory<wchar_t*> strdata(MAX_STRING_SIZE * 2 + 2);
|
||||
if(MemRead(*(duint*)data(), strdata(), strdata.size() - 2))
|
||||
{
|
||||
valueStr += "L\"";
|
||||
valueStr += StringUtils::Utf16ToUtf8(strdata());
|
||||
valueStr.push_back('\"');
|
||||
}
|
||||
else
|
||||
valueStr += "???";
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
valueStr = "???";
|
||||
if(*destCount <= valueStr.size())
|
||||
{
|
||||
*destCount = valueStr.size() + 1;
|
||||
return false;
|
||||
}
|
||||
strcpy_s(dest, *destCount, valueStr.c_str());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool visitType(const Member & member, const Type & type) override
|
||||
{
|
||||
String valueStr;
|
||||
Memory<unsigned char*> data(type.size);
|
||||
if(mAddr)
|
||||
{
|
||||
if(MemRead(mAddr + mOffset, data(), data.size()))
|
||||
{
|
||||
valueStr.assign(" = ");
|
||||
switch(type.primitive)
|
||||
{
|
||||
case Int8:
|
||||
valueStr += basicPrint<char>(data(), "%c");
|
||||
break;
|
||||
case Uint8:
|
||||
valueStr += basicPrint<unsigned char>(data(), "0x%02X");
|
||||
break;
|
||||
case Int16:
|
||||
valueStr += basicPrint<short>(data(), "%d");
|
||||
break;
|
||||
case Uint16:
|
||||
valueStr += basicPrint<short>(data(), "%u");
|
||||
break;
|
||||
case Int32:
|
||||
valueStr += basicPrint<int>(data(), "%d");
|
||||
break;
|
||||
case Uint32:
|
||||
valueStr += basicPrint<unsigned int>(data(), "%u");
|
||||
break;
|
||||
case Int64:
|
||||
valueStr += basicPrint<long long>(data(), "%lld");
|
||||
break;
|
||||
case Uint64:
|
||||
valueStr += basicPrint<unsigned long long>(data(), "%llu");
|
||||
break;
|
||||
case Dsint:
|
||||
#ifdef _WIN64
|
||||
valueStr += basicPrint<dsint>(data(), "%lld");
|
||||
#else
|
||||
valueStr += basicPrint<dsint>(data(), "%d");
|
||||
#endif //_WIN64
|
||||
break;
|
||||
case Duint:
|
||||
#ifdef _WIN64
|
||||
valueStr += basicPrint<duint>(data(), "%llu");
|
||||
#else
|
||||
valueStr += basicPrint<duint>(data(), "%u");
|
||||
#endif //_WIN64
|
||||
break;
|
||||
case Float:
|
||||
valueStr += basicPrint<float>(data(), "%f");
|
||||
break;
|
||||
case Double:
|
||||
valueStr += basicPrint<double>(data(), "%f");
|
||||
break;
|
||||
case Pointer:
|
||||
valueStr += basicPrint<void*>(data(), "0x%p");
|
||||
break;
|
||||
case PtrString:
|
||||
{
|
||||
valueStr += basicPrint<char*>(data(), "0x%p");
|
||||
Memory<char*> strdata(MAX_STRING_SIZE + 1);
|
||||
if(MemRead(*(duint*)data(), strdata(), strdata.size() - 1))
|
||||
{
|
||||
valueStr += " \"";
|
||||
valueStr += strdata();
|
||||
valueStr.push_back('\"');
|
||||
}
|
||||
else
|
||||
valueStr += " ???";
|
||||
}
|
||||
break;
|
||||
|
||||
case PtrWString:
|
||||
{
|
||||
valueStr += basicPrint<wchar_t*>(data(), "0x%p");
|
||||
Memory<wchar_t*> strdata(MAX_STRING_SIZE * 2 + 2);
|
||||
if(MemRead(*(duint*)data(), strdata(), strdata.size() - 2))
|
||||
{
|
||||
valueStr += " L\"";
|
||||
valueStr += StringUtils::Utf16ToUtf8(strdata());
|
||||
valueStr.push_back('\"');
|
||||
}
|
||||
else
|
||||
valueStr += " ???";
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
valueStr = " ???";
|
||||
}
|
||||
indent();
|
||||
String tname;
|
||||
auto ptype = mParents.empty() ? Parent::Struct : parent().type;
|
||||
if(ptype == Parent::Array)
|
||||
dprintf_untranslated("%s[%u]%s;", member.name.c_str(), parent().index++, valueStr.c_str());
|
||||
tname = StringUtils::sprintf("%s[%u]", member.name.c_str(), parent().index++);
|
||||
else
|
||||
dprintf_untranslated("%s %s%s;", type.name.c_str(), member.name.c_str(), valueStr.c_str());
|
||||
dputs_untranslated(type.pointto.empty() || mPtrDepth >= mMaxPtrDepth ? "" : " {");
|
||||
tname = StringUtils::sprintf("%s %s", type.name.c_str(), member.name.c_str());
|
||||
|
||||
TYPEDESCRIPTOR td;
|
||||
td.expanded = false;
|
||||
td.name = tname.c_str();
|
||||
td.addr = mAddr;
|
||||
td.offset = mOffset;
|
||||
td.id = type.primitive;
|
||||
td.size = type.size;
|
||||
td.callback = cbPrintPrimitive;
|
||||
td.userdata = nullptr;
|
||||
mNode = GuiTypeAddNode(mParents.empty() ? nullptr : parent().node, &td);
|
||||
|
||||
if(ptype != Parent::Union)
|
||||
mOffset += type.size;
|
||||
return true;
|
||||
|
@ -440,17 +466,41 @@ struct PrintVisitor : TypeManager::Visitor
|
|||
|
||||
bool visitStructUnion(const Member & member, const StructUnion & type) override
|
||||
{
|
||||
indent();
|
||||
dprintf_untranslated("%s %s {\n", type.isunion ? "union" : "struct", type.name.c_str());
|
||||
String tname = StringUtils::sprintf("%s %s", type.isunion ? "union" : "struct", type.name.c_str());
|
||||
|
||||
TYPEDESCRIPTOR td;
|
||||
td.expanded = true;
|
||||
td.name = tname.c_str();
|
||||
td.addr = mAddr;
|
||||
td.offset = mOffset;
|
||||
td.id = Void;
|
||||
td.size = type.size;
|
||||
td.callback = nullptr;
|
||||
td.userdata = nullptr;
|
||||
auto node = GuiTypeAddNode(mParents.empty() ? nullptr : parent().node, &td);
|
||||
|
||||
mParents.push_back(Parent(type.isunion ? Parent::Union : Parent::Struct));
|
||||
parent().node = node;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool visitArray(const Member & member) override
|
||||
{
|
||||
indent();
|
||||
dprintf_untranslated("%s %s[%d] {\n", member.type.c_str(), member.name.c_str(), member.arrsize);
|
||||
String tname = StringUtils::sprintf("%s %s[%d]", member.type.c_str(), member.name.c_str(), member.arrsize);
|
||||
|
||||
TYPEDESCRIPTOR td;
|
||||
td.expanded = member.arrsize <= 5;
|
||||
td.name = tname.c_str();
|
||||
td.addr = mAddr;
|
||||
td.offset = mOffset;
|
||||
td.id = Void;
|
||||
td.size = member.arrsize * SizeofType(member.type);
|
||||
td.callback = nullptr;
|
||||
td.userdata = nullptr;
|
||||
auto node = GuiTypeAddNode(mParents.empty() ? nullptr : parent().node, &td);
|
||||
|
||||
mParents.push_back(Parent(Parent::Array));
|
||||
parent().node = node;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -460,12 +510,15 @@ struct PrintVisitor : TypeManager::Visitor
|
|||
auto res = visitType(member, type); //print the pointer value
|
||||
if(mPtrDepth >= mMaxPtrDepth)
|
||||
return false;
|
||||
|
||||
duint value = 0;
|
||||
if(!mAddr || !MemRead(mAddr + offset, &value, sizeof(value)))
|
||||
return false;
|
||||
|
||||
mParents.push_back(Parent(Parent::Pointer));
|
||||
parent().offset = mOffset;
|
||||
parent().addr = mAddr;
|
||||
parent().node = mNode;
|
||||
mOffset = 0;
|
||||
mAddr = value;
|
||||
mPtrDepth++;
|
||||
|
@ -481,11 +534,6 @@ struct PrintVisitor : TypeManager::Visitor
|
|||
mPtrDepth--;
|
||||
}
|
||||
mParents.pop_back();
|
||||
indent();
|
||||
if(parent().type == Parent::Array)
|
||||
dprintf_untranslated("};\n");
|
||||
else
|
||||
dprintf_untranslated("} %s;\n", member.name.c_str());
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -504,6 +552,7 @@ private:
|
|||
unsigned int index = 0;
|
||||
duint addr = 0;
|
||||
duint offset = 0;
|
||||
void* node = nullptr;
|
||||
|
||||
explicit Parent(Type type)
|
||||
: type(type) { }
|
||||
|
@ -514,19 +563,12 @@ private:
|
|||
return mParents[mParents.size() - 1];
|
||||
}
|
||||
|
||||
void indent() const
|
||||
{
|
||||
if(mAddr)
|
||||
dprintf_untranslated("%p ", mAddr + mOffset);
|
||||
for(auto i = 0; i < int(mParents.size()) * 2; i++)
|
||||
dprintf_untranslated(" ");
|
||||
}
|
||||
|
||||
std::vector<Parent> mParents;
|
||||
duint mOffset = 0;
|
||||
duint mAddr = 0;
|
||||
int mPtrDepth = 0;
|
||||
int mMaxPtrDepth = 0;
|
||||
void* mNode = nullptr;
|
||||
};
|
||||
|
||||
bool cbInstrVisitType(int argc, char* argv[])
|
||||
|
@ -557,6 +599,8 @@ bool cbInstrVisitType(int argc, char* argv[])
|
|||
dputs(QT_TRANSLATE_NOOP("DBG", "VisitType failed"));
|
||||
return false;
|
||||
}
|
||||
GuiUpdateTypeWidget();
|
||||
dputs(QT_TRANSLATE_NOOP("DBG", "Done!"));
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@ namespace Types
|
|||
{
|
||||
enum Primitive
|
||||
{
|
||||
Unknown,
|
||||
Void,
|
||||
Int8,
|
||||
Uint8,
|
||||
Int16,
|
||||
|
|
|
@ -360,82 +360,6 @@ static void registercommands()
|
|||
dbgcmdnew("EnumTypes", cbInstrEnumTypes, false); //EnumTypes
|
||||
dbgcmdnew("LoadTypes", cbInstrLoadTypes, false); //LoadTypes
|
||||
dbgcmdnew("ParseTypes", cbInstrParseTypes, false); //ParseTypes
|
||||
dbgcmdnew("TestTypeGui", [](int argc, char* argv[])
|
||||
{
|
||||
TYPEDESCRIPTOR type;
|
||||
type.expanded = true;
|
||||
type.name = "struct TEST";
|
||||
type.addr = 0;
|
||||
type.id = 0;
|
||||
type.size = 19;
|
||||
type.callback = [](const TYPEDESCRIPTOR * type, char* dest, size_t* destCount)
|
||||
{
|
||||
if(type->id == -1)
|
||||
{
|
||||
*dest = '\0';
|
||||
return true;
|
||||
}
|
||||
auto value = StringUtils::sprintf("0x%X, %d (size: %d)", type->userdata, type->userdata, type->size);
|
||||
if(*destCount <= value.size())
|
||||
{
|
||||
*destCount = value.size() + 1;
|
||||
return false;
|
||||
}
|
||||
strcpy_s(dest, *destCount, value.c_str());
|
||||
return true;
|
||||
};
|
||||
type.userdata = 0;
|
||||
|
||||
auto t = GuiTypeAddNode(nullptr, &type);
|
||||
type.id = 1;
|
||||
type.addr = 0;
|
||||
type.size = 4;
|
||||
type.name = "int a";
|
||||
type.userdata = (void*)0xA;
|
||||
GuiTypeAddNode(t, &type);
|
||||
type.addr = 4;
|
||||
type.size = 1;
|
||||
type.name = "char b";
|
||||
type.userdata = (void*)0xB;
|
||||
GuiTypeAddNode(t, &type);
|
||||
type.id = 0;
|
||||
type.addr = 5;
|
||||
type.size = 10;
|
||||
type.name = "struct BLUB";
|
||||
type.userdata = 0;
|
||||
auto e = GuiTypeAddNode(t, &type);
|
||||
type.id = 1;
|
||||
type.size = 2;
|
||||
type.name = "short c";
|
||||
type.userdata = (void*)0xC;
|
||||
GuiTypeAddNode(e, &type);
|
||||
type.id = 0;
|
||||
type.addr = 7;
|
||||
type.size = 8;
|
||||
type.name = "int[2]";
|
||||
type.userdata = 0;
|
||||
type.expanded = false;
|
||||
auto d = GuiTypeAddNode(e, &type);
|
||||
type.id = 1;
|
||||
type.size = 4;
|
||||
type.name = "int d[0]";
|
||||
type.userdata = (void*)0xD0;
|
||||
GuiTypeAddNode(d, &type);
|
||||
type.addr = 11;
|
||||
type.size = 4;
|
||||
type.name = "int d[1]";
|
||||
type.userdata = (void*)0xD1;
|
||||
GuiTypeAddNode(d, &type);
|
||||
type.addr = 15;
|
||||
type.size = 4;
|
||||
type.name = "int f";
|
||||
type.userdata = (void*)0xF;
|
||||
type.expanded = true;
|
||||
GuiTypeAddNode(t, &type);
|
||||
|
||||
GuiUpdateAllViews();
|
||||
return true;
|
||||
}, false);
|
||||
|
||||
//plugins
|
||||
dbgcmdnew("StartScylla\1scylla\1imprec", cbDebugStartScylla, false); //start scylla
|
||||
|
|
|
@ -543,6 +543,8 @@ void AppearanceDialog::colorInfoListInit()
|
|||
colorInfoListAppend(tr("Memory Map %1").arg(ArchValue(tr("EIP"), tr("RIP"))), "MemoryMapCipColor", "MemoryMapCipBackgroundColor");
|
||||
colorInfoListAppend(tr("Memory Map Section Text"), "MemoryMapSectionTextColor", "");
|
||||
colorInfoListAppend(tr("Search Highlight Color"), "SearchListViewHighlightColor", "");
|
||||
colorInfoListAppend(tr("Struct primary background"), "StructBackgroundColor", "");
|
||||
colorInfoListAppend(tr("Struct secondary background"), "StructAlternateBackgroundColor", "");
|
||||
|
||||
//dev helper
|
||||
const QMap<QString, QColor>* Colors = &Config()->defaultColors;
|
||||
|
|
|
@ -1,5 +1,10 @@
|
|||
#include "StructWidget.h"
|
||||
#include "ui_StructWidget.h"
|
||||
#include "Configuration.h"
|
||||
#include "MenuBuilder.h"
|
||||
#include "LineEditDialog.h"
|
||||
#include "GotoDialog.h"
|
||||
#include <QFileDialog>
|
||||
|
||||
struct TypeDescriptor
|
||||
{
|
||||
|
@ -17,6 +22,12 @@ StructWidget::StructWidget(QWidget* parent) :
|
|||
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(Config(), SIGNAL(colorsUpdated()), this, SLOT(colorsUpdatedSlot()));
|
||||
connect(Config(), SIGNAL(fontsUpdated()), this, SLOT(fontsUpdatedSlot()));
|
||||
connect(Config(), SIGNAL(shortcutsUpdated()), this, SLOT(shortcutsUpdatedSlot()));
|
||||
colorsUpdatedSlot();
|
||||
fontsUpdatedSlot();
|
||||
setupContextMenu();
|
||||
setupColumns();
|
||||
}
|
||||
|
||||
|
@ -25,13 +36,36 @@ 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()
|
||||
{
|
||||
for(const auto & actionShortcut : actionShortcutPairs)
|
||||
actionShortcut.action->setShortcut(ConfigShortcut(actionShortcut.shortcut));
|
||||
}
|
||||
|
||||
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) << "0x" + ToHexString(dtype.type.size);
|
||||
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;
|
||||
|
@ -48,12 +82,13 @@ void StructWidget::typeClear()
|
|||
|
||||
void StructWidget::typeUpdateWidget()
|
||||
{
|
||||
QTreeWidgetItemIterator it(ui->treeWidget);
|
||||
while(*it)
|
||||
for(QTreeWidgetItemIterator it(ui->treeWidget); *it; ++it)
|
||||
{
|
||||
QString valueStr;
|
||||
QTreeWidgetItem* item = *it;
|
||||
auto type = item->data(0, Qt::UserRole).value<TypeDescriptor>();
|
||||
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] = "";
|
||||
|
@ -73,20 +108,138 @@ void StructWidget::typeUpdateWidget()
|
|||
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(type.type.addr, &data, type.type.size))
|
||||
if(DbgMemRead(addr, &data, type.type.size))
|
||||
valueStr = QString().sprintf("0x%llX, %llu", data, data, data);
|
||||
else if(type.type.addr)
|
||||
valueStr = "???";
|
||||
}
|
||||
item->setText(3, valueStr);
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
void StructWidget::setupColumns()
|
||||
{
|
||||
ui->treeWidget->setColumnWidth(0, 200); //Name
|
||||
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"));
|
||||
}
|
||||
|
||||
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)).toUtf8().constData());
|
||||
}
|
||||
|
||||
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)).toUtf8().constData());
|
||||
}
|
||||
|
||||
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).toUtf8().constData());
|
||||
}
|
||||
|
||||
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).toUtf8().constData());
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
|
|
@ -4,6 +4,9 @@
|
|||
#include <QWidget>
|
||||
#include "Bridge.h"
|
||||
|
||||
class MenuBuilder;
|
||||
class GotoDialog;
|
||||
|
||||
namespace Ui
|
||||
{
|
||||
class StructWidget;
|
||||
|
@ -18,14 +21,36 @@ public:
|
|||
~StructWidget();
|
||||
|
||||
public slots:
|
||||
void colorsUpdatedSlot();
|
||||
void fontsUpdatedSlot();
|
||||
void shortcutsUpdatedSlot();
|
||||
|
||||
void typeAddNode(void* parent, const TYPEDESCRIPTOR* type);
|
||||
void typeClear();
|
||||
void typeUpdateWidget();
|
||||
|
||||
private:
|
||||
Ui::StructWidget* ui;
|
||||
MenuBuilder* mMenuBuilder;
|
||||
GotoDialog* mGotoDialog = nullptr;
|
||||
|
||||
void setupColumns();
|
||||
void setupContextMenu();
|
||||
|
||||
private slots:
|
||||
void on_treeWidget_customContextMenuRequested(const QPoint & pos);
|
||||
|
||||
void followDumpSlot();
|
||||
void clearSlot();
|
||||
void removeSlot();
|
||||
void visitSlot();
|
||||
void loadJsonSlot();
|
||||
void parseFileSlot();
|
||||
void changeAddrSlot();
|
||||
void refreshSlot();
|
||||
|
||||
protected:
|
||||
#include "ActionHelpers.h"
|
||||
};
|
||||
|
||||
#endif // STRUCTWIDGET_H
|
||||
|
|
|
@ -10,9 +10,16 @@
|
|||
<height>300</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="contextMenuPolicy">
|
||||
<enum>Qt::DefaultContextMenu</enum>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Struct</string>
|
||||
</property>
|
||||
<property name="windowIcon">
|
||||
<iconset resource="../../resource.qrc">
|
||||
<normaloff>:/icons/images/struct.png</normaloff>:/icons/images/struct.png</iconset>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<property name="spacing">
|
||||
<number>0</number>
|
||||
|
@ -31,10 +38,8 @@
|
|||
</property>
|
||||
<item>
|
||||
<widget class="QTreeWidget" name="treeWidget">
|
||||
<property name="font">
|
||||
<font>
|
||||
<family>Lucida Console</family>
|
||||
</font>
|
||||
<property name="contextMenuPolicy">
|
||||
<enum>Qt::CustomContextMenu</enum>
|
||||
</property>
|
||||
<property name="alternatingRowColors">
|
||||
<bool>true</bool>
|
||||
|
@ -69,6 +74,8 @@
|
|||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<resources>
|
||||
<include location="../../resource.qrc"/>
|
||||
</resources>
|
||||
<connections/>
|
||||
</ui>
|
||||
|
|
|
@ -187,6 +187,8 @@ Configuration::Configuration() : QObject(), noMoreMsgbox(false)
|
|||
defaultColors.insert("MemoryMapCipBackgroundColor", QColor("#000000"));
|
||||
defaultColors.insert("MemoryMapSectionTextColor", QColor("#8B671F"));
|
||||
defaultColors.insert("SearchListViewHighlightColor", QColor("#FF0000"));
|
||||
defaultColors.insert("StructBackgroundColor", QColor("#FFF8F0"));
|
||||
defaultColors.insert("StructAlternateBackgroundColor", QColor("#DCD9CF"));
|
||||
|
||||
//bool settings
|
||||
QMap<QString, bool> disassemblyBool;
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 586 B |
Binary file not shown.
After Width: | Height: | Size: 759 B |
Binary file not shown.
After Width: | Height: | Size: 455 B |
Binary file not shown.
After Width: | Height: | Size: 811 B |
|
@ -270,5 +270,9 @@
|
|||
<file>images/breakpoint_module_add.png</file>
|
||||
<file>images/importsettings.png</file>
|
||||
<file>images/initscript.png</file>
|
||||
<file>images/struct.png</file>
|
||||
<file>images/removestruct.png</file>
|
||||
<file>images/visitstruct.png</file>
|
||||
<file>images/structaddr.png</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
|
|
Loading…
Reference in New Issue