coding style: Avoid using 'for' loop initial declarations
[deark.git] / modules / isz.c
blobb84e77e5e2bb1a2d269ea6d509fa118a21c65a16
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
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 {
17 de_ucstring *dname;
20 struct member_data {
21 i64 orig_size;
22 i64 cmpr_size;
23 i64 cmpr_data_pos;
24 UI dir_id;
25 de_ucstring *fname;
26 de_ucstring *full_fname;
27 struct de_timestamp mod_time;
30 typedef struct localctx_struct {
31 de_encoding input_encoding;
32 i64 directory_pos;
33 i64 filelist_pos;
34 i64 num_dirs;
35 i64 num_files_total;
37 struct dir_array_item *dir_array; // array[num_dirs]
38 } lctx;
40 static void extract_file(deark *c, lctx *d, struct member_data *md)
42 dbuf *outf = NULL;
43 de_finfo *fi = NULL;
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));
51 goto done;
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);
62 dcmpri.f = c->infile;
63 dcmpri.pos = md->cmpr_data_pos;
64 dcmpri.len = md->cmpr_size;
65 dcmpro.f = outf;
66 dcmpro.len_known = 1;
67 dcmpro.expected_len = md->orig_size;
69 fmtutil_dclimplode_codectype1(c, &dcmpri, &dcmpro, &dres, NULL);
70 if(dres.errcode) {
71 de_err(c, "%s: Decompression failed: %s", ucstring_getpsz_d(md->fname),
72 de_dfilter_get_errmsg(c, &dres));
73 goto done;
76 done:
77 dbuf_close(outf);
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)
97 i64 pos = pos1;
98 i64 name_len;
99 i64 segment_size;
100 int retval = 0;
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);
108 de_dbg_indent(c, 1);
110 pos += 1; // ?
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");
116 goto done;
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");
128 pos += 4;
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;
136 pos += 4; // ?
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;
152 retval = 1;
154 extract_file(c, d, md);
156 done:
157 if(md) {
158 ucstring_destroy(md->fname);
159 ucstring_destroy(md->full_fname);
160 de_free(c, md);
162 de_dbg_indent_restore(c, saved_indent_level);
163 return retval;
166 static void do_filelist(deark *c, lctx *d)
168 i64 pos = d->filelist_pos;
169 i64 i;
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;
179 done:
183 // Convert backslashes to slashes, and append a slash if path is not empty.
184 static void fixup_path(de_ucstring *s)
186 i64 i;
188 if(s->len<1) return;
190 for(i=0; i<s->len; i++) {
191 if(s->str[i]=='\\') {
192 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)
203 i64 num_files;
204 i64 segment_size;
205 i64 name_len;
206 i64 pos = pos1;
207 i64 endpos;
208 int retval = 0;
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);
215 de_dbg_indent(c, 1);
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;
238 retval = 1;
239 done:
240 de_dbg_indent_restore(c, saved_indent_level);
241 return retval;
244 static void do_dirlist(deark *c, lctx *d)
246 i64 pos;
247 i64 i;
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;
258 done:
262 static void de_run_is_z(deark *c, de_module_params *mparams)
264 lctx *d = NULL;
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
273 // 4 3A 01 02 00 = ?
274 // 8... ?
275 // 12 ui16 number of files
276 // 14 ui16 date
277 // 16 ui16 time
278 // 18... ?
279 // 41 ui32 offset of directory sequence
280 // 45... ?
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");
286 goto done;
289 de_dbg(c, "main header");
290 de_dbg_indent(c, 1);
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));
312 do_dirlist(c, d);
313 do_filelist(c, d);
315 done:
316 if(d) {
317 if(d->dir_array) {
318 i64 i;
320 for(i=0; i<d->num_dirs; i++) {
321 ucstring_destroy(d->dir_array[i].dname);
323 de_free(c, d->dir_array);
325 de_free(c, d);
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)
333 return 100;
334 return 0;
337 void de_module_is_z(deark *c, struct deark_module_info *mi)
339 mi->id = "is_z";
340 mi->desc = "InstallShield Z";
341 mi->run_fn = de_run_is_z;
342 mi->identify_fn = de_identify_is_z;