mirror of
https://github.com/rizinorg/cutter.git
synced 2025-01-18 18:38:51 +00:00
Show a tooltip with Asm\Hex preview on search hits (#1480)
* implement search preview tooltip
This commit is contained in:
parent
f1fa05e647
commit
d0458597d1
@ -2571,6 +2571,42 @@ QList<DisassemblyLine> CutterCore::disassembleLines(RVA offset, int lines)
|
|||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief return hexdump of <size> from an <offset> by a given formats
|
||||||
|
* @param address - the address from which to print the hexdump
|
||||||
|
* @param size - number of bytes to print
|
||||||
|
* @param format - the type of hexdump (qwords, words. decimal, etc)
|
||||||
|
*/
|
||||||
|
QString CutterCore::hexdump(RVA address, int size, HexdumpFormats format)
|
||||||
|
{
|
||||||
|
QString command = "px";
|
||||||
|
switch (format) {
|
||||||
|
case HexdumpFormats::Normal:
|
||||||
|
break;
|
||||||
|
case HexdumpFormats::Half:
|
||||||
|
command += "h";
|
||||||
|
break;
|
||||||
|
case HexdumpFormats::Word:
|
||||||
|
command += "w";
|
||||||
|
break;
|
||||||
|
case HexdumpFormats::Quad:
|
||||||
|
command += "q";
|
||||||
|
break;
|
||||||
|
case HexdumpFormats::Signed:
|
||||||
|
command += "d";
|
||||||
|
break;
|
||||||
|
case HexdumpFormats::Octal:
|
||||||
|
command += "o";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return cmd(QString("%1 %2 @ %3")
|
||||||
|
.arg(command)
|
||||||
|
.arg(size)
|
||||||
|
.arg(address));
|
||||||
|
}
|
||||||
|
|
||||||
QByteArray CutterCore::hexStringToBytes(const QString &hex)
|
QByteArray CutterCore::hexStringToBytes(const QString &hex)
|
||||||
{
|
{
|
||||||
QByteArray hexChars = hex.toUtf8();
|
QByteArray hexChars = hex.toUtf8();
|
||||||
@ -2672,3 +2708,62 @@ BasicBlockHighlighter* CutterCore::getBBHighlighter()
|
|||||||
{
|
{
|
||||||
return bbHighlighter;
|
return bbHighlighter;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief get a compact disassembly preview for tooltips
|
||||||
|
* @param address - the address from which to print the disassembly
|
||||||
|
* @param num_of_lines - number of instructions to print
|
||||||
|
*/
|
||||||
|
QStringList CutterCore::getDisassemblyPreview(RVA address, int num_of_lines)
|
||||||
|
{
|
||||||
|
QList<DisassemblyLine> disassemblyLines;
|
||||||
|
{
|
||||||
|
// temporarily simplify the disasm output to get it colorful and simple to read
|
||||||
|
TempConfig tempConfig;
|
||||||
|
tempConfig
|
||||||
|
.set("scr.color", COLOR_MODE_16M)
|
||||||
|
.set("asm.lines", false)
|
||||||
|
.set("asm.var", false)
|
||||||
|
.set("asm.comments", false)
|
||||||
|
.set("asm.bytes", false)
|
||||||
|
.set("asm.lines.fcn", false)
|
||||||
|
.set("asm.lines.out", false)
|
||||||
|
.set("asm.lines.bb", false)
|
||||||
|
.set("asm.bb.line", false);
|
||||||
|
|
||||||
|
disassemblyLines = disassembleLines(address, num_of_lines + 1);
|
||||||
|
}
|
||||||
|
QStringList disasmPreview;
|
||||||
|
for (const DisassemblyLine &line : disassemblyLines) {
|
||||||
|
disasmPreview << line.text;
|
||||||
|
if (disasmPreview.length() >= num_of_lines) {
|
||||||
|
disasmPreview << "...";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!disasmPreview.isEmpty()) {
|
||||||
|
return disasmPreview;
|
||||||
|
} else {
|
||||||
|
return QStringList();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief get a compact hexdump preview for tooltips
|
||||||
|
* @param address - the address from which to print the hexdump
|
||||||
|
* @param size - number of bytes to print
|
||||||
|
*/
|
||||||
|
QString CutterCore::getHexdumpPreview(RVA address, int size)
|
||||||
|
{
|
||||||
|
// temporarily simplify the disasm output to get it colorful and simple to read
|
||||||
|
TempConfig tempConfig;
|
||||||
|
tempConfig
|
||||||
|
.set("scr.color", COLOR_MODE_16M)
|
||||||
|
.set("asm.offset", true)
|
||||||
|
.set("hex.header", false)
|
||||||
|
.set("hex.cols", 16);
|
||||||
|
return ansiEscapeToHtml(hexdump(address, size, HexdumpFormats::Normal)).replace(QLatin1Char('\n'), "<br>");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -78,6 +78,7 @@ public:
|
|||||||
QString cmdFunctionAt(QString addr);
|
QString cmdFunctionAt(QString addr);
|
||||||
QString cmdFunctionAt(RVA addr);
|
QString cmdFunctionAt(RVA addr);
|
||||||
QString createFunctionAt(RVA addr, QString name);
|
QString createFunctionAt(RVA addr, QString name);
|
||||||
|
QStringList getDisassemblyPreview(RVA address, int num_of_lines);
|
||||||
|
|
||||||
/* Flags */
|
/* Flags */
|
||||||
void delFlag(RVA addr);
|
void delFlag(RVA addr);
|
||||||
@ -184,7 +185,7 @@ public:
|
|||||||
QString getConfig(const QString &k) { return getConfig(k.toUtf8().constData()); }
|
QString getConfig(const QString &k) { return getConfig(k.toUtf8().constData()); }
|
||||||
QList<QString> getColorThemes();
|
QList<QString> getColorThemes();
|
||||||
|
|
||||||
/* Assembly related methods */
|
/* Assembly\Hexdump related methods */
|
||||||
QByteArray assemble(const QString &code);
|
QByteArray assemble(const QString &code);
|
||||||
QString disassemble(const QByteArray &data);
|
QString disassemble(const QByteArray &data);
|
||||||
QString disassembleSingleInstruction(RVA addr);
|
QString disassembleSingleInstruction(RVA addr);
|
||||||
@ -192,6 +193,9 @@ public:
|
|||||||
|
|
||||||
static QByteArray hexStringToBytes(const QString &hex);
|
static QByteArray hexStringToBytes(const QString &hex);
|
||||||
static QString bytesToHexString(const QByteArray &bytes);
|
static QString bytesToHexString(const QByteArray &bytes);
|
||||||
|
enum class HexdumpFormats { Normal, Half, Word, Quad, Signed, Octal };
|
||||||
|
QString hexdump(RVA offset, int size, HexdumpFormats format);
|
||||||
|
QString getHexdumpPreview(RVA offset, int size);
|
||||||
|
|
||||||
void setCPU(QString arch, QString cpu, int bits);
|
void setCPU(QString arch, QString cpu, int bits);
|
||||||
void setEndianness(bool big);
|
void setEndianness(bool big);
|
||||||
|
@ -198,37 +198,9 @@ QVariant FunctionModel::data(const QModelIndex &index, int role) const
|
|||||||
return static_cast<int>(Qt::AlignLeft | Qt::AlignVCenter);
|
return static_cast<int>(Qt::AlignLeft | Qt::AlignVCenter);
|
||||||
|
|
||||||
case Qt::ToolTipRole: {
|
case Qt::ToolTipRole: {
|
||||||
QList<DisassemblyLine> disassemblyLines;
|
|
||||||
{
|
|
||||||
// temporarily simplify the disasm output to get it colorful and simple to read
|
|
||||||
TempConfig tempConfig;
|
|
||||||
tempConfig
|
|
||||||
.set("scr.color", COLOR_MODE_16M)
|
|
||||||
.set("asm.lines", false)
|
|
||||||
.set("asm.var", false)
|
|
||||||
.set("asm.comments", false)
|
|
||||||
.set("asm.bytes", false)
|
|
||||||
.set("asm.lines.fcn", false)
|
|
||||||
.set("asm.lines.out", false)
|
|
||||||
.set("asm.lines.bb", false)
|
|
||||||
.set("asm.bb.line", false);
|
|
||||||
|
|
||||||
disassemblyLines = Core()->disassembleLines(function.offset, kMaxTooltipDisasmPreviewLines + 1);
|
|
||||||
}
|
|
||||||
QStringList disasmPreview;
|
|
||||||
for (const DisassemblyLine &line : disassemblyLines) {
|
|
||||||
if (!function.contains(line.offset)) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
disasmPreview << line.text;
|
|
||||||
if (disasmPreview.length() >= kMaxTooltipDisasmPreviewLines) {
|
|
||||||
disasmPreview << "...";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
QStringList disasmPreview = Core()->getDisassemblyPreview(function.offset, kMaxTooltipDisasmPreviewLines);
|
||||||
const QStringList &summary = Core()->cmdList(QString("pdsf @ %1").arg(function.offset));
|
const QStringList &summary = Core()->cmdList(QString("pdsf @ %1").arg(function.offset));
|
||||||
|
|
||||||
const QFont &fnt = Config()->getFont();
|
const QFont &fnt = Config()->getFont();
|
||||||
QFontMetrics fm{ fnt };
|
QFontMetrics fm{ fnt };
|
||||||
|
|
||||||
|
@ -8,6 +8,14 @@
|
|||||||
#include <QComboBox>
|
#include <QComboBox>
|
||||||
#include <QShortcut>
|
#include <QShortcut>
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
static const int kMaxTooltipWidth = 500;
|
||||||
|
static const int kMaxTooltipDisasmPreviewLines = 10;
|
||||||
|
static const int kMaxTooltipHexdumpBytes = 64;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
static const QMap<QString, QString> kSearchBoundariesValues {
|
static const QMap<QString, QString> kSearchBoundariesValues {
|
||||||
{"io.maps", "All maps"},
|
{"io.maps", "All maps"},
|
||||||
{"io.map", "Current map"},
|
{"io.map", "Current map"},
|
||||||
@ -58,6 +66,31 @@ QVariant SearchModel::data(const QModelIndex &index, int role) const
|
|||||||
default:
|
default:
|
||||||
return QVariant();
|
return QVariant();
|
||||||
}
|
}
|
||||||
|
case Qt::ToolTipRole: {
|
||||||
|
|
||||||
|
QString previewContent = QString();
|
||||||
|
// if result is CODE, show disassembly
|
||||||
|
if (!exp.code.isEmpty()) {
|
||||||
|
previewContent = Core()->getDisassemblyPreview(exp.offset, kMaxTooltipDisasmPreviewLines)
|
||||||
|
.join("<br>");
|
||||||
|
// if result is DATA or Disassembly is N/A
|
||||||
|
} else if (!exp.data.isEmpty() || previewContent.isEmpty()) {
|
||||||
|
previewContent = Core()->getHexdumpPreview(exp.offset, kMaxTooltipHexdumpBytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
const QFont &fnt = Config()->getFont();
|
||||||
|
QFontMetrics fm{ fnt };
|
||||||
|
|
||||||
|
QString toolTipContent = QString("<html><div style=\"font-family: %1; font-size: %2pt; white-space: nowrap;\">")
|
||||||
|
.arg(fnt.family())
|
||||||
|
.arg(qMax(6, fnt.pointSize() - 1)); // slightly decrease font size, to keep more text in the same box
|
||||||
|
|
||||||
|
toolTipContent += tr("<div style=\"margin-bottom: 10px;\"><strong>Preview</strong>:<br>%1</div>")
|
||||||
|
.arg(previewContent);
|
||||||
|
|
||||||
|
toolTipContent += "</div></html>";
|
||||||
|
return toolTipContent;
|
||||||
|
}
|
||||||
case SearchDescriptionRole:
|
case SearchDescriptionRole:
|
||||||
return QVariant::fromValue(exp);
|
return QVariant::fromValue(exp);
|
||||||
default:
|
default:
|
||||||
@ -130,6 +163,7 @@ SearchWidget::SearchWidget(MainWindow *main, QAction *action) :
|
|||||||
ui(new Ui::SearchWidget)
|
ui(new Ui::SearchWidget)
|
||||||
{
|
{
|
||||||
ui->setupUi(this);
|
ui->setupUi(this);
|
||||||
|
setStyleSheet(QString("QToolTip { max-width: %1px; opacity: 230; }").arg(kMaxTooltipWidth));
|
||||||
|
|
||||||
ui->searchInCombo->blockSignals(true);
|
ui->searchInCombo->blockSignals(true);
|
||||||
QMap<QString, QString>::const_iterator mapIter;
|
QMap<QString, QString>::const_iterator mapIter;
|
||||||
|
Loading…
Reference in New Issue
Block a user