all repos — mgba @ ffa5e65856c5950a26b3aedd2cc35d7f2f5041dc

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