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(¶ms)) {
146 mCoreMemorySearch(core, ¶ms, &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(¶ms)) {
159 if (m_ui.opUnknown->isChecked()) {
160 params.op = mCORE_MEMORY_SEARCH_DELTA_ANY;
161 }
162 mCoreMemorySearchRepeat(core, ¶ms, &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}