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
18 i64 num_children
; // valid if is_dir
19 i64 orig_len
; // valid if !is_dir
23 struct de_riscos_file_attrs rfa
;
27 unsigned int lzw_maxbits
;
28 struct de_strarray
*curpath
;
31 static int do_packdir_header(deark
*c
, struct pdctx_struct
*d
)
33 unsigned int maxbits_raw
;
37 de_dbg(c
, "header at %"I64_FMT
, pos
);
39 pos
+= 5; // signature
40 maxbits_raw
= (unsigned int)de_getu32le_p(&pos
);
41 d
->lzw_maxbits
= maxbits_raw
+ 12;
42 de_dbg(c
, "lzw maxbits: %u (+12=%u)", maxbits_raw
, d
->lzw_maxbits
);
43 if(d
->lzw_maxbits
>16) {
44 de_err(c
, "Unsupported \"maxbits\" value: %u", d
->lzw_maxbits
);
53 static void decompress_zoo_lzd(deark
*c
, struct de_dfilter_in_params
*dcmpri
,
54 struct de_dfilter_out_params
*dcmpro
, struct de_dfilter_results
*dres
, int maxbits
)
56 struct de_lzw_params delzwp
;
58 de_zeromem(&delzwp
, sizeof(struct de_lzw_params
));
59 delzwp
.fmt
= DE_LZWFMT_ZOOLZD
;
60 delzwp
.max_code_size
= (unsigned int)maxbits
;
61 fmtutil_decompress_lzw(c
, dcmpri
, dcmpro
, dres
, &delzwp
);
64 static void do_packdir_file_compressed(deark
*c
, struct pdctx_struct
*d
,
65 struct pdctx_object
*md
, i64 pos
, dbuf
*outf
)
67 struct de_dfilter_in_params dcmpri
;
68 struct de_dfilter_out_params dcmpro
;
69 struct de_dfilter_results dres
;
71 de_dfilter_init_objects(c
, &dcmpri
, &dcmpro
, &dres
);
74 dcmpri
.len
= md
->cmpr_len
;
77 dcmpro
.expected_len
= md
->orig_len
;
79 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
);
118 fmtutil_riscos_append_type_to_filename(c
, fi
, fullfn
, &md
->rfa
, md
->is_dir
, 0);
119 de_finfo_set_name_from_ucstring(c
, fi
, fullfn
, DE_SNFLAG_FULLPATH
);
120 fi
->original_filename_flag
= 1;
122 fi
->timestamp
[DE_TIMESTAMPIDX_MODIFY
] = md
->rfa
.mod_time
;
124 fi
->has_riscos_data
= 1;
125 fi
->riscos_attribs
= md
->rfa
.attribs
;
126 fi
->load_addr
= md
->rfa
.load_addr
;
127 fi
->exec_addr
= md
->rfa
.exec_addr
;
129 outf
= dbuf_create_output_file(c
, NULL
, fi
, 0);
131 if(md
->is_compressed
) {
132 dbuf_enable_wbuffer(outf
);
133 do_packdir_file_compressed(c
, d
, md
, pos
, outf
);
136 dbuf_copy(c
->infile
, pos
, md
->cmpr_len
, outf
);
141 de_finfo_destroy(c
, fi
);
142 ucstring_destroy(fullfn
);
145 // The name of the root object is usually something ugly like
146 // "RAM::RamDisc0.$.MyProg". Try to make it nicer by only using the last part
148 static void convert_root_name(deark
*c
, struct pdctx_struct
*d
,
149 de_ucstring
*nsrc
, de_ucstring
*ndst
)
153 for(k
=0; k
<nsrc
->len
; k
++) {
154 i32 ch
= nsrc
->str
[k
];
155 if(ch
=='.' || ch
==':') {
156 ucstring_empty(ndst
);
159 ucstring_append_char(ndst
, ch
);
164 // Process and object, and all its descendants.
165 // Returns 0 on fatal error.
166 static int do_packdir_object(deark
*c
, struct pdctx_struct
*d
, i64 pos1
,
167 int level
, i64
*bytes_consumed1
)
169 int saved_indent_level
;
174 struct pdctx_object
*md
= NULL
;
176 int need_dirname_pop
= 0;
178 de_dbg_indent_save(c
, &saved_indent_level
);
180 if(level
>= MAX_NESTING_LEVEL
) {
184 md
= de_malloc(c
, sizeof(struct pdctx_object
));
185 de_dbg(c
, "object at %"I64_FMT
, pos1
);
187 *bytes_consumed1
= 0;
189 if(!dbuf_search_byte(c
->infile
, 0x00, pos
, 128, &foundpos
)) {
192 name_len
= foundpos
- pos1
;
193 md
->name
= ucstring_create(c
);
194 dbuf_read_to_ucstring(c
->infile
, pos
, name_len
, md
->name
, 0x0, DE_ENCODING_RISCOS
);
195 de_dbg(c
, "name: \"%s\"", ucstring_getpsz_d(md
->name
));
198 fmtutil_riscos_read_load_exec(c
, c
->infile
, &md
->rfa
, pos
);
201 length_raw
= de_getu32le_p(&pos
);
203 fmtutil_riscos_read_attribs_field(c
, c
->infile
, &md
->rfa
, pos
, 0);
210 md
->object_type
= (u32
)de_getu32le_p(&pos
);
211 de_dbg(c
, "type: %u", (unsigned int)md
->object_type
);
214 if(md
->object_type
==0) {
217 else if(md
->object_type
==1) {
221 goto done
; // unknown type
225 i64 bytes_consumed2
= 0;
229 md
->num_children
= length_raw
;
230 de_dbg(c
, "number of dir entries: %"I64_FMT
, md
->num_children
);
233 de_ucstring
*tmpstr
= ucstring_create(c
);
234 convert_root_name(c
, d
, md
->name
, tmpstr
);
235 de_strarray_push(d
->curpath
, tmpstr
);
236 ucstring_destroy(tmpstr
);
239 de_strarray_push(d
->curpath
, md
->name
);
240 need_dirname_pop
= 1;
243 md
->is_compressed
= 0;
246 do_packdir_extract_file(c
, d
, md
, pos
);
248 for(i
=0; i
<md
->num_children
; i
++) {
249 if(pos
>= c
->infile
->len
) goto done
;
250 ret
= do_packdir_object(c
, d
, pos
, level
+1, &bytes_consumed2
);
251 if((!ret
) || bytes_consumed2
<1) goto done
;
252 pos
+= bytes_consumed2
;
256 md
->orig_len
= length_raw
;
257 de_dbg(c
, "original len: %"I64_FMT
, md
->orig_len
);
259 md
->cmpr_len
= de_getu32le_p(&pos
);
260 if(md
->cmpr_len
==0xffffffffLL
) {
262 md
->cmpr_len
= md
->orig_len
;
265 md
->is_compressed
= 1;
267 de_dbg(c
, "is compressed: %d", md
->is_compressed
);
268 if(md
->is_compressed
) {
269 de_dbg(c
, "cmpr len: %"I64_FMT
, md
->cmpr_len
);
272 do_packdir_extract_file(c
, d
, md
, pos
);
277 *bytes_consumed1
= pos
- pos1
;
281 if(!retval
&& c
->error_count
==0) {
282 de_err(c
, "Can't parse object at %"I64_FMT
, pos1
);
285 ucstring_destroy(md
->name
);
288 if(need_dirname_pop
) {
289 de_strarray_pop(d
->curpath
);
291 de_dbg_indent_restore(c
, saved_indent_level
);
295 static void de_run_packdir(deark
*c
, de_module_params
*mparams
)
297 struct pdctx_struct
*d
= NULL
;
300 d
= de_malloc(c
, sizeof(struct pdctx_struct
));
302 if(!do_packdir_header(c
, d
)) goto done
;
303 d
->curpath
= de_strarray_create(c
, MAX_NESTING_LEVEL
+10);
304 do_packdir_object(c
, d
, 9, 0, &bytes_consumed
);
308 de_strarray_destroy(d
->curpath
);
313 static int de_identify_packdir(deark
*c
)
317 if(dbuf_memcmp(c
->infile
, 0, "PACK\0", 5)) return 0;
319 if(n
<=4) return 100; // maxbits = 12...16
320 if(n
<=8) return 10; // Dunno what the "maxbits" limit is.
321 return 0; // Could be Git pack format
324 void de_module_packdir(deark
*c
, struct deark_module_info
*mi
)
327 mi
->desc
= "PackDir compressed archive format";
328 mi
->run_fn
= de_run_packdir
;
329 mi
->identify_fn
= de_identify_packdir
;