From 328a41dec7d2955fc9833854156430e6c21774eb Mon Sep 17 00:00:00 2001 From: Jeffrey Pfau Date: Tue, 28 Apr 2015 22:38:11 -0700 Subject: [PATCH] Qt: Rudimentary memory viewer --- src/gba/supervisor/thread.c | 1 + src/platform/qt/CMakeLists.txt | 3 ++ src/platform/qt/MemoryModel.cpp | 89 +++++++++++++++++++++++++++++++++ src/platform/qt/MemoryModel.h | 44 ++++++++++++++++ src/platform/qt/MemoryView.cpp | 18 +++++++ src/platform/qt/MemoryView.h | 31 ++++++++++++ src/platform/qt/MemoryView.ui | 32 ++++++++++++ src/platform/qt/Window.cpp | 11 ++++ src/platform/qt/Window.h | 1 + 9 files changed, 230 insertions(+) create mode 100644 src/platform/qt/MemoryModel.cpp create mode 100644 src/platform/qt/MemoryModel.h create mode 100644 src/platform/qt/MemoryView.cpp create mode 100644 src/platform/qt/MemoryView.h create mode 100644 src/platform/qt/MemoryView.ui diff --git a/src/gba/supervisor/thread.c b/src/gba/supervisor/thread.c index 97296f1bd..e72f31f18 100644 --- a/src/gba/supervisor/thread.c +++ b/src/gba/supervisor/thread.c @@ -133,6 +133,7 @@ static THREAD_ENTRY _GBAThreadRun(void* context) { ARMInit(&cpu); gba.sync = &threadContext->sync; threadContext->gba = &gba; + threadContext->cpu = &cpu; gba.logLevel = threadContext->logLevel; gba.logHandler = threadContext->logHandler; gba.stream = threadContext->stream; diff --git a/src/platform/qt/CMakeLists.txt b/src/platform/qt/CMakeLists.txt index 7956207a5..bfdfb3477 100644 --- a/src/platform/qt/CMakeLists.txt +++ b/src/platform/qt/CMakeLists.txt @@ -56,6 +56,8 @@ set(SOURCE_FILES KeyEditor.cpp LoadSaveState.cpp LogView.cpp + MemoryModel.cpp + MemoryView.cpp MultiplayerController.cpp OverrideView.cpp PaletteView.cpp @@ -74,6 +76,7 @@ qt5_wrap_ui(UI_FILES GIFView.ui LoadSaveState.ui LogView.ui + MemoryView.ui OverrideView.ui PaletteView.ui SensorView.ui diff --git a/src/platform/qt/MemoryModel.cpp b/src/platform/qt/MemoryModel.cpp new file mode 100644 index 000000000..a85acd1ae --- /dev/null +++ b/src/platform/qt/MemoryModel.cpp @@ -0,0 +1,89 @@ +/* Copyright (c) 2013-2015 Jeffrey Pfau + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#include "MemoryModel.h" + +#include "GameController.h" + +#include +#include +#include +#include + +extern "C" { +#include "gba/memory.h" +} + +using namespace QGBA; + +MemoryModel::MemoryModel(QWidget* parent) + : QAbstractScrollArea(parent) + , m_top(0) +{ + m_font.setFamily("Source Code Pro"); + m_font.setStyleHint(QFont::Monospace); + m_font.setPointSize(12); + QFontMetrics metrics(m_font); + m_cellHeight = metrics.height(); + m_letterWidth = metrics.averageCharWidth(); + + setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn); + m_margins = QMargins(metrics.width("FFFFFF ") + 3, m_cellHeight + 1, metrics.width(" AAAAAAAAAAAAAAAA") + 3, 0); + + verticalScrollBar()->setRange(0, 0x01000000 - viewport()->size().height() / m_cellHeight); + connect(verticalScrollBar(), &QSlider::sliderMoved, [this](int position) { + m_top = position; + }); + update(); +} + +void MemoryModel::setController(GameController* controller) { + m_cpu = controller->thread()->cpu; +} + +void MemoryModel::resizeEvent(QResizeEvent*) { + verticalScrollBar()->setRange(0, 0x01000000 - viewport()->size().height() / m_cellHeight); +} + +void MemoryModel::paintEvent(QPaintEvent* event) { + QPainter painter(viewport()); + painter.setFont(m_font); + QChar c0('0'); + QSizeF cellSize = QSizeF((viewport()->size().width() - (m_margins.left() + m_margins.right())) / 16.f, m_cellHeight); + QSizeF letterSize = QSizeF(m_letterWidth, m_cellHeight); + painter.drawText(QRect(QPoint(0, 0), QSize(m_margins.left(), m_margins.top())), Qt::AlignHCenter, tr("All")); + painter.drawText(QRect(QPoint(viewport()->size().width() - m_margins.right(), 0), QSize(m_margins.right(), m_margins.top())), Qt::AlignHCenter, tr("ASCII")); + for (int x = 0; x < 16; ++x) { + painter.drawText(QRectF(QPointF(cellSize.width() * x + m_margins.left(), 0), cellSize), Qt::AlignHCenter, QString::number(x, 16).toUpper()); + } + int height = (viewport()->size().height() - m_cellHeight + 1) / m_cellHeight; + for (int y = 0; y < height; ++y) { + int yp = m_cellHeight * y + m_margins.top(); + QString data = QString("%0").arg(y + m_top, 6, 16, c0).toUpper(); + painter.drawText(QRectF(QPointF(0, yp), QSizeF(m_margins.left(), m_cellHeight)), Qt::AlignHCenter, data); + for (int x = 0; x < 16; ++x) { + uint8_t b = m_cpu->memory.load8(m_cpu, (y + m_top) * 16 + x, nullptr); + QChar c(b); + if (!c.isPrint() || c.unicode() >= 0x80) { + c = 0xFFFD; + } + data = QString("%0").arg(b, 2, 16, c0).toUpper(); + painter.drawText(QRectF(QPointF(cellSize.width() * x + m_margins.left(), yp), cellSize), Qt::AlignHCenter, data); + painter.drawText(QRectF(QPointF(viewport()->size().width() - (16 - x) * m_margins.right() / 16.f, yp), letterSize), Qt::AlignHCenter, c); + } + } + painter.drawLine(m_margins.left() - 2, 0, m_margins.left() - 2, viewport()->size().height()); + painter.drawLine(viewport()->size().width() - m_margins.right(), 0, viewport()->size().width() - m_margins.right(), viewport()->size().height()); + painter.drawLine(0, m_margins.top(), viewport()->size().width(), m_margins.top()); +} + +QString MemoryModel::headerData(int section, Qt::Orientation orientation) const { + if (orientation == Qt::Vertical) { + return QString("%0").arg(section >> 4, 8, 16, QChar('0')).toUpper(); + } else { + return QString("%0").arg(section, 2, 16, QChar('0')).toUpper(); + } +} diff --git a/src/platform/qt/MemoryModel.h b/src/platform/qt/MemoryModel.h new file mode 100644 index 000000000..7ed3a2d90 --- /dev/null +++ b/src/platform/qt/MemoryModel.h @@ -0,0 +1,44 @@ +/* Copyright (c) 2013-2015 Jeffrey Pfau + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#ifndef QGBA_MEMORY_MODEL +#define QGBA_MEMORY_MODEL + +#include +#include +#include + +struct ARMCore; + +namespace QGBA { + +class GameController; + +class MemoryModel : public QAbstractScrollArea { +Q_OBJECT + +public: + MemoryModel(QWidget* parent = nullptr); + + void setController(GameController* controller); + +protected: + void resizeEvent(QResizeEvent*) override; + void paintEvent(QPaintEvent*) override; + +private: + QString headerData(int section, Qt::Orientation orientation) const; + + ARMCore* m_cpu; + QFont m_font; + int m_cellHeight; + int m_letterWidth; + int m_top; + QMargins m_margins; +}; + +} + +#endif diff --git a/src/platform/qt/MemoryView.cpp b/src/platform/qt/MemoryView.cpp new file mode 100644 index 000000000..e8326ca04 --- /dev/null +++ b/src/platform/qt/MemoryView.cpp @@ -0,0 +1,18 @@ +/* Copyright (c) 2013-2015 Jeffrey Pfau + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "MemoryView.h" + +using namespace QGBA; + +MemoryView::MemoryView(GameController* controller, QWidget* parent) + : QWidget(parent) + , m_controller(controller) +{ + m_ui.setupUi(this); + + m_ui.hexfield->setController(controller); +} diff --git a/src/platform/qt/MemoryView.h b/src/platform/qt/MemoryView.h new file mode 100644 index 000000000..6f47fbce6 --- /dev/null +++ b/src/platform/qt/MemoryView.h @@ -0,0 +1,31 @@ +/* Copyright (c) 2013-2015 Jeffrey Pfau + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#ifndef QGBA_MEMORY_VIEW +#define QGBA_MEMORY_VIEW + +#include "MemoryModel.h" + +#include "ui_MemoryView.h" + +namespace QGBA { + +class GameController; + +class MemoryView : public QWidget { +Q_OBJECT + +public: + MemoryView(GameController* controller, QWidget* parent = nullptr); + +private: + Ui::MemoryView m_ui; + + GameController* m_controller; +}; + +} + +#endif diff --git a/src/platform/qt/MemoryView.ui b/src/platform/qt/MemoryView.ui new file mode 100644 index 000000000..61e7d7915 --- /dev/null +++ b/src/platform/qt/MemoryView.ui @@ -0,0 +1,32 @@ + + + MemoryView + + + + 0 + 0 + 527 + 544 + + + + Memory + + + + + + + + + + QGBA::MemoryModel + QWidget +
MemoryModel.h
+ 1 +
+
+ + +
diff --git a/src/platform/qt/Window.cpp b/src/platform/qt/Window.cpp index 1ecc3e433..3a2e28af9 100644 --- a/src/platform/qt/Window.cpp +++ b/src/platform/qt/Window.cpp @@ -26,6 +26,7 @@ #include "LoadSaveState.h" #include "LogView.h" #include "MultiplayerController.h" +#include "MemoryView.h" #include "OverrideView.h" #include "PaletteView.h" #include "SensorView.h" @@ -328,6 +329,11 @@ void Window::openPaletteWindow() { openView(paletteWindow); } +void Window::openMemoryWindow() { + MemoryView* memoryWindow = new MemoryView(m_controller); + openView(memoryWindow); +} + #ifdef BUILD_SDL void Window::openGamepadWindow() { const char* profile = m_inputController.profileForType(SDL_BINDING_BUTTON); @@ -964,6 +970,11 @@ void Window::setupMenu(QMenuBar* menubar) { m_gameActions.append(paletteView); addControlledAction(toolsMenu, paletteView, "paletteWindow"); + QAction* memoryView = new QAction(tr("View memory..."), toolsMenu); + connect(memoryView, SIGNAL(triggered()), this, SLOT(openMemoryWindow())); + m_gameActions.append(memoryView); + addControlledAction(toolsMenu, memoryView, "memoryView"); + ConfigOption* skipBios = m_config->addOption("skipBios"); skipBios->connect([this](const QVariant& value) { m_controller->setSkipBIOS(value.toBool()); diff --git a/src/platform/qt/Window.h b/src/platform/qt/Window.h index 010e0225c..041bc6e07 100644 --- a/src/platform/qt/Window.h +++ b/src/platform/qt/Window.h @@ -77,6 +77,7 @@ public slots: void openCheatsWindow(); void openPaletteWindow(); + void openMemoryWindow(); #ifdef BUILD_SDL void openGamepadWindow();