lzhuf: Refactored to avoid direct array access
[deark.git] / modules / png.c
blob3b258bd6e0c1db96853456204c9dd48c78a08d56
1 // This file is part of Deark.
2 // Copyright (C) 2016 Jason Summers
3 // See the file COPYING for terms of use.
5 // PNG and related formats
7 #include <deark-config.h>
8 #include <deark-private.h>
9 #include <deark-fmtutil.h>
10 DE_DECLARE_MODULE(de_module_png);
12 #define CODE_CgBI 0x43674249U
13 #define CODE_IDAT 0x49444154U
14 #define CODE_IEND 0x49454e44U
15 #define CODE_IHDR 0x49484452U
16 #define CODE_PLTE 0x504c5445U
17 #define CODE_acTL 0x6163544cU
18 #define CODE_bKGD 0x624b4744U
19 #define CODE_cHRM 0x6348524dU
20 #define CODE_caNv 0x63614e76U
21 #define CODE_eXIf 0x65584966U
22 #define CODE_exIf 0x65784966U
23 #define CODE_fcTL 0x6663544cU
24 #define CODE_fdAT 0x66644154U
25 #define CODE_gAMA 0x67414d41U
26 #define CODE_hIST 0x68495354U
27 #define CODE_htSP 0x68745350U
28 #define CODE_iCCP 0x69434350U
29 #define CODE_iTXt 0x69545874U
30 #define CODE_orNT 0x6f724e54U
31 #define CODE_pHYs 0x70485973U
32 #define CODE_sBIT 0x73424954U
33 #define CODE_sPLT 0x73504c54U
34 #define CODE_sRGB 0x73524742U
35 #define CODE_tEXt 0x74455874U
36 #define CODE_tIME 0x74494d45U
37 #define CODE_tRNS 0x74524e53U
38 #define CODE_zTXt 0x7a545874U
40 #define CODE_BACK 0x4241434bU
41 #define CODE_BASI 0x42415349U
42 #define CODE_CLIP 0x434c4950U
43 #define CODE_CLON 0x434c4f4eU
44 #define CODE_DBYK 0x4442594bU
45 #define CODE_DEFI 0x44454649U
46 #define CODE_DHDR 0x44484452U
47 #define CODE_DISC 0x44495343U
48 #define CODE_DROP 0x44524f50U
49 #define CODE_ENDL 0x454e444cU
50 #define CODE_FRAM 0x4652414dU
51 #define CODE_IJNG 0x494a4e47U
52 #define CODE_IPNG 0x49504e47U
53 #define CODE_JDAA 0x4a444141U
54 #define CODE_JDAT 0x4a444154U
55 #define CODE_JHDR 0x4a484452U
56 #define CODE_JSEP 0x4a534550U
57 #define CODE_LOOP 0x4c4f4f50U
58 #define CODE_MAGN 0x4d41474eU
59 #define CODE_MEND 0x4d454e44U
60 #define CODE_MHDR 0x4d484452U
61 #define CODE_MOVE 0x4d4f5645U
62 #define CODE_ORDR 0x4f524452U
63 #define CODE_PAST 0x50415354U
64 #define CODE_PPLT 0x50504c54U
65 #define CODE_PROM 0x50524f4dU
66 #define CODE_SAVE 0x53415645U
67 #define CODE_SEEK 0x5345454bU
68 #define CODE_SHOW 0x53484f57U
69 #define CODE_TERM 0x5445524dU
70 #define CODE_eXPI 0x65585049U
71 #define CODE_fPRI 0x66505249U
72 #define CODE_nEED 0x6e454544U
73 #define CODE_pHYg 0x70485967U
75 typedef struct localctx_struct {
76 #define DE_PNGFMT_PNG 1
77 #define DE_PNGFMT_JNG 2
78 #define DE_PNGFMT_MNG 3
79 int fmt;
80 int is_CgBI;
81 u8 check_crcs;
82 u8 color_type;
83 const char *fmt_name;
84 struct de_crcobj *crco;
85 } lctx;
87 struct text_chunk_ctx {
88 int suppress_debugstr;
89 int is_xmp;
90 int is_im_generic_profile; // ImageMagick-style generic "Raw profile type"
91 #define PROFILETYPE_8BIM 1
92 #define PROFILETYPE_IPTC 2
93 #define PROFILETYPE_XMP 3
94 #define PROFILETYPE_ICC 4
95 int im_generic_profile_type;
96 const char *im_generic_profile_type_name;
99 #define FIELD_KEYWORD 1
100 #define FIELD_LANG 2
101 #define FIELD_XKEYWORD 3
102 #define FIELD_MAIN 4
104 struct chunk_type_info_struct;
105 struct handler_params;
106 struct chunk_ctx;
108 typedef void (*chunk_handler_fn)(deark *c, lctx *d, struct handler_params *hp);
110 struct chunk_type_info_struct {
111 u32 id;
112 // The low 8 bits of flags are reserved, and should be to 0xff.
113 // They may someday be used to, for example, make MNG-only chunk table entries.
114 u32 flags;
115 const char *name;
116 chunk_handler_fn handler_fn;
119 // TODO: Merge this with chunk_ctx?
120 struct handler_params {
121 i64 dpos;
122 i64 dlen;
123 const struct chunk_type_info_struct *cti;
124 struct de_fourcc chunk4cc;
127 struct chunk_ctx {
128 struct handler_params hp;
129 i64 pos;
130 u32 crc_reported;
131 u32 crc_calc;
134 static void handler_hexdump(deark *c, lctx *d, struct handler_params *hp)
136 de_dbg_hexdump(c, c->infile, hp->dpos, hp->dlen, 256, NULL, 0x1);
139 static void on_im_generic_profile_keyword(deark *c, lctx *d,
140 struct text_chunk_ctx *tcc, struct de_stringreaderdata *srd)
142 char typestr[32];
144 tcc->is_im_generic_profile = 1;
145 tcc->im_generic_profile_type = 0;
146 tcc->im_generic_profile_type_name = NULL;
147 tcc->suppress_debugstr = 1;
149 de_bytes_to_printable_sz((const u8*)(srd->sz+17), de_strlen(srd->sz+17),
150 typestr, sizeof(typestr), 0, DE_ENCODING_ASCII);
152 if(!de_strcmp(typestr, "8bim")) {
153 tcc->im_generic_profile_type = PROFILETYPE_8BIM;
154 tcc->im_generic_profile_type_name = "Photoshop";
156 else if(!de_strcmp(typestr, "iptc")) {
157 tcc->im_generic_profile_type = PROFILETYPE_IPTC;
158 tcc->im_generic_profile_type_name = "IPTC";
160 else if(!de_strcmp(typestr, "xmp")) {
161 tcc->im_generic_profile_type = PROFILETYPE_XMP;
162 tcc->im_generic_profile_type_name = "XMP";
164 else if(!de_strcmp(typestr, "icc")) {
165 tcc->im_generic_profile_type = PROFILETYPE_ICC;
166 tcc->im_generic_profile_type_name = "ICC";
168 else {
169 if(c->extract_level<2) {
170 tcc->suppress_debugstr = 0;
175 // Generic (ImageMagick?) profile. Hex-encoded, with three header lines.
176 static void on_im_generic_profile_main(deark *c, lctx *d,
177 struct text_chunk_ctx *tcc, dbuf *inf, i64 pos1, i64 len)
179 int k;
180 i64 pos = pos1;
181 i64 dlen;
182 int dump_to_file = 0;
183 int decode_to_membuf = 0;
184 const char *ext = NULL;
186 // Skip the first three lines
187 for(k=0; k<3; k++) {
188 int ret;
189 i64 foundpos = 0;
190 ret = dbuf_search_byte(inf, 0x0a, pos, pos1+len-pos, &foundpos);
191 if(!ret) goto done;
192 pos = foundpos+1;
194 dlen = pos1+len-pos;
196 if(tcc->im_generic_profile_type==PROFILETYPE_XMP) {
197 dump_to_file = 1;
198 ext = "xmp";
200 else if(tcc->im_generic_profile_type==PROFILETYPE_8BIM) {
201 decode_to_membuf = 1;
203 else if(tcc->im_generic_profile_type==PROFILETYPE_IPTC) {
204 if(c->extract_level>=2) {
205 dump_to_file = 1;
206 ext = "iptc";
208 else {
209 decode_to_membuf = 1;
212 else if(tcc->im_generic_profile_type==PROFILETYPE_ICC) {
213 dump_to_file = 1;
214 ext = "icc";
216 else {
217 if(c->extract_level>=2) {
218 dump_to_file = 1;
219 ext = "profile.bin";
223 if(dump_to_file) {
224 dbuf *outf;
225 outf = dbuf_create_output_file(c, ext?ext:"bin", NULL, DE_CREATEFLAG_IS_AUX);
226 de_decode_base16(c, inf, pos, dlen, outf, 0);
227 dbuf_close(outf);
230 if(decode_to_membuf) {
231 dbuf *tmpf;
233 tmpf = dbuf_create_membuf(c, 0, 0);
234 de_decode_base16(c, inf, pos, dlen, tmpf, 0);
236 if(tcc->im_generic_profile_type==PROFILETYPE_8BIM) {
237 fmtutil_handle_photoshop_rsrc(c, tmpf, 0, tmpf->len, 0x0);
239 else if(tcc->im_generic_profile_type==PROFILETYPE_IPTC) {
240 fmtutil_handle_iptc(c, tmpf, 0, tmpf->len, 0x0);
243 dbuf_close(tmpf);
246 done:
250 // An internal function that does the main work of do_text_field().
251 // TODO: Clean up the text field processing code. It's gotten too messy.
252 static int do_unc_text_field(deark *c, lctx *d,
253 struct text_chunk_ctx *tcc, int which_field,
254 dbuf *srcdbuf, i64 pos, i64 bytes_avail,
255 int is_nul_terminated, de_encoding encoding, i64 *bytes_consumed)
257 const char *name;
258 int retval = 0;
259 struct de_stringreaderdata *srd = NULL;
261 *bytes_consumed = 0;
262 if(bytes_avail<0) return 0;
264 if(which_field==FIELD_MAIN && tcc->is_xmp) {
265 // The main field is never NUL terminated, so we can do this right away.
266 dbuf_create_file_from_slice(srcdbuf, pos, bytes_avail, "xmp",
267 NULL, DE_CREATEFLAG_IS_AUX);
268 retval = 1;
269 goto done;
272 if(is_nul_terminated) {
273 srd = dbuf_read_string(srcdbuf, pos, bytes_avail, DE_DBG_MAX_STRLEN,
274 DE_CONVFLAG_STOP_AT_NUL, encoding);
276 if(!srd->found_nul) goto done;
277 *bytes_consumed = srd->bytes_consumed - 1;
279 else {
280 i64 bytes_to_scan;
282 *bytes_consumed = bytes_avail;
284 bytes_to_scan = bytes_avail;
285 if(bytes_to_scan>DE_DBG_MAX_STRLEN) bytes_to_scan = DE_DBG_MAX_STRLEN;
286 srd = dbuf_read_string(srcdbuf, pos, bytes_to_scan, bytes_to_scan, 0, encoding);
289 if(which_field==FIELD_KEYWORD) {
290 if(!de_strcmp(srd->sz, "XML:com.adobe.xmp")) {
291 tcc->is_xmp = 1;
295 switch(which_field) {
296 case FIELD_KEYWORD: name="keyword"; break;
297 case FIELD_LANG: name="language"; break;
298 case FIELD_XKEYWORD: name="translated keyword"; break;
299 default: name="text";
302 if(which_field==FIELD_MAIN && tcc->is_im_generic_profile) {
303 de_dbg(c, "generic profile type: %s",
304 tcc->im_generic_profile_type_name?tcc->im_generic_profile_type_name:"?");
307 if(!(which_field==FIELD_MAIN && tcc->suppress_debugstr)) {
308 de_dbg(c, "%s: \"%s\"", name, ucstring_getpsz(srd->str));
310 retval = 1;
312 if(which_field==FIELD_KEYWORD) {
313 if(!de_strncmp(srd->sz, "Raw profile type ", 17)) {
314 on_im_generic_profile_keyword(c, d, tcc, srd);
318 if(which_field==FIELD_MAIN && tcc->is_im_generic_profile) {
319 de_dbg_indent(c, 1);
320 on_im_generic_profile_main(c, d, tcc, srcdbuf, pos, bytes_avail);
321 de_dbg_indent(c, -1);
322 goto done;
325 done:
326 de_destroy_stringreaderdata(c, srd);
327 return retval;
330 // Read and process the keyword, language, translated keyword, or main text
331 // field of a tEXt/zTXt/iTXt chunk.
332 // 'bytes_consumed' does not include the NUL separator/terminator.
333 // This is a wrapper that first decompresses the field if necessary.
334 static int do_text_field(deark *c, lctx *d,
335 struct text_chunk_ctx *tcc, int which_field,
336 i64 pos, i64 bytes_avail,
337 int is_nul_terminated, int is_compressed, de_encoding encoding,
338 i64 *bytes_consumed)
340 dbuf *tmpdbuf = NULL;
341 int retval = 0;
342 i64 bytes_consumed2;
344 if(!is_compressed) {
345 retval = do_unc_text_field(c, d, tcc,
346 which_field, c->infile, pos, bytes_avail,
347 is_nul_terminated, encoding, bytes_consumed);
348 goto done;
351 // Decompress to a membuf, then call do_unc_text_field() with that membuf.
352 // Note that a compressed field cannot be NUL-terminated.
353 *bytes_consumed = bytes_avail;
355 tmpdbuf = dbuf_create_membuf(c, 0, 0);
356 if(!fmtutil_decompress_deflate(c->infile, pos, bytes_avail, tmpdbuf, 0, NULL,
357 d->is_CgBI ? 0 : DE_DEFLATEFLAG_ISZLIB))
359 goto done;
362 retval = do_unc_text_field(c, d, tcc,
363 which_field, tmpdbuf, 0, tmpdbuf->len,
364 0, encoding, &bytes_consumed2);
366 done:
367 dbuf_close(tmpdbuf);
368 return retval;
371 static void handler_text(deark *c, lctx *d, struct handler_params *hp)
373 i64 pos;
374 i64 endpos;
375 i64 field_bytes_consumed;
376 int is_compressed = 0;
377 de_encoding encoding;
378 int ret;
379 struct text_chunk_ctx tcc;
381 de_zeromem(&tcc, sizeof(struct text_chunk_ctx));
383 endpos = hp->dpos+hp->dlen;
384 pos = hp->dpos;
386 // Keyword
387 ret = do_text_field(c, d, &tcc, FIELD_KEYWORD, pos, endpos-pos,
388 1, 0, DE_ENCODING_LATIN1, &field_bytes_consumed);
389 if(!ret) goto done;
390 pos += field_bytes_consumed;
391 pos += 1;
393 // Compression flag
394 if(hp->chunk4cc.id==CODE_iTXt) {
395 is_compressed = (int)de_getbyte(pos++);
396 de_dbg(c, "compression flag: %d", (int)is_compressed);
398 else if(hp->chunk4cc.id==CODE_zTXt) {
399 is_compressed = 1;
402 // Compression method
403 if(hp->chunk4cc.id==CODE_zTXt || hp->chunk4cc.id==CODE_iTXt) {
404 u8 cmpr_method;
405 cmpr_method = de_getbyte(pos++);
406 if(is_compressed && cmpr_method!=0) {
407 de_warn(c, "Unsupported text compression type: %d", (int)cmpr_method);
408 goto done;
412 if(hp->chunk4cc.id==CODE_iTXt) {
413 // Language tag
414 ret = do_text_field(c, d, &tcc, FIELD_LANG, pos, endpos-pos,
415 1, 0, DE_ENCODING_ASCII, &field_bytes_consumed);
416 if(!ret) goto done;
417 pos += field_bytes_consumed;
418 pos += 1;
420 // Translated keyword
421 ret = do_text_field(c, d, &tcc, FIELD_XKEYWORD, pos, endpos-pos,
422 1, 0, DE_ENCODING_UTF8, &field_bytes_consumed);
423 if(!ret) goto done;
424 pos += field_bytes_consumed;
425 pos += 1;
428 if(hp->chunk4cc.id==CODE_iTXt)
429 encoding = DE_ENCODING_UTF8;
430 else
431 encoding = DE_ENCODING_LATIN1;
433 do_text_field(c, d, &tcc, FIELD_MAIN, pos, endpos-pos,
434 0, is_compressed, encoding, &field_bytes_consumed);
436 done:
440 static void handler_CgBI(deark *c, lctx *d, struct handler_params *hp)
442 d->is_CgBI = 1;
445 static void handler_IHDR(deark *c, lctx *d, struct handler_params *hp)
447 i64 w, h;
448 u8 n;
449 const char *name;
451 if(hp->dlen<13) return;
452 w = de_getu32be(hp->dpos);
453 h = de_getu32be(hp->dpos+4);
454 de_dbg_dimensions(c, w, h);
456 n = de_getbyte(hp->dpos+8);
457 de_dbg(c, "depth: %d bits/sample", (int)n);
459 d->color_type = de_getbyte(hp->dpos+9);
460 switch(d->color_type) {
461 case 0: name="grayscale"; break;
462 case 2: name="truecolor"; break;
463 case 3: name="palette"; break;
464 case 4: name="grayscale+alpha"; break;
465 case 6: name="truecolor+alpha"; break;
466 default: name="?";
468 de_dbg(c, "color type: %d (%s)", (int)d->color_type, name);
470 n = de_getbyte(hp->dpos+12);
471 de_dbg(c, "interlaced: %d", (int)n);
474 static void handler_PLTE(deark *c, lctx *d, struct handler_params *hp)
476 // pal is a dummy variable, since we don't need to keep the palette.
477 // TODO: Maybe de_read_palette_rgb shouldn't require the palette to be returned.
478 u32 pal[256];
479 i64 nentries;
481 nentries = hp->dlen/3;
482 de_dbg(c, "num palette entries: %d", (int)nentries);
483 de_read_palette_rgb(c->infile, hp->dpos, nentries, 3, pal, DE_ARRAYCOUNT(pal), 0);
486 static void handler_sPLT(deark *c, lctx *d, struct handler_params *hp)
488 struct de_stringreaderdata *srd = NULL;
489 i64 pos = hp->dpos;
490 i64 nbytes_to_scan;
491 u8 depth;
492 i64 nentries;
493 i64 stride;
494 i64 i;
496 nbytes_to_scan = hp->dlen;
497 if(nbytes_to_scan>80) nbytes_to_scan=80;
498 srd = dbuf_read_string(c->infile, pos, nbytes_to_scan, 79, DE_CONVFLAG_STOP_AT_NUL,
499 DE_ENCODING_LATIN1);
500 if(!srd->found_nul) goto done;
501 de_dbg(c, "palette name: \"%s\"", ucstring_getpsz(srd->str));
502 pos += srd->bytes_consumed;
504 if(pos >= hp->dpos+hp->dlen) goto done;
505 depth = de_getbyte(pos++);
506 de_dbg(c, "depth: %d", (int)depth);
507 if(depth!=8 && depth!=16) goto done;
509 stride = (depth==8) ? 6 : 10;
510 nentries = (hp->dpos+hp->dlen-pos)/stride;
511 de_dbg(c, "number of entries: %d", (int)nentries);
513 if(c->debug_level<2) goto done;
514 for(i=0; i<nentries; i++) {
515 unsigned int cr, cg, cb, ca, cf;
516 if(depth==8) {
517 cr = (unsigned int)de_getbyte(pos);
518 cg = (unsigned int)de_getbyte(pos+1);
519 cb = (unsigned int)de_getbyte(pos+2);
520 ca = (unsigned int)de_getbyte(pos+3);
521 cf = (unsigned int)de_getu16be(pos+4);
522 de_dbg2(c, "pal[%3d] = (%3u,%3u,%3u,A=%u) F=%u",
523 (int)i, cr, cg, cb, ca, cf);
525 else {
526 cr = (unsigned int)de_getu16be(pos);
527 cg = (unsigned int)de_getu16be(pos+2);
528 cb = (unsigned int)de_getu16be(pos+4);
529 ca = (unsigned int)de_getu16be(pos+6);
530 cf = (unsigned int)de_getu16be(pos+8);
531 de_dbg2(c, "pal[%3d] = (%5u,%5u,%5u,A=%u) F=%u",
532 (int)i, cr, cg, cb, ca, cf);
534 pos += stride;
537 done:
538 de_destroy_stringreaderdata(c, srd);
541 static void handler_tRNS(deark *c, lctx *d, struct handler_params *hp)
543 i64 r, g, b;
545 if(d->color_type==0) {
546 if(hp->dlen<2) return;
547 r = de_getu16be(hp->dpos);
548 de_dbg(c, "transparent color gray shade: %d", (int)r);
550 else if(d->color_type==2) {
551 if(hp->dlen<6) return;
552 r = de_getu16be(hp->dpos);
553 g = de_getu16be(hp->dpos+2);
554 b = de_getu16be(hp->dpos+4);
555 de_dbg(c, "transparent color: (%d,%d,%d)", (int)r, (int)g, (int)b);
557 else if(d->color_type==3) {
558 i64 i;
559 u8 a;
561 de_dbg(c, "number of alpha values: %d", (int)hp->dlen);
562 if(c->debug_level<2) return;
563 for(i=0; i<hp->dlen && i<256; i++) {
564 a = de_getbyte(hp->dpos+i);
565 de_dbg2(c, "alpha[%3d] = %d", (int)i, (int)a);
570 static void handler_hIST(deark *c, lctx *d, struct handler_params *hp)
572 i64 i;
573 i64 v;
574 i64 nentries = hp->dlen/2;
576 de_dbg(c, "number of histogram values: %d", (int)nentries);
577 if(c->debug_level<2) return;
578 for(i=0; i<nentries; i++) {
579 v = de_getu16be(hp->dpos+i*2);
580 de_dbg2(c, "freq[%3d] = %d", (int)i, (int)v);
584 static void handler_bKGD(deark *c, lctx *d, struct handler_params *hp)
586 i64 r, g, b;
587 u8 idx;
589 if(d->color_type==0 || d->color_type==4) {
590 if(hp->dlen<2) return;
591 r = de_getu16be(hp->dpos);
592 de_dbg(c, "%s gray shade: %d", hp->cti->name, (int)r);
594 else if(d->color_type==2 || d->color_type==6) {
595 if(hp->dlen<6) return;
596 r = de_getu16be(hp->dpos);
597 g = de_getu16be(hp->dpos+2);
598 b = de_getu16be(hp->dpos+4);
599 de_dbg(c, "%s: (%d,%d,%d)", hp->cti->name, (int)r, (int)g, (int)b);
601 else if(d->color_type==3) {
602 if(hp->dlen<1) return;
603 idx = de_getbyte(hp->dpos);
604 de_dbg(c, "%s palette index: %d", hp->cti->name, (int)idx);
608 static void handler_gAMA(deark *c, lctx *d, struct handler_params *hp)
610 i64 n;
611 n = de_getu32be(hp->dpos);
612 de_dbg(c, "image gamma: %.5f", (double)n / 100000.0);
615 static void handler_pHYs(deark *c, lctx *d, struct handler_params *hp)
617 i64 dx, dy;
618 u8 u;
619 const char *name;
621 dx = de_getu32be(hp->dpos);
622 dy = de_getu32be(hp->dpos+4);
623 de_dbg(c, "density: %d"DE_CHAR_TIMES"%d", (int)dx, (int)dy);
624 u = de_getbyte(hp->dpos+8);
625 switch(u) {
626 case 0: name="unspecified"; break;
627 case 1: name="per meter"; break;
628 default: name="?";
630 de_dbg(c, "units: %d (%s)", (int)u, name);
633 static void handler_sBIT(deark *c, lctx *d, struct handler_params *hp)
635 const char *sbname[4];
636 i64 i;
638 sbname[0] = "red";
639 sbname[1] = "green";
640 sbname[2] = "blue";
641 sbname[3] = "alpha";
642 if(d->color_type==0 || d->color_type==4) {
643 sbname[0] = "gray";
644 sbname[1] = "alpha";
647 for(i=0; i<4 && i<hp->dlen; i++) {
648 u8 n;
649 n = de_getbyte(hp->dpos+i);
650 de_dbg(c, "significant %s bits: %d", sbname[i], (int)n);
654 static void handler_tIME(deark *c, lctx *d, struct handler_params *hp)
656 i64 yr;
657 u8 mo, da, hr, mi, se;
658 struct de_timestamp ts;
659 char timestamp_buf[64];
661 yr = de_getu16be(hp->dpos);
662 mo = de_getbyte(hp->dpos+2);
663 da = de_getbyte(hp->dpos+3);
664 hr = de_getbyte(hp->dpos+4);
665 mi = de_getbyte(hp->dpos+5);
666 se = de_getbyte(hp->dpos+6);
668 de_make_timestamp(&ts, yr, mo, da, hr, mi, se);
669 ts.tzcode = DE_TZCODE_UTC;
670 de_timestamp_to_string(&ts, timestamp_buf, sizeof(timestamp_buf), 0);
671 de_dbg(c, "mod time: %s", timestamp_buf);
674 static void handler_cHRM(deark *c, lctx *d, struct handler_params *hp)
676 i64 n[8];
677 double nd[8];
678 size_t i;
680 if(hp->dlen<32) return;
681 for(i=0; i<8; i++) {
682 n[i] = de_getu32be(hp->dpos+4*(i64)i);
683 nd[i] = ((double)n[i])/100000.0;
685 de_dbg(c, "white point: (%1.5f, %1.5f)", nd[0], nd[1]);
686 de_dbg(c, "red : (%1.5f, %1.5f)", nd[2], nd[3]);
687 de_dbg(c, "green : (%1.5f, %1.5f)", nd[4], nd[5]);
688 de_dbg(c, "blue : (%1.5f, %1.5f)", nd[6], nd[7]);
691 static void handler_sRGB(deark *c, lctx *d, struct handler_params *hp)
693 u8 intent;
694 const char *name;
696 if(hp->dlen<1) return;
697 intent = de_getbyte(hp->dpos);
698 switch(intent) {
699 case 0: name="perceptual"; break;
700 case 1: name="relative"; break;
701 case 2: name="saturation"; break;
702 case 3: name="absolute"; break;
703 default: name="?";
705 de_dbg(c, "rendering intent: %d (%s)", (int)intent, name);
708 static void handler_iccp(deark *c, lctx *d, struct handler_params *hp)
710 u8 cmpr_type;
711 i64 cmpr_len;
712 dbuf *f = NULL;
713 struct de_stringreaderdata *prof_name_srd = NULL;
714 de_finfo *fi = NULL;
715 char prof_name2[100];
716 size_t prof_name2_strlen;
717 i64 pos = hp->dpos;
719 prof_name_srd = dbuf_read_string(c->infile, pos, 80, 80,
720 DE_CONVFLAG_STOP_AT_NUL, DE_ENCODING_LATIN1);
721 if(!prof_name_srd->found_nul) goto done;
722 de_dbg(c, "profile name: \"%s\"", ucstring_getpsz_d(prof_name_srd->str));
723 pos += prof_name_srd->bytes_consumed;
725 // Our working copy, to use as part of the filename.
726 de_strlcpy(prof_name2, prof_name_srd->sz, sizeof(prof_name2));
727 if(!de_strcasecmp(prof_name2, "icc") ||
728 !de_strcasecmp(prof_name2, "icc profile"))
730 prof_name2[0] = '\0'; // Ignore generic name.
733 prof_name2_strlen = de_strlen(prof_name2);
734 if(prof_name2_strlen>=5) {
735 if(de_sz_has_ext(prof_name2, "icc")) {
736 // If the name already ends in ".icc", chop it off so that we don't end
737 // up with a double ".icc.icc" file extension.
738 prof_name2[prof_name2_strlen-4] = '\0';
742 cmpr_type = de_getbyte_p(&pos);
743 if(cmpr_type!=0) return;
745 cmpr_len = hp->dpos + hp->dlen - pos;
746 de_dbg(c, "compressed profile data at %"I64_FMT", len=%"I64_FMT, pos, cmpr_len);
747 fi = de_finfo_create(c);
748 if(c->filenames_from_file && prof_name2[0])
749 de_finfo_set_name_from_sz(c, fi, prof_name2, 0, DE_ENCODING_LATIN1);
750 f = dbuf_create_output_file(c, "icc", fi, DE_CREATEFLAG_IS_AUX);
751 fmtutil_decompress_deflate(c->infile, pos, cmpr_len, f, 0, NULL,
752 d->is_CgBI ? 0 : DE_DEFLATEFLAG_ISZLIB);
754 done:
755 dbuf_close(f);
756 de_finfo_destroy(c, fi);
757 de_destroy_stringreaderdata(c, prof_name_srd);
760 static void handler_eXIf(deark *c, lctx *d, struct handler_params *hp)
762 i64 pos = hp->dpos;
763 i64 len = hp->dlen;
765 if(len>=6 && !dbuf_memcmp(c->infile, pos, "Exif\0", 5)) {
766 // Some versions of the PNG-Exif proposal had the Exif data starting with
767 // an "Exif\0\0" identifier, and some files were created in this format.
768 // So we'll support it.
769 de_dbg(c, "[skipping JPEG app ID]");
770 pos += 6;
771 len -= 6;
773 if(len<8) return;
775 fmtutil_handle_exif(c, pos, len);
778 static void handler_caNv(deark *c, lctx *d, struct handler_params *hp)
780 i64 x0, x1;
782 if(hp->dlen<16) return;
783 x0 = de_geti32be(hp->dpos);
784 x1 = de_geti32be(hp->dpos+4);
785 de_dbg(c, "caNv dimensions: %dx%d", (int)x0, (int)x1);
786 x0 = de_geti32be(hp->dpos+8);
787 x1 = de_geti32be(hp->dpos+12);
788 de_dbg(c, "caNv position: %d,%d", (int)x0, (int)x1);
791 static void handler_orNT(deark *c, lctx *d, struct handler_params *hp)
793 u8 n;
794 if(hp->dlen!=1) return;
795 n = de_getbyte(hp->dpos);
796 de_dbg(c, "orientation: %d (%s)", (int)n, fmtutil_tiff_orientation_name((i64)n));
799 static void handler_htSP(deark *c, lctx *d, struct handler_params *hp)
801 i64 hotspot_x, hotspot_y;
802 u8 buf[16];
803 static const u8 uuid[16] = {0xb9,0xfe,0x4f,0x3d,0x8f,0x32,0x45,0x6f,
804 0xaa,0x02,0xdc,0xd7,0x9c,0xce,0x0e,0x24};
806 if(hp->dlen<24) return;
807 de_read(buf, hp->dpos, 16);
808 if(de_memcmp(buf, uuid, 16)) return;
809 hotspot_x = de_geti32be(hp->dpos+16);
810 hotspot_y = de_geti32be(hp->dpos+20);
811 de_dbg(c, "hotspot: (%d, %d)", (int)hotspot_x, (int)hotspot_y);
814 static void do_APNG_seqno(deark *c, lctx *d, i64 pos)
816 unsigned int n;
817 n = (unsigned int)de_getu32be(pos);
818 de_dbg(c, "seq. number: %u", n);
821 static void handler_acTL(deark *c, lctx *d, struct handler_params *hp)
823 unsigned int n;
824 i64 pos = hp->dpos;
826 if(hp->dlen<8) return;
827 n = (unsigned int)de_getu32be_p(&pos);
828 de_dbg(c, "num frames: %u", n);
829 n = (unsigned int)de_getu32be_p(&pos);
830 de_dbg(c, "num plays: %u%s", n, (n==0)?" (infinite)":"");
833 static const char *get_apng_disp_name(u8 t)
835 switch(t) {
836 case 0: return "none"; break;
837 case 1: return "background"; break;
838 case 2: return "previous"; break;
840 return "?";
843 static const char *get_apng_blend_name(u8 t)
845 switch(t) {
846 case 0: return "source"; break;
847 case 1: return "over"; break;
849 return "?";
852 static void handler_fcTL(deark *c, lctx *d, struct handler_params *hp)
854 i64 n1, n2;
855 i64 pos = hp->dpos;
856 u8 b;
858 if(hp->dlen<26) return;
859 do_APNG_seqno(c, d, pos);
860 pos += 4;
861 n1 = de_getu32be_p(&pos);
862 n2 = de_getu32be_p(&pos);
863 de_dbg_dimensions(c, n1, n2);
864 n1 = de_getu32be_p(&pos);
865 n2 = de_getu32be_p(&pos);
866 de_dbg(c, "offset: (%u, %u)", (unsigned int)n1, (unsigned int)n2);
867 n1 = de_getu16be_p(&pos);
868 n2 = de_getu16be_p(&pos);
869 de_dbg(c, "delay: %d/%d seconds", (int)n1, (int)n2);
870 b = de_getbyte_p(&pos);
871 de_dbg(c, "disposal type: %u (%s)", (unsigned int)b, get_apng_disp_name(b));
872 b = de_getbyte_p(&pos);
873 de_dbg(c, "blend type: %u (%s)", (unsigned int)b, get_apng_blend_name(b));
876 static void handler_fdAT(deark *c, lctx *d, struct handler_params *hp)
878 if(hp->dlen<4) return;
879 do_APNG_seqno(c, d, hp->dpos);
882 static void do_check_chunk_crc(deark *c, lctx *d, struct chunk_ctx *cctx, int suppress_idat_dbg)
884 if(d->crco) {
885 de_crcobj_reset(d->crco);
887 else {
888 d->crco = de_crcobj_create(c, DE_CRCOBJ_CRC32_IEEE);
891 // Don't include Length field, start with Type field
892 de_crcobj_addslice(d->crco, c->infile, cctx->pos + 4, cctx->hp.dlen + 4);
894 cctx->crc_calc = de_crcobj_getval(d->crco);
895 if(!suppress_idat_dbg) {
896 de_dbg(c, "crc32 (calculated): 0x%08x", (unsigned int)cctx->crc_calc);
900 static const struct chunk_type_info_struct chunk_type_info_arr[] = {
901 { CODE_CgBI, 0x00ff, NULL, handler_CgBI },
902 { CODE_IDAT, 0x00ff, NULL, NULL },
903 { CODE_IEND, 0x00ff, NULL, NULL },
904 { CODE_IHDR, 0x00ff, NULL, handler_IHDR },
905 { CODE_PLTE, 0x00ff, "palette", handler_PLTE },
906 { CODE_bKGD, 0x00ff, "background color", handler_bKGD },
907 { CODE_acTL, 0x00ff, "APNG animation control", handler_acTL },
908 { CODE_cHRM, 0x00ff, "chromaticities", handler_cHRM },
909 { CODE_caNv, 0x00ff, "virtual canvas info", handler_caNv },
910 { CODE_eXIf, 0x00ff, NULL, handler_eXIf },
911 { CODE_exIf, 0x00ff, NULL, handler_eXIf },
912 { CODE_fcTL, 0x00ff, "APNG frame control", handler_fcTL },
913 { CODE_fdAT, 0x00ff, "APNG frame data", handler_fdAT },
914 { CODE_gAMA, 0x00ff, "image gamma", handler_gAMA },
915 { CODE_hIST, 0x00ff, "histogram", handler_hIST },
916 { CODE_htSP, 0x00ff, "Deark-style hotspot", handler_htSP },
917 { CODE_iCCP, 0x00ff, "ICC profile", handler_iccp },
918 { CODE_iTXt, 0x00ff, NULL, handler_text },
919 { CODE_orNT, 0x00ff, NULL, handler_orNT },
920 { CODE_pHYs, 0x00ff, "physical pixel size", handler_pHYs },
921 { CODE_sBIT, 0x00ff, "significant bits", handler_sBIT },
922 { CODE_sPLT, 0x00ff, "suggested palette", handler_sPLT },
923 { CODE_sRGB, 0x00ff, NULL, handler_sRGB },
924 { CODE_tEXt, 0x00ff, NULL, handler_text },
925 { CODE_tIME, 0x00ff, "last-modification time", handler_tIME },
926 { CODE_tRNS, 0x00ff, "transparency info", handler_tRNS },
927 { CODE_zTXt, 0x00ff, NULL, handler_text },
929 { CODE_BACK, 0x0004, NULL, NULL },
930 { CODE_BASI, 0x0004, "parent object", NULL },
931 { CODE_CLIP, 0x0004, NULL, NULL },
932 { CODE_CLON, 0x0004, NULL, NULL },
933 { CODE_DBYK, 0x0004, NULL, NULL },
934 { CODE_DEFI, 0x0004, NULL, NULL },
935 { CODE_DHDR, 0x0004, "delta-PNG header", NULL },
936 { CODE_DISC, 0x0004, NULL, NULL },
937 { CODE_DROP, 0x0004, NULL, NULL },
938 { CODE_ENDL, 0x0004, NULL, NULL },
939 { CODE_FRAM, 0x0004, NULL, NULL },
940 { CODE_IJNG, 0x0004, NULL, NULL },
941 { CODE_IPNG, 0x0004, NULL, NULL },
942 { CODE_JDAA, 0x00ff, "JNG JPEG-encoded alpha data", NULL },
943 { CODE_JDAT, 0x00ff, "JNG image data", NULL },
944 { CODE_JHDR, 0x00ff, "JNG header", NULL },
945 { CODE_JSEP, 0x00ff, "8-bit/12-bit image separator", NULL },
946 { CODE_LOOP, 0x0004, NULL, NULL },
947 { CODE_MAGN, 0x0004, NULL, NULL },
948 { CODE_MEND, 0x0004, "end of MNG datastream", NULL },
949 { CODE_MHDR, 0x0004, "MNG header", NULL },
950 { CODE_MOVE, 0x0004, NULL, NULL },
951 { CODE_ORDR, 0x0004, NULL, NULL },
952 { CODE_PAST, 0x0004, NULL, NULL },
953 { CODE_PPLT, 0x0004, NULL, NULL },
954 { CODE_PROM, 0x0004, NULL, NULL },
955 { CODE_SAVE, 0x0004, NULL, NULL },
956 { CODE_SEEK, 0x0004, NULL, NULL },
957 { CODE_SHOW, 0x0004, NULL, NULL },
958 { CODE_TERM, 0x0004, NULL, NULL },
959 { CODE_eXPI, 0x0004, NULL, NULL },
960 { CODE_fPRI, 0x0004, NULL, NULL },
961 { CODE_nEED, 0x0004, NULL, NULL },
962 { CODE_pHYg, 0x0004, NULL, NULL }
965 static const struct chunk_type_info_struct *get_chunk_type_info(u32 id)
967 size_t i;
969 for(i=0; i<DE_ARRAYCOUNT(chunk_type_info_arr); i++) {
970 if(id == chunk_type_info_arr[i].id) {
971 return &chunk_type_info_arr[i];
974 return NULL;
977 static int do_identify_png_internal(deark *c)
979 u8 buf[8];
980 de_read(buf, 0, sizeof(buf));
981 if(!de_memcmp(buf, "\x89\x50\x4e\x47\x0d\x0a\x1a\x0a", 8)) return DE_PNGFMT_PNG;
982 if(!de_memcmp(buf, "\x8b\x4a\x4e\x47\x0d\x0a\x1a\x0a", 8)) return DE_PNGFMT_JNG;
983 if(!de_memcmp(buf, "\x8a\x4d\x4e\x47\x0d\x0a\x1a\x0a", 8)) return DE_PNGFMT_MNG;
984 return 0;
987 static void de_run_png(deark *c, de_module_params *mparams)
989 lctx *d = NULL;
990 i64 pos;
991 i32 prev_chunk_id = 0;
992 int suppress_idat_dbg = 0;
994 d = de_malloc(c, sizeof(lctx));
996 d->check_crcs = (u8)de_get_ext_option_bool(c, "png:checkcrc", 1);
997 de_dbg(c, "signature at %d", 0);
998 de_dbg_indent(c, 1);
999 d->fmt = do_identify_png_internal(c);
1000 switch(d->fmt) {
1001 case DE_PNGFMT_PNG:
1002 d->fmt_name = "PNG";
1003 if(de_getu32be(12)==CODE_CgBI) {
1004 d->is_CgBI = 1;
1006 break;
1007 case DE_PNGFMT_JNG: d->fmt_name = "JNG"; break;
1008 case DE_PNGFMT_MNG: d->fmt_name = "MNG"; break;
1009 default: d->fmt_name = "?";
1011 if(d->fmt>0) {
1012 if(d->is_CgBI) {
1013 de_declare_fmt(c, "CgBI");
1015 else {
1016 de_declare_fmt(c, d->fmt_name);
1019 de_dbg_indent(c, -1);
1021 pos = 8;
1022 while(pos < c->infile->len) {
1023 struct chunk_ctx cctx;
1024 char nbuf[80];
1026 de_zeromem(&cctx, sizeof(struct chunk_ctx));
1028 cctx.pos = pos;
1029 cctx.hp.dlen = de_getu32be(pos);
1030 if(pos + 8 + cctx.hp.dlen + 4 > c->infile->len) {
1031 de_warn(c, "Truncated file, or invalid data at %"I64_FMT, pos);
1032 break;
1034 dbuf_read_fourcc(c->infile, pos+4, &cctx.hp.chunk4cc, 4, 0x0);
1036 cctx.hp.cti = get_chunk_type_info(cctx.hp.chunk4cc.id);
1038 if(cctx.hp.chunk4cc.id==CODE_IDAT && suppress_idat_dbg) {
1041 else if(cctx.hp.chunk4cc.id==CODE_IDAT && prev_chunk_id==CODE_IDAT && c->debug_level<2) {
1042 de_dbg(c, "(more IDAT chunks follow)");
1043 suppress_idat_dbg = 1;
1045 else {
1046 if(cctx.hp.cti) {
1047 if(cctx.hp.cti->name) {
1048 de_snprintf(nbuf, sizeof(nbuf), " (%s)", cctx.hp.cti->name);
1050 else {
1051 de_strlcpy(nbuf, "", sizeof(nbuf));
1054 else {
1055 de_strlcpy(nbuf, " (?)", sizeof(nbuf));
1058 de_dbg(c, "chunk '%s'%s at %d dpos=%d dlen=%d",
1059 cctx.hp.chunk4cc.id_dbgstr, nbuf,
1060 (int)pos, (int)(pos+8), (int)cctx.hp.dlen);
1061 if(cctx.hp.chunk4cc.id!=CODE_IDAT) suppress_idat_dbg = 0;
1064 pos += 8;
1066 de_dbg_indent(c, 1);
1068 cctx.hp.dpos = pos;
1070 if(cctx.hp.cti) {
1071 if(cctx.hp.cti->handler_fn) {
1072 cctx.hp.cti->handler_fn(c, d, &cctx.hp);
1075 else {
1076 if(c->debug_level>=2) {
1077 handler_hexdump(c, d, &cctx.hp);
1080 pos += cctx.hp.dlen;
1082 if(d->check_crcs) {
1083 do_check_chunk_crc(c, d, &cctx, suppress_idat_dbg);
1086 cctx.crc_reported = (u32)de_getu32be(pos);
1087 if(!suppress_idat_dbg) {
1088 de_dbg(c, "crc32 (reported): 0x%08x", (unsigned int)cctx.crc_reported);
1090 pos += 4;
1091 if(d->check_crcs && (cctx.crc_reported != cctx.crc_calc)) {
1092 de_warn(c, "CRC-32 check failed for chunk at %"I64_FMT" "
1093 "(reported=0x%08x, calculated=0x%08x)", cctx.pos,
1094 (unsigned int)cctx.crc_reported, (unsigned int)cctx.crc_calc);
1097 de_dbg_indent(c, -1);
1099 prev_chunk_id = cctx.hp.chunk4cc.id;
1102 if(d) {
1103 de_crcobj_destroy(d->crco);
1104 de_free(c, d);
1108 static int de_identify_png(deark *c)
1110 int x;
1111 x = do_identify_png_internal(c);
1112 if(x!=0) return 100;
1113 return 0;
1116 void de_module_png(deark *c, struct deark_module_info *mi)
1118 mi->id = "png";
1119 mi->desc = "PNG image";
1120 mi->desc2 = "resources only";
1121 mi->run_fn = de_run_png;
1122 mi->identify_fn = de_identify_png;