mirror of
https://github.com/rizinorg/cutter
synced 2026-08-11 16:23:06 -04:00
Compare commits
6 commits
v2.5.0-rc2
...
dev
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4cb6ff6f98 | ||
|
|
26fa614d01 | ||
|
|
867c54e838 | ||
|
|
e01d86b5fa | ||
|
|
2c6c838a13 | ||
|
|
3978a4e8e4 |
18 changed files with 928 additions and 258 deletions
2
.github/workflows/ci.yml
vendored
2
.github/workflows/ci.yml
vendored
|
|
@ -397,7 +397,7 @@ jobs:
|
|||
python-version: 3.12.x
|
||||
output-id: artifact_windows
|
||||
- name: macos-x86_64
|
||||
os: macos-14
|
||||
os: macos-15-intel # Must be an x86_64 image: https://github.com/actions/runner-images#available-images
|
||||
arch: x86_64
|
||||
package: true
|
||||
- name: macos-arm64
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ set(CUTTER_EXTRA_PLUGIN_DIRS "" CACHE STRING "List of addition plugin locations"
|
|||
option(CUTTER_ENABLE_DEPENDENCY_DOWNLOADS "Enable downloading of dependencies. Setting to OFF doesn't affect any downloads done by rizin build." OFF)
|
||||
option(CUTTER_ENABLE_PACKAGING "Enable building platform-specific packages for distributing" OFF)
|
||||
option(CUTTER_ENABLE_SIGDB "Downloads and installs sigdb (only available when CUTTER_USE_BUNDLED_RIZIN=ON)." OFF)
|
||||
option(CUTTER_ENABLE_TRANSLATIONS "Build and install translation files" ON)
|
||||
option(CUTTER_PACKAGE_DEPENDENCIES "During install step include the third party dependencies." OFF)
|
||||
option(CUTTER_PACKAGE_RZ_GHIDRA "Compile and install rz-ghidra during install step." OFF)
|
||||
option(CUTTER_PACKAGE_RZ_LIBSWIFT "Compile and install rz-libswift demangler during the install step." OFF)
|
||||
|
|
|
|||
|
|
@ -53,7 +53,7 @@ endif()
|
|||
|
||||
# TODO: This version number should be fetched automatically
|
||||
# instead of being hardcoded.
|
||||
set (Rizin_VERSION 0.9)
|
||||
set (Rizin_VERSION 0.10)
|
||||
|
||||
set (RZ_LIBS rz_core rz_config rz_cons rz_io rz_util rz_flag rz_mark rz_arch rz_debug
|
||||
rz_hash rz_bin rz_lang rz_il rz_egg rz_reg rz_search rz_syscall
|
||||
|
|
|
|||
2
rizin
2
rizin
|
|
@ -1 +1 @@
|
|||
Subproject commit da228d11cfc865b06442bd66b192caeb6e5ff556
|
||||
Subproject commit 136a337e1c40236723a58ccca6549aa577e8aaa5
|
||||
|
|
@ -5,7 +5,7 @@ set -e
|
|||
cd $(dirname "${BASH_SOURCE[0]}")/..
|
||||
mkdir -p cutter-deps && cd cutter-deps
|
||||
|
||||
DEPS_BASE_URL=https://github.com/rizinorg/cutter-deps/releases/download/v18
|
||||
DEPS_BASE_URL=https://github.com/rizinorg/cutter-deps/releases/download/v19
|
||||
|
||||
if [ "$CUTTER_QT" == "5" ]; then
|
||||
DEPS_FILE_linux_x86_64=cutter-deps-q5-linux-x86_64.tar.gz
|
||||
|
|
@ -13,18 +13,18 @@ if [ "$CUTTER_QT" == "5" ]; then
|
|||
DEPS_BASE_URL=https://github.com/rizinorg/cutter-deps/releases/download/qt5-v17
|
||||
else
|
||||
DEPS_FILE_linux_x86_64=cutter-deps-linux-x86_64.tar.gz
|
||||
DEPS_SHA256_linux_x86_64=204683c44e4824df7cb9925f1939544f081e64b7a701fe95fd03f67125a165b4
|
||||
DEPS_SHA256_linux_x86_64=b4bebf95c69f6216456d31b345d660ae3347517a6c14a4a4e9d568b03289c3de
|
||||
fi
|
||||
echo $DEPS_SHA256_linux_x86_64
|
||||
|
||||
DEPS_FILE_macos_x86_64=cutter-deps-macos-x86_64.tar.gz
|
||||
DEPS_SHA256_macos_x86_64=a46c2dee12d4160410237da848075fc87c41c3850021f583d3b469a59c386650
|
||||
DEPS_SHA256_macos_x86_64=6f202bd2b18cae780d4fe4ddce662909eebb4f0cd3f1c685e5b2bb6cd46a3e7d
|
||||
|
||||
DEPS_FILE_macos_arm64=cutter-deps-macos-arm64.tar.gz
|
||||
DEPS_SHA256_macos_arm64=1ca381b562d632a370e05a85b0fe60a591a261bba0f48d97a0e40b4307c2577a
|
||||
DEPS_SHA256_macos_arm64=fa4b3c58f02e7b57a18c2fe7b8c196dd008187deda72fa1ae685c8ed661e10c6
|
||||
|
||||
DEPS_FILE_win_x86_64=cutter-deps-win-x86_64.tar.gz
|
||||
DEPS_SHA256_win_x86_64=95a3cf676178b8c58442d22ee8b21ce2530bfdae86f514901310490a8f28867a
|
||||
DEPS_SHA256_win_x86_64=3d69280bfedfdd3363fac450f814fc2f09cdd6149f60541dcdaa81f782c3ac76
|
||||
|
||||
|
||||
ARCH=x86_64
|
||||
|
|
|
|||
|
|
@ -108,9 +108,17 @@ resign_dmg() {
|
|||
notarize_bundle Cutter-rw/Cutter.app
|
||||
unmount
|
||||
trap - EXIT
|
||||
# Remove temporary signing space and restore HFS+ volume consistency.
|
||||
ee hdiutil resize -size min Cutter-rw.dmg
|
||||
OUTPUT="${1%.*}-signed.dmg"
|
||||
echo_step "Creating final read-only ${OUTPUT}"
|
||||
ee hdiutil convert -format UDZO -o "${OUTPUT}" Cutter-rw.dmg
|
||||
echo_step "Verifying filesystem in ${OUTPUT}"
|
||||
ee hdiutil attach "${OUTPUT}" -readonly -nobrowse -noautofsck -mountpoint Cutter-rw
|
||||
trap unmount EXIT
|
||||
ee diskutil verifyVolume Cutter-rw
|
||||
unmount
|
||||
trap - EXIT
|
||||
}
|
||||
|
||||
case "$1" in
|
||||
|
|
|
|||
|
|
@ -613,7 +613,9 @@ if(CUTTER_ENABLE_PACKAGING)
|
|||
target_compile_definitions(Cutter PRIVATE CUTTER_ENABLE_PACKAGING)
|
||||
endif()
|
||||
|
||||
include(Translations)
|
||||
if(CUTTER_ENABLE_TRANSLATIONS)
|
||||
include(Translations)
|
||||
endif()
|
||||
|
||||
# Install files
|
||||
install(TARGETS Cutter
|
||||
|
|
|
|||
|
|
@ -4,19 +4,55 @@
|
|||
|
||||
#include <QAbstractButton>
|
||||
#include <QAbstractItemView>
|
||||
#include <QAbstractTextDocumentLayout>
|
||||
#include <QComboBox>
|
||||
#include <QCryptographicHash>
|
||||
#include <QDockWidget>
|
||||
#include <QFileInfo>
|
||||
#include <QFontMetricsF>
|
||||
#include <QMenu>
|
||||
#include <QPlainTextEdit>
|
||||
#include <QString>
|
||||
#include <QTextBlock>
|
||||
#include <QTextEdit>
|
||||
#include <QTreeWidget>
|
||||
#include <QtCore>
|
||||
|
||||
#include <cmath>
|
||||
|
||||
namespace {
|
||||
template<typename TextEditType>
|
||||
int calculateMaxDisplayedLines(TextEditType *textEdit)
|
||||
{
|
||||
qreal lineHeight = 0;
|
||||
const QTextBlock firstBlock = textEdit->document()->begin();
|
||||
if (firstBlock.isValid()) {
|
||||
lineHeight = textEdit->document()->documentLayout()->blockBoundingRect(firstBlock).height();
|
||||
}
|
||||
|
||||
if (lineHeight <= 0) {
|
||||
const QFontMetricsF fm(textEdit->font());
|
||||
lineHeight = fm.lineSpacing();
|
||||
}
|
||||
|
||||
qreal availableHeight = textEdit->viewport()->height();
|
||||
const qreal margin = textEdit->document()->documentMargin();
|
||||
availableHeight -= margin;
|
||||
|
||||
const qreal exactLines = availableHeight / lineHeight;
|
||||
const qreal floorValue = std::floor(exactLines);
|
||||
const qreal fractionalPart = exactLines - floorValue;
|
||||
|
||||
// If the last line is more than 15% visible then count it, otherwise leave it
|
||||
// avoids the issue where there is empty space at the bottom of a textedit widget
|
||||
if (fractionalPart >= 0.15) {
|
||||
return std::ceil(availableHeight / lineHeight);
|
||||
}
|
||||
|
||||
return floorValue;
|
||||
}
|
||||
}
|
||||
|
||||
namespace qhelpers {
|
||||
|
||||
QString formatByteCount(ut64 bytecount)
|
||||
|
|
@ -174,20 +210,12 @@ void SizePolicyMinMax::restoreHeight(QWidget *widget) const
|
|||
|
||||
int getMaxFullyDisplayedLines(QTextEdit *textEdit)
|
||||
{
|
||||
const QFontMetrics fontMetrics(textEdit->document()->defaultFont());
|
||||
return (textEdit->height()
|
||||
- (textEdit->contentsMargins().top() + textEdit->contentsMargins().bottom()
|
||||
+ (int)(textEdit->document()->documentMargin() * 2)))
|
||||
/ fontMetrics.lineSpacing();
|
||||
return calculateMaxDisplayedLines(textEdit);
|
||||
}
|
||||
|
||||
int getMaxFullyDisplayedLines(QPlainTextEdit *plainTextEdit)
|
||||
{
|
||||
const QFontMetrics fontMetrics(plainTextEdit->document()->defaultFont());
|
||||
return (plainTextEdit->height()
|
||||
- (plainTextEdit->contentsMargins().top() + plainTextEdit->contentsMargins().bottom()
|
||||
+ (int)(plainTextEdit->document()->documentMargin() * 2)))
|
||||
/ fontMetrics.lineSpacing();
|
||||
return calculateMaxDisplayedLines(plainTextEdit);
|
||||
}
|
||||
|
||||
QByteArray applyColorToSvg(const QByteArray &data, QColor color)
|
||||
|
|
|
|||
|
|
@ -1187,19 +1187,15 @@ void CutterCore::setConfig(const char *k, const RzInterval &itv)
|
|||
void CutterCore::setConfig(const char *k, const QStringList &list)
|
||||
{
|
||||
CORE_LOCK();
|
||||
RzList *rzList = rz_list_newf(free);
|
||||
if (!rzList) {
|
||||
RzSetS *set = rz_set_s_new(HT_STR_DUP);
|
||||
if (!set) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (const QString &str : list) {
|
||||
char *dupStr = strdup(str.toUtf8().constData());
|
||||
if (dupStr) {
|
||||
rz_list_append(rzList, dupStr);
|
||||
}
|
||||
rz_set_s_add(set, str.toUtf8().constData());
|
||||
}
|
||||
|
||||
rz_config_set_list(core->config, k, rzList);
|
||||
rz_config_set_set(core->config, k, set);
|
||||
rz_set_s_free(set);
|
||||
}
|
||||
|
||||
int CutterCore::getConfigi(const char *k)
|
||||
|
|
@ -1226,16 +1222,12 @@ RzInterval CutterCore::getConfigItv(const char *k)
|
|||
return rz_config_get_interval(core->config, k);
|
||||
}
|
||||
|
||||
QStringList CutterCore::getConfigList(const char *k)
|
||||
QSet<QString> CutterCore::getConfigSet(const char *k)
|
||||
{
|
||||
CORE_LOCK();
|
||||
|
||||
QStringList res;
|
||||
RzList *list = rz_config_get_list(core->config, k);
|
||||
for (const auto *s : CutterRzList<const char>(list)) {
|
||||
res << QString::fromUtf8(s);
|
||||
}
|
||||
rz_list_free(list);
|
||||
RzSetS *set = rz_config_get_set(core->config, k);
|
||||
QSet<QString> res = convertRzSetS(set);
|
||||
rz_set_s_free(set);
|
||||
return res;
|
||||
}
|
||||
|
||||
|
|
@ -1295,18 +1287,18 @@ QString CutterCore::getConfig(const char *k)
|
|||
return { rz_config_get(core->config, k) };
|
||||
}
|
||||
|
||||
QStringList CutterCore::getConfigOptions(const char *k)
|
||||
QSet<QString> CutterCore::getConfigOptions(const char *k)
|
||||
{
|
||||
CORE_LOCK();
|
||||
const RzConfigNode *node = rz_config_node_get(core->config, k);
|
||||
if (!(node && node->options)) {
|
||||
return {};
|
||||
}
|
||||
QStringList list;
|
||||
for (const auto &s : CutterRzList<char>(node->options)) {
|
||||
list << s;
|
||||
QSet<QString> res;
|
||||
for (auto it = CutterRzIter<const char *>(rz_set_s_as_iter(node->options)); it; ++it) {
|
||||
res << QString::fromUtf8(*it);
|
||||
}
|
||||
return list;
|
||||
return res;
|
||||
}
|
||||
|
||||
void CutterCore::setConfig(const char *k, const QVariant &v)
|
||||
|
|
@ -4154,13 +4146,11 @@ QList<EvaluableVarDescription> CutterCore::getAllEvaluableVars()
|
|||
var.type = EvaluableVarDescription::Interval;
|
||||
const RzInterval itv = rz_config_var_get_interval(v);
|
||||
value = QVariant::fromValue(itv);
|
||||
} else if (RZ_CONFIG_VAR_IS_TYPE(flags, RZ_CONFIG_VAR_TYPE_LIST)) {
|
||||
var.type = EvaluableVarDescription::List;
|
||||
const RzList *list = rz_config_var_get_list(v);
|
||||
QStringList stringList;
|
||||
for (const auto *c : CutterRzList<const char>(list)) {
|
||||
stringList << QString(c);
|
||||
}
|
||||
} else if (RZ_CONFIG_VAR_IS_TYPE(flags, RZ_CONFIG_VAR_TYPE_SET)) {
|
||||
var.type = EvaluableVarDescription::Set;
|
||||
RzSetS *set = rz_config_var_get_set(v);
|
||||
value = QVariant::fromValue(convertRzSetS(set));
|
||||
rz_set_s_free(set);
|
||||
}
|
||||
|
||||
if (value.isNull()) {
|
||||
|
|
@ -4168,13 +4158,9 @@ QList<EvaluableVarDescription> CutterCore::getAllEvaluableVars()
|
|||
}
|
||||
var.value = value;
|
||||
|
||||
const RzList *optionsList = rz_config_var_get_options(v);
|
||||
if (optionsList) {
|
||||
RzListIter *iter;
|
||||
char *option;
|
||||
CutterRzListForeach (optionsList, iter, char, option) {
|
||||
var.options << QString::fromUtf8(option);
|
||||
}
|
||||
const RzSetS *optionsSet = rz_config_var_get_options(v);
|
||||
if (optionsSet) {
|
||||
var.options = convertRzSetS(optionsSet);
|
||||
}
|
||||
} else {
|
||||
const RzConfigNode *node = &entry->node;
|
||||
|
|
@ -4189,10 +4175,8 @@ QList<EvaluableVarDescription> CutterCore::getAllEvaluableVars()
|
|||
|
||||
var.value = QString::fromUtf8(rz_config_entry_get_as_string(entry));
|
||||
|
||||
RzListIter *iter;
|
||||
char *option;
|
||||
CutterRzListForeach (node->options, iter, char, option) {
|
||||
var.options << QString::fromUtf8(option);
|
||||
if (node->options) {
|
||||
var.options = convertRzSetS(node->options);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -495,12 +495,12 @@ public:
|
|||
bool getConfigb(const QString &k) { return getConfigb(k.toUtf8().constData()); }
|
||||
RzInterval getConfigItv(const char *k);
|
||||
RzInterval getConfigItv(const QString &k) { return getConfigItv(k.toUtf8().constData()); }
|
||||
QStringList getConfigList(const char *k);
|
||||
QStringList getConfigList(const QString &k) { return getConfigList(k.toUtf8().constData()); }
|
||||
QSet<QString> getConfigSet(const char *k);
|
||||
QSet<QString> getConfigSet(const QString &k) { return getConfigSet(k.toUtf8().constData()); }
|
||||
QString getConfig(const char *k);
|
||||
QString getConfig(const QString &k) { return getConfig(k.toUtf8().constData()); }
|
||||
QString getConfigDescription(const char *k);
|
||||
QStringList getConfigOptions(const char *k);
|
||||
QSet<QString> getConfigOptions(const char *k);
|
||||
QStringList getColorThemes();
|
||||
QHash<QString, QColor> getTheme();
|
||||
QStringList getThemeKeys();
|
||||
|
|
|
|||
|
|
@ -10,8 +10,8 @@
|
|||
#include <QColor>
|
||||
#include <QList>
|
||||
#include <QMetaType>
|
||||
#include <QSet>
|
||||
#include <QString>
|
||||
#include <QStringList>
|
||||
#include <QVariant>
|
||||
|
||||
struct FunctionDescription
|
||||
|
|
@ -448,11 +448,11 @@ struct EvaluableVarDescription
|
|||
QString description;
|
||||
bool readOnly;
|
||||
|
||||
enum Type : ut8 { Bool = 0, Int, String, Interval, List };
|
||||
enum Type : ut8 { Bool = 0, Int, String, Interval, Set };
|
||||
Type type;
|
||||
|
||||
QVariant value; ///< Can be either QString, QStringList or RzInterval depending on type
|
||||
QList<QString> options;
|
||||
QVariant value; ///< Can be either QString, QSet<QString> or RzInterval depending on type
|
||||
QSet<QString> options;
|
||||
};
|
||||
|
||||
Q_DECLARE_METATYPE(FunctionDescription)
|
||||
|
|
|
|||
|
|
@ -1 +1,12 @@
|
|||
#include "RizinCpp.h"
|
||||
|
||||
#include <QSet>
|
||||
|
||||
QSet<QString> convertRzSetS(const RzSetS *set)
|
||||
{
|
||||
QSet<QString> res;
|
||||
for (auto it = CutterRzIter<const char *>(rz_set_s_as_iter(set)); it; ++it) {
|
||||
res << QString::fromUtf8(*it);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -187,9 +187,9 @@ public:
|
|||
return *this;
|
||||
}
|
||||
operator bool() { return rzIter && rzIter->cur; }
|
||||
T &operator*() { return *reinterpret_cast<RzCoreDecodedBytes *>(rzIter->cur); }
|
||||
T *get() { return reinterpret_cast<RzCoreDecodedBytes *>(rzIter->cur); }
|
||||
T *operator->() { return reinterpret_cast<RzCoreDecodedBytes *>(rzIter->cur); }
|
||||
T &operator*() { return *reinterpret_cast<T *>(rzIter->cur); }
|
||||
T *get() { return reinterpret_cast<T *>(rzIter->cur); }
|
||||
T *operator->() { return reinterpret_cast<T *>(rzIter->cur); }
|
||||
};
|
||||
|
||||
#define CutterHtDef(xx, XX, K, VB) \
|
||||
|
|
@ -219,4 +219,6 @@ public:
|
|||
|
||||
CutterHtDef(sp, SP, const char *, void *);
|
||||
|
||||
QSet<QString> convertRzSetS(const RzSetS *set);
|
||||
|
||||
#endif // RIZINCPP_H
|
||||
|
|
|
|||
|
|
@ -125,7 +125,7 @@ QVariant RizinConfigOptionsModel::data(const QModelIndex &index, int role) const
|
|||
const auto itv = evalVar.value.value<RzInterval>();
|
||||
return QString("%1+%2").arg(rzAddressString(itv.addr), rzSizeString(itv.size));
|
||||
}
|
||||
if (evalVar.type == EvaluableVarDescription::List) {
|
||||
if (evalVar.type == EvaluableVarDescription::Set) {
|
||||
const QStringList list = evalVar.value.toStringList();
|
||||
if (list.isEmpty()) {
|
||||
return QString();
|
||||
|
|
@ -344,14 +344,16 @@ QWidget *RizinConfigOptionsDelegate::createEditor(QWidget *parent,
|
|||
|
||||
if (evalVar.type == EvaluableVarDescription::Bool
|
||||
|| evalVar.type == EvaluableVarDescription::Interval
|
||||
|| evalVar.type == EvaluableVarDescription::List) {
|
||||
|| evalVar.type == EvaluableVarDescription::Set) {
|
||||
// handled in lambda connected to ui->treeView::doubleClicked
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (!evalVar.options.isEmpty()) {
|
||||
auto *comboBox = new QComboBox(parent);
|
||||
for (const auto &opt : evalVar.options) {
|
||||
QStringList opts = evalVar.options.values();
|
||||
opts.sort();
|
||||
for (const auto &opt : opts) {
|
||||
comboBox->addItem(opt);
|
||||
}
|
||||
return comboBox;
|
||||
|
|
@ -486,7 +488,7 @@ RizinConfigOptionsWidget::RizinConfigOptionsWidget(PreferencesDialog *parent)
|
|||
return;
|
||||
}
|
||||
|
||||
if (evalVar.type == EvaluableVarDescription::List) {
|
||||
if (evalVar.type == EvaluableVarDescription::Set) {
|
||||
StringListDialog dialog(evalVar.value.toStringList(), this);
|
||||
dialog.setWindowTitle(tr("Edit List for %1").arg(evalVar.name));
|
||||
|
||||
|
|
@ -596,8 +598,8 @@ void RizinConfigOptionsWidget::handleConfigOptionChanged(const QModelIndex &topL
|
|||
if (!originalValues.contains(evalVar.name)) {
|
||||
if (evalVar.type == EvaluableVarDescription::Interval) {
|
||||
originalValues[evalVar.name] = QVariant::fromValue(Core()->getConfigItv(evalVar.name));
|
||||
} else if (evalVar.type == EvaluableVarDescription::List) {
|
||||
originalValues[evalVar.name] = QVariant::fromValue(Core()->getConfigList(evalVar.name));
|
||||
} else if (evalVar.type == EvaluableVarDescription::Set) {
|
||||
originalValues[evalVar.name] = QVariant::fromValue(Core()->getConfigSet(evalVar.name));
|
||||
} else {
|
||||
originalValues[evalVar.name] = Core()->getConfig(evalVar.name);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -215,6 +215,10 @@ const QHash<QString, Shortcut> &getDefaultShortcuts()
|
|||
{ { Qt::Key_Space },
|
||||
QT_TRANSLATE_NOOP("DisassemblyWidget", "Switch to Graph"),
|
||||
"DisassemblyWidget" } },
|
||||
{ "Disassembly.moveLeft",
|
||||
{ QList<QKeySequence> { Qt::Key_H }
|
||||
+ QKeySequence::keyBindings(QKeySequence::MoveToPreviousChar),
|
||||
QT_TRANSLATE_NOOP("DisassemblyWidget", "Move Cursor Down"), "DisassemblyWidget" } },
|
||||
{ "Disassembly.moveDown",
|
||||
{ QList<QKeySequence> { Qt::Key_J }
|
||||
+ QKeySequence::keyBindings(QKeySequence::MoveToNextLine),
|
||||
|
|
@ -223,6 +227,10 @@ const QHash<QString, Shortcut> &getDefaultShortcuts()
|
|||
{ QList<QKeySequence> { Qt::Key_K }
|
||||
+ QKeySequence::keyBindings(QKeySequence::MoveToPreviousLine),
|
||||
QT_TRANSLATE_NOOP("DisassemblyWidget", "Move Cursor Up"), "DisassemblyWidget" } },
|
||||
{ "Disassembly.moveRight",
|
||||
{ QList<QKeySequence> { Qt::Key_L }
|
||||
+ QKeySequence::keyBindings(QKeySequence::MoveToNextChar),
|
||||
QT_TRANSLATE_NOOP("DisassemblyWidget", "Move Cursor Up"), "DisassemblyWidget" } },
|
||||
{ "Disassembly.pageDown",
|
||||
{ QKeySequence::keyBindings(QKeySequence::MoveToNextPage),
|
||||
QT_TRANSLATE_NOOP("DisassemblyWidget", "Move Cursor Down By Page"),
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -19,6 +19,8 @@ class DisassemblyContextMenu;
|
|||
class DisassemblyLeftPanel;
|
||||
class AddressRangeScrollBar;
|
||||
|
||||
enum class RefreshMode : ut8 { Append, Prepend, Reset, Keep, None };
|
||||
|
||||
/**
|
||||
* @brief Main widget for showing disassembly of a binary
|
||||
*
|
||||
|
|
@ -33,48 +35,67 @@ public:
|
|||
|
||||
static QString getWidgetType();
|
||||
|
||||
QFontMetricsF getFontMetrics();
|
||||
QList<DisassemblyLine> getLines();
|
||||
|
||||
int getStartIndex() const;
|
||||
int getEndIndex() const;
|
||||
|
||||
/**
|
||||
* @brief Updates the offset and character position where the cursor selection ends
|
||||
*/
|
||||
void updateSelectionPos(const QTextCursor &cursor);
|
||||
|
||||
/**
|
||||
* @brief Updates the offset and character position where the cursor selection starts
|
||||
*/
|
||||
void updateSelectionAnchor(const QTextCursor &cursor);
|
||||
|
||||
public slots:
|
||||
/**
|
||||
* @brief Highlights the currently selected line and updates the
|
||||
* highlighting of the same words under the cursor in the visible screen.
|
||||
* This overrides all previous highlighting.
|
||||
* @return List of selections to be highlighted
|
||||
*/
|
||||
void highlightCurrentLine();
|
||||
QList<QTextEdit::ExtraSelection> highlightCurrentLine();
|
||||
/**
|
||||
* @brief Adds the PC line highlighting to the other current highlighting.
|
||||
* This should be called after highlightCurrentLine since that function
|
||||
* overrides all previous highlighting.
|
||||
* This is generally called after highlightCurrentLine
|
||||
* @return List of selections to be highlighted
|
||||
*/
|
||||
void highlightPCLine();
|
||||
QList<QTextEdit::ExtraSelection> highlightPCLine();
|
||||
void showDisasContextMenu(const QPoint &pt);
|
||||
void fontsUpdatedSlot();
|
||||
void colorsUpdatedSlot();
|
||||
void scrollInstructions(int count, bool clampToScrollBarRange = false);
|
||||
void seekPrev();
|
||||
void setPreviewMode(bool previewMode);
|
||||
QFontMetricsF getFontMetrics();
|
||||
QList<DisassemblyLine> getLines();
|
||||
|
||||
/**
|
||||
* @brief Forces the transient vertical scrollbar to appear on scroll
|
||||
*/
|
||||
void showTransientScrollBar();
|
||||
|
||||
void refreshDisasm(RVA offset = RVA_INVALID, RefreshMode mode = RefreshMode::Reset);
|
||||
|
||||
protected slots:
|
||||
void onSeekChanged(RVA offset, CutterCore::SeekHistoryType type);
|
||||
void refreshIfInRange(RVA offset);
|
||||
void instructionChanged(RVA offset);
|
||||
void refreshDisasm(RVA offset = RVA_INVALID);
|
||||
|
||||
bool updateMaxLines();
|
||||
|
||||
void cursorPositionChanged();
|
||||
/**
|
||||
* @brief Copies the currently highlighted disassembly text to the system clipboard
|
||||
*/
|
||||
void copySelection();
|
||||
|
||||
protected:
|
||||
DisassemblyContextMenu *mCtxMenu;
|
||||
DisassemblyScrollArea *mDisasScrollArea;
|
||||
DisassemblyTextEdit *mDisasTextEdit;
|
||||
DisassemblyLeftPanel *leftPanel;
|
||||
QList<DisassemblyLine> lines;
|
||||
|
||||
private:
|
||||
RVA topOffset;
|
||||
|
|
@ -101,7 +122,31 @@ private:
|
|||
int topOffsetHistoryPos = 0;
|
||||
QList<RVA> topOffsetHistory;
|
||||
|
||||
int startIndex = 0;
|
||||
int endIndex = 0;
|
||||
QList<DisassemblyLine> lines;
|
||||
|
||||
// Cursor selection related
|
||||
RVA selectionAnchorRVA = RVA_INVALID;
|
||||
/**
|
||||
* @brief metadata lines attached to instruction have the same offset as the instruction itself,
|
||||
* this keeps track of which metdata line the cursor was at inside the offset block
|
||||
*/
|
||||
int selectionAnchorSubIndex = 0;
|
||||
int selectionAnchorChar = 0;
|
||||
RVA selectionPosRVA = RVA_INVALID;
|
||||
/**
|
||||
* @brief same use-case as @ref selectionAnchorSubIndex but for cursor position instead if
|
||||
* anchor
|
||||
*/
|
||||
int selectionPosSubIndex = 0;
|
||||
int selectionPosChar = 0;
|
||||
|
||||
QList<RVA> breakpoints;
|
||||
/**
|
||||
* @brief Set whenever breakpoints have been updated or the screen has been manually refreshed
|
||||
*/
|
||||
bool breakpointsDirty = true;
|
||||
|
||||
void setupFonts();
|
||||
void setupColors();
|
||||
|
|
@ -110,9 +155,53 @@ private:
|
|||
|
||||
void connectCursorPositionChanged(bool disconnect);
|
||||
|
||||
void moveCursorRelative(bool up, bool page);
|
||||
void moveCursorRelative(QTextCursor::MoveOperation op, bool page);
|
||||
|
||||
void jumpToOffsetUnderCursor(const QTextCursor &);
|
||||
|
||||
/**
|
||||
* @brief Visually highlights the text on screen between the selection start (anchor) and the
|
||||
* current cursor position
|
||||
*/
|
||||
void updateSelection();
|
||||
void updateContextMenuSelection(bool hasSelection);
|
||||
|
||||
/**
|
||||
* @brief Finds the visual line number in the current view for a specific offset
|
||||
* @param offset The memory address (RVA) to look for
|
||||
* @param offsetSubIndex The specific sub-line to target if the instructions has metadata
|
||||
* attached to it
|
||||
* @return The index of the line, or -1 if not found
|
||||
*
|
||||
* @see DisassemblyHelper::getIndexInOffsetGroup()
|
||||
*/
|
||||
int getLineIndex(RVA offset, int offsetSubIndex) const;
|
||||
/**
|
||||
* @brief Refreshes the background colors in the disassembly view.
|
||||
* It applies highlights to both the line currently under the user's cursor and the Program
|
||||
* Counter (PC) line.
|
||||
*/
|
||||
void updateLineHighlights();
|
||||
/**
|
||||
* @brief Clears the current text selection so nothing is highlighted as selected
|
||||
*/
|
||||
void invalidateCursorSelection();
|
||||
|
||||
/**
|
||||
* @brief Metadata lines attached to an instruction have the same offset saved with it as the
|
||||
* instruction, This function returns the index of the current line within that offset
|
||||
* group/block
|
||||
* * e.g:
|
||||
* * @code
|
||||
* ; a comment
|
||||
* ; another comment <------ assume cursor is here
|
||||
* ; void func1()
|
||||
* 0x1000 mov rax, rax
|
||||
* @endcode
|
||||
*
|
||||
* Then the returned index is "1"
|
||||
*/
|
||||
int getIndexInOffsetGroup(const QTextCursor &cursor) const;
|
||||
};
|
||||
|
||||
class DisassemblyScrollArea : public QAbstractScrollArea
|
||||
|
|
@ -142,23 +231,30 @@ class DisassemblyTextEdit : public QPlainTextEdit
|
|||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit DisassemblyTextEdit(QWidget *parent = nullptr)
|
||||
: QPlainTextEdit(parent), lockScroll(false)
|
||||
{
|
||||
}
|
||||
explicit DisassemblyTextEdit(DisassemblyWidget *disasmWidget = nullptr);
|
||||
|
||||
void setLockScroll(bool lock) { this->lockScroll = lock; }
|
||||
|
||||
qreal textOffset() const;
|
||||
|
||||
void setCursorVisible(bool visible);
|
||||
|
||||
protected:
|
||||
bool viewportEvent(QEvent *event) override;
|
||||
void scrollContentsBy(int dx, int dy) override;
|
||||
void keyPressEvent(QKeyEvent *event) override;
|
||||
void mousePressEvent(QMouseEvent *event) override;
|
||||
void paintEvent(QPaintEvent *event) override;
|
||||
|
||||
void mouseMoveEvent(QMouseEvent *event) override;
|
||||
|
||||
private:
|
||||
bool lockScroll;
|
||||
QTimer *blinkTimer;
|
||||
bool cursorVisible = true;
|
||||
QColor cursorColor;
|
||||
|
||||
DisassemblyWidget *disasmWidget = nullptr;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -349,13 +349,16 @@ void GraphView::paint(QPainter &p, QPoint offset, QRect viewport, qreal scale, b
|
|||
|
||||
p.setBrush(Qt::gray);
|
||||
|
||||
// Always draw edges
|
||||
// TODO: Only draw edges if they are actually visible ...
|
||||
// Draw edges
|
||||
for (GraphEdge &edge : block.edges) {
|
||||
if (edge.polyline.empty()) {
|
||||
continue;
|
||||
}
|
||||
// Skips expensive polyline and arrow rendering if the edge's bounding box
|
||||
// is completely outside the current viewport.
|
||||
if (!edge.polyline.boundingRect().adjusted(-1, -1, 1, 1).intersects(windowF)) {
|
||||
continue;
|
||||
}
|
||||
const QPolygonF polyline = edge.polyline;
|
||||
const EdgeConfiguration ec =
|
||||
edgeConfiguration(block, &blocks[edge.target], interactive);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue