lzah: Improved some debug messages
[deark.git] / modules / packdir.c
blob1d0aa070272e67b5d9f5b8833ffc7c7a51cd9376
1 // This file is part of Deark.
2 // Copyright (C) 2019 Jason Summers
3 // See the file COPYING for terms of use.
5 // PackDir compressed archive format
7 #include <deark-config.h>
8 #include <deark-private.h>
9 #include <deark-fmtutil.h>
11 DE_DECLARE_MODULE(de_module_packdir);
13 #define MAX_NESTING_LEVEL 32
15 struct pdctx_object {
16 u32 attribs;
17 u32 object_type;
18 u8 is_dir;
19 i64 num_children; // valid if is_dir
20 i64 orig_len; // valid if !is_dir
21 i64 cmpr_len;
22 int is_compressed;
23 de_ucstring *name;
24 struct de_timestamp mod_time;
27 struct pdctx_struct {
28 unsigned int lzw_maxbits;
29 struct de_strarray *curpath;
32 static int do_packdir_header(deark *c, struct pdctx_struct *d)
34 unsigned int maxbits_raw;
35 i64 pos = 0;
36 int retval = 0;
38 de_dbg(c, "header at %"I64_FMT, pos);
39 de_dbg_indent(c, 1);
40 pos += 5; // signature
41 maxbits_raw = (unsigned int)de_getu32le_p(&pos);
42 d->lzw_maxbits = maxbits_raw + 12;
43 de_dbg(c, "lzw maxbits: %u (+12=%u)", maxbits_raw, d->lzw_maxbits);
44 if(d->lzw_maxbits>16) {
45 de_err(c, "Unspported \"maxbits\" value: %u", d->lzw_maxbits);
46 goto done;
48 retval = 1;
49 done:
50 de_dbg_indent(c, -1);
51 return retval;
54 static void decompress_zoo_lzd(deark *c, struct de_dfilter_in_params *dcmpri,
55 struct de_dfilter_out_params *dcmpro, struct de_dfilter_results *dres, int maxbits)
57 struct de_lzw_params delzwp;
59 de_zeromem(&delzwp, sizeof(struct de_lzw_params));
60 delzwp.fmt = DE_LZWFMT_ZOOLZD;
61 delzwp.max_code_size = (unsigned int)maxbits;
62 fmtutil_decompress_lzw(c, dcmpri, dcmpro, dres, &delzwp);
65 static void do_packdir_file_compressed(deark *c, struct pdctx_struct *d,
66 struct pdctx_object *md, i64 pos, dbuf *outf)
68 struct de_dfilter_in_params dcmpri;
69 struct de_dfilter_out_params dcmpro;
70 struct de_dfilter_results dres;
72 de_dfilter_init_objects(c, &dcmpri, &dcmpro, &dres);
73 dcmpri.f = c->infile;
74 dcmpri.pos = pos;
75 dcmpri.len = md->cmpr_len;
76 dcmpro.f = outf;
77 dcmpro.len_known = 1;
78 dcmpro.expected_len = md->orig_len;
80 decompress_zoo_lzd(c, &dcmpri, &dcmpro, &dres, d->lzw_maxbits);
82 if(dres.errcode) {
83 de_err(c, "%s: %s", ucstring_getpsz_d(md->name), de_dfilter_get_errmsg(c, &dres));
85 else if(outf->len != md->orig_len) {
86 de_err(c, "%s: Expected %"I64_FMT" decompressed bytes, got %"I64_FMT,
87 ucstring_getpsz_d(md->name), md->orig_len, outf->len);
91 static void do_packdir_extract_file(deark *c, struct pdctx_struct *d,
92 struct pdctx_object *md, i64 pos)
94 dbuf *outf = NULL;
95 de_finfo *fi = NULL;
96 de_ucstring *fullfn = NULL;
98 de_dbg(c, "%"I64_FMT" bytes of %scompressed data at %"I64_FMT,
99 md->cmpr_len, (md->is_compressed?"":"un"), pos);
101 if(pos + md->cmpr_len > c->infile->len) {
102 de_err(c, "Unexpected EOF");
103 goto done;
106 fi = de_finfo_create(c);
108 fullfn = ucstring_create(c);
109 if(md->is_dir) {
110 fi->is_directory = 1;
111 de_strarray_make_path(d->curpath, fullfn, DE_MPFLAG_NOTRAILINGSLASH);
113 else {
114 de_strarray_make_path(d->curpath, fullfn, 0);
115 ucstring_append_ucstring(fullfn, md->name);
117 de_finfo_set_name_from_ucstring(c, fi, fullfn, DE_SNFLAG_FULLPATH);
118 fi->original_filename_flag = 1;
120 fi->timestamp[DE_TIMESTAMPIDX_MODIFY] = md->mod_time;
122 outf = dbuf_create_output_file(c, NULL, fi, 0);
124 if(md->is_compressed) {
125 do_packdir_file_compressed(c, d, md, pos, outf);
127 else {
128 dbuf_copy(c->infile, pos, md->cmpr_len, outf);
131 done:
132 dbuf_close(outf);
133 de_finfo_destroy(c, fi);
134 ucstring_destroy(fullfn);
137 // The name of the root object is usually something ugly like
138 // "RAM::RamDisc0.$.MyProg". Try to make it nicer by only using the last part
139 // of it.
140 static void convert_root_name(deark *c, struct pdctx_struct *d,
141 de_ucstring *nsrc, de_ucstring *ndst)
143 i64 k;
145 for(k=0; k<nsrc->len; k++) {
146 i32 ch = nsrc->str[k];
147 if(ch=='.' || ch==':') {
148 ucstring_empty(ndst);
150 else {
151 ucstring_append_char(ndst, ch);
156 // Process and object, and all its descendants.
157 // Returns 0 on fatal error.
158 static int do_packdir_object(deark *c, struct pdctx_struct *d, i64 pos1,
159 int level, i64 *bytes_consumed1)
161 int saved_indent_level;
162 i64 foundpos = 0;
163 i64 pos = pos1;
164 i64 name_len;
165 i64 length_raw;
166 struct pdctx_object *md = NULL;
167 int retval = 0;
168 int need_dirname_pop = 0;
169 struct de_riscos_file_attrs rfa;
171 de_dbg_indent_save(c, &saved_indent_level);
173 if(level >= MAX_NESTING_LEVEL) {
174 goto done;
177 md = de_malloc(c, sizeof(struct pdctx_object));
178 de_dbg(c, "object at %"I64_FMT, pos1);
179 de_dbg_indent(c, 1);
180 *bytes_consumed1 = 0;
182 if(!dbuf_search_byte(c->infile, 0x00, pos, 128, &foundpos)) {
183 goto done;
185 name_len = foundpos - pos1;
186 md->name = ucstring_create(c);
187 dbuf_read_to_ucstring(c->infile, pos, name_len, md->name, 0x0, DE_ENCODING_RISCOS);
188 de_dbg(c, "name: \"%s\"", ucstring_getpsz_d(md->name));
189 pos += name_len + 1;
191 de_zeromem(&rfa, sizeof(struct de_riscos_file_attrs));
192 fmtutil_riscos_read_load_exec(c, c->infile, &rfa, pos);
193 pos += 8;
194 md->mod_time = rfa.mod_time;
196 length_raw = de_getu32le_p(&pos);
198 fmtutil_riscos_read_attribs_field(c, c->infile, &rfa, pos, 0);
199 pos += 4;
200 md->attribs = rfa.attribs;
202 if(level==0) {
203 md->object_type = 1;
205 else {
206 md->object_type = (u32)de_getu32le_p(&pos);
207 de_dbg(c, "type: %u", (unsigned int)md->object_type);
210 if(md->object_type==0) {
211 ; // regular file
213 else if(md->object_type==1) {
214 md->is_dir = 1;
216 else {
217 goto done; // unknown type
220 if(md->is_dir) {
221 i64 bytes_consumed2 = 0;
222 i64 i;
223 int ret;
225 md->num_children = length_raw;
226 de_dbg(c, "number of dir entries: %"I64_FMT, md->num_children);
228 if(level<=0) {
229 de_ucstring *tmpstr = ucstring_create(c);
230 convert_root_name(c, d, md->name, tmpstr);
231 de_strarray_push(d->curpath, tmpstr);
232 ucstring_destroy(tmpstr);
234 else {
235 de_strarray_push(d->curpath, md->name);
236 need_dirname_pop = 1;
239 md->is_compressed = 0;
240 md->orig_len = 0;
241 md->cmpr_len = 0;
242 do_packdir_extract_file(c, d, md, pos);
244 for(i=0; i<md->num_children; i++) {
245 if(pos >= c->infile->len) goto done;
246 ret = do_packdir_object(c, d, pos, level+1, &bytes_consumed2);
247 if((!ret) || bytes_consumed2<1) goto done;
248 pos += bytes_consumed2;
251 else {
252 md->orig_len = length_raw;
253 de_dbg(c, "original len: %"I64_FMT, md->orig_len);
255 md->cmpr_len = de_getu32le_p(&pos);
256 if(md->cmpr_len==0xffffffffLL) {
257 // uncompressed
258 md->cmpr_len = md->orig_len;
260 else {
261 md->is_compressed = 1;
263 de_dbg(c, "is compressed: %d", md->is_compressed);
264 if(md->is_compressed) {
265 de_dbg(c, "cmpr len: %"I64_FMT, md->cmpr_len);
268 do_packdir_extract_file(c, d, md, pos);
270 pos += md->cmpr_len;
273 *bytes_consumed1 = pos - pos1;
274 retval = 1;
276 done:
277 if(!retval && c->error_count==0) {
278 de_err(c, "Can't parse object at %"I64_FMT, pos1);
280 if(md) {
281 ucstring_destroy(md->name);
282 de_free(c, md);
284 if(need_dirname_pop) {
285 de_strarray_pop(d->curpath);
287 de_dbg_indent_restore(c, saved_indent_level);
288 return retval;
291 static void de_run_packdir(deark *c, de_module_params *mparams)
293 struct pdctx_struct *d = NULL;
294 i64 bytes_consumed;
296 d = de_malloc(c, sizeof(struct pdctx_struct));
298 if(!do_packdir_header(c, d)) goto done;
299 d->curpath = de_strarray_create(c, MAX_NESTING_LEVEL+10);
300 do_packdir_object(c, d, 9, 0, &bytes_consumed);
302 done:
303 if(d) {
304 de_strarray_destroy(d->curpath);
305 de_free(c, d);
309 static int de_identify_packdir(deark *c)
311 i64 n;
313 if(dbuf_memcmp(c->infile, 0, "PACK\0", 5)) return 0;
314 n = de_getu32le(5);
315 if(n<=4) return 100; // maxbits = 12...16
316 if(n<=8) return 10; // Dunno what the "maxbits" limit is.
317 return 0; // Could be Git pack format
320 void de_module_packdir(deark *c, struct deark_module_info *mi)
322 mi->id = "packdir";
323 mi->desc = "PackDir compressed archive format";
324 mi->run_fn = de_run_packdir;
325 mi->identify_fn = de_identify_packdir;