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