1 #include "SpriteFile.h"
6 extern unsigned char defpal
[];
7 extern int lzwdecomp(unsigned char* in
, unsigned long insz
,
8 unsigned char* out
, unsigned long outsz
);
10 #define MAX_OLD_SPRITES 0xfffe
12 static void err_unsupported_compr(int nr
) {
13 fprintf(stderr
, "unsupported compression method %d\n", nr
);
17 static int alloc_sprite_index(SpriteFile
*si
, int nsprites
) {
18 si
->offsets
= calloc(4, nsprites
);
22 static void *readfunc_n(unsigned char *in
, unsigned *out
, int bpp
)
27 *out
|= (*(in
++) << 24); /*fall-through*/
29 *out
|= (*(in
++) << 16); /*fall-through*/
31 *out
|= (*(in
++) << 8); /*fall-through*/
33 *out
|= (*(in
++) << 0); /*fall-through*/
38 static void writefunc_n(unsigned char *out
, int n
, unsigned value
, int bpp
)
44 out
[i
++] = (value
& 0xff000000) >> 24; /*fall-through*/
46 out
[i
++] = (value
& 0xff0000) >> 16; /*fall-through*/
48 out
[i
++] = (value
& 0xff00) >> 8; /*fall-through*/
50 out
[i
++] = (value
& 0xff) >> 0; /*fall-through*/
54 static char* unpackl(signed char *out
, signed char *in
, int size
, int bpp
)
58 signed char c
= *(in
++);
63 in
= readfunc_n(in
, &val
, bpp
);
65 if (n
>= size
) return 0;
66 writefunc_n(out
, n
++, val
, bpp
);
71 if (n
>= size
) return 0;
72 in
= readfunc_n(in
, &val
, bpp
);
73 writefunc_n(out
, n
++, val
, bpp
);
80 static int ags_unpack_lzw(ImageData
*d
) {
81 unsigned outsize
= d
->width
*d
->height
*d
->bytesperpixel
,
82 insize
= d
->data_size
;
83 if(outsize
< 16 || insize
< 16) {
84 // data is unpacked already.
87 unsigned char *out
= malloc(outsize
);
89 if(!lzwdecomp(d
->data
, insize
, out
, outsize
)) {
97 d
->data_size
= outsize
;
101 static int ags_unpack(ImageData
*d
) {
102 unsigned outsize
= d
->width
*d
->height
*d
->bytesperpixel
;
103 unsigned char *out
= malloc(outsize
), *p
= d
->data
, *q
= out
;
105 if(!unpackl(q
, p
, d
->width
*d
->height
, d
->bytesperpixel
)) {
113 d
->data_size
= outsize
;
117 static unsigned readfunc_p(unsigned char *in
, int n
, int bpp
)
123 out
|= (*(in
++) << 24); /*fall-through*/
125 out
|= (*(in
++) << 16); /*fall-through*/
127 out
|= (*(in
++) << 8); /*fall-through*/
129 out
|= (*(in
++) << 0); /*fall-through*/
134 static void* writefunc_p(unsigned char *out
, unsigned value
, int bpp
)
138 *(out
++) = (value
& 0xff000000) >> 24; /*fall-through*/
140 *(out
++) = (value
& 0xff0000) >> 16; /*fall-through*/
142 *(out
++) = (value
& 0xff00) >> 8; /*fall-through*/
144 *(out
++) = (value
& 0xff) >> 0; /*fall-through*/
150 static char* packl(unsigned char *out
, unsigned char *in
, int size
, int bpp
)
155 int i
= n
, j
= n
+ 1, jmax
= j
+ 126;
156 if (jmax
>= size
) jmax
= size
- 1;
158 col
= readfunc_p(in
, n
++, bpp
);
159 out
= writefunc_p(out
, 0, 1);
160 out
= writefunc_p(out
, col
, bpp
);
161 } else if(readfunc_p(in
, i
, bpp
) == readfunc_p(in
, j
, bpp
)) {
162 while((j
< jmax
) && (readfunc_p(in
, j
, bpp
) == readfunc_p(in
, j
+ 1, bpp
))) ++j
;
163 col
= readfunc_p(in
, i
, bpp
);
164 out
= writefunc_p(out
, i
-j
, 1);
165 out
= writefunc_p(out
, col
, bpp
);
168 while ((j
< jmax
) && (readfunc_p(in
, j
, bpp
) != readfunc_p(in
, j
+ 1, bpp
))) ++j
;
170 out
= writefunc_p(out
, c
++, 1);
171 memcpy(out
, in
+i
*bpp
, c
*bpp
);
179 static int ags_pack(ImageData
*d
) {
180 /* ags has no code for 24bit images :( */
181 assert(d
->bytesperpixel
!= 3 && d
->bytesperpixel
<= 4);
183 /* worst case length: entire file consisting of sequences of 2
184 identical, and one different pixel, resulting in
185 1 byte flag + 1 pixel + 1 byte flag + 1 pixel. in the case of
186 8 bit, that's 1 byte overhead every 3 pixels.
187 since this compression is linebased, additional overhead of
188 1color/line is accounted for.
190 unsigned outsize
= d
->width
*d
->height
*d
->bytesperpixel
;
191 outsize
+= 1 + (outsize
/d
->bytesperpixel
/3) + (d
->height
*d
->bytesperpixel
);
192 unsigned char *out
= malloc(outsize
), *p
= d
->data
, *q
= out
;
195 for(y
= 0; y
< d
->height
; ++y
, p
+=d
->width
*d
->bytesperpixel
) {
196 unsigned char *next
= packl(q
, p
, d
->width
, d
->bytesperpixel
);
197 outsize
+= (next
- q
);
202 d
->data_size
= outsize
;
209 unsigned short palsz
;
214 fmt_888
= 32, /* old-style palette for 8bpp images with 24bpp global pal*/
215 fmt_8888
= 33, /* palette with 32bit data */
216 fmt_565
= 34, /* palette with 16 bit rgb565 encoding */
219 static unsigned v12_pal_bpp(struct sprv12data
*v12
) {
221 case fmt_none
: return 0;
222 case fmt_888
: return 3;
223 case fmt_8888
: return 4;
224 case fmt_565
: return 2;
226 fprintf(stderr
, "data error: unknown sprite data format %d\n", v12
->fmt
);
231 static unsigned v12_palbytes(struct sprv12data
*v12
) {
232 return v12_pal_bpp(v12
) * v12
->palsz
;
235 static int unpack_v12_palette(ImageData
*d
, unsigned char *pal
, struct sprv12data
*v12
) {
236 unsigned palbpp
= v12_pal_bpp(v12
);
237 unsigned char *o
, *e
, *p
, *tmp
;
238 size_t ds
= d
->width
* d
->height
* d
->bytesperpixel
;
239 o
= tmp
= malloc(ds
);
254 unsigned char *cp
= &col
.b
[0];
257 if(idx
>= v12
->palsz
) idx
= 0;
259 for(i
= 0; i
< palbpp
; ++i
, ++cp
) {
262 if(d
->bytesperpixel
== 2) *((uint16_t*)tmp
) = col
.s
[0];
263 else if(d
->bytesperpixel
== 4) *((uint32_t*)tmp
) = col
.i
;
264 tmp
+= d
->bytesperpixel
;
273 int SpriteFile_extract(AF
* f
, SpriteFile
*sf
, int spriteno
, ImageData
*data
) {
274 struct sprv12data v12
= {0};
275 unsigned char *v12pal
= 0;
276 unsigned v12_pal_sz
= 0;
280 if(spriteno
>= sf
->num_sprites
+1) return -1;
281 if(sf
->offsets
[spriteno
] == 0) return -1;
282 AF_set_pos(f
, sf
->offsets
[spriteno
]);
283 bpp_save
= data
->bytesperpixel
= AF_read_uchar(f
);
284 v12
.fmt
= AF_read_uchar(f
);
285 if (sf
->version
>= 12) {
286 v12
.palsz
= AF_read_uchar(f
) + 1;
287 v12
.compr
= AF_read_uchar(f
);
289 data
->width
= AF_read_short(f
);
290 data
->height
= AF_read_short(f
);
291 if(sf
->version
< 12) {
292 if(sf
->compressed
) data
->data_size
= AF_read_uint(f
);
293 else data
->data_size
= data
->bytesperpixel
* data
->width
* data
->height
;
295 v12_pal_sz
= v12_palbytes(&v12
);
297 v12pal
= malloc(v12_pal_sz
);
298 if(!v12pal
) return 0;
299 if(AF_read(f
, v12pal
, v12_pal_sz
) != v12_pal_sz
)
302 data
->data_size
= AF_read_uint(f
);
304 data
->data
= malloc(data
->data_size
);
305 if(!data
->data
) goto oops
;
306 if(AF_read(f
, data
->data
, data
->data_size
) != data
->data_size
) {
313 int do_lzw
= sf
->version
>= 12 && v12
.compr
== 2;
314 if(sf
->version
>= 12 && v12
.compr
> 2)
315 err_unsupported_compr(v12
.compr
);
316 int do_rle
= sf
->version
>= 12 ? v12
.compr
== 1 : sf
->compressed
;
317 if(sf
->version
>= 12 && v12
.fmt
!= fmt_none
) {
318 /* the RLE used by v12 format is only for palettized 8bit data */
319 data
->bytesperpixel
= 1;
321 if(do_rle
&& !ags_unpack(data
)) goto oops
;
322 if(do_lzw
&& !ags_unpack_lzw(data
)) goto oops
;
323 data
->bytesperpixel
= bpp_save
; /* restore real bpp of image */
325 return unpack_v12_palette(data
, v12pal
, &v12
);
330 #include "endianness.h"
331 #define f_set_pos(F, P) fseeko(F, P, SEEK_SET)
332 #define f_get_pos(F) ftello(F)
333 #define f_write(F, P, L) fwrite(P, 1, L, F)
334 #define f_write_int64(F, I) do { uint64_t _x = end_htole64(I); f_write(F, &_x, 8); } while(0)
335 #define f_write_uint64(F, I) f_write_int64(F, I)
336 #define f_write_int(F, I) do { unsigned _x = end_htole32(I); f_write(F, &_x, 4); } while(0)
337 #define f_write_uint(F, I) f_write_int(F, I)
338 #define f_write_short(F, I) do { unsigned short _x = end_htole16(I); f_write(F, &_x, 2); } while(0)
339 #define f_write_ushort(F, I) f_write_short(F, I)
341 int SpriteFile_write_header(FILE *f
, SpriteFile
*sf
) {
342 f_write_short(f
, sf
->version
);
343 if(13 != f_write(f
, " Sprite File ", 13)) return 0;
344 switch(sf
->version
) {
345 /* override user-set compression setting,
346 if required by chosen format */
347 case 5: sf
->compressed
= 1; break;
348 case 4: sf
->compressed
= 0; break;
349 /* in case of version >= 6, set by caller*/
351 if(sf
->version
>= 6) {
352 if(1 != f_write(f
, "\0\1"+(!!sf
->compressed
), 1)) return 0;
353 f_write_int(f
, sf
->id
);
354 } else if (sf
->version
< 5) {
355 if(3*256 != f_write(f
, sf
->palette
? sf
->palette
: defpal
, 3*256))
358 sf
->sc_off
= f_get_pos(f
);
359 f_write_ushort(f
, 0); /* number of sprites */
364 int SpriteFile_add(FILE *f
, SpriteFile
*sf
, ImageData
*data
) {
365 f_write_ushort(f
, data
->bytesperpixel
);
366 if(data
->bytesperpixel
) {
367 f_write_ushort(f
, data
->width
);
368 f_write_ushort(f
, data
->height
);
371 f_write_uint(f
, data
->data_size
);
373 f_write(f
, data
->data
, data
->data_size
);
379 int SpriteFile_finalize(FILE* f
, SpriteFile
*sf
) {
380 if(sf
->num_sprites
>= MAX_OLD_SPRITES
) {
381 fprintf(stderr
, "error: 64bit spritefile support not implemented yet\n");
384 f_set_pos(f
, sf
->sc_off
);
385 f_write_ushort(f
, sf
->num_sprites
-1);
389 int SpriteFile_read(AF
* f
, SpriteFile
*sf
) {
391 sf
->version
= AF_read_short(f
);
392 unsigned char buf
[16];
393 ssize_t n
= AF_read(f
, buf
, 13);
394 if(n
!= 13) return 0;
395 if(memcmp(buf
, " Sprite File ", 13)) return 0;
397 switch(sf
->version
) {
400 sf
->palette
= malloc(256*3);
401 AF_read(f
, sf
->palette
, 256*3);
406 case 6: case 10: case 11: case 12:
408 sf
->compressed
= (buf
[0] == 1);
409 if(buf
[0] > 2 || (buf
[0] > 1 && sf
->version
< 12)) {
410 err_unsupported_compr(buf
[0]);
412 sf
->id
= AF_read_int(f
);
415 fprintf(stderr
, "unsupported sprite file version %d\n", (int) sf
->version
);
419 if(sf
->version
>= 5) sf
->palette
= 0;
421 if(sf
->version
>= 11)
422 sf
->num_sprites
= AF_read_uint(f
);
424 sf
->num_sprites
= AF_read_ushort(f
);
425 if(sf
->version
< 4) sf
->num_sprites
= 200;
427 alloc_sprite_index(sf
, sf
->num_sprites
);
429 /* https://github.com/adventuregamestudio/ags/pull/1461 */
430 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. */
433 struct sprv12data v12
= {0};
434 for(i
= 0; i
< sf
->num_sprites
; ++i
) {
435 sf
->offsets
[i
] = AF_get_pos(f
);
436 int coldep
= AF_read_uchar(f
); /* abuse little endian byte order, as this was short in formats < 12 */
437 v12
.fmt
= AF_read_uchar(f
); /* this is always 0 in pre-12 */
442 if (sf
->version
>= 12) {
443 v12
.palsz
= AF_read_uchar(f
) + 1;
444 v12
.compr
= AF_read_uchar(f
);
446 int w
= AF_read_short(f
);
447 int h
= AF_read_short(f
);
448 unsigned sprite_data_size
;
449 if(sf
->version
< 12) {
450 if(sf
->compressed
) sprite_data_size
= AF_read_uint(f
);
451 else sprite_data_size
= coldep
* w
* h
;
453 unsigned pal_sz
= v12_palbytes(&v12
);
454 if(pal_sz
) AF_read_junk(f
, pal_sz
);
455 sprite_data_size
= AF_read_uint(f
);
457 AF_read_junk(f
, sprite_data_size
);
462 /* create sprindex.dat, use after SpriteFile_read() */
463 int SpriteFile_write_sprindex(AF
* f
, SpriteFile
*sf
, FILE *outf
)
465 if(sf
->num_sprites
>= MAX_OLD_SPRITES
) {
466 fprintf(stderr
, "error: 64bit sprindex files not supported, too many sprites\n");
469 unsigned short *h
= calloc(2, sf
->num_sprites
);
470 unsigned short *w
= calloc(2, sf
->num_sprites
);
471 f_write(outf
, "SPRINDEX", 8);
472 int version
= "\1\2"[!!(sf
->version
> 5)];
473 f_write_int(outf
, version
);
474 if(version
>= 2) f_write_int(outf
, sf
->id
);
475 f_write_uint(outf
, sf
->num_sprites
-1);
476 f_write_uint(outf
, sf
->num_sprites
);
478 for(i
=0; i
<sf
->num_sprites
;++i
) {
479 if(!sf
->offsets
[i
]) continue;
480 AF_set_pos(f
, sf
->offsets
[i
]);
481 int coldep
= AF_read_short(f
);
483 fprintf(stderr
, "data error: color depth shouldn't be 0\n");
486 w
[i
] = AF_read_short(f
);
487 h
[i
] = AF_read_short(f
);
489 for(i
=0; i
<sf
->num_sprites
;++i
)
490 f_write_short(outf
, w
[i
]);
491 for(i
=0; i
<sf
->num_sprites
;++i
)
492 f_write_short(outf
, h
[i
]);
494 for(i
=0; i
<sf
->num_sprites
;++i
)
495 f_write_uint(outf
, sf
->offsets
[i
]);
497 for(i
=0; i
<sf
->num_sprites
;++i
)
498 f_write_uint64(outf
, sf
->offsets
[i
]);