#include #include #include #include #include uint32_t endianswap( uint32_t n ) { // if we're in a big endian system, we don't need this uint16_t testme = 0x1234; if ( *(uint8_t*)(&testme) == 0x12 ) return n; uint32_t on; for ( int i=0; i<4; i++ ) *(((uint8_t*)(&on))+i) = *(((uint8_t*)(&n))+(3-i)); return on; } typedef struct { uint8_t highbitcheck; char sig[3]; char crlf[2]; char dosstop; char lf; } pnghead_t; typedef struct { uint32_t length; char type[4]; uint8_t *data; uint32_t crc; } pngchunk_t; typedef struct { uint32_t width, height; uint8_t depth, type, compression, filter, interlace; } ihdr_t; typedef struct { uint32_t xppu, yppu; uint8_t unit; } phys_t; int exitval = 0; FILE *f = 0; int nchunk = 0; pnghead_t hdr = {0,0,0,0,0}; pngchunk_t chk = {0,0,0,0}; // make sure this is null at the very start uint32_t curcrc = 0; int validcrc = 0; uint8_t *tdat = 0; // accumulated data for all IDAT chunks uint32_t tdatsiz = 0; // size of it void readIHDR( ihdr_t* ihdr ) { printf(" width: %u\n height: %u\n depth: %u\n type: %u\n compression: %u\n filter: %u\n interlace: %u\n", endianswap(ihdr->width),endianswap(ihdr->height),ihdr->depth,ihdr->type,ihdr->compression,ihdr->filter,ihdr->interlace); } // read up a stream chunk void readIDAT( uint8_t* dat, uint32_t len ) { if ( tdat ) tdat = realloc(tdat,tdatsiz+len); else tdat = malloc(len); memcpy(tdat+tdatsiz,dat,len); tdatsiz += len; } // process the whole thing void processIDAT( void ) { } void readpHYs( phys_t* phys ) { printf(" xppu: %u\n yppu: %u\n unit: %u\n",endianswap(phys->xppu),endianswap(phys->yppu),phys->unit); } void readtEXt( uint8_t* data, uint32_t len ) { uint8_t *key = data; uint8_t *val = data+strlen(data)+1; uint32_t vallen = len-(val-key); printf(" '%s' :: '%.*s'\n",key,vallen,val); } void readchunkdata( pngchunk_t* chk ) { if ( !strncmp(chk->type,"IHDR",4) ) readIHDR((ihdr_t*)chk->data); else if ( !strncmp(chk->type,"pHYs",4) ) readpHYs((phys_t*)chk->data); else if ( !strncmp(chk->type,"tEXt",4) ) readtEXt(chk->data,chk->length); } int main( int argc, char **argv ) { if ( argc < 2 ) { printf("No file supplied.\n"); exitval = 0; goto endmii; } f = fopen(argv[1],"rb"); if ( !f ) { printf("Failed to open file.\n"); exitval = 1; goto endmii; } fread(&hdr,1,sizeof(hdr),f); if ( hdr.highbitcheck != 0x89 ) { printf("High bit check failed.\n"); exitval = 2; goto endmii; } if ( strncmp(hdr.sig,"PNG",3) ) { printf("PNG signature check failed.\n"); exitval = 4; goto endmii; } if ( strncmp(hdr.crlf,"\r\n",2) ) { printf("CRLF check failed.\n"); exitval = 8; goto endmii; } if ( hdr.dosstop != '\032' ) { printf("EOF check failed.\n"); exitval = 16; goto endmii; } if ( hdr.lf != '\n' ) { printf("LF check failed.\n"); exitval = 32; goto endmii; } readchunk: fread(&chk,1,8,f); // read size and type first chk.length = endianswap(chk.length); // swap from BE if ( (nchunk == 0) && strncmp(chk.type,"IHDR",4) ) { printf("First chunk is not IHDR.\n"); exitval = 64; goto endmii; } // (re)allocate data if ( chk.data ) chk.data = realloc(chk.data,chk.length); else chk.data = malloc(chk.length); fread(chk.data,1,chk.length,f); // read data fread(&(chk.crc),1,4,f); // read CRC chk.crc = endianswap(chk.crc); curcrc = crc32(0,chk.type,4); if ( chk.length ) curcrc = crc32(curcrc,chk.data,chk.length); validcrc = (curcrc == chk.crc); printf("%.4s chunk of length %u (CRC32: %08X, %s).\n",chk.type,chk.length,chk.crc,validcrc?"OK":"FAILED"); if ( !validcrc ) printf(" CRC32 ACTUAL: %08X\n",curcrc); else readchunkdata(&chk); nchunk++; if ( strncmp(chk.type,"IEND",4) ) goto readchunk; long cpos = ftell(f); fseek(f,0,SEEK_END); long epos = ftell(f); if ( epos > cpos ) printf("%lu bytes of extra data after IEND.\n",epos-cpos); endmii: if ( f ) fclose(f); if ( chk.data ) free(chk.data); return exitval; }