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 "GBAApp.h"
9
10#include <QFontDatabase>
11#include <QTimer>
12
13#include <mgba/internal/gba/gba.h>
14#ifdef M_CORE_GB
15#include <mgba/internal/gb/gb.h>
16#include <mgba/internal/gb/io.h>
17#endif
18
19using namespace QGBA;
20
21ObjView::ObjView(GameController* controller, QWidget* parent)
22 : AssetView(controller, parent)
23 , m_controller(controller)
24 , m_tileStatus{}
25 , m_objId(0)
26 , m_objInfo{}
27{
28 m_ui.setupUi(this);
29 m_ui.tile->setController(controller);
30
31 const QFont font = QFontDatabase::systemFont(QFontDatabase::FixedFont);
32
33 m_ui.x->setFont(font);
34 m_ui.y->setFont(font);
35 m_ui.w->setFont(font);
36 m_ui.h->setFont(font);
37 m_ui.address->setFont(font);
38 m_ui.priority->setFont(font);
39 m_ui.palette->setFont(font);
40 m_ui.transform->setFont(font);
41 m_ui.mode->setFont(font);
42
43 connect(m_ui.tiles, SIGNAL(indexPressed(int)), this, SLOT(translateIndex(int)));
44 connect(m_ui.objId, SIGNAL(valueChanged(int)), this, SLOT(selectObj(int)));
45 connect(m_ui.magnification, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), [this]() {
46 updateTiles(true);
47 });
48}
49
50void ObjView::selectObj(int obj) {
51 m_objId = obj;
52 updateTiles(true);
53}
54
55void ObjView::translateIndex(int index) {
56 unsigned x = index % m_objInfo.width;
57 unsigned y = index / m_objInfo.width;
58 m_ui.tile->selectIndex(x + y * m_objInfo.stride + m_tileOffset);
59}
60
61#ifdef M_CORE_GBA
62void ObjView::updateTilesGBA(bool force) {
63 const GBA* gba = static_cast<const GBA*>(m_controller->thread()->core->board);
64 const GBAObj* obj = &gba->video.oam.obj[m_objId];
65
66 unsigned shape = GBAObjAttributesAGetShape(obj->a);
67 unsigned size = GBAObjAttributesBGetSize(obj->b);
68 unsigned width = GBAVideoObjSizes[shape * 4 + size][0];
69 unsigned height = GBAVideoObjSizes[shape * 4 + size][1];
70 unsigned tile = GBAObjAttributesCGetTile(obj->c);
71 m_ui.tiles->setTileCount(width * height / 64);
72 m_ui.tiles->setMinimumSize(QSize(width, height) * m_ui.magnification->value());
73 m_ui.tiles->resize(QSize(width, height) * m_ui.magnification->value());
74 unsigned palette = GBAObjAttributesCGetPalette(obj->c);
75 unsigned tileBase = tile;
76 if (GBAObjAttributesAIs256Color(obj->a)) {
77 m_ui.palette->setText("256-color");
78 mTileCacheSetPalette(m_tileCache.get(), 1);
79 m_ui.tile->setPalette(0);
80 m_ui.tile->setPaletteSet(1, 1024, 1536);
81 palette = 1;
82 tile = tile / 2 + 1024;
83 } else {
84 m_ui.palette->setText(QString::number(palette));
85 mTileCacheSetPalette(m_tileCache.get(), 0);
86 m_ui.tile->setPalette(palette);
87 m_ui.tile->setPaletteSet(0, 2048, 3072);
88 palette += 16;
89 tile += 2048;
90 }
91 ObjInfo newInfo{
92 tile,
93 width / 8,
94 height / 8,
95 width / 8
96 };
97 if (newInfo != m_objInfo) {
98 force = true;
99 }
100 m_objInfo = newInfo;
101 m_tileOffset = tile;
102 GBARegisterDISPCNT dispcnt = gba->memory.io[0]; // FIXME: Register name can't be imported due to namespacing issues
103 if (!GBARegisterDISPCNTIsObjCharacterMapping(dispcnt)) {
104 newInfo.stride = 0x20 >> (GBAObjAttributesAGet256Color(obj->a));
105 };
106
107 int i = 0;
108 for (int y = 0; y < height / 8; ++y) {
109 for (int x = 0; x < width / 8; ++x, ++i, ++tile, ++tileBase) {
110 const uint16_t* data = mTileCacheGetTileIfDirty(m_tileCache.get(), &m_tileStatus[32 * tileBase], tile, palette);
111 if (data) {
112 m_ui.tiles->setTile(i, data);
113 } else if (force) {
114 m_ui.tiles->setTile(i, mTileCacheGetTile(m_tileCache.get(), tile, palette));
115 }
116 }
117 tile += newInfo.stride - width / 8;
118 tileBase += newInfo.stride - width / 8;
119 }
120
121 m_ui.x->setText(QString::number(GBAObjAttributesBGetX(obj->b)));
122 m_ui.y->setText(QString::number(GBAObjAttributesAGetY(obj->a)));
123 m_ui.w->setText(QString::number(width));
124 m_ui.h->setText(QString::number(height));
125
126 m_ui.address->setText(tr("0x%0").arg(BASE_OAM + m_objId * sizeof(*obj), 8, 16, QChar('0')));
127 m_ui.priority->setText(QString::number(GBAObjAttributesCGetPriority(obj->c)));
128 m_ui.flippedH->setChecked(GBAObjAttributesBIsHFlip(obj->b));
129 m_ui.flippedV->setChecked(GBAObjAttributesBIsVFlip(obj->b));
130 m_ui.enabled->setChecked(!GBAObjAttributesAIsDisable(obj->a) || GBAObjAttributesAIsTransformed(obj->a));
131 m_ui.doubleSize->setChecked(GBAObjAttributesAIsDoubleSize(obj->a) && GBAObjAttributesAIsTransformed(obj->a));
132 m_ui.mosaic->setChecked(GBAObjAttributesAIsMosaic(obj->a));
133
134 if (GBAObjAttributesAIsTransformed(obj->a)) {
135 m_ui.transform->setText(QString::number(GBAObjAttributesBGetMatIndex(obj->b)));
136 } else {
137 m_ui.transform->setText(tr("Off"));
138 }
139
140 switch (GBAObjAttributesAGetMode(obj->a)) {
141 case OBJ_MODE_NORMAL:
142 m_ui.mode->setText(tr("Normal"));
143 break;
144 case OBJ_MODE_SEMITRANSPARENT:
145 m_ui.mode->setText(tr("Trans"));
146 break;
147 case OBJ_MODE_OBJWIN:
148 m_ui.mode->setText(tr("OBJWIN"));
149 break;
150 default:
151 m_ui.mode->setText(tr("Invalid"));
152 break;
153 }
154}
155#endif
156
157#ifdef M_CORE_GB
158void ObjView::updateTilesGB(bool force) {
159 const GB* gb = static_cast<const GB*>(m_controller->thread()->core->board);
160 const GBObj* obj = &gb->video.oam.obj[m_objId];
161
162 unsigned width = 8;
163 unsigned height = 8;
164 GBRegisterLCDC lcdc = gb->memory.io[REG_LCDC];
165 if (GBRegisterLCDCIsObjSize(lcdc)) {
166 height = 16;
167 }
168 unsigned tile = obj->tile;
169 m_ui.tiles->setTileCount(width * height / 64);
170 m_ui.tiles->setMinimumSize(QSize(width, height) * m_ui.magnification->value());
171 m_ui.tiles->resize(QSize(width, height) * m_ui.magnification->value());
172 int palette = 0;
173 if (gb->model >= GB_MODEL_CGB) {
174 if (GBObjAttributesIsBank(obj->attr)) {
175 tile += 512;
176 }
177 palette = GBObjAttributesGetCGBPalette(obj->attr);
178 } else {
179 palette = GBObjAttributesGetPalette(obj->attr);
180 }
181 ObjInfo newInfo{
182 tile,
183 1,
184 height / 8,
185 1
186 };
187 if (newInfo != m_objInfo) {
188 force = true;
189 }
190 m_objInfo = newInfo;
191 m_tileOffset = tile;
192
193 int i = 0;
194 m_ui.palette->setText(QString::number(palette));
195 palette += 8;
196 mTileCacheSetPalette(m_tileCache.get(), 0);
197 m_ui.tile->setPalette(palette);
198 m_ui.tile->setPaletteSet(0, 512, 1024);
199 for (int y = 0; y < height / 8; ++y, ++i) {
200 unsigned t = tile + i;
201 const uint16_t* data = mTileCacheGetTileIfDirty(m_tileCache.get(), &m_tileStatus[16 * t], t, palette);
202 if (data) {
203 m_ui.tiles->setTile(i, data);
204 } else if (force) {
205 m_ui.tiles->setTile(i, mTileCacheGetTile(m_tileCache.get(), t, palette));
206 }
207 }
208
209 m_ui.x->setText(QString::number(obj->x));
210 m_ui.y->setText(QString::number(obj->y));
211 m_ui.w->setText(QString::number(width));
212 m_ui.h->setText(QString::number(height));
213
214 m_ui.address->setText(tr("0x%0").arg(GB_BASE_OAM + m_objId * sizeof(*obj), 4, 16, QChar('0')));
215 m_ui.priority->setText(QString::number(GBObjAttributesGetPriority(obj->attr)));
216 m_ui.flippedH->setChecked(GBObjAttributesIsXFlip(obj->attr));
217 m_ui.flippedV->setChecked(GBObjAttributesIsYFlip(obj->attr));
218 m_ui.enabled->setChecked(obj->y != 0 && obj->y < 160);
219 m_ui.doubleSize->setChecked(false);
220 m_ui.mosaic->setChecked(false);
221 m_ui.transform->setText(tr("N/A"));
222 m_ui.mode->setText(tr("N/A"));
223}
224#endif
225
226
227bool ObjView::ObjInfo::operator!=(const ObjInfo& other) {
228 return other.tile != tile ||
229 other.width != width ||
230 other.height != height ||
231 other.stride != stride;
232}