Merge branch 'graph' into development
# Conflicts: # src/gui/resource.qrc
This commit is contained in:
commit
cff67aa8ff
|
|
@ -12,14 +12,15 @@ typedef struct
|
|||
bool terminal; //node is a RET
|
||||
bool split; //node is a split (brtrue points to the next node)
|
||||
void* userdata; //user data
|
||||
ListOf(duint) exits; //exits (including brtrue and brfalse)
|
||||
ListInfo exits; //exits (including brtrue and brfalse, duint)
|
||||
ListInfo data; //block data
|
||||
} BridgeCFNodeList;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
duint entryPoint; //graph entry point
|
||||
void* userdata; //user data
|
||||
ListOf(BridgeCFNodeList) nodes; //graph nodes
|
||||
ListInfo nodes; //graph nodes (BridgeCFNodeList)
|
||||
} BridgeCFGraphList;
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
|
@ -27,6 +28,7 @@ typedef struct
|
|||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
#include <utility>
|
||||
|
||||
struct BridgeCFNode
|
||||
{
|
||||
|
|
@ -40,10 +42,11 @@ struct BridgeCFNode
|
|||
bool split; //node is a split (brtrue points to the next node)
|
||||
void* userdata; //user data
|
||||
std::vector<duint> exits; //exits (including brtrue and brfalse)
|
||||
std::vector<unsigned char> data; //block data
|
||||
|
||||
explicit BridgeCFNode(BridgeCFNodeList* nodeList)
|
||||
explicit BridgeCFNode(BridgeCFNodeList* nodeList, bool freedata = true)
|
||||
{
|
||||
if(!nodeList || !nodeList->exits || nodeList->exits->size != nodeList->exits->count * sizeof(duint))
|
||||
if(!nodeList)
|
||||
__debugbreak();
|
||||
parentGraph = nodeList->parentGraph;
|
||||
start = nodeList->start;
|
||||
|
|
@ -54,10 +57,10 @@ struct BridgeCFNode
|
|||
terminal = nodeList->terminal;
|
||||
split = nodeList->split;
|
||||
userdata = nodeList->userdata;
|
||||
auto data = (duint*)nodeList->exits->data;
|
||||
exits.resize(nodeList->exits->count);
|
||||
for(int i = 0; i < nodeList->exits->count; i++)
|
||||
exits[i] = data[i];
|
||||
if(!BridgeList<duint>::ToVector(&nodeList->exits, exits, freedata))
|
||||
__debugbreak();
|
||||
if(!BridgeList<unsigned char>::ToVector(&nodeList->data, data, freedata))
|
||||
__debugbreak();
|
||||
}
|
||||
|
||||
explicit BridgeCFNode(duint parentGraph, duint start, duint end)
|
||||
|
|
@ -77,6 +80,23 @@ struct BridgeCFNode
|
|||
: BridgeCFNode(0, 0, 0)
|
||||
{
|
||||
}
|
||||
|
||||
BridgeCFNodeList ToNodeList() const
|
||||
{
|
||||
BridgeCFNodeList out;
|
||||
out.parentGraph = parentGraph;
|
||||
out.start = start;
|
||||
out.end = end;
|
||||
out.brtrue = brtrue;
|
||||
out.brfalse = brfalse;
|
||||
out.icount = icount;
|
||||
out.terminal = terminal;
|
||||
out.split = split;
|
||||
out.userdata = userdata;
|
||||
BridgeList<duint>::CopyData(&out.exits, exits);
|
||||
BridgeList<unsigned char>::CopyData(&out.data, data);
|
||||
return std::move(out);
|
||||
}
|
||||
};
|
||||
|
||||
struct BridgeCFGraph
|
||||
|
|
@ -86,15 +106,17 @@ struct BridgeCFGraph
|
|||
std::unordered_map<duint, BridgeCFNode> nodes; //CFNode.start -> CFNode
|
||||
std::unordered_map<duint, std::unordered_set<duint>> parents; //CFNode.start -> parents
|
||||
|
||||
explicit BridgeCFGraph(BridgeCFGraphList* graphList)
|
||||
explicit BridgeCFGraph(BridgeCFGraphList* graphList, bool freedata = true)
|
||||
{
|
||||
if(!graphList || !graphList->nodes || graphList->nodes->size != graphList->nodes->count * sizeof(BridgeCFNode))
|
||||
if(!graphList || graphList->nodes.size != graphList->nodes.count * sizeof(BridgeCFNodeList))
|
||||
__debugbreak();
|
||||
entryPoint = graphList->entryPoint;
|
||||
userdata = graphList->userdata;
|
||||
auto data = (BridgeCFNode*)graphList->nodes->data;
|
||||
for(int i = 0; i < graphList->nodes->count; i++)
|
||||
AddNode(data[i]);
|
||||
auto data = (BridgeCFNodeList*)graphList->nodes.data;
|
||||
for(int i = 0; i < graphList->nodes.count; i++)
|
||||
AddNode(BridgeCFNode(&data[i], freedata));
|
||||
if(freedata && data)
|
||||
BridgeFree(data);
|
||||
}
|
||||
|
||||
explicit BridgeCFGraph(duint entryPoint)
|
||||
|
|
@ -124,6 +146,19 @@ struct BridgeCFGraph
|
|||
else
|
||||
found->second.insert(parent);
|
||||
}
|
||||
|
||||
BridgeCFGraphList ToGraphList() const
|
||||
{
|
||||
BridgeCFGraphList out;
|
||||
out.entryPoint = entryPoint;
|
||||
out.userdata = userdata;
|
||||
std::vector<BridgeCFNodeList> nodeList;
|
||||
nodeList.reserve(nodes.size());
|
||||
for(const auto & nodeIt : nodes)
|
||||
nodeList.push_back(nodeIt.second.ToNodeList());
|
||||
BridgeList<BridgeCFNodeList>::CopyData(&out.nodes, nodeList);
|
||||
return std::move(out);
|
||||
}
|
||||
};
|
||||
|
||||
#endif //__cplusplus
|
||||
|
|
|
|||
|
|
@ -120,6 +120,18 @@ public:
|
|||
return true;
|
||||
}
|
||||
|
||||
static bool ToVector(const ListInfo* listInfo, std::vector<Type> & listData, bool freedata = true)
|
||||
{
|
||||
if(!listInfo || listInfo->size != listInfo->count * sizeof(Type) || (listInfo->count && !listInfo->data))
|
||||
return false;
|
||||
listData.resize(listInfo->count);
|
||||
for(int i = 0; i < listInfo->count; i++)
|
||||
listData[i] = ((Type*)listInfo->data)[i];
|
||||
if(freedata && listInfo->data)
|
||||
BridgeFree(listInfo->data);
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
ListInfo _listInfo;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -25,8 +25,6 @@ typedef unsigned long duint;
|
|||
typedef signed long dsint;
|
||||
#endif //_WIN64
|
||||
|
||||
#include "bridgegraph.h"
|
||||
|
||||
#ifndef BRIDGE_IMPEXP
|
||||
#ifdef BUILD_BRIDGE
|
||||
#define BRIDGE_IMPEXP __declspec(dllexport)
|
||||
|
|
@ -57,6 +55,17 @@ BRIDGE_IMPEXP bool BridgeSettingFlush();
|
|||
BRIDGE_IMPEXP bool BridgeSettingRead(int* errorLine);
|
||||
BRIDGE_IMPEXP int BridgeGetDbgVersion();
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#include "bridgegraph.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
//Debugger defines
|
||||
#define MAX_LABEL_SIZE 256
|
||||
#define MAX_COMMENT_SIZE 512
|
||||
|
|
|
|||
|
|
@ -33,6 +33,8 @@ void RecursiveAnalysis::SetMarkers()
|
|||
duint icount = 0;
|
||||
for(const auto & node : function.nodes)
|
||||
{
|
||||
if(!inRange(node.second.start))
|
||||
continue;
|
||||
icount += node.second.icount;
|
||||
start = min(node.second.start, start);
|
||||
end = max(node.second.end, end);
|
||||
|
|
@ -64,11 +66,18 @@ void RecursiveAnalysis::analyzeFunction(duint entryPoint)
|
|||
{
|
||||
auto start = queue.front();
|
||||
queue.pop();
|
||||
if(visited.count(start) || !inRange(start)) //already visited or out of range
|
||||
if(visited.count(start)) //already visited
|
||||
continue;
|
||||
visited.insert(start);
|
||||
|
||||
CFNode node(graph.entryPoint, start, start);
|
||||
|
||||
if(!inRange(start)) //out of range
|
||||
{
|
||||
graph.AddNode(node);
|
||||
continue;
|
||||
}
|
||||
|
||||
while(true)
|
||||
{
|
||||
node.icount++;
|
||||
|
|
@ -151,7 +160,7 @@ void RecursiveAnalysis::analyzeFunction(duint entryPoint)
|
|||
addr += size;
|
||||
}
|
||||
}
|
||||
//third pass: correct the parents + add brtrue and brfalse to the exits
|
||||
//third pass: correct the parents + add brtrue and brfalse to the exits + get data
|
||||
graph.parents.clear();
|
||||
for(auto & nodeIt : graph.nodes)
|
||||
{
|
||||
|
|
@ -162,6 +171,14 @@ void RecursiveAnalysis::analyzeFunction(duint entryPoint)
|
|||
node.exits.push_back(node.brtrue);
|
||||
if(node.brfalse)
|
||||
node.exits.push_back(node.brfalse);
|
||||
if(node.brtrue && !node.brfalse)
|
||||
node.brtrue = 0;
|
||||
if(!node.icount)
|
||||
continue;
|
||||
auto size = node.end - node.start + (mCp.Disassemble(node.end, translateAddr(node.end)) ? mCp.Size() : 1);
|
||||
node.data.resize(size);
|
||||
for(duint i = 0; i < size; i++)
|
||||
node.data[i] = inRange(node.start + i) ? *translateAddr(node.start + i) : 0x90;
|
||||
}
|
||||
mFunctions.push_back(graph);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -65,6 +65,14 @@ public:
|
|||
return result;
|
||||
}
|
||||
|
||||
const CFGraph* GetFunctionGraph(duint entry) const
|
||||
{
|
||||
for(const auto & function : mFunctions)
|
||||
if(function.entryPoint == entry)
|
||||
return &function;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
protected:
|
||||
duint mEntryPoint;
|
||||
std::vector<CFGraph> mFunctions;
|
||||
|
|
|
|||
|
|
@ -27,10 +27,15 @@ static T callFunc(const T* argv, T(*cbFunction)(Ts...), seq<S...>)
|
|||
template<typename... Ts>
|
||||
static bool RegisterEasy(const String & name, duint(*cbFunction)(Ts...))
|
||||
{
|
||||
return ExpressionFunctions::Register(name, sizeof...(Ts), [cbFunction](int argc, duint * argv, void* userdata)
|
||||
{
|
||||
return callFunc(argv, cbFunction, typename gens<sizeof...(Ts)>::type());
|
||||
});
|
||||
auto aliases = StringUtils::Split(name, '\1');
|
||||
if(!ExpressionFunctions::Register(aliases[0], sizeof...(Ts), [cbFunction](int argc, duint * argv, void* userdata)
|
||||
{
|
||||
return callFunc(argv, cbFunction, typename gens<sizeof...(Ts)>::type());
|
||||
}))
|
||||
return false;
|
||||
for(size_t i = 1; i < aliases.size(); i++)
|
||||
ExpressionFunctions::RegisterAlias(aliases[0], aliases[i]);
|
||||
return true;
|
||||
}
|
||||
|
||||
void ExpressionFunctions::Init()
|
||||
|
|
@ -47,6 +52,10 @@ void ExpressionFunctions::Init()
|
|||
RegisterEasy("mod.size", ModSizeFromAddr);
|
||||
RegisterEasy("mod.hash", ModHashFromAddr);
|
||||
RegisterEasy("mod.entry", ModEntryFromAddr);
|
||||
|
||||
RegisterEasy("disasm.sel\1dis.sel", disasmsel);
|
||||
RegisterEasy("dump.sel", dumpsel);
|
||||
RegisterEasy("stack.sel", stacksel);
|
||||
}
|
||||
|
||||
bool ExpressionFunctions::Register(const String & name, int argc, CBEXPRESSIONFUNCTION cbFunction, void* userdata)
|
||||
|
|
@ -65,13 +74,28 @@ bool ExpressionFunctions::Register(const String & name, int argc, CBEXPRESSIONFU
|
|||
return true;
|
||||
}
|
||||
|
||||
bool ExpressionFunctions::RegisterAlias(const String & name, const String & alias)
|
||||
{
|
||||
EXCLUSIVE_ACQUIRE(LockExpressionFunctions);
|
||||
auto found = mFunctions.find(name);
|
||||
if(found == mFunctions.end())
|
||||
return false;
|
||||
if(!Register(alias, found->second.argc, found->second.cbFunction, found->second.userdata))
|
||||
return false;
|
||||
found->second.aliases.push_back(alias);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ExpressionFunctions::Unregister(const String & name)
|
||||
{
|
||||
EXCLUSIVE_ACQUIRE(LockExpressionFunctions);
|
||||
auto found = mFunctions.find(name);
|
||||
if(found == mFunctions.end())
|
||||
return false;
|
||||
auto aliases = found->second.aliases;
|
||||
mFunctions.erase(found);
|
||||
for(const auto & alias : found->second.aliases)
|
||||
Unregister(alias);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ public:
|
|||
|
||||
static void Init();
|
||||
static bool Register(const String & name, int argc, CBEXPRESSIONFUNCTION cbFunction, void* userdata = nullptr);
|
||||
static bool RegisterAlias(const String & name, const String & alias);
|
||||
static bool Unregister(const String & name);
|
||||
static bool Call(const String & name, std::vector<duint> & argv, duint & result);
|
||||
static bool GetArgc(const String & name, int & argc);
|
||||
|
|
@ -20,6 +21,7 @@ private:
|
|||
int argc;
|
||||
CBEXPRESSIONFUNCTION cbFunction;
|
||||
void* userdata;
|
||||
std::vector<String> aliases;
|
||||
};
|
||||
|
||||
static bool isValidName(const String & name);
|
||||
|
|
|
|||
|
|
@ -24,4 +24,26 @@ namespace Exprfunc
|
|||
{
|
||||
return ModGetParty(addr);
|
||||
}
|
||||
|
||||
static duint selstart(int hWindow)
|
||||
{
|
||||
SELECTIONDATA selection;
|
||||
GuiSelectionGet(hWindow, &selection);
|
||||
return selection.start;
|
||||
}
|
||||
|
||||
duint disasmsel()
|
||||
{
|
||||
return selstart(GUI_DISASSEMBLY);
|
||||
}
|
||||
|
||||
duint dumpsel()
|
||||
{
|
||||
return selstart(GUI_DUMP);
|
||||
}
|
||||
|
||||
duint stacksel()
|
||||
{
|
||||
return selstart(GUI_STACK);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,4 +8,8 @@ namespace Exprfunc
|
|||
duint srcdisp(duint addr);
|
||||
|
||||
duint modparty(duint addr);
|
||||
|
||||
duint disasmsel();
|
||||
duint dumpsel();
|
||||
duint stacksel();
|
||||
}
|
||||
|
|
@ -2817,3 +2817,32 @@ CMDRESULT cbInstrExinfo(int argc, char* argv[])
|
|||
}
|
||||
return STATUS_CONTINUE;
|
||||
}
|
||||
|
||||
CMDRESULT cbInstrGraph(int argc, char* argv[])
|
||||
{
|
||||
duint entry;
|
||||
if(argc < 2 || !valfromstring(argv[1], &entry))
|
||||
entry = GetContextDataEx(hActiveThread, UE_CIP);
|
||||
duint start, size, sel = entry;
|
||||
if(FunctionGet(entry, &start))
|
||||
entry = start;
|
||||
auto base = MemFindBaseAddr(entry, &size);
|
||||
if(!base || !MemIsValidReadPtr(entry))
|
||||
{
|
||||
dprintf("Invalid memory address " fhex "!\n", entry);
|
||||
return STATUS_ERROR;
|
||||
}
|
||||
RecursiveAnalysis analysis(base, size, entry, 0);
|
||||
analysis.Analyse();
|
||||
auto graph = analysis.GetFunctionGraph(entry);
|
||||
if(!graph)
|
||||
{
|
||||
dputs("No graph generated...");
|
||||
return STATUS_ERROR;
|
||||
}
|
||||
auto graphList = graph->ToGraphList();
|
||||
GuiGraphAt(sel);
|
||||
GuiLoadGraph(&graphList);
|
||||
GuiUpdateAllViews();
|
||||
return STATUS_CONTINUE;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -101,5 +101,6 @@ CMDRESULT cbInstrEnableGuiUpdate(int argc, char* argv[]);
|
|||
CMDRESULT cbInstrExhandlers(int argc, char* argv[]);
|
||||
CMDRESULT cbInstrInstrUndo(int argc, char* argv[]);
|
||||
CMDRESULT cbInstrExinfo(int argc, char* argv[]);
|
||||
CMDRESULT cbInstrGraph(int argc, char* argv[]);
|
||||
|
||||
#endif // _INSTRUCTION_H
|
||||
|
|
|
|||
|
|
@ -308,6 +308,7 @@ static void registercommands()
|
|||
dbgcmdnew("analrecur\1analr", cbInstrAnalrecur, true); //analyze a single function
|
||||
dbgcmdnew("analxrefs\1analx", cbInstrAnalxrefs, true); //analyze xrefs
|
||||
dbgcmdnew("analadv", cbInstrAnalyseadv, true); //analyze xref,function and data
|
||||
dbgcmdnew("graph", cbInstrGraph, true); //graph function
|
||||
}
|
||||
|
||||
static bool cbCommandProvider(char* cmd, int maxlen)
|
||||
|
|
|
|||
|
|
@ -228,109 +228,8 @@ protected:
|
|||
void invalidateCachedFont();
|
||||
|
||||
//action helpers
|
||||
private:
|
||||
struct ActionShortcut
|
||||
{
|
||||
QAction* action;
|
||||
QString shortcut;
|
||||
|
||||
ActionShortcut(QAction* action, const char* shortcut)
|
||||
: action(action),
|
||||
shortcut(shortcut)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
std::vector<ActionShortcut> actionShortcutPairs;
|
||||
|
||||
inline QAction* connectAction(QAction* action, const char* slot)
|
||||
{
|
||||
connect(action, SIGNAL(triggered(bool)), this, slot);
|
||||
return action;
|
||||
}
|
||||
|
||||
inline QAction* connectAction(QAction* action, QActionLambda::TriggerCallback callback)
|
||||
{
|
||||
auto lambda = new QActionLambda(action->parent(), callback);
|
||||
connect(action, SIGNAL(triggered(bool)), lambda, SLOT(triggeredSlot()));
|
||||
return action;
|
||||
}
|
||||
|
||||
inline QAction* connectShortcutAction(QAction* action, const char* shortcut)
|
||||
{
|
||||
actionShortcutPairs.push_back(ActionShortcut(action, shortcut));
|
||||
action->setShortcut(ConfigShortcut(shortcut));
|
||||
action->setShortcutContext(Qt::WidgetShortcut);
|
||||
addAction(action);
|
||||
return action;
|
||||
}
|
||||
|
||||
inline QAction* connectMenuAction(QMenu* menu, QAction* action)
|
||||
{
|
||||
menu->addAction(action);
|
||||
return action;
|
||||
}
|
||||
|
||||
protected:
|
||||
inline QMenu* makeMenu(const QString & title)
|
||||
{
|
||||
return new QMenu(title, this);
|
||||
}
|
||||
|
||||
inline QMenu* makeMenu(const QIcon & icon, const QString & title)
|
||||
{
|
||||
QMenu* menu = new QMenu(title, this);
|
||||
menu->setIcon(icon);
|
||||
return menu;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline QAction* makeAction(const QString & text, T slot)
|
||||
{
|
||||
return connectAction(new QAction(text, this), slot);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline QAction* makeAction(const QIcon & icon, const QString & text, T slot)
|
||||
{
|
||||
return connectAction(new QAction(icon, text, this), slot);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline QAction* makeShortcutAction(const QString & text, T slot, const char* shortcut)
|
||||
{
|
||||
return connectShortcutAction(makeAction(text, slot), shortcut);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline QAction* makeShortcutAction(const QIcon & icon, const QString & text, T slot, const char* shortcut)
|
||||
{
|
||||
return connectShortcutAction(makeAction(icon, text, slot), shortcut);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline QAction* makeMenuAction(QMenu* menu, const QString & text, T slot)
|
||||
{
|
||||
return connectMenuAction(menu, makeAction(text, slot));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline QAction* makeMenuAction(QMenu* menu, const QIcon & icon, const QString & text, T slot)
|
||||
{
|
||||
return connectMenuAction(menu, makeAction(icon, text, slot));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline QAction* makeShortcutMenuAction(QMenu* menu, const QString & text, T slot, const char* shortcut)
|
||||
{
|
||||
return connectShortcutAction(makeMenuAction(menu, text, slot), shortcut);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline QAction* makeShortcutMenuAction(QMenu* menu, const QIcon & icon, const QString & text, T slot, const char* shortcut)
|
||||
{
|
||||
return connectShortcutAction(makeMenuAction(menu, icon, text, slot), shortcut);
|
||||
}
|
||||
#include "ActionHelpers.h"
|
||||
};
|
||||
|
||||
#endif // ABSTRACTTABLEVIEW_H
|
||||
|
|
|
|||
|
|
@ -277,7 +277,7 @@ void* Bridge::processMessage(GUIMSG type, void* param1, void* param2)
|
|||
byte_t wBuffer[16];
|
||||
if(!DbgMemRead(parVA, wBuffer, 16))
|
||||
return 0;
|
||||
QBeaEngine disasm(-1);
|
||||
QBeaEngine disasm(int(ConfigUint("Disassembler", "MaxModuleSize")));
|
||||
Instruction_t instr = disasm.DisassembleAt(wBuffer, 16, 0, parVA);
|
||||
RichTextPainter::List richText;
|
||||
CapstoneTokenizer::TokenToRichText(instr.tokens, richText, 0);
|
||||
|
|
@ -578,6 +578,22 @@ void* Bridge::processMessage(GUIMSG type, void* param1, void* param2)
|
|||
case GUI_UPDATE_WATCH_VIEW:
|
||||
emit updateWatch();
|
||||
break;
|
||||
|
||||
case GUI_LOAD_GRAPH:
|
||||
{
|
||||
BridgeResult result;
|
||||
emit loadGraph((BridgeCFGraphList*)param1);
|
||||
result.Wait();
|
||||
}
|
||||
break;
|
||||
|
||||
case GUI_GRAPH_AT:
|
||||
emit graphAt(duint(param1));
|
||||
break;
|
||||
|
||||
case GUI_UPDATE_GRAPH_VIEW:
|
||||
emit updateGraph();
|
||||
break;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -124,6 +124,9 @@ signals:
|
|||
void focusDump();
|
||||
void focusStack();
|
||||
void updateWatch();
|
||||
void loadGraph(BridgeCFGraphList* graph);
|
||||
void graphAt(duint addr);
|
||||
void updateGraph();
|
||||
|
||||
private:
|
||||
QMutex* mBridgeMutex;
|
||||
|
|
|
|||
|
|
@ -327,6 +327,7 @@ void CPUDisassembly::setupRightClickContextMenu()
|
|||
});
|
||||
|
||||
mMenuBuilder->addMenu(makeMenu(DIcon("snowman.png"), tr("Decompile")), decompileMenu);
|
||||
mMenuBuilder->addAction(makeShortcutAction(DIcon("graph.png"), tr("Graph"), SLOT(graphSlot()), "ActionGraph"));
|
||||
|
||||
mMenuBuilder->addMenu(makeMenu(DIcon("help.png"), tr("Help on Symbolic Name")), [this](QMenu * menu)
|
||||
{
|
||||
|
|
@ -1653,3 +1654,9 @@ void CPUDisassembly::setEncodeTypeSlot()
|
|||
mDisasm->getEncodeMap()->setDataType(rvaToVa(getSelectionStart()), (ENCODETYPE)pAction->data().toUInt());
|
||||
GuiUpdateDisassemblyView();
|
||||
}
|
||||
|
||||
void CPUDisassembly::graphSlot()
|
||||
{
|
||||
DbgCmdExec(QString("graph %1").arg(ToPtrString(rvaToVa(getSelectionStart()))).toUtf8().constData());
|
||||
emit displayGraphWidget();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@ signals:
|
|||
void decompileAt(dsint start, dsint end);
|
||||
void displaySnowmanWidget();
|
||||
void displayLogWidget();
|
||||
void displayGraphWidget();
|
||||
|
||||
public slots:
|
||||
void toggleInt3BPActionSlot();
|
||||
|
|
@ -102,6 +103,7 @@ public slots:
|
|||
void removeAnalysisModuleSlot();
|
||||
void setEncodeTypeSlot();
|
||||
void setEncodeTypeRangeSlot();
|
||||
void graphSlot();
|
||||
|
||||
protected:
|
||||
void paintEvent(QPaintEvent* event);
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,289 @@
|
|||
#ifndef DISASSEMBLERGRAPHVIEW_H
|
||||
#define DISASSEMBLERGRAPHVIEW_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QWidget>
|
||||
#include <QAbstractScrollArea>
|
||||
#include <QPaintEvent>
|
||||
#include <QTimer>
|
||||
#include <QSize>
|
||||
#include <QResizeEvent>
|
||||
#include <tuple>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <queue>
|
||||
#include <algorithm>
|
||||
#include <QMutex>
|
||||
#include "Bridge.h"
|
||||
#include "QBeaEngine.h"
|
||||
#include "CachedFontMetrics.h"
|
||||
#include "MenuBuilder.h"
|
||||
|
||||
class DisassemblerGraphView : public QAbstractScrollArea
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
struct DisassemblerBlock;
|
||||
|
||||
struct Point
|
||||
{
|
||||
int row; //point[0]
|
||||
int col; //point[1]
|
||||
int index; //point[2]
|
||||
};
|
||||
|
||||
struct DisassemblerEdge
|
||||
{
|
||||
QColor color;
|
||||
DisassemblerBlock* dest;
|
||||
std::vector<Point> points;
|
||||
int start_index = 0;
|
||||
|
||||
QPolygonF polyline;
|
||||
QPolygonF arrow;
|
||||
|
||||
void addPoint(int row, int col, int index = 0)
|
||||
{
|
||||
Point point;
|
||||
point.row = row;
|
||||
point.col = col;
|
||||
point.index = 0;
|
||||
this->points.push_back(point);
|
||||
if(int(this->points.size()) > 1)
|
||||
this->points[this->points.size() - 2].index = index;
|
||||
}
|
||||
};
|
||||
|
||||
struct Token
|
||||
{
|
||||
int start; //token[0]
|
||||
int length; //token[1]
|
||||
QString type; //token[2]
|
||||
duint addr; //token[3]
|
||||
QString name; //token[4]
|
||||
};
|
||||
|
||||
struct HighlightToken
|
||||
{
|
||||
QString type; //highlight_token[0]
|
||||
duint addr; //highlight_token[1]
|
||||
QString name; //highlight_token[2]
|
||||
|
||||
bool equalsToken(const Token & token)
|
||||
{
|
||||
return this->type == token.type &&
|
||||
this->addr == token.addr &&
|
||||
this->name == token.name;
|
||||
}
|
||||
|
||||
static HighlightToken* fromToken(const Token & token)
|
||||
{
|
||||
//TODO: memory leaks
|
||||
auto result = new HighlightToken();
|
||||
result->type = token.type;
|
||||
result->addr = token.addr;
|
||||
result->name = token.name;
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
struct Text
|
||||
{
|
||||
std::vector<RichTextPainter::List> lines;
|
||||
|
||||
Text() {}
|
||||
|
||||
Text(const QString & text, QColor color)
|
||||
{
|
||||
RichTextPainter::List richText;
|
||||
RichTextPainter::CustomRichText_t rt;
|
||||
rt.highlight = false;
|
||||
rt.flags = RichTextPainter::FlagColor;
|
||||
rt.text = text;
|
||||
rt.textColor = color;
|
||||
richText.push_back(rt);
|
||||
lines.push_back(richText);
|
||||
}
|
||||
|
||||
Text(const RichTextPainter::List & richText)
|
||||
{
|
||||
lines.push_back(richText);
|
||||
}
|
||||
|
||||
QString ToQString() const
|
||||
{
|
||||
QString result;
|
||||
for(auto & line : lines)
|
||||
{
|
||||
for(auto & t : line)
|
||||
{
|
||||
result += t.text;
|
||||
}
|
||||
}
|
||||
return std::move(result);
|
||||
}
|
||||
};
|
||||
|
||||
struct Instr
|
||||
{
|
||||
duint addr = 0;
|
||||
Text text;
|
||||
std::vector<unsigned char> opcode; //instruction bytes
|
||||
};
|
||||
|
||||
struct Block
|
||||
{
|
||||
Text header_text;
|
||||
std::vector<Instr> instrs;
|
||||
std::vector<duint> exits;
|
||||
duint entry = 0;
|
||||
duint true_path = 0;
|
||||
duint false_path = 0;
|
||||
|
||||
void print() const
|
||||
{
|
||||
puts("----BLOCK---");
|
||||
printf("header_text: %s\n", header_text.ToQString().toUtf8().constData());
|
||||
puts("exits:");
|
||||
for(auto exit : exits)
|
||||
printf("%X ", exit);
|
||||
puts("\n--ENDBLOCK--");
|
||||
}
|
||||
};
|
||||
|
||||
struct DisassemblerBlock
|
||||
{
|
||||
DisassemblerBlock() {}
|
||||
explicit DisassemblerBlock(Block & block)
|
||||
: block(block) {}
|
||||
|
||||
void print() const
|
||||
{
|
||||
block.print();
|
||||
}
|
||||
|
||||
Block block;
|
||||
std::vector<DisassemblerEdge> edges;
|
||||
std::vector<duint> incoming;
|
||||
std::vector<duint> new_exits;
|
||||
|
||||
qreal x = 0.0;
|
||||
qreal y = 0.0;
|
||||
int width = 0;
|
||||
int height = 0;
|
||||
int col = 0;
|
||||
int col_count = 0;
|
||||
int row = 0;
|
||||
int row_count = 0;
|
||||
};
|
||||
|
||||
struct Function
|
||||
{
|
||||
bool ready;
|
||||
duint entry;
|
||||
duint update_id;
|
||||
std::vector<Block> blocks;
|
||||
};
|
||||
|
||||
struct Analysis
|
||||
{
|
||||
duint entry = 0;
|
||||
std::unordered_map<duint, Function> functions;
|
||||
bool ready = false;
|
||||
duint update_id = 0;
|
||||
QString status = "Analyzing...";
|
||||
|
||||
bool find_instr(duint addr, duint & func, duint & instr)
|
||||
{
|
||||
//TODO implement
|
||||
Q_UNUSED(addr);
|
||||
Q_UNUSED(func);
|
||||
Q_UNUSED(instr);
|
||||
return false;
|
||||
}
|
||||
|
||||
//dummy class
|
||||
};
|
||||
|
||||
DisassemblerGraphView(QWidget* parent = nullptr);
|
||||
void initFont();
|
||||
void adjustSize(int width, int height);
|
||||
void resizeEvent(QResizeEvent* event);
|
||||
duint get_cursor_pos();
|
||||
void set_cursor_pos(duint addr);
|
||||
std::tuple<duint, duint> get_selection_range();
|
||||
void set_selection_range(std::tuple<duint, duint> range);
|
||||
void copy_address();
|
||||
//void analysis_thread_proc();
|
||||
//void closeRequest();
|
||||
void paintEvent(QPaintEvent* event);
|
||||
bool isMouseEventInBlock(QMouseEvent* event);
|
||||
duint getInstrForMouseEvent(QMouseEvent* event);
|
||||
bool getTokenForMouseEvent(QMouseEvent* event, Token & token);
|
||||
bool find_instr(duint addr, Instr & instr);
|
||||
void mousePressEvent(QMouseEvent* event);
|
||||
void mouseMoveEvent(QMouseEvent* event);
|
||||
void mouseReleaseEvent(QMouseEvent* event);
|
||||
void mouseDoubleClickEvent(QMouseEvent* event);
|
||||
void prepareGraphNode(DisassemblerBlock & block);
|
||||
void adjustGraphLayout(DisassemblerBlock & block, int col, int row);
|
||||
void computeGraphLayout(DisassemblerBlock & block);
|
||||
void setupContextMenu();
|
||||
|
||||
template<typename T>
|
||||
using Matrix = std::vector<std::vector<T>>;
|
||||
using EdgesVector = Matrix<std::vector<bool>>;
|
||||
bool isEdgeMarked(EdgesVector & edges, int row, int col, int index);
|
||||
void markEdge(EdgesVector & edges, int row, int col, int index);
|
||||
int findHorizEdgeIndex(EdgesVector & edges, int row, int min_col, int max_col);
|
||||
int findVertEdgeIndex(EdgesVector & edges, int col, int min_row, int max_row);
|
||||
DisassemblerEdge routeEdge(EdgesVector & horiz_edges, EdgesVector & vert_edges, Matrix<bool> & edge_valid, DisassemblerBlock & start, DisassemblerBlock & end, QColor color);
|
||||
void renderFunction(Function & func);
|
||||
void show_cur_instr();
|
||||
bool navigate(duint addr);
|
||||
void fontChanged();
|
||||
|
||||
public slots:
|
||||
void updateTimerEvent();
|
||||
void loadGraphSlot(BridgeCFGraphList* graph);
|
||||
void graphAtSlot(duint addr);
|
||||
void updateGraphSlot();
|
||||
void followDisassemblerSlot();
|
||||
|
||||
signals:
|
||||
void showCpu();
|
||||
|
||||
private:
|
||||
QString status;
|
||||
Analysis analysis;
|
||||
duint function;
|
||||
QTimer* updateTimer;
|
||||
int baseline;
|
||||
qreal charWidth;
|
||||
int charHeight;
|
||||
int charOffset;
|
||||
int width;
|
||||
int height;
|
||||
int renderWidth;
|
||||
int renderHeight;
|
||||
int renderXOfs;
|
||||
int renderYOfs;
|
||||
duint cur_instr;
|
||||
int scroll_base_x;
|
||||
int scroll_base_y;
|
||||
duint update_id;
|
||||
bool scroll_mode;
|
||||
bool ready;
|
||||
int* desired_pos;
|
||||
std::unordered_map<duint, DisassemblerBlock> blocks;
|
||||
HighlightToken* highlight_token;
|
||||
std::vector<int> col_edge_x;
|
||||
std::vector<int> row_edge_y;
|
||||
CachedFontMetrics* mFontMetrics;
|
||||
MenuBuilder* mMenuBuilder;
|
||||
|
||||
protected:
|
||||
#include "ActionHelpers.h"
|
||||
};
|
||||
|
||||
#endif // DISASSEMBLERGRAPHVIEW_H
|
||||
|
|
@ -159,7 +159,11 @@ MainWindow::MainWindow(QWidget* parent)
|
|||
mHandlesView = new HandlesView(this);
|
||||
mHandlesView->setWindowTitle(tr("Handles"));
|
||||
mHandlesView->setWindowIcon(DIcon("handles.png"));
|
||||
mHandlesView->hide();
|
||||
|
||||
// Graph view
|
||||
mGraphView = new DisassemblerGraphView(this);
|
||||
mGraphView->setWindowTitle(tr("Graph"));
|
||||
mGraphView->setWindowIcon(DIcon("graph.png"));
|
||||
|
||||
// Create the tab widget
|
||||
mTabWidget = new MHTabWidget();
|
||||
|
|
@ -167,6 +171,8 @@ MainWindow::MainWindow(QWidget* parent)
|
|||
// Add all widgets to the list
|
||||
mWidgetList.push_back(mCpuWidget);
|
||||
mWidgetNativeNameList.push_back("CPUTab");
|
||||
mWidgetList.push_back(mGraphView);
|
||||
mWidgetNativeNameList.push_back("GraphTab");
|
||||
mWidgetList.push_back(mLogView);
|
||||
mWidgetNativeNameList.push_back("LogTab");
|
||||
mWidgetList.push_back(mNotesManager);
|
||||
|
|
@ -254,6 +260,7 @@ MainWindow::MainWindow(QWidget* parent)
|
|||
connect(mSymbolView, SIGNAL(showCpu()), this, SLOT(displayCpuWidget()));
|
||||
connect(mSymbolView, SIGNAL(showReferences()), this, SLOT(displayReferencesWidget()));
|
||||
connect(mReferenceManager, SIGNAL(showCpu()), this, SLOT(displayCpuWidget()));
|
||||
connect(mGraphView, SIGNAL(showCpu()), this, SLOT(displayCpuWidget()));
|
||||
connect(ui->actionReferences, SIGNAL(triggered()), this, SLOT(displayReferencesWidget()));
|
||||
connect(ui->actionThreads, SIGNAL(triggered()), this, SLOT(displayThreadsWidget()));
|
||||
connect(ui->actionSettings, SIGNAL(triggered()), this, SLOT(openSettings()));
|
||||
|
|
@ -281,12 +288,14 @@ MainWindow::MainWindow(QWidget* parent)
|
|||
connect(ui->actionNotes, SIGNAL(triggered()), this, SLOT(displayNotesWidget()));
|
||||
connect(ui->actionSnowman, SIGNAL(triggered()), this, SLOT(displaySnowmanWidget()));
|
||||
connect(ui->actionHandles, SIGNAL(triggered()), this, SLOT(displayHandlesWidget()));
|
||||
connect(ui->actionGraph, SIGNAL(triggered()), this, SLOT(displayGraphWidget()));
|
||||
|
||||
connect(mCpuWidget->getDisasmWidget(), SIGNAL(updateWindowTitle(QString)), this, SLOT(updateWindowTitleSlot(QString)));
|
||||
connect(mCpuWidget->getDisasmWidget(), SIGNAL(displayReferencesWidget()), this, SLOT(displayReferencesWidget()));
|
||||
connect(mCpuWidget->getDisasmWidget(), SIGNAL(displaySourceManagerWidget()), this, SLOT(displaySourceViewWidget()));
|
||||
connect(mCpuWidget->getDisasmWidget(), SIGNAL(displaySnowmanWidget()), this, SLOT(displaySnowmanWidget()));
|
||||
connect(mCpuWidget->getDisasmWidget(), SIGNAL(displayLogWidget()), this, SLOT(displayLogWidget()));
|
||||
connect(mCpuWidget->getDisasmWidget(), SIGNAL(displayGraphWidget()), this, SLOT(displayGraphWidget()));
|
||||
|
||||
connect(mCpuWidget->getDisasmWidget(), SIGNAL(showPatches()), this, SLOT(patchWindow()));
|
||||
connect(mCpuWidget->getDisasmWidget(), SIGNAL(decompileAt(dsint, dsint)), this, SLOT(decompileAt(dsint, dsint)));
|
||||
|
|
@ -450,6 +459,7 @@ void MainWindow::refreshShortcuts()
|
|||
setGlobalShortcut(ui->actionFunctions, ConfigShortcut("ViewFunctions"));
|
||||
setGlobalShortcut(ui->actionSnowman, ConfigShortcut("ViewSnowman"));
|
||||
setGlobalShortcut(ui->actionHandles, ConfigShortcut("ViewHandles"));
|
||||
setGlobalShortcut(ui->actionGraph, ConfigShortcut("ViewGraph"));
|
||||
|
||||
setGlobalShortcut(ui->actionRun, ConfigShortcut("DebugRun"));
|
||||
setGlobalShortcut(ui->actioneRun, ConfigShortcut("DebugeRun"));
|
||||
|
|
@ -886,6 +896,11 @@ void MainWindow::displaySnowmanWidget()
|
|||
showQWidgetTab(mSnowmanView);
|
||||
}
|
||||
|
||||
void MainWindow::displayGraphWidget()
|
||||
{
|
||||
showQWidgetTab(mGraphView);
|
||||
}
|
||||
|
||||
void MainWindow::hideDebugger()
|
||||
{
|
||||
DbgCmdExec("hide");
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@
|
|||
#include "TimeWastedCounter.h"
|
||||
#include "NotesManager.h"
|
||||
#include "SettingsDialog.h"
|
||||
#include "DisassemblerGraphView.h"
|
||||
|
||||
namespace Ui
|
||||
{
|
||||
|
|
@ -91,6 +92,7 @@ public slots:
|
|||
void displayReferencesWidget();
|
||||
void displayThreadsWidget();
|
||||
void displaySnowmanWidget();
|
||||
void displayGraphWidget();
|
||||
void hideDebugger();
|
||||
void openSettings();
|
||||
void openAppearance();
|
||||
|
|
@ -167,6 +169,7 @@ private:
|
|||
SnowmanView* mSnowmanView;
|
||||
HandlesView* mHandlesView;
|
||||
NotesManager* mNotesManager;
|
||||
DisassemblerGraphView* mGraphView;
|
||||
|
||||
StatusLabel* mStatusLabel;
|
||||
StatusLabel* mLastLogLabel;
|
||||
|
|
|
|||
|
|
@ -70,6 +70,7 @@
|
|||
<addaction name="actionFunctions"/>
|
||||
<addaction name="actionSnowman"/>
|
||||
<addaction name="actionHandles"/>
|
||||
<addaction name="actionGraph"/>
|
||||
</widget>
|
||||
<widget class="QMenu" name="menuDebug">
|
||||
<property name="title">
|
||||
|
|
@ -954,6 +955,15 @@
|
|||
<string>Step into (source)</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionGraph">
|
||||
<property name="icon">
|
||||
<iconset resource="../../resource.qrc">
|
||||
<normaloff>:/icons/images/graph.png</normaloff>:/icons/images/graph.png</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>&Graph</string>
|
||||
</property>
|
||||
</action>
|
||||
</widget>
|
||||
<layoutdefault spacing="6" margin="11"/>
|
||||
<resources>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,101 @@
|
|||
struct ActionShortcut
|
||||
{
|
||||
QAction* action;
|
||||
QString shortcut;
|
||||
|
||||
ActionShortcut(QAction* action, const char* shortcut)
|
||||
: action(action),
|
||||
shortcut(shortcut)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
std::vector<ActionShortcut> actionShortcutPairs;
|
||||
|
||||
inline QAction* connectAction(QAction* action, const char* slot)
|
||||
{
|
||||
connect(action, SIGNAL(triggered(bool)), this, slot);
|
||||
return action;
|
||||
}
|
||||
|
||||
inline QAction* connectAction(QAction* action, QActionLambda::TriggerCallback callback)
|
||||
{
|
||||
auto lambda = new QActionLambda(action->parent(), callback);
|
||||
connect(action, SIGNAL(triggered(bool)), lambda, SLOT(triggeredSlot()));
|
||||
return action;
|
||||
}
|
||||
|
||||
inline QAction* connectShortcutAction(QAction* action, const char* shortcut)
|
||||
{
|
||||
actionShortcutPairs.push_back(ActionShortcut(action, shortcut));
|
||||
action->setShortcut(ConfigShortcut(shortcut));
|
||||
action->setShortcutContext(Qt::WidgetShortcut);
|
||||
addAction(action);
|
||||
return action;
|
||||
}
|
||||
|
||||
inline QAction* connectMenuAction(QMenu* menu, QAction* action)
|
||||
{
|
||||
menu->addAction(action);
|
||||
return action;
|
||||
}
|
||||
|
||||
inline QMenu* makeMenu(const QString & title)
|
||||
{
|
||||
return new QMenu(title, this);
|
||||
}
|
||||
|
||||
inline QMenu* makeMenu(const QIcon & icon, const QString & title)
|
||||
{
|
||||
QMenu* menu = new QMenu(title, this);
|
||||
menu->setIcon(icon);
|
||||
return menu;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline QAction* makeAction(const QString & text, T slot)
|
||||
{
|
||||
return connectAction(new QAction(text, this), slot);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline QAction* makeAction(const QIcon & icon, const QString & text, T slot)
|
||||
{
|
||||
return connectAction(new QAction(icon, text, this), slot);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline QAction* makeShortcutAction(const QString & text, T slot, const char* shortcut)
|
||||
{
|
||||
return connectShortcutAction(makeAction(text, slot), shortcut);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline QAction* makeShortcutAction(const QIcon & icon, const QString & text, T slot, const char* shortcut)
|
||||
{
|
||||
return connectShortcutAction(makeAction(icon, text, slot), shortcut);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline QAction* makeMenuAction(QMenu* menu, const QString & text, T slot)
|
||||
{
|
||||
return connectMenuAction(menu, makeAction(text, slot));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline QAction* makeMenuAction(QMenu* menu, const QIcon & icon, const QString & text, T slot)
|
||||
{
|
||||
return connectMenuAction(menu, makeAction(icon, text, slot));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline QAction* makeShortcutMenuAction(QMenu* menu, const QString & text, T slot, const char* shortcut)
|
||||
{
|
||||
return connectShortcutAction(makeMenuAction(menu, text, slot), shortcut);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline QAction* makeShortcutMenuAction(QMenu* menu, const QIcon & icon, const QString & text, T slot, const char* shortcut)
|
||||
{
|
||||
return connectShortcutAction(makeMenuAction(menu, icon, text, slot), shortcut);
|
||||
}
|
||||
|
|
@ -227,6 +227,7 @@ Configuration::Configuration() : QObject(), noMoreMsgbox(false)
|
|||
QMap<QString, duint> tabOrderUint;
|
||||
int curTab = 0;
|
||||
tabOrderUint.insert("CPUTab", curTab++);
|
||||
tabOrderUint.insert("GraphTab", curTab++);
|
||||
tabOrderUint.insert("LogTab", curTab++);
|
||||
tabOrderUint.insert("NotesTab", curTab++);
|
||||
tabOrderUint.insert("BreakpointsTab", curTab++);
|
||||
|
|
@ -278,6 +279,7 @@ Configuration::Configuration() : QObject(), noMoreMsgbox(false)
|
|||
defaultShortcuts.insert("ViewFunctions", Shortcut(tr("View -> Functions"), "Alt+F", true));
|
||||
defaultShortcuts.insert("ViewSnowman", Shortcut(tr("View -> Snowman"), "", true));
|
||||
defaultShortcuts.insert("ViewHandles", Shortcut(tr("View -> Handles"), "", true));
|
||||
defaultShortcuts.insert("ViewGraph", Shortcut(tr("View -> Graph"), "Alt+G", true));
|
||||
|
||||
defaultShortcuts.insert("DebugRun", Shortcut(tr("Debug -> Run"), "F9", true));
|
||||
defaultShortcuts.insert("DebugeRun", Shortcut(tr("Debug -> Run (skip exceptions)"), "Shift+F9", true));
|
||||
|
|
@ -355,7 +357,7 @@ Configuration::Configuration() : QObject(), noMoreMsgbox(false)
|
|||
defaultShortcuts.insert("ActionFindPattern", Shortcut(tr("Actions -> Find Pattern"), "Ctrl+B"));
|
||||
defaultShortcuts.insert("ActionFindReferences", Shortcut(tr("Actions -> Find References"), "Ctrl+R"));
|
||||
defaultShortcuts.insert("ActionXrefs", Shortcut(tr("Actions -> xrefs..."), "X"));
|
||||
defaultShortcuts.insert("ActionAnalyzeSingleFunction", Shortcut(tr("Actions -> Analyze Single Function"), "Ctrl+Shift+A"));
|
||||
defaultShortcuts.insert("ActionAnalyzeSingleFunction", Shortcut(tr("Actions -> Analyze Single Function"), "A"));
|
||||
defaultShortcuts.insert("ActionHelpOnMnemonic", Shortcut(tr("Actions -> Help on Mnemonic"), "Ctrl+F1"));
|
||||
defaultShortcuts.insert("ActionToggleMnemonicBrief", Shortcut(tr("Actions -> Toggle Mnemonic Brief"), "Ctrl+Shift+F1"));
|
||||
defaultShortcuts.insert("ActionHighlightingMode", Shortcut(tr("Actions -> Highlighting Mode"), "Ctrl+H"));
|
||||
|
|
@ -410,6 +412,7 @@ Configuration::Configuration() : QObject(), noMoreMsgbox(false)
|
|||
defaultShortcuts.insert("ActionAbortScript", Shortcut(tr("Actions -> Abort Script"), "Esc"));
|
||||
defaultShortcuts.insert("ActionExecuteCommandScript", Shortcut(tr("Actions -> Execute Script Command"), "X"));
|
||||
defaultShortcuts.insert("ActionRefresh", Shortcut(tr("Actions -> Refresh"), "F5"));
|
||||
defaultShortcuts.insert("ActionGraph", Shortcut(tr("Actions -> Graph"), "G"));
|
||||
|
||||
Shortcuts = defaultShortcuts;
|
||||
|
||||
|
|
|
|||
Binary file not shown.
|
After Width: | Height: | Size: 825 B |
|
|
@ -249,5 +249,6 @@
|
|||
<file>images/undo.png</file>
|
||||
<file>images/binary_save.png</file>
|
||||
<file>images/animal-dog.png</file>
|
||||
<file>images/graph.png</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
|
|
|
|||
|
|
@ -165,7 +165,8 @@ SOURCES += \
|
|||
Src/Utils/CodeFolding.cpp \
|
||||
Src/Gui/WatchView.cpp \
|
||||
Src/Gui/FavouriteTools.cpp \
|
||||
Src/Gui/BrowseDialog.cpp
|
||||
Src/Gui/BrowseDialog.cpp \
|
||||
Src/Gui/DisassemblerGraphView.cpp
|
||||
|
||||
|
||||
HEADERS += \
|
||||
|
|
@ -266,7 +267,9 @@ HEADERS += \
|
|||
Src/Utils/CodeFolding.h \
|
||||
Src/Gui/WatchView.h \
|
||||
Src/Gui/FavouriteTools.h \
|
||||
Src/Gui/BrowseDialog.h
|
||||
Src/Gui/BrowseDialog.h \
|
||||
Src/Gui/DisassemblerGraphView.h \
|
||||
Src/Utils/ActionHelpers.h
|
||||
|
||||
|
||||
FORMS += \
|
||||
|
|
|
|||
Loading…
Reference in New Issue