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 const mCoreMemoryBlock& info = blocks[index];
211
212 m_region = qMakePair(info.start, info.end);
213 m_ui.segments->setValue(-1);
214 m_ui.segments->setVisible(info.maxSegment > 0);
215 m_ui.segmentColon->setVisible(info.maxSegment > 0);
216 m_ui.segments->setMaximum(info.maxSegment);
217 m_ui.hexfield->setRegion(info.start, info.end - info.start, info.shortName);
218}
219
220void MemoryView::setSegment(int segment) {
221 mCore* core = m_controller->thread()->core;
222 const mCoreMemoryBlock* blocks;
223 size_t nBlocks = core->listMemoryBlocks(core, &blocks);
224 const mCoreMemoryBlock& info = blocks[m_ui.regions->currentIndex()];
225
226 m_ui.hexfield->setSegment(info.maxSegment < segment ? info.maxSegment : segment);
227}
228
229void MemoryView::update() {
230 m_ui.hexfield->viewport()->update();
231 updateStatus();
232}
233
234void MemoryView::jumpToAddress(uint32_t address) {
235 if (address < m_region.first || address >= m_region.second) {
236 m_ui.regions->setCurrentIndex(0);
237 setIndex(0);
238 }
239 if (address < m_region.first || address >= m_region.second) {
240 return;
241 }
242 m_ui.hexfield->jumpToAddress(address);
243}
244
245void MemoryView::updateSelection(uint32_t start, uint32_t end) {
246 m_selection.first = start;
247 m_selection.second = end;
248 updateStatus();
249}
250
251void MemoryView::updateStatus() {
252 int align = m_ui.hexfield->alignment();
253 mCore* core = m_controller->thread()->core;
254 QByteArray selection(m_ui.hexfield->serialize());
255 QString text(m_ui.hexfield->decodeText(selection));
256 m_ui.stringVal->setText(text);
257
258 if (m_selection.first & (align - 1) || m_selection.second - m_selection.first != align) {
259 m_ui.sintVal->clear();
260 m_ui.sintVal->setReadOnly(true);
261 m_ui.uintVal->clear();
262 m_ui.uintVal->setReadOnly(true);
263 return;
264 }
265 union {
266 uint32_t u32;
267 int32_t i32;
268 uint16_t u16;
269 int16_t i16;
270 uint8_t u8;
271 int8_t i8;
272 } value;
273 switch (align) {
274 case 1:
275 value.u8 = core->rawRead8(core, m_selection.first, m_ui.segments->value());
276 m_ui.sintVal->setText(QString::number(value.i8));
277 m_ui.uintVal->setText(QString::number(value.u8));
278 break;
279 case 2:
280 value.u16 = core->rawRead16(core, m_selection.first, m_ui.segments->value());
281 m_ui.sintVal->setText(QString::number(value.i16));
282 m_ui.uintVal->setText(QString::number(value.u16));
283 break;
284 case 4:
285 value.u32 = core->rawRead32(core, m_selection.first, m_ui.segments->value());
286 m_ui.sintVal->setText(QString::number(value.i32));
287 m_ui.uintVal->setText(QString::number(value.u32));
288 break;
289 }
290 m_ui.sintVal->setReadOnly(false);
291 m_ui.uintVal->setReadOnly(false);
292}
293
294void MemoryView::saveRange() {
295 MemoryDump* memdump = new MemoryDump(m_controller);
296 memdump->setAttribute(Qt::WA_DeleteOnClose);
297 memdump->setAddress(m_selection.first);
298 memdump->setSegment(m_ui.segments->value());
299 memdump->setByteCount(m_selection.second - m_selection.first);
300 memdump->show();
301}