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