zip: Better parsing of Info-ZIP type 1 extra field
[deark.git] / modules / pict.c
blobc05b8d795347752f340e895d4f40659344ddc435
1 // This file is part of Deark.
2 // Copyright (C) 2016 Jason Summers
3 // See the file COPYING for terms of use.
5 // Macintosh PICT graphics
7 #include <deark-config.h>
8 #include <deark-private.h>
9 #include <deark-fmtutil.h>
10 DE_DECLARE_MODULE(de_module_pict);
12 struct pict_point {
13 i64 y, x;
16 struct pict_rect {
17 i64 t, l, b, r;
20 struct detection_info {
21 int file_version;
22 int has_fileheader;
25 typedef struct localctx_struct {
26 struct detection_info dti;
27 int version; // 1 or 2: The version mode that the parser is currently using
28 int is_extended_v2;
29 int decode_qtif;
30 dbuf *iccprofile_file;
31 } lctx;
33 typedef int (*item_decoder_fn)(deark *c, lctx *d, i64 opcode, i64 data_pos,
34 i64 *bytes_used);
36 struct opcode_info {
37 u16 opcode;
38 #define SZCODE_SPECIAL 0
39 #define SZCODE_EXACT 1
40 #define SZCODE_REGION 2
41 #define SZCODE_POLYGON 3
42 u16 size_code;
43 u32 size; // Data size, not including opcode. Logic depends on size_code.
44 const char *name;
45 item_decoder_fn fn;
48 static double pict_read_fixed(dbuf *f, i64 pos)
50 i64 n;
52 // I think QuickDraw's "Fixed point" numbers are signed, but I don't know
53 // how negative numbers are handled.
54 n = dbuf_geti32be(f, pos);
55 return ((double)n)/65536.0;
58 // Read a QuickDraw Point. Caller supplies point struct.
59 static void pict_read_point(dbuf *f, i64 pos,
60 struct pict_point *point, const char *dbgname)
62 point->y = dbuf_geti16be(f, pos);
63 point->x = dbuf_geti16be(f, pos+2);
65 if(dbgname) {
66 de_dbg(f->c, "%s: (%d,%d)", dbgname, (int)point->x, (int)point->y);
70 // Read a QuickDraw Rectangle. Caller supplies rect struct.
71 static void pict_read_rect(dbuf *f, i64 pos,
72 struct pict_rect *rect, const char *dbgname)
74 rect->t = dbuf_geti16be(f, pos);
75 rect->l = dbuf_geti16be(f, pos+2);
76 rect->b = dbuf_geti16be(f, pos+4);
77 rect->r = dbuf_geti16be(f, pos+6);
79 if(dbgname) {
80 de_dbg(f->c, "%s: (%d,%d)-(%d,%d)", dbgname, (int)rect->l, (int)rect->t,
81 (int)rect->r, (int)rect->b);
85 static int handler_RGBColor(deark *c, lctx *d, i64 opcode, i64 data_pos, i64 *bytes_used)
87 unsigned int clr16[3];
88 u8 clr8[3];
89 u32 clr;
90 char csamp[16];
91 i64 pos = data_pos;
92 i64 k;
94 for(k=0; k<3; k++) {
95 clr16[k] = (unsigned int)de_getu16be_p(&pos);
96 clr8[k] = (u8)(clr16[k]>>8);
98 clr = DE_MAKE_RGB(clr8[0], clr8[1], clr8[2]);
99 de_get_colorsample_code(c, clr, csamp, sizeof(csamp));
100 de_dbg(c, "color: (0x%04x,0x%04x,0x%04x)%s", clr16[0], clr16[1], clr16[2], csamp);
101 return 1;
104 // Version
105 static int handler_11(deark *c, lctx *d, i64 opcode, i64 data_pos, i64 *bytes_used)
107 i64 ver;
109 *bytes_used = 1;
110 ver = de_getbyte(data_pos);
111 de_dbg(c, "version: %d", (int)ver);
113 if(ver==2) {
114 d->version = 2;
116 else if(ver!=1) {
117 de_err(c, "Unsupported PICT version: %d", (int)ver);
118 return 0;
120 return 1;
123 // LongText
124 static int handler_28(deark *c, lctx *d, i64 opcode, i64 data_pos, i64 *bytes_used)
126 i64 tlen;
127 de_ucstring *s = NULL;
128 struct pict_point pt;
130 pict_read_point(c->infile, data_pos, &pt, "txLoc");
131 tlen = (i64)de_getbyte(data_pos+4);
132 s = ucstring_create(c);
133 dbuf_read_to_ucstring(c->infile, data_pos+5, tlen, s, 0, DE_ENCODING_MACROMAN);
134 de_dbg(c, "text: \"%s\"", ucstring_getpsz(s));
135 *bytes_used = 5+tlen;
136 ucstring_destroy(s);
137 return 1;
140 // DVText
141 static int handler_DxText(deark *c, lctx *d, i64 opcode, i64 data_pos, i64 *bytes_used)
143 i64 tlen;
144 i64 dx;
145 de_ucstring *s = NULL;
147 dx = (i64)de_getbyte(data_pos);
148 de_dbg(c, "%s: %d", opcode==0x2a?"dv":"dh", (int)dx);
150 tlen = (i64)de_getbyte(data_pos+1);
151 *bytes_used = 2+tlen;
153 s = ucstring_create(c);
154 dbuf_read_to_ucstring(c->infile, data_pos+2, tlen, s, 0, DE_ENCODING_MACROMAN);
155 de_dbg(c, "text: \"%s\"", ucstring_getpsz(s));
157 ucstring_destroy(s);
158 return 1;
161 // DHDVText
162 static int handler_2b(deark *c, lctx *d, i64 opcode, i64 data_pos, i64 *bytes_used)
164 i64 tlen;
165 i64 dh, dv;
166 de_ucstring *s = NULL;
168 dh = (i64)de_getbyte(data_pos);
169 dv = (i64)de_getbyte(data_pos+1);
170 de_dbg(c, "dh,dv: (%d,%d)", (int)dh, (int)dv);
172 tlen = (i64)de_getbyte(data_pos+2);
173 de_dbg(c, "text size: %d", (int)tlen);
174 *bytes_used = 3+tlen;
176 s = ucstring_create(c);
177 dbuf_read_to_ucstring(c->infile, data_pos+3, tlen, s, 0, DE_ENCODING_MACROMAN);
178 de_dbg(c, "text: \"%s\"", ucstring_getpsz(s));
180 return 1;
183 // fontName
184 static int handler_2c(deark *c, lctx *d, i64 opcode, i64 data_pos, i64 *bytes_used)
186 i64 n;
187 i64 tlen;
188 i64 id;
189 de_ucstring *s = NULL;
191 n = de_getu16be(data_pos);
192 *bytes_used = 2+n;
193 id = de_getu16be(data_pos+2);
194 de_dbg(c, "old font id: %d", (int)id);
195 tlen = (i64)de_getbyte(data_pos+4);
196 s = ucstring_create(c);
197 dbuf_read_to_ucstring(c->infile, data_pos+5, tlen, s, 0, DE_ENCODING_MACROMAN);
198 de_dbg(c, "font name: \"%s\"", ucstring_getpsz(s));
199 ucstring_destroy(s);
200 return 1;
203 static int handler_Rectangle(deark *c, lctx *d, i64 opcode, i64 data_pos, i64 *bytes_used)
205 struct pict_rect rect;
207 pict_read_rect(c->infile, data_pos, &rect, "rect");
208 return 1;
211 // final few bitmap header fields (18 bytes)
212 static void read_src_dst_mode(deark *c, lctx *d, struct fmtutil_macbitmap_info *bi, i64 pos)
214 struct pict_rect tmprect;
215 i64 n;
217 de_dbg(c, "src/dst/mode part of bitmap header, at %d", (int)pos);
218 de_dbg_indent(c, 1);
220 pict_read_rect(c->infile, pos, &tmprect, "srcRect");
221 pos += 8;
222 pict_read_rect(c->infile, pos, &tmprect, "dstRect");
223 pos += 8;
225 n = de_getu16be(pos);
226 de_dbg(c, "transfer mode: %d", (int)n);
227 //pos += 2;
228 de_dbg_indent(c, -1);
231 // Pre-scan the pixel data to figure out its size.
232 // (We could instead scan and decode it at the same time, but error handling
233 // would get really messy.)
234 // Returns 0 on fatal error (if we could not even parse the data).
235 static int get_pixdata_size(deark *c, lctx *d, struct fmtutil_macbitmap_info *bi,
236 i64 pos1, i64 *pixdata_size)
238 i64 pos;
239 i64 j;
240 i64 bytecount;
241 int retval = 0;
243 pos = pos1;
244 de_dbg(c, "PixData at %d", (int)pos);
245 de_dbg_indent(c, 1);
247 if(bi->height<0 || bi->height>65535) {
248 de_err(c, "Invalid bitmap height (%d)", (int)bi->height);
249 goto done;
252 // Make sure rowbytes is sane. We use it to decide how much memory to allocate.
253 // Note: I've seen valid bitmaps with as many as 284 extra bytes per row.
254 if(bi->rowbytes > (bi->npwidth * bi->pixelsize)/8 + 1000) {
255 de_err(c, "Bad rowBytes value (%d)", (int)bi->rowbytes);
256 goto done;
259 if(bi->packing_type>=3 || (bi->packing_type==0 && bi->rowbytes>=8)) {
260 for(j=0; j<bi->height; j++) {
261 if(bi->rowbytes > 250) {
262 bytecount = de_getu16be(pos);
263 pos+=2;
265 else {
266 bytecount = (i64)de_getbyte(pos);
267 pos+=1;
269 pos += bytecount;
272 else if(bi->packing_type==1 || (bi->packing_type==0 && bi->rowbytes<8)) {
273 pos += bi->rowbytes * bi->height; // uncompressed
275 else {
276 de_err(c, "Unsupported packing type: %d", (int)bi->packing_type);
277 goto done;
280 *pixdata_size = pos - pos1;
281 de_dbg(c, "PixData size: %d", (int)*pixdata_size);
282 retval = 1;
284 done:
285 de_dbg_indent(c, -1);
286 return retval;
289 static void decode_bitmap_rgb24(deark *c, lctx *d, struct fmtutil_macbitmap_info *bi,
290 dbuf *unc_pixels, de_bitmap *img, i64 pos)
292 i64 i, j;
293 u8 cr, cg, cb;
295 for(j=0; j<bi->height; j++) {
296 for(i=0; i<bi->pdwidth; i++) {
297 cr = dbuf_getbyte(unc_pixels, j*bi->rowspan + (bi->cmpcount-3+0)*bi->pdwidth + i);
298 cg = dbuf_getbyte(unc_pixels, j*bi->rowspan + (bi->cmpcount-3+1)*bi->pdwidth + i);
299 cb = dbuf_getbyte(unc_pixels, j*bi->rowspan + (bi->cmpcount-3+2)*bi->pdwidth + i);
300 de_bitmap_setpixel_rgb(img, i, j, DE_MAKE_RGB(cr,cg,cb));
305 static void decode_bitmap_rgb16(deark *c, lctx *d, struct fmtutil_macbitmap_info *bi,
306 dbuf *unc_pixels, de_bitmap *img, i64 pos)
308 i64 i, j;
309 u8 c0, c1; //, cg, cb;
310 u32 clr;
312 for(j=0; j<bi->height; j++) {
313 for(i=0; i<bi->pdwidth; i++) {
314 c0 = dbuf_getbyte(unc_pixels, j*bi->rowspan + i*2);
315 c1 = dbuf_getbyte(unc_pixels, j*bi->rowspan + i*2+1);
316 clr = ((u32)c0 << 8)|c1;
317 clr = de_rgb555_to_888(clr);
318 de_bitmap_setpixel_rgb(img, i, j, clr);
323 static void decode_bitmap_paletted(deark *c, lctx *d, struct fmtutil_macbitmap_info *bi,
324 dbuf *unc_pixels, de_bitmap *img, i64 pos)
326 i64 i, j;
327 u8 b;
328 u32 clr;
330 for(j=0; j<bi->height; j++) {
331 for(i=0; i<bi->pdwidth; i++) {
332 b = de_get_bits_symbol(unc_pixels, bi->pixelsize, j*bi->rowspan, i);
333 clr = bi->pal[(unsigned int)b];
334 de_bitmap_setpixel_rgb(img, i, j, clr);
339 static int decode_bitmap(deark *c, lctx *d, struct fmtutil_macbitmap_info *bi, i64 pos)
341 i64 j;
342 dbuf *unc_pixels = NULL;
343 de_bitmap *img = NULL;
344 de_finfo *fi = NULL;
345 i64 bytecount;
346 i64 bitmapsize;
347 int dst_nsamples;
348 struct de_dfilter_in_params dcmpri;
349 struct de_dfilter_out_params dcmpro;
350 struct de_dfilter_results dres;
352 de_dfilter_init_objects(c, &dcmpri, &dcmpro, &dres);
354 bi->rowspan = bi->rowbytes;
355 if(bi->pixelsize==32 && bi->cmpcount==3 && bi->cmpsize==8) {
356 bi->rowspan = (bi->rowbytes/4)*3;
359 bitmapsize = bi->height * bi->rowspan;
360 unc_pixels = dbuf_create_membuf(c, bitmapsize, 1);
362 dcmpri.f = c->infile;
363 dcmpro.f = unc_pixels;
365 for(j=0; j<bi->height; j++) {
366 if(bi->packing_type==1 || bi->rowbytes<8) {
367 bytecount = bi->rowbytes;
369 else if(bi->rowbytes > 250) {
370 bytecount = de_getu16be(pos);
371 pos+=2;
373 else {
374 bytecount = (i64)de_getbyte(pos);
375 pos+=1;
378 if(bi->packing_type==1 || bi->rowbytes<8) {
379 dbuf_copy(c->infile, pos, bytecount, unc_pixels);
381 else if(bi->packing_type==3 && bi->pixelsize==16) {
382 struct de_packbits_params pbparams;
384 de_zeromem(&pbparams, sizeof(struct de_packbits_params));
385 pbparams.is_packbits16 = 1;
386 dcmpri.pos = pos;
387 dcmpri.len = bytecount;
388 fmtutil_decompress_packbits_ex(c, &dcmpri, &dcmpro, &dres, &pbparams);
390 else {
391 dcmpri.pos = pos;
392 dcmpri.len = bytecount;
393 fmtutil_decompress_packbits_ex(c, &dcmpri, &dcmpro, &dres, NULL);
396 // Make sure the data decompressed to the right number of bytes.
397 if(unc_pixels->len != (j+1)*bi->rowspan) {
398 dbuf_truncate(unc_pixels, (j+1)*bi->rowspan);
401 pos += bytecount;
404 dst_nsamples = 3;
405 if(bi->uses_pal) {
406 if(de_is_grayscale_palette(bi->pal, bi->num_pal_entries)) {
407 dst_nsamples = 1;
411 img = de_bitmap_create2(c, bi->npwidth, bi->pdwidth, bi->height, dst_nsamples);
413 fi = de_finfo_create(c);
415 if(bi->hdpi>=1.0 && bi->vdpi>=1.0) {
416 fi->density.code = DE_DENSITY_DPI;
417 fi->density.xdens = bi->hdpi;
418 fi->density.ydens = bi->vdpi;
421 if(bi->uses_pal) {
422 decode_bitmap_paletted(c, d, bi, unc_pixels, img, pos);
424 else {
425 if(bi->pixelsize==16) {
426 decode_bitmap_rgb16(c, d, bi, unc_pixels, img, pos);
428 else {
429 decode_bitmap_rgb24(c, d, bi, unc_pixels, img, pos);
433 de_bitmap_write_to_file_finfo(img, fi, 0);
435 de_bitmap_destroy(img);
436 de_finfo_destroy(c, fi);
437 dbuf_close(unc_pixels);
438 return 1;
441 static int decode_pixdata(deark *c, lctx *d, struct fmtutil_macbitmap_info *bi, i64 pos)
443 int retval = 0;
445 de_dbg_indent(c, 1);
447 if(bi->npwidth==0 || bi->height==0) {
448 de_warn(c, "Ignoring zero-size bitmap (%d"DE_CHAR_TIMES"%d)",
449 (int)bi->npwidth, (int)bi->height);
450 goto done;
452 if(!de_good_image_dimensions(c, bi->npwidth, bi->height)) goto done;
454 if(bi->pixelsize!=1 && bi->pixelsize!=2 && bi->pixelsize!=4 && bi->pixelsize!=8 &&
455 bi->pixelsize!=16 && bi->pixelsize!=24 && bi->pixelsize!=32)
457 de_err(c, "%d bits/pixel images are not supported", (int)bi->pixelsize);
458 goto done;
460 if((bi->uses_pal && bi->pixeltype!=0) || (!bi->uses_pal && bi->pixeltype!=16)) {
461 de_err(c, "Pixel type %d is not supported", (int)bi->pixeltype);
462 goto done;
464 if(bi->cmpcount!=1 && bi->cmpcount!=3 && bi->cmpcount!=4) {
465 de_err(c, "Component count %d is not supported", (int)bi->cmpcount);
466 goto done;
468 if(bi->cmpsize!=1 && bi->cmpsize!=2 && bi->cmpsize!=4 && bi->cmpsize!=5 &&
469 bi->cmpsize!=8)
471 de_err(c, "%d-bit components are not supported", (int)bi->cmpsize);
472 goto done;
474 if(bi->packing_type!=0 && bi->packing_type!=1 && bi->packing_type!=3 && bi->packing_type!=4) {
475 de_err(c, "Packing type %d is not supported", (int)bi->packing_type);
476 goto done;
478 if((bi->uses_pal &&
479 (bi->packing_type==0 || bi->packing_type==1) &&
480 (bi->pixelsize==1 || bi->pixelsize==2 || bi->pixelsize==4 || bi->pixelsize==8) &&
481 bi->cmpcount==1 && bi->cmpsize==bi->pixelsize) ||
482 (!bi->uses_pal && bi->packing_type==3 && bi->pixelsize==16 && bi->cmpcount==3 && bi->cmpsize==5) ||
483 (!bi->uses_pal && bi->packing_type==4 && bi->pixelsize==32 && bi->cmpcount==3 && bi->cmpsize==8) ||
484 (!bi->uses_pal && bi->packing_type==4 && bi->pixelsize==32 && bi->cmpcount==4 && bi->cmpsize==8))
488 else {
489 de_err(c, "This type of image is not supported");
490 goto done;
493 if(bi->cmpcount==4) {
494 de_warn(c, "This image might have transparency, which is not supported.");
497 decode_bitmap(c, d, bi, pos);
499 done:
500 de_dbg_indent(c, -1);
501 return retval;
504 // For opcodes 0x90, 0x91, 0x98, 0x99, 0x9a, 0x9b
505 static int handler_98_9a(deark *c, lctx *d, i64 opcode, i64 pos1, i64 *bytes_used)
507 struct fmtutil_macbitmap_info *bi = NULL;
508 i64 pixdata_size = 0;
509 i64 colortable_size = 0;
510 int retval = 0;
511 i64 pos;
513 bi = de_malloc(c, sizeof(struct fmtutil_macbitmap_info));
514 pos = pos1;
516 if(opcode==0x9a || opcode==0x9b) {
517 fmtutil_macbitmap_read_baseaddr(c, c->infile, bi, pos);
518 pos += 4;
521 fmtutil_macbitmap_read_rowbytes_and_bounds(c, c->infile, bi, pos);
522 pos += 10;
524 if(bi->pixmap_flag) {
525 fmtutil_macbitmap_read_pixmap_only_fields(c, c->infile, bi, pos);
526 pos += 36;
529 if((opcode==0x90 || opcode==0x91 || opcode==0x98 || opcode==0x99) && bi->pixmap_flag) {
530 // Prepare to read the palette
531 bi->uses_pal = 1;
532 bi->has_colortable = 1;
534 else if((opcode==0x90 || opcode==0x91 || opcode==0x98 || opcode==0x99) && !bi->pixmap_flag) {
535 // Settings implied by the lack of a PixMap header
536 bi->pixelsize = 1;
537 bi->cmpcount = 1;
538 bi->cmpsize = 1;
539 bi->uses_pal = 1;
540 bi->num_pal_entries = 2;
541 bi->pal[0] = DE_STOCKCOLOR_WHITE;
542 bi->pal[1] = DE_STOCKCOLOR_BLACK;
544 else if((opcode==0x9a || opcode==0x9b) && !bi->pixmap_flag) {
545 de_err(c, "DirectBitsRect image without PixMap flag is not supported");
546 goto done;
549 if(bi->has_colortable) {
550 if(!fmtutil_macbitmap_read_colortable(c, c->infile, bi, pos, &colortable_size)) goto done;
551 pos += colortable_size;
554 read_src_dst_mode(c, d, bi, pos);
555 pos += 18;
557 if(opcode==0x91 || opcode==0x99 || opcode==0x9b) {
558 i64 rgnsize;
560 de_dbg(c, "region at %"I64_FMT, pos);
561 de_dbg_indent(c, 1);
562 rgnsize = de_getu16be(pos);
563 de_dbg(c, "region size: %d", (int)rgnsize);
564 de_dbg_indent(c, -1);
565 if(rgnsize<2) goto done;
566 pos += rgnsize;
567 if(!c->padpix) {
568 de_info(c, "Note: Ignoring clipping region. Output image might have "
569 "extraneous pixels.");
573 if(!get_pixdata_size(c, d, bi, pos, &pixdata_size)) {
574 goto done;
576 decode_pixdata(c, d, bi, pos);
577 pos += pixdata_size;
579 *bytes_used = pos - pos1;
581 retval = 1;
583 done:
584 de_free(c, bi);
585 return retval;
588 static int handler_pixpat(deark *c, lctx *d, i64 opcode, i64 pos1, i64 *bytes_used)
590 unsigned int pattype;
591 i64 pos = pos1;
592 int needmsg = 1;
593 int retval = 0;
594 i64 colortable_size = 0;
595 i64 pixdata_size = 0;
596 struct fmtutil_macbitmap_info *bi = NULL;
598 pattype = (unsigned int)de_getu16be_p(&pos);
599 de_dbg(c, "PatType: %u", pattype);
600 pos += 8; // Pat1Data
602 if(pattype==2) { // ditherPat(?)
603 pos += 6; // RGB
604 retval = 1;
605 goto done;
608 bi = de_malloc(c, sizeof(struct fmtutil_macbitmap_info));
610 fmtutil_macbitmap_read_rowbytes_and_bounds(c, c->infile, bi, pos);
611 pos += 10;
612 fmtutil_macbitmap_read_pixmap_only_fields(c, c->infile, bi, pos);
613 pos += 36;
615 bi->uses_pal = 1;
616 if(!fmtutil_macbitmap_read_colortable(c, c->infile, bi, pos, &colortable_size)) goto done;
617 pos += colortable_size;
619 if(!get_pixdata_size(c, d, bi, pos, &pixdata_size)) {
620 goto done;
622 // Note: We could extract the "PixMap" pattern easily enough here, by calling
623 // decode_pixdata(). But if we do that, maybe we should also extract the
624 // Pat1Data data above, as well as other opcodes like BkPat.
625 pos += pixdata_size;
626 retval = 1;
628 done:
629 if(!retval && needmsg) {
630 de_err(c, "Failed to parse PixPat data");
632 if(retval) {
633 *bytes_used = pos - pos1;
635 de_free(c, bi);
636 return retval;
639 static void do_iccprofile_item(deark *c, lctx *d, i64 pos, i64 len)
641 i64 selector;
642 i64 data_len;
644 if(len<4) return;
645 selector = de_getu32be(pos);
646 data_len = len-4;
647 de_dbg(c, "ICC profile segment, selector=%d, data len=%d", (int)selector,
648 (int)data_len);
650 if(selector!=1) {
651 // If this is not a Continuation segment, close any current file.
652 dbuf_close(d->iccprofile_file);
653 d->iccprofile_file = NULL;
656 if(selector==0) { // Beginning segment
657 d->iccprofile_file = dbuf_create_output_file(c, "icc", NULL, DE_CREATEFLAG_IS_AUX);
660 if(selector==0 || selector==1) {
661 // Beginning and Continuation segments normally have profile data.
662 // End segments (selector==2) are not allowed to include data.
664 if(!d->iccprofile_file) {
665 de_warn(c, "Bad ICC profile segment");
666 return;
668 dbuf_copy(c->infile, pos+4, data_len, d->iccprofile_file);
672 // ShortComment
673 static int handler_a0(deark *c, lctx *d, i64 opcode, i64 data_pos, i64 *bytes_used)
675 i64 kind;
676 kind = de_getu16be(data_pos);
677 de_dbg(c, "comment kind: %d", (int)kind);
678 return 1;
681 // LongComment
682 static int handler_a1(deark *c, lctx *d, i64 opcode, i64 data_pos, i64 *bytes_used)
684 i64 kind;
685 i64 len;
687 kind = de_getu16be(data_pos);
688 len = de_getu16be(data_pos+2);
689 de_dbg(c, "comment kind: %d, size: %d", (int)kind, (int)len);
690 *bytes_used = 4+len;
692 if(kind==100 && len>=4) {
693 struct de_fourcc sig4cc;
695 dbuf_read_fourcc(c->infile, data_pos+4, &sig4cc, 4, 0x0);
696 de_dbg(c, "application comment, signature=0x%08x '%s'",
697 (unsigned int)sig4cc.id, sig4cc.id_dbgstr);
698 de_dbg_hexdump(c, c->infile, data_pos+8, len-4, 256, NULL, 0x1);
700 else if(kind==224) {
701 do_iccprofile_item(c, d, data_pos+4, len);
703 else {
704 de_dbg_hexdump(c, c->infile, data_pos+4, len, 256, NULL, 0x1);
707 return 1;
710 // HeaderOp
711 static int handler_0c00(deark *c, lctx *d, i64 opcode, i64 data_pos, i64 *bytes_used)
713 i64 hdrver;
714 double hres, vres;
715 struct pict_rect srcrect;
717 hdrver = de_getu16be(data_pos);
718 d->is_extended_v2 = (hdrver==0xfffe);
720 de_dbg(c, "extended v2: %s", d->is_extended_v2?"yes":"no");
721 if(d->is_extended_v2) {
722 hres = pict_read_fixed(c->infile, data_pos+4);
723 vres = pict_read_fixed(c->infile, data_pos+8);
724 de_dbg(c, "dpi: %.2f"DE_CHAR_TIMES"%.2f", hres, vres);
725 pict_read_rect(c->infile, data_pos+12, &srcrect, "srcRect");
728 return 1;
731 static void do_handle_qtif_idsc(deark *c, lctx *d, i64 pos, i64 len)
733 i64 idsc_dpos, idsc_dlen;
734 i64 idat_dpos, idat_dlen;
735 dbuf *outf = NULL;
736 struct de_fourcc cmpr4cc;
738 if(d->decode_qtif) {
739 de_run_module_by_id_on_slice2(c, "qtif", "I", c->infile, pos, len);
740 return;
743 // Try to construct a .qtif file.
744 // This way, we do something potentially useful even if the image has
745 // a compression scheme that our qtif module doesn't support.
747 idsc_dpos = pos;
748 idsc_dlen = de_getu32be(idsc_dpos);
749 de_dbg(c, "idsc: pos=%"I64_FMT", len=%"I64_FMT, idsc_dpos, idsc_dlen);
750 if(idsc_dpos+idsc_dlen > pos+len) goto done;
752 dbuf_read_fourcc(c->infile, idsc_dpos+4, &cmpr4cc, 4, 0x0);
753 de_dbg(c, "compression type: \"%s\"", cmpr4cc.id_dbgstr);
755 idat_dpos = idsc_dpos + idsc_dlen;
756 idat_dlen = de_getu32be(idsc_dpos+44);
757 de_dbg(c, "idat: pos=%"I64_FMT", len=%"I64_FMT, idat_dpos, idat_dlen);
758 if(idat_dlen==0) {
759 idat_dlen = pos+len-idat_dpos; // ??
761 if(idat_dpos+idat_dlen > pos+len) goto done;
763 #define CODE_idat 0x69646174U
764 #define CODE_idsc 0x69647363U
765 outf = dbuf_create_output_file(c, "qtif", NULL, 0);
767 dbuf_writeu32be(outf, 8+idsc_dlen);
768 dbuf_writeu32be(outf, CODE_idsc);
769 dbuf_copy(c->infile, idsc_dpos, idsc_dlen, outf);
771 dbuf_writeu32be(outf, 8+idat_dlen);
772 dbuf_writeu32be(outf, CODE_idat);
773 dbuf_copy(c->infile, idat_dpos, idat_dlen, outf);
775 done:
776 dbuf_close(outf);
779 // CompressedQuickTime (0x8200) & UncompressedQuickTime (0x8201)
780 static int handler_QuickTime(deark *c, lctx *d, i64 opcode, i64 data_pos, i64 *bytes_used)
782 i64 payload_pos;
783 i64 payload_len;
784 i64 endpos;
785 i64 idsc_pos;
787 payload_len = de_getu32be(data_pos);
788 payload_pos = data_pos+4;
789 de_dbg(c, "payload: pos=%"I64_FMT", len=%"I64_FMT, payload_pos, payload_len);
790 endpos = payload_pos+payload_len;
791 if(endpos > c->infile->len) return 0;
792 *bytes_used = 4+payload_len;
794 // Following the size field seems to be 68 or 50 bytes of data,
795 // followed by QuickTime "idsc" data, followed by image data.
796 idsc_pos = payload_pos + ((opcode==0x8201) ? 50 : 68);
798 do_handle_qtif_idsc(c, d, idsc_pos, endpos-idsc_pos);
799 return 1;
802 static const struct opcode_info opcode_info_arr[] = {
803 // TODO: This list might not be complete, and it needs to be complete in
804 // order to parse all PICT files.
805 // Note that some opcode ranges are handled in do_handle_item().
806 { 0x0000, SZCODE_EXACT, 0, "NOP", NULL },
807 { 0x0001, SZCODE_REGION, 0, "Clip", NULL },
808 { 0x0002, SZCODE_EXACT, 8, "BkPat", NULL },
809 { 0x0003, SZCODE_EXACT, 2, "TxFont", NULL },
810 { 0x0004, SZCODE_EXACT, 1, "TxFace", NULL },
811 { 0x0005, SZCODE_EXACT, 2, "TxMode", NULL },
812 { 0x0006, SZCODE_EXACT, 4, "SpExtra", NULL },
813 { 0x0007, SZCODE_EXACT, 4, "PnSize", NULL },
814 { 0x0008, SZCODE_EXACT, 2, "PnMode", NULL },
815 { 0x0009, SZCODE_EXACT, 8, "PnPat", NULL },
816 { 0x000a, SZCODE_EXACT, 8, "FillPat", NULL },
817 { 0x000b, SZCODE_EXACT, 4, "OvSize", NULL },
818 { 0x000c, SZCODE_EXACT, 4, "Origin", NULL },
819 { 0x000d, SZCODE_EXACT, 2, "TxSize", NULL },
820 { 0x000e, SZCODE_EXACT, 4, "FgColor", NULL },
821 { 0x000f, SZCODE_EXACT, 4, "BkColor", NULL },
822 { 0x0010, SZCODE_EXACT, 8, "TxRatio", NULL },
823 { 0x0011, SZCODE_EXACT, 1, "Version", handler_11 },
824 { 0x0012, SZCODE_SPECIAL, 0, "BkPixPat", handler_pixpat },
825 { 0x0013, SZCODE_SPECIAL, 0, "PnPixPat", handler_pixpat },
826 { 0x0014, SZCODE_SPECIAL, 0, "FillPixPat", handler_pixpat },
827 { 0x0015, SZCODE_EXACT, 2, "PnLocHFrac", NULL },
828 { 0x0016, SZCODE_EXACT, 2, "ChExtra", NULL },
829 { 0x001a, SZCODE_EXACT, 6, "RGBFgCol", handler_RGBColor },
830 { 0x001b, SZCODE_EXACT, 6, "RGBBkCol", handler_RGBColor },
831 { 0x001c, SZCODE_EXACT, 0, "HiliteMode", NULL },
832 { 0x001d, SZCODE_EXACT, 6, "HiliteColor", handler_RGBColor },
833 { 0x001e, SZCODE_EXACT, 0, "DefHilite", NULL },
834 { 0x001f, SZCODE_EXACT, 6, "OpColor", handler_RGBColor },
835 { 0x0020, SZCODE_EXACT, 8, "Line", NULL },
836 { 0x0021, SZCODE_EXACT, 4, "LineFrom", NULL },
837 { 0x0022, SZCODE_EXACT, 6, "ShortLine", NULL },
838 { 0x0023, SZCODE_EXACT, 2, "ShortLineFrom", NULL },
839 { 0x0028, SZCODE_SPECIAL, 0, "LongText", handler_28 },
840 { 0x0029, SZCODE_SPECIAL, 0, "DHText", handler_DxText },
841 { 0x002a, SZCODE_SPECIAL, 0, "DVText", handler_DxText },
842 { 0x002b, SZCODE_SPECIAL, 0, "DHDVText", handler_2b },
843 { 0x002c, SZCODE_SPECIAL, 0, "fontName", handler_2c },
844 { 0x002d, SZCODE_SPECIAL, 0, "lineJustify", NULL },
845 { 0x002e, SZCODE_SPECIAL, 0, "glyphState", NULL },
846 { 0x0030, SZCODE_EXACT, 8, "frameRect", handler_Rectangle },
847 { 0x0031, SZCODE_EXACT, 8, "paintRect", handler_Rectangle },
848 { 0x0032, SZCODE_EXACT, 8, "eraseRect", handler_Rectangle },
849 { 0x0033, SZCODE_EXACT, 8, "invertRect", handler_Rectangle },
850 { 0x0034, SZCODE_EXACT, 8, "fillRect", handler_Rectangle },
851 { 0x0038, SZCODE_EXACT, 0, "frameSameRect", NULL },
852 { 0x0039, SZCODE_EXACT, 0, "paintSameRect", NULL },
853 { 0x003a, SZCODE_EXACT, 0, "eraseSameRect", NULL },
854 { 0x003b, SZCODE_EXACT, 0, "invertSameRect", NULL },
855 { 0x003c, SZCODE_EXACT, 0, "fillSameRect", NULL },
856 { 0x0040, SZCODE_EXACT, 8, "frameRRect", handler_Rectangle },
857 { 0x0041, SZCODE_EXACT, 8, "paintRRect", handler_Rectangle },
858 { 0x0042, SZCODE_EXACT, 8, "eraseRRect", handler_Rectangle },
859 { 0x0043, SZCODE_EXACT, 8, "invertRRect", handler_Rectangle },
860 { 0x0044, SZCODE_EXACT, 8, "fillRRect", handler_Rectangle },
861 { 0x0048, SZCODE_EXACT, 0, "frameSameRRect", NULL },
862 { 0x0049, SZCODE_EXACT, 0, "paintSameRRect", NULL },
863 { 0x004a, SZCODE_EXACT, 0, "eraseSameRRect", NULL },
864 { 0x004b, SZCODE_EXACT, 0, "invertSameRRect", NULL },
865 { 0x004c, SZCODE_EXACT, 0, "fillSameRRect", NULL },
866 { 0x0050, SZCODE_EXACT, 8, "frameOval", handler_Rectangle },
867 { 0x0051, SZCODE_EXACT, 8, "paintOval", handler_Rectangle },
868 { 0x0052, SZCODE_EXACT, 8, "eraseOval", handler_Rectangle },
869 { 0x0053, SZCODE_EXACT, 8, "invertOval", handler_Rectangle },
870 { 0x0054, SZCODE_EXACT, 8, "fillOval", handler_Rectangle },
871 { 0x0058, SZCODE_EXACT, 0, "frameSameOval", NULL },
872 { 0x0059, SZCODE_EXACT, 0, "paintSameOval", NULL },
873 { 0x005a, SZCODE_EXACT, 0, "eraseSameOval", NULL },
874 { 0x005b, SZCODE_EXACT, 0, "invertSameOval", NULL },
875 { 0x005c, SZCODE_EXACT, 0, "fillSameOval", NULL },
876 { 0x0060, SZCODE_EXACT, 12, "frameArc", NULL },
877 { 0x0061, SZCODE_EXACT, 12, "paintArc", NULL },
878 { 0x0062, SZCODE_EXACT, 12, "eraseArc", NULL },
879 { 0x0063, SZCODE_EXACT, 12, "invertArc", NULL },
880 { 0x0064, SZCODE_EXACT, 12, "fillArc", NULL },
881 { 0x0068, SZCODE_EXACT, 4, "frameSameArc", NULL },
882 { 0x0069, SZCODE_EXACT, 4, "paintSameArc", NULL },
883 { 0x006a, SZCODE_EXACT, 4, "eraseSameArc", NULL },
884 { 0x006b, SZCODE_EXACT, 4, "invertSameArc", NULL },
885 { 0x006c, SZCODE_EXACT, 4, "fillSameArc", NULL },
886 { 0x0080, SZCODE_REGION, 0, "frameRgn", NULL },
887 { 0x0081, SZCODE_REGION, 0, "paintRgn", NULL },
888 { 0x0082, SZCODE_REGION, 0, "eraseRgn", NULL },
889 { 0x0083, SZCODE_REGION, 0, "invertRgn", NULL },
890 { 0x0084, SZCODE_REGION, 0, "fillRgn", NULL },
891 { 0x0070, SZCODE_POLYGON, 0, "framePoly", NULL },
892 { 0x0071, SZCODE_POLYGON, 0, "paintPoly", NULL },
893 { 0x0072, SZCODE_POLYGON, 0, "erasePoly", NULL },
894 { 0x0073, SZCODE_POLYGON, 0, "invertPoly", NULL },
895 { 0x0074, SZCODE_POLYGON, 0, "fillPoly", NULL },
896 { 0x0090, SZCODE_SPECIAL, 0, "BitsRect", handler_98_9a },
897 { 0x0091, SZCODE_SPECIAL, 0, "BitsRgn", handler_98_9a },
898 { 0x0098, SZCODE_SPECIAL, 0, "PackBitsRect", handler_98_9a },
899 { 0x0099, SZCODE_SPECIAL, 0, "PackBitsRgn", handler_98_9a },
900 { 0x009a, SZCODE_SPECIAL, 0, "DirectBitsRect", handler_98_9a },
901 { 0x009b, SZCODE_SPECIAL, 0, "DirectBitsRgn", handler_98_9a },
902 { 0x00a0, SZCODE_EXACT, 2, "ShortComment", handler_a0 },
903 { 0x00a1, SZCODE_SPECIAL, 0, "LongComment", handler_a1 },
904 { 0x00ff, SZCODE_EXACT, 2, "opEndPic", NULL },
905 { 0x0c00, SZCODE_EXACT, 24, "HeaderOp", handler_0c00 },
906 { 0x8200, SZCODE_SPECIAL, 0, "CompressedQuickTime", handler_QuickTime },
907 { 0x8201, SZCODE_SPECIAL, 0, "UncompressedQuickTime", handler_QuickTime }
910 static const struct opcode_info *find_opcode_info(i64 opcode)
912 size_t i;
914 for(i=0; i<DE_ARRAYCOUNT(opcode_info_arr); i++) {
915 if(opcode_info_arr[i].opcode == opcode) {
916 return &opcode_info_arr[i];
919 return NULL;
922 static int do_handle_item(deark *c, lctx *d, i64 opcode_pos, i64 opcode,
923 i64 data_pos, i64 *data_bytes_used)
925 const char *opcode_name;
926 const struct opcode_info *opi;
927 i64 n;
928 struct pict_rect tmprect;
929 int ret = 0;
931 *data_bytes_used = 0;
933 opi = find_opcode_info(opcode);
934 if(opi && opi->name) opcode_name = opi->name;
935 else opcode_name = "?";
937 if(d->version==2)
938 de_dbg(c, "opcode 0x%04x (%s) at %d", (unsigned int)opcode, opcode_name, (int)opcode_pos);
939 else
940 de_dbg(c, "opcode 0x%02x (%s) at %d", (unsigned int)opcode, opcode_name, (int)opcode_pos);
942 if(opi && opi->fn) {
943 de_dbg_indent(c, 1);
944 *data_bytes_used = opi->size; // Default to the size in the table.
945 ret = opi->fn(c, d, opcode, data_pos, data_bytes_used);
946 de_dbg_indent(c, -1);
948 else if(opi && opi->size_code==SZCODE_EXACT) {
949 *data_bytes_used = opi->size;
950 ret = 1;
952 else if(opi && opi->size_code==SZCODE_REGION) {
953 n = de_getu16be(data_pos);
954 de_dbg_indent(c, 1);
955 de_dbg(c, "region size: %d", (int)n);
956 if(n>=10) {
957 pict_read_rect(c->infile, data_pos+2, &tmprect, "rect");
959 de_dbg_indent(c, -1);
960 *data_bytes_used = n;
961 ret = 1;
963 else if(opi && opi->size_code==SZCODE_POLYGON) {
964 n = de_getu16be(data_pos);
965 de_dbg_indent(c, 1);
966 de_dbg(c, "polygon size: %d", (int)n);
967 de_dbg_indent(c, -1);
968 *data_bytes_used = n;
969 ret = 1;
971 else if(opcode>=0x2c && opcode<=0x2f) {
972 // Starts with 2-byte size, size does not include the "size" field.
973 n = de_getu16be(data_pos);
974 *data_bytes_used = 2+n;
975 ret = 1;
977 else if(opcode>=0x8100 && opcode<=0xffff) {
978 // Starts with 4-byte size, size does not include the "size" field.
979 n = de_getu32be(data_pos);
980 *data_bytes_used = 4+n;
981 ret = 1;
983 else {
984 de_err(c, "Unsupported opcode: 0x%04x", (unsigned int)opcode);
987 return ret;
990 static void do_read_items(deark *c, lctx *d, i64 pos)
992 i64 opcode;
993 i64 opcode_pos;
994 i64 bytes_used;
995 int ret;
997 while(1) {
998 if(pos%2 && d->version==2) {
999 pos++; // 2-byte alignment
1002 if(pos >= c->infile->len) break;
1004 opcode_pos = pos;
1006 if(d->version==2) {
1007 opcode = de_getu16be(pos);
1008 pos+=2;
1010 else {
1011 opcode = (i64)de_getbyte(pos);
1012 pos+=1;
1015 ret = do_handle_item(c, d, opcode_pos, opcode, pos, &bytes_used);
1016 if(!ret) goto done;
1017 if(opcode==0x00ff) goto done; // End of image
1019 pos += bytes_used;
1021 done:
1025 // mode: 0=called from de_identify..., 1=called from de_run...
1026 static void do_detect_version(deark *c, struct detection_info *dti, int mode)
1028 static const u8 v1pattern[2] = { 0x11, 0x01 };
1029 static const u8 v2pattern[6] = { 0x00, 0x11, 0x02, 0xff, 0x0c, 0x00 };
1030 u8 buf[6];
1031 int v1_nohdr = 0;
1032 int v2_nohdr = 0;
1033 int v1_hdr = 0;
1034 int v2_hdr = 0;
1036 dti->file_version = 0;
1037 dti->has_fileheader = 0;
1039 de_read(buf, 522, sizeof(buf));
1040 if(!de_memcmp(buf, v2pattern, 6)) {
1041 v2_hdr = 1;
1043 else if(!de_memcmp(buf, v1pattern, 2)) {
1044 v1_hdr = 1;
1046 else {
1047 de_read(buf, 10, sizeof(buf));
1048 if(!de_memcmp(buf, v2pattern, 6)) {
1049 v2_nohdr = 1;
1051 else if(!de_memcmp(buf, v1pattern, 2)) {
1052 v1_nohdr = 1;
1056 if(!v1_hdr && !v2_hdr && !v1_nohdr && !v2_nohdr) {
1057 return;
1060 if(v2_hdr) {
1061 dti->file_version = 2;
1062 dti->has_fileheader = 1;
1063 return;
1065 else if(v2_nohdr) {
1066 dti->file_version = 2;
1067 return;
1070 if(mode==0) {
1071 // For v1, check that the file ends as expected
1072 de_read(buf, c->infile->len-2, 2);
1073 if(buf[1]==0xff) {
1074 ; // v1 files should end with 0xff
1076 else if(buf[0]==0xff && buf[1]==0x00) {
1077 ; // But a few have an extra NUL byte at the end
1081 if(v1_hdr) {
1082 dti->file_version = 1;
1083 dti->has_fileheader = 1;
1084 return;
1086 else if(v1_nohdr) {
1087 dti->file_version = 1;
1088 return;
1092 static void de_run_pict(deark *c, de_module_params *mparams)
1094 lctx *d = NULL;
1095 i64 pos = 0;
1096 i64 picsize;
1097 struct pict_rect framerect;
1099 d = de_malloc(c, sizeof(lctx));
1101 do_detect_version(c, &d->dti, 1);
1102 if(d->dti.file_version>0) {
1103 de_declare_fmtf(c, "PICT v%d%s", d->dti.file_version,
1104 d->dti.has_fileheader?"":", without file header");
1107 d->version = 1;
1109 if(d->dti.has_fileheader) {
1110 pos += 512;
1113 picsize = de_getu16be(pos);
1114 de_dbg(c, "picSize: %d", (int)picsize);
1115 pos+=2;
1116 pict_read_rect(c->infile, pos, &framerect, "picFrame");
1117 pos+=8;
1119 do_read_items(c, d, pos);
1121 dbuf_close(d->iccprofile_file);
1122 de_free(c, d);
1125 static int de_identify_pict(deark *c)
1127 struct detection_info dti;
1129 do_detect_version(c, &dti, 0);
1130 if(dti.file_version==2) {
1131 return 85;
1133 else if(dti.file_version==1) {
1134 if(dti.has_fileheader) return 25;
1135 return 15;
1137 return 0;
1140 void de_module_pict(deark *c, struct deark_module_info *mi)
1142 mi->id = "pict";
1143 mi->desc = "Macintosh PICT";
1144 mi->run_fn = de_run_pict;
1145 mi->identify_fn = de_identify_pict;