From c01e0b8dde7260ec4a9cfb590e57dee49c13ab91 Mon Sep 17 00:00:00 2001 From: Stenzek Date: Sat, 7 Sep 2024 13:19:01 +1000 Subject: [PATCH] Qt/Debugger: Highlight changed bytes in memory view --- src/duckstation-qt/debuggerwindow.cpp | 14 +++-- src/duckstation-qt/debuggerwindow.h | 1 + src/duckstation-qt/memoryviewwidget.cpp | 74 +++++++++++++++++++++++-- src/duckstation-qt/memoryviewwidget.h | 15 ++++- 4 files changed, 93 insertions(+), 11 deletions(-) diff --git a/src/duckstation-qt/debuggerwindow.cpp b/src/duckstation-qt/debuggerwindow.cpp index 13f44d7e7..211b6a869 100644 --- a/src/duckstation-qt/debuggerwindow.cpp +++ b/src/duckstation-qt/debuggerwindow.cpp @@ -95,7 +95,7 @@ void DebuggerWindow::onPauseActionToggled(bool paused) { if (!paused) { - m_registers_model->saveCurrentValues(); + saveCurrentState(); setUIEnabled(false, true); } @@ -203,7 +203,7 @@ void DebuggerWindow::onBreakpointListContextMenuRequested() void DebuggerWindow::onStepIntoActionTriggered() { Assert(System::IsPaused()); - m_registers_model->saveCurrentValues(); + saveCurrentState(); g_emu_thread->singleStepCPU(); } @@ -217,7 +217,7 @@ void DebuggerWindow::onStepOverActionTriggered() } // unpause to let it run to the breakpoint - m_registers_model->saveCurrentValues(); + saveCurrentState(); g_emu_thread->setSystemPaused(false); } @@ -231,7 +231,7 @@ void DebuggerWindow::onStepOutActionTriggered() } // unpause to let it run to the breakpoint - m_registers_model->saveCurrentValues(); + saveCurrentState(); g_emu_thread->setSystemPaused(false); } @@ -538,6 +538,12 @@ void DebuggerWindow::setUIEnabled(bool enabled, bool allow_pause) m_ui.memoryRegionBIOS->setEnabled(enabled); } +void DebuggerWindow::saveCurrentState() +{ + m_registers_model->saveCurrentValues(); + m_ui.memoryView->saveCurrentData(); +} + void DebuggerWindow::setMemoryViewRegion(Bus::MemoryRegion region) { if (m_active_memory_region == region) diff --git a/src/duckstation-qt/debuggerwindow.h b/src/duckstation-qt/debuggerwindow.h index 31525fff6..c1f7fc007 100644 --- a/src/duckstation-qt/debuggerwindow.h +++ b/src/duckstation-qt/debuggerwindow.h @@ -70,6 +70,7 @@ private: void disconnectSignals(); void createModels(); void setUIEnabled(bool enabled, bool allow_pause); + void saveCurrentState(); void setMemoryViewRegion(Bus::MemoryRegion region); void toggleBreakpoint(VirtualMemoryAddress address); void clearBreakpoints(); diff --git a/src/duckstation-qt/memoryviewwidget.cpp b/src/duckstation-qt/memoryviewwidget.cpp index 9d097c614..8621982d1 100644 --- a/src/duckstation-qt/memoryviewwidget.cpp +++ b/src/duckstation-qt/memoryviewwidget.cpp @@ -53,7 +53,10 @@ void MemoryViewWidget::setData(size_t address_offset, void* data_ptr, size_t dat m_data_editable = data_editable; m_address_offset = address_offset; m_selected_address = INVALID_SELECTED_ADDRESS; + m_last_data_start_offset = 0; + m_last_data.clear(); adjustContent(); + saveCurrentData(); } void MemoryViewWidget::setHighlightRange(size_t start, size_t end) @@ -133,6 +136,7 @@ void MemoryViewWidget::keyPressEvent(QKeyEvent* event) const char ch = key_text[0].toLatin1(); if (m_selection_was_ascii) { + expandCurrentDataToInclude(m_selected_address); std::memcpy(static_cast(m_data) + m_selected_address, &ch, sizeof(unsigned char)); m_selected_address = std::min(m_selected_address + 1, m_data_size - 1); viewport()->update(); @@ -150,6 +154,8 @@ void MemoryViewWidget::keyPressEvent(QKeyEvent* event) { m_editing_nibble++; + expandCurrentDataToInclude(m_selected_address); + unsigned char* pdata = static_cast(m_data) + m_selected_address; *pdata = (*pdata & ~(0xf0 >> (m_editing_nibble * 4))) | (nibble << ((1 - m_editing_nibble) * 4)); @@ -185,7 +191,7 @@ void MemoryViewWidget::paintEvent(QPaintEvent* event) const QColor highlight_color(100, 100, 0); const QColor selected_color = viewport()->palette().color(QPalette::Highlight); const QColor text_color = viewport()->palette().color(QPalette::WindowText); - const QColor edited_color(255, 0, 0); + const QColor edited_color(240, 30, 30); const int offsetX = horizontalScrollBar()->value(); int y = m_char_height; @@ -252,7 +258,18 @@ void MemoryViewWidget::paintEvent(QPaintEvent* event) if (m_selected_address != offset || m_editing_nibble != 0 || m_selection_was_ascii) [[likely]] { - painter.drawText(x, y, QString::asprintf("%02X", value)); + const QString text = QString::asprintf("%02X", value); + const size_t view_offset = offset - m_last_data_start_offset; // may underflow, but unsigned so it's okay + if (view_offset < m_last_data.size() && value != m_last_data[view_offset]) + { + painter.setPen(edited_color); + painter.drawText(x, y, text); + painter.setPen(text_color); + } + else + { + painter.drawText(x, y, text); + } } else { @@ -298,9 +315,18 @@ void MemoryViewWidget::paintEvent(QPaintEvent* event) else if (offset >= m_highlight_start && offset < m_highlight_end) painter.fillRect(x, y - m_char_height + 4, 2 * m_char_width, m_char_height, highlight_color); - if (!std::isprint(value)) - value = '.'; - painter.drawText(x, y, static_cast(value)); + const QChar print_char = std::isprint(value) ? static_cast(value) : static_cast('.'); + const size_t view_offset = offset - m_last_data_start_offset; // may underflow, but unsigned so it's okay + if (view_offset < m_last_data.size() && value != m_last_data[view_offset]) + { + painter.setPen(edited_color); + painter.drawText(x, y, print_char); + painter.setPen(text_color); + } + else + { + painter.drawText(x, y, print_char); + } x += 2 * m_char_width; } y += m_char_height; @@ -354,10 +380,48 @@ void MemoryViewWidget::updateSelectedByte(const QPoint& pos) { m_selected_address = new_selection; m_selection_was_ascii = new_ascii; + m_editing_nibble = -1; viewport()->update(); } } +void MemoryViewWidget::expandCurrentDataToInclude(size_t offset) +{ + offset = std::min(offset, m_data_size - 1); + if (offset < m_last_data_start_offset) + { + const size_t add_bytes = m_last_data_start_offset - offset; + const size_t old_size = m_last_data.size(); + m_last_data.resize(old_size + add_bytes); + std::memmove(&m_last_data[add_bytes], &m_last_data[0], old_size); + std::memcpy(&m_last_data[0], static_cast(m_data) + offset, add_bytes); + m_last_data_start_offset = offset; + } + else if (offset >= (m_last_data_start_offset + m_last_data.size())) + { + const size_t new_size = m_last_data_start_offset + offset + 1; + const size_t old_size = m_last_data.size(); + m_last_data.resize(new_size); + std::memcpy(&m_last_data[old_size], static_cast(m_data) + m_last_data_start_offset + old_size, + new_size - old_size); + } +} + +void MemoryViewWidget::saveCurrentData() +{ + if (!m_data) + { + m_last_data.clear(); + return; + } + + const size_t size = m_end_offset - m_start_offset; + m_last_data_start_offset = m_start_offset; + m_last_data.resize(size); + std::memcpy(m_last_data.data(), static_cast(m_data) + m_start_offset, size); + viewport()->update(); +} + void MemoryViewWidget::adjustContent() { if (!m_data) diff --git a/src/duckstation-qt/memoryviewwidget.h b/src/duckstation-qt/memoryviewwidget.h index f09f0e8f0..1791fc469 100644 --- a/src/duckstation-qt/memoryviewwidget.h +++ b/src/duckstation-qt/memoryviewwidget.h @@ -1,5 +1,9 @@ #pragma once + +#include "common/types.h" + #include +#include // Based on https://stackoverflow.com/questions/46375673/how-can-realize-my-own-memory-viewer-by-qt @@ -28,6 +32,9 @@ protected: void mouseMoveEvent(QMouseEvent* event); void keyPressEvent(QKeyEvent* event); +public Q_SLOTS: + void saveCurrentData(); + private Q_SLOTS: void adjustContent(); @@ -39,6 +46,7 @@ private: int asciiWidth() const; void updateMetrics(); void updateSelectedByte(const QPoint& pos); + void expandCurrentDataToInclude(size_t offset); void* m_data; size_t m_data_size; @@ -51,14 +59,17 @@ private: size_t m_highlight_end = 0; size_t m_selected_address = INVALID_SELECTED_ADDRESS; - int m_editing_nibble = -1; + s32 m_editing_nibble = -1; bool m_selection_was_ascii = false; bool m_data_editable = false; - unsigned m_bytes_per_line; + u32 m_bytes_per_line; int m_char_width; int m_char_height; int m_rows_visible; + + std::vector m_last_data; + size_t m_last_data_start_offset = 0; }; \ No newline at end of file