1 // This file is part of Deark.
2 // Copyright (C) 2022 Jason Summers
3 // See the file COPYING for terms of use.
5 // Windows Program Information File (.PIF)
6 // DESQview Program Information File (.DVP)
8 #include <deark-private.h>
9 DE_DECLARE_MODULE(de_module_pif
);
10 DE_DECLARE_MODULE(de_module_desqview_dvp
);
12 #define PIF_BASIC_SECTION_SIZE 369
13 #define PIF_INVALID_HEADING_POS 0xffff
16 de_encoding input_encoding_oem
;
17 de_encoding input_encoding_ansi
;
19 i64 next_section_heading_pos
;
20 struct de_inthashtable
*pos_seen
;
24 static int pif_validate_pos(deark
*c
, struct pif_ctx
*d
, i64 pos
)
26 if(de_inthashtable_add_item(c
, d
->pos_seen
, pos
, NULL
)) {
29 de_err(c
, "Bad offset detected");
33 static void do_pif_section_extract(deark
*c
, struct pif_ctx
*d
, i64 pos1
, i64 len
, const char *name
)
35 dbuf_create_file_from_slice(c
->infile
, pos1
, len
, name
, NULL
, 0);
38 static void do_pif_section_default(deark
*c
, struct pif_ctx
*d
, i64 pos1
, i64 len
)
40 de_dbg_hexdump(c
, c
->infile
, pos1
, len
, 256, NULL
, 0x1);
43 static void do_pif_section_basic(deark
*c
, struct pif_ctx
*d
, i64 pos1
, i64 len
)
51 checksum_reported
= (UI
)de_getbyte_p(&pos
);
52 de_dbg(c
, "checksum (reported): 0x%02x", checksum_reported
);
53 d
->checksum_calc
= (UI
)de_calccrc_oneshot(c
->infile
, 2, PIF_BASIC_SECTION_SIZE
-2,
55 d
->checksum_calc
&= 0xff;
56 // Note - Not all files set the checksum field. Often it's just set to 0x78,
57 // but other wrong values are common.
58 de_dbg(c
, "checksum (calculated): 0x%02x", d
->checksum_calc
);
60 ucstring_empty(d
->tmpstr
);
61 dbuf_read_to_ucstring(c
->infile
, pos
, 30, d
->tmpstr
, DE_CONVFLAG_STOP_AT_NUL
,
62 d
->input_encoding_oem
);
63 ucstring_strip_trailing_spaces(d
->tmpstr
);
64 de_dbg(c
, "title: \"%s\"", ucstring_getpsz_d(d
->tmpstr
));
67 n
= de_getu16le_p(&pos
);
68 de_dbg(c
, "max conventional mem: %"I64_FMT
" kb", n
);
69 n
= de_getu16le_p(&pos
);
70 de_dbg(c
, "min conventional mem: %"I64_FMT
" kb", n
);
72 ucstring_empty(d
->tmpstr
);
73 dbuf_read_to_ucstring(c
->infile
, pos
, 63, d
->tmpstr
, DE_CONVFLAG_STOP_AT_NUL
,
74 d
->input_encoding_oem
);
75 ucstring_strip_trailing_spaces(d
->tmpstr
);
76 de_dbg(c
, "target filename: \"%s\"", ucstring_getpsz_d(d
->tmpstr
));
79 // TODO: There's disagreement about what the next 2 bytes are.
80 n
= de_getu16le_p(&pos
);
81 de_dbg(c
, "flags1: 0x%04x", (UI
)n
);
83 ucstring_empty(d
->tmpstr
);
84 dbuf_read_to_ucstring(c
->infile
, pos
, 64, d
->tmpstr
, DE_CONVFLAG_STOP_AT_NUL
,
85 d
->input_encoding_oem
);
86 ucstring_strip_trailing_spaces(d
->tmpstr
);
87 de_dbg(c
, "work dir: \"%s\"", ucstring_getpsz_d(d
->tmpstr
));
90 ucstring_empty(d
->tmpstr
);
91 dbuf_read_to_ucstring(c
->infile
, pos
, 64, d
->tmpstr
, DE_CONVFLAG_STOP_AT_NUL
,
92 d
->input_encoding_oem
);
93 ucstring_strip_trailing_spaces(d
->tmpstr
);
94 de_dbg(c
, "params: \"%s\"", ucstring_getpsz_d(d
->tmpstr
));
100 static void do_pif_section_win286(deark
*c
, struct pif_ctx
*d
, i64 pos1
, i64 len
)
107 n
= de_getu16le_p(&pos
);
108 de_dbg(c
, "max XMS: %"I64_FMT
, n
);
109 n
= de_getu16le_p(&pos
);
110 de_dbg(c
, "min XMS: %"I64_FMT
, n
);
111 flags
= (UI
)de_getu16le_p(&pos
);
112 de_dbg(c
, "flags: 0x%04x", (UI
)flags
);
115 static void do_pif_section_win386(deark
*c
, struct pif_ctx
*d
, i64 pos1
, i64 len
)
122 n
= de_getu16le_p(&pos
);
123 de_dbg(c
, "max conventional mem: %"I64_FMT
, n
);
124 n
= de_getu16le_p(&pos
);
125 de_dbg(c
, "min conventional mem: %"I64_FMT
, n
);
126 pos
+= 2; // fg priority
127 pos
+= 2; // bg priority
128 n
= de_getu16le_p(&pos
);
129 de_dbg(c
, "max EMS: %"I64_FMT
, n
);
130 n
= de_getu16le_p(&pos
);
131 de_dbg(c
, "min EMS: %"I64_FMT
, n
);
132 n
= de_getu16le_p(&pos
);
133 de_dbg(c
, "max XMS: %"I64_FMT
, n
);
134 n
= de_getu16le_p(&pos
);
135 de_dbg(c
, "min XMS: %"I64_FMT
, n
);
136 flags
= (UI
)de_getu32le_p(&pos
);
137 de_dbg(c
, "flags1: 0x%08x", (UI
)flags
);
138 flags
= (UI
)de_getu16le_p(&pos
);
139 de_dbg(c
, "flags2: 0x%04x", (UI
)flags
);
144 ucstring_empty(d
->tmpstr
);
145 // TODO: There are questions about whether this is OEM or ANSI
146 dbuf_read_to_ucstring(c
->infile
, pos
, 64, d
->tmpstr
, DE_CONVFLAG_STOP_AT_NUL
,
147 d
->input_encoding_oem
);
148 ucstring_strip_trailing_spaces(d
->tmpstr
);
149 de_dbg(c
, "params: \"%s\"", ucstring_getpsz_d(d
->tmpstr
));
152 static void do_pif_section_winvmm(deark
*c
, struct pif_ctx
*d
, i64 pos1
, i64 len
)
160 ucstring_empty(d
->tmpstr
);
161 dbuf_read_to_ucstring(c
->infile
, pos
, 80, d
->tmpstr
, DE_CONVFLAG_STOP_AT_NUL
,
162 d
->input_encoding_ansi
);
163 de_dbg(c
, "icon file: \"%s\"", ucstring_getpsz_d(d
->tmpstr
));
166 n
= de_getu16le_p(&pos
);
167 de_dbg(c
, "icon #: %"I64_FMT
, n
);
170 ucstring_empty(d
->tmpstr
);
171 dbuf_read_to_ucstring(c
->infile
, pos
, 32, d
->tmpstr
, DE_CONVFLAG_STOP_AT_NUL
,
172 d
->input_encoding_ansi
);
173 de_dbg(c
, "raster font: \"%s\"", ucstring_getpsz_d(d
->tmpstr
));
176 ucstring_empty(d
->tmpstr
);
177 dbuf_read_to_ucstring(c
->infile
, pos
, 32, d
->tmpstr
, DE_CONVFLAG_STOP_AT_NUL
,
178 d
->input_encoding_ansi
);
179 de_dbg(c
, "TrueType font: \"%s\"", ucstring_getpsz_d(d
->tmpstr
));
182 ucstring_empty(d
->tmpstr
);
183 dbuf_read_to_ucstring(c
->infile
, pos
, 80, d
->tmpstr
, DE_CONVFLAG_STOP_AT_NUL
,
184 d
->input_encoding_oem
);
185 de_dbg(c
, "BAT file: \"%s\"", ucstring_getpsz_d(d
->tmpstr
));
190 static void do_pif_section_winnt31(deark
*c
, struct pif_ctx
*d
, i64 pos1
, i64 len
)
197 ucstring_empty(d
->tmpstr
);
198 dbuf_read_to_ucstring(c
->infile
, pos
, 64, d
->tmpstr
, DE_CONVFLAG_STOP_AT_NUL
,
199 d
->input_encoding_ansi
);
200 de_dbg(c
, "alt config.sys: \"%s\"", ucstring_getpsz_d(d
->tmpstr
));
203 ucstring_empty(d
->tmpstr
);
204 dbuf_read_to_ucstring(c
->infile
, pos
, 64, d
->tmpstr
, DE_CONVFLAG_STOP_AT_NUL
,
205 d
->input_encoding_ansi
);
206 de_dbg(c
, "alt autoexec.bat: \"%s\"", ucstring_getpsz_d(d
->tmpstr
));
209 // Returns nonzero if we should look for more sections after this.
210 // Sets d->next_section_heading_pos.
211 static int do_pif_section(deark
*c
, struct pif_ctx
*d
, i64 pos1
)
213 int saved_indent_level
;
218 struct de_stringreaderdata
*secname
= NULL
;
220 de_dbg_indent_save(c
, &saved_indent_level
);
221 de_dbg(c
, "section at %"I64_FMT
, pos1
);
224 secname
= dbuf_read_string(c
->infile
, pos
,
225 16, 16, DE_CONVFLAG_STOP_AT_NUL
, d
->input_encoding_ansi
);
226 de_dbg(c
, "name: \"%s\"", ucstring_getpsz_d(secname
->str
));
229 d
->next_section_heading_pos
= de_getu16le_p(&pos
);
230 de_dbg(c
, "next section pos: %"I64_FMT
, d
->next_section_heading_pos
);
231 dpos
= de_getu16le_p(&pos
);
232 de_dbg(c
, "data pos: %"I64_FMT
, dpos
);
233 dlen
= de_getu16le_p(&pos
);
234 de_dbg(c
, "data len: %"I64_FMT
, dlen
);
235 if(d
->next_section_heading_pos
!= PIF_INVALID_HEADING_POS
) {
240 if(!pif_validate_pos(c
, d
, dpos
)) goto done
;
243 de_dbg(c
, "section data at %"I64_FMT
", len=%"I64_FMT
, dpos
, dlen
);
245 if(!de_strcmp(secname
->sz
, "MICROSOFT PIFEX")) {
246 do_pif_section_basic(c
, d
, dpos
, dlen
);
248 else if(!de_strcmp(secname
->sz
, "WINDOWS 286 3.0")) {
249 do_pif_section_win286(c
, d
, dpos
, dlen
);
251 else if(!de_strcmp(secname
->sz
, "WINDOWS 386 3.0")) {
252 do_pif_section_win386(c
, d
, dpos
, dlen
);
254 else if(!de_strcmp(secname
->sz
, "WINDOWS VMM 4.0")) {
255 do_pif_section_winvmm(c
, d
, dpos
, dlen
);
257 else if(!de_strcmp(secname
->sz
, "WINDOWS NT 3.1")) {
258 do_pif_section_winnt31(c
, d
, pos
, dlen
);
260 else if(!de_strcmp(secname
->sz
, "AUTOEXECBAT 4.0")) {
261 do_pif_section_extract(c
, d
, dpos
, dlen
, "autoexec.bat");
263 else if(!de_strcmp(secname
->sz
, "CONFIG SYS 4.0")) {
264 do_pif_section_extract(c
, d
, dpos
, dlen
, "config.sys");
267 do_pif_section_default(c
, d
, dpos
, dlen
);
276 de_destroy_stringreaderdata(c
, secname
);
277 de_dbg_indent_restore(c
, saved_indent_level
);
281 static void do_pif_sections(deark
*c
, struct pif_ctx
*d
)
283 d
->next_section_heading_pos
= PIF_BASIC_SECTION_SIZE
;
286 i64 this_section_heading_pos
;
288 this_section_heading_pos
= d
->next_section_heading_pos
;
289 if(this_section_heading_pos
== PIF_INVALID_HEADING_POS
) break;
290 d
->next_section_heading_pos
= PIF_INVALID_HEADING_POS
;
291 if(this_section_heading_pos
+22 > c
->infile
->len
) break;
292 if(!pif_validate_pos(c
, d
, this_section_heading_pos
)) break;
294 if(!do_pif_section(c
, d
, this_section_heading_pos
)) break;
298 static void do_dvp_extensions(deark
*c
, struct pif_ctx
*d
)
303 pos
= PIF_BASIC_SECTION_SIZE
;
304 ucstring_empty(d
->tmpstr
);
305 dbuf_read_to_ucstring(c
->infile
, pos
, 2, d
->tmpstr
, 0, DE_ENCODING_ASCII
);
306 de_dbg(c
, "keys: \"%s\"", ucstring_getpsz_d(d
->tmpstr
));
308 pos
= PIF_BASIC_SECTION_SIZE
+ 13;
309 dvextver
= de_getbyte(pos
);
310 de_dbg(c
, "DV extensions ver: %u", (UI
)dvextver
);
313 static void do_pif_main(deark
*c
, de_module_params
*mparams
, int is_dvp
)
316 struct pif_ctx
*d
= NULL
;
319 d
= de_malloc(c
, sizeof(struct pif_ctx
));
320 d
->tmpstr
= ucstring_create(c
);
322 d
->input_encoding_ansi
= de_get_input_encoding(c
, NULL
, DE_ENCODING_WINDOWS1252
);
324 d
->input_encoding_oem
= DE_ENCODING_CP437
; // default
325 tmps
= de_get_ext_option(c
, "oemenc");
327 d
->input_encoding_oem
= de_encoding_name_to_code(tmps
);
328 if(d
->input_encoding_oem
== DE_ENCODING_UNKNOWN
) {
329 d
->input_encoding_oem
= DE_ENCODING_CP437
;
333 d
->pos_seen
= de_inthashtable_create(c
);
335 if(!is_dvp
&& (c
->infile
->len
< PIF_BASIC_SECTION_SIZE
+22)) {
340 do_pif_section_basic(c
, d
, 0, PIF_BASIC_SECTION_SIZE
);
343 do_pif_section_basic(c
, d
, 0, PIF_BASIC_SECTION_SIZE
);
344 do_dvp_extensions(c
, d
);
347 do_pif_sections(c
, d
);
351 ucstring_destroy(d
->tmpstr
);
352 de_inthashtable_destroy(c
, d
->pos_seen
);
357 static void de_run_pif(deark
*c
, de_module_params
*mparams
)
359 do_pif_main(c
, mparams
, 0);
362 static int de_identify_pif(deark
*c
)
364 int maybe_oldfmt
= 0;
368 if(c
->infile
->len
== PIF_BASIC_SECTION_SIZE
) {
371 else if(c
->infile
->len
>= PIF_BASIC_SECTION_SIZE
+22) {
372 has_id
= !dbuf_memcmp(c
->infile
, PIF_BASIC_SECTION_SIZE
,
373 (const u8
*)"MICROSOFT PIFEX\0", 16);
376 if(!maybe_oldfmt
&& !has_id
) return 0;
377 has_ext
= de_input_file_has_ext(c
, "pif");
379 return has_ext
? 24 : 0;
381 return has_ext
? 100 : 35;
384 static void de_help_pif(deark
*c
)
386 de_msg(c
, "-opt oemenc=... : The encoding for OEM Text items");
389 void de_module_pif(deark
*c
, struct deark_module_info
*mi
)
392 mi
->desc
= "Windows Program Information File";
393 mi
->identify_fn
= de_identify_pif
;
394 mi
->run_fn
= de_run_pif
;
395 mi
->help_fn
= de_help_pif
;
398 static void de_run_desqview_dvp(deark
*c
, de_module_params
*mparams
)
400 do_pif_main(c
, mparams
, 1);
403 static int de_identify_desqview_dvp(deark
*c
)
407 if(c
->infile
->len
!=416) return 0;
408 if(de_getbyte(0) != 0x00) return 0;
409 has_ext
= de_input_file_has_ext(c
, "dvp");
410 if(has_ext
) return 40;
414 void de_module_desqview_dvp(deark
*c
, struct deark_module_info
*mi
)
416 mi
->id
= "desqview_dvp";
417 mi
->desc
= "DESQview Program Information File";
418 mi
->identify_fn
= de_identify_desqview_dvp
;
419 mi
->run_fn
= de_run_desqview_dvp
;