all repos — mgba @ 4f671290989321fc98ec15e0ec307eaafc07d031

mGBA Game Boy Advance Emulator

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}