From 6aa90542080094333b4f8f86f37241ebffefb8e5 Mon Sep 17 00:00:00 2001 From: Marisa Lago Ballesteros Date: Fri, 6 Mar 2026 14:22:36 +0100 Subject: [PATCH] Some changes to pnhread. --- pngread.c | 485 ++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 452 insertions(+), 33 deletions(-) diff --git a/pngread.c b/pngread.c index 3b90365..4677d0c 100644 --- a/pngread.c +++ b/pngread.c @@ -4,7 +4,7 @@ #include #include -uint32_t endianswap( uint32_t n ) +uint32_t endianswap32( uint32_t n ) { // if we're in a big endian system, we don't need this uint16_t testme = 0x1234; @@ -15,6 +15,17 @@ uint32_t endianswap( uint32_t n ) return on; } +uint16_t endianswap16( uint16_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; + uint16_t on; + for ( int i=0; i<2; i++ ) + *(((uint8_t*)(&on))+i) = *(((uint8_t*)(&n))+(1-i)); + return on; +} + typedef struct { uint8_t highbitcheck; @@ -44,69 +55,465 @@ typedef struct uint8_t unit; } phys_t; +int verbose = 0; int exitval = 0; +int bailout = 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 +pnghead_t hdr = {0,{0},{0},0,0}; +pngchunk_t chk = {0,{0},0,0}; // make sure this is null at the very start +uint8_t coldepth = 0; +uint8_t coltype = 0; 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 ) +typedef struct { - 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); + char name[4]; + void(*func)(pngchunk_t *chk); +} pngfunc_t; + +void dumpData( uint8_t *dat, uint32_t len ) +{ + for ( uint32_t i=0; idata,chk->length); } -// process the whole thing -void processIDAT( void ) +const char* getcoltype( uint8_t type ) { + switch ( type ) + { + case 0: + return "Grayscale"; + case 2: + return "RGB"; + case 3: + return "Indexed"; + case 4: + return "Grayscale Alpha"; + case 6: + return "RGB Alpha"; + } + return "Invalid"; } -void readpHYs( phys_t* phys ) +const char* getcompression( uint8_t compression ) { - printf(" xppu: %u\n yppu: %u\n unit: %u\n",endianswap(phys->xppu),endianswap(phys->yppu),phys->unit); + if ( !compression ) + return "Deflate"; + return "Invalid"; } -void readtEXt( uint8_t* data, uint32_t len ) +const char* getfilter( uint8_t filter ) { - uint8_t *key = data; - uint8_t *val = data+strlen(data)+1; - uint32_t vallen = len-(val-key); + if ( !filter ) + return "Adaptive"; + return "Invalid"; +} + +const char* getinterlace( uint8_t interlace ) +{ + switch ( interlace ) + { + case 0: + return "None"; + case 1: + return "Adam7"; + } + return "Invalid"; +} + +void readIHDR( pngchunk_t *chk ) +{ + if ( chk->length != 13 ) + { + printf(" !!IHDR size is not 13, bailing out\n"); + bailout = 1; + return; + } + ihdr_t* ihdr = (ihdr_t*)(chk->data); + printf(" width: %u\n height: %u\n depth: %u\n type: %u (%s)\n" + " compression: %u (%s)\n filter: %u (%s)\n interlace: %u (%s)\n", + endianswap32(ihdr->width),endianswap32(ihdr->height), + ihdr->depth,ihdr->type,getcoltype(ihdr->type), + ihdr->compression,getcompression(ihdr->compression), + ihdr->filter,getfilter(ihdr->filter), + ihdr->interlace,getinterlace(ihdr->interlace)); + coldepth = ihdr->type; + coltype = ihdr->type; +} + +void readPLTE( pngchunk_t *chk ) +{ + if ( chk->length%3 ) + { + printf(" !!palette length not divisible by 3, may be corrupted, skipping\n"); + return; + } + unsigned ncol = chk->length/3; + for ( unsigned i=0; idata[i*3],chk->data[i*3+1],chk->data[i*3+2]); +} + +void readIDAT( pngchunk_t *chk ) +{ + if ( tdat ) tdat = realloc(tdat,tdatsiz+chk->length); + else tdat = malloc(chk->length); + memcpy(tdat+tdatsiz,chk->data,chk->length); + tdatsiz += chk->length; +} + +void readtRNS( pngchunk_t *chk ) +{ + for ( unsigned i=0; ilength; i++ ) + printf(" %d: %hhu\n",i,chk->data[i]); +} + +void readgAMA( pngchunk_t *chk ) +{ + if ( chk->length != 4 ) + { + printf(" !!gAMA size is not 4, skipping\n"); + return; + } + uint32_t rg = endianswap32(*(uint32_t*)(chk->data)); + printf(" 1/%g\n",1./(rg/100000.)); +} + +void readcHRM( pngchunk_t *chk ) +{ + if ( chk->length != 32 ) + { + printf(" !!cHRM size is not 64, skipping\n"); + return; + } + uint32_t rwx = endianswap32(*(uint32_t*)(chk->data)), + rwy = endianswap32(*(uint32_t*)(chk->data+4)), + rrx = endianswap32(*(uint32_t*)(chk->data+8)), + rry = endianswap32(*(uint32_t*)(chk->data+12)), + rgx = endianswap32(*(uint32_t*)(chk->data+16)), + rgy = endianswap32(*(uint32_t*)(chk->data+20)), + rbx = endianswap32(*(uint32_t*)(chk->data+24)), + rby = endianswap32(*(uint32_t*)(chk->data+28)); + printf(" White Point x: %g\n White Point y: %g\n" + " Red x: %g\n Red y: %g\n" + " Green x: %g\n Green y: %g\n" + " Blue x: %g\n Blue y: %g\n", + rwx/100000.,rwy/100000., + rrx/100000.,rry/100000., + rgx/100000.,rgy/100000., + rbx/100000.,rby/100000.); +} + +void readsRGB( pngchunk_t *chk ) +{ + if ( chk->length != 1 ) + { + printf(" !!sRGB size is not 1, skipping\n"); + return; + } + switch ( chk->data[0] ) + { + case 0: + printf(" Perceptual\n"); + break; + case 1: + printf(" Relative colorimetric\n"); + break; + case 2: + printf(" Saturation\n"); + break; + case 3: + printf(" Absolute colorimetric\n"); + break; + default: + printf(" Unknown\n"); + break; + } +} + +void readiCCP( pngchunk_t *chk ) +{ + // TODO process this + dumpChunk(chk); +} + +void readtEXt( pngchunk_t *chk ) +{ + char *key = (char*)chk->data; + char *val = key+strlen(key)+1; + uint32_t vallen = chk->length-(val-key); printf(" '%s' :: '%.*s'\n",key,vallen,val); } -void readchunkdata( pngchunk_t* chk ) +void readzTXt( 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); + // TODO decompress this stuff + dumpChunk(chk); } +void readiTXt( pngchunk_t *chk ) +{ + char *key = (char*)chk->data; + uint8_t cmpflag = chk->data[strlen(key)+1]; + if ( cmpflag ) + { + printf(" !!compressed iTXt not supported, skipping"); + return; + } + char *lang = (char*)(chk->data+strlen(key)+3); + char *kword = (char*)(chk->data+strlen(key)+3+strlen(lang)+1); + char *text = (char*)(chk->data+strlen(key)+3+strlen(lang)+1+strlen(kword)+1); + uint32_t textlen = chk->length-(text-key); + printf(" '%s' :: ",key); + if ( lang[0] ) printf("[%s] ",lang); + if ( kword[0] ) printf("'%s' ",kword); + if ( lang[0] || kword[0] ) printf(":: "); + printf("'%.*s'\n",textlen,text); +} + +void readbKGD( pngchunk_t *chk ) +{ + switch ( coltype ) + { + case 3: + if ( chk->length != 1 ) + { + printf(" !!bKGD size is not 1, skipping\n"); + return; + } + printf(" BG: %hhu\n",chk->data[0]); + break; + case 0: + case 4: + if ( chk->length != 2 ) + { + printf(" !!bKGD size is not 2, skipping\n"); + return; + } + printf(" BG: %hu\n",endianswap16(*(uint16_t*)(chk->data))); + break; + case 2: + case 6: + if ( chk->length != 6 ) + { + printf(" !!bKGD size is not 6, skipping\n"); + return; + } + printf(" BG: (%hu,%hu,%hu)\n", + endianswap16(*(uint16_t*)(chk->data)), + endianswap16(*(uint16_t*)(chk->data+2)), + endianswap16(*(uint16_t*)(chk->data+4))); + break; + default: + printf(" !!bKGD present for unhandled color type %d, skipping\n",coltype); + break; + } +} + +void readpHYs( pngchunk_t *chk ) +{ + if ( chk->length != 9 ) + { + printf(" !!pHYs size is not 9, skipping\n"); + return; + } + phys_t* phys = (phys_t*)(chk->data); + printf(" xppu: %u\n yppu: %u\n unit: %u\n",endianswap32(phys->xppu),endianswap32(phys->yppu),phys->unit); +} + +void readsBIT( pngchunk_t *chk ) +{ + switch ( coltype ) + { + case 0: + if ( chk->length != 1 ) + { + printf(" !!sBIT size is not 1, skipping\n"); + return; + } + printf(" %hhu\n",chk->data[0]); + break; + case 2: + case 3: + if ( chk->length != 3 ) + { + printf(" !!sBIT size is not 3, skipping\n"); + return; + } + printf(" (%hhu,%hhu,%hhu)\n",chk->data[0],chk->data[1],chk->data[2]); + break; + case 4: + if ( chk->length != 2 ) + { + printf(" !!sBIT size is not 2, skipping\n"); + return; + } + printf(" (%hhu,%hhu)\n",chk->data[0],chk->data[1]); + break; + case 6: + if ( chk->length != 4 ) + { + printf(" !!sBIT size is not 4, skipping\n"); + return; + } + printf(" (%hhu,%hhu,%hhu,%hhu)\n",chk->data[0],chk->data[1],chk->data[2],chk->data[3]); + break; + default: + printf(" !!sBIT present for unhandled color type %d, skipping\n",coltype); + break; + } +} + +void readsPLT( pngchunk_t *chk ) +{ + char *name = (char*)chk->data; + uint8_t depth = chk->data[strlen(name)+1]; + printf(" suggested palette: %s\n sample depth: %hhu\n",name,depth); + int div; + if ( depth == 16 ) + div = 10; + else if ( depth == 8 ) + div = 6; + else + { + printf(" !!invalid depth of %u, skipping\n",depth); + return; + } + uint32_t psiz = (chk->length-(strlen(name)+2)); + if ( psiz%div ) + { + printf(" !!palette length not divisible by %d, skipping\n",div); + return; + } + unsigned ncol = psiz/div; + uint8_t *pdat = chk->data+(strlen(name)+2); + for ( unsigned i=0; ilength%2 ) + { + printf(" !!hIST size not divisible by two, skipping\n"); + return; + } + unsigned ncol = chk->length/2; + for ( unsigned i=0; idata+i*2))); +} + +void readtIME( pngchunk_t *chk ) +{ + if ( chk->length != 7 ) + { + printf(" !!tIME size is not 7, skipping\n"); + return; + } + printf(" %hu-%hhu-%hhu %hhu:%hhu:%hhu\n", + endianswap16(*(uint16_t*)(chk->data)),chk->data[2],chk->data[3], + chk->data[4],chk->data[5],chk->data[6]); +} + +void readgrAb( pngchunk_t *chk ) +{ + if ( chk->length != 8 ) + { + printf(" !!grAb size is not 8, skipping\n"); + return; + } + uint32_t rx = endianswap32(*(uint32_t*)(chk->data)), ry = endianswap32(*(uint32_t*)(chk->data+4)); + int32_t x = *(int32_t*)(&rx), y = *(int32_t*)(&ry); + printf(" %+d,%+d\n",x,y); +} + +#define NUMREADERS 20 + +pngfunc_t readers[NUMREADERS] = +{ + // critical + {"IHDR",readIHDR}, + {"PLTE",readPLTE}, + {"IDAT",readIDAT}, + {"IEND",0}, + // transparency + {"tRNS",readtRNS}, + // colorspace + {"gAMA",readgAMA}, + {"cHRM",readcHRM}, + {"sRGB",readsRGB}, + {"iCCP",readiCCP}, + // text + {"tEXt",readtEXt}, + {"zTXt",readzTXt}, + {"iTXt",readiTXt}, + // misc + {"bKGD",readbKGD}, + {"pHYs",readpHYs}, + {"sBIT",readsBIT}, + {"sPLT",readsPLT}, + {"hIST",readhIST}, + {"tIME",readtIME}, + // zdoom + {"grAb",readgrAb}, + {"alPh",0} +}; + +void readchunkdata( pngchunk_t* chk ) +{ + for ( int i=0; itype,readers[i].name,4) ) continue; + if ( readers[i].func ) readers[i].func(chk); + return; + } + printf(" !!no reader for this chunk type\n"); +} + +// TODO process the whole thing (might not bother actually) +void processIDAT( void ) +{ + if ( !verbose ) return; + printf("full datastream dump follows:\n"); + dumpData(tdat,tdatsiz); +} int main( int argc, char **argv ) { - if ( argc < 2 ) + if ( (argc > 1) && (!strncmp(argv[1],"-v",2) || !strncmp(argv[1],"--verbose",9)) ) + verbose = 1; + if ( argc < (2+verbose) ) { printf("No file supplied.\n"); exitval = 0; goto endmii; } - f = fopen(argv[1],"rb"); + f = fopen(argv[1+verbose],"rb"); if ( !f ) { printf("Failed to open file.\n"); @@ -146,7 +553,7 @@ int main( int argc, char **argv ) } readchunk: fread(&chk,1,8,f); // read size and type first - chk.length = endianswap(chk.length); // swap from BE + chk.length = endianswap32(chk.length); // swap from BE if ( (nchunk == 0) && strncmp(chk.type,"IHDR",4) ) { printf("First chunk is not IHDR.\n"); @@ -158,21 +565,33 @@ readchunk: 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); + chk.crc = endianswap32(chk.crc); + curcrc = crc32(0,(uint8_t*)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 ( bailout ) goto endmii; if ( strncmp(chk.type,"IEND",4) ) goto readchunk; + if ( tdat ) processIDAT(); 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); + if ( epos > cpos ) + { + printf(" !!there are %lu bytes of extra data after IEND\n",epos-cpos); + tdatsiz = epos-cpos; + if ( tdat ) tdat = realloc(tdat,tdatsiz); + else tdat = malloc(tdatsiz); + fseek(f,cpos,SEEK_SET); + fread(tdat,1,tdatsiz,f); + if ( verbose ) dumpData(tdat,tdatsiz); + } endmii: if ( f ) fclose(f); if ( chk.data ) free(chk.data); + if ( tdat ) free(tdat); return exitval; }