fnt: Improved error handling, etc.
[deark.git] / modules / installshld.c
blobafaf9f515cb8e3a4ea535bbaf4c949240b6fc587
1 // This file is part of Deark.
2 // Copyright (C) 2021 Jason Summers
3 // See the file COPYING for terms of use.
5 // InstallShield Z
6 // InstallShield installer archive
7 // Etc.
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 {
27 de_ucstring *dname;
30 // We don't need this struct; it's just here for future expansion
31 struct isz_member_data {
32 UI dir_id;
35 struct isz_ctx {
36 struct de_arch_localctx_struct *da;
37 i64 directory_pos;
38 i64 filelist_pos;
39 i64 num_dirs;
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)
46 i64 pos = pos1;
47 i64 name_len;
48 i64 segment_size;
49 int retval = 0;
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);
61 de_dbg_indent(c, 1);
63 pos += 1; // ?
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");
69 goto done;
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;
86 pos += 4; // ?
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;
102 retval = 1;
104 md->set_name_flags |= DE_SNFLAG_FULLPATH;
105 md->dfn = dclimplode_decompressor_fn;
106 de_arch_extract_member_file(md);
108 done:
109 de_arch_destroy_md(c, md);
110 de_free(c, mdi);
111 de_dbg_indent_restore(c, saved_indent_level);
112 return retval;
115 static void isz_do_filelist(deark *c, struct isz_ctx *d)
117 i64 pos = d->filelist_pos;
118 i64 i;
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;
128 done:
132 static int isz_do_onedir(deark *c, struct isz_ctx *d, i64 dir_idx, i64 pos1, i64 *pbytes_consumed)
134 i64 num_files;
135 i64 segment_size;
136 i64 name_len;
137 i64 pos = pos1;
138 i64 endpos;
139 int retval = 0;
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);
146 de_dbg_indent(c, 1);
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;
169 retval = 1;
170 done:
171 de_dbg_indent_restore(c, saved_indent_level);
172 return retval;
175 static void isz_do_dirlist(deark *c, struct isz_ctx *d)
177 i64 pos;
178 i64 i;
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;
189 done:
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;
198 i64 tmp_pos;
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;
204 d->da->is_le = 1;
206 d->da->input_encoding = de_get_input_encoding(c, NULL, DE_ENCODING_WINDOWS1252);
208 // 0 13 5D 65 8C = signature
209 // 4 3A 01 02 00 = ?
210 // 8... ?
211 // 12 ui16 number of files
212 // 14 ui16 date
213 // 16 ui16 time
214 // 18... ?
215 // 41 ui32 offset of directory sequence
216 // 45... ?
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");
222 goto done;
225 de_dbg(c, "main header");
226 de_dbg_indent(c, 1);
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;
232 tmp_pos = 14;
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);
253 done:
254 if(d) {
255 if(d->dir_array) {
256 i64 i;
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);
264 de_free(c, d);
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)
272 return 100;
273 return 0;
276 void de_module_is_z(deark *c, struct deark_module_info *mi)
278 mi->id = "is_z";
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)
290 int retval = 0;
291 int saved_indent_level;
292 i64 pos = md->member_hdr_pos;
293 i64 nlen;
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);
299 de_dbg_indent(c, 1);
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);
306 pos += 8; // Unused?
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));
311 pos += nlen;
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));
318 pos += nlen;
320 if(!de_arch_good_cmpr_data_pos(md)) {
321 goto done;
324 md->dfn = dclimplode_decompressor_fn;
325 de_arch_extract_member_file(md);
327 md->member_hdr_size = pos - md->member_hdr_pos;
328 retval = 1;
329 done:
330 ucstring_destroy(tmps);
331 de_dbg_indent_restore(c, saved_indent_level);
332 return retval;
335 static void de_run_is_instarch(deark *c, de_module_params *mparams)
337 de_arch_lctx *da = NULL;
338 i64 pos;
339 i64 i;
340 struct de_arch_member_data *md = NULL;
342 da = de_arch_create_lctx(c);
343 da->is_le = 1;
344 da->input_encoding = de_get_input_encoding(c, NULL, DE_ENCODING_WINDOWS1252);
346 pos = 78;
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);
349 pos += 2;
351 for(i=0; i<da->num_members; i++) {
352 if(pos >= c->infile->len) goto done;
354 if(md) {
355 de_arch_destroy_md(c, md);
356 md = NULL;
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;
365 done:
366 if(md) {
367 de_arch_destroy_md(c, md);
369 if(da) {
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)) {
377 return 0;
379 return 100;
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;
401 i64 fnlen;
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,
406 md->member_hdr_pos);
407 de_dbg_indent(c, 1);
409 pos += 1;
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);
422 pos += 2; // ??
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,
428 d->input_encoding);
429 de_dbg(c, "filename: \"%s\"", ucstring_getpsz_d(md->filename));
430 pos += fnlen;
431 pos += 1; // ??
433 md->cmpr_pos = pos;
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;
442 i64 pos;
443 i64 i;
444 int saved_indent_level;
445 u8 b;
446 const char *name;
447 struct de_arch_member_data *md = NULL;
449 de_dbg_indent_save(c, &saved_indent_level);
450 d = de_arch_create_lctx(c);
451 d->is_le = 1;
452 d->input_encoding = de_get_input_encoding(c, NULL, DE_ENCODING_CP437);
454 pos = 0;
455 de_dbg(c, "archive header at %d", (int)pos);
456 de_dbg_indent(c, 1);
457 pos += 4;
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);
463 switch(b) {
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).
468 default: name = "?";
470 de_dbg(c, "filename style: %u (%s)", (UI)b, name);
471 if(b!=1 && b!=2) { d->need_errmsg = 1; goto done; }
473 pos += 4; // ??
474 de_dbg_indent(c, -1);
476 i = 0;
477 while(1) {
478 if(d->fatalerrflag) goto done;
479 if(de_getbyte(pos) != 0x12) { d->need_errmsg = 1; goto done; }
481 if(md) {
482 de_arch_destroy_md(c, md);
483 md = NULL;
486 md = de_arch_create_md(c, d);
487 md->member_idx = i;
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;
494 i++;
497 done:
498 if(d->need_errmsg) {
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)
508 i64 n;
510 n = de_getu32be(0);
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;
514 return 0;
517 void de_module_tscomp(deark *c, struct deark_module_info *mi)
519 mi->id = "tscomp";
520 mi->desc = "The Stirling Compressor";
521 mi->run_fn = de_run_tscomp;
522 mi->identify_fn = de_identify_tscomp;