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
19 i64 num_children
; // valid if is_dir
20 i64 orig_len
; // valid if !is_dir
24 struct de_timestamp mod_time
;
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
;
38 de_dbg(c
, "header at %"I64_FMT
, pos
);
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
);
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
);
75 dcmpri
.len
= md
->cmpr_len
;
78 dcmpro
.expected_len
= md
->orig_len
;
80 decompress_zoo_lzd(c
, &dcmpri
, &dcmpro
, &dres
, d
->lzw_maxbits
);
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
)
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");
106 fi
= de_finfo_create(c
);
108 fullfn
= ucstring_create(c
);
110 fi
->is_directory
= 1;
111 de_strarray_make_path(d
->curpath
, fullfn
, DE_MPFLAG_NOTRAILINGSLASH
);
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
);
128 dbuf_copy(c
->infile
, pos
, md
->cmpr_len
, 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
140 static void convert_root_name(deark
*c
, struct pdctx_struct
*d
,
141 de_ucstring
*nsrc
, de_ucstring
*ndst
)
145 for(k
=0; k
<nsrc
->len
; k
++) {
146 i32 ch
= nsrc
->str
[k
];
147 if(ch
=='.' || ch
==':') {
148 ucstring_empty(ndst
);
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
;
166 struct pdctx_object
*md
= NULL
;
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
) {
177 md
= de_malloc(c
, sizeof(struct pdctx_object
));
178 de_dbg(c
, "object at %"I64_FMT
, pos1
);
180 *bytes_consumed1
= 0;
182 if(!dbuf_search_byte(c
->infile
, 0x00, pos
, 128, &foundpos
)) {
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
));
191 de_zeromem(&rfa
, sizeof(struct de_riscos_file_attrs
));
192 fmtutil_riscos_read_load_exec(c
, c
->infile
, &rfa
, pos
);
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);
200 md
->attribs
= rfa
.attribs
;
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) {
213 else if(md
->object_type
==1) {
217 goto done
; // unknown type
221 i64 bytes_consumed2
= 0;
225 md
->num_children
= length_raw
;
226 de_dbg(c
, "number of dir entries: %"I64_FMT
, md
->num_children
);
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
);
235 de_strarray_push(d
->curpath
, md
->name
);
236 need_dirname_pop
= 1;
239 md
->is_compressed
= 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
;
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
) {
258 md
->cmpr_len
= md
->orig_len
;
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
);
273 *bytes_consumed1
= pos
- pos1
;
277 if(!retval
&& c
->error_count
==0) {
278 de_err(c
, "Can't parse object at %"I64_FMT
, pos1
);
281 ucstring_destroy(md
->name
);
284 if(need_dirname_pop
) {
285 de_strarray_pop(d
->curpath
);
287 de_dbg_indent_restore(c
, saved_indent_level
);
291 static void de_run_packdir(deark
*c
, de_module_params
*mparams
)
293 struct pdctx_struct
*d
= NULL
;
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
);
304 de_strarray_destroy(d
->curpath
);
309 static int de_identify_packdir(deark
*c
)
313 if(dbuf_memcmp(c
->infile
, 0, "PACK\0", 5)) return 0;
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
)
323 mi
->desc
= "PackDir compressed archive format";
324 mi
->run_fn
= de_run_packdir
;
325 mi
->identify_fn
= de_identify_packdir
;