all repos — mgba @ 158e2efb2c4729196650c68fd134af312c4f2e6e

mGBA Game Boy Advance Emulator

src/platform/qt/ObjView.cpp (view raw)

  1/* Copyright (c) 2013-2016 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 "ObjView.h"
  7
  8#include "CoreController.h"
  9#include "GBAApp.h"
 10
 11#include <QAction>
 12#include <QClipboard>
 13#include <QFontDatabase>
 14#include <QListWidgetItem>
 15#include <QTimer>
 16
 17#include "LogController.h"
 18#include "VFileDevice.h"
 19
 20#ifdef M_CORE_GBA
 21#include <mgba/internal/gba/gba.h>
 22#endif
 23#ifdef M_CORE_GB
 24#include <mgba/internal/gb/gb.h>
 25#endif
 26#include <mgba-util/vfs.h>
 27
 28using namespace QGBA;
 29
 30ObjView::ObjView(std::shared_ptr<CoreController> controller, QWidget* parent)
 31	: AssetView(controller, parent)
 32	, m_controller(controller)
 33{
 34	m_ui.setupUi(this);
 35	m_ui.tile->setController(controller);
 36
 37	const QFont font = QFontDatabase::systemFont(QFontDatabase::FixedFont);
 38
 39	m_ui.x->setFont(font);
 40	m_ui.y->setFont(font);
 41	m_ui.w->setFont(font);
 42	m_ui.h->setFont(font);
 43	m_ui.address->setFont(font);
 44	m_ui.priority->setFont(font);
 45	m_ui.palette->setFont(font);
 46	m_ui.transform->setFont(font);
 47	m_ui.mode->setFont(font);
 48
 49	connect(m_ui.tiles, &TilePainter::indexPressed, this, &ObjView::translateIndex);
 50	connect(m_ui.tiles, &TilePainter::needsRedraw, this, [this]() {
 51		updateTiles(true);
 52	});
 53	connect(m_ui.objId, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), this, &ObjView::selectObj);
 54	connect(m_ui.magnification, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), [this]() {
 55		updateTiles(true);
 56	});
 57	connect(m_ui.exportButton, &QAbstractButton::clicked, this, &ObjView::exportObj);
 58	connect(m_ui.copyButton, &QAbstractButton::clicked, this, &ObjView::copyObj);
 59
 60	connect(m_ui.objList, &QListWidget::currentItemChanged, [this]() {
 61		QListWidgetItem* item = m_ui.objList->currentItem();
 62		if (item) {
 63			selectObj(item->data(Qt::UserRole).toInt());
 64		}
 65	});
 66
 67	QAction* exportAction = new QAction(this);
 68	exportAction->setShortcut(QKeySequence::Save);
 69	connect(exportAction, &QAction::triggered, this, &ObjView::exportObj);
 70	addAction(exportAction);
 71
 72	QAction* copyAction = new QAction(this);
 73	copyAction->setShortcut(QKeySequence::Copy);
 74	connect(copyAction, &QAction::triggered, this, &ObjView::copyObj);
 75	addAction(copyAction);
 76}
 77
 78void ObjView::selectObj(int obj) {
 79	m_objId = obj;
 80	bool blocked = m_ui.objId->blockSignals(true);
 81	m_ui.objId->setValue(m_objId);
 82	m_ui.objId->blockSignals(blocked);
 83	if (m_objs.size() > obj) {
 84		blocked = m_ui.objList->blockSignals(true);
 85		m_ui.objList->setCurrentItem(m_objs[obj]);
 86		m_ui.objList->blockSignals(blocked);
 87	}
 88	updateTiles(true);
 89}
 90
 91void ObjView::translateIndex(int index) {
 92	unsigned x = index % m_objInfo.width;
 93	unsigned y = index / m_objInfo.width;
 94	m_ui.tile->selectIndex(x + y * m_objInfo.stride + m_tileOffset + m_boundary);
 95}
 96
 97#ifdef M_CORE_GBA
 98void ObjView::updateTilesGBA(bool force) {
 99	m_ui.objId->setMaximum(127);
100	const GBA* gba = static_cast<const GBA*>(m_controller->thread()->core->board);
101	const GBAObj* obj = &gba->video.oam.obj[m_objId];
102
103	updateObjList(128);
104
105	ObjInfo newInfo;
106	lookupObj(m_objId, &newInfo);
107
108	m_ui.tiles->setTileCount(newInfo.width * newInfo.height);
109	m_ui.tiles->setMinimumSize(QSize(newInfo.width * 8, newInfo.height * 8) * m_ui.magnification->value());
110	m_ui.tiles->resize(QSize(newInfo.width * 8, newInfo.height * 8) * m_ui.magnification->value());
111	unsigned tileBase = newInfo.tile;
112	unsigned tile = newInfo.tile;
113	if (GBAObjAttributesAIs256Color(obj->a)) {
114		m_ui.palette->setText("256-color");
115		m_ui.tile->setBoundary(1024, 1, 3);
116		m_ui.tile->setPalette(0);
117		m_boundary = 1024;
118		tileBase *= 2;
119	} else {
120		m_ui.palette->setText(QString::number(newInfo.paletteId));
121		m_ui.tile->setBoundary(2048, 0, 2);
122		m_ui.tile->setPalette(newInfo.paletteId);
123		m_boundary = 2048;
124	}
125	if (newInfo != m_objInfo) {
126		force = true;
127	}
128	m_objInfo = newInfo;
129	m_tileOffset = newInfo.tile;
130	mTileCache* tileCache = mTileCacheSetGetPointer(&m_cacheSet->tiles, newInfo.paletteSet);
131
132	int i = 0;
133	for (int y = 0; y < newInfo.height; ++y) {
134		for (int x = 0; x < newInfo.width; ++x, ++i, ++tile, ++tileBase) {
135			const color_t* data = mTileCacheGetTileIfDirty(tileCache, &m_tileStatus[16 * tileBase], tile, newInfo.paletteId);
136			if (data) {
137				m_ui.tiles->setTile(i, data);
138			} else if (force) {
139				m_ui.tiles->setTile(i, mTileCacheGetTile(tileCache, tile, newInfo.paletteId));
140			}
141		}
142		tile += newInfo.stride - newInfo.width;
143		tileBase += newInfo.stride - newInfo.width;
144	}
145
146	m_ui.x->setText(QString::number(newInfo.x));
147	m_ui.y->setText(QString::number(newInfo.y));
148	m_ui.w->setText(QString::number(newInfo.width * 8));
149	m_ui.h->setText(QString::number(newInfo.height * 8));
150
151	m_ui.address->setText(tr("0x%0").arg(BASE_OAM + m_objId * sizeof(*obj), 8, 16, QChar('0')));
152	m_ui.priority->setText(QString::number(newInfo.priority));
153	m_ui.flippedH->setChecked(newInfo.hflip);
154	m_ui.flippedV->setChecked(newInfo.vflip);
155	m_ui.enabled->setChecked(newInfo.enabled);
156	m_ui.doubleSize->setChecked(GBAObjAttributesAIsDoubleSize(obj->a) && GBAObjAttributesAIsTransformed(obj->a));
157	m_ui.mosaic->setChecked(GBAObjAttributesAIsMosaic(obj->a));
158
159	if (GBAObjAttributesAIsTransformed(obj->a)) {
160		m_ui.transform->setText(QString::number(GBAObjAttributesBGetMatIndex(obj->b)));
161	} else {
162		m_ui.transform->setText(tr("Off"));
163	}
164
165	switch (GBAObjAttributesAGetMode(obj->a)) {
166	case OBJ_MODE_NORMAL:
167		m_ui.mode->setText(tr("Normal"));
168		break;
169	case OBJ_MODE_SEMITRANSPARENT:
170		m_ui.mode->setText(tr("Trans"));
171		break;
172	case OBJ_MODE_OBJWIN:
173		m_ui.mode->setText(tr("OBJWIN"));
174		break;
175	default:
176		m_ui.mode->setText(tr("Invalid"));
177		break;
178	}
179}
180#endif
181
182#ifdef M_CORE_GB
183void ObjView::updateTilesGB(bool force) {
184	m_ui.objId->setMaximum(39);
185	const GB* gb = static_cast<const GB*>(m_controller->thread()->core->board);
186	const GBObj* obj = &gb->video.oam.obj[m_objId];
187
188	updateObjList(40);
189
190	ObjInfo newInfo;
191	lookupObj(m_objId, &newInfo);
192
193	mTileCache* tileCache = mTileCacheSetGetPointer(&m_cacheSet->tiles, 0);
194	unsigned tile = newInfo.tile;
195	m_ui.tiles->setTileCount(newInfo.height);
196	m_ui.tile->setBoundary(1024, 0, 0);
197	m_ui.tiles->setMinimumSize(QSize(8, newInfo.height * 8) * m_ui.magnification->value());
198	m_ui.tiles->resize(QSize(8, newInfo.height * 8) * m_ui.magnification->value());
199	m_ui.palette->setText(QString::number(newInfo.paletteId - 8));
200
201	if (newInfo != m_objInfo) {
202		force = true;
203	}
204	m_objInfo = newInfo;
205	m_tileOffset = tile;
206	m_boundary = 1024;
207
208	int i = 0;
209	m_ui.tile->setPalette(newInfo.paletteId);
210	for (int y = 0; y < newInfo.height; ++y, ++i) {
211		unsigned t = tile + i;
212		const color_t* data = mTileCacheGetTileIfDirty(tileCache, &m_tileStatus[8 * t], t, newInfo.paletteId);
213		if (data) {
214			m_ui.tiles->setTile(i, data);
215		} else if (force) {
216			m_ui.tiles->setTile(i, mTileCacheGetTile(tileCache, t, newInfo.paletteId));
217		}
218	}
219
220	m_ui.x->setText(QString::number(newInfo.x));
221	m_ui.y->setText(QString::number(newInfo.y));
222	m_ui.w->setText(QString::number(8));
223	m_ui.h->setText(QString::number(newInfo.height * 8));
224
225	m_ui.address->setText(tr("0x%0").arg(GB_BASE_OAM + m_objId * sizeof(*obj), 4, 16, QChar('0')));
226	m_ui.priority->setText(QString::number(newInfo.priority));
227	m_ui.flippedH->setChecked(newInfo.hflip);
228	m_ui.flippedV->setChecked(newInfo.vflip);
229	m_ui.enabled->setChecked(newInfo.enabled);
230	m_ui.doubleSize->setChecked(false);
231	m_ui.mosaic->setChecked(false);
232	m_ui.transform->setText(tr("N/A"));
233	m_ui.mode->setText(tr("N/A"));
234}
235#endif
236
237void ObjView::updateObjList(int maxObj) {
238	for (int i = 0; i < maxObj; ++i) {
239		if (m_objs.size() <= i) {
240			QListWidgetItem* item = new QListWidgetItem;
241			item->setText(QString::number(i));
242			item->setData(Qt::UserRole, i);
243			item->setSizeHint(QSize(64, 96));
244			if (m_objId == i) {
245				item->setSelected(true);
246			}
247			m_objs.append(item);
248			m_ui.objList->addItem(item);
249		}
250		QListWidgetItem* item = m_objs[i];
251		ObjInfo info;
252		lookupObj(i, &info);
253		item->setIcon(QPixmap::fromImage(std::move(compositeObj(info))));
254	}
255}
256
257void ObjView::exportObj() {
258	QString filename = GBAApp::app()->getSaveFileName(this, tr("Export sprite"),
259	                                                  tr("Portable Network Graphics (*.png)"));
260	if (filename.isNull()) {
261		return;
262	}
263	CoreController::Interrupter interrupter(m_controller);
264	QImage obj = compositeObj(m_objInfo);
265	obj.save(filename, "PNG");
266}
267
268void ObjView::copyObj() {
269	CoreController::Interrupter interrupter(m_controller);
270	GBAApp::app()->clipboard()->setImage(compositeObj(m_objInfo));
271}