1 /*
2 * OpenBOR - http://www.LavaLit.com
3 * -----------------------------------------------------------------------
4 * All rights reserved, see LICENSE in OpenBOR root for details.
5 *
6 * Copyright (c) 2004 - 2011 OpenBOR Team
7 */
8
9 // Animated GIF player.
10
11 #include <stdio.h>
12 #include <string.h>
13 #include "packfile.h"
14 #include "borendian.h"
15 #include "types.h"
16 #include "screen.h"
17 #include "anigif.h"
18
19 // Animated GIF player.
20
21
readbyte(int handle)22 static unsigned char readbyte(int handle){
23 unsigned char c = 0;
24 readpackfile(handle, &c, 1);
25 return c;
26 }
27
passgifblock(anigif_info * info)28 static void passgifblock(anigif_info* info){
29 int len;
30
31 // Skip all contained blocks
32 while((len=readbyte(info->handle))!=0) seekpackfile(info->handle, len, SEEK_CUR);
33 }
34
35
36
handle_gfx_control(anigif_info * info)37 static void handle_gfx_control(anigif_info* info){
38 int len;
39 int skip;
40 unsigned char buf[4];
41
42
43 // Handle all contained blocks
44 while((len=readbyte(info->handle))!=0){
45 skip = len - 4;
46 if(len>4) len = 4;
47 readpackfile(info->handle, buf, len);
48 if(skip>0) seekpackfile(info->handle, skip, SEEK_CUR);
49
50 if(buf[0]&1) info->transparent = buf[3];
51 info->lastdelay = (buf[2]<<8) | buf[1];
52
53 // disposal = (buf[0]>>2) & 7;
54 // inputflag = (buf[0]>>1) & 1;
55 }
56 }
57
58
59
gifextension(anigif_info * info)60 static void gifextension(anigif_info* info){
61 int function;
62
63 // Get extension function code
64 function = readbyte(info->handle);
65
66 // Note: function may be repeated multiple times (size, data, size, data)
67 switch(function){
68 case 0xF9:
69 // Graphic control
70 handle_gfx_control(info);
71 return;
72 default:
73 // 0x01 = text
74 // 0xFF = app. extension
75 // 0xFE = comment
76 passgifblock(info);
77 return;
78 }
79 }
80
81
82
83 /*
84 int readnonzero(int handle){
85 char b = 0;
86 while(b==0){
87 if(readpackfile(handle, &b, 1) < 1) return 0;
88 }
89 return b;
90 }
91 */
92
93
94
95
decodegifblock(anigif_info * info,s_screen * buf,int width,int height,gifblockstruct * gb)96 static int decodegifblock(anigif_info* info, s_screen* buf, int width, int height, gifblockstruct *gb){
97
98 unsigned char bits;
99 short bits2;
100 short codesize;
101 short codesize2;
102 short nextcode;
103 short thiscode;
104 short oldtoken;
105 short currentcode;
106 short oldcode;
107 short bitsleft;
108 short blocksize;
109 int line = 0, lineb;
110 int byte = gb->left;
111 int pass = 0;
112
113 unsigned char *p;
114 unsigned char *u;
115
116 unsigned char *q;
117 unsigned char b[255];
118
119 static unsigned char firstcodestack[4096];
120 static unsigned char lastcodestack[4096];
121 static short codestack[4096];
122
123 static short wordmasktable[] = {0x0000, 0x0001, 0x0003, 0x0007,
124 0x000f, 0x001f, 0x003f, 0x007f,
125 0x00ff, 0x01ff, 0x03ff, 0x07ff,
126 0x0fff, 0x1fff, 0x3fff, 0x7fff };
127
128 static short inctable[] = { 8, 8, 4, 2, 0 };
129 static short startable[] = { 0, 4, 2, 1, 0 };
130
131
132 // get the initial LZW code bits
133 bits = readbyte(info->handle);
134 if(bits<2 || bits>8) return 0;
135
136 p = q = b;
137 bitsleft = 8;
138
139 bits2 = 1 << bits;
140 nextcode = bits2 + 2;
141 codesize2 = 1 << (codesize = bits + 1);
142 oldcode = oldtoken = NO_CODE;
143
144 lineb = gb->top * width;
145
146 // loop until something breaks
147 for(;;){
148 if(bitsleft == 8){
149 if(++p >= q && (((blocksize = readbyte(info->handle)) < 1) ||
150 (q=(p=b) + readpackfile(info->handle, b, blocksize)) < (b+blocksize))){
151 return 1; // Done
152 }
153 bitsleft = 0;
154 }
155 thiscode = *p;
156 if((currentcode=(codesize+bitsleft)) <= 8){
157 *p >>= codesize;
158 bitsleft = currentcode;
159 }
160 else{
161 if(++p >= q && (((blocksize = readbyte(info->handle)) < 1) ||
162 (q=(p=b)+readpackfile(info->handle, b, blocksize)) < (b+blocksize))){
163 return 1; // Done
164 }
165
166 thiscode |= *p << (8 - bitsleft);
167 if(currentcode<=16) *p >>= (bitsleft = currentcode - 8);
168 else{
169 if(++p >= q && (((blocksize = readbyte(info->handle)) < 1) ||
170 (q=(p=b) + readpackfile(info->handle, b, blocksize)) < (b+blocksize))){
171 return 1; // Done
172 }
173
174 thiscode |= *p << (16 - bitsleft);
175 *p >>= (bitsleft = currentcode - 16);
176 }
177 }
178 thiscode &= wordmasktable[codesize];
179 currentcode = thiscode;
180
181 if(thiscode==(bits2+1)) break;
182 if(thiscode>nextcode){
183 return 0; // Bad code
184 }
185
186 if(thiscode==bits2){
187 nextcode = bits2 + 2;
188 codesize2 = 1 << (codesize = (bits+1));
189 oldtoken = oldcode = NO_CODE;
190 continue;
191 }
192
193 u = firstcodestack;
194
195 if(thiscode==nextcode){
196 if(oldcode==NO_CODE){
197 return 0; // Bad code
198 }
199 *u++ = oldtoken;
200 thiscode = oldcode;
201 }
202
203 while(thiscode>=bits2){
204 *u++ = lastcodestack [thiscode];
205 thiscode = codestack[thiscode];
206 }
207
208 oldtoken = thiscode;
209 do{
210 if(byte<width && byte>=0 && gb->top+line>=0 && line<(height - gb->top) && thiscode!=info->transparent)
211 {
212 switch(buf->pixelformat){
213 case PIXEL_8:
214 ((unsigned char*)buf->data)[lineb + byte] = thiscode;
215 break;
216 case PIXEL_16:
217 ((unsigned short*)buf->data)[lineb + byte] = info->local_pal?((unsigned short*)info->local_pal)[thiscode&0xff]:((unsigned short*)info->global_pal)[thiscode&0xff];
218 break;
219 case PIXEL_32:
220 ((unsigned*)buf->data)[lineb + byte] = info->local_pal?((unsigned*)info->local_pal)[thiscode&0xff]:((unsigned*)info->global_pal)[thiscode&0xff];
221 //printf("%08x, %02x, %ld\n", ((unsigned*)buf->data)[lineb + byte], thiscode&0xff, info->handle);
222 break;
223 }
224 }
225 byte++;
226 if(byte >= gb->left + gb->width){
227 byte = gb->left;
228 // check for interlaced image
229 if(gb->flags&0x40){
230 line += inctable[pass];
231 if(line >= gb->height) line = startable[++pass];
232 }
233 else ++line;
234 lineb = width*(gb->top+line);
235 }
236 if (u<=firstcodestack) break;
237 thiscode = *--u;
238 }while(1);
239
240 if(nextcode<4096 && oldcode!=NO_CODE){
241 codestack[nextcode] = oldcode;
242 lastcodestack[nextcode] = oldtoken;
243 if(++nextcode>=codesize2 && codesize<12) codesize2 = 1<<++codesize;
244 }
245 oldcode = currentcode;
246 }
247
248 return 1;
249 }
250
251
252
253
254
255
256
257
anigif_close(anigif_info * info)258 void anigif_close(anigif_info* info){
259 closepackfile(info->handle);
260 info->handle = -1;
261 }
262
263
264
265 // Returns true on success
anigif_open(char * filename,char * packfilename,unsigned char * pal,anigif_info * info)266 int anigif_open(char *filename, char *packfilename, unsigned char *pal, anigif_info* info){
267 unsigned char tpal[1024];
268 int i, j;
269 int noblackenbg = info->noblackenbg;
270
271 memset(info, 0, sizeof(anigif_info));
272
273 info->handle = -1;
274 info->transparent = -1;
275 info->noblackenbg = noblackenbg;
276
277 #if PSP || PS2 || DC
278 if((info->handle=openreadaheadpackfile(filename,packfilename,1*1024*1024, 131072))==-1) return 0;
279 #else
280 if((info->handle=openpackfile(filename,packfilename))==-1) return 0;
281 #endif
282
283 #if PSP || PS2 || DC
284 if(readpackfile(info->handle,&info->gif_header,sizeof_gifheaderstruct)!=sizeof_gifheaderstruct){
285 #else
286 if(readpackfile(info->handle,&info->gif_header,sizeof(gifheaderstruct))!=sizeof(gifheaderstruct)){
287 #endif
288 anigif_close(info);
289 return 0;
290 }
291
292 if(info->gif_header.magic[0]!='G' || info->gif_header.magic[1]!='I' || info->gif_header.magic[2]!='F'){
293 // Not a GIF file!
294 anigif_close(info);
295 return 0;
296 }
297
298 //info->current_res[0] = SwapLSB16(info->gif_header.screenwidth);
299 //info->current_res[1] = SwapLSB16(info->gif_header.screenheight);
300
301 if(info->gif_header.flags&0x80){
302 info->bitdepth = (info->gif_header.flags&7)+1;
303 info->numcolours = 1<<info->bitdepth ;
304 }else{
305 info->bitdepth = 8;
306 info->numcolours = 0;
307 }
308 info->lastdelay = 1;
309 //printf("numcolours:%d\n", info->numcolours);
310
311 // Get global palette, if present and if wanted
312 if(info->gif_header.flags&0x80){
313 info->global_pal = pal;
314 if(pal){
315 if(readpackfile(info->handle, tpal, info->numcolours*3) != info->numcolours*3){
316
317 anigif_close(info);
318 return 0;
319 }
320 //*tpal &= 0xFF000000;
321 if(!noblackenbg) tpal[0] = tpal[1] = tpal[2] = 0;
322 switch(PAL_BYTES)
323 {
324 case 768:
325 memcpy(pal, tpal, 768);
326 break;
327 case 1024:
328 for(i=0, j=0; i<1024; i+=4, j+=3)
329 {
330 *(unsigned*)(pal+i) = colour32(tpal[j], tpal[j+1], tpal[j+2]);
331 }
332 break;
333 case 512:
334 for(i=0, j=0; i<512; i+=2, j+=3)
335 {
336 *(unsigned short*)(pal+i) = colour16(tpal[j], tpal[j+1], tpal[j+2]);
337 }
338 break;
339 }
340 }
341 else seekpackfile(info->handle, info->numcolours*3, SEEK_CUR);
342 }
343
344 return 1;
345 }
346
347
348
349
350 // Returns type of action to take (frame, retry, end)
351 int anigif_decode(s_screen * screen, int *delay, int x, int y, unsigned char* pal, anigif_info* info){
352
353 gifblockstruct iblock;
354 unsigned char tpal[1024];
355 unsigned char c;
356 int i, j, numcolours;
357
358
359 if(info->handle<0) return ANIGIF_DECODE_END;
360 if(screen==NULL){
361 anigif_close(info);
362 return ANIGIF_DECODE_END;
363 }
364
365 if(readpackfile(info->handle,&c,1)!=1){
366 anigif_close(info);
367 return ANIGIF_DECODE_END;
368 }
369
370
371 switch(c){
372 case ',':
373 // An image block
374 #if PSP || PS2 || DC
375 if(readpackfile(info->handle, &iblock, sizeof_iblock)!=sizeof_iblock){
376 #else
377 if(readpackfile(info->handle, &iblock, sizeof(iblock))!=sizeof(iblock)){
378 #endif
379 anigif_close(info);
380 return ANIGIF_DECODE_END;
381 }
382
383 if(iblock.flags&0x80){
384 info->local_pal = pal;
385 numcolours = 1<<((iblock.flags&7)+1);
386 if(pal){
387 if(readpackfile(info->handle, tpal, numcolours*3) != numcolours*3){
388 anigif_close(info);
389 return ANIGIF_DECODE_END;
390 }
391 if(!info->noblackenbg) tpal[0] = tpal[1] = tpal[2] = 0;
392 switch(PAL_BYTES)
393 {
394 case 768:
395 memcpy(pal, tpal, 768);
396 break;
397 case 1024:
398 for(i=0, j=0; i<1024; i+=4, j+=3)
399 {
400 *(unsigned*)(pal+i) = colour32(tpal[j], tpal[j+1], tpal[j+2]);
401 }
402 break;
403 case 512:
404 for(i=0, j=0; i<512; i+=2, j+=3)
405 {
406 *(unsigned short*)(pal+i) = colour16(tpal[j], tpal[j+1], tpal[j+2]);
407 //printf("%d*", *(unsigned short*)(pal+i));
408 }
409 break;
410 }
411 //printf("iblock:%d,%d,%d,%d\n", SwapLSB16(iblock.left), SwapLSB16(iblock.top), SwapLSB16(iblock.width), SwapLSB16(iblock.height));
412 }
413 else seekpackfile(info->handle, numcolours*3, SEEK_CUR);
414 }
415
416 iblock.left = SwapLSB16(iblock.left);
417 iblock.top = SwapLSB16(iblock.top);
418 iblock.width = SwapLSB16(iblock.width);
419 iblock.height = SwapLSB16(iblock.height);
420 iblock.left += x;
421 iblock.top += y;
422
423 decodegifblock(info, screen, screen->width, screen->height, &iblock);
424 /*
425 if(!decodegifblock(handle, screen->data, screen->width, screen->height, &iblock)){
426 anigif_close();
427 return ANIGIF_DECODE_END;
428 }
429 */
430
431 if(delay) *delay = info->lastdelay;
432 // lastdelay = 0;
433 if(iblock.left<0) return ANIGIF_DECODE_RETRY;
434
435 return ANIGIF_DECODE_FRAME|((iblock.flags&0x80)?ANIGIF_DECODE_PAL:0);
436
437 case '!':
438 // Handle GIF extension
439 gifextension(info);
440 // if(delay) *delay = lastdelay;
441 // lastdelay = 0;
442 return ANIGIF_DECODE_RETRY;
443
444 // case 0:
445 default:
446 // Isn't this an EOF?
447 return ANIGIF_DECODE_RETRY;
448 // anigif_close();
449 // return ANIGIF_DECODE_END;
450 }
451
452 anigif_close(info);
453 return ANIGIF_DECODE_END;
454 }
455
456
457
458
459
460
461
462
463