all repos — mgba @ ffa5e65856c5950a26b3aedd2cc35d7f2f5041dc

mGBA Game Boy Advance Emulator

src/platform/qt/MemoryView.cpp (view raw)

  1/* Copyright (c) 2013-2015 Jeffrey Pfau
  2 *
  3 * This Source Code Form is subject to the terms of the Mozilla Public
  4 * License, v. 2.0. If a copy of the MPL was not distributed with this
  5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  6
  7#include "MemoryView.h"
  8
  9#include "CoreController.h"
 10#include "MemoryDump.h"
 11
 12#include <mgba/core/core.h>
 13
 14using namespace QGBA;
 15
 16IntValidator::IntValidator(bool isSigned, QObject* parent)
 17	: QValidator(parent)
 18	, m_signed(isSigned)
 19{
 20}
 21
 22QValidator::State IntValidator::validate(QString& input, int&) const {
 23	if (input.isEmpty()) {
 24		return QValidator::Intermediate;
 25	}
 26	if (input.size() == 1 && input[0] == '-') {
 27		if (m_signed) {
 28			return QValidator::Intermediate;
 29		} else {
 30			return QValidator::Invalid;
 31		}
 32	}
 33	if (input[0].isSpace()) {
 34		return QValidator::Invalid;
 35	}
 36	if (input[input.size() - 1].isSpace()) {
 37		return QValidator::Invalid;
 38	}
 39	if (input.size() > 1 && input[0] == '0') {
 40		return QValidator::Invalid;
 41	}
 42
 43	bool ok = false;
 44	qlonglong val = locale().toLongLong(input, &ok);
 45	if (!ok) {
 46		return QValidator::Invalid;
 47	}
 48
 49	qlonglong hardmax;
 50	qlonglong hardmin;
 51	qlonglong max;
 52	qlonglong min;
 53
 54	if (m_signed) {
 55		switch (m_width) {
 56		case 1:
 57			hardmax = 999LL;
 58			hardmin = -999LL;
 59			max = 0x7FLL;
 60			min = -0x80LL;
 61			break;
 62		case 2:
 63			hardmax = 99999LL;
 64			hardmin = -99999LL;
 65			max = 0x7FFFLL;
 66			min = -0x8000LL;
 67			break;
 68		case 4:
 69			hardmax = 9999999999LL;
 70			hardmin = -9999999999LL;
 71			max = 0x7FFFFFFFLL;
 72			min = -0x80000000LL;
 73			break;
 74		default:
 75			return QValidator::Invalid;
 76		}
 77	} else {
 78		hardmin = 0;
 79		min = 0;
 80
 81		switch (m_width) {
 82		case 1:
 83			hardmax = 999LL;
 84			max = 0xFFLL;
 85			break;
 86		case 2:
 87			hardmax = 99999LL;
 88			max = 0xFFFFLL;
 89			break;
 90		case 4:
 91			hardmax = 9999999999LL;
 92			max = 0xFFFFFFFFLL;
 93			break;
 94		default:
 95			return QValidator::Invalid;
 96		}
 97	}
 98	if (val < hardmin || val > hardmax) {
 99		return QValidator::Invalid;
100	}
101	if (val < min || val > max) {
102		return QValidator::Intermediate;
103	}
104	return QValidator::Acceptable;
105}
106
107MemoryView::MemoryView(std::shared_ptr<CoreController> controller, QWidget* parent)
108	: QWidget(parent)
109	, m_controller(controller)
110{
111	m_ui.setupUi(this);
112
113	m_ui.hexfield->setController(controller);
114
115	m_ui.sintVal->setValidator(&m_sintValidator);
116	m_ui.uintVal->setValidator(&m_uintValidator);
117
118	mCore* core = m_controller->thread()->core;
119	const mCoreMemoryBlock* info;
120	size_t nBlocks = core->listMemoryBlocks(core, &info);
121
122	connect(m_ui.regions, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged),
123	        this, &MemoryView::setIndex);
124	connect(m_ui.segments, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged),
125	        this, &MemoryView::setSegment);
126
127	if (info) {
128		for (size_t i = 0; i < nBlocks; ++i) {
129			if (!(info[i].flags & (mCORE_MEMORY_MAPPED | mCORE_MEMORY_VIRTUAL))) {
130				continue;
131			}
132			m_ui.regions->addItem(tr(info[i].longName));
133		}
134	}
135
136	connect(m_ui.width8, &QAbstractButton::clicked, [this]() {
137		m_ui.hexfield->setAlignment(1);
138		m_sintValidator.setWidth(1);
139		m_uintValidator.setWidth(1);
140	});
141	connect(m_ui.width16, &QAbstractButton::clicked, [this]() {
142		m_ui.hexfield->setAlignment(2);
143		m_sintValidator.setWidth(2);
144		m_uintValidator.setWidth(2);
145	});
146	connect(m_ui.width32, &QAbstractButton::clicked, [this]() {
147		m_ui.hexfield->setAlignment(4);
148		m_sintValidator.setWidth(4);
149		m_uintValidator.setWidth(4);
150	});
151	connect(m_ui.setAddress, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged),
152	        this, static_cast<void (MemoryView::*)(uint32_t)>(&MemoryView::jumpToAddress));
153	connect(m_ui.hexfield, &MemoryModel::selectionChanged, this, &MemoryView::updateSelection);
154	connect(m_ui.saveRange, &QAbstractButton::clicked, this, &MemoryView::saveRange);
155
156	connect(controller.get(), &CoreController::stopping, this, &QWidget::close);
157
158	connect(controller.get(), &CoreController::frameAvailable, this, &MemoryView::update);
159	connect(controller.get(), &CoreController::paused, this, &MemoryView::update);
160	connect(controller.get(), &CoreController::stateLoaded, this, &MemoryView::update);
161	connect(controller.get(), &CoreController::rewound, this, &MemoryView::update);
162
163	connect(m_ui.copy, &QAbstractButton::clicked, m_ui.hexfield, &MemoryModel::copy);
164	connect(m_ui.save, &QAbstractButton::clicked, m_ui.hexfield, &MemoryModel::save);
165	connect(m_ui.paste, &QAbstractButton::clicked, m_ui.hexfield, &MemoryModel::paste);
166	connect(m_ui.load, &QAbstractButton::clicked, m_ui.hexfield, &MemoryModel::load);
167
168	connect(m_ui.loadTBL, &QAbstractButton::clicked, m_ui.hexfield, &MemoryModel::loadTBL);
169
170	connect(m_ui.sintVal, &QLineEdit::returnPressed, this, [this]() {
171		int align = m_ui.hexfield->alignment();
172		mCore* core = m_controller->thread()->core;
173		int32_t value = m_ui.sintVal->text().toInt();
174		switch (align) {
175		case 1:
176			core->rawWrite8(core, m_selection.first, m_ui.segments->value(), value);
177			break;
178		case 2:
179			core->rawWrite16(core, m_selection.first, m_ui.segments->value(), value);
180			break;
181		case 4:
182			core->rawWrite32(core, m_selection.first, m_ui.segments->value(), value);
183			break;
184		}
185		update();
186	});
187	connect(m_ui.uintVal, &QLineEdit::returnPressed, this, [this]() {
188		int align = m_ui.hexfield->alignment();
189		mCore* core = m_controller->thread()->core;
190		uint32_t value = m_ui.uintVal->text().toUInt();
191		switch (align) {
192		case 1:
193			core->rawWrite8(core, m_selection.first, m_ui.segments->value(), value);
194			break;
195		case 2:
196			core->rawWrite16(core, m_selection.first, m_ui.segments->value(), value);
197			break;
198		case 4:
199			core->rawWrite32(core, m_selection.first, m_ui.segments->value(), value);
200			break;
201		}
202		update();
203	});
204}
205
206void MemoryView::setIndex(int index) {
207	mCore* core = m_controller->thread()->core;
208	const mCoreMemoryBlock* blocks;
209	size_t nBlocks = core->listMemoryBlocks(core, &blocks);
210	if (index < 0 || static_cast<size_t>(index) >= nBlocks) {
211		return;
212	}
213	const mCoreMemoryBlock& info = blocks[index];
214
215	m_region = qMakePair(info.start, info.end);
216	m_ui.segments->setValue(-1);
217	m_ui.segments->setVisible(info.maxSegment > 0);
218	m_ui.segmentColon->setVisible(info.maxSegment > 0);
219	m_ui.segments->setMaximum(info.maxSegment);
220	m_ui.hexfield->setRegion(info.start, info.end - info.start, info.shortName);
221}
222
223void MemoryView::setSegment(int segment) {
224	mCore* core = m_controller->thread()->core;
225	const mCoreMemoryBlock* blocks;
226	size_t nBlocks = core->listMemoryBlocks(core, &blocks);
227	int index = m_ui.regions->currentIndex();
228	if (index < 0 || static_cast<size_t>(index) >= nBlocks) {
229		return;
230	}
231	const mCoreMemoryBlock& info = blocks[index];
232
233	m_ui.hexfield->setSegment(info.maxSegment < segment ? info.maxSegment : segment);
234}
235
236void MemoryView::update() {
237	m_ui.hexfield->viewport()->update();
238	updateStatus();
239}
240
241void MemoryView::jumpToAddress(uint32_t address) {
242	if (address < m_region.first || address >= m_region.second) {
243		m_ui.regions->setCurrentIndex(0);
244		setIndex(0);
245	}
246	if (address < m_region.first || address >= m_region.second) {
247		return;
248	}
249	m_ui.hexfield->jumpToAddress(address);
250}
251
252void MemoryView::updateSelection(uint32_t start, uint32_t end) {
253	m_selection.first = start;
254	m_selection.second = end;
255	updateStatus();
256}
257
258void MemoryView::updateStatus() {
259	unsigned align = m_ui.hexfield->alignment();
260	mCore* core = m_controller->thread()->core;
261	QByteArray selection(m_ui.hexfield->serialize());
262	QString text(m_ui.hexfield->decodeText(selection));
263	m_ui.stringVal->setText(text);
264
265	if (m_selection.first & (align - 1) || m_selection.second - m_selection.first != align) {
266		m_ui.sintVal->clear();
267		m_ui.sintVal->setReadOnly(true);
268		m_ui.uintVal->clear();
269		m_ui.uintVal->setReadOnly(true);
270		return;
271	}
272	union {
273		uint32_t u32;
274		int32_t i32;
275		uint16_t u16;
276		int16_t i16;
277		uint8_t u8;
278		int8_t i8;
279	} value;
280	switch (align) {
281	case 1:
282		value.u8 = core->rawRead8(core, m_selection.first, m_ui.segments->value());
283		m_ui.sintVal->setText(QString::number(value.i8));
284		m_ui.uintVal->setText(QString::number(value.u8));
285		break;
286	case 2:
287		value.u16 = core->rawRead16(core, m_selection.first, m_ui.segments->value());
288		m_ui.sintVal->setText(QString::number(value.i16));
289		m_ui.uintVal->setText(QString::number(value.u16));
290		break;
291	case 4:
292		value.u32 = core->rawRead32(core, m_selection.first, m_ui.segments->value());
293		m_ui.sintVal->setText(QString::number(value.i32));
294		m_ui.uintVal->setText(QString::number(value.u32));
295		break;
296	}
297	m_ui.sintVal->setReadOnly(false);
298	m_ui.uintVal->setReadOnly(false);
299}
300
301void MemoryView::saveRange() {
302	MemoryDump* memdump = new MemoryDump(m_controller);
303	memdump->setAttribute(Qt::WA_DeleteOnClose);
304	memdump->setAddress(m_selection.first);
305	memdump->setSegment(m_ui.segments->value());
306	memdump->setByteCount(m_selection.second - m_selection.first);
307	memdump->show();
308}