]> git.bts.cx Git - benzene.git/blob - src/bz/gfx/aseprite.c
Sprites
[benzene.git] / src / bz / gfx / aseprite.c
1 #include <bz/gfx/aseprite_internal.h>
2
3 #include <bz/debug/assert.h>
4 #include <bz/debug/log.h>
5 #include <bz/math/math.h>
6 #include <bz/memory/allocator.h>
7 #include <bz/renderer/palette_internal.h>
8 #include <bz/resources/resource.h>
9 #include <bz/types/identifier_internal.h>
10 #include <stdio.h> // Apparently stb_image.h needs this??
11 #include <stb_image.h>
12 #include <string.h> // memset
13
14 #include <assert.h> // Static assertions
15
16 #define PACKED_STRUCT __attribute__((__packed__))
17
18 typedef uint8_t ASE_BYTE;
19 typedef uint16_t ASE_WORD;
20 typedef int16_t ASE_SHORT;
21 typedef uint32_t ASE_DWORD;
22 typedef uint32_t ASE_LONG;
23 typedef uint32_t ASE_FIXED;
24 typedef float ASE_FLOAT;
25 typedef double ASE_DOUBLE;
26 typedef uint64_t ASE_QWORD;
27 typedef int64_t ASE_LONG64;
28
29 struct PACKED_STRUCT ASE_STRING {
30 ASE_WORD length;
31 //ASE_BYTE characters[];
32 };
33 typedef struct ASE_STRING ASE_STRING;
34 static_assert(sizeof(ASE_STRING) == 2, "ASE_STRING is wrong size");
35
36 struct PACKED_STRUCT ASE_POINT {
37 ASE_LONG x;
38 ASE_LONG y;
39 };
40 typedef struct ASE_POINT ASE_POINT;
41 static_assert(sizeof(ASE_POINT) == 8, "ASE_POINT is wrong size");
42
43 struct PACKED_STRUCT ASE_SIZE {
44 ASE_LONG width;
45 ASE_LONG height;
46 };
47 typedef struct ASE_SIZE ASE_SIZE;
48 static_assert(sizeof(ASE_SIZE) == 8, "ASE_SIZE is wrong size");
49
50 struct PACKED_STRUCT ASE_RECT {
51 ASE_POINT origin;
52 ASE_SIZE size;
53 };
54 typedef struct ASE_RECT ASE_RECT;
55 static_assert(sizeof(ASE_RECT) == 16, "ASE_RECT is wrong size");
56
57 struct PACKED_STRUCT ASE_PIXEL_RBGA {
58 ASE_BYTE r;
59 ASE_BYTE g;
60 ASE_BYTE b;
61 ASE_BYTE a;
62 };
63 typedef struct ASE_PIXEL_RBGA ASE_PIXEL_RBGA;
64 static_assert(sizeof(ASE_PIXEL_RBGA) == 4, "ASE_PIXEL_RBGA is wrong size");
65
66 struct PACKED_STRUCT ASE_PIXEL_GRAYSCALE {
67 ASE_BYTE v;
68 ASE_BYTE a;
69 };
70 typedef struct ASE_PIXEL_GRAYSCALE ASE_PIXEL_GRAYSCALE;
71 static_assert(sizeof(ASE_PIXEL_GRAYSCALE) == 2, "ASE_PIXEL_GRAYSCALE is wrong size");
72
73 struct PACKED_STRUCT ASE_PIXEL_INDEXED {
74 ASE_BYTE idx;
75 };
76 typedef struct ASE_PIXEL_INDEXED ASE_PIXEL_INDEXED;
77 static_assert(sizeof(ASE_PIXEL_INDEXED) == 1, "ASE_PIXEL_INDEXED is wrong size");
78
79 struct PACKED_STRUCT ASE_Header {
80 ASE_DWORD fileSize;
81 ASE_WORD magicNumber; // 0xA5E0
82 ASE_WORD frames;
83 ASE_WORD width;
84 ASE_WORD height;
85 ASE_WORD depth;
86 ASE_DWORD flags;
87 ASE_WORD ___speed;
88 ASE_DWORD zero1;
89 ASE_DWORD zero2;
90 ASE_BYTE transparentColorIdx;
91 ASE_BYTE ___ignore[3];
92 ASE_WORD numColors;
93 ASE_BYTE pixelWidth;
94 ASE_BYTE pixelHeight;
95 ASE_SHORT gridX;
96 ASE_SHORT gridY;
97 ASE_WORD gridWidth;
98 ASE_WORD gridHeight;
99 ASE_BYTE ___reserved[84];
100 };
101 typedef struct ASE_Header ASE_Header;
102 static_assert(sizeof(ASE_Header) == 128, "ASE_Header is wrong size");
103
104 struct PACKED_STRUCT ASE_Frame {
105 ASE_DWORD frameBytes;
106 ASE_WORD magicNumber; // 0xF1FA
107 ASE_WORD ___numChunks;
108 ASE_WORD duration;
109 ASE_BYTE ___reserved[2];
110 ASE_DWORD numChunks;
111 };
112 typedef struct ASE_Frame ASE_Frame;
113 static_assert(sizeof(ASE_Frame) == 16, "ASE_Frame is wrong size");
114
115 struct PACKED_STRUCT ASE_Chunk {
116 ASE_DWORD chunkSize;
117 ASE_WORD type;
118 //ASE_BYTE data[];
119 };
120 typedef struct ASE_Chunk ASE_Chunk;
121 static_assert(sizeof(ASE_Chunk) == 6, "ASE_Chunk is wrong size");
122
123 enum ASE_ChunkTypes {
124 ASE_ChunkType_LayerChunk = 0x2004,
125 ASE_ChunkType_CelChunk = 0x2005,
126 ASE_ChunkType_CelExtraChunk = 0x2006,
127 ASE_ChunkType_ColorProfileChunk = 0x2007,
128 ASE_ChunkType_ExternalFilesChunk = 0x2008,
129 ASE_ChunkType_TagsChunk = 0x2018,
130 ASE_ChunkType_PaletteChunk = 0x2019,
131 ASE_ChunkType_UserDataChunk = 0x2020,
132 ASE_ChunkType_SliceChunk = 0x2022,
133 ASE_ChunkType_TilesetChunk = 0x2023,
134
135 // Ignore these, deprecated or unused...
136 ASE_ChunkType_OldPaletteChunk1 = 0x0004,
137 ASE_ChunkType_OldPaletteChunk2 = 0x0011,
138 ASE_ChunkType_MaskChunk = 0x2016,
139 ASE_ChunkType_PathChunk = 0x2017,
140 };
141
142 #if 0
143 struct PACKED_STRUCT ASE_Chunk_OldPalette {
144 ASE_WORD numPackets;
145 //BZAsepriteOldPalettePacket packets[];
146 };
147 typedef struct ASE_Chunk_OldPalette ASE_Chunk_OldPalette;
148 static_assert(sizeof(ASE_Chunk_OldPalette) == 2, "ASE_Chunk_OldPalette is wrong size");
149
150 struct PACKED_STRUCT ASE_Chunk_OldPalette_Packet {
151 ASE_BYTE skip;
152 ASE_BYTE numColors;
153 //BZAsepriteOldPaletteColor colors[];
154 };
155 typedef struct ASE_Chunk_OldPalette_Packet ASE_Chunk_OldPalette_Packet;
156 static_assert(sizeof(ASE_Chunk_OldPalette_Packet) == 2, "ASE_Chunk_OldPalette_Packet is wrong size");
157
158 struct PACKED_STRUCT ASE_Chunk_OldPalette_Packet_Color {
159 ASE_BYTE r;
160 ASE_BYTE g;
161 ASE_BYTE b;
162 };
163 typedef struct ASE_Chunk_OldPalette_Packet_Color ASE_Chunk_OldPalette_Packet_Color;
164 static_assert(sizeof(ASE_Chunk_OldPalette_Packet_Color) == 3, "ASE_Chunk_OldPalette_Packet_Color is wrong size");
165 #endif
166
167 struct PACKED_STRUCT ASE_Chunk_Layer {
168 ASE_WORD flags;
169 ASE_WORD layerType;
170 ASE_WORD childLevel;
171 ASE_WORD width;
172 ASE_WORD height;
173 ASE_WORD blendMode;
174 ASE_BYTE opacity;
175 ASE_BYTE ___reserved[3];
176 ASE_STRING name;
177 };
178 typedef struct ASE_Chunk_Layer ASE_Chunk_Layer;
179 static_assert(sizeof(ASE_Chunk_Layer) == 18, "ASE_Chunk_Layer is wrong size");
180
181 enum ASE_Chunk_Layer_BlendModes {
182 ASE_Chunk_Layer_BlendMode_Normal = 0, // Should always be this
183 };
184
185 struct PACKED_STRUCT ASE_Chunk_Layer_Tileset {
186 ASE_DWORD tilesetIndex;
187 };
188 typedef struct ASE_Chunk_Layer_Tileset ASE_Chunk_Layer_Tileset;
189 static_assert(sizeof(ASE_Chunk_Layer_Tileset) == 4, "ASE_Chunk_Layer_Tileset is wrong size");
190
191 struct PACKED_STRUCT ASE_Chunk_Cel {
192 ASE_WORD layerIndex;
193 ASE_SHORT positionX;
194 ASE_SHORT positionY;
195 ASE_BYTE opacity;
196 ASE_WORD celType;
197 ASE_SHORT zIndex;
198 ASE_BYTE ___reserved[5];
199 };
200 typedef struct ASE_Chunk_Cel ASE_Chunk_Cel;
201 static_assert(sizeof(ASE_Chunk_Cel) == 16, "ASE_Chunk_Cel is wrong size");
202
203 enum ASE_Chunk_Cel_CelTypes {
204 ASE_Chunk_Cel_CelType_RawImage = 0,
205 ASE_Chunk_Cel_CelType_LinkedCel = 1,
206 ASE_Chunk_Cel_CelType_CompressedImage = 2,
207 ASE_Chunk_Cel_CelType_CompressedTilemap = 3,
208 };
209
210 struct PACKED_STRUCT ASE_Chunk_Cel_RawImage {
211 ASE_WORD pixelWidth;
212 ASE_WORD pixelHeight;
213 //ASE_BYTE pixelData[];
214 };
215 typedef struct ASE_Chunk_Cel_RawImage ASE_Chunk_Cel_RawImage;
216 static_assert(sizeof(ASE_Chunk_Cel_RawImage) == 4, "ASE_Chunk_Cel_RawImage is wrong size");
217
218 struct PACKED_STRUCT ASE_Chunk_Cel_LinkedCel {
219 ASE_WORD framePosition;
220 };
221 typedef struct ASE_Chunk_Cel_LinkedCel ASE_Chunk_Cel_LinkedCel;
222 static_assert(sizeof(ASE_Chunk_Cel_LinkedCel) == 2, "ASE_Chunk_Cel_LinkedCel is wrong size");
223
224 struct PACKED_STRUCT ASE_Chunk_Cel_CompressedImage {
225 ASE_WORD pixelWidth;
226 ASE_WORD pixelHeight;
227 //ASE_BYTE pixelData[];
228 };
229 typedef struct ASE_Chunk_Cel_CompressedImage ASE_Chunk_Cel_CompressedImage;
230 static_assert(sizeof(ASE_Chunk_Cel_CompressedImage) == 4, "ASE_Chunk_Cel_CompressedImage is wrong size");
231
232 struct PACKED_STRUCT ASE_Chunk_Cel_CompressedTilemap {
233 ASE_WORD tileWidth;
234 ASE_WORD tileHeight;
235 ASE_WORD tileBits;
236 ASE_DWORD tileIDBitmap;
237 ASE_DWORD flipXBitmap;
238 ASE_DWORD flipYBitmap;
239 ASE_DWORD rotationBitmap;
240 ASE_BYTE ___reserved[10];
241 //ASE_DWORD tileData[];
242 };
243 typedef struct ASE_Chunk_Cel_CompressedTilemap ASE_Chunk_Cel_CompressedTilemap;
244 static_assert(sizeof(ASE_Chunk_Cel_CompressedTilemap) == 32, "ASE_Chunk_Cel_CompressedTilemap is wrong size");
245
246 struct PACKED_STRUCT ASE_Chunk_Cel_Extra {
247 ASE_DWORD flags;
248 ASE_FIXED positionX;
249 ASE_FIXED positionY;
250 ASE_FIXED width;
251 ASE_FIXED height;
252 ASE_BYTE ___reserved[16];
253 };
254 typedef struct ASE_Chunk_Cel_Extra ASE_Chunk_Cel_Extra;
255 static_assert(sizeof(ASE_Chunk_Cel_Extra) == 36, "ASE_Chunk_Cel_Extra is wrong size");
256
257 struct PACKED_STRUCT ASE_Chunk_ColorProfile {
258 ASE_WORD type;
259 ASE_WORD flags;
260 ASE_FIXED gamma;
261 ASE_BYTE ___reserved[8];
262 };
263 typedef struct ASE_Chunk_ColorProfile ASE_Chunk_ColorProfile;
264 static_assert(sizeof(ASE_Chunk_ColorProfile) == 16, "ASE_Chunk_ColorProfile is wrong size");
265
266 struct PACKED_STRUCT ASE_Chunk_ColorProfile_ICC {
267 ASE_DWORD length;
268 //ASE_BYTE data[];
269 };
270 typedef struct ASE_Chunk_ColorProfile_ICC ASE_Chunk_ColorProfile_ICC;
271 static_assert(sizeof(ASE_Chunk_ColorProfile_ICC) == 4, "ASE_Chunk_ColorProfile_ICC is wrong size");
272
273 struct PACKED_STRUCT ASE_Chunk_ExternalFiles {
274 ASE_DWORD entries;
275 ASE_BYTE ___reserved[8];
276 };
277 typedef struct ASE_Chunk_ExternalFiles ASE_Chunk_ExternalFiles;
278 static_assert(sizeof(ASE_Chunk_ExternalFiles) == 12, "ASE_Chunk_ExternalFiles is wrong size");
279
280 struct PACKED_STRUCT ASE_Chunk_ExternalFiles_Entry {
281 ASE_DWORD entryID;
282 ASE_BYTE type;
283 ASE_BYTE ___reserved[7];
284 ASE_STRING externalFilename;
285 };
286 typedef struct ASE_Chunk_ExternalFiles_Entry ASE_Chunk_ExternalFiles_Entry;
287 static_assert(sizeof(ASE_Chunk_ExternalFiles_Entry) == 14, "ASE_Chunk_ExternalFiles_Entry is wrong size");
288
289 enum ASE_Chunk_ExternalFiles_Entry_Types {
290 ASE_Chunk_ExternalFiles_EntryType_ExternalPalette = 0,
291 ASE_Chunk_ExternalFiles_EntryType_ExternalTileset = 1,
292 ASE_Chunk_ExternalFiles_EntryType_PropertiesExtension = 2,
293 ASE_Chunk_ExternalFiles_EntryType_TileManagement = 3,
294 };
295
296 struct PACKED_STRUCT ASE_Chunk_Mask {
297 ASE_SHORT positionX;
298 ASE_SHORT positionY;
299 ASE_WORD width;
300 ASE_WORD height;
301 ASE_BYTE ___reserved[8];
302 ASE_STRING maskName;
303 };
304 typedef struct ASE_Chunk_Mask ASE_Chunk_Mask;
305 static_assert(sizeof(ASE_Chunk_Mask) == 18, "ASE_Chunk_Mask is wrong size");
306
307 struct PACKED_STRUCT ASE_Chunk_Tags {
308 ASE_WORD numTags;
309 ASE_BYTE ___reserved[8];
310 };
311 typedef struct ASE_Chunk_Tags ASE_Chunk_Tags;
312 static_assert(sizeof(ASE_Chunk_Tags) == 10, "ASE_Chunk_Tags is wrong size");
313
314 struct PACKED_STRUCT ASE_Chunk_Tags_Tag {
315 ASE_WORD fromFrame;
316 ASE_WORD toFrame;
317 ASE_BYTE loopDirection;
318 ASE_WORD repeat;
319 ASE_BYTE ___reserved[6];
320 ASE_BYTE ___tagRBZ[3];
321 ASE_BYTE ___extra;
322 ASE_STRING tagName;
323 };
324 typedef struct ASE_Chunk_Tags_Tag ASE_Chunk_Tags_Tag;
325 static_assert(sizeof(ASE_Chunk_Tags_Tag) == 19, "ASE_Chunk_Tags_Tag is wrong size");
326
327 struct PACKED_STRUCT ASE_Chunk_Palette {
328 ASE_DWORD paletteSize;
329 ASE_DWORD firstColorIdx;
330 ASE_DWORD lastColorIdx;
331 ASE_BYTE ___reserved[8];
332 };
333 typedef struct ASE_Chunk_Palette ASE_Chunk_Palette;
334 static_assert(sizeof(ASE_Chunk_Palette) == 20, "ASE_Chunk_Palette is wrong size");
335
336 struct PACKED_STRUCT ASE_Chunk_Palette_Entry {
337 ASE_WORD flags;
338 ASE_BYTE r;
339 ASE_BYTE g;
340 ASE_BYTE b;
341 ASE_BYTE a;
342 };
343 typedef struct ASE_Chunk_Palette_Entry ASE_Chunk_Palette_Entry;
344 static_assert(sizeof(ASE_Chunk_Palette_Entry) == 6, "ASE_Chunk_Palette_Entry is wrong size");
345
346 struct PACKED_STRUCT ASE_Chunk_Palette_Entry_Name {
347 ASE_STRING name;
348 };
349 typedef struct ASE_Chunk_Palette_Entry_Name ASE_Chunk_Palette_Entry_Name;
350 static_assert(sizeof(ASE_Chunk_Palette_Entry_Name) == 2, "ASE_Chunk_Palette_Entry_Name is wrong size");
351
352 static bool readASEHeader(ASE_Header *headerOut, BZResourceID handle) {
353 bzAssert(headerOut != NULL);
354 bzResourcesReadBytes(handle, headerOut, sizeof(ASE_Header));
355 bzAssert(headerOut->magicNumber == 0xA5E0);
356 return true;
357 }
358
359 static bool readASEFrame(ASE_Frame *frameOut, ASE_DWORD *numChunksOut, BZResourceID handle) {
360 bzAssert(frameOut != NULL);
361 bzAssert(numChunksOut != NULL);
362
363 bzResourcesReadBytes(handle, frameOut, sizeof(ASE_Frame));
364
365 ASE_DWORD numChunks = frameOut->___numChunks;
366
367 if (numChunks == 0xFFFF) {
368 numChunks = frameOut->numChunks;
369 if (numChunks == 0) {
370 numChunks = frameOut->___numChunks; // See docs.
371 }
372 }
373
374 *numChunksOut = numChunks;
375
376 return true;
377 }
378
379 void *bzGfxLoadAsepriteImage(BZMemoryArenaID arena, size_t *framesOut, size_t *widthOut, size_t *heightOut, const char *identifierFmt, ...) {
380 bzMakeIdentifier(identifier, identifierFmt);
381
382 BZResourceID handle = bzResourcesOpenResource("sprite", "assets/sprites/%s.aseprite", identifier);
383 //PHYSFS_sint64 length = PHYSFS_fileLength(handle);
384 //void *data = alloca(length);
385 //PHYSFS_readBytes(handle, data, length);
386
387 ASE_Header header;
388 readASEHeader(&header, handle);
389
390 size_t pixelDataSize;
391 switch (header.depth) {
392 case 8:
393 pixelDataSize = sizeof(ASE_PIXEL_INDEXED);
394 break;
395
396 case 16:
397 pixelDataSize = sizeof(ASE_PIXEL_GRAYSCALE);
398 break;
399
400 case 32:
401 pixelDataSize = sizeof(ASE_PIXEL_RBGA);
402 break;
403
404 default:
405 assert(false);//, "Invalid image");
406 break;
407 }
408
409 uint8_t *outData = (uint8_t *)bzMemoryAlloc(arena, header.frames * header.width * header.height * pixelDataSize); // Side by side loading needs to happen...
410 *framesOut = (size_t)header.frames;
411 *widthOut = (size_t)header.width;
412 *heightOut = (size_t)header.height;
413
414 bzMemoryArenaPushWatermark(arena);
415 size_t zBufferSize = header.width * header.height * sizeof(ASE_SHORT);
416 ASE_SHORT *zBuffer = (ASE_SHORT *)bzMemoryAlloc(arena, zBufferSize);
417
418 size_t frameImageSize = header.width * header.height * pixelDataSize;
419
420 for (ASE_WORD frameIdx = 0; frameIdx < header.frames; ++frameIdx) {
421 ASE_Frame frame;
422 ASE_DWORD numChunks = 0;
423 readASEFrame(&frame, &numChunks, handle);
424 bzLog("FRAME %d: %d chunks", frameIdx, numChunks);
425
426 memset(zBuffer, 0, zBufferSize);
427
428 bool layerVisible[16];
429 size_t nextLayer = 0;
430
431 size_t layerStartOffset[16];
432
433 for (ASE_DWORD chunk = 0; chunk < numChunks; ++chunk) {
434 size_t chunkStartPosition = bzResourcesTell(handle);
435
436 ASE_Chunk chunk;
437 bzResourcesReadBytes(handle, &chunk, sizeof(chunk));
438
439 switch (chunk.type) {
440 case ASE_ChunkType_LayerChunk: {
441 bzLog("LAYER %d: %d chunks", frameIdx, numChunks);
442
443
444 ASE_Chunk_Layer layer;
445 bzResourcesReadBytes(handle, &layer, sizeof(layer));
446
447 layerVisible[nextLayer] = (layer.flags & 1) > 0;
448 ++nextLayer;
449
450 bzResourcesSeek(handle, chunkStartPosition + chunk.chunkSize);
451 break;
452 }
453
454 case ASE_ChunkType_CelChunk: {
455 bzLog("CELL %d: %d chunks", frameIdx, numChunks);
456 ASE_Chunk_Cel cel;
457 bzResourcesReadBytes(handle, &cel, sizeof(cel));
458
459 ///// ASE_Chunk_Cel_CelType_LinkedCel aaaahhh
460
461 if (layerVisible[cel.layerIndex] && cel.celType == ASE_Chunk_Cel_CelType_CompressedImage) {
462 bzMemoryArenaPushWatermark(arena);
463
464 ASE_Chunk_Cel_CompressedImage image;
465 bzResourcesReadBytes(handle, &image, sizeof(image));
466
467 size_t compressedSize = chunk.chunkSize - sizeof(ASE_Chunk_Cel_CompressedImage) - sizeof(ASE_Chunk_Cel) - sizeof(ASE_Chunk);
468 int8_t *compressedData = bzMemoryAlloc(arena, compressedSize); // If we do this on the stack (alloca) the Playdate will explode!
469 bzResourcesReadBytes(handle, compressedData, compressedSize);
470
471 size_t imagePixelDataSize = image.pixelWidth * image.pixelHeight * pixelDataSize;
472 int8_t *imagePixelData = bzMemoryAlloc(arena, imagePixelDataSize);
473 stbi_zlib_decode_buffer(imagePixelData, imagePixelDataSize, compressedData, compressedSize);
474
475 for (size_t y = 0; y < image.pixelHeight; ++y) {
476 for (size_t x = 0; x < image.pixelWidth; ++x) {
477 size_t outX = cel.positionX + x;
478 size_t outY = cel.positionY + y;
479
480 ASE_SHORT *currentZ = &zBuffer[outY * header.width + outX];
481
482 if (*currentZ <= cel.zIndex) {
483 for (size_t i = 0; i < pixelDataSize; ++i) {
484 uint8_t pixelData = imagePixelData[(y * image.pixelWidth + x) * pixelDataSize + i];
485 if (pixelData > 0) { // FIXME, alpha...
486 outData[frameIdx * frameImageSize + (outY * header.width + outX) * pixelDataSize + i] = pixelData;
487 }
488 }
489 *currentZ = cel.zIndex;
490 }
491 }
492 }
493
494 bzMemoryArenaPopWatermark(arena);
495 } else {
496 // Skip this chunk...
497 bzResourcesSeek(handle, chunkStartPosition + chunk.chunkSize);
498 }
499
500 break;
501 }
502
503 default:
504 // Skip this chunk...
505 bzResourcesSeek(handle, chunkStartPosition + chunk.chunkSize);
506 break;
507 }
508 }
509 }
510
511 bzMemoryArenaPopWatermark(arena);
512
513 bzResourcesCloseResource(handle);
514
515 return outData;
516 }
517
518 size_t bzGfxLoadAsepritePalette(uint32_t *colorsOut, size_t maxColors, const char *filename) {
519 BZResourceID handle = bzResourcesOpenResource("palette", "assets/palettes/%s", filename);
520
521 ASE_Header header;
522 readASEHeader(&header, handle);
523
524 size_t colorCount = 0;
525
526 for (ASE_WORD frame = 0; frame < header.frames; ++frame) {
527 ASE_Frame frame;
528 bzResourcesReadBytes(handle, &frame, sizeof(frame));
529
530 ASE_DWORD numChunks = frame.___numChunks;
531
532 if (numChunks == 0xFFFF) {
533 numChunks = frame.numChunks;
534 if (numChunks == 0) {
535 numChunks = frame.___numChunks; // See docs.
536 }
537 }
538
539 for (ASE_DWORD chunk = 0; chunk < numChunks; ++chunk) {
540 size_t chunkStartPosition = bzResourcesTell(handle);
541
542 ASE_Chunk chunk;
543 bzResourcesReadBytes(handle, &chunk, sizeof(chunk));
544
545 switch (chunk.type) {
546 case ASE_ChunkType_PaletteChunk: {
547 ASE_Chunk_Palette palette;
548 bzResourcesReadBytes(handle, &palette, sizeof(palette));
549
550 if (palette.firstColorIdx < maxColors) {
551 size_t lastIdx = bzMin(palette.lastColorIdx + 1, maxColors);
552 for (size_t i = palette.firstColorIdx; i < lastIdx; ++i) {
553 ASE_Chunk_Palette_Entry paletteEntry;
554 bzResourcesReadBytes(handle, &paletteEntry, sizeof(paletteEntry));
555 colorsOut[i] = bzPaletteMakeColor(paletteEntry.r, paletteEntry.g, paletteEntry.b);
556 }
557 colorCount = bzMax(colorCount, lastIdx);
558 }
559
560 // FIXME
561 bzResourcesSeek(handle, chunkStartPosition + chunk.chunkSize);
562
563 break;
564 }
565
566 default:
567 // Skip this chunk...
568 bzResourcesSeek(handle, chunkStartPosition + chunk.chunkSize);
569 break;
570 }
571 }
572 }
573
574 bzResourcesCloseResource(handle);
575
576 return colorCount;
577 }