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
84 struct de_crcobj
*crco
;
87 struct text_chunk_ctx
{
88 int suppress_debugstr
;
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
101 #define FIELD_XKEYWORD 3
104 struct chunk_type_info_struct
;
105 struct handler_params
;
108 typedef void (*chunk_handler_fn
)(deark
*c
, lctx
*d
, struct handler_params
*hp
);
110 struct chunk_type_info_struct
{
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.
116 chunk_handler_fn handler_fn
;
119 // TODO: Merge this with chunk_ctx?
120 struct handler_params
{
123 const struct chunk_type_info_struct
*cti
;
124 struct de_fourcc chunk4cc
;
128 struct handler_params hp
;
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
)
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";
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
)
182 int dump_to_file
= 0;
183 int decode_to_membuf
= 0;
184 const char *ext
= NULL
;
186 // Skip the first three lines
190 ret
= dbuf_search_byte(inf
, 0x0a, pos
, pos1
+len
-pos
, &foundpos
);
196 if(tcc
->im_generic_profile_type
==PROFILETYPE_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) {
209 decode_to_membuf
= 1;
212 else if(tcc
->im_generic_profile_type
==PROFILETYPE_ICC
) {
217 if(c
->extract_level
>=2) {
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);
230 if(decode_to_membuf
) {
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);
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
)
259 struct de_stringreaderdata
*srd
= NULL
;
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
);
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;
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")) {
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
));
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
) {
320 on_im_generic_profile_main(c
, d
, tcc
, srcdbuf
, pos
, bytes_avail
);
321 de_dbg_indent(c
, -1);
326 de_destroy_stringreaderdata(c
, srd
);
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
,
340 dbuf
*tmpdbuf
= NULL
;
345 retval
= do_unc_text_field(c
, d
, tcc
,
346 which_field
, c
->infile
, pos
, bytes_avail
,
347 is_nul_terminated
, encoding
, bytes_consumed
);
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
))
362 retval
= do_unc_text_field(c
, d
, tcc
,
363 which_field
, tmpdbuf
, 0, tmpdbuf
->len
,
364 0, encoding
, &bytes_consumed2
);
371 static void handler_text(deark
*c
, lctx
*d
, struct handler_params
*hp
)
375 i64 field_bytes_consumed
;
376 int is_compressed
= 0;
377 de_encoding encoding
;
379 struct text_chunk_ctx tcc
;
381 de_zeromem(&tcc
, sizeof(struct text_chunk_ctx
));
383 endpos
= hp
->dpos
+hp
->dlen
;
387 ret
= do_text_field(c
, d
, &tcc
, FIELD_KEYWORD
, pos
, endpos
-pos
,
388 1, 0, DE_ENCODING_LATIN1
, &field_bytes_consumed
);
390 pos
+= field_bytes_consumed
;
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
) {
402 // Compression method
403 if(hp
->chunk4cc
.id
==CODE_zTXt
|| hp
->chunk4cc
.id
==CODE_iTXt
) {
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
);
412 if(hp
->chunk4cc
.id
==CODE_iTXt
) {
414 ret
= do_text_field(c
, d
, &tcc
, FIELD_LANG
, pos
, endpos
-pos
,
415 1, 0, DE_ENCODING_ASCII
, &field_bytes_consumed
);
417 pos
+= field_bytes_consumed
;
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
);
424 pos
+= field_bytes_consumed
;
428 if(hp
->chunk4cc
.id
==CODE_iTXt
)
429 encoding
= DE_ENCODING_UTF8
;
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
);
440 static void handler_CgBI(deark
*c
, lctx
*d
, struct handler_params
*hp
)
445 static void handler_IHDR(deark
*c
, lctx
*d
, struct handler_params
*hp
)
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;
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.
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
;
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
,
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
;
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
);
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
);
538 de_destroy_stringreaderdata(c
, srd
);
541 static void handler_tRNS(deark
*c
, lctx
*d
, struct handler_params
*hp
)
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) {
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
)
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
)
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
)
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
)
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);
626 case 0: name
="unspecified"; break;
627 case 1: name
="per meter"; break;
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];
642 if(d
->color_type
==0 || d
->color_type
==4) {
647 for(i
=0; i
<4 && i
<hp
->dlen
; i
++) {
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
)
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
)
680 if(hp
->dlen
<32) return;
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
)
696 if(hp
->dlen
<1) return;
697 intent
= de_getbyte(hp
->dpos
);
699 case 0: name
="perceptual"; break;
700 case 1: name
="relative"; break;
701 case 2: name
="saturation"; break;
702 case 3: name
="absolute"; break;
705 de_dbg(c
, "rendering intent: %d (%s)", (int)intent
, name
);
708 static void handler_iccp(deark
*c
, lctx
*d
, struct handler_params
*hp
)
713 struct de_stringreaderdata
*prof_name_srd
= NULL
;
715 char prof_name2
[100];
716 size_t prof_name2_strlen
;
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
);
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
)
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]");
775 fmtutil_handle_exif(c
, pos
, len
);
778 static void handler_caNv(deark
*c
, lctx
*d
, struct handler_params
*hp
)
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
)
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
;
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
)
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
)
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
)
836 case 0: return "none"; break;
837 case 1: return "background"; break;
838 case 2: return "previous"; break;
843 static const char *get_apng_blend_name(u8 t
)
846 case 0: return "source"; break;
847 case 1: return "over"; break;
852 static void handler_fcTL(deark
*c
, lctx
*d
, struct handler_params
*hp
)
858 if(hp
->dlen
<26) return;
859 do_APNG_seqno(c
, d
, pos
);
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
)
885 de_crcobj_reset(d
->crco
);
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
)
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
];
977 static int do_identify_png_internal(deark
*c
)
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
;
987 static void de_run_png(deark
*c
, de_module_params
*mparams
)
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);
999 d
->fmt
= do_identify_png_internal(c
);
1002 d
->fmt_name
= "PNG";
1003 if(de_getu32be(12)==CODE_CgBI
) {
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
= "?";
1013 de_declare_fmt(c
, "CgBI");
1016 de_declare_fmt(c
, d
->fmt_name
);
1019 de_dbg_indent(c
, -1);
1022 while(pos
< c
->infile
->len
) {
1023 struct chunk_ctx cctx
;
1026 de_zeromem(&cctx
, sizeof(struct chunk_ctx
));
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
);
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;
1047 if(cctx
.hp
.cti
->name
) {
1048 de_snprintf(nbuf
, sizeof(nbuf
), " (%s)", cctx
.hp
.cti
->name
);
1051 de_strlcpy(nbuf
, "", sizeof(nbuf
));
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;
1066 de_dbg_indent(c
, 1);
1071 if(cctx
.hp
.cti
->handler_fn
) {
1072 cctx
.hp
.cti
->handler_fn(c
, d
, &cctx
.hp
);
1076 if(c
->debug_level
>=2) {
1077 handler_hexdump(c
, d
, &cctx
.hp
);
1080 pos
+= cctx
.hp
.dlen
;
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
);
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
;
1103 de_crcobj_destroy(d
->crco
);
1108 static int de_identify_png(deark
*c
)
1111 x
= do_identify_png_internal(c
);
1112 if(x
!=0) return 100;
1116 void de_module_png(deark
*c
, struct deark_module_info
*mi
)
1119 mi
->desc
= "PNG image";
1120 mi
->desc2
= "resources only";
1121 mi
->run_fn
= de_run_png
;
1122 mi
->identify_fn
= de_identify_png
;