all repos — mgba @ c04d627de8471939afd395c737181f52241df08d

mGBA Game Boy Advance Emulator

src/platform/qt/MapView.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#include "MapView.h"
  7
  8#include "CoreController.h"
  9#include "GBAApp.h"
 10#include "LogController.h"
 11
 12#include <mgba-util/png-io.h>
 13#ifdef M_CORE_GBA
 14#include <mgba/internal/gba/memory.h>
 15#endif
 16#ifdef M_CORE_GB
 17#include <mgba/internal/gb/memory.h>
 18#endif
 19
 20#include <QButtonGroup>
 21#include <QFontDatabase>
 22#include <QMouseEvent>
 23#include <QRadioButton>
 24#include <QTimer>
 25
 26using namespace QGBA;
 27
 28MapView::MapView(std::shared_ptr<CoreController> controller, QWidget* parent)
 29	: AssetView(controller, parent)
 30	, m_controller(controller)
 31{
 32	m_ui.setupUi(this);
 33	m_ui.tile->setController(controller);
 34
 35	switch (m_controller->platform()) {
 36#ifdef M_CORE_GBA
 37	case PLATFORM_GBA:
 38		m_boundary = 2048;
 39		m_addressBase = BASE_VRAM;
 40		m_addressWidth = 8;
 41		break;
 42#endif
 43#ifdef M_CORE_GB
 44	case PLATFORM_GB:
 45		m_boundary = 1024;
 46		m_addressBase = GB_BASE_VRAM;
 47		m_addressWidth = 4;
 48		break;
 49#endif
 50	default:
 51		return;
 52	}
 53	m_ui.tile->setBoundary(m_boundary, 0, 0);
 54
 55	connect(m_ui.magnification, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), [this]() {
 56		updateTiles(true);
 57	});
 58
 59	CoreController::Interrupter interrupter(m_controller);
 60	const mCoreChannelInfo* videoLayers;
 61	size_t nVideo = m_controller->thread()->core->listVideoLayers(m_controller->thread()->core, &videoLayers);
 62	QButtonGroup* group = new QButtonGroup(this);
 63	for (size_t i = 0; i < nVideo; ++i) {
 64		if (strncmp(videoLayers[i].internalName, "bg", 2) != 0) {
 65			continue;
 66		}
 67		QRadioButton* button = new QRadioButton(tr(videoLayers[i].visibleName));
 68		if (!i) {
 69			button->setChecked(true);
 70		}
 71		m_ui.bgLayout->addWidget(button);
 72		connect(button, &QAbstractButton::pressed, button, [this, i]() {
 73			selectMap(i);
 74		});
 75		group->addButton(button);
 76	}
 77#ifdef USE_PNG
 78	connect(m_ui.exportButton, &QAbstractButton::clicked, this, &MapView::exportMap);
 79#else
 80	m_ui.exportButton->setVisible(false);
 81#endif
 82	m_ui.map->installEventFilter(this);
 83	m_ui.tile->addCustomProperty("mapAddr", tr("Map Addr."));
 84	m_ui.tile->addCustomProperty("flip", tr("Mirror"));
 85	selectTile(0, 0);
 86}
 87
 88void MapView::selectMap(int map) {
 89	if (map >= mMapCacheSetSize(&m_cacheSet->maps)) {
 90		return;
 91	}
 92	if (map == m_map) {
 93		return;
 94	}
 95	m_map = map;
 96	updateTiles(true);
 97}
 98
 99void MapView::selectTile(int x, int y) {
100	CoreController::Interrupter interrupter(m_controller);
101	mMapCache* mapCache = mMapCacheSetGetPointer(&m_cacheSet->maps, m_map);
102	size_t tileCache = mTileCacheSetIndex(&m_cacheSet->tiles, mapCache->tileCache);
103	m_ui.tile->setBoundary(m_boundary, tileCache, tileCache);
104	uint32_t location = mMapCacheTileId(mapCache, x, y);
105	mMapCacheEntry* entry = &m_mapStatus[location];
106	m_ui.tile->selectIndex(entry->tileId + mapCache->tileStart);
107	m_ui.tile->setPalette(mMapCacheEntryFlagsGetPaletteId(entry->flags));
108	m_ui.tile->setFlip(mMapCacheEntryFlagsGetHMirror(entry->flags), mMapCacheEntryFlagsGetVMirror(entry->flags));
109	location <<= (mMapCacheSystemInfoGetMapAlign(mapCache->sysConfig));
110	location += m_addressBase + mapCache->mapStart;
111
112	QString flip(tr("None"));
113	if (mMapCacheEntryFlagsGetHMirror(entry->flags) && mMapCacheEntryFlagsGetVMirror(entry->flags)) {
114		flip = tr("Both");
115	} else if (mMapCacheEntryFlagsGetHMirror(entry->flags)) {
116		flip = tr("Horizontal");
117	} else if (mMapCacheEntryFlagsGetVMirror(entry->flags)) {
118		flip = tr("Vertical");
119	}
120	m_ui.tile->setCustomProperty("flip", flip);
121	m_ui.tile->setCustomProperty("mapAddr", QString("%0%1")
122		.arg(m_addressWidth == 8 ? "0x" : "")
123		.arg(location, m_addressWidth, 16, QChar('0')));
124}
125
126bool MapView::eventFilter(QObject* obj, QEvent* event) {
127	if (event->type() != QEvent::MouseButtonPress) {
128		return false;
129	}
130	int x = static_cast<QMouseEvent*>(event)->x();
131	int y = static_cast<QMouseEvent*>(event)->y();
132	x /= 8 * m_ui.magnification->value();
133	y /= 8 * m_ui.magnification->value();
134	selectTile(x, y);
135	return true;
136}
137
138void MapView::updateTilesGBA(bool force) {
139	{
140		CoreController::Interrupter interrupter(m_controller);
141		mMapCache* mapCache = mMapCacheSetGetPointer(&m_cacheSet->maps, m_map);
142		int tilesW = 1 << mMapCacheSystemInfoGetTilesWide(mapCache->sysConfig);
143		int tilesH = 1 << mMapCacheSystemInfoGetTilesHigh(mapCache->sysConfig);
144		m_rawMap = QImage(QSize(tilesW * 8, tilesH * 8), QImage::Format_ARGB32);
145		uchar* bgBits = m_rawMap.bits();
146		for (int j = 0; j < tilesH; ++j) {
147			for (int i = 0; i < tilesW; ++i) {
148				mMapCacheCleanTile(mapCache, m_mapStatus, i, j);
149			}
150			for (int i = 0; i < 8; ++i) {
151				memcpy(static_cast<void*>(&bgBits[tilesW * 32 * (i + j * 8)]), mMapCacheGetRow(mapCache, i + j * 8), tilesW * 32);
152			}
153		}
154	}
155	m_rawMap = m_rawMap.rgbSwapped();
156	QPixmap map = QPixmap::fromImage(m_rawMap.convertToFormat(QImage::Format_RGB32));
157	if (m_ui.magnification->value() > 1) {
158		map = map.scaled(map.size() * m_ui.magnification->value());
159	}
160	m_ui.map->setPixmap(map);
161}
162
163#ifdef M_CORE_GB
164void MapView::updateTilesGB(bool force) {
165	updateTilesGBA(force);
166}
167#endif
168
169#ifdef USE_PNG
170void MapView::exportMap() {
171	QString filename = GBAApp::app()->getSaveFileName(this, tr("Export map"),
172	                                                  tr("Portable Network Graphics (*.png)"));
173	VFile* vf = VFileDevice::open(filename, O_WRONLY | O_CREAT | O_TRUNC);
174	if (!vf) {
175		LOG(QT, ERROR) << tr("Failed to open output PNG file: %1").arg(filename);
176		return;
177	}
178
179	CoreController::Interrupter interrupter(m_controller);
180	png_structp png = PNGWriteOpen(vf);
181	png_infop info = PNGWriteHeaderA(png, m_rawMap.width(), m_rawMap.height());
182
183	mMapCache* mapCache = mMapCacheSetGetPointer(&m_cacheSet->maps, m_map);
184	QImage map = m_rawMap.rgbSwapped();
185	PNGWritePixelsA(png, map.width(), map.height(), map.bytesPerLine() / 4, static_cast<const void*>(map.constBits()));
186	PNGWriteClose(png, info);
187}
188#endif