2024-10-28 19:15:27 -04:00
|
|
|
#include "hexviewer.h"
|
|
|
|
|
|
2024-11-02 19:19:53 -04:00
|
|
|
/*
|
|
|
|
|
* HexViewer Constructor
|
|
|
|
|
*/
|
2024-10-28 19:15:27 -04:00
|
|
|
HexViewer::HexViewer(QWidget *parent)
|
2024-11-02 19:19:53 -04:00
|
|
|
: QAbstractScrollArea(parent)
|
|
|
|
|
, pText("")
|
|
|
|
|
, pHex("")
|
|
|
|
|
, pFileName("")
|
|
|
|
|
, pViewRect(0, 0, 800, 557)
|
|
|
|
|
, pBlinkTimer(new QTimer(this))
|
|
|
|
|
, pRules(QQueue<Rule>())
|
|
|
|
|
, pScrollValue(0)
|
|
|
|
|
, pVars(QMap<QString, BlockValue>())
|
|
|
|
|
, pFontMetrics(QFontMetrics(QFont("CommitMono", 10)))
|
|
|
|
|
, pFileDir("C:/"){
|
|
|
|
|
// Set font to monospaced option
|
2024-10-28 19:15:27 -04:00
|
|
|
setFont(QFont("CommitMono", 10));
|
2024-11-02 19:19:53 -04:00
|
|
|
// Initialize scroll bar and cursor timer
|
|
|
|
|
pInitScrollBar();
|
|
|
|
|
pInitCursorTimer();
|
2024-10-28 19:15:27 -04:00
|
|
|
}
|
|
|
|
|
|
2024-11-02 19:19:53 -04:00
|
|
|
/*
|
|
|
|
|
* HexViewer Destructor
|
|
|
|
|
*/
|
2024-10-28 19:15:27 -04:00
|
|
|
HexViewer::~HexViewer() {
|
|
|
|
|
delete pBlinkTimer;
|
|
|
|
|
}
|
|
|
|
|
|
2024-11-02 19:19:53 -04:00
|
|
|
/*
|
|
|
|
|
* Scroll Bar Initialization
|
|
|
|
|
*/
|
|
|
|
|
void HexViewer::pInitScrollBar() {
|
|
|
|
|
verticalScrollBar()->setSingleStep(DEFAULT_SINGLE_STEP);
|
|
|
|
|
verticalScrollBar()->setPageStep(DEFAULT_PAGE_STEP);
|
|
|
|
|
connect(verticalScrollBar(), &QScrollBar::valueChanged,
|
|
|
|
|
this, &HexViewer::pScrollValueChanged);
|
|
|
|
|
}
|
2024-10-28 19:15:27 -04:00
|
|
|
|
2024-11-02 19:19:53 -04:00
|
|
|
/*
|
|
|
|
|
* 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);
|
2024-10-28 19:15:27 -04:00
|
|
|
if (!pFile.open(QIODevice::ReadOnly)) {
|
2024-11-02 19:19:53 -04:00
|
|
|
const QString errorMsg("Failed to open " + pFileName);
|
|
|
|
|
HexesLogger::HexesError(20, errorMsg);
|
|
|
|
|
return;
|
2024-10-28 19:15:27 -04:00
|
|
|
}
|
2024-11-02 19:19:53 -04:00
|
|
|
// Re-search the file contents to display
|
2024-10-28 19:15:27 -04:00
|
|
|
pSeekFile();
|
|
|
|
|
}
|
|
|
|
|
|
2024-11-02 19:19:53 -04:00
|
|
|
/*
|
|
|
|
|
* Add a rule to our local queue
|
|
|
|
|
*/
|
2024-10-28 19:15:27 -04:00
|
|
|
void HexViewer::AddRule(const Rule rule) {
|
|
|
|
|
pRules.enqueue(rule);
|
2024-11-02 19:19:53 -04:00
|
|
|
// Refresh rules and view
|
|
|
|
|
pRefreshRules();
|
2024-10-28 19:15:27 -04:00
|
|
|
}
|
|
|
|
|
|
2024-11-02 19:19:53 -04:00
|
|
|
/*
|
|
|
|
|
* Adds multiple rules to our local queue
|
|
|
|
|
*/
|
2024-10-28 19:15:27 -04:00
|
|
|
void HexViewer::AddRules(const QVector<Rule> rules) {
|
2024-11-02 19:19:53 -04:00
|
|
|
if (rules.isEmpty()) { return; }
|
|
|
|
|
// Add rules from incoming queue
|
2024-10-28 19:15:27 -04:00
|
|
|
for (int i = 0; i < rules.size(); i++) {
|
|
|
|
|
pRules.enqueue(rules[i]);
|
|
|
|
|
}
|
2024-11-02 19:19:53 -04:00
|
|
|
// Refresh rules and view
|
|
|
|
|
pRefreshRules();
|
2024-10-28 19:15:27 -04:00
|
|
|
}
|
|
|
|
|
|
2024-11-02 19:19:53 -04:00
|
|
|
/*
|
|
|
|
|
* Remove a rule from our local queue
|
|
|
|
|
*/
|
2024-10-28 19:15:27 -04:00
|
|
|
void HexViewer::DeleteRule(const Rule rule) {
|
|
|
|
|
if (!pRules.contains(rule)) { return; }
|
|
|
|
|
if (pRules.size() == 0) { return; }
|
2024-11-02 19:19:53 -04:00
|
|
|
// Search for and remove rule
|
2024-10-28 19:15:27 -04:00
|
|
|
for (int i = 0; i < pRules.size(); i++) {
|
|
|
|
|
Rule currentRule = pRules.dequeue();
|
|
|
|
|
if (currentRule != rule) {
|
|
|
|
|
pRules.enqueue(currentRule);
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-11-02 19:19:53 -04:00
|
|
|
// Refresh rules and view
|
|
|
|
|
pRefreshRules();
|
2024-10-28 19:15:27 -04:00
|
|
|
}
|
|
|
|
|
|
2024-11-02 19:19:53 -04:00
|
|
|
/*
|
|
|
|
|
* Remove a rule, by name, from our local queue
|
|
|
|
|
*/
|
2024-10-28 19:15:27 -04:00
|
|
|
void HexViewer::DeleteRuleByName(const QString ruleName) {
|
|
|
|
|
if (pRules.size() == 0) { return; }
|
2024-11-02 19:19:53 -04:00
|
|
|
// Search for and remove rule
|
2024-10-28 19:15:27 -04:00
|
|
|
for (int i = 0; i < pRules.size(); i++) {
|
|
|
|
|
Rule currentRule = pRules.dequeue();
|
|
|
|
|
if (currentRule.Name() != ruleName) {
|
|
|
|
|
pRules.enqueue(currentRule);
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-11-02 19:19:53 -04:00
|
|
|
// Refresh rules and view
|
|
|
|
|
pRefreshRules();
|
2024-10-28 19:15:27 -04:00
|
|
|
}
|
|
|
|
|
|
2024-11-02 19:19:53 -04:00
|
|
|
/*
|
|
|
|
|
* Pop/dequeue a rule from our local queue
|
|
|
|
|
*/
|
2024-10-28 19:15:27 -04:00
|
|
|
void HexViewer::PopRule() {
|
|
|
|
|
if (pRules.isEmpty()) { return; }
|
|
|
|
|
pRules.dequeue();
|
2024-11-02 19:19:53 -04:00
|
|
|
// Refresh rules and view
|
|
|
|
|
pRefreshRules();
|
2024-10-28 19:15:27 -04:00
|
|
|
}
|
|
|
|
|
|
2024-11-02 19:19:53 -04:00
|
|
|
/*
|
|
|
|
|
* Clear all rules from our local queue
|
|
|
|
|
*/
|
2024-10-28 19:15:27 -04:00
|
|
|
void HexViewer::ClearRules() {
|
|
|
|
|
if (pRules.isEmpty()) { return; }
|
|
|
|
|
pRules.clear();
|
2024-11-02 19:19:53 -04:00
|
|
|
// Refresh rules and view
|
|
|
|
|
pRefreshRules();
|
2024-10-28 19:15:27 -04:00
|
|
|
}
|
|
|
|
|
|
2024-11-02 19:19:53 -04:00
|
|
|
/*
|
|
|
|
|
* Save rules to a Hexes Rule File (.hrf)
|
|
|
|
|
*/
|
2024-10-28 19:15:27 -04:00
|
|
|
void HexViewer::SaveRules() {
|
2024-11-02 19:19:53 -04:00
|
|
|
// Have user choose where to save the .hrf
|
2024-10-28 19:15:27 -04:00
|
|
|
QString filePath = QFileDialog::getSaveFileName(this, "Save Hexes Rule File",
|
2024-11-02 19:19:53 -04:00
|
|
|
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
|
2024-10-28 19:15:27 -04:00
|
|
|
QFile ruleFile(filePath);
|
|
|
|
|
if (!ruleFile.open(QIODevice::WriteOnly)) {
|
2024-11-02 19:19:53 -04:00
|
|
|
HexesLogger::HexesError(25, "Failed to save rule file!");
|
2024-10-28 19:15:27 -04:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
ruleFile.close();
|
2024-11-02 19:19:53 -04:00
|
|
|
// Write to rule file in ini format
|
2024-10-28 19:15:27 -04:00
|
|
|
QSettings ruleExport(filePath, QSettings::IniFormat);
|
|
|
|
|
ruleExport.setValue("rules", QVariant::fromValue(pRules));
|
|
|
|
|
}
|
|
|
|
|
|
2024-11-02 19:19:53 -04:00
|
|
|
/*
|
|
|
|
|
* Open a Hexes Rule File (.hrf)
|
|
|
|
|
*/
|
2024-10-28 19:15:27 -04:00
|
|
|
void HexViewer::OpenRules() {
|
2024-11-02 19:19:53 -04:00
|
|
|
// Have user choose where to save the .hrf
|
2024-10-28 19:15:27 -04:00
|
|
|
QString filePath = QFileDialog::getOpenFileName(this, "Open Hexes Rule File",
|
2024-11-02 19:19:53 -04:00
|
|
|
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
|
2024-10-28 19:15:27 -04:00
|
|
|
QFile ruleFile(filePath);
|
|
|
|
|
if (!ruleFile.open(QIODevice::ReadOnly)) {
|
2024-11-02 19:19:53 -04:00
|
|
|
HexesLogger::HexesError(30, "Failed to open rule file!");
|
2024-10-28 19:15:27 -04:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
ruleFile.close();
|
2024-11-02 19:19:53 -04:00
|
|
|
// Read from rule file in ini format
|
2024-10-28 19:15:27 -04:00
|
|
|
QSettings ruleExport(filePath, QSettings::IniFormat);
|
|
|
|
|
pRules = ruleExport.value("rules").value<QQueue<Rule>>();
|
2024-11-02 19:19:53 -04:00
|
|
|
// Notify & refresh rules and view
|
|
|
|
|
pRefreshRules();
|
|
|
|
|
emit FileOpened(filePath.split('/').last());
|
|
|
|
|
}
|
2024-10-28 19:15:27 -04:00
|
|
|
|
2024-11-02 19:19:53 -04:00
|
|
|
/*
|
|
|
|
|
* 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);
|
2024-10-28 19:15:27 -04:00
|
|
|
}
|
|
|
|
|
|
2024-11-02 19:19:53 -04:00
|
|
|
/*
|
|
|
|
|
* Get a list of rule names from local queue
|
|
|
|
|
*/
|
2024-10-28 19:15:27 -04:00
|
|
|
QStringList HexViewer::RuleNames() {
|
2024-11-02 19:19:53 -04:00
|
|
|
if (pRules.isEmpty()) { return QStringList(); }
|
|
|
|
|
// Create and return name list
|
2024-10-28 19:15:27 -04:00
|
|
|
QStringList ruleNames;
|
|
|
|
|
foreach (Rule rule, pRules) {
|
|
|
|
|
ruleNames.append(rule.Name());
|
|
|
|
|
}
|
|
|
|
|
return ruleNames;
|
|
|
|
|
}
|
|
|
|
|
|
2024-11-02 19:19:53 -04:00
|
|
|
/*
|
|
|
|
|
* 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
|
2024-10-28 19:15:27 -04:00
|
|
|
for (int i = 0; i < pRules.size(); i++) {
|
2024-11-02 19:19:53 -04:00
|
|
|
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());
|
2024-10-28 19:15:27 -04:00
|
|
|
|
2024-11-02 19:19:53 -04:00
|
|
|
// Create parse block to get value for variable
|
|
|
|
|
BlockValue val = hexStream.ParseRule(rule);
|
|
|
|
|
rule.SetValue(val.ToString());
|
2024-10-28 19:15:27 -04:00
|
|
|
|
2024-11-02 19:19:53 -04:00
|
|
|
// Execute post-skip
|
|
|
|
|
hexStream.ParseSkip(rule.PostSkipCount() + rule.PostSkipOffset());
|
2024-10-28 19:15:27 -04:00
|
|
|
|
2024-11-02 19:19:53 -04:00
|
|
|
// Add a var suffix (index) if repeating
|
|
|
|
|
if (rule.RepeatCount() > 0) {
|
|
|
|
|
pVars[rule.Name() + QString(" %1").arg(j)] = val;
|
|
|
|
|
} else {
|
|
|
|
|
pVars[rule.Name()] = val;
|
2024-10-28 19:15:27 -04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-11-02 19:19:53 -04:00
|
|
|
emit VarsChanged(pVars);
|
2024-10-28 19:15:27 -04:00
|
|
|
update();
|
|
|
|
|
}
|
|
|
|
|
|
2024-11-02 19:19:53 -04:00
|
|
|
/*
|
|
|
|
|
* Return the local variable map
|
|
|
|
|
* - Note: May not be populated
|
|
|
|
|
*/
|
2024-10-28 19:15:27 -04:00
|
|
|
QMap<QString, BlockValue> HexViewer::GetVars() {
|
|
|
|
|
return pVars;
|
|
|
|
|
}
|
|
|
|
|
|
2024-11-02 19:19:53 -04:00
|
|
|
void HexViewer::SetBaseDir(QString dir) {
|
|
|
|
|
pFileDir = dir;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Get the text representation of the data
|
|
|
|
|
*/
|
2024-10-28 19:15:27 -04:00
|
|
|
QString HexViewer::Text() {
|
|
|
|
|
return pText;
|
|
|
|
|
}
|
|
|
|
|
|
2024-11-02 19:19:53 -04:00
|
|
|
/*
|
|
|
|
|
* Get the hex representation of the data
|
|
|
|
|
*/
|
2024-10-28 19:15:27 -04:00
|
|
|
QString HexViewer::Hex() {
|
|
|
|
|
return pHex;
|
|
|
|
|
}
|
|
|
|
|
|
2024-11-02 19:19:53 -04:00
|
|
|
QString HexViewer::pFormatValuesGrid(const QVector<int> &values, int width, const QFont &font, int bitSize, int& textHeight) {
|
|
|
|
|
if (values.isEmpty()) return "";
|
2024-10-28 19:15:27 -04:00
|
|
|
|
2024-11-02 19:19:53 -04:00
|
|
|
QFontMetrics fm(font);
|
|
|
|
|
QString result;
|
2024-10-28 19:15:27 -04:00
|
|
|
|
2024-11-02 19:19:53 -04:00
|
|
|
// 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
|
|
|
|
|
}
|
2024-10-28 19:15:27 -04:00
|
|
|
|
2024-11-02 19:19:53 -04:00
|
|
|
// 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
|
|
|
|
|
}
|
2024-10-28 19:15:27 -04:00
|
|
|
|
2024-11-02 19:19:53 -04:00
|
|
|
// 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++;
|
2024-10-28 19:15:27 -04:00
|
|
|
|
2024-11-02 19:19:53 -04:00
|
|
|
// Calculate the height of the text block
|
|
|
|
|
textHeight = lineCount * fm.height();
|
2024-10-28 19:15:27 -04:00
|
|
|
|
2024-11-02 19:19:53 -04:00
|
|
|
return result;
|
2024-10-28 19:15:27 -04:00
|
|
|
}
|
|
|
|
|
|
2024-11-02 19:19:53 -04:00
|
|
|
/*
|
|
|
|
|
* Calculate the hex header rectangle
|
|
|
|
|
*/
|
|
|
|
|
QRectF HexViewer::pCalcHexHeaderRect() const {
|
|
|
|
|
qreal columnWidth = viewport()->width()
|
|
|
|
|
- (OFFSET_HEADER_WIDTH + (3 * MAX_PADDING));
|
|
|
|
|
qreal hexColumnWidth = (columnWidth / 5 * 3) - 2 * MAX_PADDING;
|
2024-10-28 19:15:27 -04:00
|
|
|
|
2024-11-02 19:19:53 -04:00
|
|
|
return QRectF(OFFSET_HEADER_WIDTH + MAX_PADDING, MIN_PADDING,
|
|
|
|
|
hexColumnWidth, HEADER_HEIGHT);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* 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;
|
|
|
|
|
|
|
|
|
|
return QRectF(OFFSET_HEADER_WIDTH + hexColumnWidth + MAX_PADDING, MIN_PADDING,
|
|
|
|
|
textColumnWidth - 2 * MAX_PADDING, MAX_PADDING);
|
|
|
|
|
}
|
2024-10-28 19:15:27 -04:00
|
|
|
|
2024-11-02 19:19:53 -04:00
|
|
|
/*
|
|
|
|
|
* 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;
|
2024-10-28 19:15:27 -04:00
|
|
|
|
2024-11-02 19:19:53 -04:00
|
|
|
return QRectF(OFFSET_HEADER_WIDTH + hexColumnWidth + textColumnWidth, MIN_PADDING,
|
|
|
|
|
ruleColumnWidth - MAX_PADDING, MAX_PADDING);
|
2024-10-28 19:15:27 -04:00
|
|
|
}
|
|
|
|
|
|
2024-11-02 19:19:53 -04:00
|
|
|
/*
|
|
|
|
|
* Calculate the offset header rectangle
|
|
|
|
|
*/
|
|
|
|
|
QRectF HexViewer::pCalcOffsetHeaderRect() const {
|
|
|
|
|
return QRectF(MIN_PADDING, MIN_PADDING,
|
|
|
|
|
OFFSET_HEADER_WIDTH, HEADER_HEIGHT);
|
2024-10-28 19:15:27 -04:00
|
|
|
}
|
|
|
|
|
|
2024-11-02 19:19:53 -04:00
|
|
|
/*
|
|
|
|
|
* 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
|
|
|
|
|
*/
|
2024-10-28 19:15:27 -04:00
|
|
|
void HexViewer::pPaintOffsetColumn(QPainter &painter) {
|
|
|
|
|
QRectF offSetColumnRect = pCalcOffsetColumnRect();
|
|
|
|
|
QRectF hexRect = pCalcHexRect();
|
|
|
|
|
|
|
|
|
|
QString offset = "";
|
|
|
|
|
int nextOffset = verticalScrollBar()->value();
|
|
|
|
|
for (int i = 0; i < qCeil(hexRect.height() / fontMetrics().height()); i++) {
|
|
|
|
|
offset += QString::number(nextOffset).rightJustified(8, '0') + " ";
|
2024-11-02 19:19:53 -04:00
|
|
|
nextOffset += pCalcCharCount();
|
2024-10-28 19:15:27 -04:00
|
|
|
}
|
|
|
|
|
offset = offset.trimmed();
|
|
|
|
|
|
|
|
|
|
painter.setPen(Qt::blue);
|
|
|
|
|
painter.drawText(offSetColumnRect, Qt::TextWordWrap | Qt::AlignHCenter, offset);
|
|
|
|
|
}
|
|
|
|
|
|
2024-11-02 19:19:53 -04:00
|
|
|
/*
|
|
|
|
|
* Calculate the hex content rectangle
|
|
|
|
|
*/
|
2024-10-28 19:15:27 -04:00
|
|
|
QRectF HexViewer::pCalcHexRect() const {
|
2024-11-02 19:19:53 -04:00
|
|
|
qreal columnWidth = viewport()->width()
|
|
|
|
|
- (OFFSET_HEADER_WIDTH + (3 * MAX_PADDING));
|
|
|
|
|
qreal hexColumnWidth = (columnWidth / 5 * 3) - 2 * MAX_PADDING;
|
2024-10-28 19:15:27 -04:00
|
|
|
|
2024-11-02 19:19:53 -04:00
|
|
|
return QRectF(OFFSET_HEADER_WIDTH + MAX_PADDING, HEADER_HEIGHT + FONT_HEIGHT,
|
|
|
|
|
hexColumnWidth, viewport()->height());
|
2024-10-28 19:15:27 -04:00
|
|
|
}
|
|
|
|
|
|
2024-11-02 19:19:53 -04:00
|
|
|
/*
|
|
|
|
|
* Paint the hex content
|
|
|
|
|
*/
|
2024-10-28 19:15:27 -04:00
|
|
|
void HexViewer::pPaintHex(QPainter &painter) {
|
|
|
|
|
QRectF hexRect = pCalcHexRect();
|
|
|
|
|
|
|
|
|
|
painter.setPen(Qt::black);
|
|
|
|
|
painter.drawText(hexRect, Qt::TextWordWrap, pHex);
|
|
|
|
|
}
|
|
|
|
|
|
2024-11-02 19:19:53 -04:00
|
|
|
/*
|
|
|
|
|
* Calculate the text content rect
|
|
|
|
|
*/
|
2024-10-28 19:15:27 -04:00
|
|
|
QRectF HexViewer::pCalcTextRect() const {
|
2024-11-02 19:19:53 -04:00
|
|
|
qreal columnWidth = viewport()->width()
|
|
|
|
|
- (OFFSET_HEADER_WIDTH + (3 * MAX_PADDING));
|
|
|
|
|
qreal hexColumnWidth = (columnWidth / 5 * 3);
|
|
|
|
|
qreal textColumnWidth = (columnWidth / 5) - MAX_PADDING / 2;
|
2024-10-28 19:15:27 -04:00
|
|
|
|
2024-11-02 19:19:53 -04:00
|
|
|
return QRectF(OFFSET_HEADER_WIDTH + hexColumnWidth, HEADER_HEIGHT + FONT_HEIGHT,
|
|
|
|
|
textColumnWidth, viewport()->height());
|
2024-10-28 19:15:27 -04:00
|
|
|
}
|
|
|
|
|
|
2024-11-02 19:19:53 -04:00
|
|
|
/*
|
|
|
|
|
* 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;
|
2024-10-28 19:15:27 -04:00
|
|
|
|
2024-11-02 19:19:53 -04:00
|
|
|
return QRectF(OFFSET_HEADER_WIDTH + hexColumnWidth + textColumnWidth, HEADER_HEIGHT + FONT_HEIGHT,
|
|
|
|
|
ruleColumnWidth, viewport()->height());
|
2024-10-28 19:15:27 -04:00
|
|
|
}
|
|
|
|
|
|
2024-11-02 19:19:53 -04:00
|
|
|
/*
|
|
|
|
|
* Paint the text content
|
|
|
|
|
*/
|
2024-10-28 19:15:27 -04:00
|
|
|
void HexViewer::pPaintText(QPainter &painter) {
|
|
|
|
|
QRectF textRect = pCalcTextRect();
|
|
|
|
|
|
|
|
|
|
painter.setPen(Qt::black);
|
|
|
|
|
painter.drawText(textRect, Qt::TextWrapAnywhere, pText);
|
|
|
|
|
}
|
|
|
|
|
|
2024-11-02 19:19:53 -04:00
|
|
|
/*
|
|
|
|
|
* Paint a hex rule over the content
|
|
|
|
|
*/
|
2024-10-28 19:15:27 -04:00
|
|
|
void HexViewer::pPaintHexRule(QPainter &painter, int ¤tXPos,
|
|
|
|
|
int ¤tYPos, int ruleLen, bool skip) {
|
|
|
|
|
int charWidth = fontMetrics().horizontalAdvance("F");
|
|
|
|
|
int charPerLine = pCalcCharCount();
|
|
|
|
|
QRectF hexRect = pCalcHexRect();
|
|
|
|
|
int scrollHeight = verticalScrollBar()->value();
|
|
|
|
|
int adjustedYPos = currentYPos - scrollHeight; // Apply scroll offset
|
|
|
|
|
|
|
|
|
|
while (ruleLen > 0) {
|
|
|
|
|
int remainingCharsOnLine = charPerLine - ((currentXPos - hexRect.left()) / (3 * charWidth));
|
|
|
|
|
int charsToDraw = qMin(remainingCharsOnLine, ruleLen);
|
|
|
|
|
int rectWidth = charsToDraw * 3 * charWidth;
|
|
|
|
|
|
|
|
|
|
// Only draw if within the visible area vertically
|
2024-11-02 19:19:53 -04:00
|
|
|
if (adjustedYPos >= hexRect.top() &&
|
|
|
|
|
adjustedYPos <= (hexRect.bottom() - fontMetrics().height())) {
|
2024-10-28 19:15:27 -04:00
|
|
|
|
|
|
|
|
QRectF skipRect(QPointF(currentXPos, adjustedYPos + 1),
|
|
|
|
|
QSizeF(rectWidth - charWidth, fontMetrics().height() - 2));
|
|
|
|
|
painter.drawRect(skipRect);
|
|
|
|
|
if (skip) {
|
|
|
|
|
painter.drawLine(skipRect.topLeft(), skipRect.bottomRight());
|
|
|
|
|
painter.drawLine(skipRect.topRight(), skipRect.bottomLeft());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
currentXPos += rectWidth;
|
|
|
|
|
ruleLen -= charsToDraw;
|
|
|
|
|
|
|
|
|
|
// Move to the next line if the current position exceeds the line width
|
|
|
|
|
if (currentXPos >= hexRect.left() + charPerLine * 3 * charWidth) {
|
|
|
|
|
currentXPos = hexRect.left();
|
|
|
|
|
adjustedYPos += fontMetrics().height();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Restore the Y position accounting for scroll offset
|
|
|
|
|
currentYPos = adjustedYPos + scrollHeight;
|
|
|
|
|
}
|
|
|
|
|
|
2024-11-02 19:19:53 -04:00
|
|
|
/*
|
|
|
|
|
* Paint a text rule over the content
|
|
|
|
|
*/
|
2024-10-28 19:15:27 -04:00
|
|
|
void HexViewer::pPaintTextRule(QPainter &painter, int ¤tXPos,
|
|
|
|
|
int ¤tYPos, int ruleLen, bool skip) {
|
|
|
|
|
int charWidth = fontMetrics().horizontalAdvance("F");
|
|
|
|
|
int charPerLine = pCalcCharCount();
|
|
|
|
|
QRectF textRect = pCalcTextRect();
|
|
|
|
|
int scrollHeight = verticalScrollBar()->value();
|
|
|
|
|
int adjustedYPos = currentYPos - scrollHeight; // Apply scroll offset
|
|
|
|
|
|
|
|
|
|
while (ruleLen > 0) {
|
|
|
|
|
int remainingCharsOnLine = charPerLine - ((currentXPos - textRect.left()) / charWidth);
|
|
|
|
|
|
|
|
|
|
// Ensure we do not run into a situation where remainingCharsOnLine becomes zero or negative
|
|
|
|
|
if (remainingCharsOnLine <= 0) {
|
|
|
|
|
currentXPos = textRect.left();
|
|
|
|
|
adjustedYPos += fontMetrics().height();
|
|
|
|
|
remainingCharsOnLine = charPerLine;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int charsToDraw = qMin(remainingCharsOnLine, ruleLen);
|
|
|
|
|
|
|
|
|
|
// If charsToDraw is still 0, break out to avoid an infinite loop
|
|
|
|
|
if (charsToDraw <= 0) break;
|
|
|
|
|
|
|
|
|
|
int rectWidth = charsToDraw * charWidth;
|
|
|
|
|
|
|
|
|
|
// Only draw if within the visible area vertically
|
2024-11-02 19:19:53 -04:00
|
|
|
if (adjustedYPos >= textRect.top() &&
|
|
|
|
|
adjustedYPos <= textRect.bottom()) {
|
2024-10-28 19:15:27 -04:00
|
|
|
|
|
|
|
|
QRectF skipRect(QPointF(currentXPos, adjustedYPos + 1),
|
|
|
|
|
QSizeF(rectWidth, fontMetrics().height() - 2));
|
|
|
|
|
painter.drawRect(skipRect);
|
|
|
|
|
if (skip) {
|
|
|
|
|
painter.drawLine(skipRect.topLeft(), skipRect.bottomRight());
|
|
|
|
|
painter.drawLine(skipRect.topRight(), skipRect.bottomLeft());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
currentXPos += rectWidth;
|
|
|
|
|
ruleLen -= charsToDraw;
|
|
|
|
|
|
|
|
|
|
// Move to the next line if the current position exceeds the line width
|
|
|
|
|
if (currentXPos >= textRect.left() + charPerLine * charWidth) {
|
|
|
|
|
currentXPos = textRect.left();
|
|
|
|
|
adjustedYPos += fontMetrics().height();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Restore the Y position accounting for scroll offset
|
|
|
|
|
currentYPos = adjustedYPos + scrollHeight;
|
|
|
|
|
}
|
|
|
|
|
|
2024-11-02 19:19:53 -04:00
|
|
|
/*
|
|
|
|
|
* Seek to a new file location, process
|
|
|
|
|
* the contents, and update the view.
|
|
|
|
|
*/
|
2024-10-28 19:15:27 -04:00
|
|
|
void HexViewer::pSeekFile() {
|
|
|
|
|
QRectF hexRect(pCalcHexRect());
|
|
|
|
|
int dataLength = (hexRect.height() / fontMetrics().height()) * pCalcCharCount();
|
|
|
|
|
|
|
|
|
|
if (dataLength < 0) {
|
|
|
|
|
return;
|
|
|
|
|
} else if (pFile.isOpen()) {
|
|
|
|
|
pFile.seek(verticalScrollBar()->value());
|
|
|
|
|
QByteArray data = pFile.read(dataLength + 2);
|
|
|
|
|
pHex = pCleanHex(data.toHex());
|
|
|
|
|
pText = QString::fromLocal8Bit(data).replace('\0', '.');//, pCalcCharCount());
|
|
|
|
|
} else {
|
2024-11-02 19:19:53 -04:00
|
|
|
//HexesLogger::HexesError(35, QString("File '%1' not open...").arg(pFile.fileName()));
|
|
|
|
|
//return;
|
2024-10-28 19:15:27 -04:00
|
|
|
}
|
|
|
|
|
pUpdateScrollBar();
|
|
|
|
|
update();
|
|
|
|
|
}
|
|
|
|
|
|
2024-11-02 19:19:53 -04:00
|
|
|
/*
|
|
|
|
|
* 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
|
|
|
|
|
*/
|
2024-10-28 19:15:27 -04:00
|
|
|
void HexViewer::pPaintRules(QPainter &painter) {
|
|
|
|
|
int ruleIndex = 1;
|
2024-11-02 19:19:53 -04:00
|
|
|
bool isSkip = false;
|
|
|
|
|
qreal fontHeight = QFontMetrics(painter.font()).height();
|
2024-10-28 19:15:27 -04:00
|
|
|
QRectF rulesRect = pCalcRuleRect();
|
|
|
|
|
|
|
|
|
|
painter.save();
|
|
|
|
|
painter.setFont(QFont("CommitMono", 7));
|
|
|
|
|
|
|
|
|
|
QRectF hexRect(pCalcHexRect());
|
|
|
|
|
int hexXPos = hexRect.left();
|
|
|
|
|
int hexYPos = hexRect.top();
|
|
|
|
|
|
|
|
|
|
QRectF textRect(pCalcTextRect());
|
|
|
|
|
int textXPos = textRect.left();
|
|
|
|
|
int textYPos = textRect.top();
|
|
|
|
|
|
2024-11-02 19:19:53 -04:00
|
|
|
int currY = 0;
|
2024-10-28 19:15:27 -04:00
|
|
|
|
2024-11-02 19:19:53 -04:00
|
|
|
foreach (Rule rule, pRules) {
|
|
|
|
|
QString typeText = "";
|
|
|
|
|
QString valueText = "";
|
|
|
|
|
int preSkipCount = rule.PreSkipCount();
|
|
|
|
|
int postSkipCount = rule.PostSkipCount();
|
|
|
|
|
QVector<int> repeatVals = QVector<int>();
|
2024-10-28 19:15:27 -04:00
|
|
|
|
2024-11-02 19:19:53 -04:00
|
|
|
QString ruleName = rule.Name();
|
2024-10-28 19:15:27 -04:00
|
|
|
|
2024-11-02 19:19:53 -04:00
|
|
|
QColor ruleBackground(rule.Color());
|
|
|
|
|
ruleBackground.setAlpha(75);
|
|
|
|
|
painter.setPen(Qt::black);
|
|
|
|
|
painter.setBrush(ruleBackground);
|
2024-10-28 19:15:27 -04:00
|
|
|
|
2024-11-02 19:19:53 -04:00
|
|
|
valueText.clear();
|
|
|
|
|
for (int i = 0; i <= rule.RepeatCount(); i++) {
|
|
|
|
|
QString formattedName = rule.Name();
|
2024-10-28 19:15:27 -04:00
|
|
|
if (rule.RepeatCount() > 0) {
|
2024-11-02 19:19:53 -04:00
|
|
|
formattedName = QString(rule.Name() + " %1").arg(i + 1);
|
2024-10-28 19:15:27 -04:00
|
|
|
}
|
2024-11-02 19:19:53 -04:00
|
|
|
// Paint pre-skip
|
2024-10-28 19:15:27 -04:00
|
|
|
if (preSkipCount > 0) {
|
|
|
|
|
pPaintHexRule(painter, hexXPos, hexYPos, preSkipCount, true);
|
|
|
|
|
pPaintTextRule(painter, textXPos, textYPos, preSkipCount, true);
|
|
|
|
|
}
|
2024-11-02 19:19:53 -04:00
|
|
|
// Paint skip or int
|
|
|
|
|
if (rule.Type() == TYPE_SKIP) {
|
2024-10-28 19:15:27 -04:00
|
|
|
pPaintHexRule(painter, hexXPos, hexYPos, rule.Length(), true);
|
|
|
|
|
pPaintTextRule(painter, textXPos, textYPos, rule.Length(), true);
|
|
|
|
|
isSkip = true;
|
2024-11-02 19:19:53 -04:00
|
|
|
} 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());
|
|
|
|
|
}
|
2024-10-28 19:15:27 -04:00
|
|
|
pPaintHexRule(painter, hexXPos, hexYPos, rule.Length());
|
|
|
|
|
pPaintTextRule(painter, textXPos, textYPos, rule.Length());
|
2024-11-02 19:19:53 -04:00
|
|
|
}
|
|
|
|
|
// Paint post-skip
|
|
|
|
|
if (postSkipCount > 0) {
|
|
|
|
|
pPaintHexRule(painter, hexXPos, hexYPos, postSkipCount, true);
|
|
|
|
|
pPaintTextRule(painter, textXPos, textYPos, postSkipCount, true);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!isSkip) {
|
|
|
|
|
int blockHeight;
|
|
|
|
|
if (repeatVals.size() > 0) {
|
|
|
|
|
valueText = pFormatValuesGrid(repeatVals, rulesRect.width(), painter.font(), rule.Length() * 8, blockHeight);
|
|
|
|
|
}
|
2024-10-28 19:15:27 -04:00
|
|
|
|
2024-11-02 19:19:53 -04:00
|
|
|
qreal ruleHeight;
|
|
|
|
|
if (rule.RepeatCount() == 0) {
|
|
|
|
|
ruleHeight = fontHeight * 2;
|
|
|
|
|
} else {
|
|
|
|
|
ruleHeight = fontHeight * 2 + blockHeight;
|
2024-10-28 19:15:27 -04:00
|
|
|
}
|
|
|
|
|
|
2024-11-02 19:19:53 -04:00
|
|
|
QRectF ruleRect(rulesRect.left() + 1, -verticalScrollBar()->value() + rulesRect.top() + 2 + currY,
|
|
|
|
|
rulesRect.width() - 2, ruleHeight);
|
2024-10-28 19:15:27 -04:00
|
|
|
QString paddedIndex = QString::number(ruleIndex).rightJustified(2, '0');
|
2024-11-02 19:19:53 -04:00
|
|
|
QString ruleText = QString("%1. %2\n%3\n%4\n").arg(paddedIndex, ruleName,
|
2024-10-28 19:15:27 -04:00
|
|
|
typeText, valueText);
|
2024-11-02 19:19:53 -04:00
|
|
|
QString ruleByteOrder = QString(rule.ByteOrder() == BYTE_ORDER_BE ? "BE" : "LE") + "\n\n";
|
2024-10-28 19:15:27 -04:00
|
|
|
painter.drawText(ruleRect, Qt::TextWrapAnywhere, ruleText);
|
2024-11-02 19:19:53 -04:00
|
|
|
painter.drawText(ruleRect, Qt::AlignRight, ruleByteOrder);
|
2024-10-28 19:15:27 -04:00
|
|
|
|
2024-11-02 19:19:53 -04:00
|
|
|
QRectF ruleBackgroundRect(rulesRect.left(), -verticalScrollBar()->value() + rulesRect.top() + 1 + currY,
|
|
|
|
|
rulesRect.width(), ruleHeight);
|
2024-10-28 19:15:27 -04:00
|
|
|
|
2024-11-02 19:19:53 -04:00
|
|
|
painter.drawRect(ruleBackgroundRect);
|
2024-10-28 19:15:27 -04:00
|
|
|
ruleIndex++;
|
2024-11-02 19:19:53 -04:00
|
|
|
currY += ruleHeight;
|
2024-10-28 19:15:27 -04:00
|
|
|
}
|
2024-11-02 19:19:53 -04:00
|
|
|
isSkip = false;
|
2024-10-28 19:15:27 -04:00
|
|
|
}
|
|
|
|
|
painter.restore();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void HexViewer::pPaintHeaders(QPainter &painter) {
|
|
|
|
|
QRectF offSetHeaderRect = pCalcOffsetHeaderRect();
|
|
|
|
|
|
|
|
|
|
painter.setPen(Qt::blue);
|
|
|
|
|
painter.drawText(offSetHeaderRect, Qt::AlignHCenter | Qt::AlignVCenter, "Offset (d)");
|
|
|
|
|
|
|
|
|
|
QRectF hexHeaderRect = pCalcHexHeaderRect();
|
|
|
|
|
|
|
|
|
|
int charWidth = fontMetrics().horizontalAdvance('F');
|
|
|
|
|
int hexCharsPerLine = qCeil(hexHeaderRect.width() / charWidth) / 3;
|
|
|
|
|
QString hexHeader = "";
|
|
|
|
|
for (int i = 0; i < hexCharsPerLine; i++) {
|
|
|
|
|
hexHeader += QString::number(i).rightJustified(2, '0') + " ";
|
|
|
|
|
}
|
|
|
|
|
hexHeader = hexHeader.trimmed();
|
|
|
|
|
|
|
|
|
|
painter.setPen(Qt::blue);
|
|
|
|
|
painter.drawText(hexHeaderRect, Qt::AlignVCenter, hexHeader);
|
|
|
|
|
|
|
|
|
|
QRectF textHeaderRect = pCalcTextHeaderRect();
|
|
|
|
|
|
|
|
|
|
painter.setPen(Qt::blue);
|
|
|
|
|
painter.drawText(textHeaderRect, Qt::AlignVCenter, "Decoded Text");
|
|
|
|
|
|
|
|
|
|
QRectF stackHeaderRect = pCalcRuleHeaderRect();
|
|
|
|
|
|
|
|
|
|
painter.setPen(Qt::blue);
|
|
|
|
|
painter.drawText(stackHeaderRect, Qt::AlignVCenter, "Rule Stack");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void HexViewer::paintEvent(QPaintEvent *event) {
|
|
|
|
|
QPainter painter(viewport());
|
|
|
|
|
if (!painter.isActive()) {
|
2024-11-02 19:19:53 -04:00
|
|
|
HexesLogger::HexesError(45, "QPainter is not active!");
|
2024-10-28 19:15:27 -04:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if (event->rect().isEmpty()) { return; }
|
|
|
|
|
painter.fillRect(pViewRect, Qt::white);
|
|
|
|
|
|
|
|
|
|
pPaintHeaders(painter);
|
|
|
|
|
pPaintOffsetColumn(painter);
|
|
|
|
|
pPaintHex(painter);
|
|
|
|
|
pPaintText(painter);
|
|
|
|
|
pPaintRules(painter);
|
|
|
|
|
|
2024-11-02 19:19:53 -04:00
|
|
|
// if (pCursorVisible) {
|
|
|
|
|
// QPoint cursorPos = pCalcCursorPosition();
|
2024-10-28 19:15:27 -04:00
|
|
|
|
2024-11-02 19:19:53 -04:00
|
|
|
// int cursorHeight = pFontMetrics.height();
|
2024-10-28 19:15:27 -04:00
|
|
|
|
2024-11-02 19:19:53 -04:00
|
|
|
// 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
|
|
|
|
|
// }
|
|
|
|
|
// }
|
2024-10-28 19:15:27 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void HexViewer::showEvent(QShowEvent *event) {
|
|
|
|
|
pUpdateScrollBar();
|
|
|
|
|
pSeekFile();
|
|
|
|
|
QAbstractScrollArea::showEvent(event);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QSize HexViewer::sizeHint() const {
|
|
|
|
|
return pCalcPaintRect().size().toSize();
|
|
|
|
|
}
|
|
|
|
|
|
2024-11-02 19:19:53 -04:00
|
|
|
void HexViewer::pScrollValueChanged(int value) {
|
|
|
|
|
pScrollValue = value;
|
|
|
|
|
pSeekFile();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void HexViewer::pBlinkCursor() {
|
|
|
|
|
pCursorVisible = !pCursorVisible;
|
|
|
|
|
update();
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-28 19:15:27 -04:00
|
|
|
void HexViewer::resizeEvent(QResizeEvent *event) {
|
|
|
|
|
QAbstractScrollArea::resizeEvent(event);
|
|
|
|
|
|
|
|
|
|
pViewRect = QRectF(QPoint(0, 0), event->size());
|
|
|
|
|
pSeekFile();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void HexViewer::keyPressEvent(QKeyEvent *event) {
|
|
|
|
|
if (event->key() == Qt::Key_Left) {
|
|
|
|
|
pCursorPosition = qMax(0, pCursorPosition - 1);
|
|
|
|
|
} else if (event->key() == Qt::Key_Right) {
|
|
|
|
|
pCursorPosition = qMin(pText.length(), pCursorPosition + 1);
|
|
|
|
|
}
|
|
|
|
|
update();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void HexViewer::mousePressEvent(QMouseEvent *event) {
|
|
|
|
|
QFontMetrics fm(font());
|
|
|
|
|
int pos = event->pos().x() / fm.averageCharWidth();
|
|
|
|
|
pCursorPosition = qMin(pos, pText.length());
|
|
|
|
|
pSelectionStart = pCursorPosition;
|
|
|
|
|
update();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void HexViewer::mouseMoveEvent(QMouseEvent *event) {
|
|
|
|
|
QFontMetrics fm(font());
|
|
|
|
|
int pos = event->pos().x() / fm.averageCharWidth();
|
|
|
|
|
pSelectionEnd = qMin(pos, pText.length());
|
|
|
|
|
update();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QString HexViewer::pCleanHex(const QString &text) {
|
|
|
|
|
QString result;
|
|
|
|
|
for (int i = 0; i < text.length(); i += 2) {
|
|
|
|
|
result.append(text.mid(i, 2)); // Get two characters at a time
|
|
|
|
|
if (i + 2 < text.length()) {
|
|
|
|
|
result.append(" "); // Add a space after every two characters
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return result.toUpper();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int HexViewer::pCalcContentHeight() const {
|
|
|
|
|
int numLines = pHex.length() / pCalcCharCount();
|
|
|
|
|
return numLines * fontMetrics().height();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int HexViewer::pCalcCharCount() const {
|
|
|
|
|
int charWidth = fontMetrics().horizontalAdvance("F");
|
|
|
|
|
QRectF hexHeaderRect(pCalcHexHeaderRect());
|
|
|
|
|
return qCeil(hexHeaderRect.width() / charWidth) / 3;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int HexViewer::pCalcLineCount() const {
|
|
|
|
|
return pText.length() / pCalcCharCount();
|
|
|
|
|
}
|
|
|
|
|
|
2024-11-02 19:19:53 -04:00
|
|
|
QPoint HexViewer::pCalcCursorPosition() const {
|
2024-10-28 19:15:27 -04:00
|
|
|
int lineHeight = fontMetrics().height();
|
|
|
|
|
|
|
|
|
|
int line = pCalcCursorLine();
|
|
|
|
|
|
|
|
|
|
int lineStart = 0;
|
|
|
|
|
for (int i = 0; i < line; ++i) {
|
|
|
|
|
lineStart += rect().width() / fontMetrics().averageCharWidth();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int cursorX = fontMetrics().horizontalAdvance(pText.mid(lineStart, pCursorPosition - lineStart));
|
|
|
|
|
int cursorY = line * lineHeight;
|
|
|
|
|
|
|
|
|
|
return QPoint(cursorX, cursorY);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QRectF HexViewer::pCalcPaintRect() const {
|
|
|
|
|
return pViewRect;
|
|
|
|
|
}
|
|
|
|
|
|
2024-11-02 19:19:53 -04:00
|
|
|
int HexViewer::pCalcCursorLine() const {
|
2024-10-28 19:15:27 -04:00
|
|
|
qreal columnWidth = rect().right() - 90 - verticalScrollBar()->width();
|
|
|
|
|
qreal scrollHeight = verticalScrollBar()->height();
|
|
|
|
|
qreal hexColumnWidth = (columnWidth / 4 * 3) - 15;
|
|
|
|
|
QRectF hexRect(95, 30, hexColumnWidth - 10, scrollHeight - 35);
|
|
|
|
|
|
|
|
|
|
int charsPerLine = hexRect.width() / fontMetrics().averageCharWidth();
|
|
|
|
|
|
|
|
|
|
return pCursorPosition / charsPerLine;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void HexViewer::pPaintCursor(QPainter &painter) {
|
|
|
|
|
Q_UNUSED(painter);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void HexViewer::pPaintSelection(QPainter &painter) {
|
|
|
|
|
Q_UNUSED(painter);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QString HexViewer::pStringToHex(const QString &text) {
|
|
|
|
|
return text.toUtf8().toHex();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void HexViewer::pUpdateScrollBar() {
|
|
|
|
|
verticalScrollBar()->setRange(0, pFile.size());
|
|
|
|
|
verticalScrollBar()->setPageStep(pCalcCharCount());
|
|
|
|
|
verticalScrollBar()->setSingleStep(pCalcCharCount());
|
|
|
|
|
|
|
|
|
|
update();
|
|
|
|
|
}
|