src/platform/qt/MemoryView.cpp (view raw)
1/* Copyright (c) 2013-2015 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 "MemoryView.h"
8
9#include "CoreController.h"
10#include "MemoryDump.h"
11
12#include <mgba/core/core.h>
13
14using namespace QGBA;
15
16IntValidator::IntValidator(bool isSigned, QObject* parent)
17 : QValidator(parent)
18 , m_signed(isSigned)
19{
20}
21
22QValidator::State IntValidator::validate(QString& input, int&) const {
23 if (input.isEmpty()) {
24 return QValidator::Intermediate;
25 }
26 if (input.size() == 1 && input[0] == '-') {
27 if (m_signed) {
28 return QValidator::Intermediate;
29 } else {
30 return QValidator::Invalid;
31 }
32 }
33 if (input[0].isSpace()) {
34 return QValidator::Invalid;
35 }
36 if (input[input.size() - 1].isSpace()) {
37 return QValidator::Invalid;
38 }
39 if (input.size() > 1 && input[0] == '0') {
40 return QValidator::Invalid;
41 }
42
43 bool ok = false;
44 qlonglong val = locale().toLongLong(input, &ok);
45 if (!ok) {
46 return QValidator::Invalid;
47 }
48
49 qlonglong hardmax;
50 qlonglong hardmin;
51 qlonglong max;
52 qlonglong min;
53
54 if (m_signed) {
55 switch (m_width) {
56 case 1:
57 hardmax = 999LL;
58 hardmin = -999LL;
59 max = 0x7FLL;
60 min = -0x80LL;
61 break;
62 case 2:
63 hardmax = 99999LL;
64 hardmin = -99999LL;
65 max = 0x7FFFLL;
66 min = -0x8000LL;
67 break;
68 case 4:
69 hardmax = 9999999999LL;
70 hardmin = -9999999999LL;
71 max = 0x7FFFFFFFLL;
72 min = -0x80000000LL;
73 break;
74 default:
75 return QValidator::Invalid;
76 }
77 } else {
78 hardmin = 0;
79 min = 0;
80
81 switch (m_width) {
82 case 1:
83 hardmax = 999LL;
84 max = 0xFFLL;
85 break;
86 case 2:
87 hardmax = 99999LL;
88 max = 0xFFFFLL;
89 break;
90 case 4:
91 hardmax = 9999999999LL;
92 max = 0xFFFFFFFFLL;
93 break;
94 default:
95 return QValidator::Invalid;
96 }
97 }
98 if (val < hardmin || val > hardmax) {
99 return QValidator::Invalid;
100 }
101 if (val < min || val > max) {
102 return QValidator::Intermediate;
103 }
104 return QValidator::Acceptable;
105}
106
107MemoryView::MemoryView(std::shared_ptr<CoreController> controller, QWidget* parent)
108 : QWidget(parent)
109 , m_controller(controller)
110{
111 m_ui.setupUi(this);
112
113 m_ui.hexfield->setController(controller);
114
115 m_ui.sintVal->setValidator(&m_sintValidator);
116 m_ui.uintVal->setValidator(&m_uintValidator);
117
118 mCore* core = m_controller->thread()->core;
119 const mCoreMemoryBlock* info;
120 size_t nBlocks = core->listMemoryBlocks(core, &info);
121
122 connect(m_ui.regions, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged),
123 this, &MemoryView::setIndex);
124 connect(m_ui.segments, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged),
125 this, &MemoryView::setSegment);
126
127 if (info) {
128 for (size_t i = 0; i < nBlocks; ++i) {
129 if (!(info[i].flags & (mCORE_MEMORY_MAPPED | mCORE_MEMORY_VIRTUAL))) {
130 continue;
131 }
132 m_ui.regions->addItem(tr(info[i].longName));
133 }
134 }
135
136 connect(m_ui.width8, &QAbstractButton::clicked, [this]() {
137 m_ui.hexfield->setAlignment(1);
138 m_sintValidator.setWidth(1);
139 m_uintValidator.setWidth(1);
140 });
141 connect(m_ui.width16, &QAbstractButton::clicked, [this]() {
142 m_ui.hexfield->setAlignment(2);
143 m_sintValidator.setWidth(2);
144 m_uintValidator.setWidth(2);
145 });
146 connect(m_ui.width32, &QAbstractButton::clicked, [this]() {
147 m_ui.hexfield->setAlignment(4);
148 m_sintValidator.setWidth(4);
149 m_uintValidator.setWidth(4);
150 });
151 connect(m_ui.setAddress, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged),
152 this, static_cast<void (MemoryView::*)(uint32_t)>(&MemoryView::jumpToAddress));
153 connect(m_ui.hexfield, &MemoryModel::selectionChanged, this, &MemoryView::updateSelection);
154 connect(m_ui.saveRange, &QAbstractButton::clicked, this, &MemoryView::saveRange);
155
156 connect(controller.get(), &CoreController::stopping, this, &QWidget::close);
157
158 connect(controller.get(), &CoreController::frameAvailable, this, &MemoryView::update);
159 connect(controller.get(), &CoreController::paused, this, &MemoryView::update);
160 connect(controller.get(), &CoreController::stateLoaded, this, &MemoryView::update);
161 connect(controller.get(), &CoreController::rewound, this, &MemoryView::update);
162
163 connect(m_ui.copy, &QAbstractButton::clicked, m_ui.hexfield, &MemoryModel::copy);
164 connect(m_ui.save, &QAbstractButton::clicked, m_ui.hexfield, &MemoryModel::save);
165 connect(m_ui.paste, &QAbstractButton::clicked, m_ui.hexfield, &MemoryModel::paste);
166 connect(m_ui.load, &QAbstractButton::clicked, m_ui.hexfield, &MemoryModel::load);
167
168 connect(m_ui.loadTBL, &QAbstractButton::clicked, m_ui.hexfield, &MemoryModel::loadTBL);
169
170 connect(m_ui.sintVal, &QLineEdit::returnPressed, this, [this]() {
171 int align = m_ui.hexfield->alignment();
172 mCore* core = m_controller->thread()->core;
173 int32_t value = m_ui.sintVal->text().toInt();
174 switch (align) {
175 case 1:
176 core->rawWrite8(core, m_selection.first, m_ui.segments->value(), value);
177 break;
178 case 2:
179 core->rawWrite16(core, m_selection.first, m_ui.segments->value(), value);
180 break;
181 case 4:
182 core->rawWrite32(core, m_selection.first, m_ui.segments->value(), value);
183 break;
184 }
185 update();
186 });
187 connect(m_ui.uintVal, &QLineEdit::returnPressed, this, [this]() {
188 int align = m_ui.hexfield->alignment();
189 mCore* core = m_controller->thread()->core;
190 uint32_t value = m_ui.uintVal->text().toUInt();
191 switch (align) {
192 case 1:
193 core->rawWrite8(core, m_selection.first, m_ui.segments->value(), value);
194 break;
195 case 2:
196 core->rawWrite16(core, m_selection.first, m_ui.segments->value(), value);
197 break;
198 case 4:
199 core->rawWrite32(core, m_selection.first, m_ui.segments->value(), value);
200 break;
201 }
202 update();
203 });
204}
205
206void MemoryView::setIndex(int index) {
207 mCore* core = m_controller->thread()->core;
208 const mCoreMemoryBlock* blocks;
209 size_t nBlocks = core->listMemoryBlocks(core, &blocks);
210 if (index < 0 || static_cast<size_t>(index) >= nBlocks) {
211 return;
212 }
213 const mCoreMemoryBlock& info = blocks[index];
214
215 m_region = qMakePair(info.start, info.end);
216 m_ui.segments->setValue(-1);
217 m_ui.segments->setVisible(info.maxSegment > 0);
218 m_ui.segmentColon->setVisible(info.maxSegment > 0);
219 m_ui.segments->setMaximum(info.maxSegment);
220 m_ui.hexfield->setRegion(info.start, info.end - info.start, info.shortName);
221}
222
223void MemoryView::setSegment(int segment) {
224 mCore* core = m_controller->thread()->core;
225 const mCoreMemoryBlock* blocks;
226 size_t nBlocks = core->listMemoryBlocks(core, &blocks);
227 int index = m_ui.regions->currentIndex();
228 if (index < 0 || static_cast<size_t>(index) >= nBlocks) {
229 return;
230 }
231 const mCoreMemoryBlock& info = blocks[index];
232
233 m_ui.hexfield->setSegment(info.maxSegment < segment ? info.maxSegment : segment);
234}
235
236void MemoryView::update() {
237 m_ui.hexfield->viewport()->update();
238 updateStatus();
239}
240
241void MemoryView::jumpToAddress(uint32_t address) {
242 if (address < m_region.first || address >= m_region.second) {
243 m_ui.regions->setCurrentIndex(0);
244 setIndex(0);
245 }
246 if (address < m_region.first || address >= m_region.second) {
247 return;
248 }
249 m_ui.hexfield->jumpToAddress(address);
250}
251
252void MemoryView::updateSelection(uint32_t start, uint32_t end) {
253 m_selection.first = start;
254 m_selection.second = end;
255 updateStatus();
256}
257
258void MemoryView::updateStatus() {
259 int align = m_ui.hexfield->alignment();
260 mCore* core = m_controller->thread()->core;
261 QByteArray selection(m_ui.hexfield->serialize());
262 QString text(m_ui.hexfield->decodeText(selection));
263 m_ui.stringVal->setText(text);
264
265 if (m_selection.first & (align - 1) || m_selection.second - m_selection.first != align) {
266 m_ui.sintVal->clear();
267 m_ui.sintVal->setReadOnly(true);
268 m_ui.uintVal->clear();
269 m_ui.uintVal->setReadOnly(true);
270 return;
271 }
272 union {
273 uint32_t u32;
274 int32_t i32;
275 uint16_t u16;
276 int16_t i16;
277 uint8_t u8;
278 int8_t i8;
279 } value;
280 switch (align) {
281 case 1:
282 value.u8 = core->rawRead8(core, m_selection.first, m_ui.segments->value());
283 m_ui.sintVal->setText(QString::number(value.i8));
284 m_ui.uintVal->setText(QString::number(value.u8));
285 break;
286 case 2:
287 value.u16 = core->rawRead16(core, m_selection.first, m_ui.segments->value());
288 m_ui.sintVal->setText(QString::number(value.i16));
289 m_ui.uintVal->setText(QString::number(value.u16));
290 break;
291 case 4:
292 value.u32 = core->rawRead32(core, m_selection.first, m_ui.segments->value());
293 m_ui.sintVal->setText(QString::number(value.i32));
294 m_ui.uintVal->setText(QString::number(value.u32));
295 break;
296 }
297 m_ui.sintVal->setReadOnly(false);
298 m_ui.uintVal->setReadOnly(false);
299}
300
301void MemoryView::saveRange() {
302 MemoryDump* memdump = new MemoryDump(m_controller);
303 memdump->setAttribute(Qt::WA_DeleteOnClose);
304 memdump->setAddress(m_selection.first);
305 memdump->setSegment(m_ui.segments->value());
306 memdump->setByteCount(m_selection.second - m_selection.first);
307 memdump->show();
308}