update README regarding ascc
[rofl0r-agsutils.git] / SpriteFile.c
blob4b1e8fd1f818f3006b5d525c197e91a7497846bc
1 #include "SpriteFile.h"
2 #include <stdlib.h>
3 #include <assert.h>
5 static int alloc_sprite_index(SpriteFile *si, int nsprites) {
6 si->offsets = calloc(4, nsprites);
7 return 1;
10 static void *readfunc_n(unsigned char *in, unsigned *out, int bpp)
12 *out = 0;
13 switch(bpp) {
14 default:
15 *out |= (*(in++) << 24); /*fall-through*/
16 case 3:
17 *out |= (*(in++) << 16); /*fall-through*/
18 case 2:
19 *out |= (*(in++) << 8); /*fall-through*/
20 case 1:
21 *out |= (*(in++) << 0); /*fall-through*/
23 return in;
26 static void writefunc_n(unsigned char *out, int n, unsigned value, int bpp)
28 out+=n*bpp;
29 unsigned i = 0;
30 switch(bpp) {
31 default:
32 out[i++] = (value & 0xff000000) >> 24; /*fall-through*/
33 case 3:
34 out[i++] = (value & 0xff0000) >> 16; /*fall-through*/
35 case 2:
36 out[i++] = (value & 0xff00) >> 8; /*fall-through*/
37 case 1:
38 out[i++] = (value & 0xff) >> 0; /*fall-through*/
42 static char* unpackl(signed char *out, signed char *in, int size, int bpp)
44 int n = 0;
45 while (n < size) {
46 signed char c = *(in++);
47 unsigned val;
48 if(c == -128) c = 0;
49 if(c < 0) {
50 int i = 1 - c;
51 in = readfunc_n(in, &val, bpp);
52 while(i--) {
53 if (n >= size) return 0;
54 writefunc_n(out, n++, val, bpp);
56 } else {
57 int i = c + 1;
58 while (i--) {
59 if (n >= size) return 0;
60 in = readfunc_n(in, &val, bpp);
61 writefunc_n(out, n++, val, bpp);
65 return in;
68 static int ags_unpack(ImageData *d) {
69 unsigned y;
70 unsigned outsize = d->width*d->height*d->bytesperpixel;
71 unsigned char *out = malloc(outsize), *p = d->data, *q = out;
72 if(!out) return 0;
73 for(y = 0; y < d->height; ++y, q+=d->width*d->bytesperpixel) {
74 p = unpackl(q, p, d->width, d->bytesperpixel);
76 free(d->data);
77 d->data = out;
78 d->data_size = outsize;
79 return 1;
82 static unsigned readfunc_p(unsigned char *in, int n, int bpp)
84 unsigned out = 0;
85 in += n*bpp;
86 switch(bpp) {
87 default:
88 out |= (*(in++) << 24); /*fall-through*/
89 case 3:
90 out |= (*(in++) << 16); /*fall-through*/
91 case 2:
92 out |= (*(in++) << 8); /*fall-through*/
93 case 1:
94 out |= (*(in++) << 0); /*fall-through*/
96 return out;
99 static void* writefunc_p(unsigned char *out, unsigned value, int bpp)
101 switch(bpp) {
102 default:
103 *(out++) = (value & 0xff000000) >> 24; /*fall-through*/
104 case 3:
105 *(out++) = (value & 0xff0000) >> 16; /*fall-through*/
106 case 2:
107 *(out++) = (value & 0xff00) >> 8; /*fall-through*/
108 case 1:
109 *(out++) = (value & 0xff) >> 0; /*fall-through*/
111 return out;
115 static char* packl(unsigned char *out, unsigned char *in, int size, int bpp)
117 int n = 0;
118 unsigned col;
119 while (n < size) {
120 int i = n, j = n + 1, jmax = j + 126;
121 if (jmax >= size) jmax = size - 1;
122 if (i == size - 1) {
123 col = readfunc_p(in, n++, bpp);
124 out = writefunc_p(out, 0, 1);
125 out = writefunc_p(out, col, bpp);
126 } else if(readfunc_p(in, i, bpp) == readfunc_p(in, j, bpp)) {
127 while((j < jmax) && (readfunc_p(in, j, bpp) == readfunc_p(in, j + 1, bpp))) ++j;
128 col = readfunc_p(in, i, bpp);
129 out = writefunc_p(out, i-j, 1);
130 out = writefunc_p(out, col, bpp);
131 n += j - i + 1;
132 } else {
133 while ((j < jmax) && (readfunc_p(in, j, bpp) != readfunc_p(in, j + 1, bpp))) ++j;
134 int c = j - i;
135 out = writefunc_p(out, c++, 1);
136 memcpy(out, in+i*bpp, c*bpp);
137 out += c*bpp;
138 n += c;
141 return out;
144 static int ags_pack(ImageData *d) {
145 /* ags has no code for 24bit images :( */
146 assert(d->bytesperpixel != 3 && d->bytesperpixel <= 4);
147 unsigned y;
148 /* worst case length: entire file consisting of sequences of 2
149 identical, and one different pixel, resulting in
150 1 byte flag + 1 pixel + 1 byte flag + 1 pixel. in the case of
151 8 bit, that's 1 byte overhead every 3 pixels.
152 since this compression is linebased, additional overhead of
153 1color/line is accounted for.
155 unsigned outsize = d->width*d->height*d->bytesperpixel;
156 outsize += 1 + (outsize/d->bytesperpixel/3) + (d->height*d->bytesperpixel);
157 unsigned char *out = malloc(outsize), *p = d->data, *q = out;
158 outsize = 0;
159 if(!out) return 0;
160 for(y = 0; y < d->height; ++y, p+=d->width*d->bytesperpixel) {
161 unsigned char *next = packl(q, p, d->width, d->bytesperpixel);
162 outsize += (next - q);
163 q = next;
165 free(d->data);
166 d->data = out;
167 d->data_size = outsize;
168 return 1;
171 int SpriteFile_extract(AF* f, SpriteFile *sf, int spriteno, ImageData *data) {
172 if(spriteno >= sf->num_sprites+1) return 0;
173 if(sf->offsets[spriteno] == 0) return 0;
174 AF_set_pos(f, sf->offsets[spriteno]);
175 data->bytesperpixel = AF_read_short(f);
176 data->width = AF_read_short(f);
177 data->height = AF_read_short(f);
178 if(sf->compressed) data->data_size = AF_read_uint(f);
179 else data->data_size = data->bytesperpixel * data->width * data->height;
180 data->data = malloc(data->data_size);
181 if(!data->data) return 0;
182 if(AF_read(f, data->data, data->data_size) != data->data_size) {
183 oops:
184 free(data->data);
185 data->data = 0;
186 return 0;
188 if(sf->compressed && !ags_unpack(data)) goto oops;
189 return 1;
192 #include "endianness.h"
193 #define f_set_pos(F, P) fseeko(F, P, SEEK_SET)
194 #define f_get_pos(F) ftello(F)
195 #define f_write(F, P, L) fwrite(P, 1, L, F)
196 #define f_write_int(F, I) do { unsigned _x = end_htole32(I); f_write(F, &_x, 4); } while(0)
197 #define f_write_uint(F, I) f_write_int(F, I)
198 #define f_write_short(F, I) do { unsigned short _x = end_htole16(I); f_write(F, &_x, 2); } while(0)
199 #define f_write_ushort(F, I) f_write_short(F, I)
201 int SpriteFile_write_header(FILE *f, SpriteFile *sf) {
202 f_write_short(f, sf->version);
203 if(13 != f_write(f, " Sprite File ", 13)) return 0;
204 switch(sf->version) {
205 /* override user-set compression setting,
206 if required by chosen format */
207 case 5: sf->compressed = 1; break;
208 case 4: sf->compressed = 0; break;
209 /* in case of version >= 6, set by caller*/
211 if(sf->version >= 6) {
212 if(1 != f_write(f, "\0\1"+(!!sf->compressed), 1)) return 0;
213 f_write_int(f, sf->id);
214 } else if (sf->version < 5) {
215 if(3*256 != f_write(f, sf->palette, 3*256))
216 return 0;
218 sf->sc_off = f_get_pos(f);
219 f_write_ushort(f, 0); /* number of sprites */
220 sf->num_sprites = 0;
221 return 1;
224 int SpriteFile_add(FILE *f, SpriteFile *sf, ImageData *data) {
225 f_write_ushort(f, data->bytesperpixel);
226 if(data->bytesperpixel) {
227 f_write_ushort(f, data->width);
228 f_write_ushort(f, data->height);
229 if(sf->compressed) {
230 ags_pack(data);
231 f_write_uint(f, data->data_size);
233 f_write(f, data->data, data->data_size);
235 ++sf->num_sprites;
236 return 1;
239 int SpriteFile_finalize(FILE* f, SpriteFile *sf) {
240 f_set_pos(f, sf->sc_off);
241 f_write_ushort(f, sf->num_sprites -1);
242 return 1;
245 int SpriteFile_read(AF* f, SpriteFile *sf) {
246 AF_set_pos(f, 0);
247 sf->version = AF_read_short(f);
248 char buf[16];
249 ssize_t n = AF_read(f, buf, 13);
250 if(n != 13) return 0;
251 if(memcmp(buf, " Sprite File ", 13)) return 0;
252 sf->id = 0;
253 if(sf->version < 4 || sf->version > 6) return 0;
254 if(sf->version == 4) sf->compressed = 0;
255 else if(sf->version == 5) sf->compressed = 1;
256 else if(sf->version >= 6) {
257 AF_read(f, buf, 1);
258 sf->compressed = (buf[0] == 1);
259 sf->id = AF_read_int(f);
262 if(sf->version < 5) {
263 sf->palette = malloc(256*3);
264 AF_read(f, sf->palette, 256*3);
265 } else sf->palette = 0;
267 sf->num_sprites = AF_read_ushort(f);
268 if(sf->version < 4) sf->num_sprites = 200;
269 sf->num_sprites++;
270 alloc_sprite_index(sf, sf->num_sprites);
272 int i;
273 for(i = 0; i < sf->num_sprites; ++i) {
274 sf->offsets[i] = AF_get_pos(f);
275 int coldep = AF_read_short(f);
276 if(!coldep) {
277 sf->offsets[i] = 0;
278 continue;
280 int w = AF_read_short(f);
281 int h = AF_read_short(f);
282 unsigned sprite_data_size;
283 if(sf->compressed) sprite_data_size = AF_read_uint(f);
284 else sprite_data_size = coldep * w * h;
285 AF_read_junk(f, sprite_data_size);
287 return 1;
290 /* create sprindex.dat, use after SpriteFile_read() */
291 int SpriteFile_write_sprindex(AF* f, SpriteFile *sf, FILE *outf)
293 unsigned short *h = calloc(2, sf->num_sprites);
294 unsigned short *w = calloc(2, sf->num_sprites);
295 f_write(outf, "SPRINDEX", 8);
296 int version = 2;
297 /* version, figure out when v1 is needed */
298 f_write_int(outf, version);
299 if(version >= 2) f_write_int(outf, sf->id);
300 f_write_uint(outf, sf->num_sprites-1);
301 f_write_uint(outf, sf->num_sprites);
302 int i;
303 for(i=0; i<sf->num_sprites;++i) {
304 AF_set_pos(f, sf->offsets[i]);
305 int coldep = AF_read_short(f);
306 if(coldep == 0) sf->offsets[i] = 0;
307 else {
308 w[i] = AF_read_short(f);
309 h[i] = AF_read_short(f);
312 for(i=0; i<sf->num_sprites;++i)
313 f_write_short(outf, w[i]);
314 for(i=0; i<sf->num_sprites;++i)
315 f_write_short(outf, h[i]);
316 for(i=0; i<sf->num_sprites;++i)
317 f_write_uint(outf, sf->offsets[i]);
318 free(h);
319 free(w);
320 return 1;