nrg: Improved support for v2
[deark.git] / modules / png.c
blob9f605ff85db0907690ca2ddaac0ab0fbfd68f7af
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
81 int fmt;
83 u8 check_crcs;
84 u8 opt_extract_from_APNG;
86 u8 is_CgBI;
87 u8 is_APNG;
88 u8 color_type;
89 u8 extract_from_APNG_enabled;
90 u8 extracted_main_APNG_image;
91 u8 found_IDAT;
92 u8 found_fcTL;
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;
98 dbuf *APNG_prefix;
99 dbuf *curr_APNG_frame;
100 } lctx;
102 struct text_chunk_ctx {
103 int suppress_debugstr;
104 int is_xmp;
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
115 #define FIELD_LANG 2
116 #define FIELD_XKEYWORD 3
117 #define FIELD_MAIN 4
119 struct chunk_type_info_struct;
120 struct handler_params;
121 struct chunk_ctx;
123 typedef void (*chunk_handler_fn)(deark *c, lctx *d, struct handler_params *hp);
125 struct chunk_type_info_struct {
126 u32 id;
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.
129 u32 flags;
130 const char *name;
131 chunk_handler_fn handler_fn;
134 // TODO: Merge this with chunk_ctx?
135 struct handler_params {
136 i64 dpos;
137 i64 dlen;
138 const struct chunk_type_info_struct *cti;
139 struct de_fourcc chunk4cc;
142 struct chunk_ctx {
143 struct handler_params hp;
144 i64 pos;
145 u32 crc_reported;
146 u32 crc_calc;
149 static void declare_png_fmt(deark *c, lctx *d)
151 if(d->fmt==DE_PNGFMT_JNG) {
152 de_declare_fmt(c, "JNG");
153 return;
155 if(d->fmt==DE_PNGFMT_MNG) {
156 de_declare_fmt(c, "MNG");
157 return;
159 if(d->fmt!=DE_PNGFMT_PNG) return;
160 if(d->is_APNG) {
161 de_declare_fmt(c, "APNG");
162 return;
164 if(d->is_CgBI) {
165 de_declare_fmt(c, "CgBI");
166 return;
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)
180 char typestr[32];
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";
206 else {
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)
217 int k;
218 i64 pos = pos1;
219 i64 dlen;
220 int dump_to_file = 0;
221 int decode_to_membuf = 0;
222 const char *ext = NULL;
224 // Skip the first three lines
225 for(k=0; k<3; k++) {
226 int ret;
227 i64 foundpos = 0;
228 ret = dbuf_search_byte(inf, 0x0a, pos, pos1+len-pos, &foundpos);
229 if(!ret) goto done;
230 pos = foundpos+1;
232 dlen = pos1+len-pos;
234 if(tcc->im_generic_profile_type==PROFILETYPE_XMP) {
235 dump_to_file = 1;
236 ext = "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) {
243 dump_to_file = 1;
244 ext = "iptc";
246 else {
247 decode_to_membuf = 1;
250 else if(tcc->im_generic_profile_type==PROFILETYPE_ICC) {
251 dump_to_file = 1;
252 ext = "icc";
254 else {
255 if(c->extract_level>=2) {
256 dump_to_file = 1;
257 ext = "profile.bin";
261 if(dump_to_file) {
262 dbuf *outf;
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);
265 dbuf_close(outf);
268 if(decode_to_membuf) {
269 dbuf *tmpf;
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);
281 dbuf_close(tmpf);
284 done:
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)
295 const char *name;
296 int retval = 0;
297 struct de_stringreaderdata *srd = NULL;
299 *bytes_consumed = 0;
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);
306 retval = 1;
307 goto done;
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;
317 else {
318 i64 bytes_to_scan;
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")) {
329 tcc->is_xmp = 1;
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));
348 retval = 1;
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) {
357 de_dbg_indent(c, 1);
358 on_im_generic_profile_main(c, d, tcc, srcdbuf, pos, bytes_avail);
359 de_dbg_indent(c, -1);
360 goto done;
363 done:
364 de_destroy_stringreaderdata(c, srd);
365 return retval;
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,
376 i64 *bytes_consumed)
378 dbuf *tmpdbuf = NULL;
379 int retval = 0;
380 i64 bytes_consumed2;
382 if(!is_compressed) {
383 retval = do_unc_text_field(c, d, tcc,
384 which_field, c->infile, pos, bytes_avail,
385 is_nul_terminated, encoding, bytes_consumed);
386 goto done;
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))
397 goto done;
400 retval = do_unc_text_field(c, d, tcc,
401 which_field, tmpdbuf, 0, tmpdbuf->len,
402 0, encoding, &bytes_consumed2);
404 done:
405 dbuf_close(tmpdbuf);
406 return retval;
409 static void handler_text(deark *c, lctx *d, struct handler_params *hp)
411 i64 pos;
412 i64 endpos;
413 i64 field_bytes_consumed;
414 int is_compressed = 0;
415 de_encoding encoding;
416 int ret;
417 struct text_chunk_ctx tcc;
419 de_zeromem(&tcc, sizeof(struct text_chunk_ctx));
421 endpos = hp->dpos+hp->dlen;
422 pos = hp->dpos;
424 // Keyword
425 ret = do_text_field(c, d, &tcc, FIELD_KEYWORD, pos, endpos-pos,
426 1, 0, DE_ENCODING_LATIN1, &field_bytes_consumed);
427 if(!ret) goto done;
428 pos += field_bytes_consumed;
429 pos += 1;
431 // Compression flag
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) {
437 is_compressed = 1;
440 // Compression method
441 if(hp->chunk4cc.id==CODE_zTXt || hp->chunk4cc.id==CODE_iTXt) {
442 u8 cmpr_method;
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);
446 goto done;
450 if(hp->chunk4cc.id==CODE_iTXt) {
451 // Language tag
452 ret = do_text_field(c, d, &tcc, FIELD_LANG, pos, endpos-pos,
453 1, 0, DE_ENCODING_ASCII, &field_bytes_consumed);
454 if(!ret) goto done;
455 pos += field_bytes_consumed;
456 pos += 1;
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);
461 if(!ret) goto done;
462 pos += field_bytes_consumed;
463 pos += 1;
466 if(hp->chunk4cc.id==CODE_iTXt)
467 encoding = DE_ENCODING_UTF8;
468 else
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);
474 done:
478 static void handler_CgBI(deark *c, lctx *d, struct handler_params *hp)
480 if(!d->found_IDAT) {
481 d->is_CgBI = 1;
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)
489 i64 w, h;
490 u8 n;
491 const char *name;
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;
508 default: name="?";
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)
520 dbuf *outf = NULL;
521 i64 srcpos;
522 int found_IDAT = 0;
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);
545 srcpos = 8;
546 while(1) {
547 i64 ck_dlen;
548 i64 ck_len;
549 u32 ck_id;
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) {
560 found_IDAT = 1;
563 if(ck_id==CODE_acTL || ck_id==CODE_fcTL || ck_id==CODE_fdAT) {
564 copy_chunk_to_default = 0;
566 else {
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
575 // filter out.
576 switch(ck_id) {
577 case CODE_PLTE:
578 case CODE_tRNS:
579 case CODE_cHRM:
580 case CODE_gAMA:
581 case CODE_sBIT:
582 case CODE_sRGB:
583 case CODE_bKGD:
584 case CODE_pHYs:
585 copy_chunk_to_prefix = 1;
586 break;
587 case CODE_IHDR:
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;
592 break;
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);
603 srcpos += ck_len;
606 done:
607 if(d->APNG_prefix) {
608 if(d->APNG_prefix->len > MAX_APNG_PREFIX_LEN) {
609 d->extract_from_APNG_enabled = 0;
612 dbuf_close(outf);
615 static void handler_IDAT(deark *c, lctx *d, struct handler_params *hp)
617 if(!d->found_IDAT) {
618 d->found_IDAT = 1;
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.
630 u32 pal[256];
631 i64 nentries;
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;
641 i64 pos = hp->dpos;
642 i64 nbytes_to_scan;
643 u8 depth;
644 i64 nentries;
645 i64 stride;
646 i64 i;
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,
651 DE_ENCODING_LATIN1);
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;
668 if(depth==8) {
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);
677 else {
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);
686 pos += stride;
689 done:
690 de_destroy_stringreaderdata(c, srd);
693 static void handler_tRNS(deark *c, lctx *d, struct handler_params *hp)
695 i64 r, g, b;
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) {
710 i64 i;
711 u8 a;
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)
724 i64 i;
725 i64 v;
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)
738 i64 r, g, b;
739 u8 idx;
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)
762 i64 n;
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)
769 i64 dx, dy;
770 u8 u;
771 const char *name;
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);
777 switch(u) {
778 case 0: name="unspecified"; break;
779 case 1: name="per meter"; break;
780 default: name="?";
782 de_dbg(c, "units: %d (%s)", (int)u, name);
783 if(u==1) {
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];
792 i64 i;
794 sbname[0] = "red";
795 sbname[1] = "green";
796 sbname[2] = "blue";
797 sbname[3] = "alpha";
798 if(d->color_type==0 || d->color_type==4) {
799 sbname[0] = "gray";
800 sbname[1] = "alpha";
803 for(i=0; i<4 && i<hp->dlen; i++) {
804 u8 n;
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)
812 i64 yr;
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)
832 i64 n[8];
833 double nd[8];
834 size_t i;
836 if(hp->dlen<32) return;
837 for(i=0; i<8; i++) {
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)
849 u8 intent;
850 const char *name;
852 if(hp->dlen<1) return;
853 intent = de_getbyte(hp->dpos);
854 switch(intent) {
855 case 0: name="perceptual"; break;
856 case 1: name="relative"; break;
857 case 2: name="saturation"; break;
858 case 3: name="absolute"; break;
859 default: name="?";
861 de_dbg(c, "rendering intent: %d (%s)", (int)intent, name);
864 static void handler_iccp(deark *c, lctx *d, struct handler_params *hp)
866 u8 cmpr_type;
867 i64 cmpr_len;
868 dbuf *f = NULL;
869 struct de_stringreaderdata *prof_name_srd = NULL;
870 de_finfo *fi = NULL;
871 char prof_name2[100];
872 size_t prof_name2_strlen;
873 i64 pos = hp->dpos;
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);
910 done:
911 dbuf_close(f);
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)
918 i64 pos = hp->dpos;
919 i64 len = hp->dlen;
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]");
926 pos += 6;
927 len -= 6;
929 if(len<8) return;
931 fmtutil_handle_exif(c, pos, len);
934 static void handler_caNv(deark *c, lctx *d, struct handler_params *hp)
936 i64 x0, x1;
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)
949 u8 n;
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)
967 u8 buf[16];
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;
974 return 1;
977 static void do_APNG_seqno(deark *c, lctx *d, i64 pos)
979 unsigned int n;
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)
986 unsigned int n;
987 i64 pos = hp->dpos;
989 if(!d->found_IDAT) {
990 d->is_APNG = 1;
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)
1002 switch(t) {
1003 case 0: return "none"; break;
1004 case 1: return "background"; break;
1005 case 2: return "previous"; break;
1007 return "?";
1010 static const char *get_apng_blend_name(u8 t)
1012 switch(t) {
1013 case 0: return "source"; break;
1014 case 1: return "over"; break;
1016 return "?";
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)
1034 i64 n1, n2;
1035 i64 pos = hp->dpos;
1036 u8 b;
1038 d->found_fcTL = 1;
1039 finish_APNG_frame(c, d); // Finished previous frame
1040 if(hp->dlen<26) return;
1041 do_APNG_seqno(c, d, pos);
1042 pos += 4;
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)
1067 u8 m[4];
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)
1075 i64 IDAT_dlen;
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;
1085 return;
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)
1124 if(d->crco) {
1125 de_crcobj_reset(d->crco);
1127 else {
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);
1140 // flags:
1141 // 0x0001 = Valid in files with a PNG signature
1142 // 0x0002 = ... JNG
1143 // 0x0004 = ... MNG
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)
1213 size_t i;
1214 u32 flags_mask;
1216 switch(d->fmt) {
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;
1233 hp->cti = cti;
1234 return;
1238 static int do_identify_png_internal(deark *c)
1240 u8 buf[8];
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;
1245 return 0;
1248 static void de_run_png(deark *c, de_module_params *mparams)
1250 lctx *d = NULL;
1251 i64 pos;
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;
1268 pos = 8;
1269 while(pos < c->infile->len) {
1270 struct chunk_ctx cctx;
1271 char nbuf[80];
1273 de_zeromem(&cctx, sizeof(struct chunk_ctx));
1275 cctx.pos = pos;
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);
1280 break;
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;
1293 else {
1294 if(cctx.hp.cti) {
1295 if(cctx.hp.cti->name) {
1296 de_snprintf(nbuf, sizeof(nbuf), " (%s)", cctx.hp.cti->name);
1298 else {
1299 de_strlcpy(nbuf, "", sizeof(nbuf));
1302 else {
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;
1312 pos += 8;
1314 de_dbg_indent(c, 1);
1316 if(cctx.hp.cti) {
1317 if(cctx.hp.cti->handler_fn) {
1318 cctx.hp.cti->handler_fn(c, d, &cctx.hp);
1321 else {
1322 if(c->debug_level>=2) {
1323 handler_hexdump(c, d, &cctx.hp);
1326 pos += cctx.hp.dlen;
1328 if(d->check_crcs) {
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);
1336 pos += 4;
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;
1348 if(d) {
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);
1355 de_free(c, d);
1359 static int de_identify_png(deark *c)
1361 int x;
1362 x = do_identify_png_internal(c);
1363 if(x!=0) return 100;
1364 return 0;
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)
1374 mi->id = "png";
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;