/* Copyright (c) 2013-2016 Jeffrey Pfau * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "ObjView.h" #include "CoreController.h" #include "GBAApp.h" #include #include #include #include #include #include "LogController.h" #include "VFileDevice.h" #ifdef M_CORE_GBA #include #endif #ifdef M_CORE_GB #include #endif #include using namespace QGBA; ObjView::ObjView(std::shared_ptr controller, QWidget* parent) : AssetView(controller, parent) , m_controller(controller) { m_ui.setupUi(this); m_ui.tile->setController(controller); const QFont font = QFontDatabase::systemFont(QFontDatabase::FixedFont); m_ui.x->setFont(font); m_ui.y->setFont(font); m_ui.w->setFont(font); m_ui.h->setFont(font); m_ui.address->setFont(font); m_ui.priority->setFont(font); m_ui.palette->setFont(font); m_ui.transform->setFont(font); m_ui.xformPA->setFont(font); m_ui.xformPB->setFont(font); m_ui.xformPC->setFont(font); m_ui.xformPD->setFont(font); m_ui.mode->setFont(font); connect(m_ui.tiles, &TilePainter::indexPressed, this, &ObjView::translateIndex); connect(m_ui.tiles, &TilePainter::needsRedraw, this, [this]() { updateTiles(true); }); connect(m_ui.objId, static_cast(&QSpinBox::valueChanged), this, &ObjView::selectObj); connect(m_ui.magnification, static_cast(&QSpinBox::valueChanged), [this]() { updateTiles(true); }); connect(m_ui.exportButton, &QAbstractButton::clicked, this, &ObjView::exportObj); connect(m_ui.copyButton, &QAbstractButton::clicked, this, &ObjView::copyObj); connect(m_ui.objList, &QListWidget::currentItemChanged, [this]() { QListWidgetItem* item = m_ui.objList->currentItem(); if (item) { selectObj(item->data(Qt::UserRole).toInt()); } }); QAction* exportAction = new QAction(this); exportAction->setShortcut(QKeySequence::Save); connect(exportAction, &QAction::triggered, this, &ObjView::exportObj); addAction(exportAction); QAction* copyAction = new QAction(this); copyAction->setShortcut(QKeySequence::Copy); connect(copyAction, &QAction::triggered, this, &ObjView::copyObj); addAction(copyAction); } void ObjView::selectObj(int obj) { m_objId = obj; bool blocked = m_ui.objId->blockSignals(true); m_ui.objId->setValue(m_objId); m_ui.objId->blockSignals(blocked); if (m_objs.size() > obj) { blocked = m_ui.objList->blockSignals(true); m_ui.objList->setCurrentItem(m_objs[obj]); m_ui.objList->blockSignals(blocked); } updateTiles(true); } void ObjView::translateIndex(int index) { unsigned x = index % m_objInfo.width; unsigned y = index / m_objInfo.width; m_ui.tile->selectIndex(x + y * m_objInfo.stride + m_tileOffset + m_boundary); } #ifdef M_CORE_GBA void ObjView::updateTilesGBA(bool force) { m_ui.objId->setMaximum(127); const GBA* gba = static_cast(m_controller->thread()->core->board); const GBAObj* obj = &gba->video.oam.obj[m_objId]; updateObjList(128); ObjInfo newInfo; lookupObj(m_objId, &newInfo); m_ui.tiles->setTileCount(newInfo.width * newInfo.height); m_ui.tiles->setMinimumSize(QSize(newInfo.width * 8, newInfo.height * 8) * m_ui.magnification->value()); m_ui.tiles->resize(QSize(newInfo.width * 8, newInfo.height * 8) * m_ui.magnification->value()); unsigned tileBase = newInfo.tile; unsigned tile = newInfo.tile; if (GBAObjAttributesAIs256Color(obj->a)) { m_ui.palette->setText("256-color"); m_ui.tile->setBoundary(1024, 1, 3); m_ui.tile->setPalette(0); m_boundary = 1024; tileBase *= 2; } else { m_ui.palette->setText(QString::number(newInfo.paletteId)); m_ui.tile->setBoundary(2048, 0, 2); m_ui.tile->setPalette(newInfo.paletteId); m_boundary = 2048; } if (newInfo != m_objInfo) { force = true; } m_objInfo = newInfo; m_tileOffset = newInfo.tile; mTileCache* tileCache = mTileCacheSetGetPointer(&m_cacheSet->tiles, newInfo.paletteSet); int i = 0; for (int y = 0; y < newInfo.height; ++y) { for (int x = 0; x < newInfo.width; ++x, ++i, ++tile, ++tileBase) { const color_t* data = mTileCacheGetTileIfDirty(tileCache, &m_tileStatus[16 * tileBase], tile, newInfo.paletteId); if (data) { m_ui.tiles->setTile(i, data); } else if (force) { m_ui.tiles->setTile(i, mTileCacheGetTile(tileCache, tile, newInfo.paletteId)); } } tile += newInfo.stride - newInfo.width; tileBase += newInfo.stride - newInfo.width; } m_ui.x->setText(QString::number(newInfo.x)); m_ui.y->setText(QString::number(newInfo.y)); m_ui.w->setText(QString::number(newInfo.width * 8)); m_ui.h->setText(QString::number(newInfo.height * 8)); m_ui.address->setText(tr("0x%0").arg(BASE_OAM + m_objId * sizeof(*obj), 8, 16, QChar('0'))); m_ui.priority->setText(QString::number(newInfo.priority)); m_ui.flippedH->setChecked(newInfo.hflip); m_ui.flippedV->setChecked(newInfo.vflip); m_ui.enabled->setChecked(newInfo.enabled); m_ui.doubleSize->setChecked(GBAObjAttributesAIsDoubleSize(obj->a) && GBAObjAttributesAIsTransformed(obj->a)); m_ui.mosaic->setChecked(GBAObjAttributesAIsMosaic(obj->a)); if (GBAObjAttributesAIsTransformed(obj->a)) { int mtxId = GBAObjAttributesBGetMatIndex(obj->b); struct GBAOAMMatrix mat; LOAD_16LE(mat.a, 0, &gba->video.oam.mat[mtxId].a); LOAD_16LE(mat.b, 0, &gba->video.oam.mat[mtxId].b); LOAD_16LE(mat.c, 0, &gba->video.oam.mat[mtxId].c); LOAD_16LE(mat.d, 0, &gba->video.oam.mat[mtxId].d); m_ui.transform->setText(QString::number(mtxId)); m_ui.xformPA->setText(QString("%0").arg(mat.a / 256., 5, 'f', 2)); m_ui.xformPB->setText(QString("%0").arg(mat.b / 256., 5, 'f', 2)); m_ui.xformPC->setText(QString("%0").arg(mat.c / 256., 5, 'f', 2)); m_ui.xformPD->setText(QString("%0").arg(mat.d / 256., 5, 'f', 2)); } else { m_ui.transform->setText(tr("Off")); m_ui.xformPA->setText(tr("---")); m_ui.xformPB->setText(tr("---")); m_ui.xformPC->setText(tr("---")); m_ui.xformPD->setText(tr("---")); } switch (GBAObjAttributesAGetMode(obj->a)) { case OBJ_MODE_NORMAL: m_ui.mode->setText(tr("Normal")); break; case OBJ_MODE_SEMITRANSPARENT: m_ui.mode->setText(tr("Trans")); break; case OBJ_MODE_OBJWIN: m_ui.mode->setText(tr("OBJWIN")); break; default: m_ui.mode->setText(tr("Invalid")); break; } } #endif #ifdef M_CORE_GB void ObjView::updateTilesGB(bool force) { m_ui.objId->setMaximum(39); const GB* gb = static_cast(m_controller->thread()->core->board); const GBObj* obj = &gb->video.oam.obj[m_objId]; updateObjList(40); ObjInfo newInfo; lookupObj(m_objId, &newInfo); mTileCache* tileCache = mTileCacheSetGetPointer(&m_cacheSet->tiles, 0); unsigned tile = newInfo.tile; m_ui.tiles->setTileCount(newInfo.height); m_ui.tile->setBoundary(1024, 0, 0); m_ui.tiles->setMinimumSize(QSize(8, newInfo.height * 8) * m_ui.magnification->value()); m_ui.tiles->resize(QSize(8, newInfo.height * 8) * m_ui.magnification->value()); m_ui.palette->setText(QString::number(newInfo.paletteId - 8)); if (newInfo != m_objInfo) { force = true; } m_objInfo = newInfo; m_tileOffset = tile; m_boundary = 1024; int i = 0; m_ui.tile->setPalette(newInfo.paletteId); for (int y = 0; y < newInfo.height; ++y, ++i) { unsigned t = tile + i; const color_t* data = mTileCacheGetTileIfDirty(tileCache, &m_tileStatus[8 * t], t, newInfo.paletteId); if (data) { m_ui.tiles->setTile(i, data); } else if (force) { m_ui.tiles->setTile(i, mTileCacheGetTile(tileCache, t, newInfo.paletteId)); } } m_ui.x->setText(QString::number(newInfo.x)); m_ui.y->setText(QString::number(newInfo.y)); m_ui.w->setText(QString::number(8)); m_ui.h->setText(QString::number(newInfo.height * 8)); m_ui.address->setText(tr("0x%0").arg(GB_BASE_OAM + m_objId * sizeof(*obj), 4, 16, QChar('0'))); m_ui.priority->setText(QString::number(newInfo.priority)); m_ui.flippedH->setChecked(newInfo.hflip); m_ui.flippedV->setChecked(newInfo.vflip); m_ui.enabled->setChecked(newInfo.enabled); m_ui.doubleSize->setChecked(false); m_ui.mosaic->setChecked(false); m_ui.transform->setText(tr("N/A")); m_ui.xformPA->setText(tr("---")); m_ui.xformPB->setText(tr("---")); m_ui.xformPC->setText(tr("---")); m_ui.xformPD->setText(tr("---")); m_ui.mode->setText(tr("N/A")); } #endif void ObjView::updateObjList(int maxObj) { for (int i = 0; i < maxObj; ++i) { if (m_objs.size() <= i) { QListWidgetItem* item = new QListWidgetItem; item->setText(QString::number(i)); item->setData(Qt::UserRole, i); item->setSizeHint(QSize(64, 96)); if (m_objId == i) { item->setSelected(true); } m_objs.append(item); m_ui.objList->addItem(item); } QListWidgetItem* item = m_objs[i]; ObjInfo info; lookupObj(i, &info); item->setIcon(QPixmap::fromImage(std::move(compositeObj(info)))); } } void ObjView::exportObj() { QString filename = GBAApp::app()->getSaveFileName(this, tr("Export sprite"), tr("Portable Network Graphics (*.png)")); if (filename.isNull()) { return; } CoreController::Interrupter interrupter(m_controller); QImage obj = compositeObj(m_objInfo); obj.save(filename, "PNG"); } void ObjView::copyObj() { CoreController::Interrupter interrupter(m_controller); GBAApp::app()->clipboard()->setImage(compositeObj(m_objInfo)); }