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(¶ms)) {
130 mCoreMemorySearch(core, ¶ms, &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(¶ms)) {
143 mCoreMemorySearchRepeat(core, ¶ms, &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}