1 // This file is part of Deark.
2 // Copyright (C) 2021 Jason Summers
3 // See the file COPYING for terms of use.
7 #include <deark-private.h>
8 #include <deark-fmtutil.h>
9 DE_DECLARE_MODULE(de_module_is_z
);
11 #define ISZ_MAX_DIRS 1000 // arbitrary
12 #define ISZ_MAX_FILES 5000 // arbitrary
13 #define ISZ_MAX_DIR_NAME_LEN 32768 // arbitrary
14 #define ISZ_SIGNATURE 0x8c655d13U
16 struct dir_array_item
{
26 de_ucstring
*full_fname
;
27 struct de_timestamp mod_time
;
30 typedef struct localctx_struct
{
31 de_encoding input_encoding
;
37 struct dir_array_item
*dir_array
; // array[num_dirs]
40 static void extract_file(deark
*c
, lctx
*d
, struct member_data
*md
)
44 struct de_dfilter_in_params dcmpri
;
45 struct de_dfilter_out_params dcmpro
;
46 struct de_dfilter_results dres
;
48 if(md
->cmpr_data_pos
+ md
->cmpr_size
> c
->infile
->len
) {
49 de_err(c
, "%s: Data goes beyond end of file",
50 ucstring_getpsz_d(md
->fname
));
54 fi
= de_finfo_create(c
);
55 de_finfo_set_name_from_ucstring(c
, fi
, md
->full_fname
, DE_SNFLAG_FULLPATH
);
56 fi
->original_filename_flag
= 1;
57 fi
->timestamp
[DE_TIMESTAMPIDX_MODIFY
] = md
->mod_time
;
59 outf
= dbuf_create_output_file(c
, NULL
, fi
, 0);
61 de_dfilter_init_objects(c
, &dcmpri
, &dcmpro
, &dres
);
63 dcmpri
.pos
= md
->cmpr_data_pos
;
64 dcmpri
.len
= md
->cmpr_size
;
67 dcmpro
.expected_len
= md
->orig_size
;
69 fmtutil_dclimplode_codectype1(c
, &dcmpri
, &dcmpro
, &dres
, NULL
);
71 de_err(c
, "%s: Decompression failed: %s", ucstring_getpsz_d(md
->fname
),
72 de_dfilter_get_errmsg(c
, &dres
));
78 de_finfo_destroy(c
, fi
);
81 static void read_timestamp(deark
*c
, lctx
*d
, struct de_timestamp
*ts
,
82 i64 pos
, const char *name
)
84 i64 mod_time_raw
, mod_date_raw
;
85 char timestamp_buf
[64];
87 mod_date_raw
= de_getu16le(pos
);
88 mod_time_raw
= de_getu16le(pos
+2);
89 de_dos_datetime_to_timestamp(ts
, mod_date_raw
, mod_time_raw
);
90 ts
->tzcode
= DE_TZCODE_LOCAL
;
91 de_timestamp_to_string(ts
, timestamp_buf
, sizeof(timestamp_buf
), 0);
92 de_dbg(c
, "%s: %s", name
, timestamp_buf
);
95 static int do_one_file(deark
*c
, lctx
*d
, i64 pos1
, i64
*pbytes_consumed
)
101 struct member_data
*md
= NULL
;
102 struct dir_array_item
*di
= NULL
;
103 int saved_indent_level
;
105 de_dbg_indent_save(c
, &saved_indent_level
);
106 md
= de_malloc(c
, sizeof(struct member_data
));
107 de_dbg(c
, "file entry at %"I64_FMT
, pos1
);
112 md
->dir_id
= (UI
)de_getu16le_p(&pos
); // 1
113 de_dbg(c
, "dir id: %u", md
->dir_id
);
114 if(md
->dir_id
>= d
->num_dirs
) {
115 de_err(c
, "Invalid directory");
118 di
= &d
->dir_array
[md
->dir_id
];
120 md
->orig_size
= de_getu32le_p(&pos
); // 3
121 de_dbg(c
, "orig size: %"I64_FMT
, md
->orig_size
);
122 md
->cmpr_size
= de_getu32le_p(&pos
); // 7
123 de_dbg(c
, "cmpr size: %"I64_FMT
, md
->cmpr_size
);
124 md
->cmpr_data_pos
= de_getu32le_p(&pos
); // 11
125 de_dbg(c
, "cmpr data pos: %"I64_FMT
, md
->cmpr_data_pos
);
127 read_timestamp(c
, d
, &md
->mod_time
, pos
, "mod time");
130 pos
+= 4; // ? (maybe a bit-field?)
132 segment_size
= de_getu16le_p(&pos
);
133 de_dbg(c
, "segment size: %"I64_FMT
, segment_size
);
134 if(segment_size
<30) goto done
;
138 name_len
= (i64
)de_getbyte_p(&pos
);
139 de_dbg(c
, "name len: %"I64_FMT
, name_len
);
140 md
->fname
= ucstring_create(c
);
141 dbuf_read_to_ucstring(c
->infile
, pos
, name_len
, md
->fname
, 0, d
->input_encoding
);
142 de_dbg(c
, "name: \"%s\"", ucstring_getpsz_d(md
->fname
));
144 if(ucstring_isempty(md
->fname
)) {
145 ucstring_append_char(md
->fname
, '_');
147 md
->full_fname
= ucstring_clone(di
->dname
);
148 ucstring_append_ucstring(md
->full_fname
, md
->fname
);
149 de_dbg(c
, "full name: \"%s\"", ucstring_getpsz_d(md
->full_fname
));
151 *pbytes_consumed
= segment_size
;
154 extract_file(c
, d
, md
);
158 ucstring_destroy(md
->fname
);
159 ucstring_destroy(md
->full_fname
);
162 de_dbg_indent_restore(c
, saved_indent_level
);
166 static void do_filelist(deark
*c
, lctx
*d
)
168 i64 pos
= d
->filelist_pos
;
171 for(i
=0; i
<d
->num_files_total
; i
++) {
172 i64 bytes_consumed
= 0;
174 if(!do_one_file(c
, d
, pos
, &bytes_consumed
)) goto done
;
175 if(bytes_consumed
<=0) goto done
;
176 pos
+= bytes_consumed
;
183 // Convert backslashes to slashes, and append a slash if path is not empty.
184 static void fixup_path(de_ucstring
*s
)
190 for(i
=0; i
<s
->len
; i
++) {
191 if(s
->str
[i
]=='\\') {
196 if(s
->str
[s
->len
-1]!='/') {
197 ucstring_append_char(s
, '/');
201 static int do_onedir(deark
*c
, lctx
*d
, i64 dir_idx
, i64 pos1
, i64
*pbytes_consumed
)
209 struct dir_array_item
*di
;
210 int saved_indent_level
;
212 de_dbg_indent_save(c
, &saved_indent_level
);
214 de_dbg(c
, "dir entry at %"I64_FMT
, pos
);
217 if(dir_idx
<0 || dir_idx
>=d
->num_dirs
) goto done
;
218 di
= &d
->dir_array
[dir_idx
];
220 num_files
= de_getu16le_p(&pos
);
221 de_dbg(c
, "num files in this dir: %"I64_FMT
, num_files
);
222 segment_size
= de_getu16le_p(&pos
);
223 de_dbg(c
, "segment size: %"I64_FMT
, segment_size
);
224 if(segment_size
<6) goto done
;
225 endpos
= pos1
+ segment_size
;
227 name_len
= de_getu16le_p(&pos
);
228 de_dbg(c
, "dir name len: %"I64_FMT
, name_len
);
229 if(pos
+name_len
> endpos
) goto done
;
230 if(name_len
> ISZ_MAX_DIR_NAME_LEN
) goto done
;
232 di
->dname
= ucstring_create(c
);
233 dbuf_read_to_ucstring(c
->infile
, pos
, name_len
, di
->dname
, 0, d
->input_encoding
);
234 de_dbg(c
, "dir name: \"%s\"", ucstring_getpsz_d(di
->dname
));
235 fixup_path(di
->dname
);
237 *pbytes_consumed
= segment_size
;
240 de_dbg_indent_restore(c
, saved_indent_level
);
244 static void do_dirlist(deark
*c
, lctx
*d
)
249 pos
= d
->directory_pos
;
250 for(i
=0; i
<d
->num_dirs
; i
++) {
251 i64 bytes_consumed
= 0;
253 if(pos
>= c
->infile
->len
) goto done
;
254 if(!do_onedir(c
, d
, i
, pos
, &bytes_consumed
)) goto done
;
255 if(bytes_consumed
<=0) goto done
;
256 pos
+= bytes_consumed
;
262 static void de_run_is_z(deark
*c
, de_module_params
*mparams
)
265 int saved_indent_level
;
266 struct de_timestamp tmp_timestamp
;
268 de_dbg_indent_save(c
, &saved_indent_level
);
269 d
= de_malloc(c
, sizeof(lctx
));
270 d
->input_encoding
= de_get_input_encoding(c
, NULL
, DE_ENCODING_WINDOWS1252
);
272 // 0 13 5D 65 8C = signature
275 // 12 ui16 number of files
279 // 41 ui32 offset of directory sequence
281 // 49 ui16 number of dirs
282 // 51 ui32 offset of files sequence
284 if(de_getu32le(0)!=ISZ_SIGNATURE
) {
285 de_err(c
, "Not an InstallShield Z file");
289 de_dbg(c
, "main header");
292 d
->num_files_total
= de_getu16le(12);
293 de_dbg(c
, "total number of files: %"I64_FMT
, d
->num_files_total
);
294 if(d
->num_files_total
>ISZ_MAX_FILES
) goto done
;
296 read_timestamp(c
, d
, &tmp_timestamp
, 14, "timestamp");
298 d
->directory_pos
= de_getu32le(41);
299 de_dbg(c
, "start of dir entries: %"I64_FMT
, d
->directory_pos
);
301 d
->num_dirs
= de_getu16le(49);
302 de_dbg(c
, "number of dirs: %"I64_FMT
, d
->num_dirs
);
303 if(d
->num_dirs
>ISZ_MAX_DIRS
) goto done
;
305 d
->filelist_pos
= de_getu32le(51);
306 de_dbg(c
, "start of file entries: %"I64_FMT
, d
->filelist_pos
);
308 de_dbg_indent(c
, -1);
310 d
->dir_array
= de_mallocarray(c
, d
->num_dirs
, sizeof(struct dir_array_item
));
320 for(i
=0; i
<d
->num_dirs
; i
++) {
321 ucstring_destroy(d
->dir_array
[i
].dname
);
323 de_free(c
, d
->dir_array
);
327 de_dbg_indent_restore(c
, saved_indent_level
);
330 static int de_identify_is_z(deark
*c
)
332 if(de_getu32le(0)==ISZ_SIGNATURE
)
337 void de_module_is_z(deark
*c
, struct deark_module_info
*mi
)
340 mi
->desc
= "InstallShield Z";
341 mi
->run_fn
= de_run_is_z
;
342 mi
->identify_fn
= de_identify_is_z
;