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 static const u8 g_png_sig
[8] = {0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a};
77 typedef struct localctx_struct
{
78 #define DE_PNGFMT_PNG 1
79 #define DE_PNGFMT_JNG 2
80 #define DE_PNGFMT_MNG 3
84 u8 opt_extract_from_APNG
;
89 u8 extract_from_APNG_enabled
;
90 u8 extracted_main_APNG_image
;
93 struct de_crcobj
*crco
;
94 struct de_crcobj
*crco_for_write
;
96 i64 curr_fcTL_width
, curr_fcTL_height
;
97 i64 APNG_prefix_IHDR_pos
;
99 dbuf
*curr_APNG_frame
;
102 struct text_chunk_ctx
{
103 int suppress_debugstr
;
105 int is_im_generic_profile
; // ImageMagick-style generic "Raw profile type"
106 #define PROFILETYPE_8BIM 1
107 #define PROFILETYPE_IPTC 2
108 #define PROFILETYPE_XMP 3
109 #define PROFILETYPE_ICC 4
110 int im_generic_profile_type
;
111 const char *im_generic_profile_type_name
;
114 #define FIELD_KEYWORD 1
116 #define FIELD_XKEYWORD 3
119 struct chunk_type_info_struct
;
120 struct handler_params
;
123 typedef void (*chunk_handler_fn
)(deark
*c
, lctx
*d
, struct handler_params
*hp
);
125 struct chunk_type_info_struct
{
127 // The low 8 bits of flags tell which formats the chunk is valid in.
128 // Can be set to 0xff for most ancillary chunks.
131 chunk_handler_fn handler_fn
;
134 // TODO: Merge this with chunk_ctx?
135 struct handler_params
{
138 const struct chunk_type_info_struct
*cti
;
139 struct de_fourcc chunk4cc
;
143 struct handler_params hp
;
149 static void declare_png_fmt(deark
*c
, lctx
*d
)
151 if(d
->fmt
==DE_PNGFMT_JNG
) {
152 de_declare_fmt(c
, "JNG");
155 if(d
->fmt
==DE_PNGFMT_MNG
) {
156 de_declare_fmt(c
, "MNG");
159 if(d
->fmt
!=DE_PNGFMT_PNG
) return;
161 de_declare_fmt(c
, "APNG");
165 de_declare_fmt(c
, "CgBI");
168 if(!d
->found_IDAT
) return;
169 de_declare_fmt(c
, "PNG");
172 static void handler_hexdump(deark
*c
, lctx
*d
, struct handler_params
*hp
)
174 de_dbg_hexdump(c
, c
->infile
, hp
->dpos
, hp
->dlen
, 256, NULL
, 0x1);
177 static void on_im_generic_profile_keyword(deark
*c
, lctx
*d
,
178 struct text_chunk_ctx
*tcc
, struct de_stringreaderdata
*srd
)
182 tcc
->is_im_generic_profile
= 1;
183 tcc
->im_generic_profile_type
= 0;
184 tcc
->im_generic_profile_type_name
= NULL
;
185 tcc
->suppress_debugstr
= 1;
187 de_bytes_to_printable_sz((const u8
*)(srd
->sz
+17), de_strlen(srd
->sz
+17),
188 typestr
, sizeof(typestr
), 0, DE_ENCODING_ASCII
);
190 if(!de_strcmp(typestr
, "8bim")) {
191 tcc
->im_generic_profile_type
= PROFILETYPE_8BIM
;
192 tcc
->im_generic_profile_type_name
= "Photoshop";
194 else if(!de_strcmp(typestr
, "iptc")) {
195 tcc
->im_generic_profile_type
= PROFILETYPE_IPTC
;
196 tcc
->im_generic_profile_type_name
= "IPTC";
198 else if(!de_strcmp(typestr
, "xmp")) {
199 tcc
->im_generic_profile_type
= PROFILETYPE_XMP
;
200 tcc
->im_generic_profile_type_name
= "XMP";
202 else if(!de_strcmp(typestr
, "icc")) {
203 tcc
->im_generic_profile_type
= PROFILETYPE_ICC
;
204 tcc
->im_generic_profile_type_name
= "ICC";
207 if(c
->extract_level
<2) {
208 tcc
->suppress_debugstr
= 0;
213 // Generic (ImageMagick?) profile. Hex-encoded, with three header lines.
214 static void on_im_generic_profile_main(deark
*c
, lctx
*d
,
215 struct text_chunk_ctx
*tcc
, dbuf
*inf
, i64 pos1
, i64 len
)
220 int dump_to_file
= 0;
221 int decode_to_membuf
= 0;
222 const char *ext
= NULL
;
224 // Skip the first three lines
228 ret
= dbuf_search_byte(inf
, 0x0a, pos
, pos1
+len
-pos
, &foundpos
);
234 if(tcc
->im_generic_profile_type
==PROFILETYPE_XMP
) {
238 else if(tcc
->im_generic_profile_type
==PROFILETYPE_8BIM
) {
239 decode_to_membuf
= 1;
241 else if(tcc
->im_generic_profile_type
==PROFILETYPE_IPTC
) {
242 if(c
->extract_level
>=2) {
247 decode_to_membuf
= 1;
250 else if(tcc
->im_generic_profile_type
==PROFILETYPE_ICC
) {
255 if(c
->extract_level
>=2) {
263 outf
= dbuf_create_output_file(c
, ext
?ext
:"bin", NULL
, DE_CREATEFLAG_IS_AUX
);
264 de_decode_base16(c
, inf
, pos
, dlen
, outf
, 0);
268 if(decode_to_membuf
) {
271 tmpf
= dbuf_create_membuf(c
, 0, 0);
272 de_decode_base16(c
, inf
, pos
, dlen
, tmpf
, 0);
274 if(tcc
->im_generic_profile_type
==PROFILETYPE_8BIM
) {
275 fmtutil_handle_photoshop_rsrc(c
, tmpf
, 0, tmpf
->len
, 0x0);
277 else if(tcc
->im_generic_profile_type
==PROFILETYPE_IPTC
) {
278 fmtutil_handle_iptc(c
, tmpf
, 0, tmpf
->len
, 0x0);
288 // An internal function that does the main work of do_text_field().
289 // TODO: Clean up the text field processing code. It's gotten too messy.
290 static int do_unc_text_field(deark
*c
, lctx
*d
,
291 struct text_chunk_ctx
*tcc
, int which_field
,
292 dbuf
*srcdbuf
, i64 pos
, i64 bytes_avail
,
293 int is_nul_terminated
, de_encoding encoding
, i64
*bytes_consumed
)
297 struct de_stringreaderdata
*srd
= NULL
;
300 if(bytes_avail
<0) return 0;
302 if(which_field
==FIELD_MAIN
&& tcc
->is_xmp
) {
303 // The main field is never NUL terminated, so we can do this right away.
304 dbuf_create_file_from_slice(srcdbuf
, pos
, bytes_avail
, "xmp",
305 NULL
, DE_CREATEFLAG_IS_AUX
);
310 if(is_nul_terminated
) {
311 srd
= dbuf_read_string(srcdbuf
, pos
, bytes_avail
, DE_DBG_MAX_STRLEN
,
312 DE_CONVFLAG_STOP_AT_NUL
, encoding
);
314 if(!srd
->found_nul
) goto done
;
315 *bytes_consumed
= srd
->bytes_consumed
- 1;
320 *bytes_consumed
= bytes_avail
;
322 bytes_to_scan
= bytes_avail
;
323 if(bytes_to_scan
>DE_DBG_MAX_STRLEN
) bytes_to_scan
= DE_DBG_MAX_STRLEN
;
324 srd
= dbuf_read_string(srcdbuf
, pos
, bytes_to_scan
, bytes_to_scan
, 0, encoding
);
327 if(which_field
==FIELD_KEYWORD
) {
328 if(!de_strcmp(srd
->sz
, "XML:com.adobe.xmp")) {
333 switch(which_field
) {
334 case FIELD_KEYWORD
: name
="keyword"; break;
335 case FIELD_LANG
: name
="language"; break;
336 case FIELD_XKEYWORD
: name
="translated keyword"; break;
337 default: name
="text";
340 if(which_field
==FIELD_MAIN
&& tcc
->is_im_generic_profile
) {
341 de_dbg(c
, "generic profile type: %s",
342 tcc
->im_generic_profile_type_name
?tcc
->im_generic_profile_type_name
:"?");
345 if(!(which_field
==FIELD_MAIN
&& tcc
->suppress_debugstr
)) {
346 de_dbg(c
, "%s: \"%s\"", name
, ucstring_getpsz(srd
->str
));
350 if(which_field
==FIELD_KEYWORD
) {
351 if(!de_strncmp(srd
->sz
, "Raw profile type ", 17)) {
352 on_im_generic_profile_keyword(c
, d
, tcc
, srd
);
356 if(which_field
==FIELD_MAIN
&& tcc
->is_im_generic_profile
) {
358 on_im_generic_profile_main(c
, d
, tcc
, srcdbuf
, pos
, bytes_avail
);
359 de_dbg_indent(c
, -1);
364 de_destroy_stringreaderdata(c
, srd
);
368 // Read and process the keyword, language, translated keyword, or main text
369 // field of a tEXt/zTXt/iTXt chunk.
370 // 'bytes_consumed' does not include the NUL separator/terminator.
371 // This is a wrapper that first decompresses the field if necessary.
372 static int do_text_field(deark
*c
, lctx
*d
,
373 struct text_chunk_ctx
*tcc
, int which_field
,
374 i64 pos
, i64 bytes_avail
,
375 int is_nul_terminated
, int is_compressed
, de_encoding encoding
,
378 dbuf
*tmpdbuf
= NULL
;
383 retval
= do_unc_text_field(c
, d
, tcc
,
384 which_field
, c
->infile
, pos
, bytes_avail
,
385 is_nul_terminated
, encoding
, bytes_consumed
);
389 // Decompress to a membuf, then call do_unc_text_field() with that membuf.
390 // Note that a compressed field cannot be NUL-terminated.
391 *bytes_consumed
= bytes_avail
;
393 tmpdbuf
= dbuf_create_membuf(c
, 0, 0);
394 if(!fmtutil_decompress_deflate(c
->infile
, pos
, bytes_avail
, tmpdbuf
, 0, NULL
,
395 d
->is_CgBI
? 0 : DE_DEFLATEFLAG_ISZLIB
))
400 retval
= do_unc_text_field(c
, d
, tcc
,
401 which_field
, tmpdbuf
, 0, tmpdbuf
->len
,
402 0, encoding
, &bytes_consumed2
);
409 static void handler_text(deark
*c
, lctx
*d
, struct handler_params
*hp
)
413 i64 field_bytes_consumed
;
414 int is_compressed
= 0;
415 de_encoding encoding
;
417 struct text_chunk_ctx tcc
;
419 de_zeromem(&tcc
, sizeof(struct text_chunk_ctx
));
421 endpos
= hp
->dpos
+hp
->dlen
;
425 ret
= do_text_field(c
, d
, &tcc
, FIELD_KEYWORD
, pos
, endpos
-pos
,
426 1, 0, DE_ENCODING_LATIN1
, &field_bytes_consumed
);
428 pos
+= field_bytes_consumed
;
432 if(hp
->chunk4cc
.id
==CODE_iTXt
) {
433 is_compressed
= (int)de_getbyte(pos
++);
434 de_dbg(c
, "compression flag: %d", (int)is_compressed
);
436 else if(hp
->chunk4cc
.id
==CODE_zTXt
) {
440 // Compression method
441 if(hp
->chunk4cc
.id
==CODE_zTXt
|| hp
->chunk4cc
.id
==CODE_iTXt
) {
443 cmpr_method
= de_getbyte(pos
++);
444 if(is_compressed
&& cmpr_method
!=0) {
445 de_warn(c
, "Unsupported text compression type: %d", (int)cmpr_method
);
450 if(hp
->chunk4cc
.id
==CODE_iTXt
) {
452 ret
= do_text_field(c
, d
, &tcc
, FIELD_LANG
, pos
, endpos
-pos
,
453 1, 0, DE_ENCODING_ASCII
, &field_bytes_consumed
);
455 pos
+= field_bytes_consumed
;
458 // Translated keyword
459 ret
= do_text_field(c
, d
, &tcc
, FIELD_XKEYWORD
, pos
, endpos
-pos
,
460 1, 0, DE_ENCODING_UTF8
, &field_bytes_consumed
);
462 pos
+= field_bytes_consumed
;
466 if(hp
->chunk4cc
.id
==CODE_iTXt
)
467 encoding
= DE_ENCODING_UTF8
;
469 encoding
= DE_ENCODING_LATIN1
;
471 do_text_field(c
, d
, &tcc
, FIELD_MAIN
, pos
, endpos
-pos
,
472 0, is_compressed
, encoding
, &field_bytes_consumed
);
478 static void handler_CgBI(deark
*c
, lctx
*d
, struct handler_params
*hp
)
482 d
->extract_from_APNG_enabled
= 0;
483 declare_png_fmt(c
, d
);
487 static void handler_IHDR(deark
*c
, lctx
*d
, struct handler_params
*hp
)
493 if(hp
->dlen
<13) return;
494 w
= de_getu32be(hp
->dpos
);
495 h
= de_getu32be(hp
->dpos
+4);
496 de_dbg_dimensions(c
, w
, h
);
498 n
= de_getbyte(hp
->dpos
+8);
499 de_dbg(c
, "depth: %d bits/sample", (int)n
);
501 d
->color_type
= de_getbyte(hp
->dpos
+9);
502 switch(d
->color_type
) {
503 case 0: name
="grayscale"; break;
504 case 2: name
="truecolor"; break;
505 case 3: name
="palette"; break;
506 case 4: name
="grayscale+alpha"; break;
507 case 6: name
="truecolor+alpha"; break;
510 de_dbg(c
, "color type: %d (%s)", (int)d
->color_type
, name
);
512 n
= de_getbyte(hp
->dpos
+12);
513 de_dbg(c
, "interlaced: %d", (int)n
);
516 // Extract the main APNG image, and prepare data that will allow us to extract
517 // the frames we encounter later.
518 static void prepare_PNG_extraction_from_APNG(deark
*c
, lctx
*d
)
524 if(!d
->is_APNG
) return;
525 if(!d
->extract_from_APNG_enabled
) return;
526 if(d
->extracted_main_APNG_image
) return;
527 if(d
->APNG_prefix
) return;
528 d
->extracted_main_APNG_image
= 1;
530 d
->APNG_prefix
= dbuf_create_membuf(c
, 0, 0);
531 #define MAX_APNG_PREFIX_LEN 4000
532 dbuf_set_length_limit(d
->APNG_prefix
, MAX_APNG_PREFIX_LEN
+1);
534 dbuf_write(d
->APNG_prefix
, g_png_sig
, 8);
536 // This is a bit crude, but simple enough. Just read the entire file in a
537 // separate pass, copying everything that isn't a special APNG chunk.
538 de_dbg2(c
, "[extracting main image from APNG]");
540 // If an fcTL chunk has not appeared yet, then this is the fallback image
541 // that will only be seen if the viewer does not support APNG.
542 outf
= dbuf_create_output_file(c
, (d
->found_fcTL
? "png" : "default.png"), NULL
, 0);
544 dbuf_write(outf
, g_png_sig
, 8);
550 int copy_chunk_to_default
;
551 int copy_chunk_to_prefix
= 0;
553 if(srcpos
+12 > c
->infile
->len
) goto done
;
554 ck_dlen
= de_getu32be(srcpos
);
555 ck_len
= ck_dlen
+ 12;
556 ck_id
= (u32
)de_getu32be(srcpos
+4);
557 if(srcpos
+ ck_len
> c
->infile
->len
) goto done
;
559 if(ck_id
==CODE_IDAT
) {
563 if(ck_id
==CODE_acTL
|| ck_id
==CODE_fcTL
|| ck_id
==CODE_fdAT
) {
564 copy_chunk_to_default
= 0;
567 copy_chunk_to_default
= 1;
570 if(d
->extract_from_APNG_enabled
&& !found_IDAT
) {
571 // This prefix could be written to many frame files, so we want it
572 // to be small. We'll be selective about which chunks we put in it
573 // (no iCCP -- too big).
574 // Note that the main image file will still include all the chunks we
585 copy_chunk_to_prefix
= 1;
588 if(ck_dlen
==13 && d
->APNG_prefix_IHDR_pos
==0) {
589 d
->APNG_prefix_IHDR_pos
= d
->APNG_prefix
->len
;
590 copy_chunk_to_prefix
= 1;
596 if(copy_chunk_to_default
) {
597 dbuf_copy(c
->infile
, srcpos
, ck_len
, outf
);
600 if(copy_chunk_to_prefix
) {
601 dbuf_copy(c
->infile
, srcpos
, ck_len
, d
->APNG_prefix
);
608 if(d
->APNG_prefix
->len
> MAX_APNG_PREFIX_LEN
) {
609 d
->extract_from_APNG_enabled
= 0;
615 static void handler_IDAT(deark
*c
, lctx
*d
, struct handler_params
*hp
)
619 // In case format declaration was deferred, do it now.
620 declare_png_fmt(c
, d
);
622 prepare_PNG_extraction_from_APNG(c
, d
);
626 static void handler_PLTE(deark
*c
, lctx
*d
, struct handler_params
*hp
)
628 // pal is a dummy variable, since we don't need to keep the palette.
629 // TODO: Maybe de_read_palette_rgb shouldn't require the palette to be returned.
633 nentries
= hp
->dlen
/3;
634 de_dbg(c
, "num palette entries: %d", (int)nentries
);
635 de_read_palette_rgb(c
->infile
, hp
->dpos
, nentries
, 3, pal
, DE_ARRAYCOUNT(pal
), 0);
638 static void handler_sPLT(deark
*c
, lctx
*d
, struct handler_params
*hp
)
640 struct de_stringreaderdata
*srd
= NULL
;
648 nbytes_to_scan
= hp
->dlen
;
649 if(nbytes_to_scan
>80) nbytes_to_scan
=80;
650 srd
= dbuf_read_string(c
->infile
, pos
, nbytes_to_scan
, 79, DE_CONVFLAG_STOP_AT_NUL
,
652 if(!srd
->found_nul
) goto done
;
653 de_dbg(c
, "palette name: \"%s\"", ucstring_getpsz(srd
->str
));
654 pos
+= srd
->bytes_consumed
;
656 if(pos
>= hp
->dpos
+hp
->dlen
) goto done
;
657 depth
= de_getbyte(pos
++);
658 de_dbg(c
, "depth: %d", (int)depth
);
659 if(depth
!=8 && depth
!=16) goto done
;
661 stride
= (depth
==8) ? 6 : 10;
662 nentries
= (hp
->dpos
+hp
->dlen
-pos
)/stride
;
663 de_dbg(c
, "number of entries: %d", (int)nentries
);
665 if(c
->debug_level
<2) goto done
;
666 for(i
=0; i
<nentries
; i
++) {
667 unsigned int cr
, cg
, cb
, ca
, cf
;
669 cr
= (unsigned int)de_getbyte(pos
);
670 cg
= (unsigned int)de_getbyte(pos
+1);
671 cb
= (unsigned int)de_getbyte(pos
+2);
672 ca
= (unsigned int)de_getbyte(pos
+3);
673 cf
= (unsigned int)de_getu16be(pos
+4);
674 de_dbg2(c
, "pal[%3d] = (%3u,%3u,%3u,A=%u) F=%u",
675 (int)i
, cr
, cg
, cb
, ca
, cf
);
678 cr
= (unsigned int)de_getu16be(pos
);
679 cg
= (unsigned int)de_getu16be(pos
+2);
680 cb
= (unsigned int)de_getu16be(pos
+4);
681 ca
= (unsigned int)de_getu16be(pos
+6);
682 cf
= (unsigned int)de_getu16be(pos
+8);
683 de_dbg2(c
, "pal[%3d] = (%5u,%5u,%5u,A=%u) F=%u",
684 (int)i
, cr
, cg
, cb
, ca
, cf
);
690 de_destroy_stringreaderdata(c
, srd
);
693 static void handler_tRNS(deark
*c
, lctx
*d
, struct handler_params
*hp
)
697 if(d
->color_type
==0) {
698 if(hp
->dlen
<2) return;
699 r
= de_getu16be(hp
->dpos
);
700 de_dbg(c
, "transparent color gray shade: %d", (int)r
);
702 else if(d
->color_type
==2) {
703 if(hp
->dlen
<6) return;
704 r
= de_getu16be(hp
->dpos
);
705 g
= de_getu16be(hp
->dpos
+2);
706 b
= de_getu16be(hp
->dpos
+4);
707 de_dbg(c
, "transparent color: (%d,%d,%d)", (int)r
, (int)g
, (int)b
);
709 else if(d
->color_type
==3) {
713 de_dbg(c
, "number of alpha values: %d", (int)hp
->dlen
);
714 if(c
->debug_level
<2) return;
715 for(i
=0; i
<hp
->dlen
&& i
<256; i
++) {
716 a
= de_getbyte(hp
->dpos
+i
);
717 de_dbg2(c
, "alpha[%3d] = %d", (int)i
, (int)a
);
722 static void handler_hIST(deark
*c
, lctx
*d
, struct handler_params
*hp
)
726 i64 nentries
= hp
->dlen
/2;
728 de_dbg(c
, "number of histogram values: %d", (int)nentries
);
729 if(c
->debug_level
<2) return;
730 for(i
=0; i
<nentries
; i
++) {
731 v
= de_getu16be(hp
->dpos
+i
*2);
732 de_dbg2(c
, "freq[%3d] = %d", (int)i
, (int)v
);
736 static void handler_bKGD(deark
*c
, lctx
*d
, struct handler_params
*hp
)
741 if(d
->color_type
==0 || d
->color_type
==4) {
742 if(hp
->dlen
<2) return;
743 r
= de_getu16be(hp
->dpos
);
744 de_dbg(c
, "%s gray shade: %d", hp
->cti
->name
, (int)r
);
746 else if(d
->color_type
==2 || d
->color_type
==6) {
747 if(hp
->dlen
<6) return;
748 r
= de_getu16be(hp
->dpos
);
749 g
= de_getu16be(hp
->dpos
+2);
750 b
= de_getu16be(hp
->dpos
+4);
751 de_dbg(c
, "%s: (%d,%d,%d)", hp
->cti
->name
, (int)r
, (int)g
, (int)b
);
753 else if(d
->color_type
==3) {
754 if(hp
->dlen
<1) return;
755 idx
= de_getbyte(hp
->dpos
);
756 de_dbg(c
, "%s palette index: %d", hp
->cti
->name
, (int)idx
);
760 static void handler_gAMA(deark
*c
, lctx
*d
, struct handler_params
*hp
)
763 n
= de_getu32be(hp
->dpos
);
764 de_dbg(c
, "image gamma: %.5f", (double)n
/ 100000.0);
767 static void handler_pHYs(deark
*c
, lctx
*d
, struct handler_params
*hp
)
773 dx
= de_getu32be(hp
->dpos
);
774 dy
= de_getu32be(hp
->dpos
+4);
775 de_dbg(c
, "density: %d"DE_CHAR_TIMES
"%d", (int)dx
, (int)dy
);
776 u
= de_getbyte(hp
->dpos
+8);
778 case 0: name
="unspecified"; break;
779 case 1: name
="per meter"; break;
782 de_dbg(c
, "units: %d (%s)", (int)u
, name
);
784 de_dbg(c
, "approx. dpi: %.3f"DE_CHAR_TIMES
"%.3f",
785 (double)dx
*0.0254, (double)dy
*0.0254);
789 static void handler_sBIT(deark
*c
, lctx
*d
, struct handler_params
*hp
)
791 const char *sbname
[4];
798 if(d
->color_type
==0 || d
->color_type
==4) {
803 for(i
=0; i
<4 && i
<hp
->dlen
; i
++) {
805 n
= de_getbyte(hp
->dpos
+i
);
806 de_dbg(c
, "significant %s bits: %d", sbname
[i
], (int)n
);
810 static void handler_tIME(deark
*c
, lctx
*d
, struct handler_params
*hp
)
813 u8 mo
, da
, hr
, mi
, se
;
814 struct de_timestamp ts
;
815 char timestamp_buf
[64];
817 yr
= de_getu16be(hp
->dpos
);
818 mo
= de_getbyte(hp
->dpos
+2);
819 da
= de_getbyte(hp
->dpos
+3);
820 hr
= de_getbyte(hp
->dpos
+4);
821 mi
= de_getbyte(hp
->dpos
+5);
822 se
= de_getbyte(hp
->dpos
+6);
824 de_make_timestamp(&ts
, yr
, mo
, da
, hr
, mi
, se
);
825 ts
.tzcode
= DE_TZCODE_UTC
;
826 de_timestamp_to_string(&ts
, timestamp_buf
, sizeof(timestamp_buf
), 0);
827 de_dbg(c
, "mod time: %s", timestamp_buf
);
830 static void handler_cHRM(deark
*c
, lctx
*d
, struct handler_params
*hp
)
836 if(hp
->dlen
<32) return;
838 n
[i
] = de_getu32be(hp
->dpos
+4*(i64
)i
);
839 nd
[i
] = ((double)n
[i
])/100000.0;
841 de_dbg(c
, "white point: (%1.5f, %1.5f)", nd
[0], nd
[1]);
842 de_dbg(c
, "red : (%1.5f, %1.5f)", nd
[2], nd
[3]);
843 de_dbg(c
, "green : (%1.5f, %1.5f)", nd
[4], nd
[5]);
844 de_dbg(c
, "blue : (%1.5f, %1.5f)", nd
[6], nd
[7]);
847 static void handler_sRGB(deark
*c
, lctx
*d
, struct handler_params
*hp
)
852 if(hp
->dlen
<1) return;
853 intent
= de_getbyte(hp
->dpos
);
855 case 0: name
="perceptual"; break;
856 case 1: name
="relative"; break;
857 case 2: name
="saturation"; break;
858 case 3: name
="absolute"; break;
861 de_dbg(c
, "rendering intent: %d (%s)", (int)intent
, name
);
864 static void handler_iccp(deark
*c
, lctx
*d
, struct handler_params
*hp
)
869 struct de_stringreaderdata
*prof_name_srd
= NULL
;
871 char prof_name2
[100];
872 size_t prof_name2_strlen
;
875 prof_name_srd
= dbuf_read_string(c
->infile
, pos
, 80, 80,
876 DE_CONVFLAG_STOP_AT_NUL
, DE_ENCODING_LATIN1
);
877 if(!prof_name_srd
->found_nul
) goto done
;
878 de_dbg(c
, "profile name: \"%s\"", ucstring_getpsz_d(prof_name_srd
->str
));
879 pos
+= prof_name_srd
->bytes_consumed
;
881 // Our working copy, to use as part of the filename.
882 de_strlcpy(prof_name2
, prof_name_srd
->sz
, sizeof(prof_name2
));
883 if(!de_strcasecmp(prof_name2
, "icc") ||
884 !de_strcasecmp(prof_name2
, "icc profile"))
886 prof_name2
[0] = '\0'; // Ignore generic name.
889 prof_name2_strlen
= de_strlen(prof_name2
);
890 if(prof_name2_strlen
>=5) {
891 if(de_sz_has_ext(prof_name2
, "icc")) {
892 // If the name already ends in ".icc", chop it off so that we don't end
893 // up with a double ".icc.icc" file extension.
894 prof_name2
[prof_name2_strlen
-4] = '\0';
898 cmpr_type
= de_getbyte_p(&pos
);
899 if(cmpr_type
!=0) return;
901 cmpr_len
= hp
->dpos
+ hp
->dlen
- pos
;
902 de_dbg(c
, "compressed profile data at %"I64_FMT
", len=%"I64_FMT
, pos
, cmpr_len
);
903 fi
= de_finfo_create(c
);
904 if(c
->filenames_from_file
&& prof_name2
[0])
905 de_finfo_set_name_from_sz(c
, fi
, prof_name2
, 0, DE_ENCODING_LATIN1
);
906 f
= dbuf_create_output_file(c
, "icc", fi
, DE_CREATEFLAG_IS_AUX
);
907 fmtutil_decompress_deflate(c
->infile
, pos
, cmpr_len
, f
, 0, NULL
,
908 d
->is_CgBI
? 0 : DE_DEFLATEFLAG_ISZLIB
);
912 de_finfo_destroy(c
, fi
);
913 de_destroy_stringreaderdata(c
, prof_name_srd
);
916 static void handler_eXIf(deark
*c
, lctx
*d
, struct handler_params
*hp
)
921 if(len
>=6 && !dbuf_memcmp(c
->infile
, pos
, "Exif\0", 5)) {
922 // Some versions of the PNG-Exif proposal had the Exif data starting with
923 // an "Exif\0\0" identifier, and some files were created in this format.
924 // So we'll support it.
925 de_dbg(c
, "[skipping JPEG app ID]");
931 fmtutil_handle_exif(c
, pos
, len
);
934 static void handler_caNv(deark
*c
, lctx
*d
, struct handler_params
*hp
)
938 if(hp
->dlen
<16) return;
939 x0
= de_geti32be(hp
->dpos
);
940 x1
= de_geti32be(hp
->dpos
+4);
941 de_dbg(c
, "caNv dimensions: %dx%d", (int)x0
, (int)x1
);
942 x0
= de_geti32be(hp
->dpos
+8);
943 x1
= de_geti32be(hp
->dpos
+12);
944 de_dbg(c
, "caNv position: %d,%d", (int)x0
, (int)x1
);
947 static void handler_orNT(deark
*c
, lctx
*d
, struct handler_params
*hp
)
950 if(hp
->dlen
!=1) return;
951 n
= de_getbyte(hp
->dpos
);
952 de_dbg(c
, "orientation: %d (%s)", (int)n
, fmtutil_tiff_orientation_name((i64
)n
));
955 static void handler_htSP(deark
*c
, lctx
*d
, struct handler_params
*hp
)
957 i64 hotspot_x
, hotspot_y
;
959 if(hp
->dlen
<24) return;
960 hotspot_x
= de_geti32be(hp
->dpos
+16);
961 hotspot_y
= de_geti32be(hp
->dpos
+20);
962 de_dbg(c
, "hotspot: (%d, %d)", (int)hotspot_x
, (int)hotspot_y
);
965 static int chunk_is_deark_htSP(deark
*c
, struct handler_params
*hp
)
968 static const u8 uuid
[16] = {0xb9,0xfe,0x4f,0x3d,0x8f,0x32,0x45,0x6f,
969 0xaa,0x02,0xdc,0xd7,0x9c,0xce,0x0e,0x24};
971 if(hp
->dlen
<24) return 0;
972 de_read(buf
, hp
->dpos
, 16);
973 if(de_memcmp(buf
, uuid
, 16)) return 0;
977 static void do_APNG_seqno(deark
*c
, lctx
*d
, i64 pos
)
980 n
= (unsigned int)de_getu32be(pos
);
981 de_dbg(c
, "seq. number: %u", n
);
984 static void handler_acTL(deark
*c
, lctx
*d
, struct handler_params
*hp
)
991 declare_png_fmt(c
, d
);
993 if(hp
->dlen
<8) return;
994 n
= (unsigned int)de_getu32be_p(&pos
);
995 de_dbg(c
, "num frames: %u", n
);
996 n
= (unsigned int)de_getu32be_p(&pos
);
997 de_dbg(c
, "num plays: %u%s", n
, (n
==0)?" (infinite)":"");
1000 static const char *get_apng_disp_name(u8 t
)
1003 case 0: return "none"; break;
1004 case 1: return "background"; break;
1005 case 2: return "previous"; break;
1010 static const char *get_apng_blend_name(u8 t
)
1013 case 0: return "source"; break;
1014 case 1: return "over"; break;
1019 static void finish_APNG_frame(deark
*c
, lctx
*d
)
1021 static const u8 IEND_data
[12] = {0, 0, 0, 0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82};
1023 if(!d
->curr_APNG_frame
) return;
1025 dbuf_write(d
->curr_APNG_frame
, IEND_data
, 12);
1026 dbuf_close(d
->curr_APNG_frame
);
1027 d
->curr_APNG_frame
= NULL
;
1028 d
->curr_fcTL_width
= 0;
1029 d
->curr_fcTL_height
= 0;
1032 static void handler_fcTL(deark
*c
, lctx
*d
, struct handler_params
*hp
)
1039 finish_APNG_frame(c
, d
); // Finished previous frame
1040 if(hp
->dlen
<26) return;
1041 do_APNG_seqno(c
, d
, pos
);
1043 d
->curr_fcTL_width
= de_getu32be_p(&pos
);
1044 d
->curr_fcTL_height
= de_getu32be_p(&pos
);
1045 de_dbg_dimensions(c
, d
->curr_fcTL_width
, d
->curr_fcTL_height
);
1046 n1
= de_getu32be_p(&pos
);
1047 n2
= de_getu32be_p(&pos
);
1048 de_dbg(c
, "offset: (%u, %u)", (unsigned int)n1
, (unsigned int)n2
);
1049 n1
= de_getu16be_p(&pos
);
1050 n2
= de_getu16be_p(&pos
);
1051 de_dbg(c
, "delay: %d/%d seconds", (int)n1
, (int)n2
);
1052 b
= de_getbyte_p(&pos
);
1053 de_dbg(c
, "disposal type: %u (%s)", (unsigned int)b
, get_apng_disp_name(b
));
1054 b
= de_getbyte_p(&pos
);
1055 de_dbg(c
, "blend type: %u (%s)", (unsigned int)b
, get_apng_blend_name(b
));
1058 static void writelistener_for_crc_cb(dbuf
*f
, void *userdata
, const u8
*buf
, i64 buf_len
)
1060 struct de_crcobj
*crco
= (struct de_crcobj
*)userdata
;
1062 de_crcobj_addbuf(crco
, buf
, buf_len
);
1065 static void writeu32be_at(dbuf
*f
, i64 pos
, i64 n
)
1069 de_writeu32be_direct(m
, n
);
1070 dbuf_write_at(f
, pos
, m
, 4);
1073 static void handler_fdAT(deark
*c
, lctx
*d
, struct handler_params
*hp
)
1077 if(hp
->dlen
<4) return;
1078 do_APNG_seqno(c
, d
, hp
->dpos
);
1080 if(!d
->found_fcTL
) return;
1081 if(d
->curr_fcTL_width
==0 || d
->curr_fcTL_height
==0) return;
1082 if(!d
->extract_from_APNG_enabled
) return;
1083 if(!d
->APNG_prefix
|| d
->APNG_prefix_IHDR_pos
==0) {
1084 d
->extract_from_APNG_enabled
= 0;
1088 // Convert the fdAT chunk to an IDAT chunk, and write it to d->curr_APNG_frame.
1090 if(!d
->crco_for_write
) {
1091 d
->crco_for_write
= de_crcobj_create(c
, DE_CRCOBJ_CRC32_IEEE
);
1094 if(!d
->curr_APNG_frame
) {
1095 d
->curr_APNG_frame
= dbuf_create_output_file(c
, "png", NULL
, 0);
1097 // Update the width & height in the APNG_prefix membuf, in-place.
1099 writeu32be_at(d
->APNG_prefix
, d
->APNG_prefix_IHDR_pos
+8, d
->curr_fcTL_width
);
1100 writeu32be_at(d
->APNG_prefix
, d
->APNG_prefix_IHDR_pos
+8+4, d
->curr_fcTL_height
);
1102 de_crcobj_reset(d
->crco_for_write
);
1103 de_crcobj_addslice(d
->crco_for_write
, d
->APNG_prefix
, d
->APNG_prefix_IHDR_pos
+4, 4+13);
1104 writeu32be_at(d
->APNG_prefix
, d
->APNG_prefix_IHDR_pos
+8+13,
1105 (i64
)de_crcobj_getval(d
->crco_for_write
));
1107 dbuf_copy(d
->APNG_prefix
, 0, d
->APNG_prefix
->len
, d
->curr_APNG_frame
);
1110 IDAT_dlen
= hp
->dlen
-4;
1111 dbuf_writeu32be(d
->curr_APNG_frame
, IDAT_dlen
);
1113 de_crcobj_reset(d
->crco_for_write
);
1114 dbuf_set_writelistener(d
->curr_APNG_frame
, writelistener_for_crc_cb
, (void*)d
->crco_for_write
);
1116 dbuf_writeu32be(d
->curr_APNG_frame
, (i64
)CODE_IDAT
);
1117 dbuf_copy(c
->infile
, hp
->dpos
+4, IDAT_dlen
, d
->curr_APNG_frame
);
1118 dbuf_set_writelistener(d
->curr_APNG_frame
, NULL
, NULL
);
1119 dbuf_writeu32be(d
->curr_APNG_frame
, (i64
)de_crcobj_getval(d
->crco_for_write
));
1122 static void do_check_chunk_crc(deark
*c
, lctx
*d
, struct chunk_ctx
*cctx
, int suppress_idat_dbg
)
1125 de_crcobj_reset(d
->crco
);
1128 d
->crco
= de_crcobj_create(c
, DE_CRCOBJ_CRC32_IEEE
);
1131 // Don't include Length field, start with Type field
1132 de_crcobj_addslice(d
->crco
, c
->infile
, cctx
->pos
+ 4, cctx
->hp
.dlen
+ 4);
1134 cctx
->crc_calc
= de_crcobj_getval(d
->crco
);
1135 if(!suppress_idat_dbg
) {
1136 de_dbg(c
, "crc32 (calculated): 0x%08x", (unsigned int)cctx
->crc_calc
);
1141 // 0x0001 = Valid in files with a PNG signature
1144 static const struct chunk_type_info_struct chunk_type_info_arr
[] = {
1145 { CODE_CgBI
, 0x0001, NULL
, handler_CgBI
},
1146 { CODE_IDAT
, 0x00ff, NULL
, handler_IDAT
},
1147 { CODE_IEND
, 0x00ff, NULL
, NULL
},
1148 { CODE_IHDR
, 0x0005, NULL
, handler_IHDR
},
1149 { CODE_PLTE
, 0x00ff, "palette", handler_PLTE
},
1150 { CODE_bKGD
, 0x00ff, "background color", handler_bKGD
},
1151 { CODE_acTL
, 0x0001, "APNG animation control", handler_acTL
},
1152 { CODE_cHRM
, 0x00ff, "chromaticities", handler_cHRM
},
1153 { CODE_caNv
, 0x00ff, "virtual canvas info", handler_caNv
},
1154 { CODE_eXIf
, 0x00ff, NULL
, handler_eXIf
},
1155 { CODE_exIf
, 0x00ff, NULL
, handler_eXIf
},
1156 { CODE_fcTL
, 0x0001, "APNG frame control", handler_fcTL
},
1157 { CODE_fdAT
, 0x0001, "APNG frame data", handler_fdAT
},
1158 { CODE_gAMA
, 0x00ff, "image gamma", handler_gAMA
},
1159 { CODE_hIST
, 0x00ff, "histogram", handler_hIST
},
1160 { CODE_htSP
, 0x00ff, "Deark-style hotspot", handler_htSP
},
1161 { CODE_iCCP
, 0x00ff, "ICC profile", handler_iccp
},
1162 { CODE_iTXt
, 0x00ff, NULL
, handler_text
},
1163 { CODE_orNT
, 0x00ff, NULL
, handler_orNT
},
1164 { CODE_pHYs
, 0x00ff, "physical pixel size", handler_pHYs
},
1165 { CODE_sBIT
, 0x00ff, "significant bits", handler_sBIT
},
1166 { CODE_sPLT
, 0x00ff, "suggested palette", handler_sPLT
},
1167 { CODE_sRGB
, 0x00ff, NULL
, handler_sRGB
},
1168 { CODE_tEXt
, 0x00ff, NULL
, handler_text
},
1169 { CODE_tIME
, 0x00ff, "last-modification time", handler_tIME
},
1170 { CODE_tRNS
, 0x00ff, "transparency info", handler_tRNS
},
1171 { CODE_zTXt
, 0x00ff, NULL
, handler_text
},
1173 { CODE_BACK
, 0x0004, NULL
, NULL
},
1174 { CODE_BASI
, 0x0004, "parent object", NULL
},
1175 { CODE_CLIP
, 0x0004, NULL
, NULL
},
1176 { CODE_CLON
, 0x0004, NULL
, NULL
},
1177 { CODE_DBYK
, 0x0004, NULL
, NULL
},
1178 { CODE_DEFI
, 0x0004, NULL
, NULL
},
1179 { CODE_DHDR
, 0x0004, "delta-PNG header", NULL
},
1180 { CODE_DISC
, 0x0004, NULL
, NULL
},
1181 { CODE_DROP
, 0x0004, NULL
, NULL
},
1182 { CODE_ENDL
, 0x0004, NULL
, NULL
},
1183 { CODE_FRAM
, 0x0004, NULL
, NULL
},
1184 { CODE_IJNG
, 0x0004, NULL
, NULL
},
1185 { CODE_IPNG
, 0x0004, NULL
, NULL
},
1186 { CODE_JDAA
, 0x0006, "JNG JPEG-encoded alpha data", NULL
},
1187 { CODE_JDAT
, 0x0006, "JNG image data", NULL
},
1188 { CODE_JHDR
, 0x0006, "JNG header", NULL
},
1189 { CODE_JSEP
, 0x0006, "8-bit/12-bit image separator", NULL
},
1190 { CODE_LOOP
, 0x0004, NULL
, NULL
},
1191 { CODE_MAGN
, 0x0004, NULL
, NULL
},
1192 { CODE_MEND
, 0x0004, "end of MNG datastream", NULL
},
1193 { CODE_MHDR
, 0x0004, "MNG header", NULL
},
1194 { CODE_MOVE
, 0x0004, NULL
, NULL
},
1195 { CODE_ORDR
, 0x0004, NULL
, NULL
},
1196 { CODE_PAST
, 0x0004, NULL
, NULL
},
1197 { CODE_PPLT
, 0x0004, NULL
, NULL
},
1198 { CODE_PROM
, 0x0004, NULL
, NULL
},
1199 { CODE_SAVE
, 0x0004, NULL
, NULL
},
1200 { CODE_SEEK
, 0x0004, NULL
, NULL
},
1201 { CODE_SHOW
, 0x0004, NULL
, NULL
},
1202 { CODE_TERM
, 0x0004, NULL
, NULL
},
1203 { CODE_eXPI
, 0x0004, NULL
, NULL
},
1204 { CODE_fPRI
, 0x0004, NULL
, NULL
},
1205 { CODE_nEED
, 0x0004, NULL
, NULL
},
1206 { CODE_pHYg
, 0x0004, NULL
, NULL
}
1209 // If found, sets hp->cti;
1210 static void get_chunk_type_info(deark
*c
, lctx
*d
,
1211 struct handler_params
*hp
)
1217 case DE_PNGFMT_MNG
: flags_mask
= 0x4; break;
1218 case DE_PNGFMT_JNG
: flags_mask
= 0x2; break;
1219 default: flags_mask
= 0x1; break;
1222 for(i
=0; i
<DE_ARRAYCOUNT(chunk_type_info_arr
); i
++) {
1223 const struct chunk_type_info_struct
*cti
= &chunk_type_info_arr
[i
];
1225 if(cti
->id
!= hp
->chunk4cc
.id
) continue;
1226 if(!(cti
->flags
& flags_mask
)) continue;
1228 // Chunks needing further tests are hardcoded here
1229 if(cti
->id
==CODE_htSP
) {
1230 if(!chunk_is_deark_htSP(c
, hp
)) continue;
1238 static int do_identify_png_internal(deark
*c
)
1241 de_read(buf
, 0, sizeof(buf
));
1242 if(!de_memcmp(buf
, g_png_sig
, 8)) return DE_PNGFMT_PNG
;
1243 if(!de_memcmp(buf
, "\x8b\x4a\x4e\x47\x0d\x0a\x1a\x0a", 8)) return DE_PNGFMT_JNG
;
1244 if(!de_memcmp(buf
, "\x8a\x4d\x4e\x47\x0d\x0a\x1a\x0a", 8)) return DE_PNGFMT_MNG
;
1248 static void de_run_png(deark
*c
, de_module_params
*mparams
)
1252 i32 prev_chunk_id
= 0;
1253 int suppress_idat_dbg
= 0;
1255 d
= de_malloc(c
, sizeof(lctx
));
1257 d
->check_crcs
= (u8
)de_get_ext_option_bool(c
, "png:checkcrc", 1);
1258 d
->opt_extract_from_APNG
= (u8
)de_get_ext_option_bool(c
, "png:extractapng", 1);
1260 de_dbg(c
, "signature at %d", 0);
1261 d
->fmt
= do_identify_png_internal(c
);
1262 declare_png_fmt(c
, d
);
1264 if(d
->fmt
==DE_PNGFMT_PNG
&& d
->opt_extract_from_APNG
) {
1265 d
->extract_from_APNG_enabled
= 1;
1269 while(pos
< c
->infile
->len
) {
1270 struct chunk_ctx cctx
;
1273 de_zeromem(&cctx
, sizeof(struct chunk_ctx
));
1276 cctx
.hp
.dpos
= pos
+ 8;
1277 cctx
.hp
.dlen
= de_getu32be(pos
);
1278 if(cctx
.hp
.dpos
+ cctx
.hp
.dlen
+ 4 > c
->infile
->len
) {
1279 de_warn(c
, "Truncated file, or invalid data at %"I64_FMT
, pos
);
1282 dbuf_read_fourcc(c
->infile
, pos
+4, &cctx
.hp
.chunk4cc
, 4, 0x0);
1284 get_chunk_type_info(c
, d
, &cctx
.hp
);
1286 if(cctx
.hp
.chunk4cc
.id
==CODE_IDAT
&& suppress_idat_dbg
) {
1289 else if(cctx
.hp
.chunk4cc
.id
==CODE_IDAT
&& prev_chunk_id
==CODE_IDAT
&& c
->debug_level
<2) {
1290 de_dbg(c
, "(more IDAT chunks follow)");
1291 suppress_idat_dbg
= 1;
1295 if(cctx
.hp
.cti
->name
) {
1296 de_snprintf(nbuf
, sizeof(nbuf
), " (%s)", cctx
.hp
.cti
->name
);
1299 de_strlcpy(nbuf
, "", sizeof(nbuf
));
1303 de_strlcpy(nbuf
, " (?)", sizeof(nbuf
));
1306 de_dbg(c
, "chunk '%s'%s at %d dpos=%d dlen=%d",
1307 cctx
.hp
.chunk4cc
.id_dbgstr
, nbuf
,
1308 (int)pos
, (int)(pos
+8), (int)cctx
.hp
.dlen
);
1309 if(cctx
.hp
.chunk4cc
.id
!=CODE_IDAT
) suppress_idat_dbg
= 0;
1314 de_dbg_indent(c
, 1);
1317 if(cctx
.hp
.cti
->handler_fn
) {
1318 cctx
.hp
.cti
->handler_fn(c
, d
, &cctx
.hp
);
1322 if(c
->debug_level
>=2) {
1323 handler_hexdump(c
, d
, &cctx
.hp
);
1326 pos
+= cctx
.hp
.dlen
;
1329 do_check_chunk_crc(c
, d
, &cctx
, suppress_idat_dbg
);
1332 cctx
.crc_reported
= (u32
)de_getu32be(pos
);
1333 if(!suppress_idat_dbg
) {
1334 de_dbg(c
, "crc32 (reported): 0x%08x", (unsigned int)cctx
.crc_reported
);
1337 if(d
->check_crcs
&& (cctx
.crc_reported
!= cctx
.crc_calc
)) {
1338 de_warn(c
, "CRC-32 check failed for chunk at %"I64_FMT
" "
1339 "(reported=0x%08x, calculated=0x%08x)", cctx
.pos
,
1340 (unsigned int)cctx
.crc_reported
, (unsigned int)cctx
.crc_calc
);
1343 de_dbg_indent(c
, -1);
1345 prev_chunk_id
= cctx
.hp
.chunk4cc
.id
;
1349 if(d
->curr_APNG_frame
) {
1350 finish_APNG_frame(c
, d
);
1352 dbuf_close(d
->APNG_prefix
);
1353 de_crcobj_destroy(d
->crco
);
1354 de_crcobj_destroy(d
->crco_for_write
);
1359 static int de_identify_png(deark
*c
)
1362 x
= do_identify_png_internal(c
);
1363 if(x
!=0) return 100;
1367 static void de_help_png(deark
*c
)
1369 de_msg(c
, "-opt png:extractapng=0 : Do not extract APNG frames");
1372 void de_module_png(deark
*c
, struct deark_module_info
*mi
)
1375 mi
->desc
= "PNG image";
1376 mi
->desc2
= "resources only";
1377 mi
->run_fn
= de_run_png
;
1378 mi
->identify_fn
= de_identify_png
;
1379 mi
->help_fn
= de_help_png
;