Support editing in hex editor (#3026)

This commit is contained in:
karliss 2022-09-06 18:46:39 +03:00 committed by GitHub
parent a2ed7971aa
commit 2e414069c5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 1084 additions and 98 deletions

File diff suppressed because it is too large Load Diff

View File

@ -14,7 +14,7 @@ struct BasicCursor
{ {
uint64_t address; uint64_t address;
bool pastEnd; bool pastEnd;
BasicCursor(uint64_t pos) : address(pos), pastEnd(false) {} explicit BasicCursor(uint64_t pos) : address(pos), pastEnd(false) {}
BasicCursor() : address(0), pastEnd(false) {} BasicCursor() : address(0), pastEnd(false) {}
BasicCursor &operator+=(int64_t offset) BasicCursor &operator+=(int64_t offset)
{ {
@ -35,6 +35,14 @@ struct BasicCursor
*this += int64_t(offset); *this += int64_t(offset);
return *this; return *this;
} }
bool moveChecked(int offset)
{
auto oldAddress = address;
*this += offset;
return address - oldAddress == uint64_t(offset);
}
BasicCursor &operator+=(uint64_t offset) BasicCursor &operator+=(uint64_t offset)
{ {
if (uint64_t(offset) > (UINT64_MAX - address)) { if (uint64_t(offset) > (UINT64_MAX - address)) {
@ -46,7 +54,10 @@ struct BasicCursor
} }
return *this; return *this;
} }
bool operator<(const BasicCursor &r) { return address < r.address || (pastEnd < r.pastEnd); } bool operator<(const BasicCursor &r) const
{
return address < r.address || (pastEnd < r.pastEnd);
}
}; };
struct HexCursor struct HexCursor
@ -74,9 +85,10 @@ struct HexCursor
class AbstractData class AbstractData
{ {
public: public:
virtual ~AbstractData() {} virtual ~AbstractData() = default;
virtual void fetch(uint64_t addr, int len) = 0; virtual void fetch(uint64_t addr, int len) = 0;
virtual bool copy(void *out, uint64_t adr, size_t len) = 0; virtual bool copy(void *out, uint64_t adr, size_t len) = 0;
virtual bool write(const uint8_t *in, uint64_t adr, size_t len) = 0;
virtual uint64_t maxIndex() = 0; virtual uint64_t maxIndex() = 0;
virtual uint64_t minIndex() = 0; virtual uint64_t minIndex() = 0;
}; };
@ -86,7 +98,7 @@ class BufferData : public AbstractData
public: public:
BufferData() { m_buffer.fill(0, 1); } BufferData() { m_buffer.fill(0, 1); }
BufferData(const QByteArray &buffer) explicit BufferData(const QByteArray &buffer)
{ {
if (buffer.isEmpty()) { if (buffer.isEmpty()) {
m_buffer.fill(0, 1); m_buffer.fill(0, 1);
@ -95,7 +107,7 @@ public:
} }
} }
~BufferData() override {} ~BufferData() override = default;
void fetch(uint64_t, int) override {} void fetch(uint64_t, int) override {}
@ -109,6 +121,16 @@ public:
return false; return false;
} }
bool write(const uint8_t *in, uint64_t addr, size_t len) override
{
if (addr < static_cast<uint64_t>(m_buffer.size())
&& (static_cast<uint64_t>(m_buffer.size()) - addr) < len) {
memcpy(m_buffer.data() + addr, in, len);
return true;
}
return false;
}
uint64_t maxIndex() override { return m_buffer.size() - 1; } uint64_t maxIndex() override { return m_buffer.size() - 1; }
private: private:
@ -118,8 +140,8 @@ private:
class MemoryData : public AbstractData class MemoryData : public AbstractData
{ {
public: public:
MemoryData() {} MemoryData() = default;
~MemoryData() override {} ~MemoryData() override = default;
static constexpr size_t BLOCK_SIZE = 4096; static constexpr size_t BLOCK_SIZE = 4096;
void fetch(uint64_t address, int length) override void fetch(uint64_t address, int length) override
@ -144,10 +166,11 @@ public:
bool copy(void *out, uint64_t addr, size_t len) override bool copy(void *out, uint64_t addr, size_t len) override
{ {
if (addr < m_firstBlockAddr || addr > m_lastValidAddr if (addr < m_firstBlockAddr
|| (m_lastValidAddr - addr + 1) || addr > m_lastValidAddr
< len /* do not merge with last check to handle overflows */ /* do not merge with previous check to handle overflows */
|| m_blocks.isEmpty()) { || (m_lastValidAddr - addr + 1) < len || m_blocks.isEmpty()) {
memset(out, 0xff, len);
return false; return false;
} }
@ -165,9 +188,47 @@ public:
return true; return true;
} }
virtual uint64_t maxIndex() override { return m_lastValidAddr; } void writeToCache(const uint8_t *in, uint64_t adr, size_t len)
{
if (adr < m_firstBlockAddr) {
uint64_t prefix = m_firstBlockAddr - adr;
if (prefix <= len) {
return;
}
in = in + prefix;
adr += prefix;
len -= prefix;
}
if (adr > m_lastValidAddr) {
return;
}
int offset = (int)(adr - m_firstBlockAddr);
int blockId = offset / BLOCK_SIZE;
int blockOffset = offset % BLOCK_SIZE;
while (len > 0 && blockId < m_blocks.size()) {
size_t l = BLOCK_SIZE - blockOffset;
l = std::min(l, len);
memcpy(m_blocks[blockId].data() + blockOffset, in, l);
len -= l;
blockOffset = 0;
adr += l;
in += l;
blockId += 1;
}
}
virtual uint64_t minIndex() override { return m_firstBlockAddr; } bool write(const uint8_t *in, uint64_t adr, size_t len) override
{
RzCoreLocked core(Core());
rz_core_write_at(core, adr, in, len);
writeToCache(in, adr, len);
emit Core()->instructionChanged(adr);
return true;
}
uint64_t maxIndex() override { return std::numeric_limits<uint64_t>::max(); }
uint64_t minIndex() override { return m_firstBlockAddr; }
private: private:
QVector<QByteArray> m_blocks; QVector<QByteArray> m_blocks;
@ -178,7 +239,11 @@ private:
class HexSelection class HexSelection
{ {
public: public:
HexSelection() { m_empty = true; } HexSelection()
{
m_empty = true;
m_start = m_end = 0;
}
inline void init(BasicCursor addr) inline void init(BasicCursor addr)
{ {
@ -189,7 +254,8 @@ public:
void set(uint64_t start, uint64_t end) void set(uint64_t start, uint64_t end)
{ {
m_empty = false; m_empty = false;
m_init = m_start = start; m_init = BasicCursor(start);
m_start = start;
m_end = end; m_end = end;
} }
@ -219,7 +285,7 @@ public:
bool contains(uint64_t pos) const { return !m_empty && m_start <= pos && pos <= m_end; } bool contains(uint64_t pos) const { return !m_empty && m_start <= pos && pos <= m_end; }
uint64_t size() uint64_t size() const
{ {
uint64_t size = 0; uint64_t size = 0;
if (!isEmpty()) if (!isEmpty())
@ -227,9 +293,9 @@ public:
return size; return size;
} }
inline bool isEmpty() { return m_empty; } inline bool isEmpty() const { return m_empty; }
inline uint64_t start() { return m_start; } inline uint64_t start() const { return m_start; }
inline uint64_t end() { return m_end; } inline uint64_t end() const { return m_end; }
private: private:
BasicCursor m_init; BasicCursor m_init;
@ -244,7 +310,7 @@ class HexWidget : public QScrollArea
public: public:
explicit HexWidget(QWidget *parent = nullptr); explicit HexWidget(QWidget *parent = nullptr);
~HexWidget(); ~HexWidget() override = default;
void setMonospaceFont(const QFont &font); void setMonospaceFont(const QFont &font);
@ -258,10 +324,12 @@ public:
ItemFormatFloat ItemFormatFloat
}; };
enum class ColumnMode { Fixed, PowerOf2 }; enum class ColumnMode { Fixed, PowerOf2 };
enum class EditWordState { Read, WriteNotStarted, WriteNotEdited, WriteEdited };
enum class HexNavigationMode { Words, WordChar, AnyChar };
void setItemSize(int nbytes); void setItemSize(int nbytes);
void setItemFormat(ItemFormat format); void setItemFormat(ItemFormat format);
void setItemEndianess(bool bigEndian); void setItemEndianness(bool bigEndian);
void setItemGroupSize(int size); void setItemGroupSize(int size);
/** /**
* @brief Sets line size in bytes. * @brief Sets line size in bytes.
@ -292,7 +360,7 @@ public slots:
void refresh(); void refresh();
void updateColors(); void updateColors();
signals: signals:
void selectionChanged(Selection selection); void selectionChanged(HexWidget::Selection selection);
void positionChanged(RVA start); void positionChanged(RVA start);
protected: protected:
@ -300,10 +368,12 @@ protected:
void resizeEvent(QResizeEvent *event) override; void resizeEvent(QResizeEvent *event) override;
void mouseMoveEvent(QMouseEvent *event) override; void mouseMoveEvent(QMouseEvent *event) override;
void mousePressEvent(QMouseEvent *event) override; void mousePressEvent(QMouseEvent *event) override;
void mouseDoubleClickEvent(QMouseEvent *event) override;
void mouseReleaseEvent(QMouseEvent *event) override; void mouseReleaseEvent(QMouseEvent *event) override;
void wheelEvent(QWheelEvent *event) override; void wheelEvent(QWheelEvent *event) override;
void keyPressEvent(QKeyEvent *event) override; void keyPressEvent(QKeyEvent *event) override;
void contextMenuEvent(QContextMenuEvent *event) override; void contextMenuEvent(QContextMenuEvent *event) override;
bool event(QEvent *event) override;
private slots: private slots:
void onCursorBlinked(); void onCursorBlinked();
@ -311,8 +381,8 @@ private slots:
void copy(); void copy();
void copyAddress(); void copyAddress();
void onRangeDialogAccepted(); void onRangeDialogAccepted();
void on_actionAddComment_triggered(); void onActionAddCommentTriggered();
void on_actionDeleteComment_triggered(); void onActionDeleteCommentTriggered();
// Write command slots // Write command slots
void w_writeString(); void w_writeString();
@ -326,6 +396,9 @@ private slots:
void w_writeWideString(); void w_writeWideString();
void w_writeCString(); void w_writeCString();
void onKeyboardEditTriggered(bool enabled);
void onKeyboardEditChanged(bool enabled);
private: private:
void updateItemLength(); void updateItemLength();
void updateCounts(); void updateCounts();
@ -338,12 +411,15 @@ private:
void updateMetrics(); void updateMetrics();
void updateAreasPosition(); void updateAreasPosition();
void updateAreasHeight(); void updateAreasHeight();
void moveCursor(int offset, bool select = false); enum class OverflowMove { Clamp, Ignore };
bool moveCursor(int offset, bool select = false,
OverflowMove overflowMove = OverflowMove::Clamp);
void moveCursorKeepEditOffset(int byteOffset, bool select, OverflowMove overflowMove);
void setCursorAddr(BasicCursor addr, bool select = false); void setCursorAddr(BasicCursor addr, bool select = false);
void updateCursorMeta(); void updateCursorMeta();
void setCursorOnAscii(bool ascii); void setCursorOnAscii(bool ascii);
bool isItemDifferentAt(uint64_t address); bool isItemDifferentAt(uint64_t address);
const QColor itemColor(uint8_t byte); QColor itemColor(uint8_t byte);
QVariant readItem(int offset, QColor *color = nullptr); QVariant readItem(int offset, QColor *color = nullptr);
QString renderItem(int offset, QColor *color = nullptr); QString renderItem(int offset, QColor *color = nullptr);
QChar renderAscii(int offset, QColor *color = nullptr); QChar renderAscii(int offset, QColor *color = nullptr);
@ -359,12 +435,13 @@ private:
/** /**
* @brief Convert mouse position to address. * @brief Convert mouse position to address.
* @param point mouse position in widget * @param point mouse position in widget
* @param middle start next position from middle of symbol. Use middle=true for vertical cursror * @param middle start next position from middle of symbol. Use middle=true for vertical cursor
* position between symbols, middle=false for insert mode cursor and getting symbol under * position between symbols, middle=false for insert mode cursor and getting symbol under
* cursor. * cursor.
* @return * @return
*/ */
BasicCursor screenPosToAddr(const QPoint &point, bool middle = false) const; BasicCursor screenPosToAddr(const QPoint &point, bool middle = false,
int *wordOffset = nullptr) const;
BasicCursor asciiPosToAddr(const QPoint &point, bool middle = false) const; BasicCursor asciiPosToAddr(const QPoint &point, bool middle = false) const;
BasicCursor currentAreaPosToAddr(const QPoint &point, bool middle = false) const; BasicCursor currentAreaPosToAddr(const QPoint &point, bool middle = false) const;
BasicCursor mousePosToAddr(const QPoint &point, bool middle = false) const; BasicCursor mousePosToAddr(const QPoint &point, bool middle = false) const;
@ -412,6 +489,27 @@ private:
inline uint64_t lastVisibleAddr() const { return (startAddress - 1) + bytesPerScreen(); } inline uint64_t lastVisibleAddr() const { return (startAddress - 1) + bytesPerScreen(); }
const QRectF &currentArea() const { return cursorOnAscii ? asciiArea : itemArea; } const QRectF &currentArea() const { return cursorOnAscii ? asciiArea : itemArea; }
bool isFixedWidth() const;
bool canKeyboardEdit();
bool flushCurrentlyEditedWord();
bool finishEditingWord(bool force = true);
void maybeFlushCharEdit();
void cancelEditedWord();
void startEditWord();
bool validCharForEdit(QChar digit);
void movePrevEditCharAny();
void typeOverwriteModeChar(QChar c);
HexNavigationMode defaultNavigationMode();
void refreshWordEditState();
bool parseWord(QString word, uint8_t *buf, size_t bufferSize) const;
bool handleAsciiWrite(QKeyEvent *event);
bool handleNumberWrite(QKeyEvent *event);
void writeZeros(uint64_t address, uint64_t length);
void hideWarningRect();
void showWarningRect(QRectF rect);
bool cursorEnabled; bool cursorEnabled;
bool cursorOnAscii; bool cursorOnAscii;
@ -436,14 +534,13 @@ private:
ItemFormat itemFormat; ItemFormat itemFormat;
bool itemBigEndian; bool itemBigEndian;
QString itemPrefix;
int visibleLines; int visibleLines;
uint64_t startAddress; uint64_t startAddress;
qreal charWidth; qreal charWidth;
int byteWidth;
qreal lineHeight; qreal lineHeight;
int addrCharLen; int addrCharLen;
int addrAreaWidth;
QFont monospaceFont; QFont monospaceFont;
bool showHeader; bool showHeader;
@ -460,6 +557,7 @@ private:
QColor b0x7fColor; QColor b0x7fColor;
QColor b0xffColor; QColor b0xffColor;
QColor printableColor; QColor printableColor;
QColor warningColor;
HexdumpRangeDialog rangeDialog; HexdumpRangeDialog rangeDialog;
@ -479,14 +577,30 @@ private:
QAction *actionCopyAddress; QAction *actionCopyAddress;
QAction *actionComment; QAction *actionComment;
QAction *actionDeleteComment; QAction *actionDeleteComment;
QAction *actionSetFlag;
QAction *actionSelectRange; QAction *actionSelectRange;
QAction *actionKeyboardEdit;
QList<QAction *> actionsWriteString; QList<QAction *> actionsWriteString;
QList<QAction *> actionsWriteOther; QList<QAction *> actionsWriteOther;
std::unique_ptr<AbstractData> oldData; std::unique_ptr<AbstractData> oldData;
std::unique_ptr<AbstractData> data; std::unique_ptr<AbstractData> data;
IOModesController ioModesController; IOModesController ioModesController;
int editWordPos = 0;
QString editWord;
EditWordState editWordState = EditWordState::Read;
HexNavigationMode navigationMode = HexNavigationMode::Words;
enum class EarlyEditFlush {
OnFinish,
EditNibble,
EditFixedWidthChar,
/* AllFormats(not implemented) */
};
EarlyEditFlush earlyEditFlush = EarlyEditFlush::EditFixedWidthChar;
bool warningRectVisible = false;
QRectF warningRect;
QTimer warningTimer;
}; };
#endif // HEXWIDGET_H #endif // HEXWIDGET_H