yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
snes_tile.cc
Go to the documentation of this file.
1#include "snes_tile.h"
2
3#include <algorithm>
4#include <cassert>
5#include <cstdint>
6#include <cstring>
7#include <stdexcept>
8#include <vector>
9
10namespace yaze {
11namespace gfx {
12
13// Bit set for object priority
14constexpr uint16_t TilePriorityBit = 0x2000;
15
16// Bit set for object hflip
17constexpr uint16_t TileHFlipBit = 0x4000;
18
19// Bit set for object vflip
20constexpr uint16_t TileVFlipBit = 0x8000;
21
22// Bits used for tile name
23constexpr uint16_t TileNameMask = 0x03FF;
24
25snes_tile8 UnpackBppTile(std::span<const uint8_t> data, const uint32_t offset,
26 const uint32_t bpp) {
27 snes_tile8 tile = {}; // Initialize to zero
28 assert(bpp >= 1 && bpp <= 8);
29 unsigned int bpp_pos[8]; // More for conveniance and readibility
30 for (int row = 0; row < 8; row++) { // Process rows first (Y coordinate)
31 for (int col = 0; col < 8; col++) { // Then columns (X coordinate)
32 if (bpp == 1) {
33 tile.data[row * 8 + col] = (data[offset + row] >> (7 - col)) & 0x01;
34 continue;
35 }
36 /* SNES bpp format interlace each byte of the first 2 bitplanes.
37 * | byte 1 of first bitplane | byte 1 of second bitplane |
38 * | byte 2 of first bitplane | byte 2 of second bitplane | ..
39 */
40 bpp_pos[0] = offset + row * 2;
41 bpp_pos[1] = offset + row * 2 + 1;
42 char mask = 1 << (7 - col);
43 tile.data[row * 8 + col] = (data[bpp_pos[0]] & mask) ? 1 : 0;
44 tile.data[row * 8 + col] |= ((data[bpp_pos[1]] & mask) ? 1 : 0) << 1;
45 if (bpp == 3) {
46 // When we have 3 bitplanes, the bytes for the third bitplane are after
47 // the 16 bytes of the 2 bitplanes.
48 bpp_pos[2] = offset + 16 + row;
49 tile.data[row * 8 + col] |= ((data[bpp_pos[2]] & mask) ? 1 : 0) << 2;
50 }
51 if (bpp >= 4) {
52 // For 4 bitplanes, the 2 added bitplanes are interlaced like the first
53 // two.
54 bpp_pos[2] = offset + 16 + row * 2;
55 bpp_pos[3] = offset + 16 + row * 2 + 1;
56 tile.data[row * 8 + col] |= ((data[bpp_pos[2]] & mask) ? 1 : 0) << 2;
57 tile.data[row * 8 + col] |= ((data[bpp_pos[3]] & mask) ? 1 : 0) << 3;
58 }
59 if (bpp == 8) {
60 bpp_pos[4] = offset + 32 + row * 2;
61 bpp_pos[5] = offset + 32 + row * 2 + 1;
62 bpp_pos[6] = offset + 48 + row * 2;
63 bpp_pos[7] = offset + 48 + row * 2 + 1;
64 tile.data[row * 8 + col] |= ((data[bpp_pos[4]] & mask) ? 1 : 0) << 4;
65 tile.data[row * 8 + col] |= ((data[bpp_pos[5]] & mask) ? 1 : 0) << 5;
66 tile.data[row * 8 + col] |= ((data[bpp_pos[6]] & mask) ? 1 : 0) << 6;
67 tile.data[row * 8 + col] |= ((data[bpp_pos[7]] & mask) ? 1 : 0) << 7;
68 }
69 }
70 }
71 return tile;
72}
73
74std::vector<uint8_t> PackBppTile(const snes_tile8& tile, const uint32_t bpp) {
75 // Allocate memory for output data
76 std::vector<uint8_t> output(bpp * 8, 0); // initialized with 0
77 unsigned maxcolor = 1 << bpp; // Fix: should be 1 << bpp, not 2 << bpp
78
79 // Iterate over all rows and columns of the tile
80 for (unsigned int row = 0; row < 8; row++) {
81 for (unsigned int col = 0; col < 8; col++) {
82 uint8_t color = tile.data[row * 8 + col];
83 if (color >= maxcolor) {
84 throw std::invalid_argument("Invalid color value.");
85 }
86
87 // 1bpp format
88 if (bpp == 1)
89 output[row] += (uint8_t)((color & 1) << (7 - col));
90
91 // 2bpp format
92 if (bpp >= 2) {
93 output[row * 2] += ((color & 1) << (7 - col));
94 output[row * 2 + 1] += (((color & 2) == 2) << (7 - col));
95 }
96
97 // 3bpp format
98 if (bpp == 3)
99 output[16 + row] += (((color & 4) == 4) << (7 - col));
100
101 // 4bpp format
102 if (bpp >= 4) {
103 output[16 + row * 2] += (((color & 4) == 4) << (7 - col));
104 output[16 + row * 2 + 1] += (((color & 8) == 8) << (7 - col));
105 }
106
107 // 8bpp format
108 if (bpp == 8) {
109 output[32 + row * 2] += (((color & 16) == 16) << (7 - col));
110 output[32 + row * 2 + 1] += (((color & 32) == 32) << (7 - col));
111 output[48 + row * 2] += (((color & 64) == 64) << (7 - col));
112 output[48 + row * 2 + 1] += (((color & 128) == 128) << (7 - col));
113 }
114 }
115 }
116 return output;
117}
118
119std::vector<uint8_t> ConvertBpp(std::span<const uint8_t> tiles,
120 uint32_t from_bpp, uint32_t to_bpp) {
121 unsigned int nb_tile = tiles.size() / (from_bpp * 8);
122 std::vector<uint8_t> converted(nb_tile * to_bpp * 8);
123
124 for (unsigned int i = 0; i < nb_tile; i++) {
125 snes_tile8 tile = UnpackBppTile(tiles, i * from_bpp * 8, from_bpp);
126 std::vector<uint8_t> packed_tile = PackBppTile(tile, to_bpp);
127 std::memcpy(converted.data() + i * to_bpp * 8, packed_tile.data(),
128 to_bpp * 8);
129 }
130 return converted;
131}
132
133std::vector<uint8_t> SnesTo8bppSheet(std::span<const uint8_t> sheet, int bpp,
134 int num_sheets) {
135 int xx = 0; // positions where we are at on the sheet
136 int yy = 0;
137 int pos = 0;
138 int ypos = 0;
139 int num_tiles = 64;
140 int buffer_size = 0x1000;
141
142 if (bpp == 2) {
143 bpp = 16;
144 num_tiles = 128;
145 buffer_size = 0x2000;
146 } else if (bpp == 3) {
147 bpp = 24;
148 } else if (bpp == 4) {
149 bpp = 32;
150 buffer_size = 0x4000;
151 } else if (bpp == 8) {
152 bpp = 64;
153 }
154
155 if (num_sheets != 1) {
156 num_tiles *= num_sheets;
157 buffer_size *= num_sheets;
158 }
159
160 // Safety check: Ensure input data is sufficient for requested tiles
161 if (static_cast<size_t>(num_tiles * bpp) > sheet.size()) {
162 // Clamp number of tiles to what we can read from input
163 // This prevents SIGSEGV if decompression returned truncated data
164 if (bpp > 0) {
165 num_tiles = static_cast<int>(sheet.size()) / bpp;
166 } else {
167 num_tiles = 0;
168 }
169 }
170
171 std::vector<uint8_t> sheet_buffer_out(buffer_size); // Zero initialized
172
173 for (int i = 0; i < num_tiles; i++) { // for each tiles, 16 per line
174 for (int y = 0; y < 8; y++) { // for each line
175 for (int x = 0; x < 8; x++) { //[0] + [1] + [16]
176 auto b1 = (sheet[(y * 2) + (bpp * pos)] & (kGraphicsBitmap[x]));
177 auto b2 = (sheet[((y * 2) + (bpp * pos)) + 1] & (kGraphicsBitmap[x]));
178 auto b3 = (sheet[(16 + y) + (bpp * pos)] & (kGraphicsBitmap[x]));
179 unsigned char b = 0;
180 if (b1 != 0) {
181 b |= 1;
182 }
183 if (b2 != 0) {
184 b |= 2;
185 }
186 if (b3 != 0 && bpp != 16) {
187 b |= 4;
188 }
189 sheet_buffer_out[x + xx + (y * 128) + (yy * 1024)] = b;
190 }
191 }
192 pos++;
193 ypos++;
194 xx += 8;
195 if (ypos >= 16) {
196 yy++;
197 xx = 0;
198 ypos = 0;
199 }
200 }
201 return sheet_buffer_out;
202}
203
204std::vector<uint8_t> IndexedToSnesSheet(std::span<const uint8_t> sheet, int bpp,
205 int num_sheets) {
206 if (sheet.empty()) {
207 return {};
208 }
209
210 const int tiles_per_row = kTilesheetWidth / 8;
211 const int default_tile_rows = (bpp == 2) ? 8 : 4;
212 const int computed_tile_rows =
213 static_cast<int>(sheet.size()) / (kTilesheetWidth * 8);
214 const int tile_rows = (computed_tile_rows > 0)
215 ? std::max(default_tile_rows, computed_tile_rows)
216 : default_tile_rows;
217 const int tiles_per_sheet = tiles_per_row * tile_rows;
218 const int total_tiles = tiles_per_sheet * num_sheets;
219 const int bytes_per_tile = bpp * 8;
220 const uint8_t max_color =
221 static_cast<uint8_t>((1u << static_cast<uint8_t>(bpp)) - 1u);
222
223 std::vector<uint8_t> output(total_tiles * bytes_per_tile, 0);
224
225 int xx = 0;
226 int yy = 0;
227 int pos = 0;
228 int ypos = 0;
229
230 for (int i = 0; i < total_tiles; i++) {
231 snes_tile8 tile = {};
232
233 for (int y = 0; y < 8; y++) {
234 for (int x = 0; x < 8; x++) {
235 const int index =
236 (x + xx) + (y * kTilesheetWidth) + (yy * kTilesheetWidth * 8);
237 if (index >= 0 && index < static_cast<int>(sheet.size())) {
238 tile.data[y * 8 + x] = sheet[index] & max_color;
239 }
240 }
241 }
242
243 auto packed_tile = PackBppTile(tile, bpp);
244 std::copy(packed_tile.begin(), packed_tile.end(),
245 output.begin() + (pos * bytes_per_tile));
246
247 pos++;
248 ypos++;
249 xx += 8;
250 if (ypos >= tiles_per_row) {
251 yy++;
252 xx = 0;
253 ypos = 0;
254 }
255 }
256
257 return output;
258}
259
260std::vector<uint8_t> Bpp8SnesToIndexed(std::vector<uint8_t> data,
261 uint64_t bpp) {
262 // 3BPP
263 // [r0,bp1],[r0,bp2],[r1,bp1],[r1,bp2],[r2,bp1],[r2,bp2],[r3,bp1],[r3,bp2]
264 // [r4,bp1],[r4,bp2],[r5,bp1],[r5,bp2],[r6,bp1],[r6,bp2],[r7,bp1],[r7,bp2]
265 // [r0,bp3],[r0,bp4],[r1,bp3],[r1,bp4],[r2,bp3],[r2,bp4],[r3,bp3],[r3,bp4]
266 // [r4,bp3],[r4,bp4],[r5,bp3],[r5,bp4],[r6,bp3],[r6,bp4],[r7,bp3],[r7,bp4]
267 // [r0,bp5],[r0,bp6],[r1,bp5],[r1,bp6],[r2,bp5],[r2,bp6],[r3,bp5],[r3,bp6]
268 // [r4,bp5],[r4,bp6],[r5,bp5],[r5,bp6],[r6,bp5],[r6,bp6],[r7,bp5],[r7,bp6]
269 // [r0,bp7],[r0,bp8],[r1,bp7],[r1,bp8],[r2,bp7],[r2,bp8],[r3,bp7],[r3,bp8]
270 // [r4,bp7],[r4,bp8],[r5,bp7],[r5,bp8],[r6,bp7],[r6,bp8],[r7,bp7],[r7,bp8]
271
272 // 16 tiles = 1024 bytes
273 auto buffer = std::vector<uint8_t>(data.size());
274 std::vector<std::vector<uint8_t>> bitmap_data;
275 bitmap_data.resize(0x80);
276 for (auto& each : bitmap_data) {
277 each.reserve(0x800);
278 }
279 int yy = 0;
280 int xx = 0;
281 int pos = 0;
282
283 const uint16_t sheet_width = 128;
284
285 // 64 = 4096 bytes
286 // 16 = 1024?
287 int ypos = 0;
288 // for each tiles //16 per lines
289 for (int i = 0; i < 4096; i++) {
290 // for each lines
291 for (int y = 0; y < 8; y++) {
292 //[0] + [1] + [16]
293 for (int x = 0; x < 8; x++) {
294 const uint16_t bitmask[] = {0x80, 0x40, 0x20, 0x10,
295 0x08, 0x04, 0x02, 0x01};
296 auto b1 = (data[(y * 2) + ((bpp * 8) * pos)] & (bitmask[x]));
297 auto b2 = (data[((y * 2) + ((bpp * 8) * pos)) + 1] & (bitmask[x]));
298 auto b3 = (data[(y * 2) + ((bpp * 8) * pos) + 16] & (bitmask[x]));
299 auto b4 = (data[(y * 2) + ((bpp * 8) * pos) + 17] & (bitmask[x]));
300 auto b5 = (data[(y * 2) + ((bpp * 8) * pos) + 32] & (bitmask[x]));
301 auto b6 = (data[(y * 2) + ((bpp * 8) * pos) + 33] & (bitmask[x]));
302 auto b7 = (data[(y * 2) + ((bpp * 8) * pos) + 48] & (bitmask[x]));
303 auto b8 = (data[(y * 2) + ((bpp * 8) * pos) + 49] & (bitmask[x]));
304
305 auto b = 0;
306 if (b1 != 0) {
307 b |= 1;
308 }
309 if (b2 != 0) {
310 b |= 2;
311 }
312 if (bpp >= 4) {
313 if (b3 != 0) {
314 b |= 4;
315 }
316 if (b4 != 0) {
317 b |= 8;
318 }
319 }
320 if (bpp >= 8) {
321 if (b5 != 0) {
322 b |= 0x10;
323 }
324 if (b6 != 0) {
325 b |= 0x20;
326 }
327 if (b7 != 0) {
328 b |= 0x40;
329 }
330 if (b8 != 0) {
331 b |= 0x80;
332 }
333 }
334 // bitmap_data[((x + xx) * sheet_width) + y + (yy * 8)] = b;
335 bitmap_data[x + xx][y + (yy * 8)] = b;
336 }
337 }
338 pos++;
339 ypos++;
340 xx += 8;
341 if (ypos >= 16) {
342 yy++;
343 xx = 0;
344 ypos = 0;
345 }
346 }
347
348 int n = 0;
349
350 for (int y = 0; y < (data.size() / 64); y++) {
351 for (int x = 0; x < sheet_width; x++) { // 128 assumption
352 if (n < data.size()) {
353 // buffer[n] = bitmap_data[(x * sheet_width) + y];
354 buffer[n] = bitmap_data[x][y];
355 n++;
356 }
357 }
358 }
359 return buffer;
360}
361
362uint16_t TileInfoToWord(TileInfo tile_info) {
363 uint16_t result = 0;
364
365 // Copy the id_ value
366 result |= tile_info.id_ & 0x3FF; // ids are 10 bits
367
368 // Set the vertical_mirror_, horizontal_mirror_, and over_ flags
369 result |= (tile_info.vertical_mirror_ ? 1 : 0) << 15;
370 result |= (tile_info.horizontal_mirror_ ? 1 : 0) << 14;
371 result |= (tile_info.over_ ? 1 : 0) << 13;
372
373 // Set the palette_
374 result |= (tile_info.palette_ & 0x07) << 10; // palettes are 3 bits
375
376 return result;
377}
378
379TileInfo WordToTileInfo(uint16_t word) {
380 // Extract the id_ value
381 uint16_t id = word & 0x3FF; // ids are 10 bits
382
383 // Extract the vertical_mirror_, horizontal_mirror_, and over_ flags
384 bool vertical_mirror = (word >> 15) & 0x01;
385 bool horizontal_mirror = (word >> 14) & 0x01;
386 bool over = (word >> 13) & 0x01;
387
388 // Extract the palette_
389 uint8_t palette = (word >> 10) & 0x07; // palettes are 3 bits
390
391 return TileInfo(id, palette, vertical_mirror, horizontal_mirror, over);
392}
393
394uint16_t TileInfoToShort(TileInfo tile_info) {
395 uint16_t value = 0;
396 // vhopppcc cccccccc
397 if (tile_info.over_) {
398 value |= TilePriorityBit;
399 }
400 if (tile_info.horizontal_mirror_) {
401 value |= TileHFlipBit;
402 }
403 if (tile_info.vertical_mirror_) {
404 value |= TileVFlipBit;
405 }
406 value |= (uint16_t)((tile_info.palette_ << 10) & 0x1C00);
407 value |= (uint16_t)(tile_info.id_ & TileNameMask);
408
409 return value;
410}
411
412TileInfo GetTilesInfo(uint16_t tile) {
413 // vhopppcc cccccccc
414 uint16_t tid = (uint16_t)(tile & TileNameMask);
415 uint8_t p = (uint8_t)((tile >> 10) & 0x07);
416
417 bool o = ((tile & TilePriorityBit) == TilePriorityBit);
418 bool h = ((tile & TileHFlipBit) == TileHFlipBit);
419 bool v = ((tile & TileVFlipBit) == TileVFlipBit);
420
421 return TileInfo(tid, p, v, h, o);
422}
423
424void CopyTile8bpp16(int x, int y, int tile, std::vector<uint8_t>& bitmap,
425 const std::vector<uint8_t>& blockset) {
426 if (tile < 0 || x < 0 || y < 0) {
427 return;
428 }
429 const size_t src_pos =
430 static_cast<size_t>((tile % 0x08) * 0x10) + (tile / 0x08) * 2048;
431 const size_t dest_pos = static_cast<size_t>(x) + y * 0x200;
432 // Copy 16 rows of 16 bytes; skip tiles outside either buffer instead of
433 // reading or writing out of bounds.
434 if (src_pos + 15 * 0x80 + 0x10 > blockset.size() ||
435 dest_pos + 15 * 0x200 + 0x10 > bitmap.size()) {
436 return;
437 }
438 const uint8_t* src = blockset.data() + src_pos;
439 uint8_t* dst = bitmap.data() + dest_pos;
440 for (int yy = 0; yy < 0x10; yy++) {
441 std::memcpy(dst + yy * 0x200, src + yy * 0x80, 0x10);
442 }
443}
444
446 std::span<const uint8_t> src) {
447 std::vector<uint8_t> dest;
448 uint8_t b0;
449 uint8_t b1;
450 uint8_t b2;
451 uint8_t b3;
452 int res;
453 int mul;
454 int y_adder = 0;
455 int src_index;
456 int dest_x;
457 int dest_y;
458 int dest_index;
459 int main_index_limit = static_cast<int>(src.size() / 32);
460 for (int main_index = 0; main_index <= main_index_limit; main_index += 32) {
461 src_index = (main_index << 5);
462 if (static_cast<size_t>(src_index + 31) >= src.size()) {
463 throw std::invalid_argument("src_index + 31 >= src.size()");
464 }
465 dest_x = main_index & 0x0F;
466 dest_y = main_index >> 4;
467 dest_index = ((dest_y << 7) + dest_x) << 3;
468 if (static_cast<size_t>(dest_index + 903) >=
469 dest.size()) { // Fixed: dest.size() check might fail if dest is empty
470 // dest might need to be pre-allocated or resized
471 if (dest.size() < static_cast<size_t>(dest_index + 904)) {
472 dest.resize(dest_index + 904);
473 }
474 }
475 for (int i = 0; i < 16; i += 2) {
476 mul = 1;
477 b0 = src[src_index + i];
478 b1 = src[src_index + i + 1];
479 b2 = src[src_index + i + 16];
480 b3 = src[src_index + i + 17];
481 for (int j = 0; j < 8; j++) {
482 res = ((b0 & mul) | ((b1 & mul) << 1) | ((b2 & mul) << 2) |
483 ((b3 & mul) << 3)) >>
484 j;
485 dest[dest_index + (7 - j) + y_adder] = (uint8_t)res;
486 mul <<= 1;
487 }
488 y_adder += 128;
489 }
490 }
491 return dest;
492}
493
494} // namespace gfx
495} // namespace yaze
SNES 16-bit tile metadata container.
Definition snes_tile.h:52
constexpr uint16_t TileNameMask
Definition snes_tile.cc:23
constexpr uint16_t TileVFlipBit
Definition snes_tile.cc:20
std::vector< uint8_t > LoadSNES4bppGFXToIndexedColorMatrix(std::span< const uint8_t > src)
Definition snes_tile.cc:445
snes_tile8 UnpackBppTile(std::span< const uint8_t > data, const uint32_t offset, const uint32_t bpp)
Definition snes_tile.cc:25
constexpr int kTilesheetWidth
Definition snes_tile.h:16
void CopyTile8bpp16(int x, int y, int tile, std::vector< uint8_t > &bitmap, const std::vector< uint8_t > &blockset)
Definition snes_tile.cc:424
uint16_t TileInfoToWord(TileInfo tile_info)
Definition snes_tile.cc:362
uint16_t TileInfoToShort(TileInfo tile_info)
Definition snes_tile.cc:394
std::vector< uint8_t > PackBppTile(const snes_tile8 &tile, const uint32_t bpp)
Definition snes_tile.cc:74
std::vector< uint8_t > Bpp8SnesToIndexed(std::vector< uint8_t > data, uint64_t bpp)
Definition snes_tile.cc:260
constexpr uint16_t TilePriorityBit
Definition snes_tile.cc:14
std::vector< uint8_t > SnesTo8bppSheet(std::span< const uint8_t > sheet, int bpp, int num_sheets)
Definition snes_tile.cc:133
std::vector< uint8_t > ConvertBpp(std::span< const uint8_t > tiles, uint32_t from_bpp, uint32_t to_bpp)
Definition snes_tile.cc:119
TileInfo GetTilesInfo(uint16_t tile)
Definition snes_tile.cc:412
constexpr uint8_t kGraphicsBitmap[8]
Definition snes_tile.h:20
TileInfo WordToTileInfo(uint16_t word)
Definition snes_tile.cc:379
std::vector< uint8_t > IndexedToSnesSheet(std::span< const uint8_t > sheet, int bpp, int num_sheets)
Definition snes_tile.cc:204
constexpr uint16_t TileHFlipBit
Definition snes_tile.cc:17
8x8 SNES tile data
uint8_t data[64]