all repos — mgba @ 2f5fb2265992581712525dad2b47fcad0a5c0372

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