/*---------------------------------------------------------------------------- Dumps GIF files Chris Giese http://SisAndHappy.com/ChrisGiese/ This code is public domain (no copyright). You can do whatever you want with it. 6 Aug 2015: - Added code to hex-dump the extension blocks 13 Feb 2015: - Added merge_pal() function to count the total number of unique colors in the GIF file. - dump_gif() now detects if an image extends beyond the width or height given in the screen descriptor (the bounding box). - Minor stylistic changes. 26 May 2011: - dump_gif() now displays if image is interlaced or not, and keeps track of how many images are in the file 21 May 2011: - Initial release ----------------------------------------------------------------------------*/ #include /* memcmp() */ #include /* printf(), fopen(), fread(), fclose() */ #if 0 #include #else typedef unsigned char uint8_t; typedef unsigned short uint16_t; typedef unsigned long uint32_t; #endif /****************************************************************************/ static uint16_t read_le16(const void *buf0) { const uint8_t *buf = buf0; uint16_t rv; rv = buf[1]; rv <<= 8; rv |= buf[0]; return rv; } /****************************************************************************/ static long get_len(FILE *f) { unsigned long rv = 0; int i; while(1) { if((i = fgetc(f)) == EOF) return -1L; rv++; if(i == 0) break; rv += i; fseek(f, i, SEEK_CUR); } return rv; } /****************************************************************************/ /* this value indicates more than 256 colors */ #define MAGIC_PAL_SIZE 257 static int merge_pal(uint32_t *gpal, unsigned *gpal_size_ptr, unsigned lpal_size, FILE *f) { unsigned i, j, gpal_size = *gpal_size_ptr; unsigned long beyond; uint32_t lpal_ent = 0L; beyond = ftell(f) + lpal_size * 3; for(i = 0; i < lpal_size; i++) { /* read (3-byte!) entry from new palette */ if(fread(&lpal_ent, 1, 3, f) != 3) return -1; /* Search existing palette for this color. Use 32-bit integer value for palette entry; for faster comparison than using e.g. memcmp(). */ for(j = 0; j < gpal_size; j++) { if(lpal_ent == gpal[j]) break; } /* this color is already defined in the existing palette, so skip it */ if(j < gpal_size) continue; /* use magic value to indicate >256 colors */ if(gpal_size >= 256) { gpal_size = MAGIC_PAL_SIZE; break; } /* add new color to existing palette */ gpal[gpal_size] = lpal_ent; gpal_size++; } fseek(f, beyond, SEEK_SET); *gpal_size_ptr = gpal_size; return 0; } /****************************************************************************/ #define BPERL 16 /* byte/line for dump */ void dump(const void *data0, unsigned count) { const uint8_t *data = data0; unsigned j, k; while(count != 0) { for(j = 0; j < BPERL; j++) { if(count == 0) break; printf("%02X ", data[j]); count--; } printf("\t"); for(k = 0; k < j; k++) { if(data[k] < ' ') printf("."); else printf("%c", data[k]); } printf("\n"); data += BPERL; } } /****************************************************************************/ static int dump_gif(FILE *f) { unsigned count, scn_wd, scn_ht, pal_size = 0; uint32_t pal[256]; /* <- 1024 bytes */ uint8_t buf[256]; long pos, len; int i, j; fseek(f, 0, SEEK_SET); if(fread(buf, 1, 13, f) != 13) return -1; scn_wd = read_le16(&buf[6]); scn_ht = read_le16(&buf[8]); printf( "Offset Size Description\n" "------- -------- -----------\n" " 0 13 Screen descriptor, %ux%u\n", scn_wd, scn_ht); if(buf[10] & 0x80) { /* i = buf[10] & 0x70; i >>= 4; */ i = buf[10] & 0x07; i++; i = 1 << i; printf(" 13 %8u Global palette, %u entries\n", i * 3, i); // fseek(f, i * 3, SEEK_CUR); if((i = merge_pal(pal, &pal_size, i, f)) != 0) return i; } count = 0; while(1) { pos = ftell(f); if((i = fgetc(f)) == EOF) return -1; if(i == '!') { #if 0 if((j = fgetc(f)) == EOF) return -1; if((len = get_len(f)) == -1L) return -1; #else unsigned long save; if((j = fgetc(f)) == EOF) return -1; save = ftell(f); if((len = get_len(f)) == -1L) return -1; len += 2; printf("%7lu %8lu (!)Extension block, type 0x%02X\n", pos, len, j); fseek(f, save, SEEK_SET); for(len = 0; ; ) { if((i = fgetc(f)) == EOF) return -1; len++; if(i == 0) break; len += i; // fseek(f, i, SEEK_CUR); /* read and dump contents of extension block; instead of skipping it */ if(fread(buf, 1, i, f) != i) return -1; printf("\t\t "); dump(buf, i); } #endif } else if(i == ',') { unsigned x, y, w, h; if(fread(buf, 1, 9, f) != 9) return -1; x = read_le16(&buf[0]); y = read_le16(&buf[2]); w = read_le16(&buf[4]); h = read_le16(&buf[6]); printf("%7lu 9 (,)Image descriptor %u, %ux%u " "at (%u,%u)", pos, count, w, h, x, y); if(buf[8] & 0x40) printf(", interlaced"); printf("\n"); if(x + w > scn_wd) printf("Error: image is wider than " "bounding box!\n"); if(y + h > scn_ht) printf("Error: image is taller than " "bounding box!\n"); count++; pos = ftell(f); if(buf[8] & 0x80) { /* i = buf[8] & 0x70; i >>= 4; */ i = buf[8] & 0x07; i++; i = 1 << i; printf("%7lu %8u Local palette, %u entries\n", pos, i * 3, i); // fseek(f, i * 3, SEEK_CUR); if((i = merge_pal(pal, &pal_size, i, f)) != 0) return i; } pos = ftell(f); if((j = fgetc(f)) == EOF) /* LZW code size */ return -1; if((len = get_len(f)) == -1L) return -1; len++; printf("%7lu %8lu LZW-compressed image data\n", pos, len); } else if(i == ';') { printf("%7lu 1 (;)End of GIF file\n", pos); break; } else { printf("%7lu ? Error; unexpected byte 0x%02X\n", pos, i); break; } } printf("%u image(s) in file\n", count); if(pal_size == MAGIC_PAL_SIZE) printf(">256"); else printf("%u", pal_size); printf(" unique colors in file\n"); return 0; } /****************************************************************************/ int main(int arg_c, char *arg_v[]) { unsigned char buf[13]; FILE *f; int i; for(i = 1; i < arg_c; i++) { if((f = fopen(arg_v[i], "rb")) == NULL) { printf("Error: can't open file '%s'\n", arg_v[i]); continue; } if(fread(buf, 1, 13, f) != 13) NOT: { fclose(f); printf("Error: file '%s' is not a GIF file\n", arg_v[i]); continue; } if(memcmp(buf, "GIF87a", 6) && memcmp(buf, "GIF89a", 6)) goto NOT; printf("GIF file '%s':\n", arg_v[i]); if(dump_gif(f)) printf("Error: unexpected end-of-file\n"); fclose(f); } return 0; }