hbmap: fix iterator truncation when size_t < 32bit
[rofl0r-agsutils.git] / SpriteFile.c
blob9a9f56e7ea78b01e169e2f52d70ecfb57116954f
1 #include "SpriteFile.h"
2 #include <stdlib.h>
3 #include <assert.h>
4 #include <stdint.h>
5 #include "miniz.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);
15 exit(1);
18 static int alloc_sprite_index(SpriteFile *si, int nsprites) {
19 si->offsets = calloc(4, nsprites);
20 return 1;
23 static void *readfunc_n(unsigned char *in, unsigned *out, int bpp)
25 *out = 0;
26 switch(bpp) {
27 default:
28 *out |= (*(in++) << 24); /*fall-through*/
29 case 3:
30 *out |= (*(in++) << 16); /*fall-through*/
31 case 2:
32 *out |= (*(in++) << 8); /*fall-through*/
33 case 1:
34 *out |= (*(in++) << 0); /*fall-through*/
36 return in;
39 static void writefunc_n(unsigned char *out, int n, unsigned value, int bpp)
41 out+=n*bpp;
42 unsigned i = 0;
43 switch(bpp) {
44 default:
45 out[i++] = (value & 0xff000000) >> 24; /*fall-through*/
46 case 3:
47 out[i++] = (value & 0xff0000) >> 16; /*fall-through*/
48 case 2:
49 out[i++] = (value & 0xff00) >> 8; /*fall-through*/
50 case 1:
51 out[i++] = (value & 0xff) >> 0; /*fall-through*/
55 static char* unpackl(signed char *out, signed char *in, int size, int bpp)
57 int n = 0;
58 while (n < size) {
59 signed char c = *(in++);
60 unsigned val;
61 if(c == -128) c = 0;
62 if(c < 0) {
63 int i = 1 - c;
64 in = readfunc_n(in, &val, bpp);
65 while(i--) {
66 if (n >= size) return 0;
67 writefunc_n(out, n++, val, bpp);
69 } else {
70 int i = c + 1;
71 while (i--) {
72 if (n >= size) return 0;
73 in = readfunc_n(in, &val, bpp);
74 writefunc_n(out, n++, val, bpp);
78 return in;
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.
86 return 1;
88 size_t newlen;
89 unsigned char *out;
90 if(!(out = tinfl_decompress_mem_to_heap(d->data, insize, &newlen, TINFL_FLAG_COMPUTE_ADLER32|TINFL_FLAG_PARSE_ZLIB_HEADER))) {
91 free(d->data);
92 d->data = 0;
93 return 0;
95 free(d->data);
96 d->data = out;
97 assert(newlen == outsize);
98 d->data_size = outsize;
99 return 1;
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.
107 return 1;
109 unsigned char *out = malloc(outsize);
110 if(!out) return 0;
111 if(!lzwdecomp(d->data, insize, out, outsize)) {
112 free(d->data);
113 d->data = 0;
114 free(out);
115 return 0;
117 free(d->data);
118 d->data = out;
119 d->data_size = outsize;
120 return 1;
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;
126 if(!out) return 0;
127 if(!unpackl(q, p, d->width*d->height, d->bytesperpixel)) {
128 free(d->data);
129 d->data = 0;
130 free(out);
131 return 0;
133 free(d->data);
134 d->data = out;
135 d->data_size = outsize;
136 return 1;
139 static unsigned readfunc_p(unsigned char *in, int n, int bpp)
141 unsigned out = 0;
142 in += n*bpp;
143 switch(bpp) {
144 default:
145 out |= (*(in++) << 24); /*fall-through*/
146 case 3:
147 out |= (*(in++) << 16); /*fall-through*/
148 case 2:
149 out |= (*(in++) << 8); /*fall-through*/
150 case 1:
151 out |= (*(in++) << 0); /*fall-through*/
153 return out;
156 static void* writefunc_p(unsigned char *out, unsigned value, int bpp)
158 switch(bpp) {
159 default:
160 *(out++) = (value & 0xff000000) >> 24; /*fall-through*/
161 case 3:
162 *(out++) = (value & 0xff0000) >> 16; /*fall-through*/
163 case 2:
164 *(out++) = (value & 0xff00) >> 8; /*fall-through*/
165 case 1:
166 *(out++) = (value & 0xff) >> 0; /*fall-through*/
168 return out;
172 static char* packl(unsigned char *out, unsigned char *in, int size, int bpp)
174 int n = 0;
175 unsigned col;
176 while (n < size) {
177 int i = n, j = n + 1, jmax = j + 126;
178 if (jmax >= size) jmax = size - 1;
179 if (i == 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);
188 n += j - i + 1;
189 } else {
190 while ((j < jmax) && (readfunc_p(in, j, bpp) != readfunc_p(in, j + 1, bpp))) ++j;
191 int c = j - i;
192 out = writefunc_p(out, c++, 1);
193 memcpy(out, in+i*bpp, c*bpp);
194 out += c*bpp;
195 n += c;
198 return out;
201 static int ags_pack(ImageData *d) {
202 /* ags has no code for 24bit images :( */
203 assert(d->bytesperpixel != 3 && d->bytesperpixel <= 4);
204 unsigned y;
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;
215 outsize = 0;
216 if(!out) return 0;
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);
220 q = next;
222 free(d->data);
223 d->data = out;
224 d->data_size = outsize;
225 return 1;
228 struct sprv12data {
229 char fmt;
230 char compr;
231 unsigned short palsz;
234 enum fmtv12 {
235 fmt_none = 0,
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) {
242 switch(v12->fmt) {
243 case fmt_none: return 0;
244 case fmt_888: return 3;
245 case fmt_8888: return 4;
246 case fmt_565: return 2;
247 default:
248 fprintf(stderr, "data error: unknown sprite data format %d\n", v12->fmt);
249 exit(1);
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);
262 if(!tmp) {
263 free(pal);
264 free(d->data);
265 d->data = 0;
266 return 0;
268 e = tmp + ds;
269 p = d->data;
270 while(tmp < e) {
271 union {
272 unsigned char b[4];
273 uint16_t s[2];
274 uint32_t i;
275 } col = {0};
276 unsigned char *cp = &col.b[0];
277 unsigned i, idx;
278 idx = *(p++);
279 if(idx >= v12->palsz) idx = 0;
280 idx *= palbpp;
281 for(i = 0; i < palbpp; ++i, ++cp) {
282 *cp = pal[idx++];
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;
288 free(d->data);
289 free(pal);
290 d->data = o;
291 d->data_size = ds;
292 return 1;
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;
299 unsigned bpp_save;
300 data->data = 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;
316 } else {
317 v12_pal_sz = v12_palbytes(&v12);
318 if(v12_pal_sz) {
319 v12pal = malloc(v12_pal_sz);
320 if(!v12pal) return 0;
321 if(AF_read(f, v12pal, v12_pal_sz) != v12_pal_sz)
322 goto oops;
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) {
329 oops:
330 free(v12pal);
331 free(data->data);
332 data->data = 0;
333 return 0;
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 */
348 if(v12pal) {
349 return unpack_v12_palette(data, v12pal, &v12);
351 return 1;
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))
380 return 0;
382 sf->sc_off = f_get_pos(f);
383 f_write_ushort(f, 0); /* number of sprites */
384 sf->num_sprites = 0;
385 return 1;
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);
393 if(sf->compressed) {
394 ags_pack(data);
395 f_write_uint(f, data->data_size);
397 f_write(f, data->data, data->data_size);
399 ++sf->num_sprites;
400 return 1;
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");
406 return 0;
408 f_set_pos(f, sf->sc_off);
409 f_write_ushort(f, sf->num_sprites -1);
410 return 1;
413 int SpriteFile_read(AF* f, SpriteFile *sf) {
414 AF_set_pos(f, 0);
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;
420 sf->id = 0;
421 switch(sf->version) {
422 case 4:
423 sf->compressed = 0;
424 sf->palette = malloc(256*3);
425 AF_read(f, sf->palette, 256*3);
426 break;
427 case 5:
428 sf->compressed = 1;
429 break;
430 case 6: case 10: case 11: case 12:
431 AF_read(f, buf, 1);
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);
437 break;
438 default:
439 fprintf(stderr, "unsupported sprite file version %d\n", (int) sf->version);
440 return 0;
443 if(sf->version >= 5) sf->palette = 0;
445 if(sf->version >= 11)
446 sf->num_sprites = AF_read_uint(f);
447 else
448 sf->num_sprites = AF_read_ushort(f);
449 if(sf->version < 4) sf->num_sprites = 200;
450 sf->num_sprites++;
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. */
456 int i;
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 */
462 if(!coldep) {
463 sf->offsets[i] = 0;
464 continue;
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;
476 } else {
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);
483 return 1;
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");
491 return 0;
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);
501 int i;
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);
506 if(coldep == 0) {
507 fprintf(stderr, "data error: color depth shouldn't be 0\n");
508 return 0;
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]);
517 if(version <= 2) {
518 for(i=0; i<sf->num_sprites;++i)
519 f_write_uint(outf, sf->offsets[i]);
520 } else {
521 for(i=0; i<sf->num_sprites;++i)
522 f_write_uint64(outf, sf->offsets[i]);
524 free(h);
525 free(w);
526 return 1;