all repos — mgba @ cb7b819fe3ecdbe1a9ff06c0dd3650a49514a191

mGBA Game Boy Advance Emulator

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

  1/* Copyright (c) 2013-2017 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 "MemorySearch.h"
  8
  9#include <mgba/core/core.h>
 10
 11#include "GameController.h"
 12#include "MemoryView.h"
 13
 14using namespace QGBA;
 15
 16MemorySearch::MemorySearch(GameController* controller, QWidget* parent)
 17	: QWidget(parent)
 18	, m_controller(controller)
 19{
 20	m_ui.setupUi(this);
 21
 22	mCoreMemorySearchResultsInit(&m_results, 0);
 23	connect(m_ui.search, &QPushButton::clicked, this, &MemorySearch::search);
 24	connect(m_ui.searchWithin, &QPushButton::clicked, this, &MemorySearch::searchWithin);
 25	connect(m_ui.refresh, &QPushButton::clicked, this, &MemorySearch::refresh);
 26	connect(m_ui.numHex, &QPushButton::clicked, this, &MemorySearch::refresh);
 27	connect(m_ui.numDec, &QPushButton::clicked, this, &MemorySearch::refresh);
 28	connect(m_ui.viewMem, &QPushButton::clicked, this, &MemorySearch::openMemory);
 29}
 30
 31MemorySearch::~MemorySearch() {
 32	mCoreMemorySearchResultsDeinit(&m_results);
 33}
 34
 35bool MemorySearch::createParams(mCoreMemorySearchParams* params) {
 36	params->memoryFlags = mCORE_MEMORY_RW;
 37	mCore* core = m_controller->thread()->core;
 38
 39	QByteArray string;
 40	bool ok = false;
 41	if (m_ui.typeNum->isChecked()) {
 42		if (m_ui.bits8->isChecked()) {
 43			params->type = mCORE_MEMORY_SEARCH_8;
 44		}
 45		if (m_ui.bits16->isChecked()) {
 46			params->type = mCORE_MEMORY_SEARCH_16;
 47		}
 48		if (m_ui.bits32->isChecked()) {
 49			params->type = mCORE_MEMORY_SEARCH_32;
 50		}
 51		if (m_ui.numHex->isChecked()) {
 52			uint32_t v = m_ui.value->text().toUInt(&ok, 16);
 53			if (ok) {
 54				switch (params->type) {
 55				case mCORE_MEMORY_SEARCH_8:
 56					ok = v < 0x100;
 57					params->value8 = v;
 58					break;
 59				case mCORE_MEMORY_SEARCH_16:
 60					ok = v < 0x10000;
 61					params->value16 = v;
 62					break;
 63				case mCORE_MEMORY_SEARCH_32:
 64					params->value32 = v;
 65					break;
 66				default:
 67					ok = false;
 68				}
 69			}
 70		}
 71		if (m_ui.numDec->isChecked()) {
 72			uint32_t v = m_ui.value->text().toUInt(&ok, 10);
 73			if (ok) {
 74				switch (params->type) {
 75				case mCORE_MEMORY_SEARCH_8:
 76					ok = v < 0x100;
 77					params->value8 = v;
 78					break;
 79				case mCORE_MEMORY_SEARCH_16:
 80					ok = v < 0x10000;
 81					params->value16 = v;
 82					break;
 83				case mCORE_MEMORY_SEARCH_32:
 84					params->value32 = v;
 85					break;
 86				default:
 87					ok = false;
 88				}
 89			}
 90		}
 91		if (m_ui.numGuess->isChecked()) {
 92			params->type = mCORE_MEMORY_SEARCH_GUESS;
 93			m_string = m_ui.value->text().toLocal8Bit();
 94			params->valueStr = m_string.constData();
 95			ok = true;
 96		}
 97	}
 98	if (m_ui.typeStr->isChecked()) {
 99		params->type = mCORE_MEMORY_SEARCH_STRING;
100		m_string = m_ui.value->text().toLocal8Bit();
101		params->valueStr = m_string.constData();
102		ok = true;
103	}
104	return ok;
105}
106
107void MemorySearch::search() {
108	mCoreMemorySearchResultsClear(&m_results);
109
110	mCoreMemorySearchParams params;
111
112	GameController::Interrupter interrupter(m_controller);
113	if (!m_controller->isLoaded()) {
114		return;
115	}
116	mCore* core = m_controller->thread()->core;
117
118	if (createParams(&params)) {
119		mCoreMemorySearch(core, &params, &m_results, LIMIT);
120	}
121
122	refresh();
123}
124
125void MemorySearch::searchWithin() {
126	mCoreMemorySearchParams params;
127
128	GameController::Interrupter interrupter(m_controller);
129	if (!m_controller->isLoaded()) {
130		return;
131	}
132	mCore* core = m_controller->thread()->core;
133
134	if (createParams(&params)) {
135		mCoreMemorySearchRepeat(core, &params, &m_results);
136	}
137
138	refresh();
139}
140
141void MemorySearch::refresh() {
142	GameController::Interrupter interrupter(m_controller);
143	if (!m_controller->isLoaded()) {
144		return;
145	}
146	mCore* core = m_controller->thread()->core;
147
148	m_ui.results->clearContents();
149	m_ui.results->setRowCount(mCoreMemorySearchResultsSize(&m_results));
150	for (size_t i = 0; i < mCoreMemorySearchResultsSize(&m_results); ++i) {
151		mCoreMemorySearchResult* result = mCoreMemorySearchResultsGetPointer(&m_results, i);
152		QTableWidgetItem* item = new QTableWidgetItem(QString("%1").arg(result->address, 8, 16, QChar('0')));
153		m_ui.results->setItem(i, 0, item);
154		QTableWidgetItem* type;
155		if (m_ui.numHex->isChecked()) {
156			switch (result->type) {
157			case mCORE_MEMORY_SEARCH_8:
158				item = new QTableWidgetItem(QString("%1").arg(core->rawRead8(core, result->address, result->segment), 2, 16, QChar('0')));
159				break;
160			case mCORE_MEMORY_SEARCH_16:
161				item = new QTableWidgetItem(QString("%1").arg(core->rawRead16(core, result->address, result->segment), 4, 16, QChar('0')));
162				break;
163			case mCORE_MEMORY_SEARCH_GUESS:
164			case mCORE_MEMORY_SEARCH_32:
165				item = new QTableWidgetItem(QString("%1").arg(core->rawRead32(core, result->address, result->segment), 8, 16, QChar('0')));
166				break;
167			case mCORE_MEMORY_SEARCH_STRING:
168				item = new QTableWidgetItem("?"); // TODO
169			}
170		} else {
171			switch (result->type) {
172			case mCORE_MEMORY_SEARCH_8:
173				item = new QTableWidgetItem(QString::number(core->rawRead8(core, result->address, result->segment)));
174				break;
175			case mCORE_MEMORY_SEARCH_16:
176				item = new QTableWidgetItem(QString::number(core->rawRead16(core, result->address, result->segment)));
177				break;
178			case mCORE_MEMORY_SEARCH_GUESS:
179			case mCORE_MEMORY_SEARCH_32:
180				item = new QTableWidgetItem(QString::number(core->rawRead32(core, result->address, result->segment)));
181				break;
182			case mCORE_MEMORY_SEARCH_STRING:
183				item = new QTableWidgetItem("?"); // TODO
184			}
185		}
186		QString divisor;
187		if (result->guessDivisor > 1) {
188			divisor = tr(" (⅟%0×)").arg(result->guessDivisor);
189		}
190		switch (result->type) {
191		case mCORE_MEMORY_SEARCH_8:
192			type = new QTableWidgetItem(tr("1 byte%0").arg(divisor));
193			break;
194		case mCORE_MEMORY_SEARCH_16:
195			type = new QTableWidgetItem(tr("2 bytes%0").arg(divisor));
196			break;
197		case mCORE_MEMORY_SEARCH_GUESS:
198		case mCORE_MEMORY_SEARCH_32:
199			type = new QTableWidgetItem(tr("4 bytes%0").arg(divisor));
200			break;
201		case mCORE_MEMORY_SEARCH_STRING:
202			item = new QTableWidgetItem("?"); // TODO
203		}
204		m_ui.results->setItem(i, 1, item);
205		m_ui.results->setItem(i, 2, type);
206	}
207	m_ui.results->sortItems(0);
208	m_ui.results->resizeColumnsToContents();
209	m_ui.results->resizeRowsToContents();
210}
211
212void MemorySearch::openMemory() {
213	auto items = m_ui.results->selectedItems();
214	if (items.empty()) {
215		return;
216	}
217	QTableWidgetItem* item = items[0];
218	uint32_t address = item->text().toUInt(nullptr, 16);
219
220	MemoryView* memView = new MemoryView(m_controller);
221	memView->jumpToAddress(address);
222
223	connect(m_controller, &GameController::gameStopped, memView, &QWidget::close);
224	memView->setAttribute(Qt::WA_DeleteOnClose);
225	memView->show();
226}