1 // This file is part of Deark.
2 // Copyright (C) 2024 Jason Summers
3 // See the file COPYING for terms of use.
5 // OS/2 PACK, and PACK2 (FTCOMP)
7 #include <deark-private.h>
8 #include <deark-fmtutil.h>
9 #include <deark-fmtutil-arch.h>
10 DE_DECLARE_MODULE(de_module_os2pack
);
11 DE_DECLARE_MODULE(de_module_os2pack2
);
13 #define OS2PACK_MINHEADERLEN 10
15 static UI
os2pack_is_member_at(dbuf
*f
, i64 pos
)
19 sig
= (UI
)dbuf_getu32be(f
, pos
);
20 return (sig
==0xa596feffU
|| sig
==0xa596ffffU
||
21 sig
==0xa5960014U
|| sig
==0xa596140aU
) ? 1 : 0;
24 static UI
os2pack2_is_member_at(dbuf
*f
, i64 pos
)
28 sig
= (UI
)dbuf_getu32be(f
, pos
);
29 if(sig
!=0xa596fdffU
) return 0;
30 if(dbuf_memcmp(f
, pos
+24, (const void*)"FTCOMP", 6)) return 0;
34 static UI
os2pack12_is_member_at(de_arch_lctx
*d
, i64 pos
)
36 if(d
->fmtcode
==0xfffd) {
37 return os2pack2_is_member_at(d
->inf
, pos
);
39 return os2pack_is_member_at(d
->inf
, pos
);
42 static void os2pack_decompressor_fn(struct de_arch_member_data
*md
)
44 fmtutil_ibmlzw_codectype1(md
->c
, md
->dcmpri
, md
->dcmpro
, md
->dres
, NULL
);
47 static void os2pack2_read_cmpr_method(deark
*c
, i64 pos
, i64 len
)
49 struct de_fourcc cmpr4cc
;
52 dbuf_read_fourcc(c
->infile
, pos
+4, &cmpr4cc
, 4, 0x0);
53 de_dbg(c
, "cmpr meth: '%s'", cmpr4cc
.id_dbgstr
); // Usually "fT19"
56 static void do_os2pack1_ea(deark
*c
, de_arch_lctx
*d
, struct de_arch_member_data
*md
,
57 i64 ea_pos
, i64 ea_len
)
59 int saved_indent_level
;
60 dbuf
*attr_data
= NULL
;
61 de_module_params
*mparams
= NULL
;
62 struct de_dfilter_in_params dcmpri
;
63 struct de_dfilter_out_params dcmpro
;
64 struct de_dfilter_results dres
;
66 de_dbg_indent_save(c
, &saved_indent_level
);
67 de_dbg(c
, "ext. attr. at %"I64_FMT
, ea_pos
);
69 attr_data
= dbuf_create_membuf(c
, 0, 0);
70 dbuf_set_length_limit(attr_data
, 1024*1024);
72 de_dfilter_init_objects(c
, &dcmpri
, &dcmpro
, &dres
);
77 fmtutil_ibmlzw_codectype1(c
, &dcmpri
, &dcmpro
, &dres
, NULL
);
78 dbuf_flush(attr_data
);
80 de_warn(c
, "Failed to decompress ext. attr. data");
83 de_dbg(c
, "decompressed len: %"I64_FMT
, attr_data
->len
);
85 mparams
= de_malloc(c
, sizeof(de_module_params
));
86 mparams
->in_params
.codes
= "R";
87 if(ucstring_isnonempty(md
->filename
)) {
88 mparams
->in_params
.str1
= md
->filename
;
89 mparams
->in_params
.flags
|= 0x8;
91 de_run_module_by_id_on_slice(c
, "ea_data", mparams
, attr_data
, 0, attr_data
->len
);
94 dbuf_close(attr_data
);
96 de_dbg_indent_restore(c
, saved_indent_level
);
99 static void do_os2pack12_member(deark
*c
, de_arch_lctx
*d
, struct de_arch_member_data
*md
)
106 i64 unk2
, unk3
, unk4
;
109 int saved_indent_level
;
111 de_dbg_indent_save(c
, &saved_indent_level
);
112 de_dbg(c
, "member at %"I64_FMT
, md
->member_hdr_pos
);
115 pos
= md
->member_hdr_pos
+ 4;
117 de_arch_read_field_dttm_p(d
, &md
->fi
->timestamp
[DE_TIMESTAMPIDX_MODIFY
], "mod",
118 DE_ARCH_TSTYPE_DOS_DT
, &pos
);
120 // Apparently a file attributes field, but don't know if it's 1, 2, or 4 bytes.
121 attribs
= (UI
)de_getbyte_p(&pos
);
122 de_arch_handle_field_dos_attr(md
, attribs
);
124 if(d
->fmtcode
==0x1400 || d
->fmtcode
==0x0a14) {
127 else if(d
->fmtcode
==0xffff) {
132 ea_pos
= de_getu32le_p(&pos
);
133 de_dbg(c
, "ext. attr. pos: %"I64_FMT
, ea_pos
);
135 de_arch_read_field_orig_len_p(md
, &pos
);
136 // TODO: Figure out why some files have 1 here, and others have the original
138 if(d
->fmtcode
==0xfffe && md
->orig_len
==1) {
140 md
->orig_len_known
= 0;
143 md
->next_member_pos
= de_getu32le_p(&pos
);
144 de_dbg(c
, "next member pos: %"I64_FMT
, md
->next_member_pos
);
145 if(md
->next_member_pos
!=0) {
146 md
->next_member_exists
= 1;
150 if(d
->fmtcode
==0xfffd) {
151 pos
+= 7; // "FTCOMP\0"
153 unk2
= de_getu16le_p(&pos
);
154 de_dbg(c
, "unk2: %u", (UI
)unk2
);
156 unk3
= de_getu16le_p(&pos
);
157 de_dbg(c
, "unk3: %u", (UI
)unk3
);
159 unk4
= de_getu32le_p(&pos
);
160 de_dbg(c
, "unk4: %"I64_FMT
, unk4
);
163 if(d
->fmtcode
==0x1400 || d
->fmtcode
==0x0a14) {
166 else if(d
->fmtcode
==0xffff) {
169 if(dbuf_search_byte(c
->infile
, 0, pos
, 260, &foundpos
)) {
170 fnlen
= foundpos
+ 1 - pos
;
178 fnlen
= de_getu16le_p(&pos
);
181 dbuf_read_to_ucstring_n(c
->infile
, pos
, fnlen
, 512, md
->filename
,
182 DE_CONVFLAG_STOP_AT_NUL
, d
->input_encoding
);
183 de_dbg(c
, "filename: \"%s\"", ucstring_getpsz_d(md
->filename
));
185 de_arch_fixup_path(md
->filename
, 0);
186 md
->set_name_flags
|= DE_SNFLAG_FULLPATH
;
190 if(md
->next_member_exists
) {
191 member_endpos
= md
->next_member_pos
;
194 member_endpos
= c
->infile
->len
;
197 if(member_endpos
> c
->infile
->len
) {
203 md
->cmpr_len
= member_endpos
- md
->cmpr_pos
;
204 // if ea_pos is set, adjust cmpr_len downward
206 if(ea_pos
<md
->cmpr_pos
|| ea_pos
>member_endpos
) {
211 md
->cmpr_len
= ea_pos
- md
->cmpr_pos
;
212 ea_len
= member_endpos
- ea_pos
;
222 de_dbg(c
, "cmpr ext. attr. at %"I64_FMT
", len=%"I64_FMT
, ea_pos
, ea_len
);
224 if(d
->fmtcode
==0xfffd) {
225 os2pack2_read_cmpr_method(c
, ea_pos
, ea_len
);
227 de_dbg_indent(c
, -1);
230 de_dbg(c
, "cmpr data at %"I64_FMT
", len=%"I64_FMT
, md
->cmpr_pos
, md
->cmpr_len
);
233 if(d
->fmtcode
==0xfffd) {
234 // Most likely, the compressed data is considered to start after the
236 // It seems to have a compression header that we can peek at.
237 os2pack2_read_cmpr_method(c
, md
->cmpr_pos
, md
->cmpr_len
);
240 if(d
->fmtcode
==0x1400 || d
->fmtcode
==0x0a14 || d
->fmtcode
==0xffff ||
243 md
->dfn
= os2pack_decompressor_fn
;
244 de_arch_extract_member_file(md
);
245 if(ea_pos
>0 && ea_len
>0) {
246 do_os2pack1_ea(c
, d
, md
, ea_pos
, ea_len
);
250 de_dbg_indent(c
, -1);
252 // TODO: There may be a few more bytes at the end of a member, purpose unknown.
253 // If so, we should adjust md->cmpr_len or ea_len accordingly.
257 de_err(c
, "Unsupported member file format");
259 de_dbg_indent_restore(c
, saved_indent_level
);
262 static void do_run_os2pack12(deark
*c
, de_module_params
*mparams
, UI ver
)
264 de_arch_lctx
*d
= NULL
;
265 struct de_arch_member_data
*md
= NULL
;
267 const char *pname
= "PACK";
269 d
= de_arch_create_lctx(c
);
276 d
->fmtcode
= (UI
)de_getu16le(2);
277 if(d
->fmtcode
!=0x1400 && d
->fmtcode
!=0x0a14 && d
->fmtcode
!=0xffff &&
285 if(d
->fmtcode
==0xfffd) {
287 de_declare_fmt(c
, "OS/2 PACK2 archive");
290 de_declare_fmtf(c
, "OS/2 PACK archive (type 0x%04x)", d
->fmtcode
);
293 // TODO: What encoding to use?
294 d
->input_encoding
= de_get_input_encoding(c
, NULL
, DE_ENCODING_WINDOWS1252
);
299 if(pos
+OS2PACK_MINHEADERLEN
> c
->infile
->len
) {
304 if(!os2pack12_is_member_at(d
, pos
)) {
310 de_arch_destroy_md(c
, md
);
313 md
= de_arch_create_md(c
, d
);
314 md
->member_hdr_pos
= pos
;
316 do_os2pack12_member(c
, d
, md
);
318 if(d
->fatalerrflag
) goto done
;
319 if(!md
->next_member_exists
) goto done
;
320 if(md
->next_member_pos
<= pos
) {
324 pos
= md
->next_member_pos
;
329 de_arch_destroy_md(c
, md
);
334 de_err(c
, "Bad or unsupported OS/2 %s archive", pname
);
336 de_arch_destroy_lctx(c
, d
);
340 static void de_run_os2pack(deark
*c
, de_module_params
*mparams
)
342 do_run_os2pack12(c
, mparams
, 1);
345 static void de_run_os2pack2(deark
*c
, de_module_params
*mparams
)
347 do_run_os2pack12(c
, mparams
, 2);
350 static int de_identify_os2pack(deark
*c
)
352 return (os2pack_is_member_at(c
->infile
, 0)) ? 100 : 0;
355 void de_module_os2pack(deark
*c
, struct deark_module_info
*mi
)
358 mi
->desc
= "OS/2 PACK archive";
359 mi
->run_fn
= de_run_os2pack
;
360 mi
->identify_fn
= de_identify_os2pack
;
363 static int de_identify_os2pack2(deark
*c
)
365 return (os2pack2_is_member_at(c
->infile
, 0)) ? 100 : 0;
368 void de_module_os2pack2(deark
*c
, struct deark_module_info
*mi
)
371 mi
->desc
= "OS/2 PACK2 archive";
372 mi
->run_fn
= de_run_os2pack2
;
373 mi
->flags
|= DE_MODFLAG_WARNPARSEONLY
;
374 mi
->identify_fn
= de_identify_os2pack2
;