1 /*
2 * dinit.c - Dumb interface, initialization
3 *
4 * This file is part of Frotz.
5 *
6 * Frotz is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * Frotz is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19 * Or visit http://www.fsf.org/
20 */
21
22 #include <libgen.h>
23 #include "dfrotz.h"
24 #include "dblorb.h"
25
26 extern f_setup_t f_setup;
27 extern z_header_t z_header;
28
29 static void print_version(void);
30
31 #define INFORMATION "\
32 An interpreter for all Infocom and other Z-Machine games.\n\
33 \n\
34 Syntax: dfrotz [options] story-file\n\
35 -a watch attribute setting \t -P alter piracy opcode\n\
36 -A watch attribute testing \t -R <path> restricted read/write\n\
37 -f <type> type of format codes \t -s # random number seed value\n\
38 -h # screen height \t -S # transcript width\n\
39 -i ignore fatal errors \t -t set Tandy bit\n\
40 -I # interpreter number \t -u # slots for multiple undo\n\
41 -o watch object movement \t -v show version information\n\
42 -O watch object locating \t -w # screen width\n\
43 -L <file> load this save file \t -x expand abbreviations g/x/z\n\
44 -m turn off MORE prompts \t -Z # error checking (see below)\n\
45 -p plain ASCII output only\n"
46
47 #define INFO2 "\
48 Error checking: 0 none, 1 first only (default), 2 all, 3 exit after any error.\n\
49 For more options and explanations, please read the manual page.\n\n\
50 While running, enter \"\\help\" to list the runtime escape sequences.\n"
51
52
53 static int user_text_width = 75;
54 static int user_text_height = 24;
55 static int user_random_seed = -1;
56 static int user_tandy_bit = 0;
57 static bool plain_ascii = FALSE;
58
59 bool do_more_prompts;
60
61 /*
62 * os_process_arguments
63 *
64 * Handle command line switches.
65 * Some variables may be set to activate special features of Frotz.
66 *
67 */
os_process_arguments(int argc,char * argv[])68 void os_process_arguments(int argc, char *argv[])
69 {
70 int c, num;
71 char *p = NULL;
72 char *format_orig = NULL;
73
74 zoptarg = NULL;
75
76 do_more_prompts = TRUE;
77 /* Parse the options */
78 do {
79 c = zgetopt(argc, argv, "-aAf:h:iI:L:moOpPs:r:R:S:tu:vw:xZ:");
80 switch(c) {
81 case 'a':
82 f_setup.attribute_assignment = 1;
83 break;
84 case 'A':
85 f_setup.attribute_testing = 1;
86 break;
87 case 'f':
88 #ifdef DISABLE_FORMATS
89 f_setup.format = FORMAT_DISABLED;
90 break;
91 #endif
92 f_setup.format = FORMAT_NORMAL;
93 format_orig = strdup(zoptarg);
94 for (num = 0; zoptarg[num] != 0; num++)
95 zoptarg[num] = tolower((int) zoptarg[num]);
96 if (strcmp(zoptarg, "irc") == 0) {
97 f_setup.format = FORMAT_IRC;
98 } else if (strcmp(zoptarg, "ansi") == 0) {
99 f_setup.format = FORMAT_ANSI;
100 } else if ((strcmp(zoptarg, "none") == 0) ||
101 (strcmp(zoptarg, "normal") == 0)) {
102 } else
103 f_setup.format = FORMAT_UNKNOWN;
104 break;
105 case 'h':
106 user_text_height = atoi(zoptarg);
107 break;
108 case 'i':
109 f_setup.ignore_errors = 1;
110 break;
111 case 'I':
112 f_setup.interpreter_number = atoi(zoptarg);
113 break;
114 case 'L':
115 f_setup.restore_mode = 1;
116 f_setup.tmp_save_name = strdup(zoptarg);
117 break;
118 case 'm':
119 do_more_prompts = FALSE;
120 break;
121 case 'o':
122 f_setup.object_movement = 1;
123 break;
124 case 'O':
125 f_setup.object_locating = 1;
126 break;
127 case 'P':
128 f_setup.piracy = 1;
129 break;
130 case 'p':
131 plain_ascii = 1;
132 break;
133 case 'r':
134 dumb_handle_setting(zoptarg, FALSE, TRUE);
135 break;
136 case 'R':
137 f_setup.restricted_path = strndup(zoptarg, PATH_MAX);
138 break;
139 case 's':
140 user_random_seed = atoi(zoptarg);
141 break;
142 case 'S':
143 f_setup.script_cols = atoi(zoptarg);
144 break;
145 case 't':
146 user_tandy_bit = 1;
147 break;
148 case 'u':
149 f_setup.undo_slots = atoi(zoptarg);
150 break;
151 case 'v':
152 print_version();
153 os_quit(EXIT_SUCCESS);
154 break;
155 case 'w':
156 user_text_width = atoi(zoptarg);
157 break;
158 case 'x':
159 f_setup.expand_abbreviations = 1;
160 break;
161 case 'Z':
162 f_setup.err_report_mode = atoi(zoptarg);
163 if ((f_setup.err_report_mode < ERR_REPORT_NEVER) ||
164 (f_setup.err_report_mode > ERR_REPORT_FATAL))
165 f_setup.err_report_mode =
166 ERR_DEFAULT_REPORT_MODE;
167 break;
168 }
169 } while (c != EOF);
170
171 if (argv[zoptind] == NULL) {
172 printf("FROTZ V%s - Dumb interface.\n", VERSION);
173 puts(INFORMATION);
174 puts(INFO2);
175 os_quit(EXIT_SUCCESS);
176 }
177
178 switch (f_setup.format) {
179 case FORMAT_IRC:
180 printf("Using IRC formatting.\n");
181 break;
182 case FORMAT_ANSI:
183 printf("Using ANSI formatting.\n");
184 f_setup.format = FORMAT_ANSI;
185 break;
186 case FORMAT_UNKNOWN:
187 printf("Unknown formatting \"%s\".\n", format_orig);
188 f_setup.format = FORMAT_NORMAL;
189 break;
190 case FORMAT_DISABLED:
191 printf("Format selection disabled at compile time.\n");
192 f_setup.format = FORMAT_NORMAL;
193 break;
194 default:
195 break;
196 }
197 if (f_setup.format == FORMAT_NORMAL)
198 printf("Using normal formatting.\n");
199
200 /* Save the story file name */
201 f_setup.story_file = strdup(argv[zoptind]);
202 f_setup.story_name = strdup(basename(argv[zoptind]));
203
204 if (argv[zoptind+1] != NULL)
205 f_setup.blorb_file = strdup(argv[zoptind+1]);
206
207 printf("Loading %s.\n", f_setup.story_file);
208
209 #ifndef NO_BLORB
210 if (f_setup.blorb_file != NULL)
211 printf("Also loading %s.\n", f_setup.blorb_file);
212 #endif
213
214 /* Now strip off the extension */
215 p = strrchr(f_setup.story_name, '.');
216 if ( p != NULL )
217 *p = '\0'; /* extension removed */
218
219 /* Create nice default file names */
220
221 f_setup.script_name = malloc((strlen(f_setup.story_name) +
222 strlen(EXT_SCRIPT)) * sizeof(char) + 1);
223 memcpy(f_setup.script_name, f_setup.story_name, (strlen(f_setup.story_name) + strlen(EXT_SCRIPT)) * sizeof(char));
224
225 strncat(f_setup.script_name, EXT_SCRIPT, strlen(EXT_SCRIPT) + 1);
226
227 f_setup.command_name = malloc((strlen(f_setup.story_name) +
228 strlen(EXT_COMMAND)) * sizeof(char) + 1);
229 memcpy(f_setup.command_name, f_setup.story_name, (strlen(f_setup.story_name) + strlen(EXT_COMMAND)) * sizeof(char));
230 strncat(f_setup.command_name, EXT_COMMAND, strlen(EXT_COMMAND) + 1);
231
232 if (!f_setup.restore_mode) {
233 f_setup.save_name = malloc((strlen(f_setup.story_name) +
234 strlen(EXT_SAVE)) * sizeof(char) + 1);
235 memcpy(f_setup.save_name, f_setup.story_name, (strlen(f_setup.story_name) + strlen(EXT_SAVE)) * sizeof(char));
236 strncat(f_setup.save_name, EXT_SAVE, strlen(EXT_SAVE) + 1);
237 } else { /* Set our auto load save as the name save */
238 f_setup.save_name = malloc((strlen(f_setup.tmp_save_name) +
239 strlen(EXT_SAVE)) * sizeof(char) + 1);
240 memcpy(f_setup.save_name, f_setup.tmp_save_name, (strlen(f_setup.tmp_save_name) + strlen(EXT_SAVE)) * sizeof(char));
241 free(f_setup.tmp_save_name);
242 }
243
244 f_setup.aux_name = malloc((strlen(f_setup.story_name) +
245 strlen(EXT_AUX)) * sizeof(char) + 1);
246 memcpy(f_setup.aux_name, f_setup.story_name, (strlen(f_setup.story_name) + strlen(EXT_AUX)) * sizeof(char));
247 strncat(f_setup.aux_name, EXT_AUX, strlen(EXT_AUX) + 1);
248 }
249
250
os_init_screen(void)251 void os_init_screen(void)
252 {
253 if (z_header.version == V3 && user_tandy_bit)
254 z_header.config |= CONFIG_TANDY;
255
256 if (z_header.version >= V5 && f_setup.undo_slots == 0)
257 z_header.flags &= ~UNDO_FLAG;
258
259 z_header.screen_rows = user_text_height;
260 z_header.screen_cols = user_text_width;
261
262 /* Use the ms-dos interpreter number for v6, because that's the
263 * kind of graphics files we understand. Otherwise, use DEC. */
264 if (f_setup.interpreter_number == INTERP_DEFAULT)
265 z_header.interpreter_number = z_header.version ==
266 6 ? INTERP_MSDOS : INTERP_DEC_20;
267 else
268 z_header.interpreter_number = f_setup.interpreter_number;
269
270 z_header.interpreter_version = 'F';
271
272 dumb_init_input();
273 dumb_init_output();
274 dumb_init_pictures();
275 }
276
277
os_random_seed(void)278 int os_random_seed (void)
279 {
280 if (user_random_seed == -1) /* Use the epoch as seed value */
281 return (time(0) & 0x7fff);
282 return user_random_seed;
283 }
284
285
286 /*
287 * os_quit
288 *
289 * Immediately and cleanly exit, passing along exit status.
290 *
291 */
os_quit(int status)292 void os_quit(int status)
293 {
294 exit(status);
295 }
296
297
os_restart_game(int UNUSED (stage))298 void os_restart_game (int UNUSED (stage)) {}
299
300
os_fatal(const char * s,...)301 void os_fatal (const char *s, ...)
302 {
303 fprintf(stderr, "\nFatal error: %s\n", s);
304 if (f_setup.ignore_errors)
305 fprintf(stderr, "Continuing anyway...\n");
306 else
307 os_quit(EXIT_FAILURE);
308 }
309
310
os_load_story(void)311 FILE *os_load_story(void)
312 {
313 #ifndef NO_BLORB
314 FILE *fp;
315
316 switch (dumb_blorb_init(f_setup.story_file)) {
317 case bb_err_NoBlorb:
318 /* printf("No blorb file found.\n\n"); */
319 break;
320 case bb_err_Format:
321 printf("Blorb file loaded, but unable to build map.\n\n");
322 break;
323 case bb_err_NotFound:
324 printf("Blorb file loaded, but lacks executable chunk.\n\n");
325 break;
326 case bb_err_None:
327 /* printf("No blorb errors.\n\n"); */
328 break;
329 }
330
331 fp = fopen(f_setup.story_file, "rb");
332
333 /* Is this a Blorb file containing Zcode? */
334 if (f_setup.exec_in_blorb)
335 fseek(fp, blorb_res.data.startpos, SEEK_SET);
336
337 return fp;
338 #else
339 return fopen(f_setup.story_file, "rb");
340 #endif
341 }
342
343
344 /*
345 * Seek into a storyfile, either a standalone file or the
346 * ZCODE chunk of a blorb file.
347 */
os_storyfile_seek(FILE * fp,long offset,int whence)348 int os_storyfile_seek(FILE * fp, long offset, int whence)
349 {
350 #ifndef NO_BLORB
351 /* Is this a Blorb file containing Zcode? */
352 if (f_setup.exec_in_blorb) {
353 switch (whence) {
354 case SEEK_END:
355 return fseek(fp, blorb_res.data.startpos + blorb_res.length + offset, SEEK_SET);
356 break;
357 case SEEK_CUR:
358 return fseek(fp, offset, SEEK_CUR);
359 break;
360 case SEEK_SET:
361 /* SEEK_SET falls through to default */
362 default:
363 return fseek(fp, blorb_res.data.startpos + offset, SEEK_SET);
364 break;
365 }
366 } else
367 return fseek(fp, offset, whence);
368 #else
369 return fseek(fp, offset, whence);
370 #endif
371 }
372
373
374 /*
375 * Tell the position in a storyfile, either a standalone file
376 * or the ZCODE chunk of a blorb file.
377 */
os_storyfile_tell(FILE * fp)378 int os_storyfile_tell(FILE * fp)
379 {
380 #ifndef NO_BLORB
381 /* Is this a Blorb file containing Zcode? */
382 if (f_setup.exec_in_blorb)
383 return ftell(fp) - blorb_res.data.startpos;
384 else
385 return ftell(fp);
386 #else
387 return ftell(fp);
388 #endif
389 }
390
391
os_init_setup(void)392 void os_init_setup(void)
393 {
394 /* Nothing here */
395 }
396
397
print_version(void)398 static void print_version(void)
399 {
400 printf("FROTZ V%s\t", VERSION);
401 printf("Dumb interface.\n");
402 printf("Commit date:\t%s\n", GIT_DATE);
403 printf("Git commit:\t%s\n", GIT_HASH);
404 printf(" Frotz was originally written by Stefan Jokisch.\n");
405 printf(" It complies with standard 1.0 of Graham Nelson's specification.\n");
406 printf(" It was ported to Unix by Galen Hazelwood.\n");
407 printf(" The core and dumb port are maintained by David Griffith.\n");
408 printf(" Frotz's homepage is https://661.org/proj/if/frotz.\n\n");
409 return;
410 }
411