all repos — mgba @ 8f1148498e12197745f62e477d9b8e07382cc72e

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