fnt: Improved error handling, etc.
[deark.git] / modules / pif.c
blob952919fd7744ee6e4ef50ab6e5049c62610d9f9d
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
15 struct pif_ctx {
16 de_encoding input_encoding_oem;
17 de_encoding input_encoding_ansi;
18 de_ucstring *tmpstr;
19 i64 next_section_heading_pos;
20 struct de_inthashtable *pos_seen;
21 UI checksum_calc;
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)) {
27 return 1;
29 de_err(c, "Bad offset detected");
30 return 0;
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)
45 i64 pos = pos1;
46 i64 n;
47 UI checksum_reported;
49 pos++; // unused
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,
54 DE_CRCOBJ_SUM_BYTES);
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));
65 pos += 30;
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));
77 pos += 63;
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));
88 pos += 64;
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));
95 pos += 64;
97 // TODO: More fields
100 static void do_pif_section_win286(deark *c, struct pif_ctx *d, i64 pos1, i64 len)
102 i64 pos = pos1;
103 i64 n;
104 UI flags;
106 if(len<6) return;
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)
117 i64 pos = pos1;
118 i64 n;
119 UI flags;
121 if(len<104) return;
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);
141 // TODO: More fields
143 pos = pos1 + 40;
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)
154 i64 pos = pos1;
155 i64 n;
157 if(len<428) return;
158 pos += 88;
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));
164 pos += 80;
166 n = de_getu16le_p(&pos);
167 de_dbg(c, "icon #: %"I64_FMT, n);
169 pos = pos1 + 234;
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));
174 pos += 32;
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));
181 pos = pos1 + 342;
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));
187 // TODO: More fields
190 static void do_pif_section_winnt31(deark *c, struct pif_ctx *d, i64 pos1, i64 len)
192 i64 pos = pos1;
194 if(len<140) return;
195 pos += 12;
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));
201 pos += 64;
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;
214 int retval = 0;
215 i64 pos = pos1;
216 i64 dpos;
217 i64 dlen;
218 struct de_stringreaderdata *secname = NULL;
220 de_dbg_indent_save(c, &saved_indent_level);
221 de_dbg(c, "section at %"I64_FMT, pos1);
222 de_dbg_indent(c, 1);
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));
227 pos += 16;
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) {
236 retval = 1;
239 if(dlen != 0) {
240 if(!pif_validate_pos(c, d, dpos)) goto done;
243 de_dbg(c, "section data at %"I64_FMT", len=%"I64_FMT, dpos, dlen);
244 de_dbg_indent(c, 1);
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");
266 else {
267 do_pif_section_default(c, d, dpos, dlen);
269 // TODO:
270 // WINDOWS NT 4.0
271 // WINDOWS PIF.402
272 // WINDOWS PIF.403
273 // WINDOWS ICO.001
275 done:
276 de_destroy_stringreaderdata(c, secname);
277 de_dbg_indent_restore(c, saved_indent_level);
278 return retval;
281 static void do_pif_sections(deark *c, struct pif_ctx *d)
283 d->next_section_heading_pos = PIF_BASIC_SECTION_SIZE;
285 while(1) {
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)
300 i64 pos;
301 u8 dvextver;
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)
315 const char *tmps;
316 struct pif_ctx *d = NULL;
317 int is_oldfmt = 0;
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");
326 if(tmps) {
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)) {
336 is_oldfmt = 1;
339 if(is_oldfmt) {
340 do_pif_section_basic(c, d, 0, PIF_BASIC_SECTION_SIZE);
342 else if(is_dvp) {
343 do_pif_section_basic(c, d, 0, PIF_BASIC_SECTION_SIZE);
344 do_dvp_extensions(c, d);
346 else {
347 do_pif_sections(c, d);
350 if(d) {
351 ucstring_destroy(d->tmpstr);
352 de_inthashtable_destroy(c, d->pos_seen);
353 de_free(c, d);
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;
365 int has_id = 0;
366 int has_ext;
368 if(c->infile->len == PIF_BASIC_SECTION_SIZE) {
369 maybe_oldfmt = 1;
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");
378 if(maybe_oldfmt) {
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)
391 mi->id = "pif";
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)
405 int has_ext;
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;
411 return 0;
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;