diff --git a/CHANGES b/CHANGES index 1f29da931..57abc718f 100644 --- a/CHANGES +++ b/CHANGES @@ -62,6 +62,7 @@ Misc: - Qt: Renderer can be changed while a game is running - Qt: Add hex index to palette view - Qt: Add transformation matrix info to sprite view + - Qt: Memory viewer now supports editing decimal values directly (closes mgba.io/i/1705) - Util: Reset vector size on deinit 0.8.3: (2020-08-03) diff --git a/src/platform/qt/MemoryView.cpp b/src/platform/qt/MemoryView.cpp index 0aefb642f..23e727354 100644 --- a/src/platform/qt/MemoryView.cpp +++ b/src/platform/qt/MemoryView.cpp @@ -13,6 +13,97 @@ using namespace QGBA; +IntValidator::IntValidator(bool isSigned, QObject* parent) + : QValidator(parent) + , m_signed(isSigned) +{ +} + +QValidator::State IntValidator::validate(QString& input, int&) const { + if (input.isEmpty()) { + return QValidator::Intermediate; + } + if (input.size() == 1 && input[0] == '-') { + if (m_signed) { + return QValidator::Intermediate; + } else { + return QValidator::Invalid; + } + } + if (input[0].isSpace()) { + return QValidator::Invalid; + } + if (input[input.size() - 1].isSpace()) { + return QValidator::Invalid; + } + if (input.size() > 1 && input[0] == '0') { + return QValidator::Invalid; + } + + bool ok = false; + qlonglong val = locale().toLongLong(input, &ok); + if (!ok) { + return QValidator::Invalid; + } + + qlonglong hardmax; + qlonglong hardmin; + qlonglong max; + qlonglong min; + + if (m_signed) { + switch (m_width) { + case 1: + hardmax = 999LL; + hardmin = -999LL; + max = 0x7FLL; + min = -0x80LL; + break; + case 2: + hardmax = 99999LL; + hardmin = -99999LL; + max = 0x7FFFLL; + min = -0x8000LL; + break; + case 4: + hardmax = 9999999999LL; + hardmin = -9999999999LL; + max = 0x7FFFFFFFLL; + min = -0x80000000LL; + break; + default: + return QValidator::Invalid; + } + } else { + hardmin = 0; + min = 0; + + switch (m_width) { + case 1: + hardmax = 999LL; + max = 0xFFLL; + break; + case 2: + hardmax = 99999LL; + max = 0xFFFFLL; + break; + case 4: + hardmax = 9999999999LL; + max = 0xFFFFFFFFLL; + break; + default: + return QValidator::Invalid; + } + } + if (val < hardmin || val > hardmax) { + return QValidator::Invalid; + } + if (val < min || val > max) { + return QValidator::Intermediate; + } + return QValidator::Acceptable; +} + MemoryView::MemoryView(std::shared_ptr controller, QWidget* parent) : QWidget(parent) , m_controller(controller) @@ -21,6 +112,9 @@ MemoryView::MemoryView(std::shared_ptr controller, QWidget* pare m_ui.hexfield->setController(controller); + m_ui.sintVal->setValidator(&m_sintValidator); + m_ui.uintVal->setValidator(&m_uintValidator); + mCore* core = m_controller->thread()->core; const mCoreMemoryBlock* info; size_t nBlocks = core->listMemoryBlocks(core, &info); @@ -39,9 +133,21 @@ MemoryView::MemoryView(std::shared_ptr controller, QWidget* pare } } - connect(m_ui.width8, &QAbstractButton::clicked, [this]() { m_ui.hexfield->setAlignment(1); }); - connect(m_ui.width16, &QAbstractButton::clicked, [this]() { m_ui.hexfield->setAlignment(2); }); - connect(m_ui.width32, &QAbstractButton::clicked, [this]() { m_ui.hexfield->setAlignment(4); }); + connect(m_ui.width8, &QAbstractButton::clicked, [this]() { + m_ui.hexfield->setAlignment(1); + m_sintValidator.setWidth(1); + m_uintValidator.setWidth(1); + }); + connect(m_ui.width16, &QAbstractButton::clicked, [this]() { + m_ui.hexfield->setAlignment(2); + m_sintValidator.setWidth(2); + m_uintValidator.setWidth(2); + }); + connect(m_ui.width32, &QAbstractButton::clicked, [this]() { + m_ui.hexfield->setAlignment(4); + m_sintValidator.setWidth(4); + m_uintValidator.setWidth(4); + }); connect(m_ui.setAddress, static_cast(&QSpinBox::valueChanged), this, static_cast(&MemoryView::jumpToAddress)); connect(m_ui.hexfield, &MemoryModel::selectionChanged, this, &MemoryView::updateSelection); @@ -60,6 +166,41 @@ MemoryView::MemoryView(std::shared_ptr controller, QWidget* pare connect(m_ui.load, &QAbstractButton::clicked, m_ui.hexfield, &MemoryModel::load); connect(m_ui.loadTBL, &QAbstractButton::clicked, m_ui.hexfield, &MemoryModel::loadTBL); + + connect(m_ui.sintVal, &QLineEdit::returnPressed, this, [this]() { + int align = m_ui.hexfield->alignment(); + mCore* core = m_controller->thread()->core; + int32_t value = m_ui.sintVal->text().toInt(); + switch (align) { + case 1: + core->rawWrite8(core, m_selection.first, m_ui.segments->value(), value); + break; + case 2: + core->rawWrite16(core, m_selection.first, m_ui.segments->value(), value); + break; + case 4: + core->rawWrite32(core, m_selection.first, m_ui.segments->value(), value); + break; + } + update(); + }); + connect(m_ui.uintVal, &QLineEdit::returnPressed, this, [this]() { + int align = m_ui.hexfield->alignment(); + mCore* core = m_controller->thread()->core; + uint32_t value = m_ui.uintVal->text().toUInt(); + switch (align) { + case 1: + core->rawWrite8(core, m_selection.first, m_ui.segments->value(), value); + break; + case 2: + core->rawWrite16(core, m_selection.first, m_ui.segments->value(), value); + break; + case 4: + core->rawWrite32(core, m_selection.first, m_ui.segments->value(), value); + break; + } + update(); + }); } void MemoryView::setIndex(int index) { @@ -116,7 +257,9 @@ void MemoryView::updateStatus() { if (m_selection.first & (align - 1) || m_selection.second - m_selection.first != align) { m_ui.sintVal->clear(); + m_ui.sintVal->setReadOnly(true); m_ui.uintVal->clear(); + m_ui.uintVal->setReadOnly(true); return; } union { @@ -144,6 +287,8 @@ void MemoryView::updateStatus() { m_ui.uintVal->setText(QString::number(value.u32)); break; } + m_ui.sintVal->setReadOnly(false); + m_ui.uintVal->setReadOnly(false); } void MemoryView::saveRange() { @@ -153,4 +298,4 @@ void MemoryView::saveRange() { memdump->setSegment(m_ui.segments->value()); memdump->setByteCount(m_selection.second - m_selection.first); memdump->show(); -} \ No newline at end of file +} diff --git a/src/platform/qt/MemoryView.h b/src/platform/qt/MemoryView.h index ac4bfe53d..f68052a06 100644 --- a/src/platform/qt/MemoryView.h +++ b/src/platform/qt/MemoryView.h @@ -5,6 +5,8 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #pragma once +#include + #include "MemoryModel.h" #include "ui_MemoryView.h" @@ -13,6 +15,20 @@ namespace QGBA { class CoreController; +class IntValidator : public QValidator { +Q_OBJECT + +public: + IntValidator(bool isSigned, QObject* parent = nullptr); + + virtual QValidator::State validate(QString& input, int& pos) const override; + void setWidth(int bytes) { m_width = bytes; } + +private: + int m_width = 1; + bool m_signed; +}; + class MemoryView : public QWidget { Q_OBJECT @@ -32,6 +48,8 @@ private slots: private: Ui::MemoryView m_ui; + IntValidator m_sintValidator{true}; + IntValidator m_uintValidator{false}; std::shared_ptr m_controller; QPair m_region; diff --git a/src/platform/qt/MemoryView.ui b/src/platform/qt/MemoryView.ui index fb43fbe22..472ed87eb 100644 --- a/src/platform/qt/MemoryView.ui +++ b/src/platform/qt/MemoryView.ui @@ -190,6 +190,9 @@ + + 10 + true @@ -208,6 +211,9 @@ + + 11 + true @@ -304,6 +310,23 @@ 1 + + regions + segments + setAddress + width8 + width16 + width32 + sintVal + uintVal + stringVal + loadTBL + copy + paste + save + saveRange + load +