bmp: Rewrote the RLE decompressor
[deark.git] / modules / os2ea.c
blob952907d1df40864a2b81022bf989ab34533c3c1b
1 // This file is part of Deark.
2 // Copyright (C) 2021 Jason Summers
3 // See the file COPYING for terms of use.
5 // OS Extended Attributes, including "EA DATA. SF" files
7 #include <deark-private.h>
8 DE_DECLARE_MODULE(de_module_ea_data);
10 struct easector_ctx {
11 i64 ea_data_len;
12 de_ucstring *fn;
15 struct eadata_ctx {
16 de_encoding input_encoding;
17 UI createflags_for_icons;
18 i64 bytes_per_cluster;
19 de_ucstring *base_filename_ref; // May be NULL. Do not free.
20 struct easector_ctx *cur_md; // May be NULL. Do not free.
23 static int eadata_is_ea_sector_at_offset(deark *c, struct eadata_ctx *d, i64 pos, int strictmode)
25 u8 b;
27 if((UI)de_getu16be(pos)!=0x4541) return 0;
28 if(strictmode) {
29 if((UI)de_getu32be(pos+4)!=0) return 0;
30 b = de_getbyte(pos+8);
31 if(b<32) return 0;
32 if((UI)de_getu32be(pos+22)!=0) return 0;
34 return 1;
37 static const char *eadata_get_data_type_name(UI t)
39 const char *name = NULL;
40 switch(t) {
41 case 0xffde: name ="multi-val/single-type"; break;
42 case 0xffdf: name ="multi-val/multi-type"; break;
43 case 0xfffe: name ="binary"; break;
44 case 0xfffd: name ="text"; break;
45 case 0xfff9: name ="icon"; break;
48 return name?name:"?";
51 static void eadata_extract_icon(deark *c, struct eadata_ctx *d, i64 pos, i64 len)
53 de_finfo *fi = NULL;
55 fi = de_finfo_create(c);
57 if(d->base_filename_ref) {
58 de_finfo_set_name_from_ucstring(c, fi, d->base_filename_ref, 0);
60 else if(d->cur_md && ucstring_isnonempty(d->cur_md->fn)) {
61 de_finfo_set_name_from_ucstring(c, fi, d->cur_md->fn, 0);
64 dbuf_create_file_from_slice(c->infile, pos, len, "os2.ico", fi, d->createflags_for_icons);
65 de_finfo_destroy(c, fi);
68 static void eadata_do_text_attrib(deark *c, struct eadata_ctx *d, i64 pos, i64 len)
70 de_ucstring *s = NULL;
72 s = ucstring_create(c);
73 // Documented as "ASCII text" -- but I wonder if the actual encoding might
74 // depend on the attribute name.
75 dbuf_read_to_ucstring_n(c->infile, pos, len, 2048, s, 0, DE_ENCODING_ASCII);
76 de_dbg(c, "text: \"%s\"", ucstring_getpsz_d(s));
77 ucstring_destroy(s);
80 static int eadata_do_attribute_lowlevel_singleval(deark *c, struct eadata_ctx *d,
81 UI attr_dtype, i64 pos1, i64 maxlen, i64 *pbytes_consumed)
83 i64 attr_dlen;
84 i64 dpos;
85 int retval = 0;
87 attr_dlen = de_getu16le(pos1);
88 de_dbg(c, "inner data len: %"I64_FMT, attr_dlen);
89 if(attr_dlen<2 || attr_dlen>maxlen) goto done;
90 *pbytes_consumed = 2 + attr_dlen;
91 retval = 1;
92 dpos = pos1+2;
94 switch(attr_dtype) {
95 case 0xfff9:
96 eadata_extract_icon(c, d, dpos, attr_dlen);
97 break;
98 case 0xfffd:
99 eadata_do_text_attrib(c, d, dpos, attr_dlen);
100 break;
101 default:
102 de_dbg_hexdump(c, c->infile, dpos, attr_dlen, 256, NULL, 0x1);
105 done:
106 return retval;
109 static int eadata_do_attribute_lowlevel(deark *c, struct eadata_ctx *d,
110 UI attr_dtype, i64 pos1, i64 nbytes_avail, i64 *pbytes_consumed, int nesting_level);
112 // multi-val, multi-type container attribute
113 static int eadata_do_MVMT(deark *c, struct eadata_ctx *d,
114 i64 pos1, i64 nbytes_avail, i64 *pbytes_consumed, int nesting_level)
116 UI codepage;
117 i64 num_entries;
118 i64 pos = pos1;
119 int retval = 0;
120 int ret;
121 i64 i;
122 int saved_indent_level;
124 de_dbg_indent_save(c, &saved_indent_level);
125 codepage = (UI)de_getu16le_p(&pos);
126 de_dbg(c, "code page: %u", codepage);
128 num_entries = de_getu16le_p(&pos);
129 de_dbg(c, "num entries: %d", (int)num_entries);
130 for(i=0; i<num_entries; i++) {
131 UI attr_dtype;
132 i64 bytes_consumed2 = 0;
134 if(pos > pos1+nbytes_avail) goto done;
135 de_dbg(c, "entry %d at %"I64_FMT, (int)i, pos);
136 de_dbg_indent(c, 1);
137 attr_dtype = (UI)de_getu16le_p(&pos);
138 de_dbg(c, "data type: 0x%04x (%s)", attr_dtype, eadata_get_data_type_name(attr_dtype));
140 ret = eadata_do_attribute_lowlevel(c, d, attr_dtype, pos, pos1+nbytes_avail-pos,
141 &bytes_consumed2, nesting_level+1);
142 if(!ret) goto done;
143 pos += bytes_consumed2;
144 de_dbg_indent(c, -1);
147 *pbytes_consumed = pos - pos1;
148 retval = 1;
149 done:
150 de_dbg_indent_restore(c, saved_indent_level);
151 return retval;
154 static int eadata_do_attribute_lowlevel(deark *c, struct eadata_ctx *d,
155 UI attr_dtype, i64 pos1, i64 nbytes_avail, i64 *pbytes_consumed, int nesting_level)
157 int retval = 0;
159 *pbytes_consumed = 0;
161 // I don't know if multi-val attributes are allowed to contain other multi-val attributes.
162 if(nesting_level>5) goto done;
164 switch(attr_dtype) {
165 case 0xffdf:
166 if(!eadata_do_MVMT(c, d, pos1, nbytes_avail, pbytes_consumed, nesting_level)) goto done;
167 break;
168 case 0xffde: // MVST (TODO)
169 case 0xffdd: // ASN1
170 goto done;
171 default:
172 if(!eadata_do_attribute_lowlevel_singleval(c, d, attr_dtype, pos1, nbytes_avail,
173 pbytes_consumed))
175 goto done;
177 break;
180 retval = 1;
182 done:
183 return retval;
186 // FEA2 structure, starting at the 'fEA' field (1 byte before the name-length byte).
187 static int eadata_do_attribute(deark *c, struct eadata_ctx *d, i64 pos1, i64 maxlen,
188 de_ucstring *tmps, i64 *pbytes_consumed)
190 i64 namelen;
191 i64 attr_dpos;
192 i64 attr_dlen;
193 i64 tmpbc;
194 UI attr_dtype;
195 i64 pos = pos1;
196 int retval = 0;
198 pos++; // fEA
199 namelen = (i64)de_getbyte_p(&pos);
201 attr_dlen = (i64)de_getu16le_p(&pos);
202 ucstring_empty(tmps);
203 dbuf_read_to_ucstring(c->infile, pos, namelen, tmps, 0, DE_ENCODING_ASCII);
204 de_dbg(c, "name: \"%s\"", ucstring_getpsz_d(tmps));
205 pos += namelen + 1;
206 attr_dpos = pos;
207 de_dbg(c, "outer data len: %"I64_FMT, attr_dlen);
208 if(attr_dpos + attr_dlen > pos1+maxlen) goto done;
210 attr_dtype = (UI)de_getu16le_p(&pos);
211 de_dbg(c, "data type: 0x%04x (%s)", attr_dtype, eadata_get_data_type_name(attr_dtype));
213 tmpbc = 0;
214 eadata_do_attribute_lowlevel(c, d, attr_dtype, attr_dpos+2, attr_dlen-2, &tmpbc, 0);
216 pos = attr_dpos + attr_dlen;
217 *pbytes_consumed = pos - pos1;
218 retval = 1;
219 done:
220 return retval;
223 // Sets md->ea_data_len.
224 static void eadata_do_ea_data(deark *c, struct eadata_ctx *d, struct easector_ctx *md,
225 i64 pos1)
227 i64 pos = pos1;
228 i64 endpos;
229 int saved_indent_level;
230 de_ucstring *s = NULL;
232 de_dbg_indent_save(c, &saved_indent_level);
233 de_dbg(c, "EA data at %"I64_FMT, pos1);
234 de_dbg_indent(c, 1);
236 md->ea_data_len = de_getu16le_p(&pos); // TODO: Is this actually a 4-byte field?
237 de_dbg(c, "data len: %"I64_FMT, md->ea_data_len);
239 pos += 2; // ?
240 endpos = pos1 + md->ea_data_len;
241 s = ucstring_create(c);
243 d->cur_md = md;
244 while(pos < endpos-4) {
245 int ret;
246 i64 bytes_consumed = 0;
248 de_dbg(c, "attribute at %"I64_FMT, pos);
249 de_dbg_indent(c, 1);
250 ret = eadata_do_attribute(c, d, pos, endpos-pos, s, &bytes_consumed);
251 de_dbg_indent(c, -1);
252 if(!ret || bytes_consumed<1) goto done;
253 pos += bytes_consumed;
256 done:
257 ucstring_destroy(s);
258 d->cur_md = NULL;
259 de_dbg_indent_restore(c, saved_indent_level);
262 static void eadata_do_raw_list(deark *c, struct eadata_ctx *d, i64 pos1, i64 len)
264 i64 pos = pos1;
265 i64 endpos;
266 de_ucstring *tmps = NULL;
267 int saved_indent_level;
269 de_dbg_indent_save(c, &saved_indent_level);
270 tmps = ucstring_create(c);
272 endpos = pos1 + len;
274 while(1) {
275 int ret;
276 i64 bytes_consumed = 0;
277 i64 offset_to_next_attr;
278 i64 attr_pos;
280 if(pos >= endpos) goto done;
282 attr_pos = pos;
283 de_dbg(c, "attribute at %"I64_FMT, attr_pos);
284 de_dbg_indent(c, 1);
285 offset_to_next_attr = de_getu32le_p(&pos);
286 de_dbg(c, "offset to next attr: %"I64_FMT, offset_to_next_attr);
288 ret = eadata_do_attribute(c, d, pos, endpos-pos, tmps, &bytes_consumed);
289 if(!ret || bytes_consumed<1) goto done;
290 if(offset_to_next_attr==0) goto done;
291 pos = attr_pos + offset_to_next_attr;
292 de_dbg_indent(c, -1);
295 done:
296 ucstring_destroy(tmps);
297 de_dbg_indent_restore(c, saved_indent_level);
300 static void eadata_do_FEA2LIST(deark *c, struct eadata_ctx *d)
302 i64 fea2list_len;
303 i64 pos1 = 0;
304 i64 pos = pos1;
306 de_dbg(c, "FEA2LIST at %"I64_FMT, pos1);
307 de_dbg_indent(c, 1);
308 fea2list_len = de_getu32le_p(&pos);
309 de_dbg(c, "list len: %"I64_FMT, fea2list_len);
310 eadata_do_raw_list(c, d, pos, fea2list_len-4);
311 de_dbg_indent(c, -1);
314 static void eadata_do_ea_sector_by_offset(deark *c, struct eadata_ctx *d, i64 pos1,
315 i64 *pbytes_consumed1)
317 i64 n;
318 i64 pos;
319 struct easector_ctx *md = NULL;
320 int saved_indent_level;
322 de_dbg_indent_save(c, &saved_indent_level);
323 if(pbytes_consumed1) {
324 *pbytes_consumed1 = 0;
326 md = de_malloc(c, sizeof(struct easector_ctx));
328 if(!eadata_is_ea_sector_at_offset(c, d, pos1, 0)) {
329 de_err(c, "EA sector not found at %"I64_FMT, pos1);
330 goto done;
333 de_dbg(c, "EA sector at %"I64_FMT, pos1);
334 de_dbg_indent(c, 1);
335 pos = pos1+2;
336 n = de_getu16le_p(&pos);
337 de_dbg(c, "sector number (consistency check): %u", (UI)n);
339 pos += 4;
341 md->fn = ucstring_create(c);
342 dbuf_read_to_ucstring(c->infile, pos, 12, md->fn, DE_CONVFLAG_STOP_AT_NUL, d->input_encoding);
343 de_dbg(c, "file name: \"%s\"", ucstring_getpsz_d(md->fn));
344 pos += 12;
346 pos += 2;
347 pos += 4;
349 eadata_do_ea_data(c, d, md, pos);
350 pos += md->ea_data_len;
352 if(pbytes_consumed1) {
353 *pbytes_consumed1 = pos - pos1;
356 done:
357 if(md) {
358 ucstring_destroy(md->fn);
359 de_free(c, md);
361 de_dbg_indent_restore(c, saved_indent_level);
364 static int eadata_id_to_offset(deark *c, struct eadata_ctx *d, UI id, i64 *poffset)
366 int retval = 0;
367 UI a_idx;
368 UI a_val;
369 UI b_val;
370 i64 cluster_num;
372 *poffset = 0;
374 a_idx = id>>7;
375 if(a_idx>=240) goto done;
376 a_val = (UI)de_getu16le(32+2*(i64)a_idx);
377 b_val = (UI)de_getu16le(512+2*(i64)id);
378 if(b_val==0xffff) goto done;
380 cluster_num = (i64)b_val + (i64)a_val;
381 *poffset = d->bytes_per_cluster * cluster_num;
383 if(eadata_is_ea_sector_at_offset(c, d, *poffset, 0)) {
384 retval = 1;
387 done:
388 return retval;
391 static void eadata_scan_file(deark *c, struct eadata_ctx *d)
393 i64 pos = 1024;
395 while(pos < c->infile->len) {
396 if(eadata_is_ea_sector_at_offset(c, d, pos, 1)) {
397 i64 bytes_consumed;
399 eadata_do_ea_sector_by_offset(c, d, pos, &bytes_consumed);
401 if(bytes_consumed<1) bytes_consumed = 1;
402 pos = de_pad_to_n(pos+bytes_consumed, 512);
404 else {
405 pos += 512;
410 static void de_run_eadata(deark *c, de_module_params *mparams)
412 int ret;
413 UI ea_id = 0;
414 i64 pos;
415 const char *s;
416 struct eadata_ctx *d = NULL;
418 de_declare_fmt(c, "OS/2 extended attributes data");
420 d = de_malloc(c, sizeof(struct eadata_ctx));
422 if(mparams && (mparams->in_params.flags & 0x8)!=0) {
423 if(ucstring_isnonempty(mparams->in_params.str1)) {
424 d->base_filename_ref = mparams->in_params.str1;
428 if(de_havemodcode(c, mparams, 'L')) {
429 d->createflags_for_icons = DE_CREATEFLAG_IS_AUX;
430 eadata_do_FEA2LIST(c, d);
432 else if(de_havemodcode(c, mparams, 'R')) {
433 d->createflags_for_icons = DE_CREATEFLAG_IS_AUX;
434 eadata_do_raw_list(c, d, 0, c->infile->len);
436 else if(mparams && (mparams->in_params.flags & 0x1)) {
437 // We're being used by another module, to handle a specific ea_id.
438 ea_id = (UI)mparams->in_params.uint1;
439 if(ea_id==0) goto done;
440 d->createflags_for_icons = DE_CREATEFLAG_IS_AUX;
442 else {
443 s = de_get_ext_option(c, "ea_data:handle");
444 if(s) {
445 ea_id = (UI)de_atoi(s);
449 d->input_encoding = de_get_input_encoding(c, mparams, DE_ENCODING_CP437);
450 d->bytes_per_cluster = 512;
452 if(ea_id==0) {
453 eadata_scan_file(c, d);
455 else {
456 ret = eadata_id_to_offset(c, d, ea_id, &pos);
457 if(!ret) goto done;
458 eadata_do_ea_sector_by_offset(c, d, pos, NULL);
461 done:
462 de_free(c, d);
465 static int de_identify_eadata(deark *c)
467 if(de_getu16be(0)!=0x4544) return 0;
468 if(de_input_file_has_ext(c, " sf")) return 100;
469 if(dbuf_is_all_zeroes(c->infile, 2, 30)) {
470 return 20;
472 return 0;
475 static void de_help_eadata(deark *c)
477 de_msg(c, "-opt ea_data:handle=<n> : Decode only EA handle/pointer <n>");
480 void de_module_ea_data(deark *c, struct deark_module_info *mi)
482 mi->id = "ea_data";
483 mi->desc = "EA DATA (OS/2 extended attributes)";
484 mi->run_fn = de_run_eadata;
485 mi->identify_fn = de_identify_eadata;
486 mi->help_fn = de_help_eadata;