all repos — mgba @ 8e46e0ea79d601ed57eab0ee365bc8a9525ed7fb

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