From cba31d826a71ef3afac1fba6383568ee85f1f258 Mon Sep 17 00:00:00 2001 From: Nikolaj Schlej Date: Sun, 23 Apr 2023 00:55:05 -0700 Subject: [PATCH] Replace QHexEdit2 with QHexView5 New widget adds better look-and-feel and comes with free proper dark mode support. --- UEFITool/CMakeLists.txt | 123 +- UEFITool/hexviewdialog.cpp | 3 +- UEFITool/hexviewdialog.h | 4 +- UEFITool/qhexedit2/chunks.cpp | 323 ----- UEFITool/qhexedit2/chunks.h | 77 - UEFITool/qhexedit2/commands.cpp | 167 --- UEFITool/qhexedit2/commands.h | 47 - UEFITool/qhexedit2/license.txt | 502 ------- UEFITool/qhexedit2/qhexedit.cpp | 1091 -------------- UEFITool/qhexedit2/qhexedit.h | 411 ------ UEFITool/qhexview5/LICENSE | 20 + .../qhexview5/model/buffer/qdevicebuffer.cpp | 130 ++ .../qhexview5/model/buffer/qdevicebuffer.h | 25 + .../qhexview5/model/buffer/qhexbuffer.cpp | 35 + UEFITool/qhexview5/model/buffer/qhexbuffer.h | 30 + .../qhexview5/model/buffer/qmemorybuffer.cpp | 19 + .../qhexview5/model/buffer/qmemorybuffer.h | 23 + .../qhexview5/model/commands/hexcommand.cpp | 3 + .../qhexview5/model/commands/hexcommand.h | 19 + .../model/commands/insertcommand.cpp | 16 + .../qhexview5/model/commands/insertcommand.h | 11 + .../model/commands/removecommand.cpp | 20 + .../qhexview5/model/commands/removecommand.h | 11 + .../model/commands/replacecommand.cpp | 20 + .../qhexview5/model/commands/replacecommand.h | 14 + UEFITool/qhexview5/model/qhexcursor.cpp | 129 ++ UEFITool/qhexview5/model/qhexcursor.h | 69 + UEFITool/qhexview5/model/qhexdelegate.cpp | 68 + UEFITool/qhexview5/model/qhexdelegate.h | 25 + UEFITool/qhexview5/model/qhexdocument.cpp | 99 ++ UEFITool/qhexview5/model/qhexdocument.h | 89 ++ UEFITool/qhexview5/model/qhexmetadata.cpp | 149 ++ UEFITool/qhexview5/model/qhexmetadata.h | 64 + UEFITool/qhexview5/model/qhexoptions.h | 55 + UEFITool/qhexview5/model/qhexutils.cpp | 314 ++++ UEFITool/qhexview5/model/qhexutils.h | 48 + UEFITool/qhexview5/qhexview.cpp | 1261 +++++++++++++++++ UEFITool/qhexview5/qhexview.h | 169 +++ UEFITool/uefitool.cpp | 2 +- UEFITool/uefitool.pro | 33 +- 40 files changed, 3033 insertions(+), 2685 deletions(-) delete mode 100644 UEFITool/qhexedit2/chunks.cpp delete mode 100644 UEFITool/qhexedit2/chunks.h delete mode 100644 UEFITool/qhexedit2/commands.cpp delete mode 100644 UEFITool/qhexedit2/commands.h delete mode 100644 UEFITool/qhexedit2/license.txt delete mode 100644 UEFITool/qhexedit2/qhexedit.cpp delete mode 100644 UEFITool/qhexedit2/qhexedit.h create mode 100644 UEFITool/qhexview5/LICENSE create mode 100644 UEFITool/qhexview5/model/buffer/qdevicebuffer.cpp create mode 100644 UEFITool/qhexview5/model/buffer/qdevicebuffer.h create mode 100644 UEFITool/qhexview5/model/buffer/qhexbuffer.cpp create mode 100644 UEFITool/qhexview5/model/buffer/qhexbuffer.h create mode 100644 UEFITool/qhexview5/model/buffer/qmemorybuffer.cpp create mode 100644 UEFITool/qhexview5/model/buffer/qmemorybuffer.h create mode 100644 UEFITool/qhexview5/model/commands/hexcommand.cpp create mode 100644 UEFITool/qhexview5/model/commands/hexcommand.h create mode 100644 UEFITool/qhexview5/model/commands/insertcommand.cpp create mode 100644 UEFITool/qhexview5/model/commands/insertcommand.h create mode 100644 UEFITool/qhexview5/model/commands/removecommand.cpp create mode 100644 UEFITool/qhexview5/model/commands/removecommand.h create mode 100644 UEFITool/qhexview5/model/commands/replacecommand.cpp create mode 100644 UEFITool/qhexview5/model/commands/replacecommand.h create mode 100644 UEFITool/qhexview5/model/qhexcursor.cpp create mode 100644 UEFITool/qhexview5/model/qhexcursor.h create mode 100644 UEFITool/qhexview5/model/qhexdelegate.cpp create mode 100644 UEFITool/qhexview5/model/qhexdelegate.h create mode 100644 UEFITool/qhexview5/model/qhexdocument.cpp create mode 100644 UEFITool/qhexview5/model/qhexdocument.h create mode 100644 UEFITool/qhexview5/model/qhexmetadata.cpp create mode 100644 UEFITool/qhexview5/model/qhexmetadata.h create mode 100644 UEFITool/qhexview5/model/qhexoptions.h create mode 100644 UEFITool/qhexview5/model/qhexutils.cpp create mode 100644 UEFITool/qhexview5/model/qhexutils.h create mode 100644 UEFITool/qhexview5/qhexview.cpp create mode 100644 UEFITool/qhexview5/qhexview.h diff --git a/UEFITool/CMakeLists.txt b/UEFITool/CMakeLists.txt index 8df2aa3..aef60d0 100644 --- a/UEFITool/CMakeLists.txt +++ b/UEFITool/CMakeLists.txt @@ -23,6 +23,7 @@ SET(PROJECT_HEADERS hexviewdialog.h gotobasedialog.h gotoaddressdialog.h + qhexview5/qhexview.h ) SET(PROJECT_SOURCES @@ -35,63 +36,73 @@ SET(PROJECT_SOURCES hexlineedit.cpp ffsfinder.cpp hexspinbox.cpp - qhexedit2/qhexedit.cpp - qhexedit2/chunks.cpp - qhexedit2/commands.cpp - ../common/fitparser.cpp - ../common/guiddatabase.cpp - ../common/nvram.cpp - ../common/nvramparser.cpp - ../common/meparser.cpp - ../common/ffsops.cpp - ../common/types.cpp - ../common/descriptor.cpp - ../common/ffs.cpp - ../common/peimage.cpp - ../common/utility.cpp - ../common/ffsbuilder.cpp - ../common/ffsparser.cpp - ../common/ffsreport.cpp - ../common/treeitem.cpp - ../common/treemodel.cpp - ../common/LZMA/LzmaCompress.c - ../common/LZMA/LzmaDecompress.c - ../common/LZMA/SDK/C/CpuArch.c - ../common/LZMA/SDK/C/Bra.c - ../common/LZMA/SDK/C/Bra86.c - ../common/LZMA/SDK/C/LzFind.c - ../common/LZMA/SDK/C/LzmaDec.c - ../common/LZMA/SDK/C/LzmaEnc.c - ../common/Tiano/EfiTianoDecompress.c - ../common/Tiano/EfiTianoCompress.c - ../common/Tiano/EfiTianoCompressLegacy.c - ../common/ustring.cpp - ../common/digest/sha1.c - ../common/digest/sha256.c - ../common/digest/sha512.c - ../common/digest/sm3.c + qhexview5/model/buffer/qhexbuffer.cpp + qhexview5/model/buffer/qdevicebuffer.cpp + qhexview5/model/buffer/qmemorybuffer.cpp + qhexview5/model/commands/hexcommand.cpp + qhexview5/model/commands/insertcommand.cpp + qhexview5/model/commands/removecommand.cpp + qhexview5/model/commands/replacecommand.cpp + qhexview5/model/qhexcursor.cpp + qhexview5/model/qhexdelegate.cpp + qhexview5/model/qhexdocument.cpp + qhexview5/model/qhexmetadata.cpp + qhexview5/model/qhexutils.cpp + qhexview5/qhexview.cpp + ../common/fitparser.cpp + ../common/guiddatabase.cpp + ../common/nvram.cpp + ../common/nvramparser.cpp + ../common/meparser.cpp + ../common/ffsops.cpp + ../common/types.cpp + ../common/descriptor.cpp + ../common/ffs.cpp + ../common/peimage.cpp + ../common/utility.cpp + ../common/ffsbuilder.cpp + ../common/ffsparser.cpp + ../common/ffsreport.cpp + ../common/treeitem.cpp + ../common/treemodel.cpp + ../common/LZMA/LzmaCompress.c + ../common/LZMA/LzmaDecompress.c + ../common/LZMA/SDK/C/CpuArch.c + ../common/LZMA/SDK/C/Bra.c + ../common/LZMA/SDK/C/Bra86.c + ../common/LZMA/SDK/C/LzFind.c + ../common/LZMA/SDK/C/LzmaDec.c + ../common/LZMA/SDK/C/LzmaEnc.c + ../common/Tiano/EfiTianoDecompress.c + ../common/Tiano/EfiTianoCompress.c + ../common/Tiano/EfiTianoCompressLegacy.c + ../common/ustring.cpp + ../common/digest/sha1.c + ../common/digest/sha256.c + ../common/digest/sha512.c + ../common/digest/sm3.c ../common/generated/ami_nvar.cpp - ../common/generated/intel_acbp_v1.cpp - ../common/generated/intel_acbp_v2.cpp - ../common/generated/intel_keym_v1.cpp - ../common/generated/intel_keym_v2.cpp - ../common/generated/intel_acm.cpp - ../common/kaitai/kaitaistream.cpp - ../common/zlib/adler32.c - ../common/zlib/compress.c - ../common/zlib/crc32.c - ../common/zlib/deflate.c - ../common/zlib/gzclose.c - ../common/zlib/gzlib.c - ../common/zlib/gzread.c - ../common/zlib/gzwrite.c - ../common/zlib/inflate.c - ../common/zlib/infback.c - ../common/zlib/inftrees.c - ../common/zlib/inffast.c - ../common/zlib/trees.c - ../common/zlib/uncompr.c - ../common/zlib/zutil.c + ../common/generated/intel_acbp_v1.cpp + ../common/generated/intel_acbp_v2.cpp + ../common/generated/intel_keym_v1.cpp + ../common/generated/intel_keym_v2.cpp + ../common/generated/intel_acm.cpp + ../common/kaitai/kaitaistream.cpp + ../common/zlib/adler32.c + ../common/zlib/compress.c + ../common/zlib/crc32.c + ../common/zlib/deflate.c + ../common/zlib/gzclose.c + ../common/zlib/gzlib.c + ../common/zlib/gzread.c + ../common/zlib/gzwrite.c + ../common/zlib/inflate.c + ../common/zlib/infback.c + ../common/zlib/inftrees.c + ../common/zlib/inffast.c + ../common/zlib/trees.c + ../common/zlib/uncompr.c + ../common/zlib/zutil.c ) QT_ADD_RESOURCES(PROJECT_SOURCES diff --git a/UEFITool/hexviewdialog.cpp b/UEFITool/hexviewdialog.cpp index 49a0729..0eccf5a 100644 --- a/UEFITool/hexviewdialog.cpp +++ b/UEFITool/hexviewdialog.cpp @@ -20,9 +20,8 @@ hexView(NULL) { // Create UI ui->setupUi(this); - hexView = new QHexEdit(this); + hexView = new QHexView(this); hexView->setReadOnly(true); - hexView->setUpperCase(true); ui->layout->addWidget(hexView); } diff --git a/UEFITool/hexviewdialog.h b/UEFITool/hexviewdialog.h index 1aa53e1..9a0e138 100644 --- a/UEFITool/hexviewdialog.h +++ b/UEFITool/hexviewdialog.h @@ -16,7 +16,7 @@ #include #include "../common/treemodel.h" -#include "qhexedit2/qhexedit.h" +#include "qhexview5/qhexview.h" #include "ui_hexviewdialog.h" class HexViewDialog : public QDialog @@ -38,7 +38,7 @@ public: void setFont(const QFont &font); private: - QHexEdit * hexView; + QHexView * hexView; }; #endif // HEXVIEWDIALOG_H diff --git a/UEFITool/qhexedit2/chunks.cpp b/UEFITool/qhexedit2/chunks.cpp deleted file mode 100644 index 80d3d86..0000000 --- a/UEFITool/qhexedit2/chunks.cpp +++ /dev/null @@ -1,323 +0,0 @@ -#include "chunks.h" -#include - -#define NORMAL 0 -#define HIGHLIGHTED 1 - -#define BUFFER_SIZE 0x10000 -#define CHUNK_SIZE 0x1000 -#define READ_CHUNK_MASK Q_INT64_C(0xfffffffffffff000) - -// ***************************************** Constructors and file settings - -Chunks::Chunks(QObject *parent): QObject(parent) -{ - QBuffer *buf = new QBuffer(this); - setIODevice(*buf); -} - -Chunks::Chunks(QIODevice &ioDevice, QObject *parent): QObject(parent) -{ - setIODevice(ioDevice); -} - -bool Chunks::setIODevice(QIODevice &ioDevice) -{ - _ioDevice = &ioDevice; - bool ok = _ioDevice->open(QIODevice::ReadOnly); - if (ok) // Try to open IODevice - { - _size = _ioDevice->size(); - _ioDevice->close(); - } - else // Fallback is an empty buffer - { - QBuffer *buf = new QBuffer(this); - _ioDevice = buf; - _size = 0; - } - _chunks.clear(); - _pos = 0; - return ok; -} - - -// ***************************************** Getting data out of Chunks - -QByteArray Chunks::data(qint64 pos, qint64 maxSize, QByteArray *highlighted) -{ - qint64 ioDelta = 0; - int chunkIdx = 0; - - Chunk chunk; - QByteArray buffer; - - // Do some checks and some arrangements - if (highlighted) - highlighted->clear(); - - if (pos >= _size) - return buffer; - - if (maxSize < 0) - maxSize = _size; - else - if ((pos + maxSize) > _size) - maxSize = _size - pos; - - _ioDevice->open(QIODevice::ReadOnly); - - while (maxSize > 0) - { - chunk.absPos = LLONG_MAX; - bool chunksLoopOngoing = true; - while ((chunkIdx < _chunks.count()) && chunksLoopOngoing) - { - // In this section, we track changes before our required data and - // we take the editdet data, if availible. ioDelta is a difference - // counter to justify the read pointer to the original data, if - // data in between was deleted or inserted. - - chunk = _chunks[chunkIdx]; - if (chunk.absPos > pos) - chunksLoopOngoing = false; - else - { - chunkIdx += 1; - qint64 count; - qint64 chunkOfs = pos - chunk.absPos; - if (maxSize > ((qint64)chunk.data.size() - chunkOfs)) - { - count = (qint64)chunk.data.size() - chunkOfs; - ioDelta += CHUNK_SIZE - chunk.data.size(); - } - else - count = maxSize; - if (count > 0) - { - buffer += chunk.data.mid((int)chunkOfs, (int)count); - maxSize -= count; - pos += count; - if (highlighted) - *highlighted += chunk.dataChanged.mid((int)chunkOfs, (int)count); - } - } - } - - if ((maxSize > 0) && (pos < chunk.absPos)) - { - // In this section, we read data from the original source. This only will - // happen, whe no copied data is available - - qint64 byteCount; - QByteArray readBuffer; - if ((chunk.absPos - pos) > maxSize) - byteCount = maxSize; - else - byteCount = chunk.absPos - pos; - - maxSize -= byteCount; - _ioDevice->seek(pos + ioDelta); - readBuffer = _ioDevice->read(byteCount); - buffer += readBuffer; - if (highlighted) - *highlighted += QByteArray(readBuffer.size(), NORMAL); - pos += readBuffer.size(); - } - } - _ioDevice->close(); - return buffer; -} - -bool Chunks::write(QIODevice &iODevice, qint64 pos, qint64 count) -{ - if (count == -1) - count = _size; - bool ok = iODevice.open(QIODevice::WriteOnly); - if (ok) - { - for (qint64 idx=pos; idx < count; idx += BUFFER_SIZE) - { - QByteArray ba = data(idx, BUFFER_SIZE); - iODevice.write(ba); - } - iODevice.close(); - } - return ok; -} - - -// ***************************************** Set and get highlighting infos - -void Chunks::setDataChanged(qint64 pos, bool dataChanged) -{ - if ((pos < 0) || (pos >= _size)) - return; - int chunkIdx = getChunkIndex(pos); - qint64 posInBa = pos - _chunks[chunkIdx].absPos; - _chunks[chunkIdx].dataChanged[(int)posInBa] = char(dataChanged); -} - -bool Chunks::dataChanged(qint64 pos) -{ - QByteArray highlighted; - data(pos, 1, &highlighted); - return bool(highlighted.at(0)); -} - - -// ***************************************** Search API - -qint64 Chunks::indexOf(const QByteArray &ba, qint64 from) -{ - qint64 result = -1; - QByteArray buffer; - - for (qint64 pos=from; (pos < _size) && (result < 0); pos += BUFFER_SIZE) - { - buffer = data(pos, BUFFER_SIZE + ba.size() - 1); - int findPos = (int)buffer.indexOf(ba); - if (findPos >= 0) - result = pos + (qint64)findPos; - } - return result; -} - -qint64 Chunks::lastIndexOf(const QByteArray &ba, qint64 from) -{ - qint64 result = -1; - QByteArray buffer; - - for (qint64 pos=from; (pos > 0) && (result < 0); pos -= BUFFER_SIZE) - { - qint64 sPos = pos - BUFFER_SIZE - (qint64)ba.size() + 1; - if (sPos < 0) - sPos = 0; - buffer = data(sPos, pos - sPos); - int findPos = (int)buffer.lastIndexOf(ba); - if (findPos >= 0) - result = sPos + (qint64)findPos; - } - return result; -} - - -// ***************************************** Char manipulations - -bool Chunks::insert(qint64 pos, char b) -{ - if ((pos < 0) || (pos > _size)) - return false; - int chunkIdx; - if (pos == _size) - chunkIdx = getChunkIndex(pos-1); - else - chunkIdx = getChunkIndex(pos); - qint64 posInBa = pos - _chunks[chunkIdx].absPos; - _chunks[chunkIdx].data.insert((int)posInBa, b); - _chunks[chunkIdx].dataChanged.insert((int)posInBa, char(1)); - for (int idx=chunkIdx+1; idx < _chunks.size(); idx++) - _chunks[idx].absPos += 1; - _size += 1; - _pos = pos; - return true; -} - -bool Chunks::overwrite(qint64 pos, char b) -{ - if ((pos < 0) || (pos >= _size)) - return false; - int chunkIdx = getChunkIndex(pos); - qint64 posInBa = pos - _chunks[chunkIdx].absPos; - _chunks[chunkIdx].data[(int)posInBa] = b; - _chunks[chunkIdx].dataChanged[(int)posInBa] = char(1); - _pos = pos; - return true; -} - -bool Chunks::removeAt(qint64 pos) -{ - if ((pos < 0) || (pos >= _size)) - return false; - int chunkIdx = getChunkIndex(pos); - qint64 posInBa = pos - _chunks[chunkIdx].absPos; - _chunks[chunkIdx].data.remove((int)posInBa, 1); - _chunks[chunkIdx].dataChanged.remove((int)posInBa, 1); - for (int idx=chunkIdx+1; idx < _chunks.size(); idx++) - _chunks[idx].absPos -= 1; - _size -= 1; - _pos = pos; - return true; -} - - -// ***************************************** Utility functions - -char Chunks::operator[](qint64 pos) -{ - return data(pos, 1)[0]; -} - -qint64 Chunks::pos() -{ - return _pos; -} - -qint64 Chunks::size() -{ - return _size; -} - -int Chunks::getChunkIndex(qint64 absPos) -{ - // This routine checks, if there is already a copied chunk available. If os, it - // returns a reference to it. If there is no copied chunk available, original - // data will be copied into a new chunk. - - int foundIdx = -1; - int insertIdx = 0; - qint64 ioDelta = 0; - - - for (int idx=0; idx < _chunks.size(); idx++) - { - Chunk chunk = _chunks[idx]; - if ((absPos >= chunk.absPos) && (absPos < (chunk.absPos + chunk.data.size()))) - { - foundIdx = idx; - break; - } - if (absPos < chunk.absPos) - { - insertIdx = idx; - break; - } - ioDelta += chunk.data.size() - CHUNK_SIZE; - insertIdx = idx + 1; - } - - if (foundIdx == -1) - { - Chunk newChunk; - qint64 readAbsPos = absPos - ioDelta; - qint64 readPos = (readAbsPos & READ_CHUNK_MASK); - _ioDevice->open(QIODevice::ReadOnly); - _ioDevice->seek(readPos); - newChunk.data = _ioDevice->read(CHUNK_SIZE); - _ioDevice->close(); - newChunk.absPos = absPos - (readAbsPos - readPos); - newChunk.dataChanged = QByteArray(newChunk.data.size(), char(0)); - _chunks.insert(insertIdx, newChunk); - foundIdx = insertIdx; - } - return foundIdx; -} - - -#ifdef MODUL_TEST -int Chunks::chunkSize() -{ - return _chunks.size(); -} - -#endif diff --git a/UEFITool/qhexedit2/chunks.h b/UEFITool/qhexedit2/chunks.h deleted file mode 100644 index 43df76c..0000000 --- a/UEFITool/qhexedit2/chunks.h +++ /dev/null @@ -1,77 +0,0 @@ -#ifndef CHUNKS_H -#define CHUNKS_H - -/** \cond docNever */ - -/*! The Chunks class is the storage backend for QHexEdit. - * - * When QHexEdit loads data, Chunks access them using a QIODevice interface. When the app uses - * a QByteArray interface, QBuffer is used to provide again a QIODevice like interface. No data - * will be changed, therefore Chunks opens the QIODevice in QIODevice::ReadOnly mode. After every - * access Chunks closes the QIODevice, that's why external applications can overwrite files while - * QHexEdit shows them. - * - * When the the user starts to edit the data, Chunks creates a local copy of a chunk of data (4 - * kilobytes) and notes all changes there. Parallel to that chunk, there is a second chunk, - * which keep track of which bytes are changed and which not. - * - */ - -#include - -struct Chunk -{ - QByteArray data; - QByteArray dataChanged; - qint64 absPos; -}; - -class Chunks: public QObject -{ -Q_OBJECT -public: - // Constructors and file settings - Chunks(QObject *parent); - Chunks(QIODevice &ioDevice, QObject *parent); - bool setIODevice(QIODevice &ioDevice); - - // Getting data out of Chunks - QByteArray data(qint64 pos=0, qint64 count=-1, QByteArray *highlighted=0); - bool write(QIODevice &iODevice, qint64 pos=0, qint64 count=-1); - - // Set and get highlighting infos - void setDataChanged(qint64 pos, bool dataChanged); - bool dataChanged(qint64 pos); - - // Search API - qint64 indexOf(const QByteArray &ba, qint64 from); - qint64 lastIndexOf(const QByteArray &ba, qint64 from); - - // Char manipulations - bool insert(qint64 pos, char b); - bool overwrite(qint64 pos, char b); - bool removeAt(qint64 pos); - - // Utility functions - char operator[](qint64 pos); - qint64 pos(); - qint64 size(); - - -private: - int getChunkIndex(qint64 absPos); - - QIODevice * _ioDevice; - qint64 _pos; - qint64 _size; - QList _chunks; - -#ifdef MODUL_TEST -public: - int chunkSize(); -#endif -}; - -/** \endcond docNever */ - -#endif // CHUNKS_H diff --git a/UEFITool/qhexedit2/commands.cpp b/UEFITool/qhexedit2/commands.cpp deleted file mode 100644 index c738fa2..0000000 --- a/UEFITool/qhexedit2/commands.cpp +++ /dev/null @@ -1,167 +0,0 @@ -#include "commands.h" -#include - - -// Helper class to store single byte commands -class CharCommand : public QUndoCommand -{ -public: - enum CCmd {insert, removeAt, overwrite}; - - CharCommand(Chunks * chunks, CCmd cmd, qint64 charPos, char newChar, - QUndoCommand *parent=0); - - void undo(); - void redo(); - bool mergeWith(const QUndoCommand *command); - int id() const { return 1234; } - -private: - Chunks * _chunks; - qint64 _charPos; - bool _wasChanged; - char _newChar; - char _oldChar; - CCmd _cmd; -}; - -CharCommand::CharCommand(Chunks * chunks, CCmd cmd, qint64 charPos, char newChar, QUndoCommand *parent) - : QUndoCommand(parent) -{ - _chunks = chunks; - _charPos = charPos; - _newChar = newChar; - _cmd = cmd; - _wasChanged = false; - _oldChar = ' '; -} - -bool CharCommand::mergeWith(const QUndoCommand *command) -{ - const CharCommand *nextCommand = static_cast(command); - bool result = false; - - if (_cmd != CharCommand::removeAt) - { - if (nextCommand->_cmd == overwrite) - if (nextCommand->_charPos == _charPos) - { - _newChar = nextCommand->_newChar; - result = true; - } - } - return result; -} - -void CharCommand::undo() -{ - switch (_cmd) - { - case insert: - _chunks->removeAt(_charPos); - break; - case overwrite: - _chunks->overwrite(_charPos, _oldChar); - _chunks->setDataChanged(_charPos, _wasChanged); - break; - case removeAt: - _chunks->insert(_charPos, _oldChar); - _chunks->setDataChanged(_charPos, _wasChanged); - break; - } -} - -void CharCommand::redo() -{ - switch (_cmd) - { - case insert: - _chunks->insert(_charPos, _newChar); - break; - case overwrite: - _oldChar = (*_chunks)[_charPos]; - _wasChanged = _chunks->dataChanged(_charPos); - _chunks->overwrite(_charPos, _newChar); - break; - case removeAt: - _oldChar = (*_chunks)[_charPos]; - _wasChanged = _chunks->dataChanged(_charPos); - _chunks->removeAt(_charPos); - break; - } -} - -UndoStack::UndoStack(Chunks * chunks, QObject * parent) - : QUndoStack(parent) -{ - _chunks = chunks; - _parent = parent; -} - -void UndoStack::insert(qint64 pos, char c) -{ - if ((pos >= 0) && (pos <= _chunks->size())) - { - QUndoCommand *cc = new CharCommand(_chunks, CharCommand::insert, pos, c); - this->push(cc); - } -} - -void UndoStack::insert(qint64 pos, const QByteArray &ba) -{ - if ((pos >= 0) && (pos <= _chunks->size())) - { - QString txt = QString(tr("Inserting %1 bytes")).arg(ba.size()); - beginMacro(txt); - for (int idx=0; idx < ba.size(); idx++) - { - QUndoCommand *cc = new CharCommand(_chunks, CharCommand::insert, pos + idx, ba.at(idx)); - this->push(cc); - } - endMacro(); - } -} - -void UndoStack::removeAt(qint64 pos, qint64 len) -{ - if ((pos >= 0) && (pos < _chunks->size())) - { - if (len==1) - { - QUndoCommand *cc = new CharCommand(_chunks, CharCommand::removeAt, pos, char(0)); - this->push(cc); - } - else - { - QString txt = QString(tr("Delete %1 chars")).arg(len); - beginMacro(txt); - for (qint64 cnt=0; cnt= 0) && (pos < _chunks->size())) - { - QUndoCommand *cc = new CharCommand(_chunks, CharCommand::overwrite, pos, c); - this->push(cc); - } -} - -void UndoStack::overwrite(qint64 pos, int len, const QByteArray &ba) -{ - if ((pos >= 0) && (pos < _chunks->size())) - { - QString txt = QString(tr("Overwrite %1 chars")).arg(len); - beginMacro(txt); - removeAt(pos, len); - insert(pos, ba); - endMacro(); - } -} diff --git a/UEFITool/qhexedit2/commands.h b/UEFITool/qhexedit2/commands.h deleted file mode 100644 index 9c34683..0000000 --- a/UEFITool/qhexedit2/commands.h +++ /dev/null @@ -1,47 +0,0 @@ -#ifndef COMMANDS_H -#define COMMANDS_H - -/** \cond docNever */ - -#include - -#include "chunks.h" - -/*! CharCommand is a class to provid undo/redo functionality in QHexEdit. -A QUndoCommand represents a single editing action on a document. CharCommand -is responsable for manipulations on single chars. It can insert. overwrite and -remove characters. A manipulation stores allways two actions -1. redo (or do) action -2. undo action. - -CharCommand also supports command compression via mergeWidht(). This allows -the user to execute a undo command contation e.g. 3 steps in a single command. -If you for example insert a new byt "34" this means for the editor doing 3 -steps: insert a "00", overwrite it with "03" and the overwrite it with "34". These -3 steps are combined into a single step, insert a "34". - -The byte array oriented commands are just put into a set of single byte commands, -which are pooled together with the macroBegin() and macroEnd() functionality of -Qt's QUndoStack. -*/ - -class UndoStack : public QUndoStack -{ - Q_OBJECT - -public: - UndoStack(Chunks *chunks, QObject * parent=0); - void insert(qint64 pos, char c); - void insert(qint64 pos, const QByteArray &ba); - void removeAt(qint64 pos, qint64 len=1); - void overwrite(qint64 pos, char c); - void overwrite(qint64 pos, int len, const QByteArray &ba); - -private: - Chunks * _chunks; - QObject * _parent; -}; - -/** \endcond docNever */ - -#endif // COMMANDS_H diff --git a/UEFITool/qhexedit2/license.txt b/UEFITool/qhexedit2/license.txt deleted file mode 100644 index f166cc5..0000000 --- a/UEFITool/qhexedit2/license.txt +++ /dev/null @@ -1,502 +0,0 @@ - GNU LESSER GENERAL PUBLIC LICENSE - Version 2.1, February 1999 - - Copyright (C) 1991, 1999 Free Software Foundation, Inc. - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - -[This is the first released version of the Lesser GPL. It also counts - as the successor of the GNU Library Public License, version 2, hence - the version number 2.1.] - - Preamble - - The licenses for most software are designed to take away your -freedom to share and change it. By contrast, the GNU General Public -Licenses are intended to guarantee your freedom to share and change -free software--to make sure the software is free for all its users. - - This license, the Lesser General Public License, applies to some -specially designated software packages--typically libraries--of the -Free Software Foundation and other authors who decide to use it. You -can use it too, but we suggest you first think carefully about whether -this license or the ordinary General Public License is the better -strategy to use in any particular case, based on the explanations below. - - When we speak of free software, we are referring to freedom of use, -not price. Our General Public Licenses are designed to make sure that -you have the freedom to distribute copies of free software (and charge -for this service if you wish); that you receive source code or can get -it if you want it; that you can change the software and use pieces of -it in new free programs; and that you are informed that you can do -these things. - - To protect your rights, we need to make restrictions that forbid -distributors to deny you these rights or to ask you to surrender these -rights. These restrictions translate to certain responsibilities for -you if you distribute copies of the library or if you modify it. - - For example, if you distribute copies of the library, whether gratis -or for a fee, you must give the recipients all the rights that we gave -you. You must make sure that they, too, receive or can get the source -code. If you link other code with the library, you must provide -complete object files to the recipients, so that they can relink them -with the library after making changes to the library and recompiling -it. And you must show them these terms so they know their rights. - - We protect your rights with a two-step method: (1) we copyright the -library, and (2) we offer you this license, which gives you legal -permission to copy, distribute and/or modify the library. - - To protect each distributor, we want to make it very clear that -there is no warranty for the free library. Also, if the library is -modified by someone else and passed on, the recipients should know -that what they have is not the original version, so that the original -author's reputation will not be affected by problems that might be -introduced by others. - - Finally, software patents pose a constant threat to the existence of -any free program. We wish to make sure that a company cannot -effectively restrict the users of a free program by obtaining a -restrictive license from a patent holder. Therefore, we insist that -any patent license obtained for a version of the library must be -consistent with the full freedom of use specified in this license. - - Most GNU software, including some libraries, is covered by the -ordinary GNU General Public License. This license, the GNU Lesser -General Public License, applies to certain designated libraries, and -is quite different from the ordinary General Public License. We use -this license for certain libraries in order to permit linking those -libraries into non-free programs. - - When a program is linked with a library, whether statically or using -a shared library, the combination of the two is legally speaking a -combined work, a derivative of the original library. The ordinary -General Public License therefore permits such linking only if the -entire combination fits its criteria of freedom. The Lesser General -Public License permits more lax criteria for linking other code with -the library. - - We call this license the "Lesser" General Public License because it -does Less to protect the user's freedom than the ordinary General -Public License. It also provides other free software developers Less -of an advantage over competing non-free programs. These disadvantages -are the reason we use the ordinary General Public License for many -libraries. However, the Lesser license provides advantages in certain -special circumstances. - - For example, on rare occasions, there may be a special need to -encourage the widest possible use of a certain library, so that it becomes -a de-facto standard. To achieve this, non-free programs must be -allowed to use the library. A more frequent case is that a free -library does the same job as widely used non-free libraries. In this -case, there is little to gain by limiting the free library to free -software only, so we use the Lesser General Public License. - - In other cases, permission to use a particular library in non-free -programs enables a greater number of people to use a large body of -free software. For example, permission to use the GNU C Library in -non-free programs enables many more people to use the whole GNU -operating system, as well as its variant, the GNU/Linux operating -system. - - Although the Lesser General Public License is Less protective of the -users' freedom, it does ensure that the user of a program that is -linked with the Library has the freedom and the wherewithal to run -that program using a modified version of the Library. - - The precise terms and conditions for copying, distribution and -modification follow. Pay close attention to the difference between a -"work based on the library" and a "work that uses the library". The -former contains code derived from the library, whereas the latter must -be combined with the library in order to run. - - GNU LESSER GENERAL PUBLIC LICENSE - TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION - - 0. This License Agreement applies to any software library or other -program which contains a notice placed by the copyright holder or -other authorized party saying it may be distributed under the terms of -this Lesser General Public License (also called "this License"). -Each licensee is addressed as "you". - - A "library" means a collection of software functions and/or data -prepared so as to be conveniently linked with application programs -(which use some of those functions and data) to form executables. - - The "Library", below, refers to any such software library or work -which has been distributed under these terms. A "work based on the -Library" means either the Library or any derivative work under -copyright law: that is to say, a work containing the Library or a -portion of it, either verbatim or with modifications and/or translated -straightforwardly into another language. (Hereinafter, translation is -included without limitation in the term "modification".) - - "Source code" for a work means the preferred form of the work for -making modifications to it. For a library, complete source code means -all the source code for all modules it contains, plus any associated -interface definition files, plus the scripts used to control compilation -and installation of the library. - - Activities other than copying, distribution and modification are not -covered by this License; they are outside its scope. The act of -running a program using the Library is not restricted, and output from -such a program is covered only if its contents constitute a work based -on the Library (independent of the use of the Library in a tool for -writing it). Whether that is true depends on what the Library does -and what the program that uses the Library does. - - 1. You may copy and distribute verbatim copies of the Library's -complete source code as you receive it, in any medium, provided that -you conspicuously and appropriately publish on each copy an -appropriate copyright notice and disclaimer of warranty; keep intact -all the notices that refer to this License and to the absence of any -warranty; and distribute a copy of this License along with the -Library. - - You may charge a fee for the physical act of transferring a copy, -and you may at your option offer warranty protection in exchange for a -fee. - - 2. You may modify your copy or copies of the Library or any portion -of it, thus forming a work based on the Library, and copy and -distribute such modifications or work under the terms of Section 1 -above, provided that you also meet all of these conditions: - - a) The modified work must itself be a software library. - - b) You must cause the files modified to carry prominent notices - stating that you changed the files and the date of any change. - - c) You must cause the whole of the work to be licensed at no - charge to all third parties under the terms of this License. - - d) If a facility in the modified Library refers to a function or a - table of data to be supplied by an application program that uses - the facility, other than as an argument passed when the facility - is invoked, then you must make a good faith effort to ensure that, - in the event an application does not supply such function or - table, the facility still operates, and performs whatever part of - its purpose remains meaningful. - - (For example, a function in a library to compute square roots has - a purpose that is entirely well-defined independent of the - application. Therefore, Subsection 2d requires that any - application-supplied function or table used by this function must - be optional: if the application does not supply it, the square - root function must still compute square roots.) - -These requirements apply to the modified work as a whole. If -identifiable sections of that work are not derived from the Library, -and can be reasonably considered independent and separate works in -themselves, then this License, and its terms, do not apply to those -sections when you distribute them as separate works. But when you -distribute the same sections as part of a whole which is a work based -on the Library, the distribution of the whole must be on the terms of -this License, whose permissions for other licensees extend to the -entire whole, and thus to each and every part regardless of who wrote -it. - -Thus, it is not the intent of this section to claim rights or contest -your rights to work written entirely by you; rather, the intent is to -exercise the right to control the distribution of derivative or -collective works based on the Library. - -In addition, mere aggregation of another work not based on the Library -with the Library (or with a work based on the Library) on a volume of -a storage or distribution medium does not bring the other work under -the scope of this License. - - 3. You may opt to apply the terms of the ordinary GNU General Public -License instead of this License to a given copy of the Library. To do -this, you must alter all the notices that refer to this License, so -that they refer to the ordinary GNU General Public License, version 2, -instead of to this License. (If a newer version than version 2 of the -ordinary GNU General Public License has appeared, then you can specify -that version instead if you wish.) Do not make any other change in -these notices. - - Once this change is made in a given copy, it is irreversible for -that copy, so the ordinary GNU General Public License applies to all -subsequent copies and derivative works made from that copy. - - This option is useful when you wish to copy part of the code of -the Library into a program that is not a library. - - 4. You may copy and distribute the Library (or a portion or -derivative of it, under Section 2) in object code or executable form -under the terms of Sections 1 and 2 above provided that you accompany -it with the complete corresponding machine-readable source code, which -must be distributed under the terms of Sections 1 and 2 above on a -medium customarily used for software interchange. - - If distribution of object code is made by offering access to copy -from a designated place, then offering equivalent access to copy the -source code from the same place satisfies the requirement to -distribute the source code, even though third parties are not -compelled to copy the source along with the object code. - - 5. A program that contains no derivative of any portion of the -Library, but is designed to work with the Library by being compiled or -linked with it, is called a "work that uses the Library". Such a -work, in isolation, is not a derivative work of the Library, and -therefore falls outside the scope of this License. - - However, linking a "work that uses the Library" with the Library -creates an executable that is a derivative of the Library (because it -contains portions of the Library), rather than a "work that uses the -library". The executable is therefore covered by this License. -Section 6 states terms for distribution of such executables. - - When a "work that uses the Library" uses material from a header file -that is part of the Library, the object code for the work may be a -derivative work of the Library even though the source code is not. -Whether this is true is especially significant if the work can be -linked without the Library, or if the work is itself a library. The -threshold for this to be true is not precisely defined by law. - - If such an object file uses only numerical parameters, data -structure layouts and accessors, and small macros and small inline -functions (ten lines or less in length), then the use of the object -file is unrestricted, regardless of whether it is legally a derivative -work. (Executables containing this object code plus portions of the -Library will still fall under Section 6.) - - Otherwise, if the work is a derivative of the Library, you may -distribute the object code for the work under the terms of Section 6. -Any executables containing that work also fall under Section 6, -whether or not they are linked directly with the Library itself. - - 6. As an exception to the Sections above, you may also combine or -link a "work that uses the Library" with the Library to produce a -work containing portions of the Library, and distribute that work -under terms of your choice, provided that the terms permit -modification of the work for the customer's own use and reverse -engineering for debugging such modifications. - - You must give prominent notice with each copy of the work that the -Library is used in it and that the Library and its use are covered by -this License. You must supply a copy of this License. If the work -during execution displays copyright notices, you must include the -copyright notice for the Library among them, as well as a reference -directing the user to the copy of this License. Also, you must do one -of these things: - - a) Accompany the work with the complete corresponding - machine-readable source code for the Library including whatever - changes were used in the work (which must be distributed under - Sections 1 and 2 above); and, if the work is an executable linked - with the Library, with the complete machine-readable "work that - uses the Library", as object code and/or source code, so that the - user can modify the Library and then relink to produce a modified - executable containing the modified Library. (It is understood - that the user who changes the contents of definitions files in the - Library will not necessarily be able to recompile the application - to use the modified definitions.) - - b) Use a suitable shared library mechanism for linking with the - Library. A suitable mechanism is one that (1) uses at run time a - copy of the library already present on the user's computer system, - rather than copying library functions into the executable, and (2) - will operate properly with a modified version of the library, if - the user installs one, as long as the modified version is - interface-compatible with the version that the work was made with. - - c) Accompany the work with a written offer, valid for at - least three years, to give the same user the materials - specified in Subsection 6a, above, for a charge no more - than the cost of performing this distribution. - - d) If distribution of the work is made by offering access to copy - from a designated place, offer equivalent access to copy the above - specified materials from the same place. - - e) Verify that the user has already received a copy of these - materials or that you have already sent this user a copy. - - For an executable, the required form of the "work that uses the -Library" must include any data and utility programs needed for -reproducing the executable from it. However, as a special exception, -the materials to be distributed need not include anything that is -normally distributed (in either source or binary form) with the major -components (compiler, kernel, and so on) of the operating system on -which the executable runs, unless that component itself accompanies -the executable. - - It may happen that this requirement contradicts the license -restrictions of other proprietary libraries that do not normally -accompany the operating system. Such a contradiction means you cannot -use both them and the Library together in an executable that you -distribute. - - 7. You may place library facilities that are a work based on the -Library side-by-side in a single library together with other library -facilities not covered by this License, and distribute such a combined -library, provided that the separate distribution of the work based on -the Library and of the other library facilities is otherwise -permitted, and provided that you do these two things: - - a) Accompany the combined library with a copy of the same work - based on the Library, uncombined with any other library - facilities. This must be distributed under the terms of the - Sections above. - - b) Give prominent notice with the combined library of the fact - that part of it is a work based on the Library, and explaining - where to find the accompanying uncombined form of the same work. - - 8. You may not copy, modify, sublicense, link with, or distribute -the Library except as expressly provided under this License. Any -attempt otherwise to copy, modify, sublicense, link with, or -distribute the Library is void, and will automatically terminate your -rights under this License. However, parties who have received copies, -or rights, from you under this License will not have their licenses -terminated so long as such parties remain in full compliance. - - 9. You are not required to accept this License, since you have not -signed it. However, nothing else grants you permission to modify or -distribute the Library or its derivative works. These actions are -prohibited by law if you do not accept this License. Therefore, by -modifying or distributing the Library (or any work based on the -Library), you indicate your acceptance of this License to do so, and -all its terms and conditions for copying, distributing or modifying -the Library or works based on it. - - 10. Each time you redistribute the Library (or any work based on the -Library), the recipient automatically receives a license from the -original licensor to copy, distribute, link with or modify the Library -subject to these terms and conditions. You may not impose any further -restrictions on the recipients' exercise of the rights granted herein. -You are not responsible for enforcing compliance by third parties with -this License. - - 11. If, as a consequence of a court judgment or allegation of patent -infringement or for any other reason (not limited to patent issues), -conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot -distribute so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you -may not distribute the Library at all. For example, if a patent -license would not permit royalty-free redistribution of the Library by -all those who receive copies directly or indirectly through you, then -the only way you could satisfy both it and this License would be to -refrain entirely from distribution of the Library. - -If any portion of this section is held invalid or unenforceable under any -particular circumstance, the balance of the section is intended to apply, -and the section as a whole is intended to apply in other circumstances. - -It is not the purpose of this section to induce you to infringe any -patents or other property right claims or to contest validity of any -such claims; this section has the sole purpose of protecting the -integrity of the free software distribution system which is -implemented by public license practices. Many people have made -generous contributions to the wide range of software distributed -through that system in reliance on consistent application of that -system; it is up to the author/donor to decide if he or she is willing -to distribute software through any other system and a licensee cannot -impose that choice. - -This section is intended to make thoroughly clear what is believed to -be a consequence of the rest of this License. - - 12. If the distribution and/or use of the Library is restricted in -certain countries either by patents or by copyrighted interfaces, the -original copyright holder who places the Library under this License may add -an explicit geographical distribution limitation excluding those countries, -so that distribution is permitted only in or among countries not thus -excluded. In such case, this License incorporates the limitation as if -written in the body of this License. - - 13. The Free Software Foundation may publish revised and/or new -versions of the Lesser General Public License from time to time. -Such new versions will be similar in spirit to the present version, -but may differ in detail to address new problems or concerns. - -Each version is given a distinguishing version number. If the Library -specifies a version number of this License which applies to it and -"any later version", you have the option of following the terms and -conditions either of that version or of any later version published by -the Free Software Foundation. If the Library does not specify a -license version number, you may choose any version ever published by -the Free Software Foundation. - - 14. If you wish to incorporate parts of the Library into other free -programs whose distribution conditions are incompatible with these, -write to the author to ask for permission. For software which is -copyrighted by the Free Software Foundation, write to the Free -Software Foundation; we sometimes make exceptions for this. Our -decision will be guided by the two goals of preserving the free status -of all derivatives of our free software and of promoting the sharing -and reuse of software generally. - - NO WARRANTY - - 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO -WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. -EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR -OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY -KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE -LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME -THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - - 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN -WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY -AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU -FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR -CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE -LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING -RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A -FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF -SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH -DAMAGES. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Libraries - - If you develop a new library, and you want it to be of the greatest -possible use to the public, we recommend making it free software that -everyone can redistribute and change. You can do so by permitting -redistribution under these terms (or, alternatively, under the terms of the -ordinary General Public License). - - To apply these terms, attach the following notices to the library. It is -safest to attach them to the start of each source file to most effectively -convey the exclusion of warranty; and each file should have at least the -"copyright" line and a pointer to where the full notice is found. - - - Copyright (C) - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - -Also add information on how to contact you by electronic and paper mail. - -You should also get your employer (if you work as a programmer) or your -school, if any, to sign a "copyright disclaimer" for the library, if -necessary. Here is a sample; alter the names: - - Yoyodyne, Inc., hereby disclaims all copyright interest in the - library `Frob' (a library for tweaking knobs) written by James Random Hacker. - - , 1 April 1990 - Ty Coon, President of Vice - -That's all there is to it! \ No newline at end of file diff --git a/UEFITool/qhexedit2/qhexedit.cpp b/UEFITool/qhexedit2/qhexedit.cpp deleted file mode 100644 index 42ff7aa..0000000 --- a/UEFITool/qhexedit2/qhexedit.cpp +++ /dev/null @@ -1,1091 +0,0 @@ -#include -#include -#include -#include -#include - -#include "qhexedit.h" -#include - - -// ********************************************************************** Constructor, destructor - -QHexEdit::QHexEdit(QWidget *parent) : QAbstractScrollArea(parent) -{ - _addressArea = true; - _addressWidth = 4; - _asciiArea = true; - _overwriteMode = true; - _highlighting = true; - _readOnly = false; - _cursorPosition = 0; - _lastEventSize = 0; - _hexCharsInLine = 47; - _bytesPerLine = 16; - _editAreaIsAscii = false; - - _chunks = new Chunks(this); - _undoStack = new UndoStack(_chunks, this); -#ifdef Q_OS_WIN32 - setFont(QFont("Courier", 10)); -#else - setFont(QFont("Monospace", 10)); -#endif - setAddressAreaColor(this->palette().alternateBase().color()); - setHighlightingColor(QColor(0xff, 0xff, 0x99, 0xff)); - setSelectionColor(this->palette().highlight().color()); - - connect(&_cursorTimer, SIGNAL(timeout()), this, SLOT(updateCursor())); - connect(verticalScrollBar(), SIGNAL(valueChanged(int)), this, SLOT(adjust())); - connect(horizontalScrollBar(), SIGNAL(valueChanged(int)), this, SLOT(adjust())); - connect(_undoStack, SIGNAL(indexChanged(int)), this, SLOT(dataChangedPrivate(int))); - - _cursorTimer.setInterval(500); - _cursorTimer.start(); - - setAddressWidth(4); - setAddressArea(true); - setAsciiArea(true); - setOverwriteMode(true); - setHighlighting(true); - setReadOnly(false); - - init(); - -} - -QHexEdit::~QHexEdit() -{ -} - -// ********************************************************************** Properties - -void QHexEdit::setAddressArea(bool addressArea) -{ - _addressArea = addressArea; - adjust(); - setCursorPosition(_cursorPosition); - viewport()->update(); -} - -bool QHexEdit::addressArea() -{ - return _addressArea; -} - -void QHexEdit::setAddressAreaColor(const QColor &color) -{ - _addressAreaColor = color; - viewport()->update(); -} - -QColor QHexEdit::addressAreaColor() -{ - return _addressAreaColor; -} - -void QHexEdit::setAddressOffset(qint64 addressOffset) -{ - _addressOffset = addressOffset; - adjust(); - setCursorPosition(_cursorPosition); - viewport()->update(); -} - -qint64 QHexEdit::addressOffset() -{ - return _addressOffset; -} - -void QHexEdit::setAddressWidth(int addressWidth) -{ - _addressWidth = addressWidth; - adjust(); - setCursorPosition(_cursorPosition); - viewport()->update(); -} - -int QHexEdit::addressWidth() -{ - qint64 size = _chunks->size(); - int n = 1; - if (size > Q_INT64_C(0x100000000)){ n += 8; size /= Q_INT64_C(0x100000000);} - if (size > 0x10000){ n += 4; size /= 0x10000;} - if (size > 0x100){ n += 2; size /= 0x100;} - if (size > 0x10){ n += 1; } - - if (n > _addressWidth) - return n; - else - return _addressWidth; -} - -void QHexEdit::setAsciiArea(bool asciiArea) -{ - if (!asciiArea) - _editAreaIsAscii = false; - _asciiArea = asciiArea; - adjust(); - setCursorPosition(_cursorPosition); - viewport()->update(); -} - -bool QHexEdit::asciiArea() -{ - return _asciiArea; -} - -void QHexEdit::setBytesPerLine(int count) -{ - _bytesPerLine = count; - _hexCharsInLine = count * 3 - 1; - - adjust(); - setCursorPosition(_cursorPosition); - viewport()->update(); -} - -int QHexEdit::bytesPerLine() -{ - return _bytesPerLine; -} - -void QHexEdit::setCursorPosition(qint64 position) -{ - // 1. delete old cursor - _blink = false; - viewport()->update(_cursorRect); - - // 2. Check, if cursor in range? - if (position > (_chunks->size() * 2 - 1)) - position = _chunks->size() * 2 - (_overwriteMode ? 1 : 0); - - if (position < 0) - position = 0; - - // 3. Calc new position of cursor - _bPosCurrent = position / 2; - _pxCursorY = (int)((position / 2 - _bPosFirst) / _bytesPerLine + 1) * _pxCharHeight; - int x = (position % (2 * _bytesPerLine)); - if (_editAreaIsAscii) - { - _pxCursorX = x / 2 * _pxCharWidth + _pxPosAsciiX; - _cursorPosition = position & 0xFFFFFFFFFFFFFFFE; - } else { - _pxCursorX = (((x / 2) * 3) + (x % 2)) * _pxCharWidth + _pxPosHexX; - _cursorPosition = position; - } - - if (_overwriteMode) - _cursorRect = QRect(_pxCursorX - horizontalScrollBar()->value(), _pxCursorY + _pxCursorWidth, _pxCharWidth, _pxCursorWidth); - else - _cursorRect = QRect(_pxCursorX - horizontalScrollBar()->value(), _pxCursorY - _pxCharHeight + 4, _pxCursorWidth, _pxCharHeight); - - // 4. Immediately draw new cursor - _blink = true; - viewport()->update(_cursorRect); - emit currentAddressChanged(_bPosCurrent); -} - -qint64 QHexEdit::cursorPosition(QPoint pos) -{ - // Calc cursor position depending on a graphical position - qint64 result = -1; - int posX = pos.x() + horizontalScrollBar()->value(); - int posY = pos.y() - 3; - if ((posX >= _pxPosHexX) && (posX < (_pxPosHexX + (1 + _hexCharsInLine) * _pxCharWidth))) - { - _editAreaIsAscii = false; - int x = (posX - _pxPosHexX) / _pxCharWidth; - x = (x / 3) * 2 + x % 3; - int y = (posY / _pxCharHeight) * 2 * _bytesPerLine; - result = _bPosFirst * 2 + x + y; - } else - if (_asciiArea && (posX >= _pxPosAsciiX) && (posX < (_pxPosAsciiX + (1 + _bytesPerLine) * _pxCharWidth))) - { - _editAreaIsAscii = true; - int x = 2 * (posX - _pxPosAsciiX) / _pxCharWidth; - int y = (posY / _pxCharHeight) * 2 * _bytesPerLine; - result = _bPosFirst * 2 + x + y; - } - return result; -} - -qint64 QHexEdit::cursorPosition() -{ - return _cursorPosition; -} - -void QHexEdit::setData(const QByteArray &ba) -{ - _data = ba; - _bData.setData(_data); - setData(_bData); -} - -QByteArray QHexEdit::data() -{ - return _chunks->data(0, -1); -} - -void QHexEdit::setHighlighting(bool highlighting) -{ - _highlighting = highlighting; - viewport()->update(); -} - -bool QHexEdit::highlighting() -{ - return _highlighting; -} - -void QHexEdit::setHighlightingColor(const QColor &color) -{ - _brushHighlighted = QBrush(color); - _penHighlighted = QPen(viewport()->palette().color(QPalette::WindowText)); - viewport()->update(); -} - -QColor QHexEdit::highlightingColor() -{ - return _brushHighlighted.color(); -} - -void QHexEdit::setOverwriteMode(bool overwriteMode) -{ - _overwriteMode = overwriteMode; - emit overwriteModeChanged(overwriteMode); -} - -bool QHexEdit::overwriteMode() -{ - return _overwriteMode; -} - -void QHexEdit::setSelectionColor(const QColor &color) -{ - _brushSelection = QBrush(color); - _penSelection = QPen(Qt::white); - viewport()->update(); -} - -QColor QHexEdit::selectionColor() -{ - return _brushSelection.color(); -} - -bool QHexEdit::isReadOnly() -{ - return _readOnly; -} - -void QHexEdit::setReadOnly(bool readOnly) -{ - _readOnly = readOnly; -} - -bool QHexEdit::isUpperCase() -{ - return _upperCase; -} - -void QHexEdit::setUpperCase(bool upperCase) -{ - _upperCase = upperCase; -} - -// ********************************************************************** Access to data of qhexedit -bool QHexEdit::setData(QIODevice &iODevice) -{ - bool ok = _chunks->setIODevice(iODevice); - init(); - dataChangedPrivate(); - return ok; -} - -QByteArray QHexEdit::dataAt(qint64 pos, qint64 count) -{ - return _chunks->data(pos, count); -} - -bool QHexEdit::write(QIODevice &iODevice, qint64 pos, qint64 count) -{ - return _chunks->write(iODevice, pos, count); -} - -// ********************************************************************** Char handling -void QHexEdit::insert(qint64 index, char ch) -{ - _undoStack->insert(index, ch); - refresh(); -} - -void QHexEdit::remove(qint64 index, qint64 len) -{ - _undoStack->removeAt(index, len); - refresh(); -} - -void QHexEdit::replace(qint64 index, char ch) -{ - _undoStack->overwrite(index, ch); - refresh(); -} - -// ********************************************************************** ByteArray handling -void QHexEdit::insert(qint64 pos, const QByteArray &ba) -{ - _undoStack->insert(pos, ba); - refresh(); -} - -void QHexEdit::replace(qint64 pos, qint64 len, const QByteArray &ba) -{ - _undoStack->overwrite(pos, (int)len, ba); - refresh(); -} - -// ********************************************************************** Utility functions -void QHexEdit::ensureVisible() -{ - if (_cursorPosition < (_bPosFirst * 2)) - verticalScrollBar()->setValue((int)(_cursorPosition / 2 / _bytesPerLine)); - if (_cursorPosition > ((_bPosFirst + (_rowsShown - 1)*_bytesPerLine) * 2)) - verticalScrollBar()->setValue((int)(_cursorPosition / 2 / _bytesPerLine) - _rowsShown + 1); - if (_pxCursorX < horizontalScrollBar()->value()) - horizontalScrollBar()->setValue(_pxCursorX); - if ((_pxCursorX + _pxCharWidth) > (horizontalScrollBar()->value() + viewport()->width())) - horizontalScrollBar()->setValue(_pxCursorX + _pxCharWidth - viewport()->width()); - viewport()->update(); -} - -qint64 QHexEdit::indexOf(const QByteArray &ba, qint64 from) -{ - qint64 pos = _chunks->indexOf(ba, from); - if (pos > -1) - { - qint64 curPos = pos*2; - setCursorPosition(curPos + ba.length()*2); - resetSelection(curPos); - setSelection(curPos + ba.length()*2); - ensureVisible(); - } - return pos; -} - -bool QHexEdit::isModified() -{ - return _modified; -} - -qint64 QHexEdit::lastIndexOf(const QByteArray &ba, qint64 from) -{ - qint64 pos = _chunks->lastIndexOf(ba, from); - if (pos > -1) - { - qint64 curPos = pos*2; - setCursorPosition(curPos - 1); - resetSelection(curPos); - setSelection(curPos + ba.length()*2); - ensureVisible(); - } - return pos; -} - -void QHexEdit::redo() -{ - _undoStack->redo(); - setCursorPosition(_chunks->pos()*(_editAreaIsAscii ? 1 : 2)); - refresh(); -} - -QString QHexEdit::selectionToReadableString() -{ - QByteArray ba = _chunks->data(getSelectionBegin(), getSelectionEnd() - getSelectionBegin()); - return toReadable(ba); -} - -void QHexEdit::setFont(const QFont &font) -{ - QWidget::setFont(font); - -#if ((QT_VERSION_MINOR >= 11) && (QT_VERSION_MAJOR == 5)) || (QT_VERSION_MAJOR >= 6) - _pxCharWidth = fontMetrics().horizontalAdvance('2'); -#else - _pxCharWidth = fontMetrics().width(QLatin1Char('2')); -#endif - - _pxCharHeight = fontMetrics().height(); - _pxGapAdr = _pxCharWidth / 2; - _pxGapAdrHex = _pxCharWidth; - _pxGapHexAscii = 2 * _pxCharWidth; - _pxCursorWidth = _pxCharHeight / 7; - _pxSelectionSub = _pxCharHeight / 5; - viewport()->update(); -} - -QString QHexEdit::toReadableString() -{ - QByteArray ba = _chunks->data(); - return toReadable(ba); -} - -void QHexEdit::undo() -{ - _undoStack->undo(); - setCursorPosition(_chunks->pos()*(_editAreaIsAscii ? 1 : 2)); - refresh(); -} - -// ********************************************************************** Handle events -void QHexEdit::keyPressEvent(QKeyEvent *event) -{ - // Cursor movements - if (event->matches(QKeySequence::MoveToNextChar)) - { - qint64 pos = _cursorPosition + 1; - if (_editAreaIsAscii) - pos += 1; - setCursorPosition(pos); - resetSelection(pos); - } - if (event->matches(QKeySequence::MoveToPreviousChar)) - { - qint64 pos = _cursorPosition - 1; - if (_editAreaIsAscii) - pos -= 1; - setCursorPosition(pos); - resetSelection(pos); - } - if (event->matches(QKeySequence::MoveToEndOfLine)) - { - qint64 pos = _cursorPosition - (_cursorPosition % (2 * _bytesPerLine)) + (2 * _bytesPerLine) - 1; - setCursorPosition(pos); - resetSelection(_cursorPosition); - } - if (event->matches(QKeySequence::MoveToStartOfLine)) - { - qint64 pos = _cursorPosition - (_cursorPosition % (2 * _bytesPerLine)); - setCursorPosition(pos); - resetSelection(_cursorPosition); - } - if (event->matches(QKeySequence::MoveToPreviousLine)) - { - setCursorPosition(_cursorPosition - (2 * _bytesPerLine)); - resetSelection(_cursorPosition); - } - if (event->matches(QKeySequence::MoveToNextLine)) - { - setCursorPosition(_cursorPosition + (2 * _bytesPerLine)); - resetSelection(_cursorPosition); - } - if (event->matches(QKeySequence::MoveToNextPage)) - { - setCursorPosition(_cursorPosition + (((_rowsShown - 1) * 2 * _bytesPerLine))); - resetSelection(_cursorPosition); - } - if (event->matches(QKeySequence::MoveToPreviousPage)) - { - setCursorPosition(_cursorPosition - (((_rowsShown - 1) * 2 * _bytesPerLine))); - resetSelection(_cursorPosition); - } - if (event->matches(QKeySequence::MoveToEndOfDocument)) - { - setCursorPosition(_chunks->size() * 2 ); - resetSelection(_cursorPosition); - } - if (event->matches(QKeySequence::MoveToStartOfDocument)) - { - setCursorPosition(0); - resetSelection(_cursorPosition); - } - - // Select commands - if (event->matches(QKeySequence::SelectAll)) - { - resetSelection(0); - setSelection(2 * _chunks->size() + 1); - } - if (event->matches(QKeySequence::SelectNextChar)) - { - qint64 pos = _cursorPosition + 1; - if (_editAreaIsAscii) - pos += 1; - setCursorPosition(pos); - setSelection(pos); - } - if (event->matches(QKeySequence::SelectPreviousChar)) - { - qint64 pos = _cursorPosition - 1; - if (_editAreaIsAscii) - pos -= 1; - setSelection(pos); - setCursorPosition(pos); - } - if (event->matches(QKeySequence::SelectEndOfLine)) - { - qint64 pos = _cursorPosition - (_cursorPosition % (2 * _bytesPerLine)) + (2 * _bytesPerLine) - 1; - setCursorPosition(pos); - setSelection(pos); - } - if (event->matches(QKeySequence::SelectStartOfLine)) - { - qint64 pos = _cursorPosition - (_cursorPosition % (2 * _bytesPerLine)); - setCursorPosition(pos); - setSelection(pos); - } - if (event->matches(QKeySequence::SelectPreviousLine)) - { - qint64 pos = _cursorPosition - (2 * _bytesPerLine); - setCursorPosition(pos); - setSelection(pos); - } - if (event->matches(QKeySequence::SelectNextLine)) - { - qint64 pos = _cursorPosition + (2 * _bytesPerLine); - setCursorPosition(pos); - setSelection(pos); - } - if (event->matches(QKeySequence::SelectNextPage)) - { - qint64 pos = _cursorPosition + (((viewport()->height() / _pxCharHeight) - 1) * 2 * _bytesPerLine); - setCursorPosition(pos); - setSelection(pos); - } - if (event->matches(QKeySequence::SelectPreviousPage)) - { - qint64 pos = _cursorPosition - (((viewport()->height() / _pxCharHeight) - 1) * 2 * _bytesPerLine); - setCursorPosition(pos); - setSelection(pos); - } - if (event->matches(QKeySequence::SelectEndOfDocument)) - { - qint64 pos = _chunks->size() * 2; - setCursorPosition(pos); - setSelection(pos); - } - if (event->matches(QKeySequence::SelectStartOfDocument)) - { - qint64 pos = 0; - setCursorPosition(pos); - setSelection(pos); - } - - // Edit Commands - if (!_readOnly) - { - /* Cut */ - if (event->matches(QKeySequence::Cut)) - { - QByteArray ba = _chunks->data(getSelectionBegin(), getSelectionEnd() - getSelectionBegin()).toHex(); - for (qint64 idx = 32; idx < ba.size(); idx +=33) - ba.insert((int)idx, "\n"); - QClipboard *clipboard = QApplication::clipboard(); - clipboard->setText(ba); - if (_overwriteMode) - { - qint64 len = getSelectionEnd() - getSelectionBegin(); - replace(getSelectionBegin(), (int)len, QByteArray((int)len, char(0))); - } - else - { - remove(getSelectionBegin(), getSelectionEnd() - getSelectionBegin()); - } - setCursorPosition(2 * getSelectionBegin()); - resetSelection(2 * getSelectionBegin()); - } else - - /* Paste */ - if (event->matches(QKeySequence::Paste)) - { - QClipboard *clipboard = QApplication::clipboard(); - QByteArray ba = QByteArray().fromHex(clipboard->text().toLatin1()); - if (_overwriteMode) - { - ba = ba.left((int)std::min(ba.size(), (_chunks->size() - _bPosCurrent))); - replace(_bPosCurrent, ba.size(), ba); - } - else - insert(_bPosCurrent, ba); - setCursorPosition(_cursorPosition + 2 * ba.size()); - resetSelection(getSelectionBegin()); - } else - - /* Delete char */ - if (event->matches(QKeySequence::Delete)) - { - if (getSelectionBegin() != getSelectionEnd()) - { - _bPosCurrent = getSelectionBegin(); - if (_overwriteMode) - { - QByteArray ba = QByteArray(getSelectionEnd() - getSelectionBegin(), char(0)); - replace(_bPosCurrent, ba.size(), ba); - } - else - { - remove(_bPosCurrent, getSelectionEnd() - getSelectionBegin()); - } - } - else - { - if (_overwriteMode) - replace(_bPosCurrent, char(0)); - else - remove(_bPosCurrent, 1); - } - setCursorPosition(2 * _bPosCurrent); - resetSelection(2 * _bPosCurrent); - } else - - /* Backspace */ - if ((event->key() == Qt::Key_Backspace) && (event->modifiers() == Qt::NoModifier)) - { - if (getSelectionBegin() != getSelectionEnd()) - { - _bPosCurrent = getSelectionBegin(); - setCursorPosition(2 * _bPosCurrent); - if (_overwriteMode) - { - QByteArray ba = QByteArray(getSelectionEnd() - getSelectionBegin(), char(0)); - replace(_bPosCurrent, ba.size(), ba); - } - else - { - remove(_bPosCurrent, getSelectionEnd() - getSelectionBegin()); - } - resetSelection(2 * _bPosCurrent); - } - else - { - bool behindLastByte = false; - if ((_cursorPosition / 2) == _chunks->size()) - behindLastByte = true; - - _bPosCurrent -= 1; - if (_overwriteMode) - replace(_bPosCurrent, char(0)); - else - remove(_bPosCurrent, 1); - - if (!behindLastByte) - _bPosCurrent -= 1; - - setCursorPosition(2 * _bPosCurrent); - resetSelection(2 * _bPosCurrent); - } - } else - - /* undo */ - if (event->matches(QKeySequence::Undo)) - { - undo(); - } else - - /* redo */ - if (event->matches(QKeySequence::Redo)) - { - redo(); - } else - - if ((QApplication::keyboardModifiers() == Qt::NoModifier) || - (QApplication::keyboardModifiers() == Qt::KeypadModifier) || - (QApplication::keyboardModifiers() == Qt::ShiftModifier) || - (QApplication::keyboardModifiers() == (Qt::AltModifier | Qt::ControlModifier)) || - (QApplication::keyboardModifiers() == Qt::GroupSwitchModifier)) - { - /* Hex and ascii input */ - int key; - if (_editAreaIsAscii) - key = (uchar)event->text()[0].toLatin1(); - else - key = int(event->text()[0].toLower().toLatin1()); - - if ((((key >= '0' && key <= '9') || (key >= 'a' && key <= 'f')) && _editAreaIsAscii == false) - || (key >= ' ' && _editAreaIsAscii)) - { - if (getSelectionBegin() != getSelectionEnd()) - { - if (_overwriteMode) - { - qint64 len = getSelectionEnd() - getSelectionBegin(); - replace(getSelectionBegin(), (int)len, QByteArray((int)len, char(0))); - } else - { - remove(getSelectionBegin(), getSelectionEnd() - getSelectionBegin()); - _bPosCurrent = getSelectionBegin(); - } - setCursorPosition(2 * _bPosCurrent); - resetSelection(2 * _bPosCurrent); - } - - // If insert mode, then insert a byte - if (_overwriteMode == false) - if ((_cursorPosition % 2) == 0) - insert(_bPosCurrent, char(0)); - - // Change content - if (_chunks->size() > 0) - { - char ch = key; - if (!_editAreaIsAscii){ - QByteArray hexValue = _chunks->data(_bPosCurrent, 1).toHex(); - if ((_cursorPosition % 2) == 0) - hexValue[0] = key; - else - hexValue[1] = key; - ch = QByteArray().fromHex(hexValue)[0]; - } - replace(_bPosCurrent, ch); - if (_editAreaIsAscii) - setCursorPosition(_cursorPosition + 2); - else - setCursorPosition(_cursorPosition + 1); - resetSelection(_cursorPosition); - } - } - } - } - - /* Copy */ - if (event->matches(QKeySequence::Copy)) - { - QByteArray ba = _chunks->data(getSelectionBegin(), getSelectionEnd() - getSelectionBegin()).toHex(); - for (qint64 idx = 32; idx < ba.size(); idx += 33) - ba.insert((int)idx, "\n"); - if(_upperCase) - ba = ba.toUpper(); - QClipboard *clipboard = QApplication::clipboard(); - clipboard->setText(ba); - } - - // Switch between insert/overwrite mode - if ((event->key() == Qt::Key_Insert) && (event->modifiers() == Qt::NoModifier)) - { - setOverwriteMode(!overwriteMode()); - setCursorPosition(_cursorPosition); - } - - // switch from hex to ascii edit - if (event->key() == Qt::Key_Tab && !_editAreaIsAscii){ - _editAreaIsAscii = true; - setCursorPosition(_cursorPosition); - } - - // switch from ascii to hex edit - if (event->key() == Qt::Key_Backtab && _editAreaIsAscii){ - _editAreaIsAscii = false; - setCursorPosition(_cursorPosition); - } - - refresh(); -} - -void QHexEdit::mouseMoveEvent(QMouseEvent * event) -{ - _blink = false; - viewport()->update(); - qint64 actPos = cursorPosition(event->pos()); - if (actPos >= 0) - { - setCursorPosition(actPos); - setSelection(actPos); - } -} - -void QHexEdit::mousePressEvent(QMouseEvent * event) -{ - _blink = false; - viewport()->update(); - qint64 cPos = cursorPosition(event->pos()); - if (cPos >= 0) - { - resetSelection(cPos); - setCursorPosition(cPos); - } -} - -void QHexEdit::paintEvent(QPaintEvent *event) -{ - QPainter painter(viewport()); - int pxOfsX = horizontalScrollBar()->value(); - - if (event->rect() != _cursorRect) - { - int pxPosStartY = _pxCharHeight; - - // draw some patterns if needed - painter.fillRect(event->rect(), viewport()->palette().color(QPalette::Base)); - if (_addressArea) - painter.fillRect(QRect(-pxOfsX, event->rect().top(), _pxPosHexX - _pxGapAdrHex/2, height()), _addressAreaColor); - if (_asciiArea) - { - int linePos = _pxPosAsciiX - (_pxGapHexAscii / 2); - painter.setPen(Qt::gray); - painter.drawLine(linePos - pxOfsX, event->rect().top(), linePos - pxOfsX, height()); - } - - painter.setPen(viewport()->palette().color(QPalette::WindowText)); - - // paint address area - if (_addressArea) - { - QString address; - for (int row=0, pxPosY = _pxCharHeight; row <= (_dataShown.size()/_bytesPerLine); row++, pxPosY +=_pxCharHeight) - { - address = QString("%1").arg(_bPosFirst + row*_bytesPerLine + _addressOffset, _addrDigits, 16, QChar('0')); - // upper or lower case - if (_upperCase) - address = address.toUpper(); - - painter.drawText(_pxPosAdrX - pxOfsX, pxPosY, address); - } - } - - // paint hex and ascii area - QPen colStandard = QPen(viewport()->palette().color(QPalette::WindowText)); - - painter.setBackgroundMode(Qt::TransparentMode); - - for (int row = 0, pxPosY = pxPosStartY; row <= _rowsShown; row++, pxPosY +=_pxCharHeight) - { - QByteArray hex; - int pxPosX = _pxPosHexX - pxOfsX; - int pxPosAsciiX2 = _pxPosAsciiX - pxOfsX; - qint64 bPosLine = (qint64)row * _bytesPerLine; - for (int colIdx = 0; ((bPosLine + colIdx) < _dataShown.size() && (colIdx < _bytesPerLine)); colIdx++) - { - QColor c = viewport()->palette().color(QPalette::Base); - painter.setPen(colStandard); - - qint64 posBa = _bPosFirst + bPosLine + colIdx; - if ((getSelectionBegin() <= posBa) && (getSelectionEnd() > posBa)) - { - c = _brushSelection.color(); - painter.setPen(_penSelection); - } - else - { - if (_highlighting) - if (_markedShown.at((int)(posBa - _bPosFirst))) - { - c = _brushHighlighted.color(); - painter.setPen(_penHighlighted); - } - } - - // render hex value - QRect r; - if (colIdx == 0) - r.setRect(pxPosX, pxPosY - _pxCharHeight + _pxSelectionSub, 2*_pxCharWidth, _pxCharHeight); - else - r.setRect(pxPosX - _pxCharWidth, pxPosY - _pxCharHeight + _pxSelectionSub, 3*_pxCharWidth, _pxCharHeight); - painter.fillRect(r, c); - hex = _hexDataShown.mid((int)((bPosLine + colIdx) * 2), 2); - - // upper or lower case - if (_upperCase) - hex = hex.toUpper(); - - painter.drawText(pxPosX, pxPosY, hex); - pxPosX += 3*_pxCharWidth; - - // render ascii value - if (_asciiArea) - { - int ch = (uchar)_dataShown.at((int)(bPosLine + colIdx)); - if ( ch < 0x20 ) - ch = '.'; - r.setRect(pxPosAsciiX2, pxPosY - _pxCharHeight + _pxSelectionSub, _pxCharWidth, _pxCharHeight); - painter.fillRect(r, c); - painter.drawText(pxPosAsciiX2, pxPosY, QChar(ch)); - pxPosAsciiX2 += _pxCharWidth; - } - } - } - painter.setBackgroundMode(Qt::TransparentMode); - painter.setPen(viewport()->palette().color(QPalette::WindowText)); - } - - // paint cursor - if (_blink && !_readOnly && hasFocus()) - painter.fillRect(_cursorRect, this->palette().color(QPalette::WindowText)); - else - painter.drawRect(QRect(_pxCursorX - pxOfsX, _pxCursorY - _pxCharHeight + 2, _pxCharWidth, _pxCharHeight - 1)); - - // emit event, if size has changed - if (_lastEventSize != _chunks->size()) - { - _lastEventSize = _chunks->size(); - emit currentSizeChanged(_lastEventSize); - } -} - -void QHexEdit::resizeEvent(QResizeEvent *) -{ - adjust(); -} - -bool QHexEdit::focusNextPrevChild(bool next) -{ - if (_addressArea) - { - if ((next && _editAreaIsAscii) || (!next && !_editAreaIsAscii)) - return QWidget::focusNextPrevChild(next); - else - return false; - } - else - { - return QWidget::focusNextPrevChild(next); - } -} - -// ********************************************************************** Handle selections -void QHexEdit::resetSelection() -{ - _bSelectionBegin = _bSelectionInit; - _bSelectionEnd = _bSelectionInit; -} - -void QHexEdit::resetSelection(qint64 pos) -{ - pos = pos / 2 ; - if (pos < 0) - pos = 0; - if (pos > _chunks->size()) - pos = _chunks->size(); - - _bSelectionInit = pos; - _bSelectionBegin = pos; - _bSelectionEnd = pos; -} - -void QHexEdit::setSelection(qint64 pos) -{ - pos = pos / 2; - if (pos < 0) - pos = 0; - if (pos > _chunks->size()) - pos = _chunks->size(); - - if (pos >= _bSelectionInit) - { - _bSelectionEnd = pos; - _bSelectionBegin = _bSelectionInit; - } - else - { - _bSelectionBegin = pos; - _bSelectionEnd = _bSelectionInit; - } -} - -int QHexEdit::getSelectionBegin() -{ - return (int)_bSelectionBegin; -} - -int QHexEdit::getSelectionEnd() -{ - return (int)_bSelectionEnd; -} - -// ********************************************************************** Private utility functions -void QHexEdit::init() -{ - _undoStack->clear(); - setAddressOffset(0); - resetSelection(0); - setCursorPosition(0); - verticalScrollBar()->setValue(0); - _modified = false; -} - -void QHexEdit::adjust() -{ - // recalc Graphics - if (_addressArea) - { - _addrDigits = addressWidth(); - _pxPosHexX = _pxGapAdr + _addrDigits*_pxCharWidth + _pxGapAdrHex; - } - else - _pxPosHexX = _pxGapAdrHex; - _pxPosAdrX = _pxGapAdr; - _pxPosAsciiX = _pxPosHexX + _hexCharsInLine * _pxCharWidth + _pxGapHexAscii; - - // set horizontalScrollBar() - int pxWidth = _pxPosAsciiX; - if (_asciiArea) - pxWidth += _bytesPerLine*_pxCharWidth; - horizontalScrollBar()->setRange(0, pxWidth - viewport()->width()); - horizontalScrollBar()->setPageStep(viewport()->width()); - - // set verticalScrollbar() - _rowsShown = ((viewport()->height()-4)/_pxCharHeight); - int lineCount = (int)(_chunks->size() / (qint64)_bytesPerLine) + 1; - verticalScrollBar()->setRange(0, lineCount - _rowsShown); - verticalScrollBar()->setPageStep(_rowsShown); - - int value = verticalScrollBar()->value(); - _bPosFirst = (qint64)value * _bytesPerLine; - _bPosLast = _bPosFirst + ((qint64)_rowsShown * _bytesPerLine) - 1; - if (_bPosLast >= _chunks->size()) - _bPosLast = _chunks->size() - 1; - readBuffers(); - setCursorPosition(_cursorPosition); -} - -void QHexEdit::dataChangedPrivate(int) -{ - _modified = _undoStack->index() != 0; - adjust(); - emit dataChanged(); -} - -void QHexEdit::refresh() -{ - ensureVisible(); - readBuffers(); -} - -void QHexEdit::readBuffers() -{ - _dataShown = _chunks->data(_bPosFirst, _bPosLast - _bPosFirst + _bytesPerLine + 1, &_markedShown); - _hexDataShown = QByteArray(_dataShown.toHex()); -} - -QString QHexEdit::toReadable(const QByteArray &ba) -{ - QString result; - - for (int i=0; i < ba.size(); i += 16) - { - QString addrStr = QString("%1").arg(_addressOffset + i, addressWidth(), 16, QChar('0')); - QString hexStr; - QString ascStr; - for (int j=0; j<16; j++) - { - if ((i + j) < ba.size()) - { - hexStr.append(" ").append(ba.mid(i+j, 1).toHex()); - char ch = ba[i + j]; - if ((ch < 0x20) || (ch > 0x7e)) - ch = '.'; - ascStr.append(QChar(ch)); - } - } - result += addrStr + " " + QString("%1").arg(hexStr, -48) + " " + QString("%1").arg(ascStr, -17) + "\n"; - } - return result; -} - -void QHexEdit::updateCursor() -{ - if (_blink) - _blink = false; - else - _blink = true; - viewport()->update(_cursorRect); -} diff --git a/UEFITool/qhexedit2/qhexedit.h b/UEFITool/qhexedit2/qhexedit.h deleted file mode 100644 index 76d8670..0000000 --- a/UEFITool/qhexedit2/qhexedit.h +++ /dev/null @@ -1,411 +0,0 @@ -#ifndef QHEXEDIT_H -#define QHEXEDIT_H - -#include -#include -#include - -#include "chunks.h" -#include "commands.h" - -#ifdef QHEXEDIT_EXPORTS -#define QHEXEDIT_API Q_DECL_EXPORT -#elif QHEXEDIT_IMPORTS -#define QHEXEDIT_API Q_DECL_IMPORT -#else -#define QHEXEDIT_API -#endif - -/** \mainpage -QHexEdit is a binary editor widget for Qt. - -\version Version 0.8.2 -\image html qhexedit.png -*/ - - -/** QHexEdit is a hex editor widget written in C++ for the Qt (Qt4, Qt5) framework. -It is a simple editor for binary data, just like QPlainTextEdit is for text -data. There are sip configuration files included, so it is easy to create -bindings for PyQt and you can use this widget also in python 2 and 3. - -QHexEdit takes the data of a QByteArray (setData()) and shows it. You can use -the mouse or the keyboard to navigate inside the widget. If you hit the keys -(0..9, a..f) you will change the data. Changed data is highlighted and can be -accessed via data(). - -Normaly QHexEdit works in the overwrite Mode. You can set overwriteMode(false) -and insert data. In this case the size of data() increases. It is also possible -to delete bytes (del or backspace), here the size of data decreases. - -You can select data with keyboard hits or mouse movements. The copy-key will -copy the selected data into the clipboard. The cut-key copies also but delets -it afterwards. In overwrite mode, the paste function overwrites the content of -the (does not change the length) data. In insert mode, clipboard data will be -inserted. The clipboard content is expected in ASCII Hex notation. Unknown -characters will be ignored. - -QHexEdit comes with undo/redo functionality. All changes can be undone, by -pressing the undo-key (usually ctr-z). They can also be redone afterwards. -The undo/redo framework is cleared, when setData() sets up a new -content for the editor. You can search data inside the content with indexOf() -and lastIndexOf(). The replace() function is to change located subdata. This -'replaced' data can also be undone by the undo/redo framework. - -QHexEdit is based on QIODevice, that's why QHexEdit can handle big amounts of -data. The size of edited data can be more then two gigabytes without any -restrictions. -*/ -class QHEXEDIT_API QHexEdit : public QAbstractScrollArea -{ - Q_OBJECT - - /*! Property address area switch the address area on or off. Set addressArea true - (show it), false (hide it). - */ - Q_PROPERTY(bool addressArea READ addressArea WRITE setAddressArea) - - /*! Property address area color sets (setAddressAreaColor()) the backgorund - color of address areas. You can also read the color (addressaAreaColor()). - */ - Q_PROPERTY(QColor addressAreaColor READ addressAreaColor WRITE setAddressAreaColor) - - /*! Property addressOffset is added to the Numbers of the Address Area. - A offset in the address area (left side) is sometimes usefull, whe you show - only a segment of a complete memory picture. With setAddressOffset() you set - this property - with addressOffset() you get the current value. - */ - Q_PROPERTY(qint64 addressOffset READ addressOffset WRITE setAddressOffset) - - /*! Set and get the minimum width of the address area, width in characters. - */ - Q_PROPERTY(int addressWidth READ addressWidth WRITE setAddressWidth) - - /*! Switch the ascii area on (true, show it) or off (false, hide it). - */ - Q_PROPERTY(bool asciiArea READ asciiArea WRITE setAsciiArea) - - /*! Set and get bytes number per line.*/ - Q_PROPERTY(int bytesPerLine READ bytesPerLine WRITE setBytesPerLine) - - /*! Porperty cursorPosition sets or gets the position of the editor cursor - in QHexEdit. Every byte in data has to cursor positions: the lower and upper - Nibble. Maximum cursor position is factor two of data.size(). - */ - Q_PROPERTY(qint64 cursorPosition READ cursorPosition WRITE setCursorPosition) - - /*! Property data holds the content of QHexEdit. Call setData() to set the - content of QHexEdit, data() returns the actual content. When calling setData() - with a QByteArray as argument, QHexEdit creates a internal copy of the data - If you want to edit big files please use setData(), based on QIODevice. - */ - Q_PROPERTY(QByteArray data READ data WRITE setData NOTIFY dataChanged) - - /*! Switch the highlighting feature on or of: true (show it), false (hide it). - */ - Q_PROPERTY(bool highlighting READ highlighting WRITE setHighlighting) - - /*! Property highlighting color sets (setHighlightingColor()) the backgorund - color of highlighted text areas. You can also read the color - (highlightingColor()). - */ - Q_PROPERTY(QColor highlightingColor READ highlightingColor WRITE setHighlightingColor) - - /*! Porperty overwrite mode sets (setOverwriteMode()) or gets (overwriteMode()) the mode - in which the editor works. In overwrite mode the user will overwrite existing data. The - size of data will be constant. In insert mode the size will grow, when inserting - new data. - */ - Q_PROPERTY(bool overwriteMode READ overwriteMode WRITE setOverwriteMode) - - /*! Property selection color sets (setSelectionColor()) the backgorund - color of selected text areas. You can also read the color - (selectionColor()). - */ - Q_PROPERTY(QColor selectionColor READ selectionColor WRITE setSelectionColor) - - /*! Property readOnly sets (setReadOnly()) or gets (isReadOnly) the mode - in which the editor works. In readonly mode the the user can only navigate - through the data and select data; modifying is not possible. This - property's default is false. - */ - Q_PROPERTY(bool readOnly READ isReadOnly WRITE setReadOnly) - - /*! Property upperCase sets (setUpperCase()) or gets (isUpperCase) the case of hex - data. Default is lowercase. - */ - Q_PROPERTY(bool upperCase READ isUpperCase WRITE setUpperCase) - - /*! Set the font of the widget. Please use fixed width fonts like Mono or Courier.*/ - Q_PROPERTY(QFont font READ font WRITE setFont) - -public: - /*! Creates an instance of QHexEdit. - \param parent Parent widget of QHexEdit. - */ - QHexEdit(QWidget *parent=0); - - // Access to data of qhexedit - - /*! Sets the data of QHexEdit. The QIODevice will be opend just before reading - and closed immediately afterwards. This is to allow other programs to rewrite - the file while editing it. - */ - bool setData(QIODevice &iODevice); - - /*! Gives back the data as a QByteArray starting at position \param pos and - delivering \param count bytes. - */ - QByteArray dataAt(qint64 pos, qint64 count=-1); - - /*! Gives back the data into a \param iODevice starting at position \param pos - and delivering \param count bytes. - */ - bool write(QIODevice &iODevice, qint64 pos=0, qint64 count=-1); - - - // Char handling - - /*! Inserts a char. - \param pos Index position, where to insert - \param ch Char, which is to insert - The char will be inserted and size of data grows. - */ - void insert(qint64 pos, char ch); - - /*! Removes len bytes from the content. - \param pos Index position, where to remove - \param len Amount of bytes to remove - */ - void remove(qint64 pos, qint64 len=1); - - /*! Replaces a char. - \param pos Index position, where to overwrite - \param ch Char, which is to insert - The char will be overwritten and size remains constant. - */ - void replace(qint64 pos, char ch); - - - // ByteArray handling - - /*! Inserts a byte array. - \param pos Index position, where to insert - \param ba QByteArray, which is to insert - The QByteArray will be inserted and size of data grows. - */ - void insert(qint64 pos, const QByteArray &ba); - - /*! Replaces \param len bytes with a byte array \param ba - \param pos Index position, where to overwrite - \param ba QByteArray, which is inserted - \param len count of bytes to overwrite - The data is overwritten and size of data may change. - */ - void replace(qint64 pos, qint64 len, const QByteArray &ba); - - - // Utility functioins - /*! Calc cursor position from graphics position - * \param point from where the cursor position should be calculated - * \return Cursor postioin - */ - qint64 cursorPosition(QPoint point); - - /*! Ensure the cursor to be visble - */ - void ensureVisible(); - - /*! Find first occurence of ba in QHexEdit data - * \param ba Data to find - * \param from Point where the search starts - * \return pos if fond, else -1 - */ - qint64 indexOf(const QByteArray &ba, qint64 from); - - /*! Returns if any changes where done on document - * \return true when document is modified else false - */ - bool isModified(); - - /*! Find last occurence of ba in QHexEdit data - * \param ba Data to find - * \param from Point where the search starts - * \return pos if fond, else -1 - */ - qint64 lastIndexOf(const QByteArray &ba, qint64 from); - - /*! Gives back a formatted image of the selected content of QHexEdit - */ - QString selectionToReadableString(); - - /*! Set Font of QHexEdit - * \param font - */ - void setFont(const QFont &font); - - /*! Gives back a formatted image of the content of QHexEdit - */ - QString toReadableString(); - - -public slots: - /*! Redoes the last operation. If there is no operation to redo, i.e. - there is no redo step in the undo/redo history, nothing happens. - */ - void redo(); - - /*! Undoes the last operation. If there is no operation to undo, i.e. - there is no undo step in the undo/redo history, nothing happens. - */ - void undo(); - -signals: - - /*! Contains the address, where the cursor is located. */ - void currentAddressChanged(qint64 address); - - /*! Contains the size of the data to edit. */ - void currentSizeChanged(qint64 size); - - /*! The signal is emitted every time, the data is changed. */ - void dataChanged(); - - /*! The signal is emitted every time, the overwrite mode is changed. */ - void overwriteModeChanged(bool state); - - -/*! \cond docNever */ -public: - ~QHexEdit(); - - // Properties - bool addressArea(); - void setAddressArea(bool addressArea); - - QColor addressAreaColor(); - void setAddressAreaColor(const QColor &color); - - qint64 addressOffset(); - void setAddressOffset(qint64 addressArea); - - int addressWidth(); - void setAddressWidth(int addressWidth); - - bool asciiArea(); - void setAsciiArea(bool asciiArea); - - int bytesPerLine(); - void setBytesPerLine(int count); - - qint64 cursorPosition(); - void setCursorPosition(qint64 position); - - QByteArray data(); - void setData(const QByteArray &ba); - - bool highlighting(); - void setHighlighting(bool mode); - - QColor highlightingColor(); - void setHighlightingColor(const QColor &color); - - bool overwriteMode(); - void setOverwriteMode(bool overwriteMode); - - bool isReadOnly(); - void setReadOnly(bool readOnly); - - bool isUpperCase(); - void setUpperCase(bool upperCase); - - QColor selectionColor(); - void setSelectionColor(const QColor &color); - -protected: - // Handle events - void keyPressEvent(QKeyEvent *event); - void mouseMoveEvent(QMouseEvent * event); - void mousePressEvent(QMouseEvent * event); - void paintEvent(QPaintEvent *event); - void resizeEvent(QResizeEvent *); - virtual bool focusNextPrevChild(bool next); -private: - // Handle selections - void resetSelection(qint64 pos); // set selectionStart and selectionEnd to pos - void resetSelection(); // set selectionEnd to selectionStart - void setSelection(qint64 pos); // set min (if below init) or max (if greater init) - int getSelectionBegin(); - int getSelectionEnd(); - - // Private utility functions - void init(); - void readBuffers(); - QString toReadable(const QByteArray &ba); - -private slots: - void adjust(); // recalc pixel positions - void dataChangedPrivate(int idx=0); // emit dataChanged() signal - void refresh(); // ensureVisible() and readBuffers() - void updateCursor(); // update blinking cursor - -private: - // Name convention: pixel positions start with _px - int _pxCharWidth, _pxCharHeight; // char dimensions (dpendend on font) - int _pxPosHexX; // X-Pos of HeaxArea - int _pxPosAdrX; // X-Pos of Address Area - int _pxPosAsciiX; // X-Pos of Ascii Area - int _pxGapAdr; // gap left from AddressArea - int _pxGapAdrHex; // gap between AddressArea and HexAerea - int _pxGapHexAscii; // gap between HexArea and AsciiArea - int _pxCursorWidth; // cursor width - int _pxSelectionSub; // offset selection rect - int _pxCursorX; // current cursor pos - int _pxCursorY; // current cursor pos - - // Name convention: absolute byte positions in chunks start with _b - qint64 _bSelectionBegin; // first position of Selection - qint64 _bSelectionEnd; // end of Selection - qint64 _bSelectionInit; // memory position of Selection - qint64 _bPosFirst; // position of first byte shown - qint64 _bPosLast; // position of last byte shown - qint64 _bPosCurrent; // current position - - // variables to store the property values - bool _addressArea; // left area of QHexEdit - QColor _addressAreaColor; - int _addressWidth; - bool _asciiArea; - qint64 _addressOffset; - int _bytesPerLine; - int _hexCharsInLine; - bool _highlighting; - bool _overwriteMode; - QBrush _brushSelection; - QPen _penSelection; - QBrush _brushHighlighted; - QPen _penHighlighted; - bool _readOnly; - bool _upperCase; - - // other variables - bool _editAreaIsAscii; // flag about the ascii mode edited - int _addrDigits; // real no of addressdigits, may be > addressWidth - bool _blink; // help get cursor blinking - QBuffer _bData; // buffer, when setup with QByteArray - Chunks *_chunks; // IODevice based access to data - QTimer _cursorTimer; // for blinking cursor - qint64 _cursorPosition; // absolute position of cursor, 1 Byte == 2 tics - QRect _cursorRect; // physical dimensions of cursor - QByteArray _data; // QHexEdit's data, when setup with QByteArray - QByteArray _dataShown; // data in the current View - QByteArray _hexDataShown; // data in view, transformed to hex - qint64 _lastEventSize; // size, which was emitted last time - QByteArray _markedShown; // marked data in view - bool _modified; // Is any data in editor modified? - int _rowsShown; // lines of text shown - UndoStack * _undoStack; // Stack to store edit actions for undo/redo - /*! \endcond docNever */ -}; - -#endif // QHEXEDIT_H diff --git a/UEFITool/qhexview5/LICENSE b/UEFITool/qhexview5/LICENSE new file mode 100644 index 0000000..d48dcab --- /dev/null +++ b/UEFITool/qhexview5/LICENSE @@ -0,0 +1,20 @@ +The MIT License (MIT) + +Copyright (c) 2014 Dax89 + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/UEFITool/qhexview5/model/buffer/qdevicebuffer.cpp b/UEFITool/qhexview5/model/buffer/qdevicebuffer.cpp new file mode 100644 index 0000000..1ba59d0 --- /dev/null +++ b/UEFITool/qhexview5/model/buffer/qdevicebuffer.cpp @@ -0,0 +1,130 @@ +#include "qdevicebuffer.h" +#include +#include + +QDeviceBuffer::QDeviceBuffer(QObject *parent) : QHexBuffer{parent} { } + +QDeviceBuffer::~QDeviceBuffer() +{ + if(!m_device) return; + + if(m_device->parent() == this) + { + if(m_device->isOpen()) m_device->close(); + m_device->deleteLater(); + } + + m_device = nullptr; +} + +uchar QDeviceBuffer::at(qint64 idx) +{ + m_device->seek(idx); + + char c = '\0'; + m_device->getChar(&c); + return static_cast(c); +} + +qint64 QDeviceBuffer::length() const { return m_device->size(); } + +void QDeviceBuffer::insert(qint64 offset, const QByteArray &data) +{ + Q_UNUSED(offset) + Q_UNUSED(data) + // Not implemented +} + +void QDeviceBuffer::replace(qint64 offset, const QByteArray& data) +{ + m_device->seek(offset); + m_device->write(data); +} + +void QDeviceBuffer::remove(qint64 offset, int length) +{ + Q_UNUSED(offset) + Q_UNUSED(length) + // Not implemented +} + +QByteArray QDeviceBuffer::read(qint64 offset, int length) +{ + m_device->seek(offset); + return m_device->read(length); +} + +bool QDeviceBuffer::read(QIODevice *device) +{ + m_device = device; + if(!m_device) return false; + if(!m_device->isOpen()) m_device->open(QIODevice::ReadWrite); + return m_device->isOpen(); +} + +void QDeviceBuffer::write(QIODevice *device) +{ + Q_UNUSED(device) + // Not implemented +} + +qint64 QDeviceBuffer::indexOf(const QByteArray& ba, qint64 from) +{ + const auto MAX = std::numeric_limits::max(); + qint64 idx = -1; + + if(from < m_device->size()) + { + idx = from; + m_device->seek(from); + + while(idx < m_device->size()) + { + QByteArray data = m_device->read(MAX); + int sidx = data.indexOf(ba); + + if(sidx >= 0) + { + idx += sidx; + break; + } + + if(idx + data.size() >= m_device->size()) return -1; + m_device->seek(m_device->pos() + data.size() - ba.size()); + } + } + + return idx; +} + +qint64 QDeviceBuffer::lastIndexOf(const QByteArray& ba, qint64 from) +{ + const auto MAX = std::numeric_limits::max(); + qint64 idx = -1; + + if(from >= 0 && ba.size() < MAX) + { + qint64 currpos = from; + + while(currpos >= 0) + { + qint64 readpos = (currpos < MAX) ? 0 : currpos - MAX; + m_device->seek(readpos); + + QByteArray data = m_device->read(currpos - readpos); + int lidx = data.lastIndexOf(ba, from); + + if(lidx >= 0) + { + idx = readpos + lidx; + break; + } + + if(readpos <= 0) break; + currpos = readpos + ba.size(); + } + + } + + return idx; +} diff --git a/UEFITool/qhexview5/model/buffer/qdevicebuffer.h b/UEFITool/qhexview5/model/buffer/qdevicebuffer.h new file mode 100644 index 0000000..0115c67 --- /dev/null +++ b/UEFITool/qhexview5/model/buffer/qdevicebuffer.h @@ -0,0 +1,25 @@ +#pragma once + +#include "qhexbuffer.h" + +class QDeviceBuffer : public QHexBuffer +{ + Q_OBJECT + + public: + explicit QDeviceBuffer(QObject *parent = nullptr); + virtual ~QDeviceBuffer(); + uchar at(qint64 idx) override; + qint64 length() const override; + void insert(qint64 offset, const QByteArray& data) override; + void replace(qint64 offset, const QByteArray& data) override; + void remove(qint64 offset, int length) override; + QByteArray read(qint64 offset, int length) override; + bool read(QIODevice* device) override; + void write(QIODevice* device) override; + qint64 indexOf(const QByteArray& ba, qint64 from) override; + qint64 lastIndexOf(const QByteArray& ba, qint64 from) override; + + protected: + QIODevice* m_device{nullptr}; +}; diff --git a/UEFITool/qhexview5/model/buffer/qhexbuffer.cpp b/UEFITool/qhexview5/model/buffer/qhexbuffer.cpp new file mode 100644 index 0000000..b50081d --- /dev/null +++ b/UEFITool/qhexview5/model/buffer/qhexbuffer.cpp @@ -0,0 +1,35 @@ +#include "qhexbuffer.h" +#include + +QHexBuffer::QHexBuffer(QObject *parent) : QObject{parent} { } +uchar QHexBuffer::at(qint64 idx) { return this->read(idx, 1).at(0); } +bool QHexBuffer::isEmpty() const { return this->length() <= 0; } + +void QHexBuffer::replace(qint64 offset, const QByteArray &data) +{ + this->remove(offset, data.length()); + this->insert(offset, data); +} + +void QHexBuffer::read(char *data, int size) +{ + QBuffer* buffer = new QBuffer(this); + buffer->setData(data, size); + + if(!buffer->isOpen()) + buffer->open(QBuffer::ReadWrite); + + this->read(buffer); +} + +void QHexBuffer::read(const QByteArray &ba) +{ + QBuffer* buffer = new QBuffer(this); + + buffer->setData(ba); + if(!buffer->isOpen()) + buffer->open(QBuffer::ReadWrite); + + this->read(buffer); +} + diff --git a/UEFITool/qhexview5/model/buffer/qhexbuffer.h b/UEFITool/qhexview5/model/buffer/qhexbuffer.h new file mode 100644 index 0000000..c00bbbd --- /dev/null +++ b/UEFITool/qhexview5/model/buffer/qhexbuffer.h @@ -0,0 +1,30 @@ +#pragma once + +#include +#include + +class QHexBuffer : public QObject +{ + Q_OBJECT + + public: + explicit QHexBuffer(QObject *parent = nullptr); + bool isEmpty() const; + + public: + virtual uchar at(qint64 idx); + virtual void replace(qint64 offset, const QByteArray& data); + virtual void read(char* data, int size); + virtual void read(const QByteArray& ba); + + public: + virtual qint64 length() const = 0; + virtual void insert(qint64 offset, const QByteArray& data) = 0; + virtual void remove(qint64 offset, int length) = 0; + virtual QByteArray read(qint64 offset, int length) = 0; + virtual bool read(QIODevice* iodevice) = 0; + virtual void write(QIODevice* iodevice) = 0; + virtual qint64 indexOf(const QByteArray& ba, qint64 from) = 0; + virtual qint64 lastIndexOf(const QByteArray& ba, qint64 from) = 0; + +}; diff --git a/UEFITool/qhexview5/model/buffer/qmemorybuffer.cpp b/UEFITool/qhexview5/model/buffer/qmemorybuffer.cpp new file mode 100644 index 0000000..b0d6a02 --- /dev/null +++ b/UEFITool/qhexview5/model/buffer/qmemorybuffer.cpp @@ -0,0 +1,19 @@ +#include "qmemorybuffer.h" +#include + +QMemoryBuffer::QMemoryBuffer(QObject *parent) : QHexBuffer{parent} { } +uchar QMemoryBuffer::at(qint64 idx) { return static_cast(m_buffer.at(idx)); } +qint64 QMemoryBuffer::length() const { return static_cast(m_buffer.length()); } +void QMemoryBuffer::insert(qint64 offset, const QByteArray &data) { m_buffer.insert(static_cast(offset), data); } +void QMemoryBuffer::remove(qint64 offset, int length) { m_buffer.remove(static_cast(offset), length); } +QByteArray QMemoryBuffer::read(qint64 offset, int length) { return m_buffer.mid(static_cast(offset), length); } + +bool QMemoryBuffer::read(QIODevice *device) +{ + m_buffer = device->readAll(); + return true; +} + +void QMemoryBuffer::write(QIODevice *device) { device->write(m_buffer); } +qint64 QMemoryBuffer::indexOf(const QByteArray& ba, qint64 from) { return m_buffer.indexOf(ba, static_cast(from)); } +qint64 QMemoryBuffer::lastIndexOf(const QByteArray& ba, qint64 from) { return m_buffer.lastIndexOf(ba, static_cast(from)); } diff --git a/UEFITool/qhexview5/model/buffer/qmemorybuffer.h b/UEFITool/qhexview5/model/buffer/qmemorybuffer.h new file mode 100644 index 0000000..4e096d2 --- /dev/null +++ b/UEFITool/qhexview5/model/buffer/qmemorybuffer.h @@ -0,0 +1,23 @@ +#pragma once + +#include "qhexbuffer.h" + +class QMemoryBuffer : public QHexBuffer +{ + Q_OBJECT + + public: + explicit QMemoryBuffer(QObject *parent = nullptr); + uchar at(qint64 idx) override; + qint64 length() const override; + void insert(qint64 offset, const QByteArray& data) override; + void remove(qint64 offset, int length) override; + QByteArray read(qint64 offset, int length) override; + bool read(QIODevice* device) override; + void write(QIODevice* device) override; + qint64 indexOf(const QByteArray& ba, qint64 from) override; + qint64 lastIndexOf(const QByteArray& ba, qint64 from) override; + + private: + QByteArray m_buffer; +}; diff --git a/UEFITool/qhexview5/model/commands/hexcommand.cpp b/UEFITool/qhexview5/model/commands/hexcommand.cpp new file mode 100644 index 0000000..c4ca0ff --- /dev/null +++ b/UEFITool/qhexview5/model/commands/hexcommand.cpp @@ -0,0 +1,3 @@ +#include "hexcommand.h" + +HexCommand::HexCommand(QHexBuffer *buffer, QHexDocument* document, QUndoCommand *parent): QUndoCommand(parent), m_hexdocument(document), m_buffer(buffer), m_offset(0), m_length(0) { } diff --git a/UEFITool/qhexview5/model/commands/hexcommand.h b/UEFITool/qhexview5/model/commands/hexcommand.h new file mode 100644 index 0000000..f43c758 --- /dev/null +++ b/UEFITool/qhexview5/model/commands/hexcommand.h @@ -0,0 +1,19 @@ +#pragma once + +#include +#include "../buffer/qhexbuffer.h" + +class QHexDocument; + +class HexCommand: public QUndoCommand +{ + public: + HexCommand(QHexBuffer* buffer, QHexDocument* document, QUndoCommand* parent = nullptr); + + protected: + QHexDocument* m_hexdocument; + QHexBuffer* m_buffer; + qint64 m_offset; + int m_length; + QByteArray m_data; +}; diff --git a/UEFITool/qhexview5/model/commands/insertcommand.cpp b/UEFITool/qhexview5/model/commands/insertcommand.cpp new file mode 100644 index 0000000..122dbb8 --- /dev/null +++ b/UEFITool/qhexview5/model/commands/insertcommand.cpp @@ -0,0 +1,16 @@ +#include "insertcommand.h" +#include "../qhexdocument.h" + +InsertCommand::InsertCommand(QHexBuffer *buffer, QHexDocument* document, qint64 offset, const QByteArray &data, QUndoCommand *parent): HexCommand(buffer, document, parent) +{ + m_offset = offset; + m_data = data; +} + +void InsertCommand::undo() +{ + m_buffer->remove(m_offset, m_data.length()); + Q_EMIT m_hexdocument->dataChanged(m_data, m_offset, QHexDocument::ChangeReason::Remove); +} + +void InsertCommand::redo() { m_buffer->insert(m_offset, m_data); } diff --git a/UEFITool/qhexview5/model/commands/insertcommand.h b/UEFITool/qhexview5/model/commands/insertcommand.h new file mode 100644 index 0000000..f1394a8 --- /dev/null +++ b/UEFITool/qhexview5/model/commands/insertcommand.h @@ -0,0 +1,11 @@ +#pragma once + +#include "hexcommand.h" + +class InsertCommand: public HexCommand +{ + public: + InsertCommand(QHexBuffer* buffer, QHexDocument* document, qint64 offset, const QByteArray& data, QUndoCommand* parent = nullptr); + void undo() override; + void redo() override; +}; diff --git a/UEFITool/qhexview5/model/commands/removecommand.cpp b/UEFITool/qhexview5/model/commands/removecommand.cpp new file mode 100644 index 0000000..42fe553 --- /dev/null +++ b/UEFITool/qhexview5/model/commands/removecommand.cpp @@ -0,0 +1,20 @@ +#include "removecommand.h" +#include "../qhexdocument.h" + +RemoveCommand::RemoveCommand(QHexBuffer *buffer, QHexDocument* document, qint64 offset, int length, QUndoCommand *parent): HexCommand(buffer, document, parent) +{ + m_offset = offset; + m_length = length; +} + +void RemoveCommand::undo() +{ + m_buffer->insert(m_offset, m_data); + Q_EMIT m_hexdocument->dataChanged(m_data, m_offset, QHexDocument::ChangeReason::Insert); +} + +void RemoveCommand::redo() +{ + m_data = m_buffer->read(m_offset, m_length); // Backup data + m_buffer->remove(m_offset, m_length); +} diff --git a/UEFITool/qhexview5/model/commands/removecommand.h b/UEFITool/qhexview5/model/commands/removecommand.h new file mode 100644 index 0000000..e707e6c --- /dev/null +++ b/UEFITool/qhexview5/model/commands/removecommand.h @@ -0,0 +1,11 @@ +#pragma once + +#include "hexcommand.h" + +class RemoveCommand: public HexCommand +{ + public: + RemoveCommand(QHexBuffer* buffer, QHexDocument* document, qint64 offset, int length, QUndoCommand* parent = nullptr); + void undo() override; + void redo() override; +}; diff --git a/UEFITool/qhexview5/model/commands/replacecommand.cpp b/UEFITool/qhexview5/model/commands/replacecommand.cpp new file mode 100644 index 0000000..6e0ec93 --- /dev/null +++ b/UEFITool/qhexview5/model/commands/replacecommand.cpp @@ -0,0 +1,20 @@ +#include "replacecommand.h" +#include "../qhexdocument.h" + +ReplaceCommand::ReplaceCommand(QHexBuffer *buffer, QHexDocument* document, qint64 offset, const QByteArray &data, QUndoCommand *parent): HexCommand(buffer, document, parent) +{ + m_offset = offset; + m_data = data; +} + +void ReplaceCommand::undo() +{ + m_buffer->replace(m_offset, m_olddata); + Q_EMIT m_hexdocument->dataChanged(m_olddata, m_offset, QHexDocument::ChangeReason::Replace); +} + +void ReplaceCommand::redo() +{ + m_olddata = m_buffer->read(m_offset, m_data.length()); + m_buffer->replace(m_offset, m_data); +} diff --git a/UEFITool/qhexview5/model/commands/replacecommand.h b/UEFITool/qhexview5/model/commands/replacecommand.h new file mode 100644 index 0000000..c4a2418 --- /dev/null +++ b/UEFITool/qhexview5/model/commands/replacecommand.h @@ -0,0 +1,14 @@ +#pragma once + +#include "hexcommand.h" + +class ReplaceCommand: public HexCommand +{ + public: + ReplaceCommand(QHexBuffer* buffer, QHexDocument* document, qint64 offset, const QByteArray& data, QUndoCommand* parent = nullptr); + void undo() override; + void redo() override; + + private: + QByteArray m_olddata; +}; diff --git a/UEFITool/qhexview5/model/qhexcursor.cpp b/UEFITool/qhexview5/model/qhexcursor.cpp new file mode 100644 index 0000000..6042128 --- /dev/null +++ b/UEFITool/qhexview5/model/qhexcursor.cpp @@ -0,0 +1,129 @@ +#include "../qhexview.h" +#include "qhexcursor.h" +#include "qhexdocument.h" + +/* + * https://stackoverflow.com/questions/10803043/inverse-column-row-major-order-transformation + * + * If the index is calculated as: + * offset = row + column*NUMROWS + * then the inverse would be: + * row = offset % NUMROWS + * column = offset / NUMROWS + * where % is modulus, and / is integer division. + */ + +QHexCursor::QHexCursor(const QHexOptions* options, QHexView* parent) : QObject(parent), m_options(options) { } +QHexView* QHexCursor::hexView() const { return qobject_cast(this->parent()); } +QHexCursor::Mode QHexCursor::mode() const { return m_mode; } +qint64 QHexCursor::offset() const { return this->positionToOffset(m_position); } +qint64 QHexCursor::address() const { return m_options->baseaddress + this->offset(); } +quint64 QHexCursor::lineAddress() const { return m_options->baseaddress + (m_position.line * m_options->linelength); } +qint64 QHexCursor::selectionStartOffset() const { return this->positionToOffset(this->selectionStart()); } +qint64 QHexCursor::selectionEndOffset() const { return this->positionToOffset(this->selectionEnd()); } +qint64 QHexCursor::line() const { return m_position.line; } +qint64 QHexCursor::column() const { return m_position.column; } + +QHexPosition QHexCursor::selectionStart() const +{ + if(m_position.line < m_selection.line) + return m_position; + + if(m_position.line == m_selection.line) + { + if(m_position.column < m_selection.column) + return m_position; + } + + return m_selection; +} + +QHexPosition QHexCursor::selectionEnd() const +{ + if(m_position.line > m_selection.line) + return m_position; + + if(m_position.line == m_selection.line) + { + if(m_position.column > m_selection.column) + return m_position; + } + + return m_selection; +} + +qint64 QHexCursor::selectionLength() const +{ + auto selstart = this->selectionStartOffset(), selend = this->selectionEndOffset(); + return selstart == selend ? 0 : selend - selstart + 1; +} + +QHexPosition QHexCursor::position() const { return m_position; } +QByteArray QHexCursor::selectedBytes() const { return this->hexView()->selectedBytes(); } +bool QHexCursor::hasSelection() const { return m_position != m_selection; } + +bool QHexCursor::isSelected(qint64 line, qint64 column) const +{ + if(!this->hasSelection()) return false; + + auto selstart = this->selectionStart(), selend = this->selectionEnd(); + if(line > selstart.line && line < selend.line) return true; + if(line == selstart.line && line == selend.line) return column >= selstart.column && column <= selend.column; + if(line == selstart.line) return column >= selstart.column; + if(line == selend.line) return column <= selend.column; + return false; +} + +void QHexCursor::setMode(Mode m) +{ + if(m_mode == m) return; + m_mode = m; + Q_EMIT modeChanged(); +} + +void QHexCursor::switchMode() +{ + switch(m_mode) + { + case Mode::Insert: this->setMode(Mode::Overwrite); break; + case Mode::Overwrite: this->setMode(Mode::Insert); break; + } +} + +void QHexCursor::move(qint64 offset) { this->move(this->offsetToPosition(offset)); } +void QHexCursor::move(qint64 line, qint64 column) { return this->move({line, column}); } + +void QHexCursor::move(QHexPosition pos) +{ + if(pos.line >= 0) m_selection.line = pos.line; + if(pos.column >= 0) m_selection.column = pos.column; + this->select(pos); +} + +void QHexCursor::select(qint64 offset) { this->select(this->offsetToPosition(offset)); } +void QHexCursor::select(qint64 line, qint64 column) { this->select({line, column}); } + +void QHexCursor::select(QHexPosition pos) +{ + if(pos.line >= 0) m_position.line = pos.line; + if(pos.column >= 0) m_position.column = pos.column; + Q_EMIT positionChanged(); +} + +void QHexCursor::selectSize(qint64 length) +{ + if(length > 0) length--; + else if(length < 0) length++; + if(length) this->select(this->offset() + length); +} + +qint64 QHexCursor::replace(const QVariant& oldvalue, const QVariant& newvalue, qint64 offset, QHexFindMode mode, unsigned int options, QHexFindDirection fd) const { return this->hexView()->replace(oldvalue, newvalue, offset, mode, options, fd); } +qint64 QHexCursor::find(const QVariant& value, qint64 offset, QHexFindMode mode, unsigned int options, QHexFindDirection fd) const { return this->hexView()->find(value, offset, mode, options, fd); } +void QHexCursor::cut(bool hex) { this->hexView()->cut(hex); } +void QHexCursor::copy(bool hex) const { this->hexView()->copy(hex); } +void QHexCursor::paste(bool hex) { this->hexView()->paste(hex); } +void QHexCursor::selectAll() { this->hexView()->selectAll(); } +void QHexCursor::removeSelection() { this->hexView()->removeSelection(); } +void QHexCursor::clearSelection() { m_position = m_selection; Q_EMIT positionChanged(); } +qint64 QHexCursor::positionToOffset(QHexPosition pos) const { return QHexUtils::positionToOffset(m_options, pos); } +QHexPosition QHexCursor::offsetToPosition(qint64 offset) const { return QHexUtils::offsetToPosition(m_options, offset); } diff --git a/UEFITool/qhexview5/model/qhexcursor.h b/UEFITool/qhexview5/model/qhexcursor.h new file mode 100644 index 0000000..46f1067 --- /dev/null +++ b/UEFITool/qhexview5/model/qhexcursor.h @@ -0,0 +1,69 @@ +#pragma once + +#include +#include "qhexoptions.h" +#include "qhexutils.h" + +class QHexView; + +class QHexCursor : public QObject +{ + Q_OBJECT + + public: + enum class Mode { Overwrite, Insert }; + + private: + explicit QHexCursor(const QHexOptions* options, QHexView *parent = nullptr); + + public: + QHexView* hexView() const; + Mode mode() const; + qint64 line() const; + qint64 column() const; + qint64 offset() const; + qint64 address() const; + quint64 lineAddress() const; + qint64 selectionStartOffset() const; + qint64 selectionEndOffset() const; + qint64 selectionLength() const; + QHexPosition position() const; + QHexPosition selectionStart() const; + QHexPosition selectionEnd() const; + QByteArray selectedBytes() const; + bool hasSelection() const; + bool isSelected(qint64 line, qint64 column) const; + void setMode(Mode m); + void move(qint64 offset); + void move(qint64 line, qint64 column); + void move(QHexPosition pos); + void select(qint64 offset); + void select(qint64 line, qint64 column); + void select(QHexPosition pos); + void selectSize(qint64 length); + qint64 replace(const QVariant& oldvalue, const QVariant& newvalue, qint64 offset, QHexFindMode mode = QHexFindMode::Text, unsigned int options = QHexFindOptions::None, QHexFindDirection fd = QHexFindDirection::Forward) const; + qint64 find(const QVariant& value, qint64 offset, QHexFindMode mode = QHexFindMode::Text, unsigned int options = QHexFindOptions::None, QHexFindDirection fd = QHexFindDirection::Forward) const; + qint64 positionToOffset(QHexPosition pos) const; + QHexPosition offsetToPosition(qint64 offset) const; + + public Q_SLOTS: + void cut(bool hex = false); + void copy(bool hex = false) const; + void paste(bool hex = false); + void selectAll(); + void removeSelection(); + void clearSelection(); + void switchMode(); + + Q_SIGNALS: + void positionChanged(); + void modeChanged(); + + private: + const QHexOptions* m_options; + Mode m_mode{Mode::Overwrite}; + QHexPosition m_position{}, m_selection{}; + + friend class QHexView; +}; + diff --git a/UEFITool/qhexview5/model/qhexdelegate.cpp b/UEFITool/qhexview5/model/qhexdelegate.cpp new file mode 100644 index 0000000..62114e8 --- /dev/null +++ b/UEFITool/qhexview5/model/qhexdelegate.cpp @@ -0,0 +1,68 @@ +#include "../qhexview.h" +#include "qhexdelegate.h" + +QHexDelegate::QHexDelegate(QObject* parent): QObject{parent} { } + +QString QHexDelegate::addressHeader(const QHexView* hexview) const +{ + Q_UNUSED(hexview); + return QString(); +} + +QString QHexDelegate::hexHeader(const QHexView* hexview) const +{ + Q_UNUSED(hexview); + return QString(); +} + +QString QHexDelegate::asciiHeader(const QHexView* hexview) const +{ + Q_UNUSED(hexview); + return QString(); +} + +void QHexDelegate::renderAddress(quint64 address, QTextCharFormat& cf, const QHexView* hexview) const +{ + Q_UNUSED(address); + Q_UNUSED(hexview); + Q_UNUSED(cf); + Q_UNUSED(hexview); +} + +void QHexDelegate::renderHeader(QTextBlockFormat& bf, const QHexView* hexview) const +{ + Q_UNUSED(bf); + Q_UNUSED(hexview); +} + +void QHexDelegate::renderHeaderPart(const QString& s, QHexArea area, QTextCharFormat& cf, const QHexView* hexview) const +{ + Q_UNUSED(s); + Q_UNUSED(area); + Q_UNUSED(cf); + Q_UNUSED(hexview); +} + +bool QHexDelegate::render(quint64 offset, quint8 b, QTextCharFormat& outcf, const QHexView* hexview) const +{ + Q_UNUSED(offset); + Q_UNUSED(b); + Q_UNUSED(outcf); + Q_UNUSED(hexview); + + return false; +} + +bool QHexDelegate::paintSeparator(QPainter* painter, QLineF line, const QHexView* hexview) const +{ + Q_UNUSED(painter); + Q_UNUSED(line); + Q_UNUSED(hexview); + return false; +} + +void QHexDelegate::paint(QPainter* painter, const QHexView* hexview) const +{ + Q_UNUSED(hexview); + hexview->paint(painter); +} diff --git a/UEFITool/qhexview5/model/qhexdelegate.h b/UEFITool/qhexview5/model/qhexdelegate.h new file mode 100644 index 0000000..b122890 --- /dev/null +++ b/UEFITool/qhexview5/model/qhexdelegate.h @@ -0,0 +1,25 @@ +#pragma once + +#include +#include +#include "qhexutils.h" + +class QHexView; + +class QHexDelegate: public QObject +{ + Q_OBJECT + + public: + explicit QHexDelegate(QObject* parent = nullptr); + virtual ~QHexDelegate() = default; + virtual QString addressHeader(const QHexView* hexview) const; + virtual QString hexHeader(const QHexView* hexview) const; + virtual QString asciiHeader(const QHexView* hexview) const; + virtual void renderAddress(quint64 address, QTextCharFormat& cf, const QHexView* hexview) const; + virtual void renderHeader(QTextBlockFormat& bf, const QHexView* hexview) const; + virtual void renderHeaderPart(const QString& s, QHexArea area, QTextCharFormat& cf, const QHexView* hexview) const; + virtual bool render(quint64 offset, quint8 b, QTextCharFormat& outcf, const QHexView* hexview) const; + virtual bool paintSeparator(QPainter* painter, QLineF line, const QHexView* hexview) const; + virtual void paint(QPainter* painter, const QHexView* hexview) const; +}; diff --git a/UEFITool/qhexview5/model/qhexdocument.cpp b/UEFITool/qhexview5/model/qhexdocument.cpp new file mode 100644 index 0000000..a23002b --- /dev/null +++ b/UEFITool/qhexview5/model/qhexdocument.cpp @@ -0,0 +1,99 @@ +#include "qhexdocument.h" +#include "buffer/qmemorybuffer.h" +#include "commands/insertcommand.h" +#include "commands/replacecommand.h" +#include "commands/removecommand.h" +#include "qhexutils.h" +#include +#include +#include + +QHexDocument::QHexDocument(QHexBuffer *buffer, QObject* parent): QObject(parent) +{ + m_buffer = buffer; + m_buffer->setParent(this); // Take Ownership + + connect(&m_undostack, &QUndoStack::canUndoChanged, this, &QHexDocument::canUndoChanged); + connect(&m_undostack, &QUndoStack::canRedoChanged, this, &QHexDocument::canRedoChanged); +} + +qint64 QHexDocument::indexOf(const QByteArray& ba, qint64 from) { return m_buffer->indexOf(ba, from); } +qint64 QHexDocument::lastIndexOf(const QByteArray& ba, qint64 from) { return m_buffer->lastIndexOf(ba, from); } +bool QHexDocument::isEmpty() const { return m_buffer->isEmpty(); } +bool QHexDocument::canUndo() const { return m_undostack.canUndo(); } +bool QHexDocument::canRedo() const { return m_undostack.canRedo(); } + +void QHexDocument::setData(const QByteArray& ba) +{ + QHexBuffer* mb = new QMemoryBuffer(); + mb->read(ba); + this->setData(mb); +} + +void QHexDocument::setData(QHexBuffer* buffer) +{ + if(!buffer) return; + + m_undostack.clear(); + buffer->setParent(this); + + auto* oldbuffer = m_buffer; + m_buffer = buffer; + if(oldbuffer) oldbuffer->deleteLater(); + + Q_EMIT canUndoChanged(false); + Q_EMIT canRedoChanged(false); + Q_EMIT changed(); + Q_EMIT reset(); +} + +qint64 QHexDocument::length() const { return m_buffer ? m_buffer->length() : 0; } +uchar QHexDocument::at(int offset) const { return m_buffer->at(offset); } + +QHexDocument* QHexDocument::fromFile(QString filename, QObject* parent) +{ + QFile f(filename); + f.open(QFile::ReadOnly); + return QHexDocument::fromMemory(f.readAll(), parent); +} + +void QHexDocument::undo() { m_undostack.undo(); Q_EMIT changed(); } +void QHexDocument::redo() { m_undostack.redo(); Q_EMIT changed(); } +void QHexDocument::insert(qint64 offset, uchar b) { this->insert(offset, QByteArray(1, b)); } +void QHexDocument::replace(qint64 offset, uchar b) { this->replace(offset, QByteArray(1, b)); } + +void QHexDocument::insert(qint64 offset, const QByteArray &data) +{ + m_undostack.push(new InsertCommand(m_buffer, this, offset, data)); + + Q_EMIT changed(); + Q_EMIT dataChanged(data, offset, ChangeReason::Insert); +} + +void QHexDocument::replace(qint64 offset, const QByteArray &data) +{ + m_undostack.push(new ReplaceCommand(m_buffer, this, offset, data)); + Q_EMIT changed(); + Q_EMIT dataChanged(data, offset, ChangeReason::Replace); +} + +void QHexDocument::remove(qint64 offset, int len) +{ + QByteArray data = m_buffer->read(offset, len); + + m_undostack.push(new RemoveCommand(m_buffer, this, offset, len)); + Q_EMIT changed(); + Q_EMIT dataChanged(data, offset, ChangeReason::Remove); +} + +QByteArray QHexDocument::read(qint64 offset, int len) const { return m_buffer->read(offset, len); } + +bool QHexDocument::saveTo(QIODevice *device) +{ + if(!device->isWritable()) return false; + m_buffer->write(device); + return true; +} + +QHexDocument* QHexDocument::fromBuffer(QHexBuffer* buffer, QObject* parent) { return new QHexDocument(buffer, parent); } +QHexDocument* QHexDocument::create(QObject* parent) { return QHexDocument::fromMemory({}, parent); } diff --git a/UEFITool/qhexview5/model/qhexdocument.h b/UEFITool/qhexview5/model/qhexdocument.h new file mode 100644 index 0000000..36b06db --- /dev/null +++ b/UEFITool/qhexview5/model/qhexdocument.h @@ -0,0 +1,89 @@ +#pragma once + +#include +#include "buffer/qhexbuffer.h" +#include "qhexmetadata.h" +#include "qhexoptions.h" + +class QHexCursor; + +class QHexDocument: public QObject +{ + Q_OBJECT + + public: + enum class ChangeReason { Insert, Remove, Replace }; + enum class FindDirection { Forward, Backward }; + Q_ENUM(ChangeReason); + Q_ENUM(FindDirection); + + private: + explicit QHexDocument(QHexBuffer* buffer, QObject *parent = nullptr); + + public: + bool isEmpty() const; + bool canUndo() const; + bool canRedo() const; + void setData(const QByteArray& ba); + void setData(QHexBuffer* buffer); + qint64 length() const; + qint64 indexOf(const QByteArray& ba, qint64 from = 0); + qint64 lastIndexOf(const QByteArray& ba, qint64 from = 0); + QByteArray read(qint64 offset, int len = 0) const; + uchar at(int offset) const; + + public Q_SLOTS: + void undo(); + void redo(); + void insert(qint64 offset, uchar b); + void replace(qint64 offset, uchar b); + void insert(qint64 offset, const QByteArray& data); + void replace(qint64 offset, const QByteArray& data); + void remove(qint64 offset, int len); + bool saveTo(QIODevice* device); + + public: + template static QHexDocument* fromDevice(QIODevice* iodevice, QObject* parent = nullptr); + template static QHexDocument* fromMemory(char *data, int size, QObject* parent = nullptr); + template static QHexDocument* fromMemory(const QByteArray& ba, QObject* parent = nullptr); + static QHexDocument* fromBuffer(QHexBuffer* buffer, QObject* parent = nullptr); + static QHexDocument* fromFile(QString filename, QObject* parent = nullptr); + static QHexDocument* create(QObject* parent = nullptr); + + Q_SIGNALS: + void canUndoChanged(bool canundo); + void canRedoChanged(bool canredo); + void dataChanged(const QByteArray& data, quint64 offset, QHexDocument::ChangeReason reason); + void changed(); + void reset(); + + private: + QHexBuffer* m_buffer; + QUndoStack m_undostack; + + friend class QHexView; +}; + +template +QHexDocument* QHexDocument::fromDevice(QIODevice* iodevice, QObject *parent) +{ + QHexBuffer* hexbuffer = new T(parent); + if(Owned) iodevice->setParent(hexbuffer); + return hexbuffer->read(iodevice) ? new QHexDocument(hexbuffer, parent) : nullptr; +} + +template +QHexDocument* QHexDocument::fromMemory(char *data, int size, QObject *parent) +{ + QHexBuffer* hexbuffer = new T(); + hexbuffer->read(data, size); + return new QHexDocument(hexbuffer, parent); +} + +template +QHexDocument* QHexDocument::fromMemory(const QByteArray& ba, QObject *parent) +{ + QHexBuffer* hexbuffer = new T(); + hexbuffer->read(ba); + return new QHexDocument(hexbuffer, parent); +} diff --git a/UEFITool/qhexview5/model/qhexmetadata.cpp b/UEFITool/qhexview5/model/qhexmetadata.cpp new file mode 100644 index 0000000..a65f95a --- /dev/null +++ b/UEFITool/qhexview5/model/qhexmetadata.cpp @@ -0,0 +1,149 @@ +#include "qhexmetadata.h" +#include "qhexcursor.h" + +QHexMetadata::QHexMetadata(const QHexOptions* options, QObject *parent) : QObject(parent), m_options(options) { } + +const QHexMetadataLine* QHexMetadata::find(qint64 line) const +{ + auto it = m_metadata.find(line); + return it != m_metadata.end() ? std::addressof(it.value()) : nullptr; +} + +QString QHexMetadata::getComment(qint64 line, qint64 column) const +{ + auto* metadataline = this->find(line); + if(!metadataline) return QString(); + + auto offset = QHexUtils::positionToOffset(m_options, {line, column}); + QStringList comments; + + for(auto& mi : *metadataline) + { + if((offset < mi.begin || offset > mi.end) || mi.comment.isEmpty()) continue; + comments.push_back(mi.comment); + } + + return comments.join("\n"); +} + +void QHexMetadata::removeMetadata(qint64 line) +{ + auto it = m_metadata.find(line); + if(it == m_metadata.end()) return; + + m_metadata.erase(it); + Q_EMIT changed(); +} + +void QHexMetadata::removeBackground(qint64 line) +{ + this->clearMetadata(line, [](QHexMetadataItem& mi) -> bool { + if(!mi.background.isValid()) return false; + + if(mi.foreground.isValid() || !mi.comment.isEmpty()) { + mi.background = QColor(); + return false; + } + + return true; + }); +} + +void QHexMetadata::removeForeground(qint64 line) +{ + this->clearMetadata(line, [](QHexMetadataItem& mi) -> bool { + if(!mi.foreground.isValid()) return false; + + if(mi.background.isValid() || !mi.comment.isEmpty()) { + mi.foreground = QColor(); + return false; + } + + return true; + }); +} + +void QHexMetadata::removeComments(qint64 line) +{ + this->clearMetadata(line, [](QHexMetadataItem& mi) -> bool { + if(mi.comment.isEmpty()) return false; + + if(mi.foreground.isValid() || mi.background.isValid()) { + mi.comment.clear(); + return false; + } + + return true; + }); +} + +void QHexMetadata::unhighlight(qint64 line) +{ + this->clearMetadata(line, [](QHexMetadataItem& mi) -> bool { + if(!mi.foreground.isValid() && !mi.background.isValid()) return false; + + if(!mi.comment.isEmpty()) { + mi.foreground = QColor(); + mi.background = QColor(); + return false; + } + + return true; + }); +} + +void QHexMetadata::clear() { m_metadata.clear(); Q_EMIT changed(); } +void QHexMetadata::copy(const QHexMetadata* metadata) { m_metadata = metadata->m_metadata; } + +void QHexMetadata::clearMetadata(qint64 line, ClearMetadataCallback&& cb) +{ + auto iit = m_metadata.find(line); + if(iit == m_metadata.end()) return; + + auto oldsize = iit->size(); + + for(auto it = iit->begin(); it != iit->end(); ) + { + if(cb(*it)) it = iit->erase(it); + else it++; + } + + if(iit->empty()) + { + this->removeMetadata(line); + return; + } + + if(oldsize != iit->size()) Q_EMIT changed(); +} + +void QHexMetadata::setMetadata(const QHexMetadataItem& mi) +{ + if(!m_options->linelength) return; + + const qint64 firstline = mi.begin / m_options->linelength; + const qint64 lastline = mi.end / m_options->linelength; + bool notify = false; + + for(auto line = firstline; line <= lastline; line++) + { + auto start = line == firstline ? mi.begin % m_options->linelength : 0; + auto length = line == lastline ? (mi.end % m_options->linelength) - start : m_options->linelength; + if(length <= 0) continue; + + notify = true; + m_metadata[line].push_back(mi); + } + + if(notify) Q_EMIT changed(); +} + +void QHexMetadata::invalidate() +{ + auto oldmetadata = m_metadata; + m_metadata.clear(); + + for(const QHexMetadataLine& line : oldmetadata) + for(const QHexMetadataItem& mi : line) + this->setMetadata(mi); +} diff --git a/UEFITool/qhexview5/model/qhexmetadata.h b/UEFITool/qhexview5/model/qhexmetadata.h new file mode 100644 index 0000000..70f4fb2 --- /dev/null +++ b/UEFITool/qhexview5/model/qhexmetadata.h @@ -0,0 +1,64 @@ +#pragma once + +#include +#include +#include +#include +#include +#include "qhexoptions.h" + +struct QHexMetadataItem +{ + qint64 begin, end; + QColor foreground, background; + QString comment; +}; + +using QHexMetadataLine = QList; + +class QHexMetadata : public QObject +{ + Q_OBJECT + + private: + using ClearMetadataCallback = std::function; + + private: + explicit QHexMetadata(const QHexOptions* options, QObject *parent = nullptr); + + public: + const QHexMetadataLine* find(qint64 line) const; + QString getComment(qint64 line, qint64 column) const; + void removeMetadata(qint64 line); + void removeBackground(qint64 line); + void removeForeground(qint64 line); + void removeComments(qint64 line); + void unhighlight(qint64 line); + void clear(); + + public: + inline void setMetadata(qint64 begin, qint64 end, const QColor &fgcolor, const QColor &bgcolor, const QString &comment) { this->setMetadata({begin, end, fgcolor, bgcolor, comment}); } + inline void setForeground(qint64 begin, qint64 end, const QColor &fgcolor) { this->setMetadata(begin, end, fgcolor, QColor(), QString()); } + inline void setBackground(qint64 begin, qint64 end, const QColor &bgcolor) { this->setMetadata(begin, end, QColor(), bgcolor, QString()); } + inline void setComment(qint64 begin, qint64 end, const QString& comment) { this->setMetadata(begin, end, QColor(), QColor(), comment); }; + inline void setMetadataSize(qint64 begin, qint64 length, const QColor &fgcolor, const QColor &bgcolor, const QString &comment) { this->setMetadata({begin, begin + length, fgcolor, bgcolor, comment}); } + inline void setForegroundSize(qint64 begin, qint64 length, const QColor &fgcolor) { this->setForeground(begin, begin + length, fgcolor); } + inline void setBackgroundSize(qint64 begin, qint64 length, const QColor &bgcolor) { this->setBackground(begin, begin + length, bgcolor); } + inline void setCommentSize(qint64 begin, qint64 length, const QString& comment) { this->setComment(begin, begin + length, comment); }; + + private: + void copy(const QHexMetadata* metadata); + void clearMetadata(qint64 line, ClearMetadataCallback&& cb); + void setMetadata(const QHexMetadataItem& mi); + void invalidate(); + + Q_SIGNALS: + void changed(); + void cleared(); + + private: + QHash m_metadata; + const QHexOptions* m_options; + + friend class QHexView; +}; diff --git a/UEFITool/qhexview5/model/qhexoptions.h b/UEFITool/qhexview5/model/qhexoptions.h new file mode 100644 index 0000000..8505a40 --- /dev/null +++ b/UEFITool/qhexview5/model/qhexoptions.h @@ -0,0 +1,55 @@ +#pragma once + +#include +#include +#include + +namespace QHexFlags { + enum: unsigned int { + None = (1 << 0), + HSeparator = (1 << 1), + VSeparator = (1 << 2), + StyledHeader = (1 << 3), + StyledAddress = (1 << 4), + NoHeader = (1 << 5), + HighlightAddress = (1 << 6), + HighlightColumn = (1 << 7), + + Separators = HSeparator | VSeparator, + Styled = StyledHeader | StyledAddress, + }; +} + +struct QHexColor +{ + QColor foreground; + QColor background; +}; + +struct QHexOptions +{ + // Appearance + QChar unprintablechar{'.'}; + QString addresslabel{""}; + QString hexlabel; + QString asciilabel; + quint64 baseaddress{0}; + unsigned int flags{QHexFlags::None}; + unsigned int linelength{0x10}; + unsigned int addresswidth{0}; + unsigned int grouplength{1}; + unsigned int scrollsteps{1}; + + // Colors & Styles + QHash bytecolors; + QColor linealternatebackground; + QColor linebackground; + QColor headercolor; + QColor commentcolor; + QColor separatorcolor; + + // Misc + bool copybreak{true}; + + inline bool hasFlag(unsigned int flag) const { return flags & flag; } +}; diff --git a/UEFITool/qhexview5/model/qhexutils.cpp b/UEFITool/qhexview5/model/qhexutils.cpp new file mode 100644 index 0000000..13125e9 --- /dev/null +++ b/UEFITool/qhexview5/model/qhexutils.cpp @@ -0,0 +1,314 @@ +#include "qhexutils.h" +#include "qhexoptions.h" +#include "../qhexview.h" +#include +#include +#include +#include +#include +#include +#include + +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + #define QHEXVIEW_VARIANT_EQ(x, t) ((x).metaType().id() == QMetaType::Q##t) +#else + #define QHEXVIEW_VARIANT_EQ(x, t) ((x).type() == QVariant::t) +#endif + +namespace QHexUtils { + +Q_GLOBAL_STATIC_WITH_ARGS(QList, HEXMAP, ({ + '0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' +})); + +namespace PatternUtils { + +Q_GLOBAL_STATIC_WITH_ARGS(QString, WILDCARD_BYTE, ("??")) + +bool check(QString& p, qint64& len) +{ + static QHash> processed; // Cache processed patterns + + auto it = processed.find(p); + + if(it != processed.end()) + { + p = it.value().first; + len = it.value().second; + return true; + } + + QString op = p; // Store unprocessed pattern + p = p.simplified().replace(" ", ""); + if(p.isEmpty() || (p.size() % 2)) return false; + + int wccount = 0; + + for(auto i = 0; i < p.size() - 2; i += 2) + { + const auto& hexb = p.mid(i, 2); + + if(hexb == *WILDCARD_BYTE) + { + wccount++; + continue; + } + + if(!isxdigit(hexb.at(0).toLatin1()) || !isxdigit(hexb.at(1).toLatin1())) + return false; + } + + if(wccount >= p.size()) return false; + len = p.size() / 2; + processed[op] = qMakePair(p, len); // Cache processed pattern + return true; +} + +bool match(const QByteArray& data, const QString& pattern) +{ + for(qint64 i = 0, idx = 0; (i <= (pattern.size() - 2)); i += 2, idx++) + { + if(idx >= data.size()) return false; + +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + QStringView hexb = QStringView{pattern}.mid(i, 2); +#else + const QStringRef& hexb = pattern.midRef(i, 2); +#endif + + if(hexb == *WILDCARD_BYTE) continue; + + bool ok = false; + auto b = static_cast(hexb.toUInt(&ok, 16)); + if(!ok || (b != data.at(idx))) return false; + } + + return true; +} + +} + +namespace { + +unsigned int countBits(uint val) +{ + if(val <= std::numeric_limits::max()) return QHexFindOptions::Int8; + if(val <= std::numeric_limits::max()) return QHexFindOptions::Int16; + if(val <= std::numeric_limits::max()) return QHexFindOptions::Int32; + + return QHexFindOptions::Int64; +} + +template +qint64 findIter(qint64 startoffset, QHexFindDirection fd, const QHexView* hexview, Function&& f) +{ + QHexDocument* hexdocument = hexview->hexDocument(); + qint64 offset = -1; + + QHexFindDirection cfd = fd; + if(cfd == QHexFindDirection::All) cfd = QHexFindDirection::Forward; + + qint64 i = startoffset; + + while(offset == -1 && (cfd == QHexFindDirection::Backward ? (i >= 0) : (i < hexdocument->length()))) + { + if(!f(i, offset)) break; + + if(cfd == QHexFindDirection::Backward) i--; + else i++; + + if(fd == QHexFindDirection::All && i >= hexdocument->length()) i = 0; + } + + return offset; +} + +qint64 findDefault(const QByteArray& value, qint64 startoffset, const QHexView* hexview, unsigned int options, QHexFindDirection fd) +{ + QHexDocument* hexdocument = hexview->hexDocument(); + if(value.size() > hexdocument->length()) return -1; + + return findIter(startoffset, fd, hexview, [options, value, hexdocument](qint64 idx, qint64& offset) -> bool { + for(auto i = 0; i < value.size(); i++) { + qint64 curroffset = idx + i; + + if(curroffset >= hexdocument->length()) { + offset = -1; + return false; + } + + uchar ch1 = hexdocument->at(curroffset); + uchar ch2 = value.at(i); + + if(!(options & QHexFindOptions::CaseSensitive)) { + ch1 = std::tolower(ch1); + ch2 = std::tolower(ch2); + } + + if(ch1 != ch2) break; + if(i == value.size() - 1) offset = idx; + } + + return true; + }); +} + +qint64 findWildcard(QString pattern, qint64 startoffset, const QHexView* hexview, QHexFindDirection fd, qint64& patternlen) +{ + QHexDocument* hexdocument = hexview->hexDocument(); + if(!PatternUtils::check(pattern, patternlen) || (patternlen >= hexdocument->length())) return -1; + + return findIter(startoffset, fd, hexview, [hexdocument, pattern, patternlen](qint64 idx, qint64& offset) -> bool { + if(PatternUtils::match(hexdocument->read(idx, patternlen), pattern)) offset = idx; + return true; + }); +} + +QByteArray variantToByteArray(QVariant value, QHexFindMode mode, unsigned int options) +{ + QByteArray v; + + switch(mode) + { + case QHexFindMode::Text: + if(QHEXVIEW_VARIANT_EQ(value, String)) v = value.toString().toUtf8(); + else if(QHEXVIEW_VARIANT_EQ(value, ByteArray)) v = value.toByteArray(); + break; + + case QHexFindMode::Hex: { + if(QHEXVIEW_VARIANT_EQ(value, String)) { + qint64 len = 0; + auto s = value.toString(); + if(!PatternUtils::check(s, len)) return { }; + + bool ok = true; + +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + for(auto i = 0; ok && i < s.size(); i += 2) v.push_back(static_cast(QStringView{s}.mid(i, 2).toUInt(&ok, 16))); +#else + for(auto i = 0; ok && i < s.size(); i += 2) v.push_back(static_cast(s.midRef(i, 2).toUInt(&ok, 16))); +#endif + + if(!ok) return { }; + } + else if(QHEXVIEW_VARIANT_EQ(value, ByteArray)) v = value.toByteArray(); + break; + } + + case QHexFindMode::Int: { + bool ok = false; + uint val = value.toUInt(&ok); + if(!ok) return QByteArray{ }; + + QDataStream ds(&v, QIODevice::WriteOnly); + + if(options & QHexFindOptions::BigEndian) { + if(options & QHexFindOptions::Int8) ds << qToBigEndian(val); + else if(options & QHexFindOptions::Int16) ds << qToBigEndian(val); + else if(options & QHexFindOptions::Int32) ds << qToBigEndian(val); + else if(options & QHexFindOptions::Int64) ds << qToBigEndian(val); + else return variantToByteArray(value, mode, options | countBits(val)); + } + else { + if(options & QHexFindOptions::Int8) ds << static_cast(val); + else if(options & QHexFindOptions::Int16) ds << static_cast(val); + else if(options & QHexFindOptions::Int32) ds << static_cast(val); + else if(options & QHexFindOptions::Int64) ds << static_cast(val); + else return variantToByteArray(value, mode, options | countBits(val)); + } + + break; + } + + case QHexFindMode::Float: { + bool ok = false; + QDataStream ds(&v, QIODevice::WriteOnly); + if(options & QHexFindOptions::Float) ds << value.toFloat(&ok); + else if(options & QHexFindOptions::Double) ds << value.toDouble(&ok); + if(!ok) return { }; + } + + default: break; + } + + return v; +} + +} // namespace + +QByteArray toHex(const QByteArray& ba, char sep) +{ + QByteArray hex(sep ? (ba.size() * 3 - 1) : (ba.size() * 2), Qt::Uninitialized); + + for(auto i = 0, o = 0; i < ba.size(); i++) + { + if(sep && i) hex[o++] = static_cast(sep); + hex[o++] = HEXMAP->at((ba.at(i) & 0xf0) >> 4); + hex[o++] = HEXMAP->at(ba.at(i) & 0x0f); + } + + return hex; +} + +QByteArray toHex(const QByteArray& ba) { return QHexUtils::toHex(ba, '\0'); } +qint64 positionToOffset(const QHexOptions* options, QHexPosition pos) { return options->linelength * pos.line + pos.column; } +QHexPosition offsetToPosition(const QHexOptions* options, qint64 offset) { return { offset / options->linelength, offset % options->linelength }; } + +QPair find(const QHexView* hexview, QVariant value, qint64 startoffset, QHexFindMode mode, unsigned int options, QHexFindDirection fd) +{ + qint64 offset = -1, size = 0; + if(startoffset == -1) startoffset = static_cast(hexview->offset()); + + if(mode == QHexFindMode::Hex && QHEXVIEW_VARIANT_EQ(value, String)) + { + offset = QHexUtils::findWildcard(value.toString(), startoffset, hexview, fd, size); + } + else + { + auto ba = variantToByteArray(value, mode, options); + + if(!ba.isEmpty()) + { + offset = QHexUtils::findDefault(ba, startoffset, hexview, options, fd); + size = ba.size(); + } + else + offset = -1; + } + + return {offset, offset > -1 ? size : 0}; +} + +bool checkPattern(QString pattern) +{ + qint64 len = 0; + return PatternUtils::check(pattern, len); +} + +QPair replace(const QHexView* hexview, QVariant oldvalue, QVariant newvalue, qint64 startoffset, QHexFindMode mode, unsigned int options, QHexFindDirection fd) +{ + auto res = QHexUtils::find(hexview, oldvalue, startoffset, mode, options, fd); + + if(res.first != -1 && res.second > 0) + { + QHexDocument* hexdocument = hexview->hexDocument(); + auto ba = variantToByteArray(newvalue, mode, options); + + if(!ba.isEmpty()) + { + hexdocument->remove(res.first, res.second); + hexdocument->insert(res.first, ba); + res.second = ba.size(); + } + else + { + res.first = -1; + res.second = 0; + } + } + + return res; +} + +} diff --git a/UEFITool/qhexview5/model/qhexutils.h b/UEFITool/qhexview5/model/qhexutils.h new file mode 100644 index 0000000..170e9e1 --- /dev/null +++ b/UEFITool/qhexview5/model/qhexutils.h @@ -0,0 +1,48 @@ +#pragma once + +#include +#include +#include +#include + +struct QHexOptions; +class QHexView; + +namespace QHexFindOptions { + enum: unsigned int { + None = (1 << 0), + CaseSensitive = (1 << 1), + Int8 = (1 << 2), + Int16 = (1 << 3), + Int32 = (1 << 4), + Int64 = (1 << 5), + Float = (1 << 6), + Double = (1 << 7), + + BigEndian = (1 << 11), + }; +} + +enum class QHexFindMode { Text, Hex, Int, Float }; +enum class QHexFindDirection { All, Forward, Backward }; +enum class QHexArea { Header, Address, Hex, Ascii, Extra }; + +struct QHexPosition { + qint64 line; qint64 column; + static inline QHexPosition invalid() { return {-1, -1}; } + inline bool isValid() const { return line >= 0 && column >= 0; } + inline bool operator==(const QHexPosition& rhs) const { return (line == rhs.line) && (column == rhs.column); } + inline bool operator!=(const QHexPosition& rhs) const { return (line != rhs.line) || (column != rhs.column); } +}; + +namespace QHexUtils { + +QByteArray toHex(const QByteArray& ba, char sep); +QByteArray toHex(const QByteArray& ba); +qint64 positionToOffset(const QHexOptions* options, QHexPosition pos); +QPair find(const QHexView* hexview, QVariant value, qint64 startoffset = 0, QHexFindMode mode = QHexFindMode::Text, unsigned int options = QHexFindOptions::None, QHexFindDirection fd = QHexFindDirection::Forward); +QPair replace(const QHexView* hexview, QVariant oldvalue, QVariant newvalue, qint64 startoffset = 0, QHexFindMode mode = QHexFindMode::Text, unsigned int options = QHexFindOptions::None, QHexFindDirection fd = QHexFindDirection::Forward); +QHexPosition offsetToPosition(const QHexOptions* options, qint64 offset); +bool checkPattern(QString pattern); + +} diff --git a/UEFITool/qhexview5/qhexview.cpp b/UEFITool/qhexview5/qhexview.cpp new file mode 100644 index 0000000..5e970b1 --- /dev/null +++ b/UEFITool/qhexview5/qhexview.cpp @@ -0,0 +1,1261 @@ +#include "qhexview.h" +#include "model/buffer/qmemorybuffer.h" +#include "model/qhexcursor.h" +#include "model/qhexutils.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(QHEXVIEW_ENABLE_DIALOGS) + #include "dialogs/hexfinddialog.h" +#endif + +#if defined(QHEXVIEW_DEBUG) + #include + #define qhexview_fmtprint(fmt, ...) qDebug("%s " fmt, __func__, __VA_ARGS__) +#else + #define qhexview_fmtprint(fmt, ...) +#endif + +QHexView::QHexView(QWidget *parent) : QAbstractScrollArea(parent), m_fontmetrics(this->font()) +{ + QFont f = QFontDatabase::systemFont(QFontDatabase::FixedFont); + + if(f.styleHint() != QFont::TypeWriter) + { + f.setFamily("Monospace"); // Force Monospaced font + f.setStyleHint(QFont::TypeWriter); + } + + this->setFont(f); + this->setMouseTracking(true); + this->setFocusPolicy(Qt::StrongFocus); + this->viewport()->setCursor(Qt::IBeamCursor); + + QPalette p = this->palette(); + p.setBrush(QPalette::Window, p.base()); + + connect(this->verticalScrollBar(), &QScrollBar::valueChanged, this, [=](int) { this->viewport()->update(); }); + + m_hexmetadata = new QHexMetadata(&m_options, this); + connect(m_hexmetadata, &QHexMetadata::changed, this, [=]() { this->viewport()->update(); }); + + m_hexcursor = new QHexCursor(&m_options, this); + this->setDocument(QHexDocument::fromMemory(QByteArray(), this)); + this->checkState(); + + connect(m_hexcursor, &QHexCursor::positionChanged, this, [=]() { + m_writing = false; + this->ensureVisible(); + Q_EMIT positionChanged(); + }); + + connect(m_hexcursor, &QHexCursor::modeChanged, this, [=]() { + m_writing = false; + this->viewport()->update(); + Q_EMIT modeChanged(); + }); +} + +QRectF QHexView::headerRect() const +{ + if(m_options.hasFlag(QHexFlags::NoHeader)) return QRectF(); + + return QRectF(0, + 0, + this->endColumnX(), + this->lineHeight()); +} + +QRectF QHexView::addressRect() const +{ + qreal y = m_options.hasFlag(QHexFlags::NoHeader) ? 0 : this->lineHeight(); + + return QRectF(0, + y, + this->endColumnX(), + this->height() - y); +} + +QRectF QHexView::hexRect() const +{ + qreal y = m_options.hasFlag(QHexFlags::NoHeader) ? 0 : this->lineHeight(); + + return QRectF(this->hexColumnX(), + y, + this->asciiColumnX() - this->hexColumnX(), + this->height() - y); +} + +QRectF QHexView::asciiRect() const +{ + qreal y = m_options.hasFlag(QHexFlags::NoHeader) ? 0 : this->lineHeight(); + + return QRectF(this->asciiColumnX(), + y, + this->endColumnX() - this->asciiColumnX(), + this->height() - y); +} + +QHexDocument* QHexView::hexDocument() const { return m_hexdocument; } +QHexCursor* QHexView::hexCursor() const { return m_hexdocument ? m_hexcursor : nullptr; } +const QHexMetadata* QHexView::hexMetadata() const { return m_hexmetadata; } +QHexOptions QHexView::options() const { return m_options; } + +void QHexView::setOptions(const QHexOptions& options) +{ + auto oldlinelength = m_options.linelength; + m_options = options; + + if(oldlinelength != m_options.linelength) + m_hexmetadata->invalidate(); + + this->checkAndUpdate(); +} + +void QHexView::setBaseAddress(quint64 baseaddress) +{ + if(m_options.baseaddress == baseaddress) return; + + m_options.baseaddress = baseaddress; + this->checkAndUpdate(); +} + +void QHexView::setDelegate(QHexDelegate* rd) +{ + if(m_hexdelegate == rd) return; + m_hexdelegate = rd; + this->checkAndUpdate(); +} + +void QHexView::setDocument(QHexDocument* doc) +{ + if(!doc) doc = QHexDocument::fromMemory(QByteArray(), this); + if(!doc->parent()) doc->setParent(this); + + m_writing = false; + m_hexmetadata->clear(); + m_hexcursor->move(0); + + if(m_hexdocument) + { + disconnect(m_hexdocument, &QHexDocument::changed, this, nullptr); + disconnect(m_hexdocument, &QHexDocument::dataChanged, this, nullptr); + disconnect(m_hexdocument, &QHexDocument::reset, this, nullptr); + } + + m_hexdocument = doc; + + connect(m_hexdocument, &QHexDocument::reset, this, [=]() { + m_writing = false; + m_hexcursor->move(0); + this->checkAndUpdate(true); + }); + + connect(m_hexdocument, &QHexDocument::dataChanged, this, &QHexView::dataChanged); + connect(m_hexdocument, &QHexDocument::changed, this, [=]() { this->checkAndUpdate(true); }); + this->checkAndUpdate(true); +} + +void QHexView::setData(const QByteArray& ba) { m_hexdocument->setData(ba); } +void QHexView::setData(QHexBuffer* buffer) { m_hexdocument->setData(buffer); } +void QHexView::setCursorMode(QHexCursor::Mode mode) { m_hexcursor->setMode(mode); } + +void QHexView::setByteColor(quint8 b, QHexColor c) +{ + m_options.bytecolors[b] = c; + this->checkAndUpdate(); +} + +void QHexView::setByteForeground(quint8 b, QColor c) +{ + m_options.bytecolors[b].foreground = c; + this->checkAndUpdate(); +} + +void QHexView::setByteBackground(quint8 b, QColor c) +{ + m_options.bytecolors[b].background = c; + this->checkAndUpdate(); +} + +void QHexView::setMetadata(qint64 begin, qint64 end, const QColor& fgcolor, const QColor& bgcolor, const QString& comment) { m_hexmetadata->setMetadata(begin, end, fgcolor, bgcolor, comment); } +void QHexView::setForeground(qint64 begin, qint64 end, const QColor& fgcolor) { m_hexmetadata->setForeground(begin, end, fgcolor); } +void QHexView::setBackground(qint64 begin, qint64 end, const QColor& bgcolor) { m_hexmetadata->setBackground(begin, end, bgcolor); } +void QHexView::setComment(qint64 begin, qint64 end, const QString& comment) { m_hexmetadata->setComment(begin, end, comment); } +void QHexView::setMetadataSize(qint64 begin, qint64 length, const QColor& fgcolor, const QColor& bgcolor, const QString& comment) { m_hexmetadata->setMetadataSize(begin, length, fgcolor, bgcolor, comment); } +void QHexView::setForegroundSize(qint64 begin, qint64 length, const QColor& fgcolor) { m_hexmetadata->setForegroundSize(begin, length, fgcolor); } +void QHexView::setBackgroundSize(qint64 begin, qint64 length, const QColor& bgcolor) { m_hexmetadata->setBackgroundSize(begin, length, bgcolor); } +void QHexView::setCommentSize(qint64 begin, qint64 length, const QString& comment) { m_hexmetadata->setCommentSize(begin, length, comment); } +void QHexView::removeMetadata(qint64 line) { m_hexmetadata->removeMetadata(line); } +void QHexView::removeBackground(qint64 line) { m_hexmetadata->removeBackground(line); } +void QHexView::removeForeground(qint64 line) { m_hexmetadata->removeForeground(line); } +void QHexView::removeComments(qint64 line) { m_hexmetadata->removeComments(line); } +void QHexView::unhighlight(qint64 line) { m_hexmetadata->unhighlight(line); } +void QHexView::clearMetadata() { m_hexmetadata->clear(); } + +#if defined(QHEXVIEW_ENABLE_DIALOGS) +void QHexView::showFind() +{ + if(!m_hexdlgfind) m_hexdlgfind = new HexFindDialog(HexFindDialog::Type::Find, this); + m_hexdlgfind->show(); +} + +void QHexView::showReplace() +{ + if(!m_hexdlgreplace) m_hexdlgreplace = new HexFindDialog(HexFindDialog::Type::Replace, this); + m_hexdlgreplace->show(); +} +#endif + +void QHexView::undo() { if(m_hexdocument) m_hexdocument->undo(); } +void QHexView::redo() { if(m_hexdocument) m_hexdocument->redo(); } + +void QHexView::cut(bool hex) +{ + this->copy(hex); + if(m_readonly) return; + + if(m_hexcursor->hasSelection()) this->removeSelection(); + else m_hexdocument->remove(m_hexcursor->offset(), 1); +} + +void QHexView::copyAs(CopyMode mode) const +{ + QClipboard* c = qApp->clipboard(); + + QByteArray bytes = m_hexcursor->hasSelection() ? m_hexcursor->selectedBytes() : + m_hexdocument->read(m_hexcursor->offset(), 1); + + switch(mode) + { + case CopyMode::HexArrayCurly: + case CopyMode::HexArraySquare: { + QString hexchar; + int i = 0; + + for(char b : bytes) { + if(!hexchar.isEmpty()) { + hexchar += ", "; + if(m_options.copybreak && !(++i % m_options.linelength)) hexchar += "\n"; + } + + hexchar += "0x" + QString::number(static_cast(b), 16).toUpper(); + } + + c->setText(QString(mode == CopyMode::HexArraySquare ? "[%1]" : "{%1}").arg(hexchar)); + break; + } + + case CopyMode::HexArrayChar: { + QString hexchar; + + for(char b : bytes) + hexchar += "\\x" + QString::number(static_cast(b), 16).toUpper(); + + c->setText(QString("\"%1\"").arg(hexchar)); + break; + } + + default: { + QString hexchar; + + for(int i = 0; i < bytes.size(); i++) { + if(!(i % m_options.grouplength)) { + if(!hexchar.isEmpty()) { + hexchar += ", "; + if(m_options.copybreak && !(i % m_options.linelength)) hexchar += "\n"; + } + + hexchar += "0x"; + } + + hexchar += QString("%1").arg(static_cast(bytes[i]), 2, 16, QLatin1Char('0')).toUpper(); + } + + c->setText(hexchar); + break; + } + } +} + +void QHexView::copy(bool hex) const +{ + QClipboard* c = qApp->clipboard(); + + QByteArray bytes = m_hexcursor->hasSelection() ? m_hexcursor->selectedBytes() : + m_hexdocument->read(m_hexcursor->offset(), 1); + + if(hex) bytes = QHexUtils::toHex(bytes, ' ').toUpper(); + c->setText(bytes); +} + +void QHexView::paste(bool hex) +{ + if(m_readonly) return; + + QClipboard* c = qApp->clipboard(); + QByteArray pastedata = c->text().toUtf8(); + if(pastedata.isEmpty()) return; + + this->removeSelection(); + if(hex) pastedata = QByteArray::fromHex(pastedata); + + if(m_hexcursor->mode() == QHexCursor::Mode::Insert) m_hexdocument->insert(m_hexcursor->offset(), pastedata); + else m_hexdocument->replace(m_hexcursor->offset(), pastedata); +} + +void QHexView::selectAll() +{ + m_hexcursor->move(0); + m_hexcursor->select(m_hexdocument->length()); +} + +void QHexView::removeSelection() +{ + if(!m_hexcursor->hasSelection()) return; + if(!m_readonly) m_hexdocument->remove(m_hexcursor->selectionStartOffset(), m_hexcursor->selectionLength() - 1); + m_hexcursor->clearSelection(); +} + +void QHexView::switchMode() { m_hexcursor->switchMode(); } + +void QHexView::setAddressWidth(unsigned int w) +{ + if(w == m_options.addresswidth) return; + m_options.addresswidth = w; + this->checkState(); +} + +void QHexView::setScrollSteps(unsigned int l) +{ + if(l == m_options.scrollsteps) return; + m_options.scrollsteps = qMax(1u, l); +} + +void QHexView::setReadOnly(bool r) { m_readonly = r; } + +void QHexView::setAutoWidth(bool r) +{ + if(m_autowidth == r) return; + m_autowidth = r; + this->checkState(); +} + +void QHexView::paint(QPainter* painter) const +{ + QTextDocument doc; + doc.setDocumentMargin(0); + doc.setUndoRedoEnabled(false); + doc.setDefaultFont(this->font()); + + QTextCursor c(&doc); + + this->drawHeader(c); + this->drawDocument(c); + + painter->translate(-this->horizontalScrollBar()->value(), 0); + doc.drawContents(painter); + this->drawSeparators(painter); +} + +void QHexView::checkOptions() +{ + if(m_options.grouplength > m_options.linelength) m_options.grouplength = m_options.linelength; + if(!m_options.scrollsteps) m_options.scrollsteps = 1; + + m_options.addresswidth = qMax(m_options.addresswidth, this->calcAddressWidth()); + + // Round to nearest multiple of 2 + m_options.grouplength = 1u << (static_cast(qFloor(m_options.grouplength / 2.0))); + + if(m_options.grouplength <= 1) m_options.grouplength = 1; + + if(!m_options.headercolor.isValid()) + m_options.headercolor = this->palette().color(QPalette::Normal, QPalette::Highlight); +} + +void QHexView::setLineLength(unsigned int l) +{ + if(l == m_options.linelength) return; + m_options.linelength = l; + m_hexmetadata->invalidate(); + this->checkAndUpdate(true); +} + +void QHexView::setGroupLength(unsigned int l) +{ + if(l == m_options.grouplength) return; + m_options.grouplength = l; + this->checkAndUpdate(true); +} + +void QHexView::checkState() +{ + if(!m_hexdocument) return; + this->checkOptions(); + + int doclines = static_cast(this->lines()), vislines = this->visibleLines(true); + qint64 vscrollmax = doclines - vislines; + if(doclines >= vislines) vscrollmax++; + + this->verticalScrollBar()->setRange(0, qMax(0, vscrollmax)); + this->verticalScrollBar()->setPageStep(vislines - 1); + this->verticalScrollBar()->setSingleStep(m_options.scrollsteps); + + int vw = this->verticalScrollBar()->isVisible() ? this->verticalScrollBar()->width() : 0; + + static int oldmw = 0; + if(!oldmw) oldmw = this->maximumWidth(); + this->setMaximumWidth(m_autowidth ? qCeil(this->endColumnX() + vw + 3) : oldmw); + + this->horizontalScrollBar()->setRange(0, qMax(0, this->endColumnX() - this->width() + vw + 3)); + this->horizontalScrollBar()->setPageStep(this->width()); +} + +void QHexView::checkAndUpdate(bool calccolumns) +{ + this->checkState(); + if(calccolumns) this->calcColumns(); + this->viewport()->update(); +} + +void QHexView::calcColumns() +{ + if(!m_hexdocument) return; + + m_hexcolumns.clear(); + m_hexcolumns.reserve(m_options.linelength); + + auto x = this->hexColumnX(), cw = this->cellWidth() * 2; + + for(auto i = 0u; i < m_options.linelength; i++) + { + for(auto j = 0u; j < m_options.grouplength; j++, x += cw) + m_hexcolumns.push_back(QRect(x, 0, cw, 0)); + + x += this->cellWidth(); + } +} + +void QHexView::ensureVisible() +{ + if(!m_hexdocument) return; + + auto pos = m_hexcursor->position(); + auto vlines = this->visibleLines(); + + if(pos.line >= (this->verticalScrollBar()->value() + vlines)) + this->verticalScrollBar()->setValue(pos.line - vlines + 1); + else if(pos.line < this->verticalScrollBar()->value()) + this->verticalScrollBar()->setValue(pos.line); + else + this->viewport()->update(); +} + +void QHexView::drawSeparators(QPainter* p) const +{ + if(!m_options.hasFlag(QHexFlags::Separators)) return; + + auto oldpen = p->pen(); + p->setPen(m_options.separatorcolor.isValid() ? m_options.separatorcolor : this->palette().color(QPalette::Dark)); + + if(m_options.hasFlag(QHexFlags::HSeparator)) + { + QLineF l(0, m_fontmetrics.lineSpacing(), this->endColumnX(), m_fontmetrics.lineSpacing()); + if(!m_hexdelegate || !m_hexdelegate->paintSeparator(p, l, this)) p->drawLine(l); + } + + if(m_options.hasFlag(QHexFlags::VSeparator)) + { + QLineF l1(this->hexColumnX(), 0, this->hexColumnX(), this->height()); + QLineF l2(this->asciiColumnX(), 0, this->asciiColumnX(), this->height()); + + if(!m_hexdelegate || (m_hexdelegate && !m_hexdelegate->paintSeparator(p, l1, this))) + p->drawLine(l1); + + if(!m_hexdelegate || (m_hexdelegate && !m_hexdelegate->paintSeparator(p, l2, this))) + p->drawLine(l2); + } + + p->setPen(oldpen); +} + +void QHexView::drawHeader(QTextCursor& c) const +{ + if(m_options.hasFlag(QHexFlags::NoHeader)) return; + + static const auto RESET_FORMAT = [](const QHexOptions& options, QTextCharFormat& cf) { + cf = { }; + cf.setForeground(options.headercolor); + }; + + QString addresslabel; + if(m_hexdelegate) addresslabel = m_hexdelegate->addressHeader(this); + if(addresslabel.isEmpty() && !m_options.addresslabel.isEmpty()) addresslabel = m_options.addresslabel; + + QTextCharFormat cf; + RESET_FORMAT(m_options, cf); + if(m_hexdelegate) m_hexdelegate->renderHeaderPart(addresslabel, QHexArea::Address, cf, this); + c.insertText(" " + QHexView::reduced(addresslabel, this->addressWidth()) + " ", cf); + + if(m_hexdelegate) RESET_FORMAT(m_options, cf); + + QString hexlabel; + if(m_hexdelegate) hexlabel = m_hexdelegate->hexHeader(this); + if(hexlabel.isEmpty()) hexlabel = m_options.hexlabel; + + if(hexlabel.isNull()) + { + c.insertText(" ", { }); + + for(auto i = 0u; i < m_options.linelength; i += m_options.grouplength) + { + QString h = QString::number(i, 16).rightJustified(m_options.grouplength * 2, '0').toUpper(); + + if(m_hexdelegate) + { + RESET_FORMAT(m_options, cf); + m_hexdelegate->renderHeaderPart(h, QHexArea::Hex, cf, this); + } + + if(m_hexcursor->column() == static_cast(i) && m_options.hasFlag(QHexFlags::HighlightColumn)) + { + cf.setBackground(this->palette().color(QPalette::Highlight)); + cf.setForeground(this->palette().color(QPalette::HighlightedText)); + } + + c.insertText(h, cf); + c.insertText(" ", { }); + RESET_FORMAT(m_options, cf); + } + } + else + { + if(m_hexdelegate) m_hexdelegate->renderHeaderPart(hexlabel, QHexArea::Hex, cf, this); + c.insertText(" " + QHexView::reduced(hexlabel, (this->hexColumnWidth() / this->cellWidth()) - 1) + " "); + } + + if(m_hexdelegate) RESET_FORMAT(m_options, cf); + + QString asciilabel; + if(m_hexdelegate) asciilabel = m_hexdelegate->asciiHeader(this); + if(asciilabel.isEmpty()) asciilabel = m_options.asciilabel; + + if(asciilabel.isNull()) + { + c.insertText(" ", { }); + + for(unsigned int i = 0; i < m_options.linelength; i++) + { + QString a = QString::number(i, 16).toUpper(); + + if(m_hexdelegate) + { + RESET_FORMAT(m_options, cf); + m_hexdelegate->renderHeaderPart(a, QHexArea::Ascii, cf, this); + } + + if(m_hexcursor->column() == static_cast(i) && m_options.hasFlag(QHexFlags::HighlightColumn)) + { + cf.setBackground(this->palette().color(QPalette::Highlight)); + cf.setForeground(this->palette().color(QPalette::HighlightedText)); + } + + c.insertText(a, cf); + RESET_FORMAT(m_options, cf); + } + + c.insertText(" ", { }); + } + else + { + if(m_hexdelegate) m_hexdelegate->renderHeaderPart(asciilabel, QHexArea::Ascii, cf, this); + c.insertText(" " + QHexView::reduced(asciilabel, ((this->endColumnX() - this->asciiColumnX() - this->cellWidth()) / this->cellWidth()) - 1) + " "); + } + + QTextBlockFormat bf; + if(m_options.hasFlag(QHexFlags::StyledHeader)) bf.setBackground(this->palette().color(QPalette::Window)); + if(m_hexdelegate) m_hexdelegate->renderHeader(bf, this); + c.setBlockFormat(bf); + c.insertBlock(); +} + +void QHexView::drawDocument(QTextCursor& c) const +{ + if(!m_hexdocument) return; + + qreal y = !m_options.hasFlag(QHexFlags::NoHeader) ? this->lineHeight() : 0; + quint64 line = static_cast(this->verticalScrollBar()->value()); + + QTextCharFormat addrformat; + addrformat.setForeground(this->palette().color(QPalette::Normal, QPalette::Highlight)); + + for(qint64 l = 0; m_hexdocument->isEmpty() || (line < this->lines() && l < this->visibleLines()); l++, line++, y += this->lineHeight()) + { + quint64 address = line * m_options.linelength + this->baseAddress(); + QString addrstr = QString::number(address, 16).rightJustified(this->addressWidth(), '0').toUpper(); + + // Address Part + QTextCharFormat acf; + acf.setForeground(m_options.headercolor); + + if(m_options.hasFlag(QHexFlags::StyledAddress)) + acf.setBackground(this->palette().color(QPalette::Window)); + + if(m_hexdelegate) m_hexdelegate->renderAddress(address, acf, this); + + if(m_hexcursor->line() == static_cast(line) && m_options.hasFlag(QHexFlags::HighlightAddress)) + { + acf.setBackground(this->palette().color(QPalette::Highlight)); + acf.setForeground(this->palette().color(QPalette::HighlightedText)); + } + + c.insertText(" " + addrstr + " ", acf); + + auto linebytes = this->getLine(line); + c.insertText(" ", { }); + + // Hex Part + for(auto column = 0u; column < m_options.linelength; ) + { + QTextCharFormat cf; + + for(auto byteidx = 0u; byteidx < m_options.grouplength; byteidx++, column++) + { + QString s = linebytes.isEmpty() || column >= static_cast(linebytes.size()) ? " " : QString(QHexUtils::toHex(linebytes.mid(column, 1)).toUpper()); + quint8 b = static_cast(column) < linebytes.size() ? linebytes.at(column) : 0x00; + cf = this->drawFormat(c, b, s, QHexArea::Hex, line, column, static_cast(column) < linebytes.size()); + } + + c.insertText(" ", cf); + } + + c.insertText(" ", { }); + + // Ascii Part + for(auto column = 0u; column < m_options.linelength; column++) + { + auto s = linebytes.isEmpty() || + column >= static_cast(linebytes.size()) ? QChar(' ') : + (QChar::isPrint(linebytes.at(column)) ? QChar(linebytes.at(column)) : m_options.unprintablechar); + + quint8 b = static_cast(column) < linebytes.size() ? linebytes.at(column) : 0x00; + this->drawFormat(c, b, s, QHexArea::Ascii, line, column, static_cast(column) < linebytes.size()); + } + + QTextBlockFormat bf; + + if(m_options.linealternatebackground.isValid() && line % 2) + bf.setBackground(m_options.linealternatebackground); + else if(m_options.linebackground.isValid() && !(line % 2)) + bf.setBackground(m_options.linebackground); + + c.setBlockFormat(bf); + c.insertBlock({}); + if(m_hexdocument->isEmpty()) break; + } +} + +unsigned int QHexView::calcAddressWidth() const +{ + if(!m_hexdocument) return 0; + + auto maxaddr = static_cast(m_options.baseaddress + m_hexdocument->length()); + if(maxaddr <= std::numeric_limits::max()) return 8; + return QString::number(maxaddr, 16).size(); +} + +int QHexView::visibleLines(bool absolute) const +{ + int vl = static_cast(qCeil(this->viewport()->height() / this->lineHeight())); + if(!m_options.hasFlag(QHexFlags::NoHeader)) vl--; + return absolute ? vl : qMin(this->lines(), vl); +} + +qint64 QHexView::getLastColumn(qint64 line) const { return this->getLine(line).size() - 1; } +qint64 QHexView::lastLine() const { return qMax(0, this->lines() - 1); } + +qreal QHexView::hexColumnWidth() const +{ + int l = 0; + + for(auto i = 0u; i < m_options.linelength; i += m_options.grouplength) + l += (2 * m_options.grouplength) + 1; + + return this->getNCellsWidth(l); +} + +unsigned int QHexView::addressWidth() const +{ + if(!m_hexdocument || m_options.addresswidth) return m_options.addresswidth; + return this->calcAddressWidth(); +} + +unsigned int QHexView::lineLength() const { return m_options.linelength; } +bool QHexView::canUndo() const { return m_hexdocument && m_hexdocument->canUndo(); } +bool QHexView::canRedo() const { return m_hexdocument && m_hexdocument->canRedo(); } +quint64 QHexView::offset() const { return m_hexcursor->offset(); } +quint64 QHexView::address() const { return m_hexcursor->address(); } +QHexPosition QHexView::position() const { return m_hexcursor->position(); } +QHexPosition QHexView::selectionStart() const { return m_hexcursor->selectionStart(); } +QHexPosition QHexView::selectionEnd() const { return m_hexcursor->selectionEnd(); } +quint64 QHexView::selectionStartOffset() const { return m_hexcursor->selectionStartOffset(); } +quint64 QHexView::selectionEndOffset() const { return m_hexcursor->selectionEndOffset(); } +quint64 QHexView::baseAddress() const { return m_options.baseaddress; } + +quint64 QHexView::lines() const +{ + if(!m_hexdocument) return 0; + + auto lines = static_cast(qCeil(m_hexdocument->length() / static_cast(m_options.linelength))); + return !m_hexdocument->isEmpty() && !lines ? 1 : lines; +} + +qint64 QHexView::replace(const QVariant& oldvalue, const QVariant& newvalue, qint64 offset, QHexFindMode mode, unsigned int options, QHexFindDirection fd) const +{ + auto res = QHexUtils::replace(this, oldvalue, newvalue, offset, mode, options, fd); + + if(res.first > -1) + { + m_hexcursor->move(res.first); + m_hexcursor->selectSize(res.second); + } + + return res.first; +} + +qint64 QHexView::find(const QVariant& value, qint64 offset, QHexFindMode mode, unsigned int options, QHexFindDirection fd) const +{ + auto res = QHexUtils::find(this, value, offset, mode, options, fd); + + if(res.first > -1) + { + m_hexcursor->move(res.first); + m_hexcursor->selectSize(res.second); + } + + return res.first; +} + +qreal QHexView::hexColumnX() const { return this->getNCellsWidth(this->addressWidth() + 2); } +qreal QHexView::asciiColumnX() const { return this->hexColumnX() + this->hexColumnWidth() + this->cellWidth(); } +qreal QHexView::endColumnX() const { return this->asciiColumnX() + this->getNCellsWidth(m_options.linelength + 1) + this->cellWidth(); } +qreal QHexView::getNCellsWidth(int n) const { return n * this->cellWidth(); } + +qreal QHexView::cellWidth() const +{ +#if QT_VERSION >= QT_VERSION_CHECK(5, 11, 0) + return m_fontmetrics.horizontalAdvance(" "); +#else + return m_fontmetrics.width(" "); +#endif +} + +qreal QHexView::lineHeight() const { return m_fontmetrics.height(); } + +QHexPosition QHexView::positionFromPoint(QPoint pt) const +{ + QHexPosition pos = QHexPosition::invalid(); + auto abspt = this->absolutePoint(pt); + + switch(this->areaFromPoint(pt)) + { + case QHexArea::Hex: { + pos.column = -1; + + for(qint64 i = 0; i < m_hexcolumns.size(); i++) { + if(m_hexcolumns.at(i).left() > abspt.x()) break; + pos.column = i; + } + + break; + } + + case QHexArea::Ascii: pos.column = qMax(qFloor((abspt.x() - this->asciiColumnX()) / this->cellWidth()) - 1, 0); break; + case QHexArea::Address: pos.column = 0; break; + case QHexArea::Header: return QHexPosition::invalid(); + default: break; + } + + pos.line = qMin(this->verticalScrollBar()->value() + (abspt.y() / this->lineHeight()), this->lines()); + if(!m_options.hasFlag(QHexFlags::NoHeader)) pos.line = qMax(0, pos.line - 1); + + auto docline = this->getLine(pos.line); + pos.column = qMin(pos.column, docline.isEmpty() ? 0 : docline.size()); + + qhexview_fmtprint("line: %lld, col: %lld", pos.line, pos.column); + return pos; +} + +QPoint QHexView::absolutePoint(QPoint pt) const { return pt + QPoint(this->horizontalScrollBar()->value(), 0); } + +QHexArea QHexView::areaFromPoint(QPoint pt) const +{ + pt = this->absolutePoint(pt); + qreal line = this->verticalScrollBar()->value() + pt.y() / this->lineHeight(); + + if(!m_options.hasFlag(QHexFlags::NoHeader) && !qFloor(line)) return QHexArea::Header; + if(pt.x() < this->hexColumnX()) return QHexArea::Address; + if(pt.x() < this->asciiColumnX()) return QHexArea::Hex; + if(pt.x() < this->endColumnX()) return QHexArea::Ascii; + return QHexArea::Extra; +} + +QTextCharFormat QHexView::drawFormat(QTextCursor& c, quint8 b, const QString& s, QHexArea area, qint64 line, qint64 column, bool applyformat) const +{ + QTextCharFormat cf, selcf; + QHexPosition pos{line, column}; + + if(applyformat) + { + auto offset = m_hexcursor->positionToOffset(pos); + bool hasdelegate = m_hexdelegate && m_hexdelegate->render(offset, b, cf, this); + + if(!hasdelegate) + { + auto it = m_options.bytecolors.find(b); + + if(it != m_options.bytecolors.end()) + { + if(it->background.isValid()) cf.setBackground(it->background); + if(it->foreground.isValid()) cf.setForeground(it->foreground); + } + } + + const auto* metadataline = m_hexmetadata->find(line); + + if(metadataline) + { + for(const auto& metadata : *metadataline) + { + if(offset < metadata.begin || offset >= metadata.end) continue; + + if(!hasdelegate) + { + if(metadata.foreground.isValid()) cf.setForeground(metadata.foreground); + + if(metadata.background.isValid()) + { + cf.setBackground(metadata.background); + + if(!metadata.foreground.isValid()) + cf.setForeground(this->getReadableColor(metadata.background)); + } + } + + if(!metadata.comment.isEmpty()) + { + cf.setUnderlineColor(m_options.commentcolor.isValid() ? m_options.commentcolor : this->palette().color(QPalette::WindowText)); + cf.setUnderlineStyle(QTextCharFormat::UnderlineStyle::SingleUnderline); + } + + if(offset == metadata.begin) // Remove previous metadata's style, if needed + { + if(metadata.comment.isEmpty()) selcf.setUnderlineStyle(QTextCharFormat::UnderlineStyle::NoUnderline); + if(!metadata.foreground.isValid()) selcf.setForeground(Qt::color1); + if(!metadata.background.isValid()) selcf.setBackground(Qt::transparent); + } + + if(offset < metadata.end - 1 && column < this->getLastColumn(line)) + selcf = cf; + } + } + + if(hasdelegate && column < this->getLastColumn(line)) selcf = cf; + } + + if(this->hexCursor()->isSelected(line, column)) + { + auto offset = this->hexCursor()->positionToOffset(pos); + auto selend = this->hexCursor()->selectionEndOffset(); + + cf.setBackground(this->palette().color(QPalette::Normal, QPalette::Highlight)); + cf.setForeground(this->palette().color(QPalette::Normal, QPalette::HighlightedText)); + if(offset < selend && column < this->getLastColumn(line)) selcf = cf; + } + + if(this->hexCursor()->position() == pos) + { + auto cursorbg = this->palette().color(this->hasFocus() ? QPalette::Normal : QPalette::Disabled, QPalette::WindowText); + auto cursorfg = this->palette().color(this->hasFocus() ? QPalette::Normal : QPalette::Disabled, QPalette::Base); + auto discursorbg = this->palette().color(QPalette::Disabled, QPalette::WindowText); + auto discursorfg = this->palette().color(QPalette::Disabled, QPalette::Base); + + switch(m_hexcursor->mode()) + { + case QHexCursor::Mode::Insert: + cf.setUnderlineColor(m_currentarea == area ? cursorbg : discursorbg); + cf.setUnderlineStyle(QTextCharFormat::UnderlineStyle::SingleUnderline); + break; + + case QHexCursor::Mode::Overwrite: + cf.setBackground(m_currentarea == area ? cursorbg : discursorbg); + cf.setForeground(m_currentarea == area ? cursorfg : discursorfg); + break; + } + } + + c.insertText(s, cf); + return selcf; +} + +void QHexView::moveNext(bool select) +{ + auto line = this->hexCursor()->line(), column = this->hexCursor()->column(); + + if(column >= m_options.linelength - 1) + { + line++; + column = 0; + } + else + column++; + + qint64 offset = this->hexCursor()->mode() == QHexCursor::Mode::Insert ? 1 : 0; + if(select) this->hexCursor()->select(qMin(line, this->lines()), qMin(column, this->getLastColumn(line) + offset)); + else this->hexCursor()->move(qMin(line, this->lines()), qMin(column, this->getLastColumn(line) + offset)); +} + +void QHexView::movePrevious(bool select) +{ + auto line = this->hexCursor()->line(), column = this->hexCursor()->column(); + + if(column <= 0) + { + if(!line) return; + column = this->getLine(--line).size() - 1; + } + else + column--; + + if(select) this->hexCursor()->select(qMin(line, this->lines()), qMin(column, this->getLastColumn(line))); + else this->hexCursor()->move(qMin(line, this->lines()), qMin(column, this->getLastColumn(line))); +} + +bool QHexView::keyPressMove(QKeyEvent* e) +{ + if(e->matches(QKeySequence::MoveToNextChar) || e->matches(QKeySequence::SelectNextChar)) + this->moveNext(e->matches(QKeySequence::SelectNextChar)); + else if(e->matches(QKeySequence::MoveToPreviousChar) || e->matches(QKeySequence::SelectPreviousChar)) + this->movePrevious(e->matches(QKeySequence::SelectPreviousChar)); + else if(e->matches(QKeySequence::MoveToNextLine) || e->matches(QKeySequence::SelectNextLine)) + { + if(this->hexCursor()->line() == this->lastLine()) return true; + auto nextline = this->hexCursor()->line() + 1; + if(e->matches(QKeySequence::MoveToNextLine)) this->hexCursor()->move(nextline, this->hexCursor()->column()); + else this->hexCursor()->select(nextline, this->hexCursor()->column()); + } + else if(e->matches(QKeySequence::MoveToPreviousLine) || e->matches(QKeySequence::SelectPreviousLine)) + { + if(!this->hexCursor()->line()) return true; + auto prevline = this->hexCursor()->line() - 1; + if(e->matches(QKeySequence::MoveToPreviousLine)) this->hexCursor()->move(prevline, this->hexCursor()->column()); + else this->hexCursor()->select(prevline, this->hexCursor()->column()); + } + else if(e->matches(QKeySequence::MoveToNextPage) || e->matches(QKeySequence::SelectNextPage)) + { + if(this->lastLine() == this->hexCursor()->line()) return true; + auto pageline = qMin(this->lastLine(), this->hexCursor()->line() + this->visibleLines()); + if(e->matches(QKeySequence::MoveToNextPage)) this->hexCursor()->move(pageline, this->hexCursor()->column()); + else this->hexCursor()->select(pageline, this->hexCursor()->column()); + } + else if(e->matches(QKeySequence::MoveToPreviousPage) || e->matches(QKeySequence::SelectPreviousPage)) + { + if(!this->hexCursor()->line()) return true; + auto pageline = qMax(0, this->hexCursor()->line() - this->visibleLines()); + if(e->matches(QKeySequence::MoveToPreviousPage)) this->hexCursor()->move(pageline, this->hexCursor()->column()); + else this->hexCursor()->select(pageline, this->hexCursor()->column()); + } + else if(e->matches(QKeySequence::MoveToStartOfDocument) || e->matches(QKeySequence::SelectStartOfDocument)) + { + if(!this->hexCursor()->line()) return true; + if(e->matches(QKeySequence::MoveToStartOfDocument)) this->hexCursor()->move(0, 0); + else this->hexCursor()->select(0, 0); + } + else if(e->matches(QKeySequence::MoveToEndOfDocument) || e->matches(QKeySequence::SelectEndOfDocument)) + { + if(this->lastLine() == this->hexCursor()->line()) return true; + if(e->matches(QKeySequence::MoveToEndOfDocument)) this->hexCursor()->move(this->lastLine(), this->getLastColumn(this->hexCursor()->line())); + else this->hexCursor()->select(this->lastLine(), this->getLastColumn(this->lastLine())); + } + else if(e->matches(QKeySequence::MoveToStartOfLine) || e->matches(QKeySequence::SelectStartOfLine)) + { + auto offset = this->hexCursor()->positionToOffset({this->hexCursor()->line(), 0}); + if(e->matches(QKeySequence::MoveToStartOfLine)) this->hexCursor()->move(offset); + else this->hexCursor()->select(offset); + } + else if(e->matches(QKeySequence::SelectEndOfLine) || e->matches(QKeySequence::MoveToEndOfLine)) + { + auto offset = this->hexCursor()->positionToOffset({this->hexCursor()->line(), this->getLastColumn(this->hexCursor()->line())}); + if(e->matches(QKeySequence::SelectEndOfLine)) this->hexCursor()->select(offset); + else this->hexCursor()->move(offset); + } + else + return false; + + return true; +} + +bool QHexView::keyPressTextInput(QKeyEvent* e) +{ + if(m_readonly || e->text().isEmpty() || (e->modifiers() & Qt::ControlModifier)) return false; + + bool atend = m_hexcursor->offset() >= m_hexdocument->length(); + if(atend && m_hexcursor->mode() == QHexCursor::Mode::Overwrite) return false; + + char key = e->text().at(0).toLatin1(); + + switch(m_currentarea) + { + case QHexArea::Hex: { + if(!isxdigit(key)) return false; + + bool ok = false; + auto val = static_cast(QString(key).toUInt(&ok, 16)); + if(!ok) return false; + m_hexcursor->removeSelection(); + + quint8 ch = m_hexdocument->isEmpty() || m_hexcursor->offset() >= m_hexdocument->length() ? '\x00' : m_hexdocument->at(m_hexcursor->offset()); + ch = m_writing ? (ch << 4) | val : val; + + if(!m_writing && (m_hexcursor->mode() == QHexCursor::Mode::Insert)) m_hexdocument->insert(m_hexcursor->offset(), val); + else m_hexdocument->replace(m_hexcursor->offset(), ch); + + m_writing = !m_writing; + if(!m_writing) + this->moveNext(); + + break; + } + + case QHexArea::Ascii: { + if(!QChar::isPrint(key)) return false; + m_hexcursor->removeSelection(); + if(m_hexcursor->mode() == QHexCursor::Mode::Insert) m_hexdocument->insert(m_hexcursor->offset(), key); + else m_hexdocument->replace(m_hexcursor->offset(), key); + this->moveNext(); + break; + } + + default: return false; + } + + return true; +} + +bool QHexView::keyPressAction(QKeyEvent* e) +{ + if(e->modifiers() != Qt::NoModifier) + { + if(e->matches(QKeySequence::SelectAll)) this->selectAll(); + else if(!m_readonly && e->matches(QKeySequence::Undo)) this->undo(); + else if(!m_readonly && e->matches(QKeySequence::Redo)) this->redo(); + else if(!m_readonly && e->matches(QKeySequence::Cut)) this->cut(m_currentarea != QHexArea::Ascii); + else if(e->matches(QKeySequence::Copy)) this->copy(m_currentarea != QHexArea::Ascii); + else if(!m_readonly && e->matches(QKeySequence::Paste)) this->paste(m_currentarea != QHexArea::Ascii); + else return false; + + return true; + } + + if(m_readonly) return false; + + switch(e->key()) + { + case Qt::Key_Backspace: + case Qt::Key_Delete: { + if(!m_hexcursor->hasSelection()) { + auto offset = m_hexcursor->offset(); + if(offset <= 0) return true; + + if(e->key() == Qt::Key_Backspace) m_hexdocument->remove(offset - 1, 1); + else m_hexdocument->remove(offset, 1); + } + else + { + auto oldpos = m_hexcursor->selectionStart(); + m_hexcursor->removeSelection(); + m_hexcursor->move(oldpos); + } + + if(e->key() == Qt::Key_Backspace) this->movePrevious(); + m_writing = false; + break; + } + + case Qt::Key_Insert: + m_writing = false; + m_hexcursor->switchMode(); + break; + + default: return false; + } + + return true; +} + +bool QHexView::event(QEvent* e) +{ + switch(e->type()) + { + case QEvent::FontChange: + m_fontmetrics = QFontMetricsF(this->font()); + this->checkAndUpdate(true); + return true; + + case QEvent::ToolTip: { + if(m_hexdocument && (m_currentarea == QHexArea::Hex || m_currentarea == QHexArea::Ascii)) { + auto* helpevent = static_cast(e); + auto pos = this->positionFromPoint(helpevent->pos()); + auto comment = m_hexmetadata->getComment(pos.line, pos.column); + if(!comment.isEmpty()) QToolTip::showText(helpevent->globalPos(), comment); + return true; + } + + break; + } + + default: break; + } + + return QAbstractScrollArea::event(e); +} + +void QHexView::showEvent(QShowEvent* e) +{ + QAbstractScrollArea::showEvent(e); + this->checkAndUpdate(true); +} + +void QHexView::paintEvent(QPaintEvent*) +{ + if(!m_hexdocument) return; + + QPainter painter(this->viewport()); + if(m_hexdelegate) m_hexdelegate->paint(&painter, this); + else this->paint(&painter); +} + +void QHexView::resizeEvent(QResizeEvent* e) +{ + this->checkState(); + QAbstractScrollArea::resizeEvent(e); +} + +void QHexView::focusInEvent(QFocusEvent* e) +{ + QAbstractScrollArea::focusInEvent(e); + if(m_hexdocument) this->viewport()->update(); +} + +void QHexView::focusOutEvent(QFocusEvent* e) +{ + QAbstractScrollArea::focusOutEvent(e); + if(m_hexdocument) this->viewport()->update(); +} + +void QHexView::mousePressEvent(QMouseEvent* e) +{ + QAbstractScrollArea::mousePressEvent(e); + if(!m_hexdocument || e->button() != Qt::LeftButton) return; + + auto pos = this->positionFromPoint(e->pos()); + if(!pos.isValid()) return; + + auto area = this->areaFromPoint(e->pos()); + qhexview_fmtprint("%d", static_cast(area)); + + switch(area) + { + case QHexArea::Address: this->hexCursor()->move(pos.line, 0); break; + case QHexArea::Hex: m_currentarea = area; this->hexCursor()->move(pos); break; + case QHexArea::Ascii: m_currentarea = area; this->hexCursor()->move(pos.line, pos.column); break; + default: return; + } + + this->viewport()->update(); +} + +void QHexView::mouseMoveEvent(QMouseEvent* e) +{ + QAbstractScrollArea::mouseMoveEvent(e); + if(!this->hexCursor()) return; + + e->accept(); + auto area = this->areaFromPoint(e->pos()); + + switch(area) + { + case QHexArea::Header: this->viewport()->setCursor(Qt::ArrowCursor); return; + case QHexArea::Address: this->viewport()->setCursor(Qt::ArrowCursor); break; + default: this->viewport()->setCursor(Qt::IBeamCursor); break; + } + + if(e->buttons() == Qt::LeftButton) + { + auto pos = this->positionFromPoint(e->pos()); + if(!pos.isValid()) return; + if(area == QHexArea::Ascii || area == QHexArea::Hex) m_currentarea = area; + this->hexCursor()->select(pos); + } +} + +void QHexView::wheelEvent(QWheelEvent* e) +{ + e->ignore(); + if(!m_hexdocument || !this->verticalScrollBar()->isVisible()) return; + + auto ydelta = e->angleDelta().y(); + if(ydelta > 0) this->verticalScrollBar()->setValue(this->verticalScrollBar()->value() - m_options.scrollsteps); + else if(ydelta < 0) this->verticalScrollBar()->setValue(this->verticalScrollBar()->value() + m_options.scrollsteps); +} + +void QHexView::keyPressEvent(QKeyEvent* e) +{ + bool handled = false; + + if(this->hexCursor()) + { + handled = this->keyPressMove(e); + if(!handled) handled = this->keyPressAction(e); + if(!handled) handled = this->keyPressTextInput(e); + } + + if(handled) e->accept(); + else QAbstractScrollArea::keyPressEvent(e); +} + +QString QHexView::reduced(const QString& s, int maxlen) +{ + if(s.length() <= maxlen) return s.leftJustified(maxlen); + return s.mid(0, maxlen - 1) + "\u2026"; +} + +bool QHexView::isColorLight(QColor c) +{ + return std::sqrt(0.299 * std::pow(c.red(), 2) + + 0.587 * std::pow(c.green(), 2) + + 0.114 * std::pow(c.blue(), 2)) > 127.5; +} + +QColor QHexView::getReadableColor(QColor c) const +{ + QPalette palette = this->palette(); + return QHexView::isColorLight(c) ? palette.color(QPalette::Normal, QPalette::WindowText) : palette.color(QPalette::Normal, QPalette::HighlightedText); +} + +QByteArray QHexView::selectedBytes() const { return m_hexcursor->hasSelection() ? m_hexdocument->read(m_hexcursor->selectionStartOffset(), m_hexcursor->selectionLength()) : QByteArray{ }; } +QByteArray QHexView::getLine(qint64 line) const { return m_hexdocument ? m_hexdocument->read(line * m_options.linelength, m_options.linelength) : QByteArray{ }; } diff --git a/UEFITool/qhexview5/qhexview.h b/UEFITool/qhexview5/qhexview.h new file mode 100644 index 0000000..6ecafa7 --- /dev/null +++ b/UEFITool/qhexview5/qhexview.h @@ -0,0 +1,169 @@ +#pragma once + +#define QHEXVIEW_VERSION 5.0 + +#include +#include +#include +#include +#include +#include "model/qhexdelegate.h" +#include "model/qhexdocument.h" +#include "model/qhexcursor.h" + +#if defined(QHEXVIEW_ENABLE_DIALOGS) +class HexFindDialog; +#endif + +class QHexView : public QAbstractScrollArea +{ + Q_OBJECT + + public: + enum class CopyMode { Visual, HexArraySquare, HexArrayCurly, HexArrayChar }; + Q_ENUM(CopyMode); + + public: + explicit QHexView(QWidget *parent = nullptr); + QRectF headerRect() const; + QRectF addressRect() const; + QRectF hexRect() const; + QRectF asciiRect() const; + QHexDocument* hexDocument() const; + QHexCursor* hexCursor() const; + const QHexMetadata* hexMetadata() const; + QHexOptions options() const; + QColor getReadableColor(QColor c) const; + QByteArray selectedBytes() const; + QByteArray getLine(qint64 line) const; + unsigned int addressWidth() const; + unsigned int lineLength() const; + bool canUndo() const; + bool canRedo() const; + quint64 offset() const; + quint64 address() const; + QHexPosition position() const; + QHexPosition selectionStart() const; + QHexPosition selectionEnd() const; + quint64 selectionStartOffset() const; + quint64 selectionEndOffset() const; + quint64 baseAddress() const; + quint64 lines() const; + qint64 replace(const QVariant& oldvalue, const QVariant& newvalue, qint64 offset, QHexFindMode mode = QHexFindMode::Text, unsigned int options = QHexFindOptions::None, QHexFindDirection fd = QHexFindDirection::Forward) const; + qint64 find(const QVariant& value, qint64 offset, QHexFindMode mode = QHexFindMode::Text, unsigned int options = QHexFindOptions::None, QHexFindDirection fd = QHexFindDirection::Forward) const; + void setOptions(const QHexOptions& options); + void setBaseAddress(quint64 baseaddress); + void setDelegate(QHexDelegate* rd); + void setDocument(QHexDocument* doc); + void setData(const QByteArray& ba); + void setData(QHexBuffer* buffer); + void setCursorMode(QHexCursor::Mode mode); + void setByteColor(quint8 b, QHexColor c); + void setByteForeground(quint8 b, QColor c); + void setByteBackground(quint8 b, QColor c); + void setMetadata(qint64 begin, qint64 end, const QColor &fgcolor, const QColor &bgcolor, const QString &comment); + void setForeground(qint64 begin, qint64 end, const QColor &fgcolor); + void setBackground(qint64 begin, qint64 end, const QColor &bgcolor); + void setComment(qint64 begin, qint64 end, const QString& comment); + void setMetadataSize(qint64 begin, qint64 length, const QColor &fgcolor, const QColor &bgcolor, const QString &comment); + void setForegroundSize(qint64 begin, qint64 length, const QColor &fgcolor); + void setBackgroundSize(qint64 begin, qint64 length, const QColor &bgcolor); + void setCommentSize(qint64 begin, qint64 length, const QString& comment); + void removeMetadata(qint64 line); + void removeBackground(qint64 line); + void removeForeground(qint64 line); + void removeComments(qint64 line); + void unhighlight(qint64 line); + void clearMetadata(); + + public Q_SLOTS: +#if defined(QHEXVIEW_ENABLE_DIALOGS) + void showFind(); + void showReplace(); +#endif + void undo(); + void redo(); + void cut(bool hex = false); + void copyAs(CopyMode mode = CopyMode::Visual) const; + void copy(bool hex = false) const; + void paste(bool hex = false); + void selectAll(); + void removeSelection(); + void switchMode(); + void setAddressWidth(unsigned int w); + void setLineLength(unsigned int l); + void setGroupLength(unsigned int l); + void setScrollSteps(unsigned int l); + void setReadOnly(bool r); + void setAutoWidth(bool r); + + private: + void paint(QPainter* painter) const; + void checkOptions(); + void checkState(); + void checkAndUpdate(bool calccolumns = false); + void calcColumns(); + void ensureVisible(); + void drawSeparators(QPainter* p) const; + void drawHeader(QTextCursor& c) const; + void drawDocument(QTextCursor& c) const; + QTextCharFormat drawFormat(QTextCursor& c, quint8 b, const QString& s, QHexArea area, qint64 line, qint64 column, bool applyformat) const; + unsigned int calcAddressWidth() const; + int visibleLines(bool absolute = false) const; + qint64 getLastColumn(qint64 line) const; + qint64 lastLine() const; + qreal getNCellsWidth(int n) const; + qreal hexColumnWidth() const; + qreal hexColumnX() const; + qreal asciiColumnX() const; + qreal endColumnX() const; + qreal cellWidth() const; + qreal lineHeight() const; + QHexPosition positionFromPoint(QPoint pt) const; + QPoint absolutePoint(QPoint pt) const; + QHexArea areaFromPoint(QPoint pt) const; + void moveNext(bool select = false); + void movePrevious(bool select = false); + bool keyPressMove(QKeyEvent* e); + bool keyPressTextInput(QKeyEvent* e); + bool keyPressAction(QKeyEvent* e); + + protected: + bool event(QEvent* e) override; + void showEvent(QShowEvent* e) override; + void paintEvent(QPaintEvent*) override; + void resizeEvent(QResizeEvent* e) override; + void focusInEvent(QFocusEvent* e) override; + void focusOutEvent(QFocusEvent* e) override; + void mousePressEvent(QMouseEvent* e) override; + void mouseMoveEvent(QMouseEvent* e) override; + void wheelEvent(QWheelEvent* e) override; + void keyPressEvent(QKeyEvent *e) override; + + private: + static QString reduced(const QString& s, int maxlen); + static bool isColorLight(QColor c); + + Q_SIGNALS: + void dataChanged(const QByteArray& data, quint64 offset, QHexDocument::ChangeReason reason); + void positionChanged(); + void modeChanged(); + + private: + bool m_readonly{false}, m_writing{false}, m_autowidth{false}; + QHexArea m_currentarea{QHexArea::Ascii}; + QList m_hexcolumns; + QFontMetricsF m_fontmetrics; + QHexOptions m_options; + QHexCursor* m_hexcursor{nullptr}; + QHexDocument* m_hexdocument{nullptr}; + QHexMetadata* m_hexmetadata{nullptr}; + QHexDelegate* m_hexdelegate{nullptr}; +#if defined(QHEXVIEW_ENABLE_DIALOGS) + HexFindDialog *m_hexdlgfind{nullptr}, *m_hexdlgreplace{nullptr}; +#endif + + friend class QHexDelegate; + friend class QHexCursor; +}; + diff --git a/UEFITool/uefitool.cpp b/UEFITool/uefitool.cpp index 1028f40..ea3f309 100644 --- a/UEFITool/uefitool.cpp +++ b/UEFITool/uefitool.cpp @@ -585,7 +585,7 @@ void UEFITool::about() tr("UEFITool %1.

" "Copyright (c) 2013-2023, Nikolaj Schlej.

" "Program icon made by Alexander Zhidkov.

" - "GUI uses QHexEdit2 library made by Simsys.
" + "GUI uses QHexView made by Antonio Davide.
" "Qt-less engine uses Bstrlib made by Paul Hsieh.
" "Engine uses Tiano compression code made by TianoCore developers.
" "Engine uses LZMA compression code made by Igor Pavlov.
" diff --git a/UEFITool/uefitool.pro b/UEFITool/uefitool.pro index cd890c6..44f23ce 100644 --- a/UEFITool/uefitool.pro +++ b/UEFITool/uefitool.pro @@ -62,9 +62,20 @@ HEADERS += uefitool.h \ ../common/zlib/zlib.h \ ../common/zlib/crc32.h \ ../version.h \ - qhexedit2/qhexedit.h \ - qhexedit2/chunks.h \ - qhexedit2/commands.h + qhexview5/model/buffer/qhexbuffer.h \ + qhexview5/model/buffer/qdevicebuffer.h \ + qhexview5/model/buffer/qmemorybuffer.h \ + qhexview5/model/commands/hexcommand.h \ + qhexview5/model/commands/insertcommand.h \ + qhexview5/model/commands/removecommand.h \ + qhexview5/model/commands/replacecommand.h \ + qhexview5/model/qhexcursor.h \ + qhexview5/model/qhexdelegate.h \ + qhexview5/model/qhexdocument.h \ + qhexview5/model/qhexmetadata.h \ + qhexview5/model/qhexoptions.h \ + qhexview5/model/qhexutils.h \ + qhexview5/qhexview.h SOURCES += uefitool_main.cpp \ uefitool.cpp \ @@ -127,9 +138,19 @@ SOURCES += uefitool_main.cpp \ ../common/zlib/trees.c \ ../common/zlib/uncompr.c \ ../common/zlib/zutil.c \ - qhexedit2/qhexedit.cpp \ - qhexedit2/chunks.cpp \ - qhexedit2/commands.cpp + qhexview5/model/buffer/qhexbuffer.cpp \ + qhexview5/model/buffer/qdevicebuffer.cpp \ + qhexview5/model/buffer/qmemorybuffer.cpp \ + qhexview5/model/commands/hexcommand.cpp \ + qhexview5/model/commands/insertcommand.cpp \ + qhexview5/model/commands/removecommand.cpp \ + qhexview5/model/commands/replacecommand.cpp \ + qhexview5/model/qhexcursor.cpp \ + qhexview5/model/qhexdelegate.cpp \ + qhexview5/model/qhexdocument.cpp \ + qhexview5/model/qhexmetadata.cpp \ + qhexview5/model/qhexutils.cpp \ + qhexview5/qhexview.cpp FORMS += uefitool.ui \ searchdialog.ui \