Some changes to pnhread.

This commit is contained in:
Marisa the Magician 2026-03-06 14:22:36 +01:00
commit 6aa9054208

485
pngread.c
View file

@ -4,7 +4,7 @@
#include <string.h> #include <string.h>
#include <zlib.h> #include <zlib.h>
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 // if we're in a big endian system, we don't need this
uint16_t testme = 0x1234; uint16_t testme = 0x1234;
@ -15,6 +15,17 @@ uint32_t endianswap( uint32_t n )
return on; 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 typedef struct
{ {
uint8_t highbitcheck; uint8_t highbitcheck;
@ -44,69 +55,465 @@ typedef struct
uint8_t unit; uint8_t unit;
} phys_t; } phys_t;
int verbose = 0;
int exitval = 0; int exitval = 0;
int bailout = 0;
FILE *f = 0; FILE *f = 0;
int nchunk = 0; int nchunk = 0;
pnghead_t hdr = {0,0,0,0,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 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; uint32_t curcrc = 0;
int validcrc = 0; int validcrc = 0;
uint8_t *tdat = 0; // accumulated data for all IDAT chunks uint8_t *tdat = 0; // accumulated data for all IDAT chunks
uint32_t tdatsiz = 0; // size of it 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", char name[4];
endianswap(ihdr->width),endianswap(ihdr->height),ihdr->depth,ihdr->type,ihdr->compression,ihdr->filter,ihdr->interlace); void(*func)(pngchunk_t *chk);
} pngfunc_t;
void dumpData( uint8_t *dat, uint32_t len )
{
for ( uint32_t i=0; i<len; i++ )
{
printf(" %02x",dat[i]);
if ( !((i+1)&15) || ((i+1) == len) ) putchar('\n');
}
} }
// read up a stream chunk void dumpChunk( pngchunk_t *chk )
void readIDAT( uint8_t* dat, uint32_t len )
{ {
if ( tdat ) tdat = realloc(tdat,tdatsiz+len); printf(" !!parser for this chunk not implemented, hex dump follows\n");
else tdat = malloc(len); dumpData(chk->data,chk->length);
memcpy(tdat+tdatsiz,dat,len);
tdatsiz += len;
} }
// process the whole thing const char* getcoltype( uint8_t type )
void processIDAT( void )
{ {
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; if ( !filter )
uint8_t *val = data+strlen(data)+1; return "Adaptive";
uint32_t vallen = len-(val-key); 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; i<ncol; i++ )
printf(" %d: (%hhu,%hhu,%hhu)\n",i,chk->data[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; i<chk->length; 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); printf(" '%s' :: '%.*s'\n",key,vallen,val);
} }
void readchunkdata( pngchunk_t* chk ) void readzTXt( pngchunk_t *chk )
{ {
if ( !strncmp(chk->type,"IHDR",4) ) // TODO decompress this stuff
readIHDR((ihdr_t*)chk->data); dumpChunk(chk);
else if ( !strncmp(chk->type,"pHYs",4) )
readpHYs((phys_t*)chk->data);
else if ( !strncmp(chk->type,"tEXt",4) )
readtEXt(chk->data,chk->length);
} }
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; i<ncol; i++ )
{
if ( div == 10 )
printf(" %u: (%hu,%hu,%hu) [%hu]\n",i,
endianswap16(*(uint16_t*)(pdat+i*10)),
endianswap16(*(uint16_t*)(pdat+i*10+2)),
endianswap16(*(uint16_t*)(pdat+i*10+4)),
endianswap16(*(uint16_t*)(pdat+i*10+6)));
else if ( div == 6 )
printf(" %u: (%hhu,%hhu,%hhu) [%hu]\n",i,
*(uint8_t*)(pdat+i*6),
*(uint8_t*)(pdat+i*6+1),
*(uint8_t*)(pdat+i*6+2),
endianswap16(*(uint16_t*)(pdat+i*6+3)));
}
}
void readhIST( pngchunk_t *chk )
{
if ( chk->length%2 )
{
printf(" !!hIST size not divisible by two, skipping\n");
return;
}
unsigned ncol = chk->length/2;
for ( unsigned i=0; i<ncol; i++ )
printf(" %u: %hu\n",i,endianswap16(*(uint16_t*)(chk->data+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; i<NUMREADERS; i++ )
{
if ( strncmp(chk->type,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 ) 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"); printf("No file supplied.\n");
exitval = 0; exitval = 0;
goto endmii; goto endmii;
} }
f = fopen(argv[1],"rb"); f = fopen(argv[1+verbose],"rb");
if ( !f ) if ( !f )
{ {
printf("Failed to open file.\n"); printf("Failed to open file.\n");
@ -146,7 +553,7 @@ int main( int argc, char **argv )
} }
readchunk: readchunk:
fread(&chk,1,8,f); // read size and type first 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) ) if ( (nchunk == 0) && strncmp(chk.type,"IHDR",4) )
{ {
printf("First chunk is not IHDR.\n"); printf("First chunk is not IHDR.\n");
@ -158,21 +565,33 @@ readchunk:
else chk.data = malloc(chk.length); else chk.data = malloc(chk.length);
fread(chk.data,1,chk.length,f); // read data fread(chk.data,1,chk.length,f); // read data
fread(&(chk.crc),1,4,f); // read CRC fread(&(chk.crc),1,4,f); // read CRC
chk.crc = endianswap(chk.crc); chk.crc = endianswap32(chk.crc);
curcrc = crc32(0,chk.type,4); curcrc = crc32(0,(uint8_t*)chk.type,4);
if ( chk.length ) curcrc = crc32(curcrc,chk.data,chk.length); if ( chk.length ) curcrc = crc32(curcrc,chk.data,chk.length);
validcrc = (curcrc == chk.crc); validcrc = (curcrc == chk.crc);
printf("%.4s chunk of length %u (CRC32: %08X, %s).\n",chk.type,chk.length,chk.crc,validcrc?"OK":"FAILED"); 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); if ( !validcrc ) printf(" CRC32 ACTUAL: %08X\n",curcrc);
else readchunkdata(&chk); else readchunkdata(&chk);
nchunk++; nchunk++;
if ( bailout ) goto endmii;
if ( strncmp(chk.type,"IEND",4) ) goto readchunk; if ( strncmp(chk.type,"IEND",4) ) goto readchunk;
if ( tdat ) processIDAT();
long cpos = ftell(f); long cpos = ftell(f);
fseek(f,0,SEEK_END); fseek(f,0,SEEK_END);
long epos = ftell(f); 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: endmii:
if ( f ) fclose(f); if ( f ) fclose(f);
if ( chk.data ) free(chk.data); if ( chk.data ) free(chk.data);
if ( tdat ) free(tdat);
return exitval; return exitval;
} }