stuff/pngread.c
2022-01-01 20:50:20 +01:00

178 lines
3.9 KiB
C

#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <zlib.h>
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;
}