src/platform/qt/MemoryDump.cpp (view raw)
1/* Copyright (c) 2013-2019 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#include "MemoryDump.h"
7
8#include "CoreController.h"
9#include "GBAApp.h"
10#include "LogController.h"
11
12using namespace QGBA;
13
14MemoryDump::MemoryDump(std::shared_ptr<CoreController> controller, QWidget* parent)
15 : QDialog(parent, Qt::WindowTitleHint | Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint)
16 , m_controller(controller)
17{
18 m_ui.setupUi(this);
19
20 connect(this, &QDialog::accepted, this, &MemoryDump::save);
21}
22
23void MemoryDump::save() {
24 QString filename = GBAApp::app()->getSaveFileName(this, tr("Save memory region"));
25 if (filename.isNull()) {
26 return;
27 }
28 QFile outfile(filename);
29 if (!outfile.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
30 LOG(QT, WARN) << tr("Failed to open output file: %1").arg(filename);
31 return;
32 }
33 QByteArray out(serialize());
34 outfile.write(out);
35}
36
37void MemoryDump::setAddress(uint32_t start) {
38 m_ui.address->setValue(start);
39}
40
41void MemoryDump::setSegment(int seg) {
42 m_ui.segment->setValue(seg);
43}
44
45void MemoryDump::setByteCount(uint32_t count) {
46 m_ui.bytes->setValue(count);
47}
48
49QByteArray MemoryDump::serialize() {
50 CoreController::Interrupter interrupter(m_controller);
51 mCore* core = m_controller->thread()->core;
52 const mCoreMemoryBlock* blocks;
53 size_t nBlocks = core->listMemoryBlocks(core, &blocks);
54
55 int size = m_ui.bytes->value();
56 uint32_t start = m_ui.address->value();
57 int segment = m_ui.segment->value();
58 bool spanSegments = m_ui.spanSegments->isChecked();
59
60 QByteArray mem;
61 while (size) {
62 const mCoreMemoryBlock* bestMatch = NULL;
63 const char* block = NULL;
64 size_t blockSize = 0;
65 for (size_t i = 0; i < nBlocks; ++i) {
66 if (blocks[i].end <= start) {
67 continue;
68 }
69 if (blocks[i].start > start) {
70 continue;
71 }
72 block = static_cast<const char*>(core->getMemoryBlock(core, blocks[i].id, &blockSize));
73 if (block) {
74 bestMatch = &blocks[i];
75 break;
76 } else if (!bestMatch) {
77 bestMatch = &blocks[i];
78 blockSize = 0;
79 }
80 }
81 if (!spanSegments) {
82 blockSize = bestMatch->end - bestMatch->start;
83 } else if (!blockSize) {
84 blockSize = bestMatch->size;
85 }
86 size_t segmentSize = bestMatch->end - bestMatch->start;
87 if (bestMatch->segmentStart) {
88 segmentSize = bestMatch->segmentStart - bestMatch->start;
89 }
90 if (segment > 0) {
91 start += segment * segmentSize;
92 }
93 int maxFromRegion = blockSize - (start - bestMatch->start);
94 if (maxFromRegion <= 0) {
95 continue;
96 }
97 int fromRegion = std::min(size, maxFromRegion);
98 if (block && (segment >= 0 || segmentSize == blockSize)) {
99 block = &block[start - bestMatch->start];
100 mem.append(QByteArray::fromRawData(block, fromRegion));
101 size -= fromRegion;
102 start += fromRegion;
103 if (spanSegments) {
104 break;
105 }
106 } else {
107 for (int i = 0; i < 16; ++i) {
108 char datum = core->rawRead8(core, start, segment);
109 mem.append(datum);
110 ++start;
111 --size;
112 if (!size) {
113 break;
114 }
115 }
116 }
117 }
118
119 return mem;
120}