1ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik
2ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik/* png-fix-itxt version 1.0.0
3ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik *
49b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett * Copyright 2015 Glenn Randers-Pehrson
59b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett * Last changed in libpng 1.6.18 [July 23, 2015]
6ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik *
7ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik * This code is released under the libpng license.
8ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik * For conditions of distribution and use, see the disclaimer
9ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik * and license in png.h
10ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik *
1106f1087a94f1e48298e89d77ccc51a0ced871958Matt Sarett * Usage:
12ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik *
13ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik *     png-fix-itxt.exe < bad.png > good.png
14ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik *
15ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik * Fixes a PNG file written with libpng-1.6.0 or 1.6.1 that has one or more
16ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik * uncompressed iTXt chunks.  Assumes that the actual length is greater
17ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik * than or equal to the value in the length byte, and that the CRC is
18ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik * correct for the actual length.  This program hunts for the CRC and
19ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik * adjusts the length byte accordingly.  It is not an error to process a
20ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik * PNG file that has no iTXt chunks or one that has valid iTXt chunks;
21ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik * such files will simply be copied.
22ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik *
23ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik * Requires zlib (for crc32 and Z_NULL); build with
24ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik *
25ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik *     gcc -O -o png-fix-itxt png-fix-itxt.c -lz
26ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik *
27ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik * If you need to handle iTXt chunks larger than 500000 kbytes you must
28ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik * rebuild png-fix-itxt with a larger values of MAX_LENGTH (or a smaller value
29ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik * if you know you will never encounter such huge iTXt chunks).
30ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik */
31ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik
32ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik#include <stdio.h>
33ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik#include <zlib.h>
34ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik
35ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik#define MAX_LENGTH 500000
36ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik
379b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett/* Read one character (inchar), also return octet (c), break if EOF */
389b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett#define GETBREAK inchar=getchar(); \
399b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett                 c=(inchar & 0xffU);\
409b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett                 if (inchar != c) break
41ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craikint
42ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craikmain(void)
43ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik{
44ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik   unsigned int i;
45ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik   unsigned char buf[MAX_LENGTH];
46ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik   unsigned long crc;
47ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik   unsigned char c;
48ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik   int inchar;
49ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik
50ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik/* Skip 8-byte signature */
51ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik   for (i=8; i; i--)
52ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik   {
539b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett      GETBREAK;
54ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik      putchar(c);
55ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik   }
56ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik
579b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarettif (inchar == c) /* !EOF */
58ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craikfor (;;)
59ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik {
60ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik   /* Read the length */
61ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik   unsigned long length; /* must be 32 bits! */
629b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   GETBREAK; buf[0] = c; length  = c; length <<= 8;
639b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   GETBREAK; buf[1] = c; length += c; length <<= 8;
649b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   GETBREAK; buf[2] = c; length += c; length <<= 8;
659b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   GETBREAK; buf[3] = c; length += c;
66ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik
67ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik   /* Read the chunkname */
689b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   GETBREAK; buf[4] = c;
699b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   GETBREAK; buf[5] = c;
709b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   GETBREAK; buf[6] = c;
719b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   GETBREAK; buf[7] = c;
72ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik
73ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik
74ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik   /* The iTXt chunk type expressed as integers is (105, 84, 88, 116) */
75ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik   if (buf[4] == 105 && buf[5] == 84 && buf[6] == 88 && buf[7] == 116)
76ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik   {
77ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik      if (length >= MAX_LENGTH-12)
78ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik         break;  /* To do: handle this more gracefully */
79ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik
80ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik      /* Initialize the CRC */
81ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik      crc = crc32(0, Z_NULL, 0);
82ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik
83ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik      /* Copy the data bytes */
84ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik      for (i=8; i < length + 12; i++)
85ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik      {
869b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett         GETBREAK; buf[i] = c;
87ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik      }
88ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik
899b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett      if (inchar != c) /* EOF */
909b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett         break;
919b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
92ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik      /* Calculate the CRC */
93ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik      crc = crc32(crc, buf+4, (uInt)length+4);
94ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik
95ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik      for (;;)
96ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik      {
97ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik        /* Check the CRC */
989b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett        if (((crc >> 24) & 0xffU) == buf[length+8] &&
999b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett            ((crc >> 16) & 0xffU) == buf[length+9] &&
1009b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett            ((crc >>  8) & 0xffU) == buf[length+10] &&
1019b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett            ((crc      ) & 0xffU) == buf[length+11])
102ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik           break;
103ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik
104ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik        length++;
105ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik
106ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik        if (length >= MAX_LENGTH-12)
107ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik           break;
108ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik
1099b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett        GETBREAK;
1109b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett        buf[length+11] = c;
111ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik
112ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik        /* Update the CRC */
113ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik        crc = crc32(crc, buf+7+length, 1);
114ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik      }
115ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik
1169b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett      if (inchar != c) /* EOF */
1179b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett         break;
1189b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
119ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik      /* Update length bytes */
1209b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett      buf[0] = (unsigned char)((length >> 24) & 0xffU);
1219b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett      buf[1] = (unsigned char)((length >> 16) & 0xffU);
1229b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett      buf[2] = (unsigned char)((length >>  8) & 0xffU);
1239b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett      buf[3] = (unsigned char)((length      ) & 0xffU);
124ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik
125ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik      /* Write the fixed iTXt chunk (length, name, data, crc) */
126ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik      for (i=0; i<length+12; i++)
127ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik         putchar(buf[i]);
128ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik   }
129ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik
130ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik   else
131ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik   {
1329b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett      if (inchar != c) /* EOF */
1339b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett         break;
1349b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
135ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik      /* Copy bytes that were already read (length and chunk name) */
136ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik      for (i=0; i<8; i++)
137ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik         putchar(buf[i]);
138ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik
139ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik      /* Copy data bytes and CRC */
140ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik      for (i=8; i< length+12; i++)
141ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik      {
1429b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett         GETBREAK;
143ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik         putchar(c);
144ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik      }
145ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik
1469b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett      if (inchar != c) /* EOF */
147ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik      {
148ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik         break;
149ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik      }
150ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik
151ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik   /* The IEND chunk type expressed as integers is (73, 69, 78, 68) */
152ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik      if (buf[4] == 73 && buf[5] == 69 && buf[6] == 78 && buf[7] == 68)
153ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik         break;
154ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik   }
155ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik
1569b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   if (inchar != c) /* EOF */
157ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik      break;
158ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik
159ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik   if (buf[4] == 73 && buf[5] == 69 && buf[6] == 78 && buf[7] == 68)
160ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik     break;
161ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik }
162ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik
163ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik return 0;
164ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik}
165