#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX_SIZE (16 * 1024 * 1024)
/* crc - from rfc2083 */
/* Table of CRCs of all 8-bit messages. */
unsigned long crc_table[256];
/* Flag: has the table been computed? Initially false. */
int crc_table_computed = 0;
/* Make the table for a fast CRC. */
void make_crc_table(void)
{
unsigned long c;
int n, k;
for (n = 0; n < 256; n++) {
c = (unsigned long) n;
for (k = 0; k < 8; k++) {
if (c & 1)
c = 0xedb88320L ^ (c >> 1);
else
c = c >> 1;
}
crc_table[n] = c;
}
crc_table_computed = 1;
}
/* Update a running CRC with the bytes buf[0..len-1]--the CRC
should be initialized to all 1's, and the transmitted value
is the 1's complement of the final running CRC (see the
crc() routine below)). */
unsigned long update_crc(unsigned long crc, const char *buf,
int len)
{
unsigned long c = crc;
int n;
if (!crc_table_computed)
make_crc_table();
for (n = 0; n < len; n++) {
c = crc_table[(c ^ buf[n]) & 0xff] ^ (c >> 8);
}
return c;
}
/* Return the CRC of the bytes buf[0..len-1]. */
unsigned long calculate_crc(const char *buf, int len)
{
return update_crc(0xffffffffL, buf, len) ^ 0xffffffffL;
}
/* png handling */
struct png_data {
int color_type;
};
typedef void (*handler_ptr)(const char *buf, int len, struct png_data *png);
struct handler {
const char *type;
handler_ptr func;
};
void validate(int val, const char *msg)
{
if(!val) {
fprintf(stderr, "Invalid file: %s\n", msg);
exit(1);
}
}
int get_2_byte_big_endian(const char *buf)
{
return ((unsigned char)buf[0] << 8) |
(unsigned char)buf[1];
}
int get_big_endian(const char *buf)
{
return ((unsigned char)buf[0] << 24) |
((unsigned char)buf[1] << 16) |
((unsigned char)buf[2] << 8) |
(unsigned char)buf[3];
}
void header_handler(const char *buf, int len, struct png_data *png)
{
validate(len == 13, "header must be 13 bytes");
printf("Width: %d\n", get_big_endian(buf));
printf("Height: %d\n", get_big_endian(buf + 4));
printf("Bit depth: %d\n", (unsigned char)buf[8]);
png->color_type = (unsigned char)buf[9];
printf("Color type: %d\n", png->color_type);
printf("Compression method: %d\n", (unsigned char)buf[10]);
printf("Filter method: %d\n", (unsigned char)buf[11]);
printf("Interlace method: %d\n", (unsigned char)buf[12]);
}
void background_handler(const char *buf, int len, struct png_data *png)
{
switch(png->color_type) {
case 3:
validate(len == 1, "color type 3 must have 1 byte");
printf("Palette index: %d\n", (unsigned char)buf[0]);
break;
case 0:
case 4:
validate(len == 2, "color type 0/4 must have 2 bytes");
printf("Gray: %d\n", get_2_byte_big_endian(buf));
break;
case 2:
case 6:
validate(len == 6, "color type 2/6 must have 6 bytes");
printf("R: %d\n", get_2_byte_big_endian(buf));
printf("G: %d\n", get_2_byte_big_endian(buf + 2));
printf("B: %d\n", get_2_byte_big_endian(buf + 4));
break;
default:
printf("Unknown background color type\n");
break;
}
}
void phys_handler(const char *buf, int len, struct png_data *png)
{
validate(len == 9, "phys len==9");
printf("pixels/unit, x: %d\n", get_big_endian(buf));
printf("pixels/unit, y: %d\n", get_big_endian(buf + 4));
printf("unit: %d\n", (unsigned char)buf[8]);
}
void time_handler(const char *buf, int len, struct png_data *png)
{
validate(len == 7, "time len==7");
printf("Year: %d\n", get_2_byte_big_endian(buf));
printf("Month: %d\n", (unsigned char)buf[2]);
printf("Day: %d\n", (unsigned char)buf[3]);
printf("Hour: %d\n", (unsigned char)buf[4]);
printf("Minute: %d\n", (unsigned char)buf[5]);
printf("Second: %d\n", (unsigned char)buf[6]);
}
const struct handler handlers[] = {
{ "IHDR", header_handler },
{ "bKGD", background_handler },
{ "pHYs", phys_handler },
{ "tIME", time_handler },
{ NULL, NULL }
};
void check_header(const char *buf)
{
validate((unsigned char)buf[0] == 0x89, "header byte 1");
validate((unsigned char)buf[1] == 'P', "header byte 2");
validate((unsigned char)buf[2] == 'N', "header byte 3");
validate((unsigned char)buf[3] == 'G', "header byte 4");
validate((unsigned char)buf[4] == '\r', "header byte 5");
validate((unsigned char)buf[5] == '\n', "header byte 6");
validate((unsigned char)buf[6] == 0x1a, "header byte 7");
validate((unsigned char)buf[7] == '\n', "header byte 8");
}
void check_crc(const char *crcbuf, const char *buf, int len)
{
int found_crc = get_big_endian(crcbuf);
int calc_crc = calculate_crc(buf, len);
printf("CRC correct: %d\n", found_crc == calc_crc);
}
int main(int argc, char **argv)
{
if(argc != 2) {
printf("Usage: %s <png file>\n", argv[0]);
return 1;
}
char *buf = (char *)malloc(MAX_SIZE);
if(!buf) {
fprintf(stderr, "Couldn't allocate memory\n");
return 1;
}
FILE *f = fopen(argv[1], "r");
if(!f) {
perror("fopen");
free(buf);
return 1;
}
int size = fread(buf, 1, MAX_SIZE, f);
if(size < 8) {
fprintf(stderr, "Too small file\n");
fclose(f);
free(buf);
return 1;
}
check_header(buf);
int pos = 8;
struct png_data png;
memset(&png, 0x00, sizeof(png));
while(pos + 12 <= size) {
char lenbuf[4];
memcpy(lenbuf, buf + pos, 4);
int len = get_big_endian(lenbuf);
char chunkbuf[5];
chunkbuf[4] = 0;
memcpy(chunkbuf, buf + pos + 4, 4);
printf("chunk: %s - len: %d (%d)\n", chunkbuf, len, size - (pos + len + 12));
for(int i = 0; handlers[i].type != NULL; i++) {
if(!strcmp(chunkbuf, handlers[i].type)) {
handlers[i].func(buf + pos + 8, len, &png);
break;
}
}
check_crc(buf + pos + 8 + len, buf + pos + 4, len + 4);
pos += 12 + len;
}
fclose(f);
free(buf);
return 0;
}