#include #include #include "byte.h" #include "fontsettings.h" #include "theme.h" static const char* g_byteMapping[] = {" ", "☺", "☻", "♥", "♦", "♣", "♠", "•", "◘", "○", "◙", "♂", "♀", "♪", "♫", "☼", "▸", "◂", "↕", "‼", "¶", "§", "▬", "↨", "↑", "↓", "→", "←", "∟", "↔", "▴", "▾", " ", "!", "\"", "#", "$", "%", "&", "'", "(", ")", "*", "+", ",", "-", ".", "/", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", ":", ";", "<", "=", ">", "?", "@", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "[", "\\", "]", "^", "_", "`", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "{", "|", "}", "~", "⌂", "Ç", "ü", "é", "â", "ä", "à", "å", "ç", "ê", "ë", "è", "ï", "î", "ì", "Ä", "Å", "É", "æ", "Æ", "ô", "ö", "ò", "û", "ù", "ÿ", "Ö", "Ü", "¢", "£", "¥", "₧", "ƒ", "á", "í", "ó", "ú", "ñ", "Ñ", "ª", "º", "¿", "⌐", "¬", "½", "¼", "¡", "«", "»", "░", "▒", "▓", "│", "┤", "╡", "╢", "╖", "╕", "╣", "║", "╗", "╝", "╜", "╛", "┐", "└", "┴", "┬", "├", "─", "┼", "╞", "╟", "╚", "╔", "╩", "╦", "╠", "═", "╬", "╧", "╨", "╤", "╥", "╙", "╘", "╒", "╓", "╫", "╪", "┘", "┌", "█", "▄", "▌", "▐", "▀", "α", "ß", "Γ", "π", "Σ", "σ", "µ", "τ", "Φ", "Θ", "Ω", "δ", "∞", "φ", "ε", "∩", "≡", "±", "≥", "≤", "⌠", "⌡", "÷", "≈", "°", "∙", "·", "√", "ⁿ", "²", "■", " "}; ByteView::ByteView(QWidget* parent, BinaryViewRef data) : QAbstractScrollArea(parent), m_render(this) { setBinaryDataNavigable(true); setupView(this); m_data = data; setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn); setFocusPolicy(Qt::StrongFocus); m_cursorAddr = m_data->GetStart(); m_prevCursorAddr = m_cursorAddr; m_selectionStartAddr = m_cursorAddr; m_topAddr = m_cursorAddr; m_topLine = 0; m_selectionVisible = false; m_caretVisible = false; m_caretBlink = true; m_cols = 128; m_updatesRequired = false; m_visibleRows = 1; updateRanges(); QSize areaSize = viewport()->size(); adjustSize(areaSize.width(), areaSize.height()); if (m_allocatedLength > 0x7fffffff) m_scrollBarMultiplier = (m_allocatedLength / 0x7fffffff) + 1; else m_scrollBarMultiplier = 1; m_wheelDelta = 0; m_updatingScrollBar = false; verticalScrollBar()->setRange(0, (int)((m_allocatedLength - 1) / m_scrollBarMultiplier)); connect(verticalScrollBar(), &QScrollBar::sliderMoved, this, &ByteView::scrollBarMoved); connect(verticalScrollBar(), &QScrollBar::actionTriggered, this, &ByteView::scrollBarAction); m_cursorTimer = new QTimer(this); m_cursorTimer->setInterval(500); m_cursorTimer->setSingleShot(false); connect(m_cursorTimer, &QTimer::timeout, this, &ByteView::cursorTimerEvent); m_cursorTimer->start(); m_updateTimer = new QTimer(this); m_updateTimer->setInterval(200); m_updateTimer->setSingleShot(false); // connect(m_updateTimer, &QTimer::timeout, this, &ByteView::updateTimerEvent); actionHandler()->bindAction("Move Cursor Up", UIAction([=, this]() { up(false); })); actionHandler()->bindAction("Move Cursor Down", UIAction([=, this]() { down(false); })); actionHandler()->bindAction("Move Cursor Left", UIAction([=, this]() { left(1, false); })); actionHandler()->bindAction("Move Cursor Right", UIAction([=, this]() { right(1, false); })); actionHandler()->bindAction("Move Cursor Word Left", UIAction([=, this]() { left(8, false); })); actionHandler()->bindAction("Move Cursor Word Right", UIAction([=, this]() { right(8, false); })); actionHandler()->bindAction("Extend Selection Up", UIAction([=, this]() { up(true); })); actionHandler()->bindAction("Extend Selection Down", UIAction([=, this]() { down(true); })); actionHandler()->bindAction("Extend Selection Left", UIAction([=, this]() { left(1, true); })); actionHandler()->bindAction("Extend Selection Right", UIAction([=, this]() { right(1, true); })); actionHandler()->bindAction("Extend Selection Word Left", UIAction([=, this]() { left(8, true); })); actionHandler()->bindAction("Extend Selection Word Right", UIAction([=, this]() { right(8, true); })); actionHandler()->bindAction("Page Up", UIAction([=, this]() { pageUp(false); })); actionHandler()->bindAction("Page Down", UIAction([=, this]() { pageDown(false); })); actionHandler()->bindAction("Extend Selection Page Up", UIAction([=, this]() { pageUp(true); })); actionHandler()->bindAction("Extend Selection Page Down", UIAction([=, this]() { pageDown(true); })); actionHandler()->bindAction("Move Cursor to Start of Line", UIAction([=, this]() { moveToStartOfLine(false); })); actionHandler()->bindAction("Move Cursor to End of Line", UIAction([=, this]() { moveToEndOfLine(false); })); actionHandler()->bindAction("Move Cursor to Start of View", UIAction([=, this]() { moveToStartOfView(false); })); actionHandler()->bindAction("Move Cursor to End of View", UIAction([=, this]() { moveToEndOfView(false); })); actionHandler()->bindAction("Extend Selection to Start of Line", UIAction([=, this]() { moveToStartOfLine(true); })); actionHandler()->bindAction("Extend Selection to End of Line", UIAction([=, this]() { moveToEndOfLine(true); })); actionHandler()->bindAction("Extend Selection to Start of View", UIAction([=, this]() { moveToStartOfView(true); })); actionHandler()->bindAction("Extend Selection to End of View", UIAction([=, this]() { moveToEndOfView(true); })); } BinaryViewRef ByteView::getData() { return m_data; } QFont ByteView::getFont() { QFont userFont = getMonospaceFont(this); #ifdef Q_OS_MACOS // Some fonts aren't fixed width across all characters, use a known good one QFont font("Menlo", userFont.pointSize()); font.setKerning(false); #else QFont font = userFont; #endif return font; } uint64_t ByteView::getStart() { return m_data->GetStart(); } uint64_t ByteView::getEnd() { return m_data->GetEnd(); } uint64_t ByteView::getLength() { return getEnd() - getStart(); } uint64_t ByteView::getCurrentOffset() { return m_cursorAddr; } BNAddressRange ByteView::getSelectionOffsets() { uint64_t start = m_selectionStartAddr; uint64_t end = m_cursorAddr; if (end < start) { uint64_t t = start; start = end; end = t; } return {start, end}; } void ByteView::setSelectionOffsets(BNAddressRange range) { navigate(range.start); m_selectionStartAddr = range.start; m_cursorAddr = range.end; } void ByteView::updateRanges() { m_ranges = m_data->GetBackedAddressRanges(); m_allocatedLength = 0; for (auto& i : m_ranges) m_allocatedLength += i.end - i.start; } void ByteView::setTopToAddress(uint64_t addr) { for (auto& i : m_ranges) { if ((addr >= i.start) && (addr <= i.end)) { m_topAddr = addr - ((addr - i.start) % (uint64_t)m_cols); if (m_topAddr < i.start) m_topAddr = i.start; return; } if (i.start > addr) { m_topAddr = i.start; return; } } m_topAddr = m_data->GetEnd(); } bool ByteView::navigate(uint64_t addr) { if (addr < getStart()) return false; if (addr > getEnd()) return false; m_cursorAddr = getStart(); for (auto& i : m_ranges) { if (i.start > addr) break; if (addr > i.end) m_cursorAddr = i.end; else if (addr >= i.start) m_cursorAddr = addr; else m_cursorAddr = i.start; } setTopToAddress(m_cursorAddr); refreshLines(); showContextAroundTop(); selectNone(); repositionCaret(); return true; } void ByteView::updateFonts() { QSize areaSize = viewport()->size(); adjustSize(areaSize.width(), areaSize.height()); } void ByteView::adjustSize(int width, int height) { m_render.setFont(getFont()); m_addrWidth = QString::number(m_data->GetEnd(), 16).size(); if (m_addrWidth < 8) m_addrWidth = 8; int cols = ((width - 4) / m_render.getFontWidth()) - ((int)m_addrWidth + 2); if (cols < 1) cols = 1; if ((size_t)cols != m_cols) { m_cols = (size_t)cols; if (m_topLine < m_lines.size()) setTopToAddress(m_lines[m_topLine].address); else setTopToAddress(m_cursorAddr); refreshLines(); } m_visibleRows = (size_t)((height - 4) / m_render.getFontHeight()); verticalScrollBar()->setPageStep((int)(m_visibleRows * m_cols / m_scrollBarMultiplier)); refreshAtCurrentLocation(); viewport()->update(); } uint64_t ByteView::getContiguousOffsetForAddress(uint64_t addr) { uint64_t offset = 0; for (auto& i : m_ranges) { if ((addr >= i.start) && (addr <= i.end)) { offset += addr - i.start; break; } offset += i.end - i.start; } return offset; } uint64_t ByteView::getAddressForContiguousOffset(uint64_t offset) { uint64_t cur = 0; for (auto& i : m_ranges) { if (offset < (cur + (i.end - i.start))) return i.start + (offset - cur); cur += i.end - i.start; } return m_data->GetEnd(); } void ByteView::refreshLines() { uint64_t addr = m_topAddr; m_lines.clear(); m_topLine = 0; m_bottomAddr = m_topAddr; updateRanges(); if (m_allocatedLength > 0x7fffffff) m_scrollBarMultiplier = (m_allocatedLength / 0x7fffffff) + 1; else m_scrollBarMultiplier = 1; m_updatingScrollBar = true; verticalScrollBar()->setRange(0, (int)((m_allocatedLength - 1) / m_scrollBarMultiplier)); verticalScrollBar()->setValue((int)(getContiguousOffsetForAddress(addr) / m_scrollBarMultiplier)); m_updatingScrollBar = false; updateCache(); viewport()->update(); UIContext::updateStatus(); } void ByteView::refreshAtCurrentLocation() { if (m_topLine < m_lines.size()) m_topAddr = m_lines[m_topLine].address; refreshLines(); } ByteViewLine ByteView::createLine(uint64_t addr, size_t length, bool separator) { if (separator) { return ByteViewLine {addr, length, "", true}; } else { BinaryNinja::DataBuffer data = m_data->ReadBuffer(addr, length); QString line; for (size_t i = 0; i < data.GetLength(); i++) line.append(QString(g_byteMapping[data[i]])); return ByteViewLine {addr, length, line, false}; } } bool ByteView::cachePreviousLines() { bool prevEndValid = false; uint64_t prevEnd = 0; for (auto& i : m_ranges) { if ((m_topAddr > i.start) && (m_topAddr <= i.end)) { uint64_t startLine = m_topAddr - ((m_topAddr - i.start) % m_cols); if (startLine == m_topAddr) startLine -= m_cols; if (startLine < i.start) startLine = i.start; ByteViewLine line = createLine(startLine, m_topAddr - startLine, false); m_lines.insert(m_lines.begin(), line); m_topLine++; m_topAddr = startLine; return true; } else if (i.start >= m_topAddr) { if (!prevEndValid) return false; ByteViewLine line = createLine(prevEnd, i.start - prevEnd, true); m_lines.insert(m_lines.begin(), line); m_topLine++; m_topAddr = prevEnd; return true; } prevEnd = i.end; prevEndValid = true; } if (!prevEndValid) return false; ByteViewLine line = createLine(prevEnd, m_topAddr - prevEnd, true); m_lines.insert(m_lines.begin(), line); m_topLine++; m_topAddr = prevEnd; return true; } bool ByteView::cacheNextLines() { uint64_t lastAddr = m_data->GetStart(); for (auto& i : m_ranges) { if ((m_bottomAddr >= i.start) && (m_bottomAddr < i.end)) { uint64_t endLine = m_bottomAddr + m_cols; if (endLine > i.end) endLine = i.end; ByteViewLine line = createLine(m_bottomAddr, endLine - m_bottomAddr, false); m_lines.push_back(line); m_bottomAddr = endLine; return true; } else if (i.start > m_bottomAddr) { ByteViewLine line = createLine(m_bottomAddr, i.start - m_bottomAddr, true); m_lines.push_back(line); m_bottomAddr = i.start; return true; } lastAddr = i.end; } if (m_bottomAddr == lastAddr) { // Ensure there is a place for the cursor at the end of the file if ((m_lines.size() > 0) && (m_lines[m_lines.size() - 1].length != m_cols)) return false; ByteViewLine line = createLine(lastAddr, 0, false); m_lines.push_back(line); m_bottomAddr++; return true; } return false; } void ByteView::updateCache() { // Cache enough for the current page and the next page while ((m_lines.size() - m_topLine) <= (m_visibleRows * 2)) { if (!cacheNextLines()) break; } // Cache enough for the previous page while (m_topLine <= m_visibleRows) { if (!cachePreviousLines()) break; } // Trim cache if (m_topLine > (m_visibleRows * 4)) { m_lines.erase(m_lines.begin(), m_lines.begin() + (m_topLine - (m_visibleRows * 4))); m_topLine = m_visibleRows * 4; m_topAddr = m_lines[0].address; } if ((m_lines.size() - m_topLine) > (m_visibleRows * 5)) { m_bottomAddr = m_lines[m_topLine + (m_visibleRows * 5)].address; m_lines.erase(m_lines.begin() + (m_topLine + (m_visibleRows * 5)), m_lines.end()); } } void ByteView::scrollLines(int count) { int newOffset = (int)m_topLine + count; if (newOffset < 0) m_topLine = 0; else if (newOffset >= (int)m_lines.size()) m_topLine = m_lines.size() - 1; else m_topLine = newOffset; updateCache(); viewport()->update(); if (m_topLine < m_lines.size()) { m_updatingScrollBar = true; uint64_t addr = m_lines[m_topLine].address; verticalScrollBar()->setValue((int)(getContiguousOffsetForAddress(addr) / m_scrollBarMultiplier)); m_updatingScrollBar = false; } } void ByteView::showContextAroundTop() { size_t scroll = m_visibleRows / 4; if (scroll > m_topLine) m_topLine = 0; else m_topLine -= scroll; if (m_topLine < m_lines.size()) { m_updatingScrollBar = true; uint64_t addr = m_lines[m_topLine].address; verticalScrollBar()->setValue((int)(getContiguousOffsetForAddress(addr) / m_scrollBarMultiplier)); m_updatingScrollBar = false; } updateCache(); } void ByteView::repositionCaret() { updateCache(); bool found = false; for (size_t i = 0; i < m_lines.size(); i++) { if (((m_cursorAddr >= m_lines[i].address) && (m_cursorAddr < (m_lines[i].address + m_lines[i].length))) || (((i + 1) == m_lines.size()) && (m_cursorAddr == (m_lines[i].address + m_lines[i].length)))) { if (i < m_topLine) m_topLine = i; else if (i > (m_topLine + m_visibleRows - 1)) m_topLine = i - (m_visibleRows - 1); m_updatingScrollBar = true; uint64_t addr = m_lines[m_topLine].address; verticalScrollBar()->setValue((int)(getContiguousOffsetForAddress(addr) / m_scrollBarMultiplier)); m_updatingScrollBar = false; updateCache(); viewport()->update(); found = true; break; } } if (!found) { setTopToAddress(m_cursorAddr); refreshLines(); showContextAroundTop(); } // Force caret to be visible and repaint m_caretBlink = true; m_cursorTimer->stop(); m_cursorTimer->start(); updateCaret(); UIContext::updateStatus(); updateCrossReferenceSelection(); } void ByteView::updateCaret() { // Rerender both the old caret position and the new caret position for (size_t i = m_topLine; (i < m_lines.size()) && (i < (m_topLine + m_visibleRows)); i++) { if (((m_prevCursorAddr >= m_lines[i].address) && (m_prevCursorAddr <= (m_lines[i].address + m_lines[i].length))) || ((m_cursorAddr >= m_lines[i].address) && (m_cursorAddr <= (m_lines[i].address + m_lines[i].length)))) { viewport()->update(0, (int)(i - m_topLine) * m_render.getFontHeight(), viewport()->size().width(), m_render.getFontHeight() + 3); } } } void ByteView::resizeEvent(QResizeEvent* event) { adjustSize(event->size().width(), event->size().height()); } void ByteView::paintEvent(QPaintEvent* event) { QPainter p(viewport()); m_render.init(p); int charWidth = m_render.getFontWidth(); int charHeight = m_render.getFontHeight(); // Compute range that needs to be updated int topY = event->rect().y(); int botY = topY + event->rect().height(); topY = (topY - 2) / charHeight; botY = ((botY - 2) / charHeight) + 1; // Compute selection range bool selection = false; BNAddressRange selectionRange = getSelectionOffsets(); if (selectionRange.start != selectionRange.end) selection = true; // Draw selection if (selection) { bool startValid = false; bool endValid = false; int startY = 0; int endY = 0; int startX = 0; int endX = 0; for (size_t i = 0; i < m_lines.size(); i++) { if (selectionRange.start >= m_lines[i].address) { startY = (int)(i - m_topLine); startX = (int)(selectionRange.start - m_lines[i].address); if (startX > (int)m_cols) startX = (int)m_cols; startValid = true; } if (selectionRange.end >= m_lines[i].address) { endY = (int)(i - m_topLine); endX = (int)(selectionRange.end - m_lines[i].address); if (endX > (int)m_cols) endX = (int)m_cols; endValid = true; } } if (startValid && endValid) { p.setPen(getThemeColor(SelectionColor)); p.setBrush(getThemeColor(SelectionColor)); if (startY == endY) { p.drawRect(2 + ((int)m_addrWidth + 2 + startX) * charWidth, 2 + startY * charHeight, (endX - startX) * charWidth, charHeight + 1); } else { p.drawRect(2 + ((int)m_addrWidth + 2 + startX) * charWidth, 2 + startY * charHeight, ((int)m_cols - startX) * charWidth, charHeight + 1); if (endX > 0) { p.drawRect(2 + ((int)m_addrWidth + 2) * charWidth, 2 + endY * charHeight, endX * charWidth, charHeight + 1); } } if ((endY - startY) > 1) { p.drawRect(2 + ((int)m_addrWidth + 2) * charWidth, 2 + (startY + 1) * charHeight, (int)m_cols * charWidth, ((endY - startY) - 1) * charHeight + 1); } } } // Paint each line QColor color = palette().color(QPalette::WindowText); for (int y = topY; y < botY; y++) { if ((y + (int)m_topLine) < 0) continue; if ((y + (int)m_topLine) >= (int)m_lines.size()) break; if (m_lines[y + m_topLine].separator) { QRect lineRect = QRect(0, 2 + y * charHeight, event->rect().width(), charHeight); m_render.drawLinearDisassemblyLineBackground(p, NonContiguousSeparatorLineType, lineRect, lineRect, 0); continue; } uint64_t lineStartAddr = m_lines[y + m_topLine].address; QString addrStr = QString::number(lineStartAddr, 16).rightJustified(8, '0'); size_t length = m_lines[y + m_topLine].length; QString text = m_lines[y + m_topLine].text; bool hasCursor = false; int cursorCol = 0; if (((m_cursorAddr >= lineStartAddr) && (m_cursorAddr < (lineStartAddr + length))) || (((y + (int)m_topLine + 1) >= (int)m_lines.size()) && (m_cursorAddr == (lineStartAddr + length)))) { cursorCol = (int)(m_cursorAddr - lineStartAddr); hasCursor = true; } m_render.drawText(p, 2, 2 + y * charHeight, getThemeColor(AddressColor), addrStr); m_render.drawText(p, 2 + ((int)m_addrWidth + 2) * charWidth, 2 + y * charHeight, color, text); if (m_caretVisible && m_caretBlink && !selection && hasCursor) { p.setPen(Qt::NoPen); p.setBrush(palette().color(QPalette::WindowText)); p.drawRect( 2 + ((int)m_addrWidth + 2 + cursorCol) * charWidth, 2 + y * charHeight, charWidth, charHeight + 1); QColor caretTextColor = palette().color(QPalette::Base); BinaryNinja::DataBuffer byteValue = m_data->ReadBuffer(lineStartAddr + cursorCol, 1); if (byteValue.GetLength() == 1) { QString byteStr = g_byteMapping[byteValue[0]]; m_render.drawText( p, 2 + ((int)m_addrWidth + 2 + cursorCol) * charWidth, 2 + y * charHeight, caretTextColor, byteStr); } } } } void ByteView::wheelEvent(QWheelEvent* event) { if (event->angleDelta().x()) // ignore horizontal scrolling return; m_wheelDelta -= event->angleDelta().y(); if ((m_wheelDelta <= -40) || (m_wheelDelta >= 40)) { int lines = m_wheelDelta / 40; m_wheelDelta -= lines * 40; scrollLines(lines); } } void ByteView::scrollBarMoved(int value) { if (m_updatingScrollBar) return; m_wheelDelta = 0; uint64_t addr = getAddressForContiguousOffset((uint64_t)value * m_scrollBarMultiplier); setTopToAddress(addr); refreshLines(); if (m_lines.size() > 0) { for (size_t i = 1; i < m_lines.size() - 1; i++) { if ((m_lines[i].address + m_lines[i].length) > addr) { m_topLine = i - 1; break; } } } updateCache(); } void ByteView::scrollBarAction(int action) { switch (action) { case QAbstractSlider::SliderSingleStepAdd: m_wheelDelta = 0; scrollLines(1); break; case QAbstractSlider::SliderSingleStepSub: m_wheelDelta = 0; scrollLines(-1); break; case QAbstractSlider::SliderPageStepAdd: m_wheelDelta = 0; scrollLines((int)m_visibleRows); break; case QAbstractSlider::SliderPageStepSub: m_wheelDelta = 0; scrollLines(-(int)m_visibleRows); break; case QAbstractSlider::SliderToMinimum: m_wheelDelta = 0; setTopToAddress(getStart()); verticalScrollBar()->setValue((int)(getContiguousOffsetForAddress(m_topAddr) / m_scrollBarMultiplier)); refreshLines(); break; case QAbstractSlider::SliderToMaximum: m_wheelDelta = 0; setTopToAddress(getEnd()); verticalScrollBar()->setValue((int)(getContiguousOffsetForAddress(m_topAddr) / m_scrollBarMultiplier)); refreshLines(); break; default: break; } } void ByteView::cursorTimerEvent() { m_caretBlink = !m_caretBlink; updateCaret(); } void ByteView::focusInEvent(QFocusEvent*) { m_caretVisible = true; updateCaret(); } void ByteView::focusOutEvent(QFocusEvent*) { m_caretVisible = false; updateCaret(); } void ByteView::selectNone() { for (auto& i : m_lines) { if ((m_cursorAddr >= i.address) && (m_cursorAddr < (i.address + i.length)) && i.separator) { m_cursorAddr = i.address + i.length; break; } } m_selectionStartAddr = m_cursorAddr; if (m_selectionVisible) viewport()->update(); repositionCaret(); UIContext::updateStatus(); } void ByteView::selectAll() { m_selectionStartAddr = getStart(); m_cursorAddr = getEnd(); viewport()->update(); UIContext::updateStatus(); updateCrossReferenceSelection(); } void ByteView::adjustAddressAfterBackwardMovement() { uint64_t lastAddr = getStart(); for (auto& i : m_ranges) { if ((m_cursorAddr >= i.start) && (m_cursorAddr < i.end)) break; if (i.start > m_cursorAddr) { m_cursorAddr = lastAddr; break; } lastAddr = i.end - 1; } } void ByteView::adjustAddressAfterForwardMovement() { for (auto& i : m_ranges) { if ((m_cursorAddr >= i.start) && (m_cursorAddr < i.end)) break; if (i.start > m_cursorAddr) { m_cursorAddr = i.start; break; } } } void ByteView::left(int count, bool selecting) { if (m_cursorAddr > (getStart() + count)) m_cursorAddr -= count; else m_cursorAddr = getStart(); adjustAddressAfterBackwardMovement(); if (!selecting) selectNone(); repositionCaret(); if (m_selectionVisible || selecting) viewport()->update(); } void ByteView::right(int count, bool selecting) { if (m_cursorAddr <= (getEnd() - count)) m_cursorAddr += count; else m_cursorAddr = getEnd(); adjustAddressAfterForwardMovement(); if (!selecting) selectNone(); repositionCaret(); if (m_selectionVisible || selecting) viewport()->update(); } void ByteView::up(bool selecting) { left((int)m_cols, selecting); } void ByteView::down(bool selecting) { right((int)m_cols, selecting); } void ByteView::pageUp(bool selecting) { for (size_t i = 0; i < m_lines.size(); i++) { if (((m_cursorAddr >= m_lines[i].address) && (m_cursorAddr < (m_lines[i].address + m_lines[i].length))) || (((i + 1) == m_lines.size()) && (m_cursorAddr == (m_lines[i].address + m_lines[i].length)))) { if (i < m_visibleRows) { m_cursorAddr = getStart(); } else { uint64_t lineOfs = m_cursorAddr - m_lines[i].address; m_cursorAddr = m_lines[i - m_visibleRows].address + lineOfs; if (m_cursorAddr < m_lines[i - m_visibleRows].address) m_cursorAddr = m_lines[i - m_visibleRows].address; else if (m_cursorAddr >= (m_lines[i - m_visibleRows].address + m_lines[i - m_visibleRows].length)) m_cursorAddr = m_lines[i - m_visibleRows].address + m_lines[i - m_visibleRows].length - 1; break; } } } adjustAddressAfterBackwardMovement(); if (m_topLine > m_visibleRows) m_topLine -= m_visibleRows; else m_topLine = 0; if (m_topLine < m_lines.size()) { m_updatingScrollBar = true; uint64_t addr = m_lines[m_topLine].address; verticalScrollBar()->setValue((int)(getContiguousOffsetForAddress(addr) / m_scrollBarMultiplier)); m_updatingScrollBar = false; } if (!selecting) selectNone(); repositionCaret(); viewport()->update(); } void ByteView::pageDown(bool selecting) { for (size_t i = 0; i < m_lines.size(); i++) { if (((m_cursorAddr >= m_lines[i].address) && (m_cursorAddr < (m_lines[i].address + m_lines[i].length))) || (((i + 1) == m_lines.size()) && (m_cursorAddr == (m_lines[i].address + m_lines[i].length)))) { if (i >= (m_lines.size() - m_visibleRows)) { m_cursorAddr = getEnd(); } else { uint64_t lineOfs = m_cursorAddr - m_lines[i].address; m_cursorAddr = m_lines[i + m_visibleRows].address + lineOfs; if (m_cursorAddr < m_lines[i + m_visibleRows].address) m_cursorAddr = m_lines[i + m_visibleRows].address; else if (m_cursorAddr >= (m_lines[i + m_visibleRows].address + m_lines[i + m_visibleRows].length)) m_cursorAddr = m_lines[i + m_visibleRows].address + m_lines[i + m_visibleRows].length - 1; break; } } } adjustAddressAfterForwardMovement(); if ((m_topLine + m_visibleRows) < m_lines.size()) m_topLine += m_visibleRows; else if (m_lines.size() > 0) m_topLine = m_lines.size() - 1; if (m_topLine < m_lines.size()) { m_updatingScrollBar = true; uint64_t addr = m_lines[m_topLine].address; verticalScrollBar()->setValue((int)(getContiguousOffsetForAddress(addr) / m_scrollBarMultiplier)); m_updatingScrollBar = false; } if (!selecting) selectNone(); repositionCaret(); viewport()->update(); } void ByteView::moveToStartOfLine(bool selecting) { for (auto& i : m_lines) { if ((m_cursorAddr >= i.address) && (m_cursorAddr < (i.address + i.length))) { m_cursorAddr = i.address; break; } } if (!selecting) selectNone(); repositionCaret(); if (m_selectionVisible || selecting) viewport()->update(); } void ByteView::moveToEndOfLine(bool selecting) { for (auto& i : m_lines) { if ((m_cursorAddr >= i.address) && (m_cursorAddr < (i.address + i.length))) { m_cursorAddr = i.address + i.length - 1; break; } } if (!selecting) selectNone(); repositionCaret(); if (m_selectionVisible || selecting) viewport()->update(); } void ByteView::moveToStartOfView(bool selecting) { m_cursorAddr = getStart(); if (!selecting) selectNone(); repositionCaret(); if (m_selectionVisible || selecting) viewport()->update(); } void ByteView::moveToEndOfView(bool selecting) { m_cursorAddr = getEnd(); if (!selecting) selectNone(); repositionCaret(); if (m_selectionVisible || selecting) viewport()->update(); } uint64_t ByteView::addressFromLocation(int x, int y) { if (y < 0) y = 0; if (x < 0) x = 0; if (x > (int)m_cols) x = (int)m_cols; if ((y + (int)m_topLine) >= (int)m_lines.size()) return getEnd(); if (m_lines[y + m_topLine].separator) return m_lines[y + m_topLine].address - 1; uint64_t result = m_lines[y + m_topLine].address + x; if (result >= (m_lines[y + m_topLine].address + m_lines[y + m_topLine].length)) { if ((y + (int)m_topLine) == ((int)m_lines.size() - 1)) return getEnd(); else return m_lines[y + m_topLine].address + m_lines[y + m_topLine].length - 1; } return result; } void ByteView::mousePressEvent(QMouseEvent* event) { if (event->button() != Qt::LeftButton) return; int x = (event->pos().x() - 2) / m_render.getFontWidth() - ((int)m_addrWidth + 2); int y = (event->pos().y() - 2) / m_render.getFontHeight(); m_lastMouseX = x; m_lastMouseY = y; m_cursorAddr = addressFromLocation(x, y); if ((event->modifiers() & Qt::ShiftModifier) == 0) selectNone(); repositionCaret(); if ((event->modifiers() & Qt::ShiftModifier) != 0) viewport()->update(); } void ByteView::mouseMoveEvent(QMouseEvent* event) { if (event->buttons() != Qt::LeftButton) return; int x = (event->pos().x() - 2) / m_render.getFontWidth() - ((int)m_addrWidth + 2); int y = (event->pos().y() - 2) / m_render.getFontHeight(); if ((x == m_lastMouseX) && (y == m_lastMouseY)) return; m_lastMouseX = x; m_lastMouseY = y; m_cursorAddr = addressFromLocation(x, y); repositionCaret(); viewport()->update(); } ByteViewType::ByteViewType() : ViewType("Bytes", "Byte Overview") {} int ByteViewType::getPriority(BinaryViewRef, const QString&) { return 1; } QWidget* ByteViewType::create(BinaryViewRef data, ViewFrame* frame) { return new ByteView(frame, data); }