Datafile: don't fail on overly long gui names
[rofl0r-agsutils.git] / SpriteFile.c
blobde539ba277b0b47d48a70a226158098ff1a6e3b4
1 #include "SpriteFile.h"
2 #include <stdlib.h>
3 #include <assert.h>
4 #include <stdint.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);
14 exit(1);
17 static int alloc_sprite_index(SpriteFile *si, int nsprites) {
18 si->offsets = calloc(4, nsprites);
19 return 1;
22 static void *readfunc_n(unsigned char *in, unsigned *out, int bpp)
24 *out = 0;
25 switch(bpp) {
26 default:
27 *out |= (*(in++) << 24); /*fall-through*/
28 case 3:
29 *out |= (*(in++) << 16); /*fall-through*/
30 case 2:
31 *out |= (*(in++) << 8); /*fall-through*/
32 case 1:
33 *out |= (*(in++) << 0); /*fall-through*/
35 return in;
38 static void writefunc_n(unsigned char *out, int n, unsigned value, int bpp)
40 out+=n*bpp;
41 unsigned i = 0;
42 switch(bpp) {
43 default:
44 out[i++] = (value & 0xff000000) >> 24; /*fall-through*/
45 case 3:
46 out[i++] = (value & 0xff0000) >> 16; /*fall-through*/
47 case 2:
48 out[i++] = (value & 0xff00) >> 8; /*fall-through*/
49 case 1:
50 out[i++] = (value & 0xff) >> 0; /*fall-through*/
54 static char* unpackl(signed char *out, signed char *in, int size, int bpp)
56 int n = 0;
57 while (n < size) {
58 signed char c = *(in++);
59 unsigned val;
60 if(c == -128) c = 0;
61 if(c < 0) {
62 int i = 1 - c;
63 in = readfunc_n(in, &val, bpp);
64 while(i--) {
65 if (n >= size) return 0;
66 writefunc_n(out, n++, val, bpp);
68 } else {
69 int i = c + 1;
70 while (i--) {
71 if (n >= size) return 0;
72 in = readfunc_n(in, &val, bpp);
73 writefunc_n(out, n++, val, bpp);
77 return in;
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.
85 return 1;
87 unsigned char *out = malloc(outsize);
88 if(!out) return 0;
89 if(!lzwdecomp(d->data, insize, out, outsize)) {
90 free(d->data);
91 d->data = 0;
92 free(out);
93 return 0;
95 free(d->data);
96 d->data = out;
97 d->data_size = outsize;
98 return 1;
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;
104 if(!out) return 0;
105 if(!unpackl(q, p, d->width*d->height, d->bytesperpixel)) {
106 free(d->data);
107 d->data = 0;
108 free(out);
109 return 0;
111 free(d->data);
112 d->data = out;
113 d->data_size = outsize;
114 return 1;
117 static unsigned readfunc_p(unsigned char *in, int n, int bpp)
119 unsigned out = 0;
120 in += n*bpp;
121 switch(bpp) {
122 default:
123 out |= (*(in++) << 24); /*fall-through*/
124 case 3:
125 out |= (*(in++) << 16); /*fall-through*/
126 case 2:
127 out |= (*(in++) << 8); /*fall-through*/
128 case 1:
129 out |= (*(in++) << 0); /*fall-through*/
131 return out;
134 static void* writefunc_p(unsigned char *out, unsigned value, int bpp)
136 switch(bpp) {
137 default:
138 *(out++) = (value & 0xff000000) >> 24; /*fall-through*/
139 case 3:
140 *(out++) = (value & 0xff0000) >> 16; /*fall-through*/
141 case 2:
142 *(out++) = (value & 0xff00) >> 8; /*fall-through*/
143 case 1:
144 *(out++) = (value & 0xff) >> 0; /*fall-through*/
146 return out;
150 static char* packl(unsigned char *out, unsigned char *in, int size, int bpp)
152 int n = 0;
153 unsigned col;
154 while (n < size) {
155 int i = n, j = n + 1, jmax = j + 126;
156 if (jmax >= size) jmax = size - 1;
157 if (i == 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);
166 n += j - i + 1;
167 } else {
168 while ((j < jmax) && (readfunc_p(in, j, bpp) != readfunc_p(in, j + 1, bpp))) ++j;
169 int c = j - i;
170 out = writefunc_p(out, c++, 1);
171 memcpy(out, in+i*bpp, c*bpp);
172 out += c*bpp;
173 n += c;
176 return out;
179 static int ags_pack(ImageData *d) {
180 /* ags has no code for 24bit images :( */
181 assert(d->bytesperpixel != 3 && d->bytesperpixel <= 4);
182 unsigned y;
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;
193 outsize = 0;
194 if(!out) return 0;
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);
198 q = next;
200 free(d->data);
201 d->data = out;
202 d->data_size = outsize;
203 return 1;
206 struct sprv12data {
207 char fmt;
208 char compr;
209 unsigned short palsz;
212 enum fmtv12 {
213 fmt_none = 0,
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) {
220 switch(v12->fmt) {
221 case fmt_none: return 0;
222 case fmt_888: return 3;
223 case fmt_8888: return 4;
224 case fmt_565: return 2;
225 default:
226 fprintf(stderr, "data error: unknown sprite data format %d\n", v12->fmt);
227 exit(1);
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);
240 if(!tmp) {
241 free(pal);
242 free(d->data);
243 d->data = 0;
244 return 0;
246 e = tmp + ds;
247 p = d->data;
248 while(tmp < e) {
249 union {
250 unsigned char b[4];
251 uint16_t s[2];
252 uint32_t i;
253 } col = {0};
254 unsigned char *cp = &col.b[0];
255 unsigned i, idx;
256 idx = *(p++);
257 if(idx >= v12->palsz) idx = 0;
258 idx *= palbpp;
259 for(i = 0; i < palbpp; ++i, ++cp) {
260 *cp = pal[idx++];
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;
266 free(d->data);
267 free(pal);
268 d->data = o;
269 d->data_size = ds;
270 return 1;
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;
277 unsigned bpp_save;
278 data->data = 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;
294 } else {
295 v12_pal_sz = v12_palbytes(&v12);
296 if(v12_pal_sz) {
297 v12pal = malloc(v12_pal_sz);
298 if(!v12pal) return 0;
299 if(AF_read(f, v12pal, v12_pal_sz) != v12_pal_sz)
300 goto oops;
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) {
307 oops:
308 free(v12pal);
309 free(data->data);
310 data->data = 0;
311 return 0;
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 */
324 if(v12pal) {
325 return unpack_v12_palette(data, v12pal, &v12);
327 return 1;
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))
356 return 0;
358 sf->sc_off = f_get_pos(f);
359 f_write_ushort(f, 0); /* number of sprites */
360 sf->num_sprites = 0;
361 return 1;
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);
369 if(sf->compressed) {
370 ags_pack(data);
371 f_write_uint(f, data->data_size);
373 f_write(f, data->data, data->data_size);
375 ++sf->num_sprites;
376 return 1;
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");
382 return 0;
384 f_set_pos(f, sf->sc_off);
385 f_write_ushort(f, sf->num_sprites -1);
386 return 1;
389 int SpriteFile_read(AF* f, SpriteFile *sf) {
390 AF_set_pos(f, 0);
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;
396 sf->id = 0;
397 switch(sf->version) {
398 case 4:
399 sf->compressed = 0;
400 sf->palette = malloc(256*3);
401 AF_read(f, sf->palette, 256*3);
402 break;
403 case 5:
404 sf->compressed = 1;
405 break;
406 case 6: case 10: case 11: case 12:
407 AF_read(f, buf, 1);
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);
413 break;
414 default:
415 fprintf(stderr, "unsupported sprite file version %d\n", (int) sf->version);
416 return 0;
419 if(sf->version >= 5) sf->palette = 0;
421 if(sf->version >= 11)
422 sf->num_sprites = AF_read_uint(f);
423 else
424 sf->num_sprites = AF_read_ushort(f);
425 if(sf->version < 4) sf->num_sprites = 200;
426 sf->num_sprites++;
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. */
432 int i;
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 */
438 if(!coldep) {
439 sf->offsets[i] = 0;
440 continue;
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;
452 } else {
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);
459 return 1;
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");
467 return 0;
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);
477 int i;
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);
482 if(coldep == 0) {
483 fprintf(stderr, "data error: color depth shouldn't be 0\n");
484 return 0;
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]);
493 if(version <= 2) {
494 for(i=0; i<sf->num_sprites;++i)
495 f_write_uint(outf, sf->offsets[i]);
496 } else {
497 for(i=0; i<sf->num_sprites;++i)
498 f_write_uint64(outf, sf->offsets[i]);
500 free(h);
501 free(w);
502 return 1;