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