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