cutter/src/widgets/HexWidget.cpp

1585 lines
48 KiB
C++
Raw Normal View History

2019-05-16 16:03:48 +00:00
#include "HexWidget.h"
#include "Cutter.h"
#include "Configuration.h"
#include "dialogs/WriteCommandsDialogs.h"
2019-05-16 16:03:48 +00:00
#include <QPainter>
#include <QPaintEvent>
#include <QResizeEvent>
#include <QMouseEvent>
#include <QKeyEvent>
#include <QWheelEvent>
#include <QtEndian>
#include <QScrollBar>
#include <QMenu>
#include <QClipboard>
#include <QApplication>
#include <QInputDialog>
#include <QPushButton>
#include <QJsonObject>
#include <QJsonArray>
#include <QRegularExpression>
#include <QToolTip>
2019-05-16 16:03:48 +00:00
static constexpr uint64_t MAX_COPY_SIZE = 128 * 1024 * 1024;
static constexpr int MAX_LINE_WIDTH_PRESET = 32;
static constexpr int MAX_LINE_WIDTH_BYTES = 128 * 1024;
2019-05-16 16:03:48 +00:00
HexWidget::HexWidget(QWidget *parent) :
QScrollArea(parent),
cursorEnabled(true),
cursorOnAscii(false),
updatingSelection(false),
itemByteLen(1),
itemGroupSize(1),
rowSizeBytes(16),
columnMode(ColumnMode::PowerOf2),
itemFormat(ItemFormatHex),
itemBigEndian(false),
addrCharLen(AddrWidth64),
showHeader(true),
showAscii(true),
showExHex(true),
showExAddr(true)
{
setMouseTracking(true);
setFocusPolicy(Qt::FocusPolicy::StrongFocus);
connect(horizontalScrollBar(), &QScrollBar::valueChanged, this, [this]() { viewport()->update(); });
connect(Config(), &Configuration::colorsUpdated, this, &HexWidget::updateColors);
connect(Config(), &Configuration::fontsUpdated, this, [this]() { setMonospaceFont(
Config()->getFont()); });
2019-05-16 16:03:48 +00:00
auto sizeActionGroup = new QActionGroup(this);
for (int i = 1; i <= 8; i *= 2) {
QAction *action = new QAction(QString::number(i), this);
action->setCheckable(true);
action->setActionGroup(sizeActionGroup);
connect(action, &QAction::triggered, this, [=]() { setItemSize(i); });
actionsItemSize.append(action);
}
actionsItemSize.at(0)->setChecked(true);
/* Follow the order in ItemFormat enum */
QStringList names;
names << tr("Hexadecimal");
names << tr("Octal");
names << tr("Decimal");
names << tr("Signed decimal");
names << tr("Float");
auto formatActionGroup = new QActionGroup(this);
for (int i = 0; i < names.length(); ++i) {
QAction *action = new QAction(names.at(i), this);
action->setCheckable(true);
action->setActionGroup(formatActionGroup);
connect(action, &QAction::triggered, this, [=]() { setItemFormat(static_cast<ItemFormat>(i)); });
actionsItemFormat.append(action);
}
actionsItemFormat.at(0)->setChecked(true);
actionsItemFormat.at(ItemFormatFloat)->setEnabled(false);
rowSizeMenu = new QMenu(tr("Bytes per row"), this);
auto columnsActionGroup = new QActionGroup(this);
for (int i = 1; i <= MAX_LINE_WIDTH_PRESET; i *= 2) {
QAction *action = new QAction(QString::number(i), rowSizeMenu);
action->setCheckable(true);
action->setActionGroup(columnsActionGroup);
connect(action, &QAction::triggered, this, [=]() { setFixedLineSize(i); });
rowSizeMenu->addAction(action);
}
rowSizeMenu->addSeparator();
actionRowSizePowerOf2 = new QAction(tr("Power of 2"), this);
actionRowSizePowerOf2->setCheckable(true);
actionRowSizePowerOf2->setActionGroup(columnsActionGroup);
connect(actionRowSizePowerOf2, &QAction::triggered, this, [=]() { setColumnMode(ColumnMode::PowerOf2); });
rowSizeMenu->addAction(actionRowSizePowerOf2);
actionItemBigEndian = new QAction(tr("Big Endian"), this);
actionItemBigEndian->setCheckable(true);
actionItemBigEndian->setEnabled(false);
connect(actionItemBigEndian, &QAction::triggered, this, &HexWidget::setItemEndianess);
actionHexPairs = new QAction(tr("Bytes as pairs"), this);
actionHexPairs->setCheckable(true);
connect(actionHexPairs, &QAction::triggered, this, &HexWidget::onHexPairsModeEnabled);
actionCopy = new QAction(tr("Copy"), this);
addAction(actionCopy);
actionCopy->setShortcutContext(Qt::ShortcutContext::WidgetWithChildrenShortcut);
2019-05-16 16:03:48 +00:00
actionCopy->setShortcut(QKeySequence::Copy);
connect(actionCopy, &QAction::triggered, this, &HexWidget::copy);
actionCopyAddress = new QAction(tr("Copy address"), this);
actionCopyAddress->setShortcutContext(Qt::ShortcutContext::WidgetWithChildrenShortcut);
2019-05-16 16:03:48 +00:00
actionCopyAddress->setShortcut(Qt::CTRL + Qt::SHIFT + Qt::Key_C);
connect(actionCopyAddress, &QAction::triggered, this, &HexWidget::copyAddress);
addAction(actionCopyAddress);
actionSelectRange = new QAction(tr("Select range"), this);
connect(actionSelectRange, &QAction::triggered, this, [this]() { rangeDialog.open(cursor.address); });
addAction(actionSelectRange);
connect(&rangeDialog, &QDialog::accepted, this, &HexWidget::onRangeDialogAccepted);
actionsWriteString.reserve(5);
QAction* actionWriteString = new QAction(tr("Write string"), this);
connect(actionWriteString, &QAction::triggered, this, &HexWidget::w_writeString);
actionsWriteString.append(actionWriteString);
QAction* actionWriteLenString = new QAction(tr("Write length and string"), this);
connect(actionWriteLenString, &QAction::triggered, this, &HexWidget::w_writePascalString);
actionsWriteString.append(actionWriteLenString);
QAction* actionWriteWideString = new QAction(tr("Write wide string"), this);
connect(actionWriteWideString, &QAction::triggered, this, &HexWidget::w_writeWideString);
actionsWriteString.append(actionWriteWideString);
QAction* actionWriteCString = new QAction(tr("Write zero terminated string"), this);
connect(actionWriteCString, &QAction::triggered, this, &HexWidget::w_writeCString);
actionsWriteString.append(actionWriteCString);
QAction* actionWrite64 = new QAction(tr("Write De\\Encoded Base64 string"), this);
connect(actionWrite64, &QAction::triggered, this, &HexWidget::w_write64);
actionsWriteString.append(actionWrite64);
actionsWriteOther.reserve(4);
QAction* actionWriteZeros = new QAction(tr("Write zeros"), this);
connect(actionWriteZeros, &QAction::triggered, this, &HexWidget::w_writeZeros);
actionsWriteOther.append(actionWriteZeros);
QAction* actionWriteRandom = new QAction(tr("Write random bytes"), this);
connect(actionWriteRandom, &QAction::triggered, this, &HexWidget::w_writeRandom);
actionsWriteOther.append(actionWriteRandom);
QAction* actionDuplicateFromOffset = new QAction(tr("Duplicate from offset"), this);
connect(actionDuplicateFromOffset, &QAction::triggered, this, &HexWidget::w_duplFromOffset);
actionsWriteOther.append(actionDuplicateFromOffset);
QAction* actionIncDec = new QAction(tr("Increment/Decrement"), this);
connect(actionIncDec, &QAction::triggered, this, &HexWidget::w_increaseDecrease);
actionsWriteOther.append(actionIncDec);
2019-05-16 16:03:48 +00:00
connect(this, &HexWidget::selectionChanged, this, [this](Selection selection) {
actionCopy->setEnabled(!selection.empty);
});
updateMetrics();
updateItemLength();
startAddress = 0ULL;
cursor.address = 0ULL;
data.reset(new MemoryData());
oldData.reset(new MemoryData());
2019-05-16 16:03:48 +00:00
fetchData();
updateCursorMeta();
connect(&cursor.blinkTimer, &QTimer::timeout, this, &HexWidget::onCursorBlinked);
cursor.setBlinkPeriod(1000);
cursor.startBlinking();
updateColors();
}
HexWidget::~HexWidget()
{
}
void HexWidget::setMonospaceFont(const QFont &font)
{
if (!(font.styleHint() & QFont::Monospace)) {
/* FIXME: Use default monospace font
setFont(XXX); */
}
QScrollArea::setFont(font);
monospaceFont = font.resolve(this->font());
2019-05-16 16:03:48 +00:00
updateMetrics();
fetchData();
updateCursorMeta();
viewport()->update();
}
void HexWidget::setItemSize(int nbytes)
{
static const QVector<int> values({1, 2, 4, 8});
if (!values.contains(nbytes))
return;
itemByteLen = nbytes;
if (itemByteLen > rowSizeBytes) {
rowSizeBytes = itemByteLen;
}
actionsItemFormat.at(ItemFormatFloat)->setEnabled(nbytes >= 4);
actionItemBigEndian->setEnabled(nbytes != 1);
updateItemLength();
fetchData();
updateCursorMeta();
viewport()->update();
}
void HexWidget::setItemFormat(ItemFormat format)
{
itemFormat = format;
bool sizeEnabled = true;
if (format == ItemFormatFloat)
sizeEnabled = false;
actionsItemSize.at(0)->setEnabled(sizeEnabled);
actionsItemSize.at(1)->setEnabled(sizeEnabled);
updateItemLength();
fetchData();
updateCursorMeta();
viewport()->update();
}
void HexWidget::setItemGroupSize(int size)
{
itemGroupSize = size;
updateCounts();
fetchData();
updateCursorMeta();
viewport()->update();
}
/**
* @brief Checks if Item at the address changed compared to the last read data.
* @param address Address of Item to be compared.
* @return True if Item is different, False if Item is equal or last read didn't contain the address.
* @see HexWidget#readItem
*
* Checks if current Item at the address changed compared to the last read data.
* It is assumed that the current read data buffer contains the address.
*/
bool HexWidget::isItemDifferentAt(uint64_t address) {
char oldItem[sizeof(uint64_t)] = {};
char newItem[sizeof(uint64_t)] = {};
if (data->copy(newItem, address, static_cast<size_t>(itemByteLen)) &&
oldData->copy(oldItem, address, static_cast<size_t>(itemByteLen))) {
return memcmp(oldItem, newItem, sizeof(oldItem)) != 0;
}
return false;
}
2019-05-16 16:03:48 +00:00
void HexWidget::updateCounts()
{
actionHexPairs->setEnabled(rowSizeBytes > 1 && itemByteLen == 1
&& itemFormat == ItemFormat::ItemFormatHex);
actionHexPairs->setChecked(Core()->getConfigb("hex.pairs"));
2019-05-16 16:03:48 +00:00
if (actionHexPairs->isChecked() && actionHexPairs->isEnabled()) {
itemGroupSize = 2;
} else {
itemGroupSize = 1;
}
if (columnMode == ColumnMode::PowerOf2) {
int last_good_size = itemGroupByteLen();
for (int i = itemGroupByteLen(); i <= MAX_LINE_WIDTH_BYTES; i *= 2) {
rowSizeBytes = i;
itemColumns = rowSizeBytes / itemGroupByteLen();
updateAreasPosition();
if (horizontalScrollBar()->maximum() == 0) {
last_good_size = rowSizeBytes;
} else {
break;
}
}
rowSizeBytes = last_good_size;
}
itemColumns = rowSizeBytes / itemGroupByteLen();
// ensure correct action is selected when changing line size programmatically
if (columnMode == ColumnMode::Fixed) {
int w = 1;
const auto &actions = rowSizeMenu->actions();
for (auto action : actions) {
action->setChecked(false);
}
for (auto action : actions) {
if (w > MAX_LINE_WIDTH_PRESET) {
break;
}
if (rowSizeBytes == w) {
action->setChecked(true);
}
w *= 2;
}
} else if (columnMode == ColumnMode::PowerOf2) {
actionRowSizePowerOf2->setChecked(true);
}
updateAreasPosition();
}
void HexWidget::setFixedLineSize(int lineSize)
{
if (lineSize < 1 || lineSize < itemGroupByteLen() || lineSize % itemGroupByteLen()) {
updateCounts();
return;
}
rowSizeBytes = lineSize;
columnMode = ColumnMode::Fixed;
updateCounts();
fetchData();
updateCursorMeta();
viewport()->update();
}
void HexWidget::setColumnMode(ColumnMode mode)
{
columnMode = mode;
updateCounts();
fetchData();
updateCursorMeta();
viewport()->update();
}
void HexWidget::selectRange(RVA start, RVA end)
{
BasicCursor endCursor(end);
endCursor += 1;
setCursorAddr(endCursor);
selection.set(start, end);
cursorEnabled = false;
emit selectionChanged(getSelection());
}
void HexWidget::clearSelection()
{
setCursorAddr(cursor.address, false);
emit selectionChanged(getSelection());
}
HexWidget::Selection HexWidget::getSelection()
{
return Selection{selection.isEmpty(), selection.start(), selection.end()};
}
void HexWidget::seek(uint64_t address)
{
setCursorAddr(address);
}
void HexWidget::refresh()
{
fetchData();
viewport()->update();
}
void HexWidget::setItemEndianess(bool bigEndian)
{
itemBigEndian = bigEndian;
updateCursorMeta(); // Update cached item character
viewport()->update();
}
void HexWidget::updateColors()
{
borderColor = Config()->getColor("gui.border");
backgroundColor = Config()->getColor("gui.background");
b0x00Color = Config()->getColor("b0x00");
b0x7fColor = Config()->getColor("b0x7f");
b0xffColor = Config()->getColor("b0xff");
printableColor = Config()->getColor("ai.write");
defColor = Config()->getColor("btext");
addrColor = Config()->getColor("func_var_addr");
diffColor = Config()->getColor("graph.diff.unmatch");
2019-05-16 16:03:48 +00:00
updateCursorMeta();
viewport()->update();
}
void HexWidget::paintEvent(QPaintEvent *event)
{
QPainter painter(viewport());
painter.setFont(monospaceFont);
int xOffset = horizontalScrollBar()->value();
if (xOffset > 0)
painter.translate(QPoint(-xOffset, 0));
if (event->rect() == cursor.screenPos) {
/* Cursor blink */
drawCursor(painter);
return;
}
painter.fillRect(event->rect().translated(xOffset, 0), backgroundColor);
drawHeader(painter);
drawAddrArea(painter);
drawItemArea(painter);
drawAsciiArea(painter);
if (!cursorEnabled)
return;
drawCursor(painter, true);
}
void HexWidget::updateWidth()
{
int max = (showAscii ? asciiArea.right() : itemArea.right()) - viewport()->width();
if (max < 0)
max = 0;
else
max += charWidth;
horizontalScrollBar()->setMaximum(max);
horizontalScrollBar()->setSingleStep(charWidth);
}
void HexWidget::resizeEvent(QResizeEvent *event)
{
int oldByteCount = bytesPerScreen();
updateCounts();
if (event->oldSize().height() == event->size().height() && oldByteCount == bytesPerScreen())
return;
updateAreasHeight();
fetchData(); // rowCount was changed
updateCursorMeta();
viewport()->update();
}
void HexWidget::mouseMoveEvent(QMouseEvent *event)
{
QPoint pos = event->pos();
pos.rx() += horizontalScrollBar()->value();
auto mouseAddr = mousePosToAddr(pos).address;
QString metaData = getFlagsAndComment(mouseAddr);
if (!metaData.isEmpty() && itemArea.contains(pos)) {
QToolTip::showText(event->globalPos(), metaData.replace(",", ", "), this);
} else {
QToolTip::hideText();
}
2019-05-16 16:03:48 +00:00
if (!updatingSelection) {
if (itemArea.contains(pos) || asciiArea.contains(pos))
setCursor(Qt::IBeamCursor);
else
setCursor(Qt::ArrowCursor);
return;
}
auto &area = currentArea();
if (pos.x() < area.left())
pos.setX(area.left());
else if (pos.x() > area.right())
pos.setX(area.right());
auto addr = currentAreaPosToAddr(pos, true);
setCursorAddr(addr, true);
/* Stop blinking */
cursorEnabled = false;
viewport()->update();
}
void HexWidget::mousePressEvent(QMouseEvent *event)
{
QPoint pos(event->pos());
pos.rx() += horizontalScrollBar()->value();
if (event->button() == Qt::LeftButton) {
bool selectingData = itemArea.contains(pos);
bool selecting = selectingData || asciiArea.contains(pos);
if (selecting) {
updatingSelection = true;
setCursorOnAscii(!selectingData);
auto cursorPosition = currentAreaPosToAddr(pos, true);
setCursorAddr(cursorPosition, event->modifiers() == Qt::ShiftModifier);
viewport()->update();
}
}
}
void HexWidget::mouseReleaseEvent(QMouseEvent *event)
{
if (event->button() == Qt::LeftButton) {
if (selection.isEmpty()) {
2019-05-16 16:03:48 +00:00
selection.init(cursor.address);
cursorEnabled = true;
}
2019-05-16 16:03:48 +00:00
updatingSelection = false;
}
}
void HexWidget::wheelEvent(QWheelEvent *event)
{
int dy = event->delta();
int64_t delta = 3 * itemRowByteLen();
if (dy > 0)
delta = -delta;
if (dy == 0)
return;
2019-05-19 20:11:09 +00:00
if (delta < 0 && startAddress < static_cast<uint64_t>(-delta)) {
2019-05-16 16:03:48 +00:00
startAddress = 0;
2019-05-19 20:11:09 +00:00
} else if (delta > 0 && data->maxIndex() < static_cast<uint64_t>(bytesPerScreen())) {
2019-05-16 16:03:48 +00:00
startAddress = 0;
} else {
startAddress += delta;
}
fetchData();
if ((data->maxIndex() - startAddress) <= static_cast<uint64_t>(bytesPerScreen() + delta - 1)) {
startAddress = (data->maxIndex() - bytesPerScreen()) + 1;
}
2019-05-16 16:03:48 +00:00
if (cursor.address >= startAddress && cursor.address <= lastVisibleAddr()) {
/* Don't enable cursor blinking if selection isn't empty */
cursorEnabled = selection.isEmpty();
updateCursorMeta();
} else {
cursorEnabled = false;
}
viewport()->update();
}
void HexWidget::keyPressEvent(QKeyEvent *event)
{
bool select = false;
auto moveOrSelect = [event, &select](QKeySequence::StandardKey moveSeq, QKeySequence::StandardKey selectSeq) ->bool {
if (event->matches(moveSeq)) {
select = false;
return true;
} else if (event->matches(selectSeq)) {
select = true;
return true;
}
return false;
};
if (moveOrSelect(QKeySequence::MoveToNextLine, QKeySequence::SelectNextLine)) {
moveCursor(itemRowByteLen(), select);
} else if (moveOrSelect(QKeySequence::MoveToPreviousLine, QKeySequence::SelectPreviousLine)) {
moveCursor(-itemRowByteLen(), select);
} else if (moveOrSelect(QKeySequence::MoveToNextChar, QKeySequence::SelectNextChar)) {
moveCursor(cursorOnAscii ? 1 : itemByteLen, select);
} else if (moveOrSelect(QKeySequence::MoveToPreviousChar, QKeySequence::SelectPreviousChar)) {
moveCursor(cursorOnAscii ? -1 : -itemByteLen, select);
} else if (moveOrSelect(QKeySequence::MoveToNextPage, QKeySequence::SelectNextPage)) {
moveCursor(bytesPerScreen(), select);
} else if (moveOrSelect(QKeySequence::MoveToPreviousPage, QKeySequence::SelectPreviousPage)) {
moveCursor(-bytesPerScreen(), select);
} else if (moveOrSelect(QKeySequence::MoveToStartOfLine, QKeySequence::SelectStartOfLine)) {
int linePos = int((cursor.address % itemRowByteLen()) - (startAddress % itemRowByteLen()));
moveCursor(-linePos, select);
} else if (moveOrSelect(QKeySequence::MoveToEndOfLine, QKeySequence::SelectEndOfLine)) {
int linePos = int((cursor.address % itemRowByteLen()) - (startAddress % itemRowByteLen()));
moveCursor(itemRowByteLen() - linePos, select);
}
//viewport()->update();
}
void HexWidget::contextMenuEvent(QContextMenuEvent *event)
{
QPoint pt = event->pos();
bool mouseOutsideSelection = false;
2019-05-16 16:03:48 +00:00
if (event->reason() == QContextMenuEvent::Mouse) {
auto mouseAddr = mousePosToAddr(pt).address;
if (asciiArea.contains(pt)) {
cursorOnAscii = true;
} else if (itemArea.contains(pt)) {
cursorOnAscii = false;
}
if (selection.isEmpty()) {
2019-05-16 16:03:48 +00:00
seek(mouseAddr);
} else {
mouseOutsideSelection = !selection.contains(mouseAddr);
2019-05-16 16:03:48 +00:00
}
}
auto disableOutsideSelectionActions = [this](bool disable) {
actionCopyAddress->setDisabled(disable);
};
2019-05-16 16:03:48 +00:00
QMenu *menu = new QMenu();
QMenu *sizeMenu = menu->addMenu(tr("Item size:"));
sizeMenu->addActions(actionsItemSize);
QMenu *formatMenu = menu->addMenu(tr("Item format:"));
formatMenu->addActions(actionsItemFormat);
menu->addMenu(rowSizeMenu);
menu->addAction(actionHexPairs);
menu->addAction(actionItemBigEndian);
QMenu *writeMenu = menu->addMenu(tr("Edit"));
writeMenu->addActions(actionsWriteString);
writeMenu->addSeparator();
writeMenu->addActions(actionsWriteOther);
2019-05-16 16:03:48 +00:00
menu->addSeparator();
menu->addAction(actionCopy);
disableOutsideSelectionActions(mouseOutsideSelection);
2019-05-16 16:03:48 +00:00
menu->addAction(actionCopyAddress);
menu->addActions(this->actions());
menu->exec(mapToGlobal(pt));
disableOutsideSelectionActions(false);
2019-05-16 16:03:48 +00:00
menu->deleteLater();
}
void HexWidget::onCursorBlinked()
{
if (!cursorEnabled)
return;
cursor.blink();
QRect cursorRect(cursor.screenPos.x(), cursor.screenPos.y(), cursor.screenPos.width(), cursor.screenPos.height());
viewport()->update(cursorRect.translated(-horizontalScrollBar()->value(), 0));
2019-05-16 16:03:48 +00:00
}
void HexWidget::onHexPairsModeEnabled(bool enable)
{
// Sync configuration
Core()->setConfig("hex.pairs", enable);
2019-05-16 16:03:48 +00:00
if (enable) {
setItemGroupSize(2);
} else {
setItemGroupSize(1);
}
}
void HexWidget::copy()
{
if (selection.isEmpty() || selection.size() > MAX_COPY_SIZE)
return;
QClipboard *clipboard = QApplication::clipboard();
if (cursorOnAscii) {
clipboard->setText(Core()->cmdRawAt(QString("psx %1")
.arg(selection.size()),
selection.start()).trimmed());
2019-05-16 16:03:48 +00:00
} else {
clipboard->setText(Core()->cmdRawAt(QString("p8 %1")
.arg(selection.size()),
selection.start()).trimmed()); //TODO: copy in the format shown
2019-05-16 16:03:48 +00:00
}
}
void HexWidget::copyAddress()
{
uint64_t addr = cursor.address;
if (!selection.isEmpty()) {
addr = selection.start();
}
QClipboard *clipboard = QApplication::clipboard();
clipboard->setText(RAddressString(addr));
}
void HexWidget::onRangeDialogAccepted()
{
if (rangeDialog.empty()) {
seek(rangeDialog.getStartAddress());
return;
}
selectRange(rangeDialog.getStartAddress(), rangeDialog.getEndAddress());
}
void HexWidget::w_writeString()
{
if (!ioModesController.prepareForWriting()) {
return;
}
bool ok = false;
QInputDialog d;
d.setInputMode(QInputDialog::InputMode::TextInput);
QString str = d.getText(this, tr("Write string"),
tr("String:"), QLineEdit::Normal, "", &ok);
if (ok && !str.isEmpty()) {
Core()->cmdRawAt(QString("w %1")
.arg(str),
getLocationAddress());
refresh();
}
}
void HexWidget::w_increaseDecrease()
{
if (!ioModesController.prepareForWriting()) {
return;
}
IncrementDecrementDialog d;
int ret = d.exec();
if (ret == QDialog::Rejected) {
return;
}
QString mode = d.getMode() == IncrementDecrementDialog::Increase ? "+" : "-";
Core()->cmdRawAt(QString("w%1%2 %3")
.arg(QString::number(d.getNBytes()))
.arg(mode)
.arg(QString::number(d.getValue())),
getLocationAddress());
refresh();
}
void HexWidget::w_writeZeros()
{
if (!ioModesController.prepareForWriting()) {
return;
}
bool ok = false;
QInputDialog d;
int size = 1;
if (!selection.isEmpty() && selection.size() <= INT_MAX) {
size = static_cast<int>(selection.size());
}
QString str = QString::number(d.getInt(this, tr("Write zeros"),
tr("Number of zeros:"), size, 1, 0x7FFFFFFF, 1, &ok));
if (ok && !str.isEmpty()) {
Core()->cmdRawAt(QString("w0 %1")
.arg(str),
getLocationAddress());
refresh();
}
}
void HexWidget::w_write64()
{
if (!ioModesController.prepareForWriting()) {
return;
}
Base64EnDecodedWriteDialog d;
int ret = d.exec();
if (ret == QDialog::Rejected) {
return;
}
QString mode = d.getMode() == Base64EnDecodedWriteDialog::Encode ? "e" : "d";
QByteArray str = d.getData();
if (mode == "d" && (QString(str).contains(QRegularExpression("[^a-zA-Z0-9+/=]")) ||
str.length() % 4 != 0 || str.isEmpty())) {
QMessageBox::critical(this, tr("Error"),
tr("Error occured during decoding your input.\n"
"Please, make sure, that it is a valid base64 string and try again."));
return;
}
Core()->cmdRawAt(QString("w6%1 %2")
.arg(mode)
.arg((mode == "e" ? str.toHex() : str).toStdString().c_str()),
getLocationAddress());
refresh();
}
void HexWidget::w_writeRandom()
{
if (!ioModesController.prepareForWriting()) {
return;
}
bool ok = false;
QInputDialog d;
int size = 1;
if (!selection.isEmpty() && selection.size() <= INT_MAX) {
size = static_cast<int>(selection.size());
}
QString nbytes = QString::number(d.getInt(this, tr("Write random"),
tr("Number of bytes:"), size, 1, 0x7FFFFFFF, 1, &ok));
if (ok && !nbytes.isEmpty()) {
Core()->cmdRawAt(QString("wr %1")
.arg(nbytes),
getLocationAddress());
refresh();
}
}
void HexWidget::w_duplFromOffset()
{
if (!ioModesController.prepareForWriting()) {
return;
}
DuplicateFromOffsetDialog d;
int ret = d.exec();
if (ret == QDialog::Rejected) {
return;
}
RVA copyFrom = d.getOffset();
QString nBytes = QString::number(d.getNBytes());
Core()->cmdRawAt(QString("wd %1 %2")
.arg(copyFrom)
.arg(nBytes),
getLocationAddress());
refresh();
}
void HexWidget::w_writePascalString()
{
if (!ioModesController.prepareForWriting()) {
return;
}
bool ok = false;
QInputDialog d;
d.setInputMode(QInputDialog::InputMode::TextInput);
QString str = d.getText(this, tr("Write Pascal string"),
tr("String:"), QLineEdit::Normal, "", &ok);
if (ok && !str.isEmpty()) {
Core()->cmdRawAt(QString("ws %1")
.arg(str),
getLocationAddress());
refresh();
}
}
void HexWidget::w_writeWideString()
{
if (!ioModesController.prepareForWriting()) {
return;
}
bool ok = false;
QInputDialog d;
d.setInputMode(QInputDialog::InputMode::TextInput);
QString str = d.getText(this, tr("Write wide string"),
tr("String:"), QLineEdit::Normal, "", &ok);
if (ok && !str.isEmpty()) {
Core()->cmdRawAt(QString("ww %1")
.arg(str),
getLocationAddress());
refresh();
}
}
void HexWidget::w_writeCString()
{
if (!ioModesController.prepareForWriting()) {
return;
}
bool ok = false;
QInputDialog d;
d.setInputMode(QInputDialog::InputMode::TextInput);
QString str = d.getText(this, tr("Write zero-terminated string"),
tr("String:"), QLineEdit::Normal, "", &ok);
if (ok && !str.isEmpty()) {
Core()->cmdRawAt(QString("wz %1")
.arg(str),
getLocationAddress());
refresh();
}
}
2019-05-16 16:03:48 +00:00
void HexWidget::updateItemLength()
{
itemPrefixLen = 0;
switch (itemFormat) {
case ItemFormatHex:
itemCharLen = 2 * itemByteLen;
if (itemByteLen > 1 && showExHex)
itemPrefixLen = hexPrefix.length();
break;
case ItemFormatOct:
itemCharLen = (itemByteLen * 8 + 3) / 3;
break;
case ItemFormatDec:
switch (itemByteLen) {
case 1:
itemCharLen = 3;
break;
case 2:
itemCharLen = 5;
break;
case 4:
itemCharLen = 10;
break;
case 8:
itemCharLen = 20;
break;
}
break;
case ItemFormatSignedDec:
switch (itemByteLen) {
case 1:
itemCharLen = 4;
break;
case 2:
itemCharLen = 6;
break;
case 4:
itemCharLen = 11;
break;
case 8:
itemCharLen = 20;
break;
}
break;
case ItemFormatFloat:
if (itemByteLen < 4)
itemByteLen = 4;
// FIXME
itemCharLen = 3 * itemByteLen;
break;
}
itemCharLen += itemPrefixLen;
updateCounts();
}
void HexWidget::drawHeader(QPainter &painter)
{
if (!showHeader)
return;
int offset = 0;
QRectF rect(itemArea.left(), 0, itemWidth(), lineHeight);
2019-05-16 16:03:48 +00:00
painter.setPen(addrColor);
for (int j = 0; j < itemColumns; ++j) {
for (int k = 0; k < itemGroupSize; ++k, offset += itemByteLen) {
painter.drawText(rect, Qt::AlignVCenter | Qt::AlignRight, QString::number(offset, 16).toUpper());
rect.translate(itemWidth(), 0);
}
rect.translate(columnSpacingWidth(), 0);
}
rect.moveLeft(asciiArea.left());
rect.setWidth(charWidth);
for (int j = 0; j < itemRowByteLen(); ++j) {
painter.drawText(rect, Qt::AlignVCenter | Qt::AlignRight, QString::number(j % 16, 16).toUpper());
rect.translate(charWidth, 0);
}
}
void HexWidget::drawCursor(QPainter &painter, bool shadow)
{
if (shadow) {
QPen pen(Qt::gray);
pen.setStyle(Qt::DashLine);
painter.setPen(pen);
shadowCursor.screenPos.setWidth(cursorOnAscii ? itemWidth() : charWidth);
2019-05-16 16:03:48 +00:00
painter.drawRect(shadowCursor.screenPos);
painter.setPen(Qt::SolidLine);
}
painter.setPen(cursor.cachedColor);
QRectF charRect(cursor.screenPos);
2019-05-16 16:03:48 +00:00
charRect.setWidth(charWidth);
painter.fillRect(charRect, backgroundColor);
painter.drawText(charRect, Qt::AlignVCenter, cursor.cachedChar);
if (cursor.isVisible) {
painter.setCompositionMode(QPainter::RasterOp_SourceXorDestination);
painter.fillRect(cursor.screenPos, QColor(0xff, 0xff, 0xff));
}
}
void HexWidget::drawAddrArea(QPainter &painter)
{
uint64_t offset = startAddress;
QString addrString;
QSizeF areaSize((addrCharLen + (showExAddr ? 2 : 0)) * charWidth, lineHeight);
QRectF strRect(addrArea.topLeft(), areaSize);
2019-05-16 16:03:48 +00:00
painter.setPen(addrColor);
for (int line = 0;
line < visibleLines && offset <= data->maxIndex();
++line, strRect.translate(0, lineHeight), offset += itemRowByteLen()) {
addrString = QString("%1").arg(offset, addrCharLen, 16, QLatin1Char('0'));
if (showExAddr)
addrString.prepend(hexPrefix);
painter.drawText(strRect, Qt::AlignVCenter, addrString);
}
painter.setPen(borderColor);
qreal vLineOffset = itemArea.left() - charWidth;
painter.drawLine(QLineF(vLineOffset, 0, vLineOffset, viewport()->height()));
2019-05-16 16:03:48 +00:00
}
void HexWidget::drawItemArea(QPainter &painter)
{
QRectF itemRect(itemArea.topLeft(), QSizeF(itemWidth(), lineHeight));
2019-05-16 16:03:48 +00:00
QColor itemColor;
QString itemString;
fillSelectionBackground(painter);
uint64_t itemAddr = startAddress;
for (int line = 0; line < visibleLines; ++line) {
itemRect.moveLeft(itemArea.left());
for (int j = 0; j < itemColumns; ++j) {
for (int k = 0; k < itemGroupSize && itemAddr <= data->maxIndex(); ++k, itemAddr += itemByteLen) {
itemString = renderItem(itemAddr - startAddress, &itemColor);
if (!getFlagsAndComment(itemAddr).isEmpty()) {
QColor markerColor(borderColor);
markerColor.setAlphaF(0.5);
const auto shape = rangePolygons(itemAddr, itemAddr, false)[0];
painter.setPen(markerColor);
painter.drawPolyline(shape);
}
if (selection.contains(itemAddr) && !cursorOnAscii) {
2019-05-16 16:03:48 +00:00
itemColor = palette().highlightedText().color();
}
if (isItemDifferentAt(itemAddr)) {
itemColor.setRgb(diffColor.rgb());
}
2019-05-16 16:03:48 +00:00
painter.setPen(itemColor);
painter.drawText(itemRect, Qt::AlignVCenter, itemString);
itemRect.translate(itemWidth(), 0);
if (cursor.address == itemAddr) {
auto &itemCursor = cursorOnAscii ? shadowCursor : cursor;
itemCursor.cachedChar = itemString.at(0);
itemCursor.cachedColor = itemColor;
}
}
itemRect.translate(columnSpacingWidth(), 0);
}
itemRect.translate(0, lineHeight);
}
painter.setPen(borderColor);
qreal vLineOffset = asciiArea.left() - charWidth;
painter.drawLine(QLineF(vLineOffset, 0, vLineOffset, viewport()->height()));
2019-05-16 16:03:48 +00:00
}
void HexWidget::drawAsciiArea(QPainter &painter)
{
QRectF charRect(asciiArea.topLeft(), QSizeF(charWidth, lineHeight));
2019-05-16 16:03:48 +00:00
fillSelectionBackground(painter, true);
uint64_t address = startAddress;
QChar ascii;
QColor color;
for (int line = 0; line < visibleLines; ++line, charRect.translate(0, lineHeight)) {
charRect.moveLeft(asciiArea.left());
for (int j = 0; j < itemRowByteLen() && address <= data->maxIndex(); ++j, ++address) {
ascii = renderAscii(address - startAddress, &color);
if (selection.contains(address) && cursorOnAscii) {
2019-05-16 16:03:48 +00:00
color = palette().highlightedText().color();
}
if (isItemDifferentAt(address)) {
color.setRgb(diffColor.rgb());
}
2019-05-16 16:03:48 +00:00
painter.setPen(color);
/* Dots look ugly. Use fillRect() instead of drawText(). */
if (ascii == '.') {
qreal a = cursor.screenPos.width();
QPointF p = charRect.bottomLeft();
p.rx() += (charWidth - a) / 2 + 1;
p.ry() += - 2 * a;
painter.fillRect(QRectF(p, QSizeF(a, a)), color);
2019-05-16 16:03:48 +00:00
} else {
painter.drawText(charRect, Qt::AlignVCenter, ascii);
}
charRect.translate(charWidth, 0);
if (cursor.address == address) {
auto &itemCursor = cursorOnAscii ? cursor : shadowCursor;
itemCursor.cachedChar = ascii;
itemCursor.cachedColor = color;
}
}
}
}
void HexWidget::fillSelectionBackground(QPainter &painter, bool ascii)
{
if (selection.isEmpty()) {
2019-05-16 16:03:48 +00:00
return;
}
const auto parts = rangePolygons(selection.start(), selection.end(), ascii);
for (const auto &shape : parts) {
QColor highlightColor = palette().color(QPalette::Highlight);
if (ascii == cursorOnAscii) {
painter.setBrush(highlightColor);
painter.drawPolygon(shape);
} else {
painter.setPen(highlightColor);
painter.drawPolyline(shape);
}
}
}
2019-05-16 16:03:48 +00:00
QVector<QPolygonF> HexWidget::rangePolygons(RVA start, RVA last, bool ascii)
{
if (last < startAddress || start > lastVisibleAddr()) {
return {};
2019-05-16 16:03:48 +00:00
}
QRectF rect;
const QRectF area = QRectF(ascii ? asciiArea : itemArea);
/* Convert absolute values to relative */
int startOffset = std::max(uint64_t(start), startAddress) - startAddress;
int endOffset = std::min(uint64_t(last), lastVisibleAddr()) - startAddress;
QVector<QPolygonF> parts;
auto getRectangle = [&](int offset) {
return QRectF(ascii ? asciiRectangle(offset) : itemRectangle(offset));
};
auto startRect = getRectangle(startOffset);
auto endRect = getRectangle(endOffset);
if (!ascii) {
if (int startFraction = startOffset % itemByteLen) {
startRect.setLeft(startRect.left() + startFraction * startRect.width() / itemByteLen);
}
if (int endFraction = itemByteLen - 1 - (endOffset % itemByteLen)) {
endRect.setRight(endRect.right() - endFraction * endRect.width() / itemByteLen);
}
}
if (endOffset - startOffset + 1 <= rowSizeBytes) {
if (startOffset / rowSizeBytes == endOffset / rowSizeBytes) { // single row
rect = startRect;
rect.setRight(endRect.right());
parts.push_back(QPolygonF(rect));
2019-05-16 16:03:48 +00:00
} else {
2020-01-28 08:02:17 +00:00
// two separate rectangles
rect = startRect;
rect.setRight(area.right());
parts.push_back(QPolygonF(rect));
rect = endRect;
rect.setLeft(area.left());
parts.push_back(QPolygonF(rect));
}
} else {
// single multiline shape
QPolygonF shape;
shape << startRect.topLeft();
rect = getRectangle(startOffset + rowSizeBytes - 1 - startOffset % rowSizeBytes);
shape << rect.topRight();
if (endOffset % rowSizeBytes != rowSizeBytes - 1) {
rect = getRectangle(endOffset - endOffset % rowSizeBytes - 1);
shape << rect.bottomRight() << endRect.topRight();
2019-05-16 16:03:48 +00:00
}
shape << endRect.bottomRight();
shape << getRectangle(endOffset - endOffset % rowSizeBytes).bottomLeft();
if (startOffset % rowSizeBytes) {
rect = getRectangle(startOffset - startOffset % rowSizeBytes + rowSizeBytes);
shape << rect.topLeft() << startRect.bottomLeft();
}
shape << shape.first(); // close the shape
parts.push_back(shape);
2019-05-16 16:03:48 +00:00
}
return parts;
2019-05-16 16:03:48 +00:00
}
void HexWidget::updateMetrics()
{
QFontMetricsF fontMetrics(this->monospaceFont);
lineHeight = fontMetrics.height();
charWidth = fontMetrics.width(QLatin1Char('F'));
2019-05-16 16:03:48 +00:00
updateCounts();
updateAreasHeight();
qreal cursorWidth = std::max(charWidth / 3, 1.);
2019-05-16 16:03:48 +00:00
cursor.screenPos.setHeight(lineHeight);
shadowCursor.screenPos.setHeight(lineHeight);
cursor.screenPos.setWidth(cursorWidth);
if (cursorOnAscii) {
cursor.screenPos.moveTopLeft(asciiArea.topLeft());
shadowCursor.screenPos.setWidth(itemWidth());
shadowCursor.screenPos.moveTopLeft(itemArea.topLeft());
} else {
cursor.screenPos.moveTopLeft(itemArea.topLeft());
shadowCursor.screenPos.setWidth(charWidth);
shadowCursor.screenPos.moveTopLeft(asciiArea.topLeft());
}
}
void HexWidget::updateAreasPosition()
{
const qreal spacingWidth = areaSpacingWidth();
2019-05-16 16:03:48 +00:00
qreal yOffset = showHeader ? lineHeight : 0;
2019-05-16 16:03:48 +00:00
addrArea.setTopLeft(QPointF(0, yOffset));
2019-05-16 16:03:48 +00:00
addrArea.setWidth((addrCharLen + (showExAddr ? 2 : 0)) * charWidth);
itemArea.setTopLeft(QPointF(addrArea.right() + spacingWidth, yOffset));
2019-05-16 16:03:48 +00:00
itemArea.setWidth(itemRowWidth());
asciiArea.setTopLeft(QPointF(itemArea.right() + spacingWidth, yOffset));
2019-05-16 16:03:48 +00:00
asciiArea.setWidth(asciiRowWidth());
updateWidth();
}
void HexWidget::updateAreasHeight()
{
visibleLines = static_cast<int>((viewport()->height() - itemArea.top()) / lineHeight);
2019-05-16 16:03:48 +00:00
qreal height = visibleLines * lineHeight;
2019-05-16 16:03:48 +00:00
addrArea.setHeight(height);
itemArea.setHeight(height);
asciiArea.setHeight(height);
}
void HexWidget::moveCursor(int offset, bool select)
{
BasicCursor addr = cursor.address;
addr += offset;
if (addr.address > data->maxIndex()) {
addr.address = data->maxIndex();
}
setCursorAddr(addr, select);
}
void HexWidget::setCursorAddr(BasicCursor addr, bool select)
{
if (!select) {
bool clearingSelection = !selection.isEmpty();
selection.init(addr);
if (clearingSelection)
emit selectionChanged(getSelection());
}
emit positionChanged(addr.address);
cursor.address = addr.address;
/* Pause cursor repainting */
cursorEnabled = false;
if (select) {
selection.update(addr);
emit selectionChanged(getSelection());
}
uint64_t addressValue = cursor.address;
/* Update data cache if necessary */
if (!(addressValue >= startAddress && addressValue <= lastVisibleAddr())) {
/* Align start address */
addressValue -= (addressValue % itemRowByteLen());
/* FIXME: handling Page Up/Down */
if (addressValue == startAddress + bytesPerScreen()) {
startAddress += itemRowByteLen();
} else {
startAddress = addressValue;
}
fetchData();
if (startAddress > (data->maxIndex() - bytesPerScreen()) + 1) {
startAddress = (data->maxIndex() - bytesPerScreen()) + 1;
}
2019-05-16 16:03:48 +00:00
}
updateCursorMeta();
/* Draw cursor */
cursor.isVisible = !select;
viewport()->update();
/* Resume cursor repainting */
cursorEnabled = selection.isEmpty();
}
void HexWidget::updateCursorMeta()
{
QPointF point;
QPointF pointAscii;
2019-05-16 16:03:48 +00:00
int offset = cursor.address - startAddress;
int itemOffset = offset;
int asciiOffset;
/* Calc common Y coordinate */
point.ry() = (itemOffset / itemRowByteLen()) * lineHeight;
pointAscii.setY(point.y());
itemOffset %= itemRowByteLen();
asciiOffset = itemOffset;
/* Calc X coordinate on the item area */
point.rx() = (itemOffset / itemGroupByteLen()) * columnExWidth();
itemOffset %= itemGroupByteLen();
point.rx() += (itemOffset / itemByteLen) * itemWidth();
/* Calc X coordinate on the ascii area */
pointAscii.rx() = asciiOffset * charWidth;
point += itemArea.topLeft();
pointAscii += asciiArea.topLeft();
cursor.screenPos.moveTopLeft(cursorOnAscii ? pointAscii : point);
shadowCursor.screenPos.moveTopLeft(cursorOnAscii ? point : pointAscii);
}
void HexWidget::setCursorOnAscii(bool ascii)
{
cursorOnAscii = ascii;
}
const QColor HexWidget::itemColor(uint8_t byte)
{
QColor color(defColor);
if (byte == 0x00)
color = b0x00Color;
else if (byte == 0x7f)
color = b0x7fColor;
else if (byte == 0xff)
color = b0xffColor;
else if (IS_PRINTABLE(byte)) {
color = printableColor;
}
return color;
}
template<class T>
static T fromBigEndian(const void * src)
{
#if QT_VERSION >= QT_VERSION_CHECK(5, 12, 0)
return qFromBigEndian<T>(src);
#else
T result;
memcpy(&result, src, sizeof(T));
return qFromBigEndian<T>(result);
#endif
}
template<class T>
static T fromLittleEndian(const void * src)
{
#if QT_VERSION >= QT_VERSION_CHECK(5, 12, 0)
return qFromLittleEndian<T>(src);
#else
T result;
memcpy(&result, src, sizeof(T));
return qFromLittleEndian<T>(result);
#endif
}
2019-05-16 16:03:48 +00:00
QVariant HexWidget::readItem(int offset, QColor *color)
{
quint8 byte;
quint16 word;
quint32 dword;
quint64 qword;
float float32;
double float64;
2019-05-16 16:03:48 +00:00
quint8 bytes[sizeof(uint64_t)];
data->copy(bytes, startAddress + offset, static_cast<size_t>(itemByteLen));
2019-05-16 16:03:48 +00:00
const bool signedItem = itemFormat == ItemFormatSignedDec;
if (color) {
*color = defColor;
}
2019-05-16 16:03:48 +00:00
switch (itemByteLen) {
case 1:
byte = bytes[0];
2019-05-16 16:03:48 +00:00
if (color)
*color = itemColor(byte);
if (!signedItem)
return QVariant(static_cast<quint64>(byte));
return QVariant(static_cast<qint64>(static_cast<qint8>(byte)));
case 2:
if (itemBigEndian)
word = fromBigEndian<quint16>(bytes);
2019-05-16 16:03:48 +00:00
else
word = fromLittleEndian<quint16>(bytes);
2019-05-16 16:03:48 +00:00
if (!signedItem)
return QVariant(static_cast<quint64>(word));
return QVariant(static_cast<qint64>(static_cast<qint16>(word)));
case 4:
if (itemBigEndian)
dword = fromBigEndian<quint32>(bytes);
2019-05-16 16:03:48 +00:00
else
dword = fromLittleEndian<quint32>(bytes);
2019-05-16 16:03:48 +00:00
if (itemFormat == ItemFormatFloat) {
memcpy(&float32, &dword, sizeof(float32));
return QVariant(float32);
2019-05-16 16:03:48 +00:00
}
if (!signedItem)
return QVariant(static_cast<quint64>(dword));
return QVariant(static_cast<qint64>(static_cast<qint32>(dword)));
case 8:
if (itemBigEndian)
qword = fromBigEndian<quint64>(bytes);
2019-05-16 16:03:48 +00:00
else
qword = fromLittleEndian<quint64>(bytes);
2019-05-16 16:03:48 +00:00
if (itemFormat == ItemFormatFloat) {
memcpy(&float64, &qword, sizeof(float64));
return QVariant(float64);
2019-05-16 16:03:48 +00:00
}
if (!signedItem)
return QVariant(qword);
return QVariant(static_cast<qint64>(qword));
}
return QVariant();
}
QString HexWidget::renderItem(int offset, QColor *color)
{
QString item;
QVariant itemVal = readItem(offset, color);
int itemLen = itemCharLen - itemPrefixLen; /* Reserve space for prefix */
//FIXME: handle broken itemVal ( QVariant() )
switch (itemFormat) {
case ItemFormatHex:
item = QString("%1").arg(itemVal.toULongLong(), itemLen, 16, QLatin1Char('0'));
if (itemByteLen > 1 && showExHex)
item.prepend(hexPrefix);
break;
case ItemFormatOct:
item = QString("%1").arg(itemVal.toULongLong(), itemLen, 8, QLatin1Char('0'));
break;
case ItemFormatDec:
item = QString("%1").arg(itemVal.toULongLong(), itemLen, 10);
break;
case ItemFormatSignedDec:
item = QString("%1").arg(itemVal.toLongLong(), itemLen, 10);
break;
case ItemFormatFloat:
item = QString("%1").arg(itemVal.toDouble(), itemLen);
break;
}
return item;
}
QChar HexWidget::renderAscii(int offset, QColor *color)
{
uchar byte;
data->copy(&byte, startAddress + offset, sizeof(byte));
2019-05-16 16:03:48 +00:00
if (color) {
*color = itemColor(byte);
}
if (!IS_PRINTABLE(byte)) {
byte = '.';
}
return QChar(byte);
}
/**
* @brief Gets the available flags and comment at a specific address.
* @param address Address of Item to be checked.
* @return String containing the flags and comment available at the address.
*/
QString HexWidget::getFlagsAndComment(uint64_t address)
{
QString flagNames = Core()->listFlagsAsStringAt(address);
QString metaData = flagNames.isEmpty() ? "" : "Flags: " + flagNames.trimmed();
QString comment = Core()->getCommentAt(address);
if (!comment.isEmpty()) {
if (!metaData.isEmpty()) {
metaData.append("\n");
}
metaData.append("Comment: " + comment.trimmed());
}
return metaData;
}
2019-05-16 16:03:48 +00:00
void HexWidget::fetchData()
{
data.swap(oldData);
2019-05-16 16:03:48 +00:00
data->fetch(startAddress, bytesPerScreen());
}
BasicCursor HexWidget::screenPosToAddr(const QPoint &point, bool middle) const
{
QPointF pt = point - itemArea.topLeft();
2019-05-16 16:03:48 +00:00
int relativeAddress = 0;
int line = static_cast<int>(pt.y() / lineHeight);
relativeAddress += line * itemRowByteLen();
int column = static_cast<int>(pt.x() / columnExWidth());
relativeAddress += column * itemGroupByteLen();
pt.rx() -= column * columnExWidth();
2019-05-16 16:03:48 +00:00
auto roundingOffset = middle ? itemWidth() / 2 : 0;
relativeAddress += static_cast<int>((pt.x() + roundingOffset) / itemWidth()) * itemByteLen;
2019-05-16 16:03:48 +00:00
BasicCursor result(startAddress);
result += relativeAddress;
return result;
}
BasicCursor HexWidget::asciiPosToAddr(const QPoint &point, bool middle) const
{
QPointF pt = point - asciiArea.topLeft();
2019-05-16 16:03:48 +00:00
int relativeAddress = 0;
relativeAddress += static_cast<int>(pt.y() / lineHeight) * itemRowByteLen();
2019-05-16 16:03:48 +00:00
auto roundingOffset = middle ? (charWidth / 2) : 0;
relativeAddress += static_cast<int>((pt.x() + (roundingOffset)) / charWidth);
2019-05-16 16:03:48 +00:00
BasicCursor result(startAddress);
result += relativeAddress;
return result;
}
BasicCursor HexWidget::currentAreaPosToAddr(const QPoint &point, bool middle) const
{
return cursorOnAscii ? asciiPosToAddr(point, middle) : screenPosToAddr(point, middle);
}
BasicCursor HexWidget::mousePosToAddr(const QPoint &point, bool middle) const
{
return asciiArea.contains(point) ? asciiPosToAddr(point, middle) : screenPosToAddr(point, middle);
}
QRectF HexWidget::itemRectangle(int offset)
2019-05-16 16:03:48 +00:00
{
qreal x;
qreal y;
2019-05-16 16:03:48 +00:00
qreal width = itemWidth();
2019-05-16 16:03:48 +00:00
y = (offset / itemRowByteLen()) * lineHeight;
offset %= itemRowByteLen();
x = (offset / itemGroupByteLen()) * columnExWidth();
offset %= itemGroupByteLen();
x += (offset / itemByteLen) * itemWidth();
if (offset == 0) {
x -= charWidth / 2;
width += charWidth / 2;
}
2019-07-09 18:55:07 +00:00
if (static_cast<int>(offset) == itemGroupByteLen() - 1) {
width += charWidth / 2;
}
2019-05-16 16:03:48 +00:00
x += itemArea.x();
y += itemArea.y();
return QRectF(x, y, width, lineHeight);
2019-05-16 16:03:48 +00:00
}
QRectF HexWidget::asciiRectangle(int offset)
2019-05-16 16:03:48 +00:00
{
QPointF p;
2019-05-16 16:03:48 +00:00
p.ry() = (offset / itemRowByteLen()) * lineHeight;
2019-05-16 16:03:48 +00:00
offset %= itemRowByteLen();
p.rx() = offset * charWidth;
2019-05-16 16:03:48 +00:00
p += asciiArea.topLeft();
2019-05-16 16:03:48 +00:00
return QRectF(p, QSizeF(charWidth, lineHeight));
2019-05-16 16:03:48 +00:00
}
RVA HexWidget::getLocationAddress() {
return !selection.isEmpty() ? selection.start() : cursor.address;
}