1 #include "SpriteFile.h"
7 extern unsigned char defpal
[];
8 extern int lzwdecomp(unsigned char* in
, unsigned long insz
,
9 unsigned char* out
, unsigned long outsz
);
11 #define MAX_OLD_SPRITES 0xfffe
13 static void err_unsupported_compr(int nr
) {
14 fprintf(stderr
, "unsupported compression method %d\n", nr
);
18 static int alloc_sprite_index(SpriteFile
*si
, int nsprites
) {
19 si
->offsets
= calloc(4, nsprites
);
23 static void *readfunc_n(unsigned char *in
, unsigned *out
, int bpp
)
28 *out
|= (*(in
++) << 24); /*fall-through*/
30 *out
|= (*(in
++) << 16); /*fall-through*/
32 *out
|= (*(in
++) << 8); /*fall-through*/
34 *out
|= (*(in
++) << 0); /*fall-through*/
39 static void writefunc_n(unsigned char *out
, int n
, unsigned value
, int bpp
)
45 out
[i
++] = (value
& 0xff000000) >> 24; /*fall-through*/
47 out
[i
++] = (value
& 0xff0000) >> 16; /*fall-through*/
49 out
[i
++] = (value
& 0xff00) >> 8; /*fall-through*/
51 out
[i
++] = (value
& 0xff) >> 0; /*fall-through*/
55 static char* unpackl(signed char *out
, signed char *in
, int size
, int bpp
)
59 signed char c
= *(in
++);
64 in
= readfunc_n(in
, &val
, bpp
);
66 if (n
>= size
) return 0;
67 writefunc_n(out
, n
++, val
, bpp
);
72 if (n
>= size
) return 0;
73 in
= readfunc_n(in
, &val
, bpp
);
74 writefunc_n(out
, n
++, val
, bpp
);
81 static int ags_unpack_defl(ImageData
*d
) {
82 unsigned outsize
= d
->width
*d
->height
*d
->bytesperpixel
,
83 insize
= d
->data_size
;
84 if(outsize
< 16 || insize
< 16) {
85 // data is unpacked already.
90 if(!(out
= tinfl_decompress_mem_to_heap(d
->data
, insize
, &newlen
, TINFL_FLAG_COMPUTE_ADLER32
|TINFL_FLAG_PARSE_ZLIB_HEADER
))) {
97 assert(newlen
== outsize
);
98 d
->data_size
= outsize
;
102 static int ags_unpack_lzw(ImageData
*d
) {
103 unsigned outsize
= d
->width
*d
->height
*d
->bytesperpixel
,
104 insize
= d
->data_size
;
105 if(outsize
< 16 || insize
< 16) {
106 // data is unpacked already.
109 unsigned char *out
= malloc(outsize
);
111 if(!lzwdecomp(d
->data
, insize
, out
, outsize
)) {
119 d
->data_size
= outsize
;
123 static int ags_unpack(ImageData
*d
) {
124 unsigned outsize
= d
->width
*d
->height
*d
->bytesperpixel
;
125 unsigned char *out
= malloc(outsize
), *p
= d
->data
, *q
= out
;
127 if(!unpackl(q
, p
, d
->width
*d
->height
, d
->bytesperpixel
)) {
135 d
->data_size
= outsize
;
139 static unsigned readfunc_p(unsigned char *in
, int n
, int bpp
)
145 out
|= (*(in
++) << 24); /*fall-through*/
147 out
|= (*(in
++) << 16); /*fall-through*/
149 out
|= (*(in
++) << 8); /*fall-through*/
151 out
|= (*(in
++) << 0); /*fall-through*/
156 static void* writefunc_p(unsigned char *out
, unsigned value
, int bpp
)
160 *(out
++) = (value
& 0xff000000) >> 24; /*fall-through*/
162 *(out
++) = (value
& 0xff0000) >> 16; /*fall-through*/
164 *(out
++) = (value
& 0xff00) >> 8; /*fall-through*/
166 *(out
++) = (value
& 0xff) >> 0; /*fall-through*/
172 static char* packl(unsigned char *out
, unsigned char *in
, int size
, int bpp
)
177 int i
= n
, j
= n
+ 1, jmax
= j
+ 126;
178 if (jmax
>= size
) jmax
= size
- 1;
180 col
= readfunc_p(in
, n
++, bpp
);
181 out
= writefunc_p(out
, 0, 1);
182 out
= writefunc_p(out
, col
, bpp
);
183 } else if(readfunc_p(in
, i
, bpp
) == readfunc_p(in
, j
, bpp
)) {
184 while((j
< jmax
) && (readfunc_p(in
, j
, bpp
) == readfunc_p(in
, j
+ 1, bpp
))) ++j
;
185 col
= readfunc_p(in
, i
, bpp
);
186 out
= writefunc_p(out
, i
-j
, 1);
187 out
= writefunc_p(out
, col
, bpp
);
190 while ((j
< jmax
) && (readfunc_p(in
, j
, bpp
) != readfunc_p(in
, j
+ 1, bpp
))) ++j
;
192 out
= writefunc_p(out
, c
++, 1);
193 memcpy(out
, in
+i
*bpp
, c
*bpp
);
201 static int ags_pack(ImageData
*d
) {
202 /* ags has no code for 24bit images :( */
203 assert(d
->bytesperpixel
!= 3 && d
->bytesperpixel
<= 4);
205 /* worst case length: entire file consisting of sequences of 2
206 identical, and one different pixel, resulting in
207 1 byte flag + 1 pixel + 1 byte flag + 1 pixel. in the case of
208 8 bit, that's 1 byte overhead every 3 pixels.
209 since this compression is linebased, additional overhead of
210 1color/line is accounted for.
212 unsigned outsize
= d
->width
*d
->height
*d
->bytesperpixel
;
213 outsize
+= 1 + (outsize
/d
->bytesperpixel
/3) + (d
->height
*d
->bytesperpixel
);
214 unsigned char *out
= malloc(outsize
), *p
= d
->data
, *q
= out
;
217 for(y
= 0; y
< d
->height
; ++y
, p
+=d
->width
*d
->bytesperpixel
) {
218 unsigned char *next
= packl(q
, p
, d
->width
, d
->bytesperpixel
);
219 outsize
+= (next
- q
);
224 d
->data_size
= outsize
;
231 unsigned short palsz
;
236 fmt_888
= 32, /* old-style palette for 8bpp images with 24bpp global pal*/
237 fmt_8888
= 33, /* palette with 32bit data */
238 fmt_565
= 34, /* palette with 16 bit rgb565 encoding */
241 static unsigned v12_pal_bpp(struct sprv12data
*v12
) {
243 case fmt_none
: return 0;
244 case fmt_888
: return 3;
245 case fmt_8888
: return 4;
246 case fmt_565
: return 2;
248 fprintf(stderr
, "data error: unknown sprite data format %d\n", v12
->fmt
);
253 static unsigned v12_palbytes(struct sprv12data
*v12
) {
254 return v12_pal_bpp(v12
) * v12
->palsz
;
257 static int unpack_v12_palette(ImageData
*d
, unsigned char *pal
, struct sprv12data
*v12
) {
258 unsigned palbpp
= v12_pal_bpp(v12
);
259 unsigned char *o
, *e
, *p
, *tmp
;
260 size_t ds
= d
->width
* d
->height
* d
->bytesperpixel
;
261 o
= tmp
= malloc(ds
);
276 unsigned char *cp
= &col
.b
[0];
279 if(idx
>= v12
->palsz
) idx
= 0;
281 for(i
= 0; i
< palbpp
; ++i
, ++cp
) {
284 if(d
->bytesperpixel
== 2) *((uint16_t*)tmp
) = col
.s
[0];
285 else if(d
->bytesperpixel
== 4) *((uint32_t*)tmp
) = col
.i
;
286 tmp
+= d
->bytesperpixel
;
295 int SpriteFile_extract(AF
* f
, SpriteFile
*sf
, int spriteno
, ImageData
*data
) {
296 struct sprv12data v12
= {0};
297 unsigned char *v12pal
= 0;
298 unsigned v12_pal_sz
= 0;
302 if(spriteno
>= sf
->num_sprites
+1) return -1;
303 if(sf
->offsets
[spriteno
] == 0) return -1;
304 AF_set_pos(f
, sf
->offsets
[spriteno
]);
305 bpp_save
= data
->bytesperpixel
= AF_read_uchar(f
);
306 v12
.fmt
= AF_read_uchar(f
);
307 if (sf
->version
>= 12) {
308 v12
.palsz
= AF_read_uchar(f
) + 1;
309 v12
.compr
= AF_read_uchar(f
);
311 data
->width
= AF_read_short(f
);
312 data
->height
= AF_read_short(f
);
313 if(sf
->version
< 12) {
314 if(sf
->compressed
) data
->data_size
= AF_read_uint(f
);
315 else data
->data_size
= data
->bytesperpixel
* data
->width
* data
->height
;
317 v12_pal_sz
= v12_palbytes(&v12
);
319 v12pal
= malloc(v12_pal_sz
);
320 if(!v12pal
) return 0;
321 if(AF_read(f
, v12pal
, v12_pal_sz
) != v12_pal_sz
)
324 data
->data_size
= AF_read_uint(f
);
326 data
->data
= malloc(data
->data_size
);
327 if(!data
->data
) goto oops
;
328 if(AF_read(f
, data
->data
, data
->data_size
) != data
->data_size
) {
335 int do_defl
= sf
->version
>= 12 && v12
.compr
== 3;
336 int do_lzw
= sf
->version
>= 12 && v12
.compr
== 2;
337 if(sf
->version
>= 12 && v12
.compr
> 3)
338 err_unsupported_compr(v12
.compr
);
339 int do_rle
= sf
->version
>= 12 ? v12
.compr
== 1 : sf
->compressed
;
340 if(sf
->version
>= 12 && v12
.fmt
!= fmt_none
) {
341 /* the RLE used by v12 format is only for palettized 8bit data */
342 data
->bytesperpixel
= 1;
344 if(do_rle
&& !ags_unpack(data
)) goto oops
;
345 if(do_lzw
&& !ags_unpack_lzw(data
)) goto oops
;
346 if(do_defl
&& !ags_unpack_defl(data
)) goto oops
;
347 data
->bytesperpixel
= bpp_save
; /* restore real bpp of image */
349 return unpack_v12_palette(data
, v12pal
, &v12
);
354 #include "endianness.h"
355 #define f_set_pos(F, P) fseeko(F, P, SEEK_SET)
356 #define f_get_pos(F) ftello(F)
357 #define f_write(F, P, L) fwrite(P, 1, L, F)
358 #define f_write_int64(F, I) do { uint64_t _x = end_htole64(I); f_write(F, &_x, 8); } while(0)
359 #define f_write_uint64(F, I) f_write_int64(F, I)
360 #define f_write_int(F, I) do { unsigned _x = end_htole32(I); f_write(F, &_x, 4); } while(0)
361 #define f_write_uint(F, I) f_write_int(F, I)
362 #define f_write_short(F, I) do { unsigned short _x = end_htole16(I); f_write(F, &_x, 2); } while(0)
363 #define f_write_ushort(F, I) f_write_short(F, I)
365 int SpriteFile_write_header(FILE *f
, SpriteFile
*sf
) {
366 f_write_short(f
, sf
->version
);
367 if(13 != f_write(f
, " Sprite File ", 13)) return 0;
368 switch(sf
->version
) {
369 /* override user-set compression setting,
370 if required by chosen format */
371 case 5: sf
->compressed
= 1; break;
372 case 4: sf
->compressed
= 0; break;
373 /* in case of version >= 6, set by caller*/
375 if(sf
->version
>= 6) {
376 if(1 != f_write(f
, "\0\1"+(!!sf
->compressed
), 1)) return 0;
377 f_write_int(f
, sf
->id
);
378 } else if (sf
->version
< 5) {
379 if(3*256 != f_write(f
, sf
->palette
? sf
->palette
: defpal
, 3*256))
382 sf
->sc_off
= f_get_pos(f
);
383 f_write_ushort(f
, 0); /* number of sprites */
388 int SpriteFile_add(FILE *f
, SpriteFile
*sf
, ImageData
*data
) {
389 f_write_ushort(f
, data
->bytesperpixel
);
390 if(data
->bytesperpixel
) {
391 f_write_ushort(f
, data
->width
);
392 f_write_ushort(f
, data
->height
);
395 f_write_uint(f
, data
->data_size
);
397 f_write(f
, data
->data
, data
->data_size
);
403 int SpriteFile_finalize(FILE* f
, SpriteFile
*sf
) {
404 if(sf
->num_sprites
>= MAX_OLD_SPRITES
) {
405 fprintf(stderr
, "error: 64bit spritefile support not implemented yet\n");
408 f_set_pos(f
, sf
->sc_off
);
409 f_write_ushort(f
, sf
->num_sprites
-1);
413 int SpriteFile_read(AF
* f
, SpriteFile
*sf
) {
415 sf
->version
= AF_read_short(f
);
416 unsigned char buf
[16];
417 ssize_t n
= AF_read(f
, buf
, 13);
418 if(n
!= 13) return 0;
419 if(memcmp(buf
, " Sprite File ", 13)) return 0;
421 switch(sf
->version
) {
424 sf
->palette
= malloc(256*3);
425 AF_read(f
, sf
->palette
, 256*3);
430 case 6: case 10: case 11: case 12:
432 sf
->compressed
= (buf
[0] == 1);
433 if(buf
[0] > 3 || (buf
[0] > 1 && sf
->version
< 12)) {
434 err_unsupported_compr(buf
[0]);
436 sf
->id
= AF_read_int(f
);
439 fprintf(stderr
, "unsupported sprite file version %d\n", (int) sf
->version
);
443 if(sf
->version
>= 5) sf
->palette
= 0;
445 if(sf
->version
>= 11)
446 sf
->num_sprites
= AF_read_uint(f
);
448 sf
->num_sprites
= AF_read_ushort(f
);
449 if(sf
->version
< 4) sf
->num_sprites
= 200;
451 alloc_sprite_index(sf
, sf
->num_sprites
);
453 /* https://github.com/adventuregamestudio/ags/pull/1461 */
454 if(sf
->version
>= 12) AF_read_int(f
); /* In the file's header 4 more bytes are appended to the end of meta data. First byte contains "sprite storage flags", which is a collection of flags describing which storage methods were allowed when writing this file. This is purely for information, for Editor or other tools that may want to know this. Other 3 bytes are reserved. */
457 struct sprv12data v12
= {0};
458 for(i
= 0; i
< sf
->num_sprites
; ++i
) {
459 sf
->offsets
[i
] = AF_get_pos(f
);
460 int coldep
= AF_read_uchar(f
); /* abuse little endian byte order, as this was short in formats < 12 */
461 v12
.fmt
= AF_read_uchar(f
); /* this is always 0 in pre-12 */
466 if (sf
->version
>= 12) {
467 v12
.palsz
= AF_read_uchar(f
) + 1;
468 v12
.compr
= AF_read_uchar(f
);
470 int w
= AF_read_short(f
);
471 int h
= AF_read_short(f
);
472 unsigned sprite_data_size
;
473 if(sf
->version
< 12) {
474 if(sf
->compressed
) sprite_data_size
= AF_read_uint(f
);
475 else sprite_data_size
= coldep
* w
* h
;
477 unsigned pal_sz
= v12_palbytes(&v12
);
478 if(pal_sz
) AF_read_junk(f
, pal_sz
);
479 sprite_data_size
= AF_read_uint(f
);
481 AF_read_junk(f
, sprite_data_size
);
486 /* create sprindex.dat, use after SpriteFile_read() */
487 int SpriteFile_write_sprindex(AF
* f
, SpriteFile
*sf
, FILE *outf
)
489 if(sf
->num_sprites
>= MAX_OLD_SPRITES
) {
490 fprintf(stderr
, "error: 64bit sprindex files not supported, too many sprites\n");
493 unsigned short *h
= calloc(2, sf
->num_sprites
);
494 unsigned short *w
= calloc(2, sf
->num_sprites
);
495 f_write(outf
, "SPRINDEX", 8);
496 int version
= "\1\2"[!!(sf
->version
> 5)];
497 f_write_int(outf
, version
);
498 if(version
>= 2) f_write_int(outf
, sf
->id
);
499 f_write_uint(outf
, sf
->num_sprites
-1);
500 f_write_uint(outf
, sf
->num_sprites
);
502 for(i
=0; i
<sf
->num_sprites
;++i
) {
503 if(!sf
->offsets
[i
]) continue;
504 AF_set_pos(f
, sf
->offsets
[i
]);
505 int coldep
= AF_read_short(f
);
507 fprintf(stderr
, "data error: color depth shouldn't be 0\n");
510 w
[i
] = AF_read_short(f
);
511 h
[i
] = AF_read_short(f
);
513 for(i
=0; i
<sf
->num_sprites
;++i
)
514 f_write_short(outf
, w
[i
]);
515 for(i
=0; i
<sf
->num_sprites
;++i
)
516 f_write_short(outf
, h
[i
]);
518 for(i
=0; i
<sf
->num_sprites
;++i
)
519 f_write_uint(outf
, sf
->offsets
[i
]);
521 for(i
=0; i
<sf
->num_sprites
;++i
)
522 f_write_uint64(outf
, sf
->offsets
[i
]);