diff --git a/Hexes.pro b/Hexes.pro index cb99711..77fb89e 100644 --- a/Hexes.pro +++ b/Hexes.pro @@ -11,6 +11,8 @@ CONFIG += c++17 SOURCES += \ blockvalue.cpp \ deleteruledialog.cpp \ + hexeslogger.cpp \ + hexstream.cpp \ hexviewer.cpp \ main.cpp \ mainwindow.cpp \ @@ -22,6 +24,8 @@ SOURCES += \ HEADERS += \ blockvalue.h \ deleteruledialog.h \ + hexeslogger.h \ + hexstream.h \ hexviewer.h \ mainwindow.h \ rule.h \ diff --git a/Hexes.pro.user b/Hexes.pro.user index 7167b58..136909f 100644 --- a/Hexes.pro.user +++ b/Hexes.pro.user @@ -1,6 +1,6 @@ - + EnvironmentId @@ -78,7 +78,7 @@ Desktop Qt 6.7.2 MSVC2019 64bit Desktop Qt 6.7.2 MSVC2019 64bit qt.qt6.672.win64_msvc2019_64_kit - 0 + 1 0 0 @@ -232,7 +232,7 @@ true true true - C:/Ext/Projects/Qt/Hexes/build/Desktop_Qt_6_7_2_MSVC2019_64bit-Debug + C:/Ext/Projects/Qt/Hexes/build/Desktop_Qt_6_7_2_MSVC2019_64bit-Release 1 diff --git a/Resources.qrc b/Resources.qrc index 95b51dc..45b8f49 100644 --- a/Resources.qrc +++ b/Resources.qrc @@ -4,5 +4,8 @@ delete.png + skip.png + general.png + repeat.png diff --git a/blockvalue.cpp b/blockvalue.cpp index a114a00..2e7f938 100644 --- a/blockvalue.cpp +++ b/blockvalue.cpp @@ -1,32 +1,123 @@ #include "blockvalue.h" -BlockValue::BlockValue(QObject *parent) - : QObject{parent}, - pBlockType{BLOCK_TYPE_INT8}, - pName{""}, - pData{} { - +/* + * Block Value default constructor + */ +BlockValue::BlockValue() { + pName = ""; + pBlockType = BLOCK_TYPE_NONE; + pData = QVariant::fromValue(0); } -BlockValue::BlockValue(const BlockValue &blockVal) { - pBlockType = blockVal.BlockType(); - pName = blockVal.Name(); - pData = blockVal.Value(); +/* + * Block Value copy constructor + */ +BlockValue::BlockValue(const BlockValue &aBlockVal) { + pName = aBlockVal.Name(); + pBlockType = aBlockVal.BlockType(); + pData = aBlockVal.ToVariant(); } -BlockValue &BlockValue::operator=(const BlockValue &blockVal) { - if (this == &blockVal) { +/* + * Block Value constructor w/ name + */ +BlockValue::BlockValue(const QString aName) { + pName = aName; + pBlockType = BLOCK_TYPE_NONE; + pData = QVariant::fromValue(0); +} + +/* + * Block Value constructor w/ name & int8 + */ +BlockValue::BlockValue(const QString aName, qint8 aVal) { + pName = aName; + pBlockType = BLOCK_TYPE_INT8; + pData = QVariant::fromValue(aVal); +} + +/* + * Block Value constructor w/ name & uint8 + */ +BlockValue::BlockValue(const QString aName, quint8 aVal) { + pName = aName; + pBlockType = BLOCK_TYPE_UINT8; + pData = QVariant::fromValue(aVal); +} + +/* + * Block Value constructor w/ name & int16 + */ +BlockValue::BlockValue(const QString aName, qint16 aVal) { + pName = aName; + pBlockType = BLOCK_TYPE_INT16; + pData = QVariant::fromValue(aVal); +} + +/* + * Block Value constructor w/ name & uint16 + */ +BlockValue::BlockValue(const QString aName, quint16 aVal) { + pName = aName; + pBlockType = BLOCK_TYPE_UINT16; + pData = QVariant::fromValue(aVal); +} + +/* + * Block Value constructor w/ name & int32 + */ +BlockValue::BlockValue(const QString aName, qint32 aVal) { + pName = aName; + pBlockType = BLOCK_TYPE_INT32; + pData = QVariant::fromValue(aVal); +} + +/* + * Block Value constructor w/ name & uint32 + */ +BlockValue::BlockValue(const QString aName, quint32 aVal) { + pName = aName; + pBlockType = BLOCK_TYPE_UINT32; + pData = QVariant::fromValue(aVal); +} + +/* + * Block Value constructor w/ name & int64 + */ +BlockValue::BlockValue(const QString aName, qint64 aVal) { + pName = aName; + pBlockType = BLOCK_TYPE_INT64; + pData = QVariant::fromValue(aVal); +} + +/* + * Block Value constructor w/ name & uint64 + */ +BlockValue::BlockValue(const QString aName, quint64 aVal) { + pName = aName; + pBlockType = BLOCK_TYPE_UINT64; + pData = QVariant::fromValue(aVal); +} + +/* + * Block Value = operator implementation + */ +BlockValue &BlockValue::operator=(const BlockValue &aBlockVal) { + if (this == &aBlockVal) { return *this; } - pBlockType = blockVal.BlockType(); - pName = blockVal.Name(); - pData = blockVal.Value(); + pBlockType = aBlockVal.BlockType(); + pName = aBlockVal.Name(); + pData = aBlockVal.ToVariant(); return *this; } -int BlockValue::ValueToInt() { +/* + * Return Block Value as an integer + */ +int BlockValue::ToInt() const { switch ((int)pBlockType) { case 0: // BLOCK_TYPE_INT8 return static_cast(pData.value()); @@ -56,62 +147,44 @@ int BlockValue::ValueToInt() { return -1; } -void BlockValue::SetName(QString name) { - pName = name; +/* + * Return Block Value as a string + */ +QString BlockValue::ToString() const { + return QString::number(ToInt()); } +/* + * Set Block Value type (uint8, int16, etc) + */ +QVariant BlockValue::ToVariant() const { + return pData; +} + +/* + * Set Block Value's name + */ +void BlockValue::SetName(const QString aName) { + pName = aName; +} + +/* + * Returns Block Value's name + */ QString BlockValue::Name() const { return pName; } -void BlockValue::SetBlockType(BLOCK_TYPE blockType) { - pBlockType = blockType; +/* + * Set Block Balue type (uint8, int16, etc) + */ +void BlockValue::SetBlockType(BLOCK_TYPE aBlockType) { + pBlockType = aBlockType; } +/* + * Get Block Balue type (uint8, int16, etc) + */ BLOCK_TYPE BlockValue::BlockType() const { return pBlockType; } - -void BlockValue::SetValue(qint8 val) { - pBlockType = BLOCK_TYPE_INT8; - pData = QVariant::fromValue(val); -} - -void BlockValue::SetValue(quint8 val) { - pBlockType = BLOCK_TYPE_UINT8; - pData = QVariant::fromValue(val); -} - -void BlockValue::SetValue(qint16 val) { - pBlockType = BLOCK_TYPE_INT16; - pData = QVariant::fromValue(val); -} - -void BlockValue::SetValue(quint16 val) { - pBlockType = BLOCK_TYPE_UINT16; - pData = QVariant::fromValue(val); -} - -void BlockValue::SetValue(qint32 val) { - pBlockType = BLOCK_TYPE_INT32; - pData = QVariant::fromValue(val); -} - -void BlockValue::SetValue(quint32 val) { - pBlockType = BLOCK_TYPE_UINT32; - pData = QVariant::fromValue(val); -} - -void BlockValue::SetValue(qint64 val) { - pBlockType = BLOCK_TYPE_INT64; - pData = QVariant::fromValue(val); -} - -void BlockValue::SetValue(quint64 val) { - pBlockType = BLOCK_TYPE_UINT64; - pData = QVariant::fromValue(val); -} - -QVariant BlockValue::Value() const { - return pData; -} diff --git a/blockvalue.h b/blockvalue.h index e7b6968..933723f 100644 --- a/blockvalue.h +++ b/blockvalue.h @@ -4,6 +4,12 @@ #include #include +/* + * BLOCK_TYPE enum + * + * Value types that the BlockVlaue class can + * ingest and convert to/from. + */ enum BLOCK_TYPE { BLOCK_TYPE_NONE = 0, BLOCK_TYPE_INT8 = 1, @@ -16,34 +22,47 @@ enum BLOCK_TYPE { BLOCK_TYPE_UINT64 = 8, }; -class BlockValue : public QObject +/* + * BlockValue class + * + * Represents a variable value resulting from + * parsing a block of data from the input. + * + * Constructed preferrably with a name and value. + */ + +class BlockValue { - Q_OBJECT -public: - explicit BlockValue(QObject *parent = nullptr); - BlockValue(const BlockValue &blockVal); - BlockValue &operator=(const BlockValue &blockVal); +public: /*** PUBLIC MEMBERS ***/ + /* Block Value Constructors */ + BlockValue(); // Default + BlockValue(const BlockValue &aBlockVal); // Copy + BlockValue(const QString aName); // Name + BlockValue(const QString aName, qint8 aVal); // Name + int8 + BlockValue(const QString aName, quint8 aVal); // Name + uint8 + BlockValue(const QString aName, qint16 aVal); // Name + int16 + BlockValue(const QString aName, quint16 aVal); // Name + uint16 + BlockValue(const QString aName, qint32 aVal); // Name + int32 + BlockValue(const QString aName, quint32 aVal); // Name + uint32 + BlockValue(const QString aName, qint64 aVal); // Name + int64 + BlockValue(const QString aName, quint64 aVal); // Name + uint64 - void SetName(QString name); - QString Name() const; + /* Block Value Operators */ + BlockValue &operator=(const BlockValue &aBlockVal); // = operator - void SetBlockType(BLOCK_TYPE blockType); - BLOCK_TYPE BlockType() const; + /* Setters and Getters */ + void SetName(const QString aName); // Set Name + QString Name() const; // Get Name + void SetBlockType(BLOCK_TYPE aBlockType); // Set Block Value Type + BLOCK_TYPE BlockType() const; // Get Block Value Type - void SetValue(qint8 val); - void SetValue(quint8 val); - void SetValue(qint16 val); - void SetValue(quint16 val); - void SetValue(qint32 val); - void SetValue(quint32 val); - void SetValue(qint64 val); - void SetValue(quint64 val); + /* Data Access Methods */ + int ToInt() const; // Convert to integer + QString ToString() const; // Convert to string + QVariant ToVariant() const; // Return as raw data - QVariant Value() const; - int ValueToInt(); - -private: +private: /*** PRIVATE MEMBERS ***/ QString pName; BLOCK_TYPE pBlockType; QVariant pData; diff --git a/general.png b/general.png new file mode 100644 index 0000000..8532cf4 Binary files /dev/null and b/general.png differ diff --git a/hexeslogger.cpp b/hexeslogger.cpp new file mode 100644 index 0000000..ca524df --- /dev/null +++ b/hexeslogger.cpp @@ -0,0 +1,43 @@ +#include "hexeslogger.h" + +HexesLogger *HexesLogger::pInstance = nullptr; +QWidget *HexesLogger::pParent = nullptr; + +void HexesLogger::MakeInstance(QWidget *aParent) { + pInstance = new HexesLogger(aParent); +} + +void HexesLogger::HexesInfo(const QString pInfoMsg) { + QMessageBox::information(pParent, + HEXES_INFO_TITLE, + QString(HEXES_INFO_TEMPLATE).arg(pInfoMsg)); +} + +void HexesLogger::HexesError(int pErrorId, const QString pErrorMsg) { + QString errorIdString = QString::number(pErrorId); + QMessageBox::warning(pParent, + HEXES_ERROR_TITLE, + QString(HEXES_ERROR_TEMPLATE).arg(errorIdString, pErrorMsg)); +} + +void HexesLogger::HexesFatal(int pFatalId, const QString pFatalMsg) { + QString fatalIdString = QString::number(pFatalId); + QMessageBox::critical(pParent, + HEXES_FATAL_TITLE, + QString(HEXES_FATAL_TEMPLATE).arg(fatalIdString, pFatalMsg)); +} + +void HexesLogger::DeleteInstance() { + delete pInstance; +} + +HexesLogger::HexesLogger(QWidget *aParent) { + pParent = aParent; +} + +HexesLogger* HexesLogger::Instance() { + if (!pInstance) { + MakeInstance(); + } + return pInstance; +} diff --git a/hexeslogger.h b/hexeslogger.h new file mode 100644 index 0000000..c1a44f3 --- /dev/null +++ b/hexeslogger.h @@ -0,0 +1,32 @@ +#ifndef HEXESLOGGER_H +#define HEXESLOGGER_H + +#include +#include + +const QString HEXES_INFO_TITLE = "Hexes Info Message"; +const QString HEXES_ERROR_TITLE = "Hexes Error Message"; +const QString HEXES_FATAL_TITLE = "Hexes Fatal Message"; +const QString HEXES_INFO_TEMPLATE = "Hexes Info: %1"; +const QString HEXES_ERROR_TEMPLATE = "Hexes Error [%1]: %2"; +const QString HEXES_FATAL_TEMPLATE = "Hexes Fatal [%1]: %2"; + +class HexesLogger +{ +public: + static HexesLogger* Instance(); + static void DeleteInstance(); + static void MakeInstance(QWidget *aParent = nullptr); + + static void HexesInfo(const QString pInfoMsg); + static void HexesError(int pErrorId, const QString pErrorMsg); + static void HexesFatal(int pFatalId, const QString pFatalMsg); + +private: + HexesLogger(QWidget *aParent = nullptr); + + static QWidget *pParent; + static HexesLogger *pInstance; +}; + +#endif // HEXESLOGGER_H diff --git a/hexstream.cpp b/hexstream.cpp new file mode 100644 index 0000000..687f67b --- /dev/null +++ b/hexstream.cpp @@ -0,0 +1,142 @@ +#include "hexstream.h" + +HexStream::HexStream() + : QDataStream() { + +} + +HexStream::HexStream(QIODevice *aIODevice) + : QDataStream(aIODevice) { + +} + +HexStream::HexStream(QByteArray *aData, OpenMode aFlags) + : QDataStream(aData, aFlags) { + +} + +HexStream::HexStream(const QByteArray &aData) + : QDataStream(aData) { + +} + +HexStream::~HexStream() { + +} + +void HexStream::SetHexByteOrder(BYTE_ORDER aByteOrder) { + pByteOrder = aByteOrder; + if (aByteOrder == BYTE_ORDER_BE) { + setByteOrder(BigEndian); + } else { + setByteOrder(BigEndian); + } +} + +BYTE_ORDER HexStream::HexByteOrder() { + return pByteOrder; +} + +void HexStream::ParseSkip(const Rule &aRule) { + // Skip hex bytes if valid + skipRawData(aRule.Length()); +} + +void HexStream::ParseSkip(int aSkipLen) { + // Skip hex 'aSkipLen' bytes + skipRawData(aSkipLen); +} + +BlockValue HexStream::ParseInt8(const Rule &aRule) { + qint8 val; + operator >>(val); + return BlockValue(aRule.Name(), val); +} + +BlockValue HexStream::ParseUInt8(const Rule &aRule) { + quint8 val; + operator >>(val); + return BlockValue(aRule.Name(), val); +} + +BlockValue HexStream::ParseInt16(const Rule &aRule) { + qint16 val; + operator >>(val); + return BlockValue(aRule.Name(), val); +} + +BlockValue HexStream::ParseUInt16(const Rule &aRule) { + quint16 val; + operator >>(val); + return BlockValue(aRule.Name(), val); +} + +BlockValue HexStream::ParseInt32(const Rule &aRule) { + qint32 val; + operator >>(val); + return BlockValue(aRule.Name(), val); +} + +BlockValue HexStream::ParseUInt32(const Rule &aRule) { + quint32 val; + operator >>(val); + return BlockValue(aRule.Name(), val); +} + +BlockValue HexStream::ParseInt64(const Rule &aRule) { + qint64 val; + operator >>(val); + return BlockValue(aRule.Name(), val); +} + +BlockValue HexStream::ParseUInt64(const Rule &aRule) { + quint64 val; + operator >>(val); + return BlockValue(aRule.Name(), val); +} + +BlockValue HexStream::ParseRule(const Rule &aRule) { + if (!aRule) { + HexesLogger::HexesError(50, "Attempted to parse null rule!"); + return BlockValue("INVALID"); + } + if (aRule.Name().isEmpty()) { + HexesLogger::HexesError(55, "Attempted to parse rule w/ null name!"); + return BlockValue("INVALID"); + } + if (aRule.Length() < 0) { + HexesLogger::HexesError(60, "Attempted to rule w/ invalid length!"); + return BlockValue(aRule.Name()); + } + + switch ((int)aRule.Type()) { + case 1: // Skip + ParseSkip(aRule); + break; + case 2: // int8 + return ParseInt8(aRule); + break; + case 3: // uint8 + return ParseUInt8(aRule); + break; + case 4: // int16 + return ParseInt16(aRule); + break; + case 5: // uint16 + return ParseUInt16(aRule); + break; + case 6: // int32 + return ParseInt32(aRule); + break; + case 7: // uint32 + return ParseUInt32(aRule); + break; + case 8: // int64 + return ParseInt64(aRule); + break; + case 9: // uint64 + return ParseUInt64(aRule); + break; + } + return BlockValue(aRule.Name()); +} diff --git a/hexstream.h b/hexstream.h new file mode 100644 index 0000000..3d6ad28 --- /dev/null +++ b/hexstream.h @@ -0,0 +1,38 @@ +#ifndef HEXSTREAM_H +#define HEXSTREAM_H + +#include "rule.h" +#include "blockvalue.h" +#include "hexeslogger.h" + +#include + +class HexStream : public QDataStream +{ +public: + HexStream(); + explicit HexStream(QIODevice *aIODevice); + HexStream(QByteArray *aData, OpenMode aFlags); + HexStream(const QByteArray &aData); + ~HexStream(); + + void SetHexByteOrder(BYTE_ORDER aByteOrder); + BYTE_ORDER HexByteOrder(); + + void ParseSkip(const Rule &aRule); + void ParseSkip(int aSkipLen); + BlockValue ParseInt8(const Rule &aRule); + BlockValue ParseUInt8(const Rule &aRule); + BlockValue ParseInt16(const Rule &aRule); + BlockValue ParseUInt16(const Rule &aRule); + BlockValue ParseInt32(const Rule &aRule); + BlockValue ParseUInt32(const Rule &aRule); + BlockValue ParseInt64(const Rule &aRule); + BlockValue ParseUInt64(const Rule &aRule); + BlockValue ParseRule(const Rule &aRule); + +private: + BYTE_ORDER pByteOrder; +}; + +#endif // HEXSTREAM_H diff --git a/hexviewer.cpp b/hexviewer.cpp index 194a2ff..0f895f9 100644 --- a/hexviewer.cpp +++ b/hexviewer.cpp @@ -1,147 +1,217 @@ #include "hexviewer.h" +/* + * HexViewer Constructor + */ HexViewer::HexViewer(QWidget *parent) - : QAbstractScrollArea(parent) { - pText = ""; - pHex = ""; - pFileName = ""; - pViewRect = QRectF(0, 0, 800, 557); - pBlinkTimer = new QTimer(this); - pRules = QQueue(); - pScrollValue = 0; - pVars = QMap(); - + : QAbstractScrollArea(parent) + , pText("") + , pHex("") + , pFileName("") + , pViewRect(0, 0, 800, 557) + , pBlinkTimer(new QTimer(this)) + , pRules(QQueue()) + , pScrollValue(0) + , pVars(QMap()) + , pFontMetrics(QFontMetrics(QFont("CommitMono", 10))) + , pFileDir("C:/"){ + // Set font to monospaced option setFont(QFont("CommitMono", 10)); - - qRegisterMetaType("Rule"); - qRegisterMetaType>("QQueue"); - //qRegisterMetaTypeStreamOperators>("QQueue"); - - verticalScrollBar()->setSingleStep(45); - verticalScrollBar()->setPageStep(45); - connect(verticalScrollBar(), &QScrollBar::valueChanged, this, [this](int value) { - pScrollValue = value; - pSeekFile(); - }); - - connect(pBlinkTimer, &QTimer::timeout, this, [this]() { - pCursorVisible = !pCursorVisible; - update(); // Trigger update to show/hide cursor - }); - pBlinkTimer->start(500); // Blinking interval + // Initialize scroll bar and cursor timer + pInitScrollBar(); + pInitCursorTimer(); } +/* + * HexViewer Destructor + */ HexViewer::~HexViewer() { delete pBlinkTimer; } -void HexViewer::SetFileName(const QString fileName) { - pFileName = fileName; +/* + * Scroll Bar Initialization + */ +void HexViewer::pInitScrollBar() { + verticalScrollBar()->setSingleStep(DEFAULT_SINGLE_STEP); + verticalScrollBar()->setPageStep(DEFAULT_PAGE_STEP); + connect(verticalScrollBar(), &QScrollBar::valueChanged, + this, &HexViewer::pScrollValueChanged); +} - pFile.setFileName(pFileName); +/* + * Cursor Blink Timer Initialization + */ +void HexViewer::pInitCursorTimer() { + connect(pBlinkTimer, &QTimer::timeout, + this, &HexViewer::pBlinkCursor); + pBlinkTimer->start(500); // Blinking interval +} + +/* + * Set filename of hex file we want to open + */ +void HexViewer::SetFileName(const QString fileName) { + if (fileName.isEmpty()) { return; } + // Set file name & open for read-only + pFile.setFileName(pFileName = fileName); if (!pFile.open(QIODevice::ReadOnly)) { - qDebug() << "Failed to open " << pFileName; - } else { - qDebug() << "Opened " << pFileName; + const QString errorMsg("Failed to open " + pFileName); + HexesLogger::HexesError(20, errorMsg); + return; } + // Re-search the file contents to display pSeekFile(); } +/* + * Add a rule to our local queue + */ void HexViewer::AddRule(const Rule rule) { pRules.enqueue(rule); - - emit RuleNamesChanged(RuleNames()); - RunRules(); + // Refresh rules and view + pRefreshRules(); } +/* + * Adds multiple rules to our local queue + */ void HexViewer::AddRules(const QVector rules) { + if (rules.isEmpty()) { return; } + // Add rules from incoming queue for (int i = 0; i < rules.size(); i++) { pRules.enqueue(rules[i]); } - - emit RuleNamesChanged(RuleNames()); - RunRules(); + // Refresh rules and view + pRefreshRules(); } +/* + * Remove a rule from our local queue + */ void HexViewer::DeleteRule(const Rule rule) { if (!pRules.contains(rule)) { return; } if (pRules.size() == 0) { return; } - + // Search for and remove rule for (int i = 0; i < pRules.size(); i++) { Rule currentRule = pRules.dequeue(); - if (currentRule != rule) { pRules.enqueue(currentRule); } } - - emit RuleNamesChanged(RuleNames()); - update(); + // Refresh rules and view + pRefreshRules(); } +/* + * Remove a rule, by name, from our local queue + */ void HexViewer::DeleteRuleByName(const QString ruleName) { if (pRules.size() == 0) { return; } - + // Search for and remove rule for (int i = 0; i < pRules.size(); i++) { Rule currentRule = pRules.dequeue(); - if (currentRule.Name() != ruleName) { pRules.enqueue(currentRule); } } - - emit RuleNamesChanged(RuleNames()); - update(); + // Refresh rules and view + pRefreshRules(); } +/* + * Pop/dequeue a rule from our local queue + */ void HexViewer::PopRule() { if (pRules.isEmpty()) { return; } - pRules.dequeue(); - emit RuleNamesChanged(RuleNames()); - update(); + // Refresh rules and view + pRefreshRules(); } +/* + * Clear all rules from our local queue + */ void HexViewer::ClearRules() { if (pRules.isEmpty()) { return; } - pRules.clear(); - emit RuleNamesChanged(RuleNames()); - update(); + // Refresh rules and view + pRefreshRules(); } +/* + * Save rules to a Hexes Rule File (.hrf) + */ void HexViewer::SaveRules() { + // Have user choose where to save the .hrf QString filePath = QFileDialog::getSaveFileName(this, "Save Hexes Rule File", - "C:/", "Rule File (*.hrf);;All Files(*.*)"); + pFileDir, "Rule File (*.hrf);;All Files(*.*)"); + QString tempFilePath = filePath; + pFileDir = tempFilePath.replace(tempFilePath.split('/').last(), ""); + emit BaseDirChanged(pFileDir); + + // Open rule file, or report error, then close QFile ruleFile(filePath); if (!ruleFile.open(QIODevice::WriteOnly)) { - qDebug() << "Failed to save rule file!"; + HexesLogger::HexesError(25, "Failed to save rule file!"); return; } ruleFile.close(); - + // Write to rule file in ini format QSettings ruleExport(filePath, QSettings::IniFormat); ruleExport.setValue("rules", QVariant::fromValue(pRules)); } +/* + * Open a Hexes Rule File (.hrf) + */ void HexViewer::OpenRules() { + // Have user choose where to save the .hrf QString filePath = QFileDialog::getOpenFileName(this, "Open Hexes Rule File", - "C:/", "Rule File (*.hrf);;All Files(*.*)"); + pFileDir, "Rule File (*.hrf);;All Files(*.*)"); + QString tempFilePath = filePath; + pFileDir = tempFilePath.replace(tempFilePath.split('/').last(), ""); + emit BaseDirChanged(pFileDir); + + // Open rule file, or report error, then close QFile ruleFile(filePath); if (!ruleFile.open(QIODevice::ReadOnly)) { - qDebug() << "Failed to save rule file!"; + HexesLogger::HexesError(30, "Failed to open rule file!"); return; } ruleFile.close(); - + // Read from rule file in ini format QSettings ruleExport(filePath, QSettings::IniFormat); pRules = ruleExport.value("rules").value>(); - emit RuleNamesChanged(RuleNames()); - - RunRules(); + // Notify & refresh rules and view + pRefreshRules(); + emit FileOpened(filePath.split('/').last()); } +/* + * Open a binary input file + */ +void HexViewer::OpenFile() { + // Have user choose where to save the .hrf + QString filePath = QFileDialog::getOpenFileName(this, "Open Input File", + pFileDir, "All Files(*.*)"); + + QString tempFilePath = filePath; + pFileDir = tempFilePath.replace(tempFilePath.split('/').last(), ""); + emit BaseDirChanged(pFileDir); + + SetFileName(filePath); + // Notify & refresh rules and view + pRefreshRules(); + emit FileOpened(filePath); +} + +/* + * Get a list of rule names from local queue + */ QStringList HexViewer::RuleNames() { + if (pRules.isEmpty()) { return QStringList(); } + // Create and return name list QStringList ruleNames; foreach (Rule rule, pRules) { ruleNames.append(rule.Name()); @@ -149,162 +219,190 @@ QStringList HexViewer::RuleNames() { return ruleNames; } -void HexViewer::RunRules() { - QDataStream inStream(QByteArray::fromHex(pHex.toUtf8())); - +/* + * Re-parse rules and update the variables' values + */ +void HexViewer::ParseRules() { + // Read in hex data as a byte-stream + HexStream hexStream(QByteArray::fromHex(pHex.toUtf8())); + // Iterate through local rules for (int i = 0; i < pRules.size(); i++) { - Rule rule = pRules[i]; + Rule &rule = pRules[i]; // Current rule + qDebug() << rule.Name(); + if ((rule.Type() == TYPE_NONE) || (rule.Type() == TYPE_SKIP)) { + hexStream.ParseSkip(rule.Length() + rule.SkipOffset()); + continue; + } + // Repeat rule per defined repeat count + for (int j = 0; j <= rule.RepeatCount() + rule.RepeatOffset(); j++) { + // Set byte order and execute pre-skip + hexStream.SetHexByteOrder(rule.ByteOrder()); + hexStream.ParseSkip(rule.PreSkipCount() + rule.PreSkipOffset()); - for (int j = 0; j <= rule.RepeatCount(); j++) { - if (rule.ByteOrder() == BYTE_ORDER_BE) { - inStream.setByteOrder(QDataStream::BigEndian); - } else { - inStream.setByteOrder(QDataStream::LittleEndian); - } + // Create parse block to get value for variable + BlockValue val = hexStream.ParseRule(rule); + rule.SetValue(val.ToString()); - QString ruleName = rule.Name().toUpper(); + // Execute post-skip + hexStream.ParseSkip(rule.PostSkipCount() + rule.PostSkipOffset()); + + // Add a var suffix (index) if repeating if (rule.RepeatCount() > 0) { - ruleName += QString(" %1").arg(j); + pVars[rule.Name() + QString(" %1").arg(j)] = val; + } else { + pVars[rule.Name()] = val; } - - BlockValue val; - val.SetName(ruleName); - - if (rule.PreSkipCount() > 0) { - inStream.skipRawData(rule.PreSkipCount()); - } - - switch ((int)rule.Type()) { - case 1: // Skip - inStream.skipRawData(rule.Length()); - break; - case 2: // int8 - qint8 val_8; - inStream >> val_8; - rule.SetValue(QString::number(val_8)); - val.SetValue(val_8); - break; - case 3: // uint8 - quint8 val_u8; - inStream >> val_u8; - rule.SetValue(QString::number(val_u8)); - val.SetValue(val_u8); - break; - case 4: // int16 - qint16 val_16; - inStream >> val_16; - rule.SetValue(QString::number(val_16)); - val.SetValue(val_16); - break; - case 5: // uint16 - quint16 val_u16; - inStream >> val_u16; - rule.SetValue(QString::number(val_u16)); - val.SetValue(val_u16); - break; - case 6: // int32 - qint32 val_32; - inStream >> val_32; - rule.SetValue(QString::number(val_32)); - val.SetValue(val_32); - break; - case 7: // uint32 - quint32 val_u32; - inStream >> val_u32; - rule.SetValue(QString::number(val_u32)); - val.SetValue(val_u32); - break; - case 8: // int64 - qint64 val_64; - inStream >> val_64; - rule.SetValue(QString::number(val_64)); - val.SetValue(val_64); - break; - case 9: // uint64 - quint64 val_u64; - inStream >> val_u64; - rule.SetValue(QString::number(val_u64)); - val.SetValue(val_u64); - break; - } - if (rule.PostSkipCount() > 0) { - inStream.skipRawData(rule.PostSkipCount()); - } - - if ((rule.Type() != TYPE_NONE) - && (rule.Type() != TYPE_SKIP)) { - pVars[ruleName] = val; - emit VarsChanged(pVars); - } - pRules[i] = rule; } } + emit VarsChanged(pVars); update(); } +/* + * Return the local variable map + * - Note: May not be populated + */ QMap HexViewer::GetVars() { return pVars; } +void HexViewer::SetBaseDir(QString dir) { + pFileDir = dir; +} + +/* + * Get the text representation of the data + */ QString HexViewer::Text() { return pText; } +/* + * Get the hex representation of the data + */ QString HexViewer::Hex() { return pHex; } -QRectF HexViewer::pCalcOffsetColumnRect() { - return QRectF(5, 30, 80, height() - 35); +QString HexViewer::pFormatValuesGrid(const QVector &values, int width, const QFont &font, int bitSize, int& textHeight) { + if (values.isEmpty()) return ""; + + QFontMetrics fm(font); + QString result; + + // Determine the width for the index based on the number of digits in the largest index + int maxIndexDigits = QString::number(values.size()).length(); + + // Determine padding width for values based on bit size (minimal necessary width) + int valuePaddingWidth = 0; + switch (bitSize) { + case 8: valuePaddingWidth = 3; break; // int8 max is 255 + case 16: valuePaddingWidth = 5; break; // int16 max is 65535 + case 32: valuePaddingWidth = 10; break; // int32 max is 4294967295 + case 64: valuePaddingWidth = 20; break; // int64 max is very large + default: valuePaddingWidth = 10; break; // Default to int32-like width + } + + // Calculate a compact maximum entry width (index + value) with minimal padding + int maxEntryWidth = fm.horizontalAdvance(QString("%1=%2 ") + .arg(values.size(), maxIndexDigits) + .arg(0, valuePaddingWidth, 10, QChar('0'))); + + // Determine the optimal number of entries per line based on available width + int entriesPerLine = std::max(1, width / maxEntryWidth); + + // Build the grid-like output + int lineCount = 0; + for (int i = 0; i < values.size(); ++i) { + if (i > 0 && i % entriesPerLine == 0) { + result += "\n"; + lineCount++; + } else if (i > 0) { + result += ", "; // Minimal spacing between entries + } + + // Format each entry with a left-aligned index and right-aligned value + result += QString("%1=%2") + .arg(i + 1, maxIndexDigits, 10, QChar(' ')) // Left-pad index minimally + .arg(values[i], -valuePaddingWidth, 10, QChar(' ')); // Right-pad value minimally + } + + // Account for the final line + lineCount++; + + // Calculate the height of the text block + textHeight = lineCount * fm.height(); + + return result; } +/* + * Calculate the hex header rectangle + */ QRectF HexViewer::pCalcHexHeaderRect() const { - QRectF paintRect = pCalcPaintRect(); + qreal columnWidth = viewport()->width() + - (OFFSET_HEADER_WIDTH + (3 * MAX_PADDING)); + qreal hexColumnWidth = (columnWidth / 5 * 3) - 2 * MAX_PADDING; - qreal columnWidth = paintRect.right() - 125; - qreal hexColumnWidth = (columnWidth / 5 * 3) - 15; - - return QRectF(95, 5, hexColumnWidth - 10, 15); + return QRectF(OFFSET_HEADER_WIDTH + MAX_PADDING, MIN_PADDING, + hexColumnWidth, HEADER_HEIGHT); } -QRectF HexViewer::pCalcTextHeaderRect() { - QRectF paintRect = pCalcPaintRect(); +/* + * Calculate the text header rectangle + */ +QRectF HexViewer::pCalcTextHeaderRect() const { + qreal columnWidth = viewport()->width() + - (OFFSET_HEADER_WIDTH + (3 * MAX_PADDING)); + qreal hexColumnWidth = (columnWidth / 5 * 3) - MAX_PADDING; + qreal textColumnWidth = (columnWidth / 5) + MAX_PADDING; - qreal columnWidth = paintRect.right() - 125; - qreal hexColumnWidth = (columnWidth / 5 * 3) - 15; - qreal textColumnWidth = (columnWidth / 5) + 15; - - return QRectF(95 + hexColumnWidth, 5, textColumnWidth - 10, 15); + return QRectF(OFFSET_HEADER_WIDTH + hexColumnWidth + MAX_PADDING, MIN_PADDING, + textColumnWidth - 2 * MAX_PADDING, MAX_PADDING); } -QRectF HexViewer::pCalcRuleHeaderRect() { - QRectF paintRect = pCalcPaintRect(); +/* + * Calculate the rule header rectangle + */ +QRectF HexViewer::pCalcRuleHeaderRect() const { + qreal columnWidth = viewport()->width() + - (OFFSET_HEADER_WIDTH + (3 * MAX_PADDING)); + qreal hexColumnWidth = (columnWidth / 5 * 3); + qreal textColumnWidth = (columnWidth / 5) + MAX_PADDING; + qreal ruleColumnWidth = (columnWidth / 5) + MAX_PADDING; - qreal columnWidth = paintRect.right() - 125; - - qreal hexColumnWidth = (columnWidth / 5 * 3) - 15; - qreal textColumnWidth = (columnWidth / 5) + 15; - qreal ruleColumnWidth = (columnWidth / 5) + 15; - - return QRectF(95 + hexColumnWidth + textColumnWidth, 5, ruleColumnWidth - 10, 15); + return QRectF(OFFSET_HEADER_WIDTH + hexColumnWidth + textColumnWidth, MIN_PADDING, + ruleColumnWidth - MAX_PADDING, MAX_PADDING); } -QRectF HexViewer::pCalcOffsetHeaderRect() { - return QRectF(5, 5, 80, 15); +/* + * Calculate the offset header rectangle + */ +QRectF HexViewer::pCalcOffsetHeaderRect() const { + return QRectF(MIN_PADDING, MIN_PADDING, + OFFSET_HEADER_WIDTH, HEADER_HEIGHT); } +/* + * Calculate the offset column rectangle + */ +QRectF HexViewer::pCalcOffsetColumnRect() const { + return QRectF(MIN_PADDING, FONT_HEIGHT + HEADER_HEIGHT, + OFFSET_HEADER_WIDTH, height() - STATUS_BAR_HEIGHT); +} + +/* + * Paint the offset column + */ void HexViewer::pPaintOffsetColumn(QPainter &painter) { QRectF offSetColumnRect = pCalcOffsetColumnRect(); QRectF hexRect = pCalcHexRect(); - int charWidth = fontMetrics().horizontalAdvance('F'); - int hexCharsPerLine = qCeil(hexRect.width() / charWidth) / 3; - QString offset = ""; int nextOffset = verticalScrollBar()->value(); for (int i = 0; i < qCeil(hexRect.height() / fontMetrics().height()); i++) { offset += QString::number(nextOffset).rightJustified(8, '0') + " "; - nextOffset += hexCharsPerLine; + nextOffset += pCalcCharCount(); } offset = offset.trimmed(); @@ -312,16 +410,21 @@ void HexViewer::pPaintOffsetColumn(QPainter &painter) { painter.drawText(offSetColumnRect, Qt::TextWordWrap | Qt::AlignHCenter, offset); } +/* + * Calculate the hex content rectangle + */ QRectF HexViewer::pCalcHexRect() const { - QRectF paintRect = pCalcPaintRect(); + qreal columnWidth = viewport()->width() + - (OFFSET_HEADER_WIDTH + (3 * MAX_PADDING)); + qreal hexColumnWidth = (columnWidth / 5 * 3) - 2 * MAX_PADDING; - qreal columnWidth = paintRect.right() - 125; - qreal hexColumnWidth = (columnWidth / 5 * 3) - 15; - qreal scrollHeight = paintRect.height(); - - return QRectF(95, 30, hexColumnWidth - 10, scrollHeight - 35); + return QRectF(OFFSET_HEADER_WIDTH + MAX_PADDING, HEADER_HEIGHT + FONT_HEIGHT, + hexColumnWidth, viewport()->height()); } +/* + * Paint the hex content + */ void HexViewer::pPaintHex(QPainter &painter) { QRectF hexRect = pCalcHexRect(); @@ -329,42 +432,36 @@ void HexViewer::pPaintHex(QPainter &painter) { painter.drawText(hexRect, Qt::TextWordWrap, pHex); } +/* + * Calculate the text content rect + */ QRectF HexViewer::pCalcTextRect() const { - QRectF paintRect = pCalcPaintRect(); - qreal columnWidth = paintRect.right() - 125; - qreal hexColumnWidth = 80 + (columnWidth / 5 * 3); - qreal textColumnWidth = (columnWidth / 5); - qreal scrollHeight = paintRect.height(); - int charWidth = fontMetrics().horizontalAdvance("F"); + qreal columnWidth = viewport()->width() + - (OFFSET_HEADER_WIDTH + (3 * MAX_PADDING)); + qreal hexColumnWidth = (columnWidth / 5 * 3); + qreal textColumnWidth = (columnWidth / 5) - MAX_PADDING / 2; - return QRectF(hexColumnWidth, 30, textColumnWidth - charWidth, scrollHeight - 35); + return QRectF(OFFSET_HEADER_WIDTH + hexColumnWidth, HEADER_HEIGHT + FONT_HEIGHT, + textColumnWidth, viewport()->height()); } -QRectF HexViewer::pCalcRuleRect() { - QRectF paintRect = pCalcPaintRect(); +/* + * Calculate rule sidebar rectangle + */ +QRectF HexViewer::pCalcRuleRect() const { + qreal columnWidth = viewport()->width() + - (OFFSET_HEADER_WIDTH + (3 * MAX_PADDING)); + qreal hexColumnWidth = (columnWidth / 5 * 3); + qreal textColumnWidth = (columnWidth / 5) + MAX_PADDING; + qreal ruleColumnWidth = (columnWidth / 5) + MAX_PADDING; - qreal columnWidth = paintRect.right() - 125; - - qreal hexColumnWidth = (columnWidth / 5 * 3) - 15; - qreal textColumnWidth = (columnWidth / 5) + 15; - qreal rulesColumnWidth = (columnWidth / 5) + 15; - qreal scrollHeight = pCalcContentHeight(); - - return QRectF(95 + hexColumnWidth + textColumnWidth, 30, rulesColumnWidth - 10, scrollHeight - 35); + return QRectF(OFFSET_HEADER_WIDTH + hexColumnWidth + textColumnWidth, HEADER_HEIGHT + FONT_HEIGHT, + ruleColumnWidth, viewport()->height()); } -QString HexViewer::pInsertNewlinesAtIntervals(const QString &input, int interval) { - QString result = input; - - for (int i = interval; i < result.length(); i += interval) { - result.insert(i, '\n'); - i++; - } - - return result; -} - - +/* + * Paint the text content + */ void HexViewer::pPaintText(QPainter &painter) { QRectF textRect = pCalcTextRect(); @@ -372,6 +469,9 @@ void HexViewer::pPaintText(QPainter &painter) { painter.drawText(textRect, Qt::TextWrapAnywhere, pText); } +/* + * Paint a hex rule over the content + */ void HexViewer::pPaintHexRule(QPainter &painter, int ¤tXPos, int ¤tYPos, int ruleLen, bool skip) { int charWidth = fontMetrics().horizontalAdvance("F"); @@ -386,8 +486,8 @@ void HexViewer::pPaintHexRule(QPainter &painter, int ¤tXPos, int rectWidth = charsToDraw * 3 * charWidth; // Only draw if within the visible area vertically - if (adjustedYPos + fontMetrics().height() >= hexRect.top() && - adjustedYPos <= hexRect.bottom()) { + if (adjustedYPos >= hexRect.top() && + adjustedYPos <= (hexRect.bottom() - fontMetrics().height())) { QRectF skipRect(QPointF(currentXPos, adjustedYPos + 1), QSizeF(rectWidth - charWidth, fontMetrics().height() - 2)); @@ -412,6 +512,9 @@ void HexViewer::pPaintHexRule(QPainter &painter, int ¤tXPos, currentYPos = adjustedYPos + scrollHeight; } +/* + * Paint a text rule over the content + */ void HexViewer::pPaintTextRule(QPainter &painter, int ¤tXPos, int ¤tYPos, int ruleLen, bool skip) { int charWidth = fontMetrics().horizontalAdvance("F"); @@ -438,8 +541,8 @@ void HexViewer::pPaintTextRule(QPainter &painter, int ¤tXPos, int rectWidth = charsToDraw * charWidth; // Only draw if within the visible area vertically - if (adjustedYPos + fontMetrics().height() >= textRect.top() && - adjustedYPos <= textRect.bottom()) { + if (adjustedYPos >= textRect.top() && + adjustedYPos <= textRect.bottom()) { QRectF skipRect(QPointF(currentXPos, adjustedYPos + 1), QSizeF(rectWidth, fontMetrics().height() - 2)); @@ -464,11 +567,12 @@ void HexViewer::pPaintTextRule(QPainter &painter, int ¤tXPos, currentYPos = adjustedYPos + scrollHeight; } - - +/* + * Seek to a new file location, process + * the contents, and update the view. + */ void HexViewer::pSeekFile() { QRectF hexRect(pCalcHexRect()); - qDebug() << "hexRect width" << hexRect.width(); int dataLength = (hexRect.height() / fontMetrics().height()) * pCalcCharCount(); if (dataLength < 0) { @@ -479,15 +583,34 @@ void HexViewer::pSeekFile() { pHex = pCleanHex(data.toHex()); pText = QString::fromLocal8Bit(data).replace('\0', '.');//, pCalcCharCount()); } else { - qDebug() << QString("File '%1' not open...").arg(pFile.fileName()); - return; + //HexesLogger::HexesError(35, QString("File '%1' not open...").arg(pFile.fileName())); + //return; } pUpdateScrollBar(); update(); } +/* + * void pRefreshRules() + * + * Notify rules have changed and update view + */ +void HexViewer::pRefreshRules() { + // Notify current rules have changed + emit RuleNamesChanged(RuleNames()); + // Parse new vars and values + ParseRules(); +} + +/* + * void pPaintRules(QPainter &painter) + * + * Paint the hex rules to the sidebar + */ void HexViewer::pPaintRules(QPainter &painter) { int ruleIndex = 1; + bool isSkip = false; + qreal fontHeight = QFontMetrics(painter.font()).height(); QRectF rulesRect = pCalcRuleRect(); painter.save(); @@ -501,96 +624,85 @@ void HexViewer::pPaintRules(QPainter &painter) { int textXPos = textRect.left(); int textYPos = textRect.top(); - QRectF clipRect(0, rulesRect.top(), width(), rulesRect.height()); - painter.setClipRect(clipRect); + int currY = 0; - QQueue rules = QQueue(pRules); + foreach (Rule rule, pRules) { + QString typeText = ""; + QString valueText = ""; + int preSkipCount = rule.PreSkipCount(); + int postSkipCount = rule.PostSkipCount(); + QVector repeatVals = QVector(); - foreach (Rule rule, rules) { - QString ruleName = rule.Name().toUpper(); - if (rule.RepeatCount() > 0) { - ruleName += " %1"; - } + QString ruleName = rule.Name(); + QColor ruleBackground(rule.Color()); + ruleBackground.setAlpha(75); + painter.setPen(Qt::black); + painter.setBrush(ruleBackground); + + valueText.clear(); for (int i = 0; i <= rule.RepeatCount(); i++) { - QString formattedName = ruleName; - int preSkipCount = rule.PreSkipCount(); - int postSkipCount = rule.PostSkipCount(); - + QString formattedName = rule.Name(); if (rule.RepeatCount() > 0) { - formattedName = formattedName.arg(i + 1); + formattedName = QString(rule.Name() + " %1").arg(i + 1); } - - painter.setPen(QPen(rule.Color(), 1)); - - QString typeText = ""; - QString valueText = ""; - - QColor ruleBackground(rule.Color()); - ruleBackground.setAlpha(75); - painter.setBrush(ruleBackground); - - // Draw the rule information in the rule stack on the right side - QRectF ruleBackgroundRect(rulesRect.left(), -verticalScrollBar()->value() + rulesRect.top() + 1 + (QFontMetrics(painter.font()).height() * 3) * (ruleIndex - 1), - rulesRect.width(), QFontMetrics(painter.font()).height() * 3); - QRectF ruleRect(rulesRect.left() + 1, -verticalScrollBar()->value() + rulesRect.top() + 2 + (QFontMetrics(painter.font()).height() * 3) * (ruleIndex - 1), - rulesRect.width() - 2, QFontMetrics(painter.font()).height() * 3); - painter.drawRect(ruleBackgroundRect); - + // Paint pre-skip if (preSkipCount > 0) { - //typeText = QString("Skip %1 byte(s)").arg(rule.Length()); pPaintHexRule(painter, hexXPos, hexYPos, preSkipCount, true); pPaintTextRule(painter, textXPos, textYPos, preSkipCount, true); - //isSkip = true; } - if (postSkipCount > 0) { - //typeText = QString("Skip %1 byte(s)").arg(rule.Length()); - pPaintHexRule(painter, hexXPos, hexYPos, postSkipCount, true); - pPaintTextRule(painter, textXPos, textYPos, postSkipCount, true); - //isSkip = true; - } - - bool isSkip = false; - switch ((int)rule.Type()) { - case 1: // TYPE_SKIP - typeText = QString("Skip %1 byte(s)").arg(rule.Length()); + // Paint skip or int + if (rule.Type() == TYPE_SKIP) { pPaintHexRule(painter, hexXPos, hexYPos, rule.Length(), true); pPaintTextRule(painter, textXPos, textYPos, rule.Length(), true); isSkip = true; - break; - - case 2: // TYPE_INT_8 - case 3: // TYPE_UINT_8 - case 4: // TYPE_INT_16 - case 5: // TYPE_UINT_16 - case 6: // TYPE_INT_32 - case 7: // TYPE_UINT_32 - case 8: // TYPE_INT_64 - case 9: // TYPE_UINT_64 - typeText = QString("%1-BIT %2 INTEGER").arg(rule.Length() * 8).arg(rule.Type() % 2 == 0 ? "" : "US"); - valueText = pVars[formattedName].Value().toString(); + } else { + typeText = QString("%1-BIT %2 INTEGER").arg(rule.Length() * 8) + .arg(rule.Type() % 2 == 0 ? "" : "US"); + valueText += pVars[formattedName].ToString() + "\n"; + if (rule.RepeatCount() > 0) { + repeatVals.push_back(pVars[formattedName].ToInt()); + } pPaintHexRule(painter, hexXPos, hexYPos, rule.Length()); pPaintTextRule(painter, textXPos, textYPos, rule.Length()); - break; - - default: - typeText = "UH-OH"; - break; } - - painter.setPen(Qt::black); - QString paddedIndex = QString::number(ruleIndex).rightJustified(2, '0'); - QString ruleText = QString("%1. %2\n%3\n%4\n").arg(paddedIndex, formattedName, - typeText, valueText); - painter.drawText(ruleRect, Qt::TextWrapAnywhere, ruleText); - - QString ruleByteOrder = QString(rule.ByteOrder() == BYTE_ORDER_BE ? "BE" : "LE") + "\n\n"; - if (!isSkip) { - painter.drawText(ruleRect, Qt::AlignRight, ruleByteOrder); + // Paint post-skip + if (postSkipCount > 0) { + pPaintHexRule(painter, hexXPos, hexYPos, postSkipCount, true); + pPaintTextRule(painter, textXPos, textYPos, postSkipCount, true); } - - ruleIndex++; } + + if (!isSkip) { + int blockHeight; + if (repeatVals.size() > 0) { + valueText = pFormatValuesGrid(repeatVals, rulesRect.width(), painter.font(), rule.Length() * 8, blockHeight); + } + + qreal ruleHeight; + if (rule.RepeatCount() == 0) { + ruleHeight = fontHeight * 2; + } else { + ruleHeight = fontHeight * 2 + blockHeight; + } + + QRectF ruleRect(rulesRect.left() + 1, -verticalScrollBar()->value() + rulesRect.top() + 2 + currY, + rulesRect.width() - 2, ruleHeight); + QString paddedIndex = QString::number(ruleIndex).rightJustified(2, '0'); + QString ruleText = QString("%1. %2\n%3\n%4\n").arg(paddedIndex, ruleName, + typeText, valueText); + QString ruleByteOrder = QString(rule.ByteOrder() == BYTE_ORDER_BE ? "BE" : "LE") + "\n\n"; + painter.drawText(ruleRect, Qt::TextWrapAnywhere, ruleText); + painter.drawText(ruleRect, Qt::AlignRight, ruleByteOrder); + + QRectF ruleBackgroundRect(rulesRect.left(), -verticalScrollBar()->value() + rulesRect.top() + 1 + currY, + rulesRect.width(), ruleHeight); + + painter.drawRect(ruleBackgroundRect); + ruleIndex++; + currY += ruleHeight; + } + isSkip = false; } painter.restore(); } @@ -628,7 +740,7 @@ void HexViewer::pPaintHeaders(QPainter &painter) { void HexViewer::paintEvent(QPaintEvent *event) { QPainter painter(viewport()); if (!painter.isActive()) { - qWarning() << "QPainter is not active"; + HexesLogger::HexesError(45, "QPainter is not active!"); return; } if (event->rect().isEmpty()) { return; } @@ -640,24 +752,22 @@ void HexViewer::paintEvent(QPaintEvent *event) { pPaintText(painter); pPaintRules(painter); - if (pCursorVisible) { - QPoint cursorPos = pCalcCursorPosition(); + // if (pCursorVisible) { + // QPoint cursorPos = pCalcCursorPosition(); - QFontMetrics fm(font()); - int cursorHeight = fm.height(); + // int cursorHeight = pFontMetrics.height(); - painter.drawLine(cursorPos.x(), cursorPos.y(), cursorPos.x(), cursorPos.y() + cursorHeight); - } - if (pSelectionStart >= 0 && pSelectionEnd >= pSelectionStart) { - if (pSelectionStart >= 0 && pSelectionEnd >= pSelectionStart) { - QFontMetrics fm(font()); - int startX = fm.horizontalAdvance(pText.left(pSelectionStart)); - int endX = fm.horizontalAdvance(pText.left(pSelectionEnd)); - int height = fm.height(); + // painter.drawLine(cursorPos.x(), cursorPos.y(), cursorPos.x(), cursorPos.y() + cursorHeight); + // } + // if (pSelectionStart >= 0 && pSelectionEnd >= pSelectionStart) { + // if (pSelectionStart >= 0 && pSelectionEnd >= pSelectionStart) { + // int startX = pFontMetrics.horizontalAdvance(pText.left(pSelectionStart)); + // int endX = pFontMetrics.horizontalAdvance(pText.left(pSelectionEnd)); + // int height = pFontMetrics.height(); - painter.fillRect(QRect(startX, 0, endX - startX, height), QColor(0, 120, 215, 100)); // Semi-transparent blue - } - } + // painter.fillRect(QRect(startX, 0, endX - startX, height), QColor(0, 120, 215, 100)); // Semi-transparent blue + // } + // } } void HexViewer::showEvent(QShowEvent *event) { @@ -670,6 +780,16 @@ QSize HexViewer::sizeHint() const { return pCalcPaintRect().size().toSize(); } +void HexViewer::pScrollValueChanged(int value) { + pScrollValue = value; + pSeekFile(); +} + +void HexViewer::pBlinkCursor() { + pCursorVisible = !pCursorVisible; + update(); +} + void HexViewer::resizeEvent(QResizeEvent *event) { QAbstractScrollArea::resizeEvent(event); @@ -727,7 +847,7 @@ int HexViewer::pCalcLineCount() const { return pText.length() / pCalcCharCount(); } -QPoint HexViewer::pCalcCursorPosition() { +QPoint HexViewer::pCalcCursorPosition() const { int lineHeight = fontMetrics().height(); int line = pCalcCursorLine(); @@ -747,7 +867,7 @@ QRectF HexViewer::pCalcPaintRect() const { return pViewRect; } -int HexViewer::pCalcCursorLine() { +int HexViewer::pCalcCursorLine() const { qreal columnWidth = rect().right() - 90 - verticalScrollBar()->width(); qreal scrollHeight = verticalScrollBar()->height(); qreal hexColumnWidth = (columnWidth / 4 * 3) - 15; diff --git a/hexviewer.h b/hexviewer.h index f07b9ae..4efe2f8 100644 --- a/hexviewer.h +++ b/hexviewer.h @@ -3,6 +3,8 @@ #include "rule.h" #include "blockvalue.h" +#include "hexstream.h" +#include "hexeslogger.h" #include #include @@ -16,6 +18,16 @@ #include #include +const int DEFAULT_SINGLE_STEP = 45; +const int DEFAULT_PAGE_STEP = 45; + +const int OFFSET_HEADER_WIDTH = 80; +const int MIN_PADDING = 5; +const int MAX_PADDING = 15; +const int HEADER_HEIGHT = 15; +const int STATUS_BAR_HEIGHT = 35; +const int FONT_HEIGHT = 15; + class HexViewer : public QAbstractScrollArea { Q_OBJECT @@ -27,16 +39,20 @@ public: void SetFileName(const QString fileName); QStringList RuleNames(); - void RunRules(); + void ParseRules(); void DeclareVar(); void SetVar(QString varName, QVariant value); QVariant GetVar(QString varName); QMap GetVars(); + void SetBaseDir(QString dir); + QString Text(); QString Hex(); + QString pFormatValuesGrid(const QVector& values, int width, const QFont& font, int bitSize, int &textHeight); + public slots: void AddRule(const Rule rule); void AddRules(const QVector rules); @@ -47,6 +63,7 @@ public slots: void SaveRules(); void OpenRules(); + void OpenFile(); protected: void resizeEvent(QResizeEvent *event) override; @@ -58,9 +75,15 @@ protected: QSize sizeHint() const override; +private slots: + void pScrollValueChanged(int value); + void pBlinkCursor(); + signals: void RuleNamesChanged(QStringList ruleNames); void VarsChanged(QMap vars); + void FileOpened(const QString aFileName); + void BaseDirChanged(QString dir); private: QString pFileName; @@ -69,6 +92,8 @@ private: QRectF pViewRect; QFile pFile; QMap pVars; + QFontMetrics pFontMetrics; + QString pFileDir; int pScrollValue; int pCursorPosition; // Cursor position in the text @@ -78,39 +103,55 @@ private: QTimer *pBlinkTimer; // Timer for blinking cursor QQueue pRules; + // Init, update, & utility functions + void pInitCursorTimer(); + void pInitScrollBar(); + void pUpdateScrollBar(); QString pCleanHex(const QString &text); QString pStringToHex(const QString &text); - void pUpdateScrollBar(); + void pSeekFile(); + void pRefreshRules(); + // Cursor functions void pPaintCursor(QPainter &painter); + int pCalcCursorLine() const; + QPoint pCalcCursorPosition() const; + + // Selection functions void pPaintSelection(QPainter &painter); + + // Header functions void pPaintHeaders(QPainter &painter); + QRectF pCalcHexHeaderRect() const; + QRectF pCalcOffsetHeaderRect() const; + QRectF pCalcRuleHeaderRect() const; + QRectF pCalcTextHeaderRect() const; + + // Hex content functions void pPaintHex(QPainter &painter); + QRectF pCalcHexRect() const; + + // Text content functions void pPaintText(QPainter &painter); + QRectF pCalcTextRect() const; + + // Offset functions void pPaintOffsetColumn(QPainter &painter); + QRectF pCalcOffsetColumnRect() const; + + // Rule functions void pPaintRules(QPainter &painter); + QRectF pCalcRuleRect() const; void pPaintHexRule(QPainter &painter, int ¤tXPos, int ¤tYPos, int ruleLen, bool skip = false); void pPaintTextRule(QPainter &painter, int ¤tXPos, int ¤tYPos, int ruleLen, bool skip = false); - void pSeekFile(); - + // Other calculations + QRectF pCalcPaintRect() const; int pCalcContentHeight() const; int pCalcCharCount() const; int pCalcLineCount() const; - QRectF pCalcPaintRect() const; - QRectF pCalcTextRect() const; - QRectF pCalcHexRect() const; - QRectF pCalcHexHeaderRect() const; - int pCalcCursorLine(); - QPoint pCalcCursorPosition(); - QRectF pCalcOffsetColumnRect(); - QRectF pCalcOffsetHeaderRect(); - QRectF pCalcRuleHeaderRect(); - QRectF pCalcTextHeaderRect(); - QRectF pCalcRuleRect(); - QString pInsertNewlinesAtIntervals(const QString &input, int interval); }; #endif // HEXVIEWER_H diff --git a/main.cpp b/main.cpp index fd3e533..6ad399d 100644 --- a/main.cpp +++ b/main.cpp @@ -2,9 +2,38 @@ #include +class Rule; + +void HexesLogOutput(QtMsgType type, const QMessageLogContext &context, const QString &msg) +{ + QByteArray localMsg = msg.toLocal8Bit(); + switch (type) { + case QtDebugMsg: + fprintf(stderr, "Debug: %s (%s:%u, %s)\n", localMsg.constData(), context.file, context.line, context.function); + break; + case QtInfoMsg: + fprintf(stderr, "Info: %s (%s:%u, %s)\n", localMsg.constData(), context.file, context.line, context.function); + break; + case QtWarningMsg: + fprintf(stderr, "Warning: %s (%s:%u, %s)\n", localMsg.constData(), context.file, context.line, context.function); + break; + case QtCriticalMsg: + fprintf(stderr, "Critical: %s (%s:%u, %s)\n", localMsg.constData(), context.file, context.line, context.function); + break; + case QtFatalMsg: + fprintf(stderr, "Fatal: %s (%s:%u, %s)\n", localMsg.constData(), context.file, context.line, context.function); + abort(); + } +} + int main(int argc, char *argv[]) { + qInstallMessageHandler(HexesLogOutput); QApplication a(argc, argv); + + qRegisterMetaType("Rule"); + qRegisterMetaType>("QQueue"); + MainWindow w; w.show(); return a.exec(); diff --git a/mainwindow.cpp b/mainwindow.cpp index 262bbc6..4caf19c 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -1,16 +1,21 @@ #include "mainwindow.h" #include "ui_mainwindow.h" -#include - MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) , ui(new Ui::MainWindow) { + HexesLogger::MakeInstance(this); + ui->setupUi(this); + pSettingsName = "mainwindow.ini"; + pRecentFiles = QStringList(); + pActions = QVector(); + pBaseDir = "C:/"; + pViewer = new HexViewer(this); setCentralWidget(pViewer); - pViewer->SetFileName("C:/Ext/Projects/Qt/Hexes/nazi_zombie_factory_patch.zone"); + //pViewer->SetFileName("C:/Ext/Projects/Qt/Hexes/nazi_zombie_factory_patch.zone"); //ui->statusbar->showMessage(QString("File Size: %1 bytes").arg(data.size())); @@ -22,6 +27,11 @@ MainWindow::MainWindow(QWidget *parent) connect(pViewer, &HexViewer::RuleNamesChanged, pRuleDialog, &RuleDialog::SetRuleNames); connect(pViewer, &HexViewer::VarsChanged, pRuleDialog, &RuleDialog::SetVars); + connect(pViewer, &HexViewer::BaseDirChanged, pRuleDialog, &RuleDialog::SetBaseDir); + connect(pRuleDialog, &RuleDialog::BaseDirChanged, pViewer, &HexViewer::SetBaseDir); + connect(pViewer, &HexViewer::BaseDirChanged, this, &MainWindow::pSetBaseDir); + connect(pRuleDialog, &RuleDialog::BaseDirChanged, this, &MainWindow::pSetBaseDir); + pDelRuleDialog = new DeleteRuleDialog(this); pDelRuleDialog->SetRuleNames(pViewer->RuleNames()); connect(pViewer, &HexViewer::RuleNamesChanged, pDelRuleDialog, &DeleteRuleDialog::SetRuleNames); @@ -30,12 +40,99 @@ MainWindow::MainWindow(QWidget *parent) connect(ui->actionClear_Rules, &QAction::triggered, pViewer, &HexViewer::ClearRules); connect(ui->actionPop_Rule, &QAction::triggered, pViewer, &HexViewer::PopRule); - connect(ui->actionRun_Rules, &QAction::triggered, pViewer, &HexViewer::RunRules); - connect(ui->actionSave, &QAction::triggered, pViewer, &HexViewer::SaveRules); - connect(ui->actionOpen, &QAction::triggered, pViewer, &HexViewer::OpenRules); + connect(ui->actionRun_Rules, &QAction::triggered, pViewer, &HexViewer::ParseRules); + connect(ui->actionOpen, &QAction::triggered, pViewer, &HexViewer::OpenFile); + connect(ui->actionSave_Rule_File, &QAction::triggered, pViewer, &HexViewer::SaveRules); + connect(ui->actionOpen_Rule_File, &QAction::triggered, pViewer, &HexViewer::OpenRules); + connect(pViewer, &HexViewer::FileOpened, this, &MainWindow::pAddRecentFile); + connect(ui->actionExit, &QAction::triggered, this, &MainWindow::close); + + QSettings HexesSettings(pSettingsName, QSettings::IniFormat); + if (HexesSettings.contains("file/basedir")) { + pBaseDir = HexesSettings.value("file/basedir").toString(); + pViewer->SetBaseDir(pBaseDir); + pRuleDialog->SetBaseDir(pBaseDir); + } +} + +void MainWindow::pClearActions() { + for (int i = 0; i < pActions.size(); i++) { + delete pActions[i]; + } + pActions.clear(); +} + +void MainWindow::pSetBaseDir(QString baseDir) { + pBaseDir = baseDir; +} + +void MainWindow::showEvent(QShowEvent *event) { + QMainWindow::showEvent(event); + + pInitializeRecentFiles(); +} + +void MainWindow::pClearRecentFiles() { + QMenu *recentFileMenu = ui->menuRecent_Files; + recentFileMenu->clear(); + pRecentFiles.clear(); + + pClearActions(); + + pActions << new QAction("Clear Recents...", recentFileMenu); + recentFileMenu->addAction(pActions.last()); + connect(pActions.last(), &QAction::triggered, this, &MainWindow::pClearRecentFiles); +} + +void MainWindow::pAddRecentFile(QString fileName) { + setWindowTitle(QString("Hexes - %1").arg(fileName)); + + QMenu *recentFileMenu = ui->menuRecent_Files; + recentFileMenu->clear(); + + pRecentFiles.push_back(fileName); + if (pRecentFiles.size() > 5) { + pRecentFiles.pop_front(); + } + + QVector pActions; + for (int i = 0; i < pRecentFiles.size(); i++) { + QString recentFileName = pRecentFiles[i]; + pActions << new QAction(recentFileName, recentFileMenu); + } + recentFileMenu->addActions(pActions); +} + +void MainWindow::pInitializeRecentFiles() { + pClearActions(); + + QMenu *recentFileMenu = ui->menuRecent_Files; + QSettings HexesSettings(pSettingsName, QSettings::IniFormat); + if (HexesSettings.contains("menu/recentfiles")) { + pRecentFiles = HexesSettings.value("menu/recentfiles").toStringList(); + for (int i = 0; i < pRecentFiles.size(); i++) { + QString recentFileName = pRecentFiles[i]; + pActions << new QAction(recentFileName, recentFileMenu); + recentFileMenu->addAction(pActions.last()); + connect(pActions.last(), &QAction::triggered, this, [&]() { + pViewer->SetFileName(recentFileName); + }); + } + recentFileMenu->addSeparator(); + pActions << new QAction("Clear Recents...", recentFileMenu); + recentFileMenu->addAction(pActions.last()); + connect(pActions.last(), &QAction::triggered, this, &MainWindow::pClearRecentFiles); + } } MainWindow::~MainWindow() { + QSettings HexesSettings(pSettingsName, QSettings::IniFormat); + HexesSettings.setValue("menu/recentfiles", QVariant::fromValue(pRecentFiles)); + HexesSettings.setValue("file/basedir", QVariant::fromValue(pBaseDir)); + + pClearActions(); + HexesLogger::DeleteInstance(); + delete ui; delete pViewer; delete pRuleDialog; diff --git a/mainwindow.h b/mainwindow.h index f6e81aa..78f8bc6 100644 --- a/mainwindow.h +++ b/mainwindow.h @@ -4,12 +4,14 @@ #include "hexviewer.h" #include "ruledialog.h" #include "deleteruledialog.h" +#include "hexeslogger.h" #include #include #include #include #include +#include QT_BEGIN_NAMESPACE namespace Ui { class MainWindow; } @@ -23,10 +25,26 @@ public: MainWindow(QWidget *parent = nullptr); ~MainWindow(); + void AddRecentFile(const QString fileName); + +private slots: + void pInitializeRecentFiles(); + void pAddRecentFile(QString fileName); + void pClearRecentFiles(); + void pClearActions(); + void pSetBaseDir(QString baseDir); + +protected: + void showEvent(QShowEvent* event) override; + private: Ui::MainWindow *ui; HexViewer *pViewer; RuleDialog *pRuleDialog; DeleteRuleDialog *pDelRuleDialog; + QString pSettingsName; + QStringList pRecentFiles; + QVector pActions; + QString pBaseDir; }; #endif // MAINWINDOW_H diff --git a/mainwindow.ui b/mainwindow.ui index 0eebe70..4d43b70 100644 --- a/mainwindow.ui +++ b/mainwindow.ui @@ -33,15 +33,26 @@ File + + + Recent Files + + + + - + + + + + @@ -215,6 +226,55 @@ + + + RecentFiles + + + + + Clear Recents + + + + CommitMono + 7 + + + + + + Open Rule File + + + + CommitMono + 8 + + + + + + Save Rule File + + + + CommitMono + 8 + + + + + + Clear Recents + + + + CommitMono + 8 + + + diff --git a/nazi_zombie_factory_patch.zone b/nazi_zombie_factory_patch.zone deleted file mode 100644 index fcfaed7..0000000 Binary files a/nazi_zombie_factory_patch.zone and /dev/null differ diff --git a/repeat.png b/repeat.png new file mode 100644 index 0000000..0192df3 Binary files /dev/null and b/repeat.png differ diff --git a/rule.cpp b/rule.cpp index 953d1ff..fadbf38 100644 --- a/rule.cpp +++ b/rule.cpp @@ -11,6 +11,10 @@ Rule::Rule() { pRepeatCount = 0; pPreSkipCount = 0; pPostSkipCount = 0; + pRepeatOffset = 0; + pSkipOffset = 0; + pPreSkipOffset = 0; + pPostSkipOffset = 0; } Rule::Rule(const Rule &rule) { @@ -24,6 +28,10 @@ Rule::Rule(const Rule &rule) { pRepeatCount = rule.RepeatCount(); pPreSkipCount = rule.PreSkipCount(); pPostSkipCount = rule.PostSkipCount(); + pRepeatOffset = rule.RepeatOffset(); + pSkipOffset = rule.SkipOffset(); + pPreSkipOffset = rule.PreSkipOffset(); + pPostSkipOffset = rule.PostSkipOffset(); } Rule &Rule::operator=(const Rule &rule) { @@ -41,10 +49,29 @@ Rule &Rule::operator=(const Rule &rule) { pRepeatCount = rule.RepeatCount(); pPreSkipCount = rule.PreSkipCount(); pPostSkipCount = rule.PostSkipCount(); + pRepeatOffset = rule.RepeatOffset(); + pSkipOffset = rule.SkipOffset(); + pPreSkipOffset = rule.PreSkipOffset(); + pPostSkipOffset = rule.PostSkipOffset(); return *this; } +bool Rule::operator!() const { + bool nameNull = pName.isEmpty(); + bool typeNull = pType == TYPE_NONE; + bool propsNull = pProps.isEmpty(); + bool byteOrderNull = pByteOrder == BYTE_ORDER_NONE; + bool lengthNull = pLength < 0; + bool valueNull = pValue.isLower() || pValue.isEmpty(); + bool repeatNull = pRepeatCount < 0; + bool preSkipNull = pPreSkipCount < 0; + bool postSkipNull = pPostSkipCount < 0; + return nameNull && typeNull && propsNull && + byteOrderNull && lengthNull && valueNull && + repeatNull && preSkipNull && postSkipNull; +} + bool Rule::operator==(const Rule &rule) const { bool nameMatch = pName == rule.Name(); bool typeMatch = pType == rule.Type(); @@ -65,7 +92,7 @@ bool Rule::operator!=(const Rule &rule) const { } void Rule::SetName(const QString name) { - pName = name; + pName = name.toUpper(); } QString Rule::Name() const { @@ -137,6 +164,7 @@ int Rule::RepeatCount() const { } void Rule::SetPreSkipCount(int preSkipCount) { + if (pPreSkipCount < 0) { return; } pPreSkipCount = preSkipCount; } @@ -152,6 +180,38 @@ int Rule::PostSkipCount() const { return pPostSkipCount; } +void Rule::SetRepeatOffset(int repeatOffset) { + pRepeatOffset = repeatOffset; +} + +int Rule::RepeatOffset() const { + return pRepeatOffset; +} + +void Rule::SetSkipOffset(int skipOffset) { + pSkipOffset = skipOffset; +} + +int Rule::SkipOffset() const { + return pSkipOffset; +} + +void Rule::SetPreSkipOffset(int preSkipOffset) { + pPreSkipOffset = preSkipOffset; +} + +int Rule::PreSkipOffset() const { + return pPreSkipOffset; +} + +void Rule::SetPostSkipOffset(int postSkipOffset) { + pPostSkipOffset = postSkipOffset; +} + +int Rule::PostSkipOffset() const { + return pPostSkipOffset; +} + QColor Rule::pGenerateColor() { QRandomGenerator64 *globalRand = QRandomGenerator64::global(); double rRand = globalRand->generateDouble(); diff --git a/rule.h b/rule.h index fa7c257..64fe424 100644 --- a/rule.h +++ b/rule.h @@ -35,6 +35,7 @@ public: Rule(const Rule &rule); Rule &operator=(const Rule &rule); + bool operator!() const; bool operator==(const Rule& rule) const; bool operator!=(const Rule& rule) const; @@ -46,6 +47,13 @@ public: arch << rule.ByteOrder(); arch << rule.Length(); arch << rule.Value(); + arch << rule.PreSkipCount(); + arch << rule.RepeatCount(); + arch << rule.PostSkipCount(); + arch << rule.SkipOffset(); + arch << rule.RepeatOffset(); + arch << rule.PreSkipOffset(); + arch << rule.PostSkipOffset(); return arch; } friend QDataStream &operator>>(QDataStream &arch, Rule &rule) { @@ -56,31 +64,75 @@ public: BYTE_ORDER ruleByteOrder = BYTE_ORDER_NONE; int ruleLength = 0; QString ruleValue = ""; + int preSkipCount = 0; + int repeatCount = 0; + int postSkipCount = 0; + int repeatOffset; + int skipOffset; + int preSkipOffset; + int postSkipOffset; arch >> ruleName; rule.SetName(ruleName); - arch >> ruleType; rule.SetType(ruleType); - arch >> ruleProps; rule.SetProperties(ruleProps); - arch >> ruleColor; rule.SetColor(ruleColor); - arch >> ruleByteOrder; rule.SetByteOrder(ruleByteOrder); - arch >> ruleLength; rule.SetLength(ruleLength); - arch >> ruleValue; rule.SetValue(ruleValue); + arch >> preSkipCount; + rule.SetPreSkipCount(preSkipCount); + arch >> repeatCount; + rule.SetRepeatCount(repeatCount); + arch >> postSkipCount; + rule.SetPostSkipCount(postSkipCount); + arch >> repeatOffset; + rule.SetRepeatOffset(repeatOffset); + arch >> skipOffset; + rule.SetSkipOffset(skipOffset); + arch >> preSkipOffset; + rule.SetPreSkipOffset(preSkipOffset); + arch >> postSkipOffset; + rule.SetPostSkipOffset(postSkipOffset); return arch; } + operator QString() const { + return QString("Rule: %1\n" + "-Type: %2\n" + "-Color: %4\n" + "-ByteOrder: %5\n" + "-Length: %6\n" + "-Value: %7\n" + "-PreSkipCount: %8\n" + "-RepeatCount: %9\n" + "-PostSkipCount: %10\n" + "-SkipOffset: %11\n" + "-RepeatOffset: %12\n" + "-PreSkipOffset: %13\n" + "-PostSkipOffset: %14\n") + .arg(pName) + .arg(pType) + .arg(pColor.name()) + .arg(pByteOrder) + .arg(pLength) + .arg(pValue) + .arg(pPreSkipCount) + .arg(pRepeatCount) + .arg(pPostSkipCount) + .arg(pSkipOffset) + .arg(pRepeatOffset) + .arg(pPreSkipOffset) + .arg(pPostSkipOffset); + } + void SetName(const QString name); QString Name() const; @@ -114,6 +166,18 @@ public: void SetPostSkipCount(int postSkipCount); int PostSkipCount() const; + void SetRepeatOffset(int repeatOffset); + int RepeatOffset() const; + + void SetSkipOffset(int skipOffset); + int SkipOffset() const; + + void SetPreSkipOffset(int preSkipOffset); + int PreSkipOffset() const; + + void SetPostSkipOffset(int postSkipOffset); + int PostSkipOffset() const; + private: QString pName; RULE_TYPE pType; @@ -125,6 +189,10 @@ private: int pRepeatCount; int pPreSkipCount; int pPostSkipCount; + int pRepeatOffset; + int pSkipOffset; + int pPreSkipOffset; + int pPostSkipOffset; QColor pGenerateColor(); int pGetTypeLength(RULE_TYPE ruleType); diff --git a/ruledialog.cpp b/ruledialog.cpp index b0f5bc6..5645574 100644 --- a/ruledialog.cpp +++ b/ruledialog.cpp @@ -9,12 +9,42 @@ RuleDialog::RuleDialog(QWidget *parent) : pRuleNames = QStringList(); pVars = QMap(); pDefaultRuleCount = 1; - pRules = QVector(); + pRules = QQueue(); + pSnippetDir = "C:/"; - pHideLayout(ui->layout_Skip); - ui->groupBox_2->hide(); - ui->groupBox_PreSkip->hide(); - ui->groupBox_PostSkip->hide(); + ui->toolBox->setCurrentIndex(0); + + ui->groupBox_SkipOptions->hide(); + pHideLayout(ui->layout_ByteOrder); + + ui->label_Pre_SkipOffset->hide(); + ui->spinBox_Pre_Offset->hide(); + + ui->label_SkipOffset->hide(); + ui->spinBox_Skip_Offset->hide(); + + ui->label_Post_SkipOffset->hide(); + ui->spinBox_Post_Offset->hide(); + + ui->label_RepeatOffset->hide(); + ui->spinBox_Repeat_Offset->hide(); + + connect(ui->checkBox_PreSkip_Offset, &QCheckBox::toggled, this, [this](bool toggled) { + ui->label_Pre_SkipOffset->setVisible(toggled); + ui->spinBox_Pre_Offset->setVisible(toggled); + }); + connect(ui->checkBox_Skip_Offset, &QCheckBox::toggled, this, [this](bool toggled) { + ui->label_SkipOffset->setVisible(toggled); + ui->spinBox_Skip_Offset->setVisible(toggled); + }); + connect(ui->checkBox_PostSkip_Offset, &QCheckBox::toggled, this, [this](bool toggled) { + ui->label_Post_SkipOffset->setVisible(toggled); + ui->spinBox_Post_Offset->setVisible(toggled); + }); + connect(ui->checkBox_Repeat_Offset, &QCheckBox::toggled, this, [this](bool toggled) { + ui->label_RepeatOffset->setVisible(toggled); + ui->spinBox_Repeat_Offset->setVisible(toggled); + }); connect(ui->verticalScrollBar, &QScrollBar::valueChanged, ui->widget_RulePreview, &RulePreview::SetScrollValue); connect(ui->widget_RulePreview, &RulePreview::ScrollMaxChanged, ui->verticalScrollBar, &QScrollBar::setMaximum); @@ -27,13 +57,18 @@ RuleDialog::RuleDialog(QWidget *parent) : connect(ui->pushButton_Save, &QPushButton::clicked, this, &RuleDialog::pSaveRules); connect(ui->comboBox_Type, &QComboBox::currentIndexChanged, this, &RuleDialog::pTypeChanged); + connect(ui->pushButton_Import, &QPushButton::clicked, this, &RuleDialog::pImportSnippet); + connect(ui->pushButton_Export, &QPushButton::clicked, this, &RuleDialog::pExportSnippet); + connect(this, &RuleDialog::RulesChanged, ui->widget_RulePreview, &RulePreview::SetRules); - connect(ui->groupBox_RepeatRule, &QGroupBox::toggled, this, &RuleDialog::pRepeatToggled); + ui->comboBox_Repeat_Vars->hide(); + connect(ui->radioButton_Repeat_PrevVar, &QRadioButton::toggled, this, &RuleDialog::pRepeat_PrevVarToggled); + connect(ui->radioButton_Repeat_StaticVal, &QRadioButton::toggled, this, &RuleDialog::pRepeat_StaticValToggled); - ui->comboBox_Vars->hide(); - connect(ui->radioButton_PrevVar, &QRadioButton::toggled, this, &RuleDialog::pPrevVarToggled); - connect(ui->radioButton_StaticVal, &QRadioButton::toggled, this, &RuleDialog::pStaticValToggled); + ui->comboBox_Skip_Vars->hide(); + connect(ui->radioButton_Skip_PrevVar, &QRadioButton::toggled, this, &RuleDialog::pSkip_PrevVarToggled); + connect(ui->radioButton_Skip_StaticVal, &QRadioButton::toggled, this, &RuleDialog::pSkip_StaticValToggled); ui->comboBox_Pre_Vars->hide(); connect(ui->radioButton_Pre_StaticVal, &QRadioButton::toggled, this, &RuleDialog::pPre_StaticValToggled); @@ -49,97 +84,100 @@ RuleDialog::~RuleDialog() { } void RuleDialog::pQueueRule() { - int index = ui->comboBox_Type->currentIndex(); - RULE_TYPE type = (RULE_TYPE)index; - if (type == TYPE_NONE) { return; } + QString name = ui->lineEdit_Name->text(); + if (name == "") { return; } - int repeatCount = 0; - int preSkip = 0; - int postSkip = 0; - if (ui->groupBox_RepeatRule->isChecked()) { - if (ui->radioButton_PrevVar->isChecked()) { - QString itemText = ui->comboBox_Vars->currentText(); + RULE_TYPE type = (RULE_TYPE)ui->comboBox_Type->currentIndex(); + + Rule rule; + rule.SetName(name); + rule.SetType(type); + + if (type == TYPE_NONE) { + return; + } else if (type == TYPE_SKIP) { + if (ui->radioButton_Skip_PrevVar->isChecked()) { + QString itemText = ui->comboBox_Skip_Vars->currentText(); const QString leadingNumber = itemText.split('.').first() + "."; const QString varText = itemText.replace(leadingNumber, "") .split('(').first().trimmed(); BlockValue blockVal = pVars[varText]; - repeatCount = blockVal.ValueToInt(); - } else if (ui->radioButton_StaticVal->isChecked()) { - repeatCount = ui->spinBox_2->value(); + rule.SetLength(blockVal.ToInt()); + } else if (ui->radioButton_Skip_StaticVal->isChecked()) { + rule.SetLength(ui->spinBox_Skip_StaticVal->value()); } } - QString name = ui->lineEdit_Name->text(); - if (name == "") { return; } + + if (ui->radioButton_Repeat_StaticVal->isChecked()) { + rule.SetRepeatCount(ui->spinBox_Repeat_StaticVal->value()); + } else { + QString itemText = ui->comboBox_Repeat_Vars->currentText(); + const QString leadingNumber = itemText.split('.').first() + "."; + const QString varText = itemText.replace(leadingNumber, "") + .split('(').first().trimmed(); + BlockValue blockVal = pVars[varText]; + rule.SetRepeatCount(blockVal.ToInt()); + } if (ui->groupBox_PreSkip->isChecked()) { if (ui->radioButton_Pre_StaticVal->isChecked()) { - preSkip = ui->spinBox_Pre_StaticVal->value(); + rule.SetPreSkipCount(ui->spinBox_Pre_StaticVal->value()); } else { QString itemText = ui->comboBox_Pre_Vars->currentText(); const QString leadingNumber = itemText.split('.').first() + "."; const QString varText = itemText.replace(leadingNumber, "") .split('(').first().trimmed(); BlockValue blockVal = pVars[varText]; - preSkip = blockVal.ValueToInt(); + rule.SetPreSkipCount(blockVal.ToInt()); } } if (ui->groupBox_PostSkip->isChecked()) { if (ui->radioButton_Post_StaticVal->isChecked()) { - postSkip = ui->spinBox_Post_StaticVal->value(); + rule.SetPostSkipCount(ui->spinBox_Post_StaticVal->value()); } else { QString itemText = ui->comboBox_Post_Vars->currentText(); const QString leadingNumber = itemText.split('.').first() + "."; const QString varText = itemText.replace(leadingNumber, "") .split('(').first().trimmed(); BlockValue blockVal = pVars[varText]; - postSkip = blockVal.ValueToInt(); + rule.SetPostSkipCount(blockVal.ToInt()); } } - QString ruleName = name; - - BYTE_ORDER byteOrder = BYTE_ORDER_NONE; if (ui->radioButton_BE->isChecked()) { - byteOrder = BYTE_ORDER_BE; + rule.SetByteOrder(BYTE_ORDER_BE); } else { - byteOrder = BYTE_ORDER_LE; + rule.SetByteOrder(BYTE_ORDER_LE); } - Rule rule; - rule.SetName(ruleName); - rule.SetType(type); - rule.SetByteOrder(byteOrder); - rule.SetRepeatCount(repeatCount); - rule.SetPreSkipCount(preSkip); - rule.SetPostSkipCount(postSkip); + rule.SetSkipOffset(ui->spinBox_Skip_Offset->value()); + rule.SetPreSkipOffset(ui->spinBox_Pre_Offset->value()); + rule.SetPostSkipOffset(ui->spinBox_Post_Offset->value()); + rule.SetRepeatOffset(ui->spinBox_Repeat_Offset->value()); - if (index == 1) { // skip - rule.SetLength(ui->spinBox->value()); - } - - pRules.push_back(rule); - ui->widget_RulePreview->SetRules(pRules); + pRules.enqueue(rule); + ui->widget_RulePreview->SetRules(pRules.toList()); } void RuleDialog::pSaveRules() { - emit RulesChanged(pRules); + emit RulesChanged(pRules.toList()); close(); } void RuleDialog::pClearRules() { pRules.clear(); - ui->widget_RulePreview->SetRules(pRules); + ui->widget_RulePreview->SetRules(pRules.toList()); } void RuleDialog::pPopFirstRule() { pRules.pop_front(); - ui->widget_RulePreview->SetRules(pRules); + ui->widget_RulePreview->SetRules(pRules.toList()); } void RuleDialog::pPopLastRule() { pRules.pop_back(); - ui->widget_RulePreview->SetRules(pRules); + ui->widget_RulePreview->SetRules(pRules.toList()); } void RuleDialog::pCancelDialog() { @@ -147,6 +185,70 @@ void RuleDialog::pCancelDialog() { close(); } +void RuleDialog::pImportSnippet() { + QStringList filters; + filters.append("Rule File (*.hrs)"); + filters.append("All Files(*.*)"); + + QFileDialog importDialog(this); + importDialog.setWindowTitle("Open Hexes Rule Snippet"); + importDialog.setFileMode(QFileDialog::ExistingFile); + importDialog.setDirectory(pSnippetDir); + importDialog.setAcceptMode(QFileDialog::AcceptOpen); + importDialog.setNameFilters(filters); + if (importDialog.exec() != QDialog::Accepted) { + qDebug() << "Open snippet dialog was not accepted!"; + return; + } + + QString snippetPath = importDialog.selectedFiles().first(); + QString tempSnippetPath = snippetPath; + pSnippetDir = tempSnippetPath.replace(tempSnippetPath.split('/').last(), ""); + emit BaseDirChanged(pSnippetDir); + + QFile snippetFile(snippetPath); + if (!snippetFile.open(QIODevice::ReadOnly)) { + qDebug() << "Failed to open snippet file!"; + return; + } + snippetFile.close(); + + QSettings snippetImport(snippetPath, QSettings::IniFormat); + pRules = snippetImport.value("rules").value>(); + ui->widget_RulePreview->SetRules(pRules.toList()); +} + +void RuleDialog::pExportSnippet() { + QStringList filters; + filters.append("Rule File (*.hrs)"); + filters.append("All Files(*.*)"); + + QFileDialog exportDialog(this); + exportDialog.setWindowTitle("Save Hexes Rule Snippet"); + exportDialog.setDirectory(pSnippetDir); + exportDialog.setAcceptMode(QFileDialog::AcceptSave); + exportDialog.setNameFilters(filters); + if (exportDialog.exec() != QDialog::Accepted) { + qDebug() << "Save snippet dialog was not accepted!"; + return; + } + + QString snippetPath = exportDialog.selectedFiles().first(); + QString tempSnippetPath = snippetPath; + pSnippetDir = tempSnippetPath.replace(tempSnippetPath.split('/').last(), ""); + emit BaseDirChanged(pSnippetDir); + + QFile snippetFile(snippetPath); + if (!snippetFile.open(QIODevice::WriteOnly)) { + qDebug() << "Failed to save snippet file!"; + return; + } + snippetFile.close(); + + QSettings snippetExport(snippetPath, QSettings::IniFormat); + snippetExport.setValue("rules", QVariant::fromValue(pRules)); +} + void RuleDialog::pHideLayout(QLayout* layout) { if (!layout) return; @@ -171,16 +273,17 @@ void RuleDialog::pShowLayout(QLayout* layout) { } } - void RuleDialog::pTypeChanged(int index) { - pHideLayout(ui->layout_Skip); + ui->groupBox_SkipOptions->hide(); pShowLayout(ui->layout_ByteOrder); switch (index) { case 0: // none + ui->groupBox_SkipOptions->hide(); + pHideLayout(ui->layout_ByteOrder); break; case 1: // skip - pShowLayout(ui->layout_Skip); + ui->groupBox_SkipOptions->show(); pHideLayout(ui->layout_ByteOrder); break; case 2: // int8 [1] @@ -202,19 +305,24 @@ void RuleDialog::pTypeChanged(int index) { } } -void RuleDialog::pRepeatToggled(bool toggled) { - ui->groupBox_PreSkip->setVisible(toggled); - ui->groupBox_PostSkip->setVisible(toggled); +void RuleDialog::pSkip_PrevVarToggled(bool toggled) { + ui->comboBox_Skip_Vars->setVisible(toggled); + ui->spinBox_Skip_StaticVal->setVisible(!toggled); } -void RuleDialog::pPrevVarToggled(bool toggled) { - ui->comboBox_Vars->setVisible(toggled); - ui->spinBox_2->setVisible(!toggled); +void RuleDialog::pSkip_StaticValToggled(bool toggled) { + ui->comboBox_Skip_Vars->setVisible(!toggled); + ui->spinBox_Skip_StaticVal->setVisible(toggled); } -void RuleDialog::pStaticValToggled(bool toggled) { - ui->comboBox_Vars->setVisible(!toggled); - ui->spinBox_2->setVisible(toggled); +void RuleDialog::pRepeat_PrevVarToggled(bool toggled) { + ui->comboBox_Repeat_Vars->setVisible(toggled); + ui->spinBox_Repeat_StaticVal->setVisible(!toggled); +} + +void RuleDialog::pRepeat_StaticValToggled(bool toggled) { + ui->comboBox_Repeat_Vars->setVisible(!toggled); + ui->spinBox_Repeat_StaticVal->setVisible(toggled); } void RuleDialog::pPre_PrevVarToggled(bool toggled) { @@ -238,21 +346,50 @@ void RuleDialog::pPost_StaticValToggled(bool toggled) { } void RuleDialog::closeEvent(QCloseEvent *event) { - pDefaultRuleCount++; + ui->toolBox->setCurrentIndex(0); + if (pRuleNames.contains(QString("RULE %1").arg(pDefaultRuleCount))) { + pDefaultRuleCount++; + } const QString defaultName = QString("RULE %1").arg(pDefaultRuleCount); ui->lineEdit_Name->setText(defaultName); + ui->comboBox_Type->setCurrentIndex(0); ui->radioButton_LE->setChecked(true); - ui->spinBox->setValue(1); - ui->radioButton_StaticVal->setChecked(true); - ui->spinBox_2->setValue(1); - ui->comboBox_Vars->setCurrentIndex(0); + ui->spinBox_Skip_StaticVal->setValue(1); + ui->checkBox_Skip_Offset->setChecked(false); + ui->label_SkipOffset->hide(); + ui->spinBox_Skip_Offset->hide(); + ui->radioButton_Skip_StaticVal->setChecked(true); + ui->spinBox_Skip_StaticVal->setValue(0); + ui->comboBox_Skip_Vars->setCurrentIndex(0); + + ui->groupBox_PreSkip->setChecked(false); + ui->checkBox_PreSkip_Offset->setChecked(false); + ui->radioButton_Pre_StaticVal->setChecked(true); + ui->spinBox_Pre_StaticVal->show(); + ui->comboBox_Pre_Vars->hide(); + ui->spinBox_Pre_Offset->setValue(0); + ui->spinBox_Pre_StaticVal->setValue(0); + + ui->groupBox_PostSkip->setChecked(false); + ui->checkBox_PostSkip_Offset->setChecked(false); + ui->radioButton_Post_StaticVal->setChecked(true); + ui->spinBox_Post_StaticVal->show(); + ui->comboBox_Post_Vars->hide(); + ui->spinBox_Post_Offset->setValue(0); + ui->spinBox_Post_StaticVal->setValue(0); + + ui->radioButton_Repeat_StaticVal->setChecked(true); + ui->checkBox_Repeat_Offset->setChecked(false); + ui->label_RepeatOffset->hide(); + ui->spinBox_Repeat_Offset->hide(); + ui->spinBox_Repeat_StaticVal->setValue(0); pRules.clear(); - ui->widget_RulePreview->SetRules(pRules); + ui->widget_RulePreview->SetRules(pRules.toList()); QDialog::closeEvent(event); } @@ -265,17 +402,24 @@ void RuleDialog::SetRule(const Rule rule) { ui->lineEdit_Name->setText(rule.Name()); if (rule.Type() == TYPE_SKIP) { - ui->spinBox->setValue(rule.Length()); + ui->spinBox_Skip_StaticVal->setValue(rule.Length()); } } +void RuleDialog::SetBaseDir(QString dir) { + pSnippetDir = dir; +} + void RuleDialog::SetRuleNames(QStringList ruleNames) { pRuleNames = ruleNames; } void RuleDialog::SetVars(QMap vars) { - ui->comboBox_Vars->clear(); - ui->comboBox_Vars->addItem("Select Variable"); + ui->comboBox_Repeat_Vars->clear(); + ui->comboBox_Repeat_Vars->addItem("Select Variable"); + + ui->comboBox_Skip_Vars->clear(); + ui->comboBox_Skip_Vars->addItem("Select Variable"); ui->comboBox_Pre_Vars->clear(); ui->comboBox_Pre_Vars->addItem("Select Variable"); @@ -289,8 +433,9 @@ void RuleDialog::SetVars(QMap vars) { QString varName = (QString)key; BlockValue varVal = (BlockValue)value; QString itemName = QString("%1. %2 (%3)") - .arg(QString::number(i), varName, varVal.Value().toString()); - ui->comboBox_Vars->addItem(itemName); + .arg(QString::number(i), varName, varVal.ToVariant().toString()); + ui->comboBox_Repeat_Vars->addItem(itemName); + ui->comboBox_Skip_Vars->addItem(itemName); ui->comboBox_Pre_Vars->addItem(itemName); ui->comboBox_Post_Vars->addItem(itemName); } diff --git a/ruledialog.h b/ruledialog.h index be8123b..a385426 100644 --- a/ruledialog.h +++ b/ruledialog.h @@ -23,6 +23,8 @@ public: Rule GetRule(); void SetRule(const Rule rule); + void SetBaseDir(QString dir); + void SetRuleNames(QStringList ruleNames); void SetVars(QMap vars); @@ -33,18 +35,22 @@ private slots: void pPopFirstRule(); void pPopLastRule(); void pCancelDialog(); + void pImportSnippet(); + void pExportSnippet(); void pTypeChanged(int index); - void pRepeatToggled(bool toggled); - void pPrevVarToggled(bool toggled); - void pStaticValToggled(bool toggled); + void pSkip_PrevVarToggled(bool toggled); + void pSkip_StaticValToggled(bool toggled); void pPre_PrevVarToggled(bool toggled); void pPre_StaticValToggled(bool toggled); void pPost_PrevVarToggled(bool toggled); void pPost_StaticValToggled(bool toggled); + void pRepeat_PrevVarToggled(bool toggled); + void pRepeat_StaticValToggled(bool toggled); signals: void RuleChanged(Rule rule); void RulesChanged(QVector rules); + void BaseDirChanged(QString dir); protected: void closeEvent(QCloseEvent *event) override; @@ -55,7 +61,8 @@ private: QStringList pRuleNames; QMap pVars; int pDefaultRuleCount; - QVector pRules; + QQueue pRules; + QString pSnippetDir; void pHideLayout(QLayout *layout); void pShowLayout(QLayout *layout); diff --git a/ruledialog.ui b/ruledialog.ui index eee0378..3baaf48 100644 --- a/ruledialog.ui +++ b/ruledialog.ui @@ -6,8 +6,8 @@ 0 0 - 546 - 595 + 680 + 651 @@ -19,6 +19,9 @@ Add Rule + + + @@ -35,6 +38,7 @@ CommitMono + 8 @@ -45,6 +49,12 @@ + + + CommitMono + 8 + + @@ -56,6 +66,12 @@ + + + CommitMono + 8 + + @@ -63,6 +79,12 @@ + + + CommitMono + 8 + + Qt::Orientation::Horizontal @@ -86,10 +108,28 @@ 0 + + + 200 + 0 + + + + + CommitMono + 8 + + + + + CommitMono + 8 + + 0 @@ -112,489 +152,15 @@ - + - + CommitMono + 8 - - Rule Options - - - - - - - - - CommitMono - - - - Name: - - - - - - - - CommitMono - - - - RULE - - - 10 - - - Rule Name - - - - - - - - - - - Type: - - - - - - - - CommitMono - - - - - Select Input - - - - - Skip n Bytes - - - - - int8 [1] - - - - - uint8 [1] - - - - - int16 [2] - - - - - uint16 [2] - - - - - int32 [3] - - - - - uint32 [3] - - - - - int64 [4] - - - - - uint64 [4] - - - - - - - - - - - - - CommitMono - - - - Little Endian - - - true - - - - - - - - CommitMono - - - - Big Endian - - - - - - - - - - - - CommitMono - - - - Skip: - - - - - - - - CommitMono - - - - bytes - - - 1 - - - 100000 - - - - - - - - - - - - - CommitMono - - - - Repeat Rule - - - true - - - false - - - - - - - - - CommitMono - - - - Static Value - - - true - - - - - - - - CommitMono - - - - Previous Var - - - - - - - - - - - - CommitMono - - - - Repeat #: - - - - - - - - CommitMono - - - - times - - - 1 - - - 100000 - - - - - - - - CommitMono - - - - - Select Variable - - - - - - - - - - - - - - CommitMono - - - - Pre-Repeat Skip - - - true - - - false - - - - - - - - - CommitMono - - - - Static Value - - - true - - - - - - - - CommitMono - - - - Previous Var - - - - - - - - - - - - CommitMono - - - - Skip: - - - - - - - - CommitMono - - - - bytes - - - 1 - - - 100000 - - - - - - - - CommitMono - - - - - Select Variable - - - - - - - - - - - - - - CommitMono - - - - Post-Repeat Skip - - - true - - - false - - - - - - - - - CommitMono - - - - Static Value - - - true - - - - - - - - CommitMono - - - - Previous Var - - - - - - - - - - - - CommitMono - - - - Skip: - - - - - - - - CommitMono - - - - bytes - - - 1 - - - 100000 - - - - - - - - CommitMono - - - - - Select Variable - - - - - - - - - - - Qt::Orientation::Vertical @@ -607,71 +173,1154 @@ - - - - - - CommitMono - - - - Queue Rule - - - - - - - - CommitMono - - - - Pop First - - - - - - - - CommitMono - - - - Pop Last - - - - - - - - CommitMono - - - - Clear - - - - - - - + CommitMono + 8 - - GroupBox + + Queue Rule + + + + + CommitMono + 8 + + + + Pop First + + + + + + + + CommitMono + 8 + + + + Pop Last + + + + + + + + CommitMono + 8 + + + + Clear + + + + + + + + CommitMono + 8 + + + + Qt::Orientation::Vertical + + + + 20 + 40 + + + + + + + + + CommitMono + 8 + + + + Options + + + + + + + CommitMono + 8 + + + + 0 + + + + + 0 + 0 + 424 + 460 + + + + + :/icons/general.png:/icons/general.png + + + General Options + + + + + + + + + CommitMono + 8 + + + + Name: + + + + + + + + CommitMono + 8 + + + + RULE + + + 10 + + + Rule Name + + + + + + + + + + + Type: + + + + + + + + CommitMono + 8 + + + + + Select Input + + + + + Skip n Bytes + + + + + int8 [1] + + + + + uint8 [1] + + + + + int16 [2] + + + + + uint16 [2] + + + + + int32 [3] + + + + + uint32 [3] + + + + + int64 [4] + + + + + uint64 [4] + + + + + + + + + + + + + CommitMono + 8 + + + + Little Endian + + + true + + + + + + + + CommitMono + 8 + + + + Big Endian + + + + + + + + + Skip Options + + + + + + + + + CommitMono + 8 + + + + Static Value + + + true + + + + + + + + CommitMono + 8 + + + + Previous Var + + + + + + + + + + + + 0 + 0 + + + + Manual Offset: + + + + + + + + 0 + 0 + + + + + CommitMono + 8 + + + + + + + + + + + + 0 + 0 + + + + + CommitMono + 8 + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 0 + + + + + CommitMono + 8 + + + + -100 + + + 100 + + + + + + + + + + + + 0 + 0 + + + + + CommitMono + 8 + + + + Skip Length: + + + + + + + + 150 + 0 + + + + + CommitMono + 8 + + + + bytes + + + 0 + + + 100000 + + + 0 + + + + + + + + CommitMono + 8 + + + + + Select Variable + + + + + + + + + + + + + Qt::Orientation::Vertical + + + + 20 + 40 + + + + + + + + + CommitMono + 8 + + + + Tip: Watch out for files that mix the byte order between Big Endian and Little Endian! + + + Qt::AlignmentFlag::AlignCenter + + + true + + + + + + + + + 0 + 0 + 424 + 460 + + + + + :/icons/skip.png:/icons/skip.png + + + Extra Skip Options + + + + + + + CommitMono + 8 + + + + Pre-Rule Skip + + + true + + + false + + + + + + + + + CommitMono + 8 + + + + Static Value + + + true + + + + + + + + CommitMono + 8 + + + + Previous Var + + + + + + + + + + + + 0 + 0 + + + + Manual Offset: + + + + + + + + 0 + 0 + + + + + CommitMono + 8 + + + + + + + + + + + + 0 + 0 + + + + + CommitMono + 8 + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 0 + + + + + CommitMono + 8 + + + + -100 + + + 100 + + + + + + + + + + + + 0 + 0 + + + + + CommitMono + 8 + + + + Skip Length: + + + + + + + + 150 + 0 + + + + + CommitMono + 8 + + + + bytes + + + 0 + + + 100000 + + + 0 + + + + + + + + CommitMono + 8 + + + + + Select Variable + + + + + + + + + + + + + + CommitMono + 8 + + + + Post-Rule Skip + + + true + + + false + + + + + + + + + CommitMono + 8 + + + + Static Value + + + true + + + + + + + + CommitMono + 8 + + + + Previous Var + + + + + + + + + + + + 0 + 0 + + + + Manual Offset: + + + + + + + + 0 + 0 + + + + + CommitMono + 8 + + + + + + + + + + + + 0 + 0 + + + + + CommitMono + 8 + + + + + + + + + + + + + 0 + 0 + + + + + 75 + 0 + + + + + CommitMono + 8 + + + + -100 + + + 100 + + + + + + + + + + + + 0 + 0 + + + + + CommitMono + 8 + + + + Skip Length: + + + + + + + + 150 + 0 + + + + + CommitMono + 8 + + + + bytes + + + 0 + + + 100000 + + + 0 + + + + + + + + CommitMono + 8 + + + + + Select Variable + + + + + + + + + + + + + + CommitMono + 8 + + + + Qt::Orientation::Vertical + + + + 20 + 143 + + + + + + + + + CommitMono + 8 + + + + Tip: Want to share some of your custom rules? Try the "Export Snippet" feature at the bottom of the screen! + + + Qt::AlignmentFlag::AlignCenter + + + true + + + + + + + + + 0 + 0 + 379 + 460 + + + + + :/icons/repeat.png:/icons/repeat.png + + + Repeat Options + + + + + + + + + CommitMono + 8 + + + + Static Value + + + true + + + + + + + + CommitMono + 8 + + + + Previous Var + + + + + + + + + + + + 0 + 0 + + + + Manual Offset: + + + + + + + + 0 + 0 + + + + + + + + + + + + + + + + + + + + 75 + 0 + + + + -100 + + + 100 + + + + + + + + + + + + 0 + 0 + + + + + CommitMono + 8 + + + + Repeat #: + + + + + + + + 150 + 0 + + + + + CommitMono + 8 + + + + times + + + 0 + + + 100000 + + + 0 + + + + + + + + CommitMono + 8 + + + + + Select Variable + + + + + + + + + + Qt::Orientation::Vertical + + + + 20 + 36 + + + + + + + + + CommitMono + 9 + + + + TextLabel + + + Qt::AlignmentFlag::AlignCenter + + + true + + + + + + + + + + @@ -681,6 +1330,7 @@ CommitMono + 8 @@ -693,6 +1343,7 @@ CommitMono + 8 @@ -705,6 +1356,7 @@ CommitMono + 8 @@ -714,6 +1366,12 @@ + + + CommitMono + 8 + + Qt::Orientation::Horizontal @@ -730,6 +1388,7 @@ CommitMono + 8 diff --git a/rulepreview.cpp b/rulepreview.cpp index d82e977..3b5ef9b 100644 --- a/rulepreview.cpp +++ b/rulepreview.cpp @@ -72,8 +72,9 @@ void RulePreview::paintEvent(QPaintEvent *event) { int ruleIndex = 1; QRectF rulesRect = event->rect(); - painter.setBrush(Qt::white); - painter.drawRect(rulesRect); + painter.setBrush(QColor(45, 45, 45)); + painter.setPen(QPen(QColor(60, 60, 60), 2)); + painter.drawRoundedRect(rulesRect, 8, 8); painter.setFont(QFont("CommitMono", 7)); @@ -88,12 +89,13 @@ void RulePreview::paintEvent(QPaintEvent *event) { QColor ruleBackground(rule.Color()); ruleBackground.setAlpha(75); painter.setBrush(ruleBackground); + int offset = ruleIndex * 5; // Draw the rule information in the rule stack on the right side - QRectF ruleBackgroundRect(rulesRect.left(), -pScrollValue + rulesRect.top() + 1 + (QFontMetrics(painter.font()).height() * 3) * (ruleIndex - 1), - rulesRect.width(), QFontMetrics(painter.font()).height() * 3); - QRectF ruleRect(rulesRect.left() + 1, -pScrollValue + rulesRect.top() + 2 + (QFontMetrics(painter.font()).height() * 3) * (ruleIndex - 1), - rulesRect.width() - 2, QFontMetrics(painter.font()).height() * 3); + QRectF ruleBackgroundRect(rulesRect.left() + 10, offset - pScrollValue + rulesRect.top() + 11 + (QFontMetrics(painter.font()).height() * 3) * (ruleIndex - 1), + rulesRect.width() - 20, QFontMetrics(painter.font()).height() * 3); + QRectF ruleRect(rulesRect.left() + 11, offset - pScrollValue + rulesRect.top() + 12 + (QFontMetrics(painter.font()).height() * 3) * (ruleIndex - 1), + rulesRect.width() - 22, QFontMetrics(painter.font()).height() * 3); painter.drawRect(ruleBackgroundRect); pScrollMax = qMax(pScrollMax, (int)ruleRect.bottom()); @@ -119,7 +121,7 @@ void RulePreview::paintEvent(QPaintEvent *event) { break; } - painter.setPen(Qt::black); + painter.setPen(Qt::white); QString paddedIndex = QString::number(ruleIndex).rightJustified(2, '0'); QString ruleName = rule.Name().toUpper(); @@ -129,7 +131,6 @@ void RulePreview::paintEvent(QPaintEvent *event) { if (!isSkip) { painter.drawText(ruleRect, Qt::AlignRight, ruleByteOrder); } - ruleIndex++; } emit ScrollMaxChanged(qMax(0, static_cast(pScrollMax - rulesRect.height()))); diff --git a/skip.png b/skip.png new file mode 100644 index 0000000..f44d314 Binary files /dev/null and b/skip.png differ