1 // This file is part of Deark.
2 // Copyright (C) 2019 Jason Summers
3 // See the file COPYING for terms of use.
6 // (AXS File Concatenation Protocol)
8 #include <deark-config.h>
9 #include <deark-private.h>
10 #include <deark-fmtutil.h>
11 DE_DECLARE_MODULE(de_module_afcp
);
13 #define CODE_IPTC 0x49505443U
14 #define CODE_Nail 0x4e61696cU
15 #define CODE_PrVw 0x50725677U
28 struct de_fourcc id4cc
;
31 static int do_afcprec_thumbnail(deark
*c
, struct afcpctx
*d
, struct afcprecord
*ar
)
37 struct de_fourcc imgtype
;
42 if(ar
->dlen
<10) goto done
;
43 recver
= (unsigned int)dbuf_getu16x(c
->infile
, pos
, d
->is_le
);
45 de_dbg(c
, "record version: %u", recver
);
46 if(recver
!=1) goto done
;
48 w
= dbuf_getu16x(c
->infile
, pos
, d
->is_le
);
50 h
= dbuf_getu16x(c
->infile
, pos
, d
->is_le
);
52 de_dbg(c
, "reported dimensions: %d"DE_CHAR_TIMES
"%d", (int)w
, (int)h
);
53 // Nothing after this point is adequately documented in the spec.
55 dbuf_read_fourcc(c
->infile
, pos
, &imgtype
, 4, 0);
57 de_dbg(c
, "image type: '%s'", imgtype
.id_dbgstr
);
59 ret
= dbuf_search(c
->infile
, (const u8
*)"\xff\xd8\xff", 3, pos
,
60 ar
->dpos
+ ar
->dlen
- pos
, &foundpos
);
62 de_dbg(c
, "[failed to find thumbnail image data]");
65 if(ar
->id4cc
.id
==CODE_PrVw
) ext
= "afcppreview.jpg";
66 else ext
= "afcpthumb.jpg";
67 dbuf_create_file_from_slice(c
->infile
, foundpos
, ar
->dpos
+ ar
->dlen
- foundpos
, ext
,
68 NULL
, DE_CREATEFLAG_IS_AUX
);
75 static void do_afcp_record(deark
*c
, struct afcpctx
*d
, i64 pos
)
77 struct afcprecord
*ar
= NULL
;
79 ar
= de_malloc(c
, sizeof(struct afcprecord
));
80 dbuf_read_fourcc(c
->infile
, pos
, &ar
->id4cc
, 4, 0);
81 de_dbg(c
, "id: '%s'", ar
->id4cc
.id_dbgstr
);
82 ar
->dlen
= dbuf_getu32x(c
->infile
, pos
+4, d
->is_le
);
83 de_dbg(c
, "dlen: %"I64_FMT
, ar
->dlen
);
84 ar
->dpos
= dbuf_getu32x(c
->infile
, pos
+8, d
->is_le
);
85 de_dbg(c
, "dpos: %"I64_FMT
, ar
->dpos
);
86 if(ar
->dpos
<d
->start_tag_offs
|| ar
->dpos
+ar
->dlen
>d
->endpos
) goto done
;
88 if(ar
->id4cc
.id
==CODE_IPTC
) {
89 de_dbg(c
, "IPTC-IIM data");
91 fmtutil_handle_iptc(c
, c
->infile
, ar
->dpos
, ar
->dlen
, 0x0);
94 else if(ar
->id4cc
.id
==CODE_Nail
|| ar
->id4cc
.id
==CODE_PrVw
) {
95 do_afcprec_thumbnail(c
, d
, ar
);
98 if(c
->debug_level
>=2) {
99 de_dbg_hexdump(c
, c
->infile
, ar
->dpos
, ar
->dlen
, 256, NULL
, 0x1);
106 static void do_afcp_recordlist(deark
*c
, struct afcpctx
*d
)
108 int saved_indent_level
;
112 de_dbg_indent_save(c
, &saved_indent_level
);
113 pos
= d
->start_tag_offs
+ 12;
114 de_dbg(c
, "record list at %"I64_FMT
, pos
);
117 for(i
=0; i
<d
->record_count
; i
++) {
118 if(i
+12 > d
->endpos
) goto done
;
119 de_dbg(c
, "record[%d] at %"I64_FMT
, (int)i
, pos
);
121 do_afcp_record(c
, d
, pos
);
122 de_dbg_indent(c
, -1);
127 de_dbg_indent_restore(c
, saved_indent_level
);
130 static int do_afcp_starttagrec(deark
*c
, struct afcpctx
*d
)
133 int saved_indent_level
;
134 unsigned int version
;
136 de_dbg_indent_save(c
, &saved_indent_level
);
137 de_dbg(c
, "start tag record at %"I64_FMT
, d
->start_tag_offs
);
139 if(de_getbyte(d
->start_tag_offs
+3)!=d
->endian_code
) {
140 de_err(c
, "AFCP start tag not found at %"I64_FMT
, d
->start_tag_offs
);
143 version
= (unsigned int)dbuf_getu16x(c
->infile
, d
->start_tag_offs
+4, d
->is_le
);
144 de_dbg(c
, "version: %u", version
);
145 d
->record_count
= dbuf_getu16x(c
->infile
, d
->start_tag_offs
+6, d
->is_le
);
146 de_dbg(c
, "record count: %d", (int)d
->record_count
);
149 de_warn(c
, "Unexpected AFCP version number: %u", version
);
154 de_dbg_indent_restore(c
, saved_indent_level
);
158 static int do_afcp_eofrec(deark
*c
, struct afcpctx
*d
)
162 int saved_indent_level
;
164 de_dbg_indent_save(c
, &saved_indent_level
);
166 eofrecpos
= d
->endpos
- 12;
167 de_dbg(c
, "EOF record at %"I64_FMT
, eofrecpos
);
169 d
->endian_code
= de_getbyte(eofrecpos
+3);
170 if(d
->endian_code
=='*') d
->is_le
= 1;
171 else if(d
->endian_code
=='!') d
->is_le
= 0;
173 de_err(c
, "Invalid AFCP format");
176 de_dbg(c
, "is-little-endian: %d", d
->is_le
);
178 d
->start_tag_offs
= dbuf_getu32x(c
->infile
, eofrecpos
+4, d
->is_le
);
179 de_dbg(c
, "start tag offset: %"I64_FMT
, d
->start_tag_offs
);
183 de_dbg_indent_restore(c
, saved_indent_level
);
187 static void de_run_afcp(deark
*c
, de_module_params
*mparams
)
189 struct afcpctx
*d
= NULL
;
190 int saved_indent_level
;
192 de_dbg_indent_save(c
, &saved_indent_level
);
193 d
= de_malloc(c
, sizeof(struct afcpctx
));
194 d
->endpos
= c
->infile
->len
;
195 de_dbg(c
, "AFCP data, ending at %"I64_FMT
, d
->endpos
);
198 if(!do_afcp_eofrec(c
, d
)) goto done
;
199 if(!do_afcp_starttagrec(c
, d
)) goto done
;
200 do_afcp_recordlist(c
, d
);
204 de_dbg_indent_restore(c
, saved_indent_level
);
207 void de_module_afcp(deark
*c
, struct deark_module_info
*mi
)
210 mi
->desc
= "AXS File Concatenation Protocol";
211 mi
->run_fn
= de_run_afcp
;
212 mi
->flags
|= DE_MODFLAG_HIDDEN
;