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_sf32(deark
*c
, struct typedec_params
*p
)
140 i64 pos
= p
->pos1
+ 8;
144 count
= (p
->len
-8)/4;
145 for(i
=0; i
<count
&& i
<64; i
++) {
146 val
= read_s15Fixed16Number(c
->infile
, pos
);
148 de_dbg(c
, "arr[%d] = %.5f", (int)i
, val
);
152 static void typedec_XYZ(deark
*c
, struct typedec_params
*p
)
158 xyz_count
= (p
->len
-8)/12;
159 for(k
=0; k
<xyz_count
; k
++) {
162 read_XYZ(c
, p
->pos1
+8+12*k
, &xyz
);
163 de_dbg(c
, "XYZ[%d]: %.5f, %.5f, %.5f", (int)k
,
164 xyz
.v
[0], xyz
.v
[1], xyz
.v
[2]);
168 static void typedec_text(deark
*c
, struct typedec_params
*p
)
170 de_ucstring
*s
= NULL
;
171 i64 textlen
= p
->len
-8;
174 if(textlen
<0) goto done
;
176 if(p
->type_id
==0x75746638U
) {
177 enc
= DE_ENCODING_UTF8
;
180 enc
= DE_ENCODING_ASCII
;
183 s
= ucstring_create(c
);
184 dbuf_read_to_ucstring_n(c
->infile
, p
->pos1
+8, textlen
, DE_DBG_MAX_STRLEN
,
186 ucstring_truncate_at_NUL(s
);
187 de_dbg(c
, "text: \"%s\"", ucstring_getpsz(s
));
194 static void typedec_desc(deark
*c
, struct typedec_params
*p
)
196 de_ucstring
*s
= NULL
;
197 i64 invdesclen
, uloclen
;
201 de_encoding encoding
;
204 if(p
->len
<12) goto done
;
208 // ASCII invariant description
209 invdesclen
= de_getu32be(pos
); // invariant desc. len, including NUL byte
211 s
= ucstring_create(c
);
212 dbuf_read_to_ucstring_n(c
->infile
, pos
, invdesclen
, DE_DBG_MAX_STRLEN
,
213 s
, 0, DE_ENCODING_ASCII
);
214 ucstring_truncate_at_NUL(s
);
215 de_dbg(c
, "invariant desc.: \"%s\"", ucstring_getpsz(s
));
217 if(pos
>= p
->pos1
+p
->len
) goto done
;
219 // Unicode localizable description
222 langcode
= de_getu32be(pos
);
225 uloclen
= de_getu32be(pos
);
229 // TODO: How to interpret the language code?
230 de_dbg(c
, "language code: %d", (int)langcode
);
234 bytes_to_read
= uloclen
*2;
236 encoding
= DE_ENCODING_UTF16BE
;
239 // Check for a BOM. The spec doesn't say much about the format of
240 // Unicode text in 'desc' tags. It does say that "All profile data must
241 // be encoded as big-endian", so maybe that means UTF-16LE is not
242 // allowed. In practice, some strings begin with a BOM.
243 firstchar
= (i32
)de_getu16be(lstrstartpos
);
244 if(firstchar
==0xfeff) { // UTF-16BE BOM
248 else if(firstchar
==0xffef) { // UTF-16LE BOM
251 encoding
= DE_ENCODING_UTF16LE
;
255 dbuf_read_to_ucstring_n(c
->infile
, lstrstartpos
, bytes_to_read
, DE_DBG_MAX_STRLEN
*2,
257 ucstring_truncate_at_NUL(s
);
259 de_dbg(c
, "localizable desc.: \"%s\"", ucstring_getpsz(s
));
262 if(pos
>= p
->pos1
+p
->len
) goto done
;
264 // Macintosh localizable description
271 static void do_mluc_record(deark
*c
, lctx
*d
, i64 tagstartpos
,
272 i64 pos
, i64 recsize
)
274 de_ucstring
*s
= NULL
;
278 s
= ucstring_create(c
);
280 dbuf_read_to_ucstring(c
->infile
, pos
, 2, s
, 0, DE_ENCODING_ASCII
);
281 de_dbg(c
, "language code: '%s'", ucstring_getpsz(s
));
284 dbuf_read_to_ucstring(c
->infile
, pos
+2, 2, s
, 0, DE_ENCODING_ASCII
);
285 de_dbg(c
, "country code: '%s'", ucstring_getpsz(s
));
288 string_len
= de_getu32be(pos
+4);
289 string_offset
= de_getu32be(pos
+8);
290 de_dbg(c
, "string offset=%d+%d, len=%d bytes", (int)tagstartpos
,
291 (int)string_offset
, (int)string_len
);
293 dbuf_read_to_ucstring_n(c
->infile
, tagstartpos
+string_offset
, string_len
, DE_DBG_MAX_STRLEN
*2,
294 s
, 0, DE_ENCODING_UTF16BE
);
295 ucstring_truncate_at_NUL(s
);
296 de_dbg(c
, "string: \"%s\"", ucstring_getpsz(s
));
301 static void typedec_mluc(deark
*c
, struct typedec_params
*p
)
306 i64 rec_array_startpos
;
309 if(p
->len
<12) goto done
;
312 num_recs
= de_getu32be(pos
);
313 de_dbg(c
, "number of records: %d", (int)num_recs
);
316 recsize
= de_getu32be(pos
);
317 de_dbg(c
, "record size: %d", (int)recsize
);
318 if(recsize
<12) goto done
;
321 rec_array_startpos
= pos
;
323 for(rec
=0; rec
<num_recs
; rec
++) {
324 if(rec_array_startpos
+rec
*recsize
> p
->pos1
+p
->len
) break;
326 de_dbg(c
, "record #%d at %d", (int)rec
, (int)pos
);
328 do_mluc_record(c
, p
->d
, p
->pos1
, pos
, recsize
);
329 de_dbg_indent(c
, -1);
337 static void do_tag_data(deark
*c
, lctx
*d
, struct tagset_type
*tgs
, i64 tagindex
,
338 i64 tagdataoffset
, i64 tagdatalen
);
340 static void typedec_tagArray_tagStruct(deark
*c
, struct typedec_params
*p
)
342 struct de_fourcc ty4cc
;
343 struct de_fourcc tmp4cc
;
345 struct tagset_type
*tgs
= NULL
;
346 i64 pos
= p
->pos1
+ 8;
347 i64 endpos
= p
->pos1
+ p
->len
;
348 int is_struct
= (p
->type_id
== 0x74737472U
);
351 const char *struct_name
;
352 int saved_indent_level
;
354 de_dbg_indent_save(c
, &saved_indent_level
);
355 de_zeromem(&tmp4cc
, sizeof(struct de_fourcc
));
357 tgs
= de_malloc(c
, sizeof(struct tagset_type
));
358 tgs
->data_area_pos
= p
->pos1
;
359 tgs
->data_area_len
= p
->len
;
360 tgs
->nesting_level
= p
->tgs
->nesting_level
+1;
361 if(tgs
->nesting_level
> MAX_NESTING_LEVEL
) goto done
;
364 array_item_size
= 12;
365 struct_name
= "struct";
369 struct_name
= "array";
372 dbuf_read_fourcc(c
->infile
, pos
, &ty4cc
, 4, 0x0);
374 de_dbg(c
, "%s type: %s", struct_name
,
375 format_4cc_dbgstr(&ty4cc
, d
->tmpbuf1
, sizeof(d
->tmpbuf1
), 0));
376 tgs
->num_tags
= de_getu32be_p(&pos
);
377 de_dbg(c
, "number of elements: %"I64_FMT
, tgs
->num_tags
);
378 if(tgs
->num_tags
>MAX_TAGS_PER_TAGSET
) {
379 de_err(c
, "Invalid or excessive number of elements: %u", (UI
)tgs
->num_tags
);
383 tgs
->tags_seen
= de_mallocarray(c
, tgs
->num_tags
, sizeof(struct tag_seen_type
));
385 for(i
=0; i
<tgs
->num_tags
; i
++) {
386 i64 elem_pos_rel
, elem_pos_abs
;
390 if(pos
+array_item_size
> endpos
) break;
393 dbuf_read_fourcc(c
->infile
, pos
, &tmp4cc
, 4, 0x0);
397 elem_pos_rel
= de_getu32be_p(&pos
);
398 elem_dlen
= de_getu32be_p(&pos
);
399 elem_pos_abs
= p
->pos1
+ elem_pos_rel
;
402 format_4cc_dbgstr(&tmp4cc
, d
->tmpbuf1
, sizeof(d
->tmpbuf1
), 0);
403 de_snprintf(d
->tmpbuf2
, sizeof(d
->tmpbuf2
), " %s", d
->tmpbuf1
);
406 d
->tmpbuf2
[0] = '\0';
408 de_dbg(c
, "elem #%d%s tpos=%"I64_FMT
" dpos=%"I64_FMT
" (%"I64_FMT
"+%"I64_FMT
"), dlen=%"I64_FMT
,
409 (int)i
, d
->tmpbuf2
, tpos
, elem_pos_abs
, p
->pos1
, elem_pos_rel
, elem_dlen
);
412 do_tag_data(c
, d
, tgs
, i
, elem_pos_rel
, elem_dlen
);
413 de_dbg_indent(c
, -1);
417 destroy_tagset(c
, tgs
);
418 de_dbg_indent_restore(c
, saved_indent_level
);
421 static void do_dict_string(deark
*c
, struct typedec_params
*p
, i64 itempos
, const char *itemname
)
423 de_ucstring
*s
= NULL
;
424 i64 dpos_rel
, dpos_abs
, dlen
;
426 dpos_rel
= de_getu32be(itempos
);
427 dpos_abs
= p
->pos1
+ dpos_rel
;
428 dlen
= de_getu32be(itempos
+4);
429 if(dpos_abs
+ dlen
> p
->pos1
+ p
->len
) goto done
;
430 s
= ucstring_create(c
);
431 dbuf_read_to_ucstring_n(c
->infile
, dpos_abs
, dlen
, DE_DBG_MAX_STRLEN
, s
, 0, DE_ENCODING_UTF16BE
);
432 de_dbg(c
, "%s: dpos=%"I64_FMT
", dlen=%"I64_FMT
", string=\"%s\"", itemname
, dpos_abs
, dlen
,
433 ucstring_getpsz_d(s
));
439 static void typedec_dict(deark
*c
, struct typedec_params
*p
)
441 i64 pos
= p
->pos1
+ 8;
446 if(p
->len
<16) goto done
;
448 nrec
= de_getu32be_p(&pos
);
449 de_dbg(c
, "num records: %u", (UI
)nrec
);
450 reclen
= de_getu32be_p(&pos
);
451 de_dbg(c
, "rec len: %u", (UI
)reclen
);
452 if(reclen
!=16 && reclen
!=24 && reclen
!=32) goto done
;
453 if(pos
+nrec
*reclen
> p
->pos1
+ p
->len
) goto done
;
454 if(nrec
>MAX_TAGS_PER_TAGSET
) goto done
;
456 for(i
=0; i
<nrec
; i
++) {
457 do_dict_string(c
, p
, pos
, "name");
459 do_dict_string(c
, p
, pos
, "value");
462 do_dict_string(c
, p
, pos
, "display name");
466 do_dict_string(c
, p
, pos
, "display value");
475 static void typedec_hexdump(deark
*c
, struct typedec_params
*p
)
480 rsvd
= (UI
)de_getu32be(p
->pos1
+4);
481 de_dbg(c
, "reserved/etc.: 0x%08x", rsvd
);
482 de_dbg_hexdump(c
, c
->infile
, p
->pos1
+8, p
->len
-8, 256, NULL
, 0x1);
485 static const struct datatypeinfo datatypeinfo_arr
[] = {
486 { 0x58595a20U
, 0, "XYZ", typedec_XYZ
}, // XYZ
487 { 0x62666420U
, 0, "ucrbg", NULL
}, // bfd
488 { 0x6368726dU
, 0, "chromaticity", NULL
}, // chrm
489 { 0x636c726fU
, 0, "colorantOrder", NULL
}, // clro
490 { 0x636c7274U
, 0, "colorantTable", NULL
}, // clrt
491 { 0x63726469U
, 0, "crdInfo", NULL
}, // crdi
492 { 0x63757266U
, 0, "segmentedCurve", NULL
}, // curf
493 { 0x63757276U
, 0, "curve", NULL
}, // curv
494 { 0x64657363U
, 0, "textDescription", typedec_desc
}, // desc
495 { 0x64617461U
, 0, "data", NULL
}, // data
496 { 0x64657673U
, 0, "deviceSettings", NULL
}, // devs
497 { 0x64696374U
, 0, "dictionary array", typedec_dict
}, // dict
498 { 0x6474696dU
, 0, "dateTime", NULL
}, // dtim
499 { 0x666c3136U
, 0, "float16Array", NULL
}, // fl16
500 { 0x666c3332U
, 0, "float32Array", NULL
}, // fl32
501 { 0x666c3634U
, 0, "float64Array", NULL
}, // fl64
502 { 0x67626420U
, 0, "gamutBoundaryDescription", NULL
}, // gbd
503 { 0x6d414220U
, 0, "lutAToB", NULL
}, // mAB
504 { 0x6d424120U
, 0, "lutBToA", NULL
}, // mBA
505 { 0x6d656173U
, 0, "measurement", NULL
}, // meas
506 { 0x6d667431U
, 0, "lut8", NULL
}, // mft1
507 { 0x6d667432U
, 0, "lut16", NULL
}, // mft2
508 { 0x6d6c7563U
, 0, "multiLocalizedUnicode", typedec_mluc
}, // mluc
509 { 0x6d706574U
, 0, "multiProcessElements", NULL
}, // mpet
510 { 0x6e636c32U
, 0, "namedColor2", NULL
}, // ncl2
511 { 0x6e636f6cU
, 0, "namedColor", NULL
}, // ncol
512 { 0x70617261U
, 0, "parametricCurve", NULL
}, // para
513 { 0x70736571U
, 0, "profileSequenceDesc", NULL
}, // pseq
514 { 0x70736964U
, 0, "profileSequenceIdentifier", NULL
}, // psid
515 { 0x72637332U
, 0, "responseCurveSet16", NULL
}, // rcs2
516 { 0x73663332U
, 0, "s15Fixed16Array", typedec_sf32
}, // sf32
517 { 0x7363726eU
, 0, "screening", NULL
}, // scrn
518 { 0x73696720U
, 0, "signature", NULL
}, // sig
519 { 0x7376636eU
, 0, "spectralViewingConditions", NULL
}, // svcn
520 { 0x74617279U
, 0, "tagArray", typedec_tagArray_tagStruct
}, // tary
521 { 0x74657874U
, 0, "text", typedec_text
}, // text
522 { 0x74737472U
, 0, "tagStruct", typedec_tagArray_tagStruct
}, // tstr
523 { 0x75663332U
, 0, "u16Fixed16Array", NULL
}, // uf32
524 { 0x75693038U
, 0, "uInt8Array", NULL
}, // ui08
525 { 0x75693136U
, 0, "uInt16Array", NULL
}, // ui16
526 { 0x75693332U
, 0, "uInt32Array", NULL
}, // ui32
527 { 0x75693634U
, 0, "uInt64Array", NULL
}, // ui64
528 { 0x75743136U
, 0, "utf16", NULL
}, // ut16
529 { 0x75746638U
, 0, "utf8", typedec_text
}, // utf8
530 { 0x76636774U
, 0, "Video Card Gamma Type", NULL
}, // vcgt (Apple)
531 { 0x76696577U
, 0, "viewingConditions", NULL
} // view
534 static const struct taginfo taginfo_arr
[] = {
535 { 0x41324230U
, 0, "AToB0", NULL
}, // A2B0
536 { 0x41324231U
, 0, "AToB1", NULL
}, // A2B1
537 { 0x41324232U
, 0, "AToB2", NULL
}, // A2B2
538 { 0x41324233U
, 0, "AToB3", NULL
}, // A2B3
539 { 0x42324130U
, 0, "BToA0", NULL
}, // B2A0
540 { 0x42324131U
, 0, "BToA1", NULL
}, // B2A1
541 { 0x42324132U
, 0, "BToA2", NULL
}, // B2A2
542 { 0x42324133U
, 0, "BToA3", NULL
}, // B2A3
543 { 0x42324430U
, 0, "BToD0", NULL
}, // B2D0
544 { 0x42324431U
, 0, "BToD1", NULL
}, // B2D1
545 { 0x42324432U
, 0, "BToD2", NULL
}, // B2D2
546 { 0x42324433U
, 0, "BToD3", NULL
}, // B2D3
547 { 0x44324230U
, 0, "DToB0", NULL
}, // D2B0
548 { 0x44324231U
, 0, "DToB1", NULL
}, // D2B1
549 { 0x44324232U
, 0, "DToB2", NULL
}, // D2B2
550 { 0x44324233U
, 0, "DToB3", NULL
}, // D2B3
551 { 0x62545243U
, 0, "blueTRC", NULL
}, // bTRC
552 { 0x6258595aU
, 0x1, "blueColorant", NULL
}, // bXYZ
553 { 0x6258595aU
, 0x2, "blueMatrixColumn", NULL
}, // bXYZ
554 { 0x62666420U
, 0, "ucrbg", NULL
}, // bfd
555 { 0x626b7074U
, 0, "mediaBlackPoint", NULL
}, // bkpt
556 { 0x63327370U
, 0, "customToStandardPcc", NULL
}, // c2sp
557 { 0x63616c74U
, 0, "calibrationDateTime", NULL
}, // calt
558 { 0x63657074U
, 0, "colorEncodingParams", NULL
}, // cept
559 { 0x63686164U
, 0, "chromaticAdaptation", NULL
}, // chad
560 { 0x6368726dU
, 0, "chromaticity", NULL
}, // chrm
561 { 0x63696973U
, 0, "colorimetricIntentImageState", NULL
}, // ciis
562 { 0x636c6f74U
, 0, "colorantTableOut", NULL
}, // clot
563 { 0x636c726fU
, 0, "colorantOrder", NULL
}, // clro
564 { 0x636c7274U
, 0, "colorantTable", NULL
}, // clrt
565 { 0x63707274U
, 0, "copyright", NULL
}, // cprt
566 { 0x63726469U
, 0, "crdInfo", NULL
}, // crdi
567 { 0x63736e6dU
, 0, "colorSpaceName", NULL
}, // csnm
568 { 0x64657363U
, 0, "profileDescription", NULL
}, // desc
569 { 0x64657673U
, 0, "deviceSettings", NULL
}, // devs
570 { 0x646d6464U
, 0, "deviceModelDesc", NULL
}, // dmdd
571 { 0x646d6e64U
, 0, "deviceMfgDesc", NULL
}, // dmnd
572 { 0x67616d74U
, 0, "gamut", NULL
}, // gamt
573 { 0x67626431U
, 0, "gamutBoundaryDescription1", NULL
}, // gbd1
574 { 0x67545243U
, 0, "greenTRC", NULL
}, // gTRC
575 { 0x6758595aU
, 0x1, "greenColorant", NULL
}, // gXYZ
576 { 0x6758595aU
, 0x2, "greenMatrixColumn", NULL
}, // gXYZ
577 { 0x6b545243U
, 0, "grayTRC", NULL
}, // kTRC
578 { 0x6c756d69U
, 0, "luminance", NULL
}, // lumi
579 { 0x6d656173U
, 0, "measurement", NULL
}, // meas
580 { 0x6e636c32U
, 0, "namedColor2", NULL
}, // ncl2
581 { 0x6e636f6cU
, 0, "namedColor", NULL
}, // ncol
582 { 0x70726530U
, 0, "preview0", NULL
}, // pre0
583 { 0x70726531U
, 0, "preview1", NULL
}, // pre1
584 { 0x70726532U
, 0, "preview2", NULL
}, // pre2
585 { 0x70733269U
, 0, "ps2RenderingIntent", NULL
}, // ps2i
586 { 0x70733273U
, 0, "ps2CSA", NULL
}, // ps2s
587 { 0x70736430U
, 0, "ps2CRD0", NULL
}, // psd0
588 { 0x70736431U
, 0, "ps2CRD1", NULL
}, // psd1
589 { 0x70736432U
, 0, "ps2CRD2", NULL
}, // psd2
590 { 0x70736433U
, 0, "ps2CRD3", NULL
}, // psd3
591 { 0x70736571U
, 0, "profileSequenceDesc", NULL
}, // pseq
592 { 0x70736964U
, 0, "profileSequenceIdentifier", NULL
}, // psid
593 { 0x72545243U
, 0, "redTRC", NULL
}, // rTRC
594 { 0x7258595aU
, 0x1, "redColorant", NULL
}, // rXYZ
595 { 0x7258595aU
, 0x2, "redMatrixColumn", NULL
}, // rXYZ
596 { 0x72657370U
, 0, "outputResponse", NULL
}, // resp
597 { 0x72666e6dU
, 0, "referenceName", NULL
}, // rfnm
598 { 0x72696730U
, 0, "perceptualRenderingIntentGamut", NULL
}, // rig0
599 { 0x72696732U
, 0, "saturationRenderingIntentGamut", NULL
}, // rig2
600 { 0x73326370U
, 0, "standardToCustomPcc", NULL
}, // s2cp
601 { 0x73637264U
, 0, "screeningDesc", NULL
}, // scrd
602 { 0x7363726eU
, 0, "screening", NULL
}, // scrn
603 { 0x7376636eU
, 0, "spectralViewingConditions", NULL
}, // svcn
604 { 0x74617267U
, 0, "charTarget", NULL
}, // targ
605 { 0x74656368U
, 0, "technology", NULL
}, // tech
606 { 0x76636774U
, 0, "Video Card Gamma Type", NULL
}, // vcgt (Apple)
607 { 0x76696577U
, 0, "viewingConditions", NULL
}, // view
608 { 0x76756564U
, 0, "viewingCondDesc", NULL
}, // vued
609 { 0x77747074U
, 0, "mediaWhitePoint", NULL
} // wtpt
612 static void do_read_header(deark
*c
, lctx
*d
, i64 pos
)
617 struct de_fourcc tmp4cc
;
622 de_dbg(c
, "header at %d", (int)pos
);
625 d
->profile_size
= de_getu32be(pos
+0);
626 de_dbg(c
, "profile size: %"I64_FMT
, d
->profile_size
);
628 dbuf_read_fourcc(c
->infile
, pos
+4, &tmp4cc
, 4, 0x0);
629 de_dbg(c
, "preferred CMM type: %s",
630 format_4cc_dbgstr(&tmp4cc
, d
->tmpbuf1
, sizeof(d
->tmpbuf1
), 0x3));
632 profile_ver_raw
= (u32
)de_getu32be(pos
+8);
633 d
->profile_ver_major
= 10*((profile_ver_raw
&0xf0000000U
)>>28) +
634 ((profile_ver_raw
&0x0f000000U
)>>24);
635 d
->profile_ver_minor
= (profile_ver_raw
&0x00f00000U
)>>20;
636 d
->profile_ver_bugfix
= (profile_ver_raw
&0x000f0000U
)>>16;
637 de_dbg(c
, "profile version: %u.%u.%u", d
->profile_ver_major
,
638 d
->profile_ver_minor
, d
->profile_ver_bugfix
);
640 dbuf_read_fourcc(c
->infile
, pos
+12, &tmp4cc
, 4, 0x0);
641 de_dbg(c
, "profile/device class: %s",
642 format_4cc_dbgstr(&tmp4cc
, d
->tmpbuf1
, sizeof(d
->tmpbuf1
), 0x1));
644 dbuf_read_fourcc(c
->infile
, pos
+16, &tmp4cc
, 4, 0x0);
646 if(d
->profile_ver_major
>=5) tmpflags
|= 0x2;
647 de_dbg(c
, "colour space: %s",
648 format_4cc_dbgstr(&tmp4cc
, d
->tmpbuf1
, sizeof(d
->tmpbuf1
), tmpflags
));
650 dbuf_read_fourcc(c
->infile
, pos
+20, &tmp4cc
, 4, 0x0);
652 if(d
->profile_ver_major
>=5) tmpflags
|= 0x2;
654 format_4cc_dbgstr(&tmp4cc
, d
->tmpbuf1
, sizeof(d
->tmpbuf1
), tmpflags
));
656 dbg_timestamp(c
, pos
+24, "creation time");
658 dbuf_read_fourcc(c
->infile
, pos
+36, &tmp4cc
, 4, 0x0);
659 de_dbg(c
, "file signature: %s",
660 format_4cc_dbgstr(&tmp4cc
, d
->tmpbuf1
, sizeof(d
->tmpbuf1
), 0x1));
662 dbuf_read_fourcc(c
->infile
, pos
+40, &tmp4cc
, 4, 0x0);
663 de_dbg(c
, "primary platform: %s",
664 format_4cc_dbgstr(&tmp4cc
, d
->tmpbuf1
, sizeof(d
->tmpbuf1
), 0x3));
666 // TODO: Decode profile flags
667 x
= de_getu32be(pos
+44);
668 de_dbg(c
, "profile flags: 0x%08x", (UI
)x
);
670 dbuf_read_fourcc(c
->infile
, pos
+48, &tmp4cc
, 4, 0x0);
671 de_dbg(c
, "device manufacturer: %s",
672 format_4cc_dbgstr(&tmp4cc
, d
->tmpbuf1
, sizeof(d
->tmpbuf1
), 0x3));
674 dbuf_read_fourcc(c
->infile
, pos
+52, &tmp4cc
, 4, 0x0);
675 de_dbg(c
, "device model: %s",
676 format_4cc_dbgstr(&tmp4cc
, d
->tmpbuf1
, sizeof(d
->tmpbuf1
), 0x3));
678 // TODO: Decode device attributes
679 xu
= dbuf_getu64be(c
->infile
, pos
+56);
680 de_dbg(c
, "device attribs: 0x%016"U64_FMTx
, xu
);
682 x
= de_getu32be(pos
+64);
684 case 0: name
="perceptual"; break;
685 case 1: name
="relative colorimetric"; break;
686 case 2: name
="saturation"; break;
687 case 3: name
="absolute colorimetric"; break;
688 default: name
="?"; break;
690 de_dbg(c
, "rendering intent: %d (%s)", (int)x
, name
);
692 read_XYZ(c
, pos
+68, &xyz
);
693 de_dbg(c
, "illuminant: %.5f, %.5f, %.5f", xyz
.v
[0], xyz
.v
[1], xyz
.v
[2]);
695 dbuf_read_fourcc(c
->infile
, pos
+80, &tmp4cc
, 4, 0x0);
696 de_dbg(c
, "profile creator: %s",
697 format_4cc_dbgstr(&tmp4cc
, d
->tmpbuf1
, sizeof(d
->tmpbuf1
), 0x3));
699 // TODO: pos=84-99 Profile ID
701 de_dbg_indent(c
, -1);
704 static const struct datatypeinfo
*lookup_datatypeinfo(u32 id
)
707 for(k
=0; k
<DE_ARRAYCOUNT(datatypeinfo_arr
); k
++) {
708 if(datatypeinfo_arr
[k
].id
== id
) {
709 return &datatypeinfo_arr
[k
];
715 static const struct taginfo
*lookup_taginfo(lctx
*d
, u32 id
)
718 for(k
=0; k
<DE_ARRAYCOUNT(taginfo_arr
); k
++) {
719 if((taginfo_arr
[k
].flags
& 0x1) && d
->profile_ver_major
>=4) continue;
720 if((taginfo_arr
[k
].flags
& 0x2) && d
->profile_ver_major
<4) continue;
721 if(taginfo_arr
[k
].id
== id
) {
722 return &taginfo_arr
[k
];
728 static int is_duplicate_data(deark
*c
, lctx
*d
, struct tagset_type
*tgs
,
729 i64 tagindex
, i64 tagdataoffset
, i64 tagdatalen
,
734 for(k
=0; k
<tagindex
&& k
<tgs
->num_tags
; k
++) {
735 if(tgs
->tags_seen
[k
].offset
==tagdataoffset
&&
736 tgs
->tags_seen
[k
].len
==tagdatalen
)
747 // tagdataoffset is relative to tgs->data_area_pos
748 static void do_tag_data(deark
*c
, lctx
*d
, struct tagset_type
*tgs
, i64 tagindex
,
749 i64 tagdataoffset
, i64 tagdatalen
)
751 struct de_fourcc tagtype4cc
;
752 struct typedec_params tdp
;
753 const struct datatypeinfo
*dti
;
757 if(tagindex
>= tgs
->num_tags
) return;
758 if(tagdatalen
<1) goto done
;
759 if(tagdataoffset
+tagdatalen
> tgs
->data_area_len
) {
760 de_err(c
, "Tag #%d data exceeds its bounds", (int)tagindex
);
763 if(is_duplicate_data(c
, d
, tgs
, tagindex
, tagdataoffset
, tagdatalen
, &idx_of_dup
)) {
764 de_dbg(c
, "[data is a duplicate of tag #%d]", (int)idx_of_dup
);
768 if(tagdatalen
<4) goto done
;
770 dbuf_read_fourcc(c
->infile
, tgs
->data_area_pos
+tagdataoffset
, &tagtype4cc
, 4, 0x0);
771 dti
= lookup_datatypeinfo(tagtype4cc
.id
);
772 if(dti
&& dti
->name
) dtname
=dti
->name
;
774 de_dbg(c
, "data type: %s (%s)",
775 format_4cc_dbgstr(&tagtype4cc
, d
->tmpbuf1
, sizeof(d
->tmpbuf1
), 0x0), dtname
);
777 de_zeromem(&tdp
, sizeof(struct typedec_params
));
780 tdp
.pos1
= tgs
->data_area_pos
+tagdataoffset
;
781 tdp
.len
= tagdatalen
;
782 tdp
.type_id
= tagtype4cc
.id
;
784 if(dti
&& dti
->dtdfn
) {
787 else if(c
->debug_level
>=2) {
788 typedec_hexdump(c
, &tdp
);
792 tgs
->tags_seen
[tagindex
].offset
= tagdataoffset
;
793 tgs
->tags_seen
[tagindex
].len
= tagdatalen
;
796 static void do_main_tag(deark
*c
, lctx
*d
, struct tagset_type
*tgs
,
797 i64 tagindex
, i64 pos_in_tagtable
)
799 struct de_fourcc tag4cc
;
800 const struct taginfo
*ti
;
805 dbuf_read_fourcc(c
->infile
, pos_in_tagtable
, &tag4cc
, 4, 0x0);
806 tagdataoffset
= de_getu32be(pos_in_tagtable
+4);
807 tagdatalen
= de_getu32be(pos_in_tagtable
+8);
808 ti
= lookup_taginfo(d
, tag4cc
.id
);
813 de_dbg(c
, "tag #%d %s (%s) tpos=%"I64_FMT
" dpos=%"I64_FMT
" dlen=%"I64_FMT
, (int)tagindex
,
814 format_4cc_dbgstr(&tag4cc
, d
->tmpbuf1
, sizeof(d
->tmpbuf1
), 0x0), tname
,
815 pos_in_tagtable
, tgs
->data_area_pos
+tagdataoffset
, tagdatalen
);
818 do_tag_data(c
, d
, tgs
, tagindex
, tagdataoffset
, tagdatalen
);
819 de_dbg_indent(c
, -1);
822 static void do_read_main_tags(deark
*c
, lctx
*d
, i64 pos1
)
825 struct tagset_type
*tgs
= NULL
;
827 de_dbg(c
, "tag table at %"I64_FMT
, pos1
);
830 tgs
= de_malloc(c
, sizeof(struct tagset_type
));
831 tgs
->data_area_pos
= 0;
832 if(d
->profile_size
!=0) {
833 tgs
->data_area_len
= de_min_int(d
->profile_size
, c
->infile
->len
);
836 tgs
->data_area_len
= c
->infile
->len
;
838 tgs
->num_tags
= de_getu32be(pos1
);
839 de_dbg(c
, "number of tags: %d", (int)tgs
->num_tags
);
840 if(tgs
->num_tags
>MAX_TAGS_PER_TAGSET
) {
841 de_err(c
, "Invalid or excessive number of tags: %d", (int)tgs
->num_tags
);
844 de_dbg(c
, "expected start of data segment: %d", (int)(pos1
+4+12*tgs
->num_tags
));
846 // Make a place to record some information about each tag we encounter in the table.
847 tgs
->tags_seen
= de_mallocarray(c
, tgs
->num_tags
, sizeof(struct tag_seen_type
));
849 for(tagindex
=0; tagindex
<tgs
->num_tags
; tagindex
++) {
850 do_main_tag(c
, d
, tgs
, tagindex
, pos1
+4+12*tagindex
);
854 destroy_tagset(c
, tgs
);
855 de_dbg_indent(c
, -1);
858 static void de_run_iccprofile(deark
*c
, de_module_params
*mparams
)
863 d
= de_malloc(c
, sizeof(lctx
));
866 do_read_header(c
, d
, pos
);
868 do_read_main_tags(c
, d
, pos
);
873 static int de_identify_iccprofile(deark
*c
)
875 if(!dbuf_memcmp(c
->infile
, 36, "acsp", 4))
880 void de_module_iccprofile(deark
*c
, struct deark_module_info
*mi
)
882 mi
->id
= "iccprofile";
883 mi
->desc
= "ICC profile";
884 mi
->run_fn
= de_run_iccprofile
;
885 mi
->identify_fn
= de_identify_iccprofile
;