/*---------------------------------------------------------------------------- .PNG file dumper Chris Giese http://SisAndHappy.com/ChrisGiese This code is public domain (no copyright). You can do whatever you want with it. Feb 24, 2012: read_be32() redone Mar 20, 2010: fixed a dumb mistake: was overwriting buf such that dump_ihdr() was never called. Jan 26, 2009: now checking and displaying CRC of chunks in .PNG file. July 19, 2007: initial release. ----------------------------------------------------------------------------*/ #include #include /****************************************************************************/ static unsigned long read_be32(const void *buf0) { const unsigned char *buf = buf0; unsigned long rv; rv = buf[0]; /* MSB */ rv <<= 8; rv |= buf[1]; rv <<= 8; rv |= buf[2]; rv <<= 8; rv |= buf[3]; /* LSB */ return rv; } /****************************************************************************/ static int dump_ihdr(const unsigned char *buf) { static const char *ctype[] = { "grayscale", "UNKNOWN", "RGB", "palette color" }; /**/ unsigned depth, color_type, interlace; unsigned long wd, ht; int rv = 0; wd = read_be32(&buf[0]); ht = read_be32(&buf[4]); depth = buf[8]; color_type = buf[9]; /* sanity checking: color type & depth */ switch(color_type) { case 0: if(depth != 1 && depth != 2 && depth != 4 && depth != 8 && depth != 16) BAD: { printf("depth %u is invalid for color type %u\n", depth, color_type); rv = -1; } break; case 3: if(depth != 1 && depth != 2 && depth != 4 && depth != 8) goto BAD; break; case 2: case 4: case 6: if(depth != 8 && depth != 16) goto BAD; break; default: printf("invalid color type %u\n", color_type); rv = -1; } /* compression scheme (0=zlib) */ if(buf[10] > 0) { printf("compression method %u not supported)\n", buf[10]); rv = -1; } /* filter scheme (0=default) */ if(buf[11] > 0) { printf("filter scheme %u not supported\n", buf[11]); rv = -1; } /* interlace (0=none, 1=Adam-7) */ interlace = buf[12]; if(interlace > 1) { printf("interlace scheme %u not supported\n", interlace); rv = -1; } printf("Image:%lux%lux%u", wd, ht, depth); printf(", color type:%s", ctype[color_type & 3]); printf(", alpha transparency:%s", (color_type & 4) ? "yes" : "no"); printf(", interlace:%s\n", interlace ? "yes (Adam7)" : "no"); return rv; } /***************************************************************************** CRC code lifted from the appendix in the .PNG spec *****************************************************************************/ static unsigned long update_crc(unsigned long c, unsigned char *buf, unsigned len) { static unsigned long crc_table[256]; static char init; /**/ unsigned i; if(!init) { unsigned long m; unsigned j, k; for(j = 0; j < 256; j++) { m = j; for(k = 0; k < 8; k++) { if(m & 1) m = 0xEDB88320L ^ (m >> 1); else m = m >> 1; } crc_table[j] = m; } init = 1; } for(i = 0; i < len; i++) c = crc_table[(unsigned)((c ^ buf[i]) & 0xFF)] ^ (c >> 8); return c; } /****************************************************************************/ int main(int arg_c, char *arg_v[]) { unsigned long len, pos, calc_crc, file_crc; unsigned char hdr[12]; FILE *f; /* must have exactly one arg on command line */ if(arg_c != 2) { printf(".PNG file dumper\n"); return 1; } /* open input file */ if((f = fopen(arg_v[1], "rb")) == NULL) { printf("Error: can't open input file '%s'\n", arg_v[1]); return 2; } /* read file header and validate */ if(fread(hdr, 1, 8, f) != 8) NOT: { printf("Error: file '%s' is not a .PNG file\n", arg_v[1]); fclose(f); return 3; } if(memcmp(&hdr[0], "\x89PNG\x0D\x0A\x1A\x0A", 8)) goto NOT; printf( "File Chunk Chunk Calc File\n" "offset ID length CRC CRC\n" "------ ----- ------ -------- --------\n"); do { #define BUF_SIZE 256 unsigned char buf[BUF_SIZE]; pos = ftell(f); /* load chunk length and ID to hdr[0-7] */ if(fread(hdr, 1, 8, f) != 8) ERR: { printf("Error reading PNG file '%s'\n", arg_v[1]); fclose(f); return 5; } /* include chunk ID in CRC */ calc_crc = update_crc(0xFFFFFFFFL, &hdr[4], 4); len = read_be32(&hdr[0]); printf("%6lu %-4.4s %6lu", pos, &hdr[4], len); /* load chunk data and update CRC */ for(; len != 0; ) { unsigned i; i = (len < BUF_SIZE) ? (unsigned)len : BUF_SIZE; if(fread(buf, 1, i, f) != i) goto ERR; calc_crc = update_crc(calc_crc, buf, i); len -= i; } calc_crc ^= 0xFFFFFFFFL; /* read 4-byte CRC at end of chunk to hdr[8-11] */ if(fread(&hdr[8], 1, 4, f) != 4) goto ERR; file_crc = read_be32(&hdr[8]); printf(" %08lX %08lX", calc_crc, file_crc); if(calc_crc == file_crc) printf(" OK\n"); else printf(" ***ERROR***\n"); /* validate IHDR chunk */ if(!memcmp(&hdr[4], "IHDR", 4)) { if(dump_ihdr(buf) != 0) { printf("Invalid .PNG file '%s'\n", arg_v[1]); fclose(f); return 4; } } } while(memcmp(&hdr[4], "IEND", 4)); fclose(f); return 0; }