1 // This file is part of Deark.
2 // Copyright (C) 2016 Jason Summers
3 // See the file COPYING for terms of use.
7 #include <deark-config.h>
8 #include <deark-private.h>
9 DE_DECLARE_MODULE(de_module_iccprofile
);
11 #define MAX_TAGS_PER_TAGSET 500
12 #define MAX_NESTING_LEVEL 16
14 struct tag_seen_type
{
15 i64 offset
; // relative to tagset_type::data_area_pos
24 struct tag_seen_type
*tags_seen
;
31 typedef struct localctx_struct
{
32 unsigned int profile_ver_major
;
33 unsigned int profile_ver_minor
;
34 unsigned int profile_ver_bugfix
;
40 struct typedec_params
{
42 struct tagset_type
*tgs
;
47 typedef void (*datatype_decoder_fn_type
)(deark
*c
, struct typedec_params
*p
);
53 datatype_decoder_fn_type dtdfn
;
59 // 0x1 = ignore if version >= 4.0.0
60 // 0x2 = ignore if version < 4.0.0
67 static double read_s15Fixed16Number(dbuf
*f
, i64 pos
)
71 n
= dbuf_geti16be(f
, pos
);
72 frac
= dbuf_getu16be(f
, pos
+2);
73 return (double)n
+ ((double)frac
)/65536.0;
76 static void dbg_timestamp(deark
*c
, i64 pos1
, const char *name
)
81 char timestamp_buf
[64];
84 n
[i
] = de_getu16be_p(&pos
);
88 struct de_timestamp ts
;
90 de_make_timestamp(&ts
, n
[0], n
[1], n
[2], n
[3], n
[4], n
[5]);
91 ts
.tzcode
= DE_TZCODE_UTC
;
92 de_dbg_timestamp_to_string(c
, &ts
, timestamp_buf
, sizeof(timestamp_buf
), 0);
95 de_strlcpy(timestamp_buf
, "(none)", sizeof(timestamp_buf
));
98 de_dbg(c
, "%s: %s", name
, timestamp_buf
);
101 static void read_XYZ(deark
*c
, i64 pos
, struct XYZ_type
*xyz
)
103 xyz
->v
[0] = read_s15Fixed16Number(c
->infile
, pos
);
104 xyz
->v
[1] = read_s15Fixed16Number(c
->infile
, pos
+4);
105 xyz
->v
[2] = read_s15Fixed16Number(c
->infile
, pos
+8);
108 static void destroy_tagset(deark
*c
, struct tagset_type
*tgs
)
111 de_free(c
, tgs
->tags_seen
);
115 // flag 0x1: Include the hex value
116 // flag 0x2: Interpret 0 as (none)
117 // Returns a copy of the buf pointer.
118 static const char *format_4cc_dbgstr(const struct de_fourcc
*tmp4cc
,
119 char *buf
, size_t buflen
, unsigned int flags
)
123 if((tmp4cc
->id
==0) && (flags
&0x2))
124 de_strlcpy(str
, "(none)", sizeof(str
));
126 de_snprintf(str
, sizeof(str
), "'%s'", tmp4cc
->id_dbgstr
);
129 de_snprintf(buf
, buflen
, "0x%08x=%s", (unsigned int)tmp4cc
->id
, str
);
131 de_strlcpy(buf
, str
, buflen
);
136 static void typedec_XYZ(deark
*c
, struct typedec_params
*p
)
142 xyz_count
= (p
->len
-8)/12;
143 for(k
=0; k
<xyz_count
; k
++) {
146 read_XYZ(c
, p
->pos1
+8+12*k
, &xyz
);
147 de_dbg(c
, "XYZ[%d]: %.5f, %.5f, %.5f", (int)k
,
148 xyz
.v
[0], xyz
.v
[1], xyz
.v
[2]);
152 static void typedec_text(deark
*c
, struct typedec_params
*p
)
154 de_ucstring
*s
= NULL
;
155 i64 textlen
= p
->len
-8;
158 if(textlen
<0) goto done
;
160 if(p
->type_id
==0x75746638U
) {
161 enc
= DE_ENCODING_UTF8
;
164 enc
= DE_ENCODING_ASCII
;
167 s
= ucstring_create(c
);
168 dbuf_read_to_ucstring_n(c
->infile
, p
->pos1
+8, textlen
, DE_DBG_MAX_STRLEN
,
170 ucstring_truncate_at_NUL(s
);
171 de_dbg(c
, "text: \"%s\"", ucstring_getpsz(s
));
178 static void typedec_desc(deark
*c
, struct typedec_params
*p
)
180 de_ucstring
*s
= NULL
;
181 i64 invdesclen
, uloclen
;
185 de_encoding encoding
;
188 if(p
->len
<12) goto done
;
192 // ASCII invariant description
193 invdesclen
= de_getu32be(pos
); // invariant desc. len, including NUL byte
195 s
= ucstring_create(c
);
196 dbuf_read_to_ucstring_n(c
->infile
, pos
, invdesclen
, DE_DBG_MAX_STRLEN
,
197 s
, 0, DE_ENCODING_ASCII
);
198 ucstring_truncate_at_NUL(s
);
199 de_dbg(c
, "invariant desc.: \"%s\"", ucstring_getpsz(s
));
201 if(pos
>= p
->pos1
+p
->len
) goto done
;
203 // Unicode localizable description
206 langcode
= de_getu32be(pos
);
209 uloclen
= de_getu32be(pos
);
213 // TODO: How to interpret the language code?
214 de_dbg(c
, "language code: %d", (int)langcode
);
218 bytes_to_read
= uloclen
*2;
220 encoding
= DE_ENCODING_UTF16BE
;
223 // Check for a BOM. The spec doesn't say much about the format of
224 // Unicode text in 'desc' tags. It does say that "All profile data must
225 // be encoded as big-endian", so maybe that means UTF-16LE is not
226 // allowed. In practice, some strings begin with a BOM.
227 firstchar
= (i32
)de_getu16be(lstrstartpos
);
228 if(firstchar
==0xfeff) { // UTF-16BE BOM
232 else if(firstchar
==0xffef) { // UTF-16LE BOM
235 encoding
= DE_ENCODING_UTF16LE
;
239 dbuf_read_to_ucstring_n(c
->infile
, lstrstartpos
, bytes_to_read
, DE_DBG_MAX_STRLEN
*2,
241 ucstring_truncate_at_NUL(s
);
243 de_dbg(c
, "localizable desc.: \"%s\"", ucstring_getpsz(s
));
246 if(pos
>= p
->pos1
+p
->len
) goto done
;
248 // Macintosh localizable description
255 static void do_mluc_record(deark
*c
, lctx
*d
, i64 tagstartpos
,
256 i64 pos
, i64 recsize
)
258 de_ucstring
*s
= NULL
;
262 s
= ucstring_create(c
);
264 dbuf_read_to_ucstring(c
->infile
, pos
, 2, s
, 0, DE_ENCODING_ASCII
);
265 de_dbg(c
, "language code: '%s'", ucstring_getpsz(s
));
268 dbuf_read_to_ucstring(c
->infile
, pos
+2, 2, s
, 0, DE_ENCODING_ASCII
);
269 de_dbg(c
, "country code: '%s'", ucstring_getpsz(s
));
272 string_len
= de_getu32be(pos
+4);
273 string_offset
= de_getu32be(pos
+8);
274 de_dbg(c
, "string offset=%d+%d, len=%d bytes", (int)tagstartpos
,
275 (int)string_offset
, (int)string_len
);
277 dbuf_read_to_ucstring_n(c
->infile
, tagstartpos
+string_offset
, string_len
, DE_DBG_MAX_STRLEN
*2,
278 s
, 0, DE_ENCODING_UTF16BE
);
279 ucstring_truncate_at_NUL(s
);
280 de_dbg(c
, "string: \"%s\"", ucstring_getpsz(s
));
285 static void typedec_mluc(deark
*c
, struct typedec_params
*p
)
290 i64 rec_array_startpos
;
293 if(p
->len
<12) goto done
;
296 num_recs
= de_getu32be(pos
);
297 de_dbg(c
, "number of records: %d", (int)num_recs
);
300 recsize
= de_getu32be(pos
);
301 de_dbg(c
, "record size: %d", (int)recsize
);
302 if(recsize
<12) goto done
;
305 rec_array_startpos
= pos
;
307 for(rec
=0; rec
<num_recs
; rec
++) {
308 if(rec_array_startpos
+rec
*recsize
> p
->pos1
+p
->len
) break;
310 de_dbg(c
, "record #%d at %d", (int)rec
, (int)pos
);
312 do_mluc_record(c
, p
->d
, p
->pos1
, pos
, recsize
);
313 de_dbg_indent(c
, -1);
321 static void do_tag_data(deark
*c
, lctx
*d
, struct tagset_type
*tgs
, i64 tagindex
,
322 i64 tagdataoffset
, i64 tagdatalen
);
324 static void typedec_tagArray_tagStruct(deark
*c
, struct typedec_params
*p
)
326 struct de_fourcc ty4cc
;
327 struct de_fourcc tmp4cc
;
329 struct tagset_type
*tgs
= NULL
;
330 i64 pos
= p
->pos1
+ 8;
331 i64 endpos
= p
->pos1
+ p
->len
;
332 int is_struct
= (p
->type_id
== 0x74737472U
);
335 const char *struct_name
;
336 int saved_indent_level
;
338 de_dbg_indent_save(c
, &saved_indent_level
);
339 de_zeromem(&tmp4cc
, sizeof(struct de_fourcc
));
341 tgs
= de_malloc(c
, sizeof(struct tagset_type
));
342 tgs
->data_area_pos
= p
->pos1
;
343 tgs
->data_area_len
= p
->len
;
344 tgs
->nesting_level
= p
->tgs
->nesting_level
+1;
345 if(tgs
->nesting_level
> MAX_NESTING_LEVEL
) goto done
;
348 array_item_size
= 12;
349 struct_name
= "struct";
353 struct_name
= "array";
356 dbuf_read_fourcc(c
->infile
, pos
, &ty4cc
, 4, 0x0);
358 de_dbg(c
, "%s type: %s", struct_name
,
359 format_4cc_dbgstr(&ty4cc
, d
->tmpbuf1
, sizeof(d
->tmpbuf1
), 0));
360 tgs
->num_tags
= de_getu32be_p(&pos
);
361 de_dbg(c
, "number of elements: %"I64_FMT
, tgs
->num_tags
);
362 if(tgs
->num_tags
>MAX_TAGS_PER_TAGSET
) {
363 de_err(c
, "Invalid or excessive number of elements: %u", (UI
)tgs
->num_tags
);
367 tgs
->tags_seen
= de_mallocarray(c
, tgs
->num_tags
, sizeof(struct tag_seen_type
));
369 for(i
=0; i
<tgs
->num_tags
; i
++) {
370 i64 elem_pos_rel
, elem_pos_abs
;
374 if(pos
+array_item_size
> endpos
) break;
377 dbuf_read_fourcc(c
->infile
, pos
, &tmp4cc
, 4, 0x0);
381 elem_pos_rel
= de_getu32be_p(&pos
);
382 elem_dlen
= de_getu32be_p(&pos
);
383 elem_pos_abs
= p
->pos1
+ elem_pos_rel
;
386 format_4cc_dbgstr(&tmp4cc
, d
->tmpbuf1
, sizeof(d
->tmpbuf1
), 0);
387 de_snprintf(d
->tmpbuf2
, sizeof(d
->tmpbuf2
), " %s", d
->tmpbuf1
);
390 d
->tmpbuf2
[0] = '\0';
392 de_dbg(c
, "elem #%d%s tpos=%"I64_FMT
" dpos=%"I64_FMT
" (%"I64_FMT
"+%"I64_FMT
"), dlen=%"I64_FMT
,
393 (int)i
, d
->tmpbuf2
, tpos
, elem_pos_abs
, p
->pos1
, elem_pos_rel
, elem_dlen
);
396 do_tag_data(c
, d
, tgs
, i
, elem_pos_rel
, elem_dlen
);
397 de_dbg_indent(c
, -1);
401 destroy_tagset(c
, tgs
);
402 de_dbg_indent_restore(c
, saved_indent_level
);
405 static void do_dict_string(deark
*c
, struct typedec_params
*p
, i64 itempos
, const char *itemname
)
407 de_ucstring
*s
= NULL
;
408 i64 dpos_rel
, dpos_abs
, dlen
;
410 dpos_rel
= de_getu32be(itempos
);
411 dpos_abs
= p
->pos1
+ dpos_rel
;
412 dlen
= de_getu32be(itempos
+4);
413 if(dpos_abs
+ dlen
> p
->pos1
+ p
->len
) goto done
;
414 s
= ucstring_create(c
);
415 dbuf_read_to_ucstring_n(c
->infile
, dpos_abs
, dlen
, DE_DBG_MAX_STRLEN
, s
, 0, DE_ENCODING_UTF16BE
);
416 de_dbg(c
, "%s: dpos=%"I64_FMT
", dlen=%"I64_FMT
", string=\"%s\"", itemname
, dpos_abs
, dlen
,
417 ucstring_getpsz_d(s
));
423 static void typedec_dict(deark
*c
, struct typedec_params
*p
)
425 i64 pos
= p
->pos1
+ 8;
430 if(p
->len
<16) goto done
;
432 nrec
= de_getu32be_p(&pos
);
433 de_dbg(c
, "num records: %u", (UI
)nrec
);
434 reclen
= de_getu32be_p(&pos
);
435 de_dbg(c
, "rec len: %u", (UI
)reclen
);
436 if(reclen
!=16 && reclen
!=24 && reclen
!=32) goto done
;
437 if(pos
+nrec
*reclen
> p
->pos1
+ p
->len
) goto done
;
438 if(nrec
>MAX_TAGS_PER_TAGSET
) goto done
;
440 for(i
=0; i
<nrec
; i
++) {
441 do_dict_string(c
, p
, pos
, "name");
443 do_dict_string(c
, p
, pos
, "value");
446 do_dict_string(c
, p
, pos
, "display name");
450 do_dict_string(c
, p
, pos
, "display value");
459 static void typedec_hexdump(deark
*c
, struct typedec_params
*p
)
464 rsvd
= (UI
)de_getu32be(p
->pos1
+4);
465 de_dbg(c
, "reserved/etc.: 0x%08x", rsvd
);
466 de_dbg_hexdump(c
, c
->infile
, p
->pos1
+8, p
->len
-8, 256, NULL
, 0x1);
469 static const struct datatypeinfo datatypeinfo_arr
[] = {
470 { 0x58595a20U
, 0, "XYZ", typedec_XYZ
}, // XYZ
471 { 0x62666420U
, 0, "ucrbg", NULL
}, // bfd
472 { 0x6368726dU
, 0, "chromaticity", NULL
}, // chrm
473 { 0x636c726fU
, 0, "colorantOrder", NULL
}, // clro
474 { 0x636c7274U
, 0, "colorantTable", NULL
}, // clrt
475 { 0x63726469U
, 0, "crdInfo", NULL
}, // crdi
476 { 0x63757266U
, 0, "segmentedCurve", NULL
}, // curf
477 { 0x63757276U
, 0, "curve", NULL
}, // curv
478 { 0x64657363U
, 0, "textDescription", typedec_desc
}, // desc
479 { 0x64617461U
, 0, "data", NULL
}, // data
480 { 0x64657673U
, 0, "deviceSettings", NULL
}, // devs
481 { 0x64696374U
, 0, "dictionary array", typedec_dict
}, // dict
482 { 0x6474696dU
, 0, "dateTime", NULL
}, // dtim
483 { 0x666c3136U
, 0, "float16Array", NULL
}, // fl16
484 { 0x666c3332U
, 0, "float32Array", NULL
}, // fl32
485 { 0x666c3634U
, 0, "float64Array", NULL
}, // fl64
486 { 0x67626420U
, 0, "gamutBoundaryDescription", NULL
}, // gbd
487 { 0x6d414220U
, 0, "lutAToB", NULL
}, // mAB
488 { 0x6d424120U
, 0, "lutBToA", NULL
}, // mBA
489 { 0x6d656173U
, 0, "measurement", NULL
}, // meas
490 { 0x6d667431U
, 0, "lut8", NULL
}, // mft1
491 { 0x6d667432U
, 0, "lut16", NULL
}, // mft2
492 { 0x6d6c7563U
, 0, "multiLocalizedUnicode", typedec_mluc
}, // mluc
493 { 0x6d706574U
, 0, "multiProcessElements", NULL
}, // mpet
494 { 0x6e636c32U
, 0, "namedColor2", NULL
}, // ncl2
495 { 0x6e636f6cU
, 0, "namedColor", NULL
}, // ncol
496 { 0x70617261U
, 0, "parametricCurve", NULL
}, // para
497 { 0x70736571U
, 0, "profileSequenceDesc", NULL
}, // pseq
498 { 0x70736964U
, 0, "profileSequenceIdentifier", NULL
}, // psid
499 { 0x72637332U
, 0, "responseCurveSet16", NULL
}, // rcs2
500 { 0x73663332U
, 0, "s15Fixed16Array", NULL
}, // sf32
501 { 0x7363726eU
, 0, "screening", NULL
}, // scrn
502 { 0x73696720U
, 0, "signature", NULL
}, // sig
503 { 0x7376636eU
, 0, "spectralViewingConditions", NULL
}, // svcn
504 { 0x74617279U
, 0, "tagArray", typedec_tagArray_tagStruct
}, // tary
505 { 0x74657874U
, 0, "text", typedec_text
}, // text
506 { 0x74737472U
, 0, "tagStruct", typedec_tagArray_tagStruct
}, // tstr
507 { 0x75663332U
, 0, "u16Fixed16Array", NULL
}, // uf32
508 { 0x75693038U
, 0, "uInt8Array", NULL
}, // ui08
509 { 0x75693136U
, 0, "uInt16Array", NULL
}, // ui16
510 { 0x75693332U
, 0, "uInt32Array", NULL
}, // ui32
511 { 0x75693634U
, 0, "uInt64Array", NULL
}, // ui64
512 { 0x75743136U
, 0, "utf16", NULL
}, // ut16
513 { 0x75746638U
, 0, "utf8", typedec_text
}, // utf8
514 { 0x76636774U
, 0, "Video Card Gamma Type", NULL
}, // vcgt (Apple)
515 { 0x76696577U
, 0, "viewingConditions", NULL
} // view
518 static const struct taginfo taginfo_arr
[] = {
519 { 0x41324230U
, 0, "AToB0", NULL
}, // A2B0
520 { 0x41324231U
, 0, "AToB1", NULL
}, // A2B1
521 { 0x41324232U
, 0, "AToB2", NULL
}, // A2B2
522 { 0x41324233U
, 0, "AToB3", NULL
}, // A2B3
523 { 0x42324130U
, 0, "BToA0", NULL
}, // B2A0
524 { 0x42324131U
, 0, "BToA1", NULL
}, // B2A1
525 { 0x42324132U
, 0, "BToA2", NULL
}, // B2A2
526 { 0x42324133U
, 0, "BToA3", NULL
}, // B2A3
527 { 0x42324430U
, 0, "BToD0", NULL
}, // B2D0
528 { 0x42324431U
, 0, "BToD1", NULL
}, // B2D1
529 { 0x42324432U
, 0, "BToD2", NULL
}, // B2D2
530 { 0x42324433U
, 0, "BToD3", NULL
}, // B2D3
531 { 0x44324230U
, 0, "DToB0", NULL
}, // D2B0
532 { 0x44324231U
, 0, "DToB1", NULL
}, // D2B1
533 { 0x44324232U
, 0, "DToB2", NULL
}, // D2B2
534 { 0x44324233U
, 0, "DToB3", NULL
}, // D2B3
535 { 0x62545243U
, 0, "blueTRC", NULL
}, // bTRC
536 { 0x6258595aU
, 0x1, "blueColorant", NULL
}, // bXYZ
537 { 0x6258595aU
, 0x2, "blueMatrixColumn", NULL
}, // bXYZ
538 { 0x62666420U
, 0, "ucrbg", NULL
}, // bfd
539 { 0x626b7074U
, 0, "mediaBlackPoint", NULL
}, // bkpt
540 { 0x63327370U
, 0, "customToStandardPcc", NULL
}, // c2sp
541 { 0x63616c74U
, 0, "calibrationDateTime", NULL
}, // calt
542 { 0x63657074U
, 0, "colorEncodingParams", NULL
}, // cept
543 { 0x63686164U
, 0, "chromaticAdaptation", NULL
}, // chad
544 { 0x6368726dU
, 0, "chromaticity", NULL
}, // chrm
545 { 0x63696973U
, 0, "colorimetricIntentImageState", NULL
}, // ciis
546 { 0x636c6f74U
, 0, "colorantTableOut", NULL
}, // clot
547 { 0x636c726fU
, 0, "colorantOrder", NULL
}, // clro
548 { 0x636c7274U
, 0, "colorantTable", NULL
}, // clrt
549 { 0x63707274U
, 0, "copyright", NULL
}, // cprt
550 { 0x63726469U
, 0, "crdInfo", NULL
}, // crdi
551 { 0x63736e6dU
, 0, "colorSpaceName", NULL
}, // csnm
552 { 0x64657363U
, 0, "profileDescription", NULL
}, // desc
553 { 0x64657673U
, 0, "deviceSettings", NULL
}, // devs
554 { 0x646d6464U
, 0, "deviceModelDesc", NULL
}, // dmdd
555 { 0x646d6e64U
, 0, "deviceMfgDesc", NULL
}, // dmnd
556 { 0x67616d74U
, 0, "gamut", NULL
}, // gamt
557 { 0x67626431U
, 0, "amutBoundaryDescription1", NULL
}, // gbd1
558 { 0x67545243U
, 0, "greenTRC", NULL
}, // gTRC
559 { 0x6758595aU
, 0x1, "greenColorant", NULL
}, // gXYZ
560 { 0x6758595aU
, 0x2, "greenMatrixColumn", NULL
}, // gXYZ
561 { 0x6b545243U
, 0, "grayTRC", NULL
}, // kTRC
562 { 0x6c756d69U
, 0, "luminance", NULL
}, // lumi
563 { 0x6d656173U
, 0, "measurement", NULL
}, // meas
564 { 0x6e636c32U
, 0, "namedColor2", NULL
}, // ncl2
565 { 0x6e636f6cU
, 0, "namedColor", NULL
}, // ncol
566 { 0x70726530U
, 0, "preview0", NULL
}, // pre0
567 { 0x70726531U
, 0, "preview1", NULL
}, // pre1
568 { 0x70726532U
, 0, "preview2", NULL
}, // pre2
569 { 0x70733269U
, 0, "ps2RenderingIntent", NULL
}, // ps2i
570 { 0x70733273U
, 0, "ps2CSA", NULL
}, // ps2s
571 { 0x70736430U
, 0, "ps2CRD0", NULL
}, // psd0
572 { 0x70736431U
, 0, "ps2CRD1", NULL
}, // psd1
573 { 0x70736432U
, 0, "ps2CRD2", NULL
}, // psd2
574 { 0x70736433U
, 0, "ps2CRD3", NULL
}, // psd3
575 { 0x70736571U
, 0, "profileSequenceDesc", NULL
}, // pseq
576 { 0x70736964U
, 0, "profileSequenceIdentifier", NULL
}, // psid
577 { 0x72545243U
, 0, "redTRC", NULL
}, // rTRC
578 { 0x7258595aU
, 0x1, "redColorant", NULL
}, // rXYZ
579 { 0x7258595aU
, 0x2, "redMatrixColumn", NULL
}, // rXYZ
580 { 0x72657370U
, 0, "outputResponse", NULL
}, // resp
581 { 0x72666e6dU
, 0, "referenceName", NULL
}, // rfnm
582 { 0x72696730U
, 0, "perceptualRenderingIntentGamut", NULL
}, // rig0
583 { 0x72696732U
, 0, "saturationRenderingIntentGamut", NULL
}, // rig2
584 { 0x73326370U
, 0, "standardToCustomPcc", NULL
}, // s2cp
585 { 0x73637264U
, 0, "screeningDesc", NULL
}, // scrd
586 { 0x7363726eU
, 0, "screening", NULL
}, // scrn
587 { 0x7376636eU
, 0, "spectralViewingConditions", NULL
}, // svcn
588 { 0x74617267U
, 0, "charTarget", NULL
}, // targ
589 { 0x74656368U
, 0, "technology", NULL
}, // tech
590 { 0x76636774U
, 0, "Video Card Gamma Type", NULL
}, // vcgt (Apple)
591 { 0x76696577U
, 0, "viewingConditions", NULL
}, // view
592 { 0x76756564U
, 0, "viewingCondDesc", NULL
}, // vued
593 { 0x77747074U
, 0, "mediaWhitePoint", NULL
} // wtpt
596 static void do_read_header(deark
*c
, lctx
*d
, i64 pos
)
601 struct de_fourcc tmp4cc
;
606 de_dbg(c
, "header at %d", (int)pos
);
609 d
->profile_size
= de_getu32be(pos
+0);
610 de_dbg(c
, "profile size: %"I64_FMT
, d
->profile_size
);
612 dbuf_read_fourcc(c
->infile
, pos
+4, &tmp4cc
, 4, 0x0);
613 de_dbg(c
, "preferred CMM type: %s",
614 format_4cc_dbgstr(&tmp4cc
, d
->tmpbuf1
, sizeof(d
->tmpbuf1
), 0x3));
616 profile_ver_raw
= (u32
)de_getu32be(pos
+8);
617 d
->profile_ver_major
= 10*((profile_ver_raw
&0xf0000000U
)>>28) +
618 ((profile_ver_raw
&0x0f000000U
)>>24);
619 d
->profile_ver_minor
= (profile_ver_raw
&0x00f00000U
)>>20;
620 d
->profile_ver_bugfix
= (profile_ver_raw
&0x000f0000U
)>>16;
621 de_dbg(c
, "profile version: %u.%u.%u", d
->profile_ver_major
,
622 d
->profile_ver_minor
, d
->profile_ver_bugfix
);
624 dbuf_read_fourcc(c
->infile
, pos
+12, &tmp4cc
, 4, 0x0);
625 de_dbg(c
, "profile/device class: %s",
626 format_4cc_dbgstr(&tmp4cc
, d
->tmpbuf1
, sizeof(d
->tmpbuf1
), 0x1));
628 dbuf_read_fourcc(c
->infile
, pos
+16, &tmp4cc
, 4, 0x0);
630 if(d
->profile_ver_major
>=5) tmpflags
|= 0x2;
631 de_dbg(c
, "colour space: %s",
632 format_4cc_dbgstr(&tmp4cc
, d
->tmpbuf1
, sizeof(d
->tmpbuf1
), tmpflags
));
634 dbuf_read_fourcc(c
->infile
, pos
+20, &tmp4cc
, 4, 0x0);
636 if(d
->profile_ver_major
>=5) tmpflags
|= 0x2;
638 format_4cc_dbgstr(&tmp4cc
, d
->tmpbuf1
, sizeof(d
->tmpbuf1
), tmpflags
));
640 dbg_timestamp(c
, pos
+24, "creation time");
642 dbuf_read_fourcc(c
->infile
, pos
+36, &tmp4cc
, 4, 0x0);
643 de_dbg(c
, "file signature: %s",
644 format_4cc_dbgstr(&tmp4cc
, d
->tmpbuf1
, sizeof(d
->tmpbuf1
), 0x1));
646 dbuf_read_fourcc(c
->infile
, pos
+40, &tmp4cc
, 4, 0x0);
647 de_dbg(c
, "primary platform: %s",
648 format_4cc_dbgstr(&tmp4cc
, d
->tmpbuf1
, sizeof(d
->tmpbuf1
), 0x3));
650 // TODO: Decode profile flags
651 x
= de_getu32be(pos
+44);
652 de_dbg(c
, "profile flags: 0x%08x", (UI
)x
);
654 dbuf_read_fourcc(c
->infile
, pos
+48, &tmp4cc
, 4, 0x0);
655 de_dbg(c
, "device manufacturer: %s",
656 format_4cc_dbgstr(&tmp4cc
, d
->tmpbuf1
, sizeof(d
->tmpbuf1
), 0x3));
658 dbuf_read_fourcc(c
->infile
, pos
+52, &tmp4cc
, 4, 0x0);
659 de_dbg(c
, "device model: %s",
660 format_4cc_dbgstr(&tmp4cc
, d
->tmpbuf1
, sizeof(d
->tmpbuf1
), 0x3));
662 // TODO: Decode device attributes
663 xu
= dbuf_getu64be(c
->infile
, pos
+56);
664 de_dbg(c
, "device attribs: 0x%016"U64_FMTx
, xu
);
666 x
= de_getu32be(pos
+64);
668 case 0: name
="perceptual"; break;
669 case 1: name
="relative colorimetric"; break;
670 case 2: name
="saturation"; break;
671 case 3: name
="absolute colorimetric"; break;
672 default: name
="?"; break;
674 de_dbg(c
, "rendering intent: %d (%s)", (int)x
, name
);
676 read_XYZ(c
, pos
+68, &xyz
);
677 de_dbg(c
, "illuminant: %.5f, %.5f, %.5f", xyz
.v
[0], xyz
.v
[1], xyz
.v
[2]);
679 dbuf_read_fourcc(c
->infile
, pos
+80, &tmp4cc
, 4, 0x0);
680 de_dbg(c
, "profile creator: %s",
681 format_4cc_dbgstr(&tmp4cc
, d
->tmpbuf1
, sizeof(d
->tmpbuf1
), 0x3));
683 // TODO: pos=84-99 Profile ID
685 de_dbg_indent(c
, -1);
688 static const struct datatypeinfo
*lookup_datatypeinfo(u32 id
)
691 for(k
=0; k
<DE_ARRAYCOUNT(datatypeinfo_arr
); k
++) {
692 if(datatypeinfo_arr
[k
].id
== id
) {
693 return &datatypeinfo_arr
[k
];
699 static const struct taginfo
*lookup_taginfo(lctx
*d
, u32 id
)
702 for(k
=0; k
<DE_ARRAYCOUNT(taginfo_arr
); k
++) {
703 if((taginfo_arr
[k
].flags
& 0x1) && d
->profile_ver_major
>=4) continue;
704 if((taginfo_arr
[k
].flags
& 0x2) && d
->profile_ver_major
<4) continue;
705 if(taginfo_arr
[k
].id
== id
) {
706 return &taginfo_arr
[k
];
712 static int is_duplicate_data(deark
*c
, lctx
*d
, struct tagset_type
*tgs
,
713 i64 tagindex
, i64 tagdataoffset
, i64 tagdatalen
,
718 for(k
=0; k
<tagindex
&& k
<tgs
->num_tags
; k
++) {
719 if(tgs
->tags_seen
[k
].offset
==tagdataoffset
&&
720 tgs
->tags_seen
[k
].len
==tagdatalen
)
731 // tagdataoffset is relative to tgs->data_area_pos
732 static void do_tag_data(deark
*c
, lctx
*d
, struct tagset_type
*tgs
, i64 tagindex
,
733 i64 tagdataoffset
, i64 tagdatalen
)
735 struct de_fourcc tagtype4cc
;
736 struct typedec_params tdp
;
737 const struct datatypeinfo
*dti
;
741 if(tagindex
>= tgs
->num_tags
) return;
742 if(tagdatalen
<1) goto done
;
743 if(tagdataoffset
+tagdatalen
> tgs
->data_area_len
) {
744 de_err(c
, "Tag #%d data exceeds its bounds", (int)tagindex
);
747 if(is_duplicate_data(c
, d
, tgs
, tagindex
, tagdataoffset
, tagdatalen
, &idx_of_dup
)) {
748 de_dbg(c
, "[data is a duplicate of tag #%d]", (int)idx_of_dup
);
752 if(tagdatalen
<4) goto done
;
754 dbuf_read_fourcc(c
->infile
, tgs
->data_area_pos
+tagdataoffset
, &tagtype4cc
, 4, 0x0);
755 dti
= lookup_datatypeinfo(tagtype4cc
.id
);
756 if(dti
&& dti
->name
) dtname
=dti
->name
;
758 de_dbg(c
, "data type: %s (%s)",
759 format_4cc_dbgstr(&tagtype4cc
, d
->tmpbuf1
, sizeof(d
->tmpbuf1
), 0x0), dtname
);
761 de_zeromem(&tdp
, sizeof(struct typedec_params
));
764 tdp
.pos1
= tgs
->data_area_pos
+tagdataoffset
;
765 tdp
.len
= tagdatalen
;
766 tdp
.type_id
= tagtype4cc
.id
;
768 if(dti
&& dti
->dtdfn
) {
771 else if(c
->debug_level
>=2) {
772 typedec_hexdump(c
, &tdp
);
776 tgs
->tags_seen
[tagindex
].offset
= tagdataoffset
;
777 tgs
->tags_seen
[tagindex
].len
= tagdatalen
;
780 static void do_main_tag(deark
*c
, lctx
*d
, struct tagset_type
*tgs
,
781 i64 tagindex
, i64 pos_in_tagtable
)
783 struct de_fourcc tag4cc
;
784 const struct taginfo
*ti
;
789 dbuf_read_fourcc(c
->infile
, pos_in_tagtable
, &tag4cc
, 4, 0x0);
790 tagdataoffset
= de_getu32be(pos_in_tagtable
+4);
791 tagdatalen
= de_getu32be(pos_in_tagtable
+8);
792 ti
= lookup_taginfo(d
, tag4cc
.id
);
797 de_dbg(c
, "tag #%d %s (%s) tpos=%"I64_FMT
" dpos=%"I64_FMT
" dlen=%"I64_FMT
, (int)tagindex
,
798 format_4cc_dbgstr(&tag4cc
, d
->tmpbuf1
, sizeof(d
->tmpbuf1
), 0x0), tname
,
799 pos_in_tagtable
, tgs
->data_area_pos
+tagdataoffset
, tagdatalen
);
802 do_tag_data(c
, d
, tgs
, tagindex
, tagdataoffset
, tagdatalen
);
803 de_dbg_indent(c
, -1);
806 static void do_read_main_tags(deark
*c
, lctx
*d
, i64 pos1
)
809 struct tagset_type
*tgs
= NULL
;
811 de_dbg(c
, "tag table at %"I64_FMT
, pos1
);
814 tgs
= de_malloc(c
, sizeof(struct tagset_type
));
815 tgs
->data_area_pos
= 0;
816 if(d
->profile_size
!=0) {
817 tgs
->data_area_len
= de_min_int(d
->profile_size
, c
->infile
->len
);
820 tgs
->data_area_len
= c
->infile
->len
;
822 tgs
->num_tags
= de_getu32be(pos1
);
823 de_dbg(c
, "number of tags: %d", (int)tgs
->num_tags
);
824 if(tgs
->num_tags
>MAX_TAGS_PER_TAGSET
) {
825 de_err(c
, "Invalid or excessive number of tags: %d", (int)tgs
->num_tags
);
828 de_dbg(c
, "expected start of data segment: %d", (int)(pos1
+4+12*tgs
->num_tags
));
830 // Make a place to record some information about each tag we encounter in the table.
831 tgs
->tags_seen
= de_mallocarray(c
, tgs
->num_tags
, sizeof(struct tag_seen_type
));
833 for(tagindex
=0; tagindex
<tgs
->num_tags
; tagindex
++) {
834 do_main_tag(c
, d
, tgs
, tagindex
, pos1
+4+12*tagindex
);
838 destroy_tagset(c
, tgs
);
839 de_dbg_indent(c
, -1);
842 static void de_run_iccprofile(deark
*c
, de_module_params
*mparams
)
847 d
= de_malloc(c
, sizeof(lctx
));
850 do_read_header(c
, d
, pos
);
852 do_read_main_tags(c
, d
, pos
);
857 static int de_identify_iccprofile(deark
*c
)
859 if(!dbuf_memcmp(c
->infile
, 36, "acsp", 4))
864 void de_module_iccprofile(deark
*c
, struct deark_module_info
*mi
)
866 mi
->id
= "iccprofile";
867 mi
->desc
= "ICC profile";
868 mi
->run_fn
= de_run_iccprofile
;
869 mi
->identify_fn
= de_identify_iccprofile
;