1 // This file is part of Deark.
2 // Copyright (C) 2021 Jason Summers
3 // See the file COPYING for terms of use.
6 // InstallShield installer archive
9 #include <deark-private.h>
10 #include <deark-fmtutil.h>
11 #include <deark-fmtutil-arch.h>
12 DE_DECLARE_MODULE(de_module_is_z
);
13 DE_DECLARE_MODULE(de_module_is_instarch
);
14 DE_DECLARE_MODULE(de_module_tscomp
);
16 #define ISZ_MAX_DIRS 1000 // arbitrary
17 #define ISZ_MAX_FILES 5000 // arbitrary
18 #define ISZ_MAX_DIR_NAME_LEN 32768 // arbitrary
19 #define ISZ_SIGNATURE 0x8c655d13U
21 static void dclimplode_decompressor_fn(struct de_arch_member_data
*md
)
23 fmtutil_dclimplode_codectype1(md
->c
, md
->dcmpri
, md
->dcmpro
, md
->dres
, NULL
);
26 struct isz_dir_array_item
{
30 // We don't need this struct; it's just here for future expansion
31 struct isz_member_data
{
36 struct de_arch_localctx_struct
*da
;
41 struct isz_dir_array_item
*dir_array
; // array[num_dirs]
44 static int isz_do_one_file(deark
*c
, struct isz_ctx
*d
, i64 pos1
, i64
*pbytes_consumed
)
50 struct de_arch_member_data
*md
= NULL
;
51 struct isz_member_data
*mdi
= NULL
;
52 struct isz_dir_array_item
*di
= NULL
;
53 int saved_indent_level
;
55 de_dbg_indent_save(c
, &saved_indent_level
);
56 mdi
= de_malloc(c
, sizeof(struct isz_member_data
));
57 md
= de_arch_create_md(c
, d
->da
);
58 md
->userdata
= (void*)mdi
;
60 de_dbg(c
, "file entry at %"I64_FMT
, pos1
);
65 mdi
->dir_id
= (UI
)de_getu16le_p(&pos
); // 1
66 de_dbg(c
, "dir id: %u", mdi
->dir_id
);
67 if(mdi
->dir_id
>= d
->num_dirs
) {
68 de_err(c
, "Invalid directory");
71 di
= &d
->dir_array
[mdi
->dir_id
];
73 de_arch_read_field_orig_len_p(md
, &pos
); // 3
74 de_arch_read_field_cmpr_len_p(md
, &pos
); // 7
75 md
->cmpr_pos
= de_getu32le_p(&pos
); // 11
76 de_dbg(c
, "cmpr data pos: %"I64_FMT
, md
->cmpr_pos
);
77 de_arch_read_field_dttm_p(d
->da
, &md
->fi
->timestamp
[DE_TIMESTAMPIDX_MODIFY
], "mod",
78 DE_ARCH_TSTYPE_DOS_DT
, &pos
);
80 pos
+= 4; // ? (maybe a bit-field?)
82 segment_size
= de_getu16le_p(&pos
);
83 de_dbg(c
, "segment size: %"I64_FMT
, segment_size
);
84 if(segment_size
<30) goto done
;
88 name_len
= (i64
)de_getbyte_p(&pos
);
89 de_dbg(c
, "name len: %"I64_FMT
, name_len
);
90 md
->tmpfn_base
= ucstring_create(c
);
91 dbuf_read_to_ucstring(c
->infile
, pos
, name_len
, md
->tmpfn_base
, 0, d
->da
->input_encoding
);
92 de_dbg(c
, "name: \"%s\"", ucstring_getpsz_d(md
->tmpfn_base
));
94 if(ucstring_isempty(md
->tmpfn_base
)) {
95 ucstring_append_char(md
->tmpfn_base
, '_');
97 ucstring_append_ucstring(md
->filename
, di
->dname
);
98 ucstring_append_ucstring(md
->filename
, md
->tmpfn_base
);
99 de_dbg(c
, "full name: \"%s\"", ucstring_getpsz_d(md
->filename
));
101 *pbytes_consumed
= segment_size
;
104 md
->set_name_flags
|= DE_SNFLAG_FULLPATH
;
105 md
->dfn
= dclimplode_decompressor_fn
;
106 de_arch_extract_member_file(md
);
109 de_arch_destroy_md(c
, md
);
111 de_dbg_indent_restore(c
, saved_indent_level
);
115 static void isz_do_filelist(deark
*c
, struct isz_ctx
*d
)
117 i64 pos
= d
->filelist_pos
;
120 for(i
=0; i
<d
->da
->num_members
; i
++) {
121 i64 bytes_consumed
= 0;
123 if(!isz_do_one_file(c
, d
, pos
, &bytes_consumed
)) goto done
;
124 if(bytes_consumed
<=0) goto done
;
125 pos
+= bytes_consumed
;
132 static int isz_do_onedir(deark
*c
, struct isz_ctx
*d
, i64 dir_idx
, i64 pos1
, i64
*pbytes_consumed
)
140 struct isz_dir_array_item
*di
;
141 int saved_indent_level
;
143 de_dbg_indent_save(c
, &saved_indent_level
);
145 de_dbg(c
, "dir entry at %"I64_FMT
, pos
);
148 if(dir_idx
<0 || dir_idx
>=d
->num_dirs
) goto done
;
149 di
= &d
->dir_array
[dir_idx
];
151 num_files
= de_getu16le_p(&pos
);
152 de_dbg(c
, "num files in this dir: %"I64_FMT
, num_files
);
153 segment_size
= de_getu16le_p(&pos
);
154 de_dbg(c
, "segment size: %"I64_FMT
, segment_size
);
155 if(segment_size
<6) goto done
;
156 endpos
= pos1
+ segment_size
;
158 name_len
= de_getu16le_p(&pos
);
159 de_dbg(c
, "dir name len: %"I64_FMT
, name_len
);
160 if(pos
+name_len
> endpos
) goto done
;
161 if(name_len
> ISZ_MAX_DIR_NAME_LEN
) goto done
;
163 di
->dname
= ucstring_create(c
);
164 dbuf_read_to_ucstring(c
->infile
, pos
, name_len
, di
->dname
, 0, d
->da
->input_encoding
);
165 de_dbg(c
, "dir name: \"%s\"", ucstring_getpsz_d(di
->dname
));
166 de_arch_fixup_path(di
->dname
, 0x1);
168 *pbytes_consumed
= segment_size
;
171 de_dbg_indent_restore(c
, saved_indent_level
);
175 static void isz_do_dirlist(deark
*c
, struct isz_ctx
*d
)
180 pos
= d
->directory_pos
;
181 for(i
=0; i
<d
->num_dirs
; i
++) {
182 i64 bytes_consumed
= 0;
184 if(pos
>= c
->infile
->len
) goto done
;
185 if(!isz_do_onedir(c
, d
, i
, pos
, &bytes_consumed
)) goto done
;
186 if(bytes_consumed
<=0) goto done
;
187 pos
+= bytes_consumed
;
193 static void de_run_is_z(deark
*c
, de_module_params
*mparams
)
195 struct isz_ctx
*d
= NULL
;
196 int saved_indent_level
;
197 struct de_timestamp tmp_timestamp
;
200 de_dbg_indent_save(c
, &saved_indent_level
);
201 d
= de_malloc(c
, sizeof(struct isz_ctx
));
202 d
->da
= de_arch_create_lctx(c
);
203 d
->da
->userdata
= (void*)d
;
206 d
->da
->input_encoding
= de_get_input_encoding(c
, NULL
, DE_ENCODING_WINDOWS1252
);
208 // 0 13 5D 65 8C = signature
211 // 12 ui16 number of files
215 // 41 ui32 offset of directory sequence
217 // 49 ui16 number of dirs
218 // 51 ui32 offset of files sequence
220 if(de_getu32le(0)!=ISZ_SIGNATURE
) {
221 de_err(c
, "Not an InstallShield Z file");
225 de_dbg(c
, "main header");
228 d
->da
->num_members
= de_getu16le(12);
229 de_dbg(c
, "total number of files: %"I64_FMT
, d
->da
->num_members
);
230 if(d
->da
->num_members
>ISZ_MAX_FILES
) goto done
;
233 de_arch_read_field_dttm_p(d
->da
, &tmp_timestamp
, "archive",
234 DE_ARCH_TSTYPE_DOS_DT
, &tmp_pos
);
236 d
->directory_pos
= de_getu32le(41);
237 de_dbg(c
, "start of dir entries: %"I64_FMT
, d
->directory_pos
);
239 d
->num_dirs
= de_getu16le(49);
240 de_dbg(c
, "number of dirs: %"I64_FMT
, d
->num_dirs
);
241 if(d
->num_dirs
>ISZ_MAX_DIRS
) goto done
;
243 d
->filelist_pos
= de_getu32le(51);
244 de_dbg(c
, "start of file entries: %"I64_FMT
, d
->filelist_pos
);
246 de_dbg_indent(c
, -1);
248 d
->dir_array
= de_mallocarray(c
, d
->num_dirs
, sizeof(struct isz_dir_array_item
));
250 isz_do_dirlist(c
, d
);
251 isz_do_filelist(c
, d
);
258 for(i
=0; i
<d
->num_dirs
; i
++) {
259 ucstring_destroy(d
->dir_array
[i
].dname
);
261 de_free(c
, d
->dir_array
);
263 de_arch_destroy_lctx(c
, d
->da
);
266 de_dbg_indent_restore(c
, saved_indent_level
);
269 static int de_identify_is_z(deark
*c
)
271 if(de_getu32le(0)==ISZ_SIGNATURE
)
276 void de_module_is_z(deark
*c
, struct deark_module_info
*mi
)
279 mi
->desc
= "InstallShield Z";
280 mi
->run_fn
= de_run_is_z
;
281 mi
->identify_fn
= de_identify_is_z
;
284 // **************************************************************************
285 // InstallShield installer archive
286 // **************************************************************************
288 static int do_instarch_member(deark
*c
, de_arch_lctx
*da
, struct de_arch_member_data
*md
)
291 int saved_indent_level
;
292 i64 pos
= md
->member_hdr_pos
;
294 de_ucstring
*tmps
= NULL
;
296 de_dbg_indent_save(c
, &saved_indent_level
);
297 de_dbg(c
, "member at %"I64_FMT
, md
->member_hdr_pos
);
301 md
->cmpr_pos
= de_getu32le_p(&pos
);
302 de_dbg(c
, "cmpr. data pos: %"I64_FMT
, md
->cmpr_pos
);
303 de_arch_read_field_cmpr_len_p(md
, &pos
);
305 de_arch_read_field_orig_len_p(md
, &pos
);
308 nlen
= de_getu16le_p(&pos
);
309 dbuf_read_to_ucstring(c
->infile
, pos
, nlen
, md
->filename
, 0, da
->input_encoding
);
310 de_dbg(c
, "name 1: \"%s\"", ucstring_getpsz_d(md
->filename
));
313 // I don't know why each file seemingly has two names.
314 nlen
= de_getu16le_p(&pos
);
315 tmps
= ucstring_create(c
);
316 dbuf_read_to_ucstring(c
->infile
, pos
, nlen
, tmps
, 0, da
->input_encoding
);
317 de_dbg(c
, "name 2: \"%s\"", ucstring_getpsz_d(tmps
));
320 if(!de_arch_good_cmpr_data_pos(md
)) {
324 md
->dfn
= dclimplode_decompressor_fn
;
325 de_arch_extract_member_file(md
);
327 md
->member_hdr_size
= pos
- md
->member_hdr_pos
;
330 ucstring_destroy(tmps
);
331 de_dbg_indent_restore(c
, saved_indent_level
);
335 static void de_run_is_instarch(deark
*c
, de_module_params
*mparams
)
337 de_arch_lctx
*da
= NULL
;
340 struct de_arch_member_data
*md
= NULL
;
342 da
= de_arch_create_lctx(c
);
344 da
->input_encoding
= de_get_input_encoding(c
, NULL
, DE_ENCODING_WINDOWS1252
);
347 da
->num_members
= de_getu16le_p(&pos
); // This might actually be a 32-bit field
348 de_dbg(c
, "number of members: %"I64_FMT
, da
->num_members
);
351 for(i
=0; i
<da
->num_members
; i
++) {
352 if(pos
>= c
->infile
->len
) goto done
;
355 de_arch_destroy_md(c
, md
);
358 md
= de_arch_create_md(c
, da
);
359 md
->member_hdr_pos
= pos
;
360 if(!do_instarch_member(c
, da
, md
)) goto done
;
361 if(md
->member_hdr_size
<=0) goto done
;
362 pos
+= md
->member_hdr_size
;
367 de_arch_destroy_md(c
, md
);
370 de_arch_destroy_lctx(c
, da
);
374 static int de_identify_is_instarch(deark
*c
)
376 if(dbuf_memcmp(c
->infile
, 0, "\x2a\xab\x79\xd8\x00\x01", 6)) {
382 void de_module_is_instarch(deark
*c
, struct deark_module_info
*mi
)
384 mi
->id
= "is_instarch";
385 mi
->id_alias
[0] = "is_inst32i";
386 mi
->desc
= "InstallShield installer archive (_inst32i.ex_)";
387 mi
->run_fn
= de_run_is_instarch
;
388 mi
->identify_fn
= de_identify_is_instarch
;
391 // **************************************************************************
392 // The Stirling Compressor" ("TSComp")
393 // **************************************************************************
395 // Probably only TSComp v1.3 is supported.
397 // Caller creates/destroys md, and sets a few fields.
398 static void tscomp_do_member(deark
*c
, de_arch_lctx
*d
, struct de_arch_member_data
*md
)
400 i64 pos
= md
->member_hdr_pos
;
402 int saved_indent_level
;
404 de_dbg_indent_save(c
, &saved_indent_level
);
405 de_dbg(c
, "member #%u at %"I64_FMT
, (UI
)md
->member_idx
,
410 de_arch_read_field_cmpr_len_p(md
, &pos
);
412 md
->next_member_pos
= de_getu32le_p(&pos
);
413 de_dbg(c
, "next member pos: %"I64_FMT
, md
->next_member_pos
);
414 if(md
->next_member_pos
&& (md
->next_member_pos
>md
->member_hdr_pos
) &&
415 (md
->next_member_pos
<c
->infile
->len
))
417 md
->next_member_exists
= 1;
420 de_arch_read_field_dttm_p(d
, &md
->fi
->timestamp
[DE_TIMESTAMPIDX_MODIFY
], "mod",
421 DE_ARCH_TSTYPE_DOS_DT
, &pos
);
424 fnlen
= de_getbyte_p(&pos
);
426 // STOP_AT_NUL is probably not needed.
427 dbuf_read_to_ucstring(c
->infile
, pos
, fnlen
, md
->filename
, DE_CONVFLAG_STOP_AT_NUL
,
429 de_dbg(c
, "filename: \"%s\"", ucstring_getpsz_d(md
->filename
));
434 md
->dfn
= dclimplode_decompressor_fn
;
435 de_arch_extract_member_file(md
);
436 de_dbg_indent_restore(c
, saved_indent_level
);
439 static void de_run_tscomp(deark
*c
, de_module_params
*mparams
)
441 de_arch_lctx
*d
= NULL
;
444 int saved_indent_level
;
447 struct de_arch_member_data
*md
= NULL
;
449 de_dbg_indent_save(c
, &saved_indent_level
);
450 d
= de_arch_create_lctx(c
);
452 d
->input_encoding
= de_get_input_encoding(c
, NULL
, DE_ENCODING_CP437
);
455 de_dbg(c
, "archive header at %d", (int)pos
);
459 b
= de_getbyte_p(&pos
);
460 if(b
!=0x08) { d
->need_errmsg
= 1; goto done
; }
461 pos
+= 3; // version?? (01 03 00)
462 b
= de_getbyte_p(&pos
);
464 case 1: name
= "without wildcard"; break;
465 case 2: name
= "with wildcard"; break;
466 // 0: seems to identify an "old" version (but it might be a significantly
467 // different format).
470 de_dbg(c
, "filename style: %u (%s)", (UI
)b
, name
);
471 if(b
!=1 && b
!=2) { d
->need_errmsg
= 1; goto done
; }
474 de_dbg_indent(c
, -1);
478 if(d
->fatalerrflag
) goto done
;
479 if(de_getbyte(pos
) != 0x12) { d
->need_errmsg
= 1; goto done
; }
482 de_arch_destroy_md(c
, md
);
486 md
= de_arch_create_md(c
, d
);
488 md
->member_hdr_pos
= pos
;
490 tscomp_do_member(c
, d
, md
);
492 if(!md
->next_member_exists
) goto done
;
493 pos
= md
->next_member_pos
;
499 de_err(c
, "Bad or unsupported TSComp format");
501 de_arch_destroy_md(c
, md
);
502 de_arch_destroy_lctx(c
, d
);
503 de_dbg_indent_restore(c
, saved_indent_level
);
506 static int de_identify_tscomp(deark
*c
)
511 // Note: The "13" might be a version number. The "8c" is a mystery,
512 // and seems to be ignored.
513 if(n
== 0x655d138cU
) return 100;
517 void de_module_tscomp(deark
*c
, struct deark_module_info
*mi
)
520 mi
->desc
= "The Stirling Compressor";
521 mi
->run_fn
= de_run_tscomp
;
522 mi
->identify_fn
= de_identify_tscomp
;