enlightenment for armor's effect on spell casting
[NetHack.git] / win / share / gifread.c
blob18c0dd5de08cd658ef946ad5b984ae686641f40c
1 /* GIF reading routines based on those in pbmplus:ppm/giftoppm.c, bearing
2 * following copyright notice:
3 */
5 /* +-------------------------------------------------------------------+ */
6 /* | Copyright 1990, David Koblas. | */
7 /* | Permission to use, copy, modify, and distribute this software | */
8 /* | and its documentation for any purpose and without fee is hereby | */
9 /* | granted, provided that the above copyright notice appear in all | */
10 /* | copies and that both that copyright notice and this permission | */
11 /* | notice appear in supporting documentation. This software is | */
12 /* | provided "as is" without express or implied warranty. | */
13 /* +-------------------------------------------------------------------+ */
16 * $NHDT-Date: 1432512803 2015/05/25 00:13:23 $ $NHDT-Branch: master $:$NHDT-Revision: 1.5 $
19 #include "config.h"
20 #include "tile.h"
22 #ifndef MONITOR_HEAP
23 extern long *alloc(unsigned int);
24 #endif
26 #define PPM_ASSIGN(p, red, grn, blu) \
27 do { \
28 (p).r = (red); \
29 (p).g = (grn); \
30 (p).b = (blu); \
31 } while (0)
33 #define MAX_LWZ_BITS 12
35 #define INTERLACE 0x40
36 #define LOCALCOLORMAP 0x80
37 #define BitSet(byte, bit) (((byte) & (bit)) == (bit))
39 #define ReadOK(file, buffer, len) \
40 (fread((genericptr_t) buffer, (int) len, 1, file) != 0)
42 #define LM_to_uint(a, b) (((b) << 8) | (a))
44 struct gifscreen {
45 int Width;
46 int Height;
47 int Colors;
48 int ColorResolution;
49 int Background;
50 int AspectRatio;
51 int Interlace;
52 } GifScreen;
54 struct {
55 int transparent;
56 int delayTime;
57 int inputFlag;
58 int disposal;
59 } Gif89 = { -1, -1, -1, 0 };
61 int ZeroDataBlock = FALSE;
63 static FILE *gif_file;
64 static int tiles_across, tiles_down, curr_tiles_across, curr_tiles_down;
65 static pixel **image;
66 static unsigned char input_code_size;
68 static int GetDataBlock(FILE * fd, unsigned char *buf);
69 static void DoExtension(FILE * fd, int label);
70 static boolean ReadColorMap(FILE * fd, int number);
71 static void read_header(FILE * fd);
72 static int GetCode(FILE * fd, int code_size, int flag);
73 static int LWZReadByte(FILE * fd, int flag); /*, int input_code_size);*/
74 static void ReadInterleavedImage(FILE * fd, int len, int height);
75 static void ReadTileStrip(FILE * fd, int len);
77 /* These should be in gif.h, but there isn't one. */
78 boolean fopen_gif_file(const char *, const char *);
79 boolean read_gif_tile(pixel(*) [TILE_X]);
80 int fclose_gif_file(void);
82 static int
83 GetDataBlock(FILE *fd, unsigned char *buf)
85 unsigned char count;
87 if (!ReadOK(fd, &count, 1)) {
88 Fprintf(stderr, "error in getting DataBlock size\n");
89 return -1;
92 ZeroDataBlock = (count == 0);
94 if ((count != 0) && (!ReadOK(fd, buf, count))) {
95 Fprintf(stderr, "error in reading DataBlock\n");
96 return -1;
99 return count;
102 static void
103 DoExtension(FILE *fd, int label)
105 static char buf[256];
106 const char *str;
108 switch (label) {
109 case 0x01: /* Plain Text Extension */
110 str = "Plain Text Extension";
111 #ifdef notdef
112 if (GetDataBlock(fd, (unsigned char *) buf) == 0)
115 lpos = LM_to_uint(buf[0], buf[1]);
116 tpos = LM_to_uint(buf[2], buf[3]);
117 width = LM_to_uint(buf[4], buf[5]);
118 height = LM_to_uint(buf[6], buf[7]);
119 cellw = buf[8];
120 cellh = buf[9];
121 foreground = buf[10];
122 background = buf[11];
124 while (GetDataBlock(fd, (unsigned char *) buf) != 0) {
125 PPM_ASSIGN(image[ypos][xpos], cmap[CM_RED][v], cmap[CM_GREEN][v],
126 cmap[CM_BLUE][v]);
127 ++index;
130 return;
131 #else
132 break;
133 #endif
134 case 0xff: /* Application Extension */
135 str = "Application Extension";
136 break;
137 case 0xfe: /* Comment Extension */
138 str = "Comment Extension";
139 while (GetDataBlock(fd, (unsigned char *) buf) != 0) {
140 Fprintf(stderr, "gif comment: %s\n", buf);
142 return;
143 case 0xf9: /* Graphic Control Extension */
144 str = "Graphic Control Extension";
145 (void) GetDataBlock(fd, (unsigned char *) buf);
146 Gif89.disposal = (buf[0] >> 2) & 0x7;
147 Gif89.inputFlag = (buf[0] >> 1) & 0x1;
148 Gif89.delayTime = LM_to_uint(buf[1], buf[2]);
149 if ((buf[0] & 0x1) != 0)
150 Gif89.transparent = buf[3];
152 while (GetDataBlock(fd, (unsigned char *) buf) != 0)
154 return;
155 default:
156 str = buf;
157 Sprintf(buf, "UNKNOWN (0x%02x)", label);
158 break;
161 Fprintf(stderr, "got a '%s' extension\n", str);
163 while (GetDataBlock(fd, (unsigned char *) buf) != 0)
167 static boolean
168 ReadColorMap(FILE *fd, int number)
170 int i;
171 unsigned char rgb[3];
173 for (i = 0; i < number; ++i) {
174 if (!ReadOK(fd, rgb, sizeof(rgb))) {
175 return (FALSE);
178 ColorMap[CM_RED][i] = rgb[0];
179 ColorMap[CM_GREEN][i] = rgb[1];
180 ColorMap[CM_BLUE][i] = rgb[2];
182 colorsinmap = number;
183 return TRUE;
187 * Read gif header, including colormaps. We expect only one image per
188 * file, so if that image has a local colormap, overwrite the global one.
190 static void
191 read_header(FILE *fd)
193 unsigned char buf[16];
194 unsigned char c;
195 char version[4];
197 if (!ReadOK(fd, buf, 6)) {
198 Fprintf(stderr, "error reading magic number\n");
199 exit(EXIT_FAILURE);
202 if (strncmp((genericptr_t) buf, "GIF", 3) != 0) {
203 Fprintf(stderr, "not a GIF file\n");
204 exit(EXIT_FAILURE);
207 (void) strncpy(version, (char *) buf + 3, 3);
208 version[3] = '\0';
210 if ((strcmp(version, "87a") != 0) && (strcmp(version, "89a") != 0)) {
211 Fprintf(stderr, "bad version number, not '87a' or '89a'\n");
212 exit(EXIT_FAILURE);
215 if (!ReadOK(fd, buf, 7)) {
216 Fprintf(stderr, "failed to read screen descriptor\n");
217 exit(EXIT_FAILURE);
220 GifScreen.Width = LM_to_uint(buf[0], buf[1]);
221 GifScreen.Height = LM_to_uint(buf[2], buf[3]);
222 GifScreen.Colors = 2 << (buf[4] & 0x07);
223 GifScreen.ColorResolution = (((buf[4] & 0x70) >> 3) + 1);
224 GifScreen.Background = buf[5];
225 GifScreen.AspectRatio = buf[6];
227 if (BitSet(buf[4], LOCALCOLORMAP)) { /* Global Colormap */
228 if (!ReadColorMap(fd, GifScreen.Colors)) {
229 Fprintf(stderr, "error reading global colormap\n");
230 exit(EXIT_FAILURE);
234 if (GifScreen.AspectRatio != 0 && GifScreen.AspectRatio != 49) {
235 Fprintf(stderr, "warning - non-square pixels\n");
238 for (;;) {
239 if (!ReadOK(fd, &c, 1)) {
240 Fprintf(stderr, "EOF / read error on image data\n");
241 exit(EXIT_FAILURE);
244 if (c == ';') { /* GIF terminator */
245 return;
248 if (c == '!') { /* Extension */
249 if (!ReadOK(fd, &c, 1)) {
250 Fprintf(stderr,
251 "EOF / read error on extension function code\n");
252 exit(EXIT_FAILURE);
254 DoExtension(fd, (int) c);
255 continue;
258 if (c != ',') { /* Not a valid start character */
259 Fprintf(stderr, "bogus character 0x%02x, ignoring\n", (int) c);
260 continue;
263 if (!ReadOK(fd, buf, 9)) {
264 Fprintf(stderr, "couldn't read left/top/width/height\n");
265 exit(EXIT_FAILURE);
268 if (BitSet(buf[8], LOCALCOLORMAP)) {
269 /* replace global color map with local */
270 GifScreen.Colors = 1 << ((buf[8] & 0x07) + 1);
271 if (!ReadColorMap(fd, GifScreen.Colors)) {
272 Fprintf(stderr, "error reading local colormap\n");
273 exit(EXIT_FAILURE);
276 if (GifScreen.Width != LM_to_uint(buf[4], buf[5])) {
277 Fprintf(stderr, "warning: widths don't match\n");
278 GifScreen.Width = LM_to_uint(buf[4], buf[5]);
280 if (GifScreen.Height != LM_to_uint(buf[6], buf[7])) {
281 Fprintf(stderr, "warning: heights don't match\n");
282 GifScreen.Height = LM_to_uint(buf[6], buf[7]);
284 GifScreen.Interlace = BitSet(buf[8], INTERLACE);
285 return;
289 static int
290 GetCode(FILE *fd, int code_size, int flag)
292 static unsigned char buf[280];
293 static int curbit, lastbit, done, last_byte;
294 int i, j, ret;
295 unsigned char count;
297 if (flag) {
298 curbit = 0;
299 lastbit = 0;
300 done = FALSE;
301 return 0;
304 if ((curbit + code_size) >= lastbit) {
305 if (done) {
306 if (curbit >= lastbit)
307 Fprintf(stderr, "ran off the end of my bits\n");
308 return -1;
310 buf[0] = buf[last_byte - 2];
311 buf[1] = buf[last_byte - 1];
313 if ((count = GetDataBlock(fd, &buf[2])) == 0)
314 done = TRUE;
316 last_byte = 2 + count;
317 curbit = (curbit - lastbit) + 16;
318 lastbit = (2 + count) * 8;
321 ret = 0;
322 for (i = curbit, j = 0; j < code_size; ++i, ++j)
323 ret |= ((buf[i / 8] & (1 << (i % 8))) != 0) << j;
325 curbit += code_size;
327 return ret;
330 static int
331 LWZReadByte(FILE *fd, int flag) /*, int input_code_size)*/
333 static int fresh = FALSE;
334 int code, incode;
335 static int code_size, set_code_size;
336 static int max_code, max_code_size;
337 static int firstcode, oldcode;
338 static int clear_code, end_code;
339 static int table[2][(1 << MAX_LWZ_BITS)];
340 static int stack[(1 << (MAX_LWZ_BITS)) * 2], *sp;
341 int i;
343 if (flag) {
344 set_code_size = input_code_size;
345 code_size = set_code_size + 1;
346 clear_code = 1 << set_code_size;
347 end_code = clear_code + 1;
348 max_code_size = 2 * clear_code;
349 max_code = clear_code + 2;
351 (void) GetCode(fd, 0, TRUE);
353 fresh = TRUE;
355 for (i = 0; i < clear_code; ++i) {
356 table[0][i] = 0;
357 table[1][i] = i;
359 for (; i < (1 << MAX_LWZ_BITS); ++i)
360 table[0][i] = table[1][0] = 0;
362 sp = stack;
364 return 0;
365 } else if (fresh) {
366 fresh = FALSE;
367 do {
368 firstcode = oldcode = GetCode(fd, code_size, FALSE);
369 } while (firstcode == clear_code);
370 return firstcode;
373 if (sp > stack)
374 return *--sp;
376 while ((code = GetCode(fd, code_size, FALSE)) >= 0) {
377 if (code == clear_code) {
378 for (i = 0; i < clear_code; ++i) {
379 table[0][i] = 0;
380 table[1][i] = i;
382 for (; i < (1 << MAX_LWZ_BITS); ++i)
383 table[0][i] = table[1][i] = 0;
384 code_size = set_code_size + 1;
385 max_code_size = 2 * clear_code;
386 max_code = clear_code + 2;
387 sp = stack;
388 firstcode = oldcode = GetCode(fd, code_size, FALSE);
389 return firstcode;
390 } else if (code == end_code) {
391 int count;
392 unsigned char buf[260];
394 if (ZeroDataBlock)
395 return -2;
397 while ((count = GetDataBlock(fd, buf)) > 0)
400 if (count != 0)
401 Fprintf(stderr,
402 "missing EOD in data stream (common occurrence)\n");
403 return -2;
406 incode = code;
408 if (code >= max_code) {
409 *sp++ = firstcode;
410 code = oldcode;
413 while (code >= clear_code) {
414 *sp++ = table[1][code];
415 if (code == table[0][code]) {
416 Fprintf(stderr, "circular table entry BIG ERROR\n");
417 exit(EXIT_FAILURE);
419 code = table[0][code];
422 *sp++ = firstcode = table[1][code];
424 if ((code = max_code) < (1 << MAX_LWZ_BITS)) {
425 table[0][code] = oldcode;
426 table[1][code] = firstcode;
427 ++max_code;
428 if ((max_code >= max_code_size)
429 && (max_code_size < (1 << MAX_LWZ_BITS))) {
430 max_code_size *= 2;
431 ++code_size;
435 oldcode = incode;
437 if (sp > stack)
438 return *--sp;
440 return code;
443 static void
444 ReadInterleavedImage(FILE *fd, int len, int height)
446 int v;
447 int xpos = 0, ypos = 0, pass = 0;
449 while ((v = LWZReadByte(fd, FALSE /*, (int) input_code_size*/ )) >= 0) {
450 PPM_ASSIGN(image[ypos][xpos], ColorMap[CM_RED][v],
451 ColorMap[CM_GREEN][v], ColorMap[CM_BLUE][v]);
453 ++xpos;
454 if (xpos == len) {
455 xpos = 0;
456 switch (pass) {
457 case 0:
458 case 1:
459 ypos += 8;
460 break;
461 case 2:
462 ypos += 4;
463 break;
464 case 3:
465 ypos += 2;
466 break;
469 if (ypos >= height) {
470 ++pass;
471 switch (pass) {
472 case 1:
473 ypos = 4;
474 break;
475 case 2:
476 ypos = 2;
477 break;
478 case 3:
479 ypos = 1;
480 break;
481 default:
482 goto fini;
486 if (ypos >= height)
487 break;
490 fini:
491 if (LWZReadByte(fd, FALSE /*, (int) input_code_size*/ ) >= 0)
492 Fprintf(stderr, "too much input data, ignoring extra...\n");
495 static void
496 ReadTileStrip(FILE *fd, int len)
498 int v;
499 int xpos = 0, ypos = 0;
501 while ((v = LWZReadByte(fd, FALSE /*, (int) input_code_size*/ )) >= 0) {
502 PPM_ASSIGN(image[ypos][xpos], ColorMap[CM_RED][v],
503 ColorMap[CM_GREEN][v], ColorMap[CM_BLUE][v]);
505 ++xpos;
506 if (xpos == len) {
507 xpos = 0;
508 ++ypos;
510 if (ypos >= TILE_Y)
511 break;
515 boolean
516 fopen_gif_file(const char *filename, const char *type)
518 int i;
520 if (strcmp(type, RDBMODE)) {
521 Fprintf(stderr, "using reading routine for non-reading?\n");
522 return FALSE;
524 gif_file = fopen(filename, type);
525 if (gif_file == (FILE *) 0) {
526 Fprintf(stderr, "cannot open gif file %s\n", filename);
527 return FALSE;
530 read_header(gif_file);
531 if (GifScreen.Width % TILE_X) {
532 Fprintf(stderr, "error: width %d not divisible by %d\n",
533 GifScreen.Width, TILE_X);
534 exit(EXIT_FAILURE);
536 tiles_across = GifScreen.Width / TILE_X;
537 curr_tiles_across = 0;
538 if (GifScreen.Height % TILE_Y) {
539 Fprintf(stderr, "error: height %d not divisible by %d\n",
540 GifScreen.Height, TILE_Y);
541 /* exit(EXIT_FAILURE) */;
543 tiles_down = GifScreen.Height / TILE_Y;
544 curr_tiles_down = 0;
546 if (GifScreen.Interlace) {
547 /* sigh -- hope this doesn't happen on micros */
548 image = (pixel **) alloc(GifScreen.Height * sizeof(pixel *));
549 for (i = 0; i < GifScreen.Height; i++) {
550 image[i] = (pixel *) alloc(GifScreen.Width * sizeof(pixel));
552 } else {
553 image = (pixel **) alloc(TILE_Y * sizeof(pixel *));
554 for (i = 0; i < TILE_Y; i++) {
555 image[i] = (pixel *) alloc(GifScreen.Width * sizeof(pixel));
560 ** Initialize the Compression routines
562 if (!ReadOK(gif_file, &input_code_size, 1)) {
563 Fprintf(stderr, "EOF / read error on image data\n");
564 exit(EXIT_FAILURE);
567 if (LWZReadByte(gif_file, TRUE /*, (int) input_code_size*/ ) < 0) {
568 Fprintf(stderr, "error reading image\n");
569 exit(EXIT_FAILURE);
572 /* read first section */
573 if (GifScreen.Interlace) {
574 ReadInterleavedImage(gif_file, GifScreen.Width, GifScreen.Height);
575 } else {
576 ReadTileStrip(gif_file, GifScreen.Width);
578 return TRUE;
581 /* Read a tile. Returns FALSE when there are no more tiles */
582 boolean
583 read_gif_tile(pixel (*pixels)[TILE_X])
585 int i, j;
587 if (curr_tiles_down >= tiles_down)
588 return FALSE;
589 if (curr_tiles_across == tiles_across) {
590 curr_tiles_across = 0;
591 curr_tiles_down++;
592 if (curr_tiles_down >= tiles_down)
593 return FALSE;
594 if (!GifScreen.Interlace)
595 ReadTileStrip(gif_file, GifScreen.Width);
597 if (GifScreen.Interlace) {
598 for (j = 0; j < TILE_Y; j++) {
599 for (i = 0; i < TILE_X; i++) {
600 pixels[j][i] = image[curr_tiles_down * TILE_Y
601 + j][curr_tiles_across * TILE_X + i];
604 } else {
605 for (j = 0; j < TILE_Y; j++) {
606 for (i = 0; i < TILE_X; i++) {
607 pixels[j][i] = image[j][curr_tiles_across * TILE_X + i];
611 curr_tiles_across++;
613 /* check for "filler" tile */
614 for (j = 0; j < TILE_Y; j++) {
615 for (i = 0; i < TILE_X && i < 4; i += 2) {
616 if (pixels[j][i].r != ColorMap[CM_RED][0]
617 || pixels[j][i].g != ColorMap[CM_GREEN][0]
618 || pixels[j][i].b != ColorMap[CM_BLUE][0]
619 || pixels[j][i + 1].r != ColorMap[CM_RED][1]
620 || pixels[j][i + 1].g != ColorMap[CM_GREEN][1]
621 || pixels[j][i + 1].b != ColorMap[CM_BLUE][1])
622 return TRUE;
625 return FALSE;
629 fclose_gif_file(void)
631 int i;
633 if (GifScreen.Interlace) {
634 for (i = 0; i < GifScreen.Height; i++) {
635 free((genericptr_t) image[i]);
637 free((genericptr_t) image);
638 } else {
639 for (i = 0; i < TILE_Y; i++) {
640 free((genericptr_t) image[i]);
642 free((genericptr_t) image);
644 return (fclose(gif_file));
647 #ifndef AMIGA
648 static const char *const std_args[] = {
649 "tilemap", /* dummy argv[0] */
650 "monsters.gif", "monsters.txt",
651 "objects.gif", "objects.txt",
652 "other.gif", "other.txt",
656 main(int argc, char *argv[])
658 pixel pixels[TILE_Y][TILE_X];
660 if (argc == 1) {
661 argc = SIZE(std_args);
662 argv = (char **) std_args;
663 } else if (argc != 3) {
664 Fprintf(stderr, "usage: gif2txt giffile txtfile\n");
665 exit(EXIT_FAILURE);
668 while (argc > 1) {
669 if (!fopen_gif_file(argv[1], RDBMODE))
670 exit(EXIT_FAILURE);
672 init_colormap();
674 if (!fopen_text_file(argv[2], WRTMODE)) {
675 (void) fclose_gif_file();
676 exit(EXIT_FAILURE);
679 while (read_gif_tile(pixels))
680 (void) write_text_tile(pixels);
682 (void) fclose_gif_file();
683 (void) fclose_text_file();
685 argc -= 2;
686 argv += 2;
688 exit(EXIT_SUCCESS);
689 /*NOTREACHED*/
690 return 0;
692 #endif