1 /*
2  * osdepend.c
3  *
4  * All non screen specific operating system dependent routines.
5  *
6  * Olaf Barthel 28-Jul-1992
7  *
8  */
9 
10 #include "ztypes.h"
11 #include "arguments.h"
12 #include "version.h"
13 
14 /* File names will be O/S dependent */
15 
16 #if defined(AMIGA)
17 #define SAVE_NAME   "Story.Save"   /* Default save name */
18 #define SCRIPT_NAME "PRT:"         /* Default script name */
19 #define RECORD_NAME "Story.Record" /* Default record name */
20 #else /* defined(AMIGA) */
21 #define SAVE_NAME   "story.sav"   /* Default save name */
22 #define SCRIPT_NAME "story.lis"   /* Default script name */
23 #define RECORD_NAME "record.lis"  /* Default record name */
24 #endif /* defined(AMIGA) */
25 
26 static optionname optionnamelist[] = {
27     {"bindings", OPT_BINDINGS, TRUE},
28     {"strictz", OPT_STRICTZ, TRUE},
29     {"spec", OPT_SPEC, TRUE},
30     {"geometry", OPT_GEOMETRY, TRUE},
31     {"statgeometry", OPT_STATGEOMETRY, TRUE},
32     {"inputstyle", OPT_INPUTSTYLE, TRUE},
33     {"marginx", OPT_MARGINX, TRUE},
34     {"leading", OPT_LEADING, TRUE},
35     {"justify", OPT_JUSTIFY, TRUE},
36     {"autoresize", OPT_AUTORESIZE, TRUE},
37     {"resizeupward", OPT_RESIZEUPWARD, TRUE},
38     {"autoclear", OPT_AUTOCLEAR, TRUE},
39     {"history", OPT_HISTORY, TRUE},
40     {"buffer", OPT_BUFFER, TRUE},
41     {"background", OPT_BACKGROUND, TRUE},
42     {"foreground", OPT_FOREGROUND, TRUE},
43     {"bg", OPT_BACKGROUND, FALSE},
44     {"fg", OPT_FOREGROUND, FALSE},
45     {"greycolor", OPT_GREYCOLOR, TRUE},
46 
47     {"n-color", OPT_STYLECOLOR+0, TRUE},
48     {"r-color", OPT_STYLECOLOR+1, TRUE},
49     {"b-color", OPT_STYLECOLOR+2, TRUE},
50     {"rb-color", OPT_STYLECOLOR+3, TRUE},
51     {"i-color", OPT_STYLECOLOR+4, TRUE},
52     {"ri-color", OPT_STYLECOLOR+5, TRUE},
53     {"bi-color", OPT_STYLECOLOR+6, TRUE},
54     {"rbi-color", OPT_STYLECOLOR+7, TRUE},
55     {"f-color", OPT_STYLECOLOR+8, TRUE},
56     {"rf-color", OPT_STYLECOLOR+9, TRUE},
57     {"bf-color", OPT_STYLECOLOR+10, TRUE},
58     {"rbf-color", OPT_STYLECOLOR+11, TRUE},
59     {"if-color", OPT_STYLECOLOR+12, TRUE},
60     {"rif-color", OPT_STYLECOLOR+13, TRUE},
61     {"bif-color", OPT_STYLECOLOR+14, TRUE},
62     {"rbif-color", OPT_STYLECOLOR+15, TRUE},
63 
64     {"n-font", OPT_STYLEFONT+0, TRUE},
65     {"r-font", OPT_STYLEFONT+1, TRUE},
66     {"b-font", OPT_STYLEFONT+2, TRUE},
67     {"rb-font", OPT_STYLEFONT+3, TRUE},
68     {"i-font", OPT_STYLEFONT+4, TRUE},
69     {"ri-font", OPT_STYLEFONT+5, TRUE},
70     {"bi-font", OPT_STYLEFONT+6, TRUE},
71     {"rbi-font", OPT_STYLEFONT+7, TRUE},
72     {"f-font", OPT_STYLEFONT+8, TRUE},
73     {"rf-font", OPT_STYLEFONT+9, TRUE},
74     {"bf-font", OPT_STYLEFONT+10, TRUE},
75     {"rbf-font", OPT_STYLEFONT+11, TRUE},
76     {"if-font", OPT_STYLEFONT+12, TRUE},
77     {"rif-font", OPT_STYLEFONT+13, TRUE},
78     {"bif-font", OPT_STYLEFONT+14, TRUE},
79     {"rbif-font", OPT_STYLEFONT+15, TRUE},
80 
81     {"help", OPT_HELP, TRUE},
82 
83     {NULL, 0, FALSE}
84 };
85 
86 unixoption unixoptionlist[NUMOPTIONS];
87 
88 int strictz_declare_spec;
89 
90 #ifdef STRICTZ
91 
92 /* Define stuff for stricter Z-code error checking, for the generic
93    Unix/DOS/etc terminal-window interface. Feel free to change the way
94    player prefs are specified, or replace report_zstrict_error()
95    completely if you want to change the way errors are reported. */
96 
97 /* There are four error reporting modes: never report errors;
98   report only the first time a given error type occurs; report
99   every time an error occurs; or treat all errors as fatal
100   errors, killing the interpreter. I strongly recommend
101   "report once" as the default. But you can compile in a
102   different default by changing the definition of
103   STRICTZ_DEFAULT_REPORT_MODE. In any case, the player can
104   specify a report mode on the command line by typing "-s 0"
105   through "-s 3". */
106 
107 int strictz_report_mode;
108 static int strictz_error_count[STRICTZ_NUM_ERRORS];
109 
110 #endif /* STRICTZ */
111 
112 
113 #if !defined(AMIGA)
114 
115 /*
116  * process_arguments
117  *
118  * Do any argument preprocessing necessary before the game is
119  * started. This may include selecting a specific game file or
120  * setting interface-specific options.
121  *
122  */
123 
124 #ifdef __STDC__
process_arguments(int argc,char * argv[])125 void process_arguments (int argc, char *argv[])
126 #else
127 void process_arguments (argc, argv)
128 int argc;
129 char *argv[];
130 #endif
131 {
132     int c, errflg = 0;
133     int num;
134     char *gamename = NULL;
135 
136     {
137       zword_t zw;
138       int little_endian=0;
139 
140       /* Find out if we are big-endian */
141       zw=0x0001;
142       if ( *(zbyte_t *)&zw ) {
143 	little_endian=1;
144       }
145 
146 #if defined(BIG_END_MODE) || defined(LITTLE_END_MODE)
147 
148 #ifdef BIG_END_MODE
149       if (little_endian) {
150 	fprintf (stderr, "%s: On this machine, this program must be compiled with LITTLE_END_MODE defined! Adjust the Makefile and recompile.\n", PROGRAMNAME);
151 	exit(EXIT_FAILURE);
152       }
153 #else
154       if (!little_endian) {
155 	fprintf (stderr, "%s: On this machine, this program must be compiled with BIG_END_MODE defined! Adjust the Makefile and recompile.\n", PROGRAMNAME);
156 	exit(EXIT_FAILURE);
157       }
158 #endif
159 
160 #else
161       fprintf (stderr, "%s: This program must be compiled with either BIG_END_MODE or LITTLE_END_MODE defined! Adjust the Makefile and recompile.\n", PROGRAMNAME);
162       exit(EXIT_FAILURE);
163 
164 #endif
165     }
166 
167 #ifdef STRICTZ
168     /* Initialize the STRICTZ variables. */
169     strictz_report_mode = STRICTZ_DEFAULT_REPORT_MODE;
170     for (num=0; num<STRICTZ_NUM_ERRORS; num++) {
171         strictz_error_count[num] = 0;
172     }
173 #endif /* STRICTZ */
174 
175     /* Parse the options */
176     /* this is considerably changed --zarf */
177 
178     num = 0;
179     for (c=0; c<NUMOPTIONS; c++) {
180 	unixoptionlist[c].val = NULL;
181 	unixoptionlist[c].name = NULL;
182     }
183     for (c=0; optionnamelist[c].name; c++) {
184 	if (optionnamelist[c].canonical) {
185 	    unixoptionlist[optionnamelist[c].key].name = optionnamelist[c].name;
186 	}
187     }
188 
189     errflg = 0;
190     for (c=1; !errflg && c<argc; c++) {
191 	if (argv[c][0]=='-') {
192 	    /* ### if I were clever, this would be a binary search */
193 	    for (num=0; optionnamelist[num].name; num++) {
194 		if (!strcmp(optionnamelist[num].name, argv[c]+1)) {
195 		    break;
196 		}
197 	    }
198 	    if (optionnamelist[num].name) {
199 		num = optionnamelist[num].key;
200 		if (num==OPT_HELP) {
201 		    errflg = 2;
202 		}
203 		else {
204 		    if (c+1 >= argc) {
205 			fprintf(stderr, "%s: option %s requires a value to follow it\n", PROGRAMNAME, argv[c]);
206 			errflg = 1;
207 		    }
208 		    else {
209 			c++;
210 			unixoptionlist[num].val = argv[c];
211 		    }
212 		}
213 	    }
214 	    else {
215 		fprintf(stderr, "%s: unknown option: %s\n", PROGRAMNAME, argv[c]);
216 		errflg = 1;
217 	    }
218 	}
219 	else {
220 	    if (!gamename)
221 		gamename = argv[c];
222 	    else {
223 		fprintf(stderr, "%s: unknown option after story-file: %s\n", PROGRAMNAME, argv[c]);
224 		errflg = 1;
225 	    }
226 	}
227     }
228 
229     /* Display usage */
230 
231     if (errflg || !gamename) {
232         fprintf (stderr, "usage: %s [options...] story-file\n\n", argv[0]);
233 	fprintf (stderr, "XZip - a Z-machine interpreter.\nVersion %s by Andrew Plotkin (erkyrath@eblong.com).\n", XZIPVERSION);
234         fprintf (stderr, "Based on ZIP Version 2.0 by Mark Howell.\n");
235         fprintf (stderr, "Plays Z-machine files of versions 1-5 and version 8.\n\n");
236 	fprintf (stderr, "\t-help: display full list of options\n");
237 	if (errflg==2) {
238 	    fprintf(stderr, "\n");
239 	    for (c=0; c<NUM_SIMPLE_OPTIONS; c++) {
240 		if (unixoptionlist[c].name)
241 		    fprintf(stderr, "\t-%s\n", unixoptionlist[c].name);
242 	    }
243 	    /* sue me, this is hardwired */
244 	    fprintf(stderr, "\t-%s\n", "XXX-color");
245 	    fprintf(stderr, "\t-%s\n", "XXX-font");
246 	    fprintf(stderr, "\t(for XXX in n, r, b, rb, i, ri, bi, rbi, f, rf, bf, rbf, if, rif, bif, rbif)\n");
247 	}
248 	exit (EXIT_FAILURE);
249     }
250 
251     /* Open the story file */
252 
253     open_story (gamename);
254 
255 }/* process_arguments */
256 
257 #endif /* !defined(AMIGA) */
258 
259 #if !defined(AMIGA)
260 
261 /*
262  * file_cleanup
263  *
264  * Perform actions when a file is successfully closed. Flag can be one of:
265  * GAME_SAVE, GAME_RESTORE, GAME_SCRIPT.
266  *
267  */
268 
269 #ifdef __STDC__
file_cleanup(const char * file_name,int flag)270 void file_cleanup (const char *file_name, int flag)
271 #else
272 void file_cleanup (file_name, flag)
273 const char *file_name;
274 int flag;
275 #endif
276 {
277 
278 }/* file_cleanup */
279 
280 #endif /* !defined(AMIGA) */
281 
282 #if !defined(AMIGA)
283 
284 /*
285  * sound
286  *
287  * Play a sound file or a note.
288  *
289  * argc = 1: argv[0] = note# (range 1 - 3)
290  *
291  *           Play note.
292  *
293  * argc = 2: argv[0] = 0
294  *           argv[1] = 3
295  *
296  *           Stop playing current sound.
297  *
298  * argc = 2: argv[0] = 0
299  *           argv[1] = 4
300  *
301  *           Free allocated resources.
302  *
303  * argc = 3: argv[0] = ID# of sound file to replay.
304  *           argv[1] = 2
305  *           argv[2] = Volume to replay sound with, this value
306  *                     can range between 1 and 8.
307  *
308  * argc = 4: argv[0] = ID# of sound file to replay.
309  *           argv[1] = 2
310  *           argv[2] = Control information
311  *           argv[3] = Volume information
312  *
313  *           Volume information:
314  *
315  *               0x34FB -> Fade sound in
316  *               0x3507 -> Fade sound out
317  *               other  -> Replay sound at maximum volume
318  *
319  *           Control information:
320  *
321  *               This word is divided into two bytes,
322  *               the upper byte determines the number of
323  *               cycles to play the sound (e.g. how many
324  *               times a clock chimes or a dog barks).
325  *               The meaning of the lower byte is yet to
326  *               be discovered :)
327  *
328  */
329 
330 #ifdef __STDC__
sound(int argc,zword_t * argv)331 void sound (int argc, zword_t *argv)
332 #else
333 void sound (argc, argv)
334 int argc;
335 zword_t *argv;
336 #endif
337 {
338 
339     /* Supply default parameters */
340 
341     if (argc < 4)
342         argv[3] = 0;
343     if (argc < 3)
344         argv[2] = 0xff;
345     if (argc < 2)
346         argv[1] = 2;
347 
348     /* Generic bell sounder */
349 
350     if (argc == 1 || argv[1] == 2) {
351 	xio_bell();    /* --zarf */
352 	/* display_char ('\007'); */
353     }
354 
355 }/* sound */
356 
357 #endif /* !defined(AMIGA) */
358 
359 /*
360  * get_file_name
361  *
362  * Return the name of a file. Flag can be one of:
363  *    GAME_SAVE    - Save file (write only)
364  *    GAME_RESTORE - Save file (read only)
365  *    GAME_SCRIPT  - Script file (write only)
366  *    GAME_RECORD  - Keystroke record file (write only)
367  *    GAME_PLABACK - Keystroke record file (read only)
368  *
369  */
370 
371 #ifdef __STDC__
get_file_name(char * file_name,char * default_name,int flag)372 int get_file_name (char *file_name, char *default_name, int flag)
373 #else
374 int get_file_name (file_name, default_name, flag)
375 char *file_name;
376 char *default_name;
377 int flag;
378 #endif
379 {
380     char buffer[127 + 2]; /* 127 is the biggest positive char */
381     int status = 0;
382 
383     /* If no default file name then supply the standard name */
384 
385     if (default_name[0] == '\0') {
386         if (flag == GAME_SCRIPT)
387             strcpy (default_name, SCRIPT_NAME);
388         else if (flag == GAME_RECORD || flag == GAME_PLAYBACK)
389             strcpy (default_name, RECORD_NAME);
390         else /* (flag == GAME_SAVE || flag == GAME_RESTORE) */
391             strcpy (default_name, SAVE_NAME);
392     }
393 
394     /* Prompt for the file name */
395 
396     output_line ("Enter a file name.");
397     output_string ("(Default is \"");
398     output_string (default_name);
399     output_string ("\"): ");
400 
401     buffer[0] = 127;
402     buffer[1] = 0;
403     (void) get_line (buffer, 0, 0);
404 
405     /* Copy file name from the input buffer */
406 
407     if (h_type > V4) {
408         unsigned char len = buffer[1];
409         memcpy (file_name, &buffer[2], len);
410         file_name[len] = '\0';
411     } else
412         strcpy (file_name, &buffer[1]);
413 
414     /* If nothing typed then use the default name */
415 
416     if (file_name[0] == '\0')
417         strcpy (file_name, default_name);
418 
419 #if !defined(VMS) /* VMS has file version numbers, so cannot overwrite */
420 
421     /* Check if we are going to overwrite the file */
422 
423     if (flag == GAME_SAVE || flag == GAME_SCRIPT || flag == GAME_RECORD) {
424         FILE *tfp;
425         char c;
426 
427         /* Try to access the file */
428 
429         tfp = fopen (file_name, "r");
430         if (tfp != NULL) {
431             fclose (tfp);
432 
433             /* If it succeeded then prompt to overwrite */
434 
435             output_line ("You are about to write over an existing file.");
436             output_string ("Proceed? (Y/N) ");
437 
438             do {
439                 c = (char) input_character (0);
440                 c = (char) toupper (c);
441             } while (c != 'Y' && c != 'N');
442 
443             output_char (c);
444             output_new_line ();
445 
446             /* If no overwrite then fail the routine */
447 
448             if (c == 'N')
449                 status = 1;
450         }
451     }
452 #endif /* !defined(VMS) */
453 
454     /* Record the file name if it was OK */
455 
456     if (status == 0)
457         record_line (file_name, strlen(file_name));
458 
459     return (status);
460 
461 }/* get_file_name */
462 
463 #if !defined(AMIGA)
464 
465 /*
466  * fatal
467  *
468  * Display message and stop interpreter.
469  *
470  */
471 
472 #ifdef __STDC__
fatal(const char * s)473 void fatal (const char *s)
474 #else
475 void fatal (s)
476 const char *s;
477 #endif
478 {
479 
480     reset_screen ();
481     printf ("\nFatal error: %s (PC = %lx)\n", s, pc);
482     exit (1);
483 
484 }/* fatal */
485 
486 #endif /* !defined(AMIGA) */
487 
488 #if !defined(AMIGA)
489 
490 /*
491  * report_strictz_error
492  *
493  * This handles Z-code error conditions which ought to be fatal errors,
494  * but which players might want to ignore for the sake of finishing the
495  * game.
496  *
497  * The error is provided as both a numeric code and a string. This allows
498  * us to print a warning the first time a particular error occurs, and
499  * ignore it thereafter.
500  *
501  * errnum : Numeric code for error (0 to STRICTZ_NUM_ERRORS-1)
502  * errstr : Text description of error
503  *
504  */
505 
506 #ifdef STRICTZ
507 
508 #ifdef __STDC__
report_strictz_error(int errnum,const char * errstr)509 void report_strictz_error (int errnum, const char *errstr)
510 #else /* __STDC__ */
511 void report_strictz_error (errnum, errstr)
512 int errnum;
513 const char *errstr;
514 #endif /* __STDC__ */
515 {
516     int wasfirst;
517 
518     if (errnum <= 0 || errnum >= STRICTZ_NUM_ERRORS)
519         return;
520 
521     if (strictz_report_mode == STRICTZ_REPORT_FATAL) {
522         fatal(errstr);
523         return;
524     }
525 
526     wasfirst = (strictz_error_count[errnum] == 0);
527     strictz_error_count[errnum]++;
528 
529     if ((strictz_report_mode == STRICTZ_REPORT_ALWAYS)
530         || (strictz_report_mode == STRICTZ_REPORT_ONCE && wasfirst)) {
531         char buf[256];
532         sprintf(buf, "Warning: %s (PC = %lx)", errstr, pc);
533         write_string(buf);
534 
535         if (strictz_report_mode == STRICTZ_REPORT_ONCE) {
536             write_string(" (will ignore further occurrences)");
537         }
538         else {
539             sprintf(buf, " (occurrence %d)", strictz_error_count[errnum]);
540             write_string(buf);
541         }
542         new_line();
543     }
544 
545 } /* report_strictz_error */
546 
547 #endif /* STRICTZ */
548 
549 #endif /* !defined(AMIGA) */
550 
551 #if !defined(AMIGA)
552 
553 /*
554  * fit_line
555  *
556  * This routine determines whether a line of text will still fit
557  * on the screen.
558  *
559  * line : Line of text to test.
560  * pos  : Length of text line (in characters).
561  * max  : Maximum number of characters to fit on the screen.
562  *
563  */
564 
565 #ifdef __STDC__
fit_line(const char * line_buffer,int pos,int max)566 int fit_line (const char *line_buffer, int pos, int max)
567 #else
568 int fit_line (line_buffer, pos, max)
569 const char *line_buffer;
570 int pos;
571 int max;
572 #endif
573 {
574 
575     return (pos < max);
576 
577 }/* fit_line */
578 
579 #endif /* !defined(AMIGA) */
580 
581 #if !defined(AMIGA)
582 
583 /*
584  * print_status
585  *
586  * Print the status line (type 3 games only).
587  *
588  * argv[0] : Location name
589  * argv[1] : Moves/Time
590  * argv[2] : Score
591  *
592  * Depending on how many arguments are passed to this routine
593  * it is to print the status line. The rendering attributes
594  * and the status line window will be have been activated
595  * when this routine is called. It is to return FALSE if it
596  * cannot render the status line in which case the interpreter
597  * will use display_char() to render it on its own.
598  *
599  * This routine has been provided in order to support
600  * proportional-spaced fonts.
601  *
602  */
603 
604 #ifdef __STDC__
print_status(int argc,char * argv[])605 int print_status (int argc, char *argv[])
606 #else
607 int print_status (argc, argv)
608 int argc;
609 char *argv[];
610 #endif
611 {
612 
613     return (FALSE);
614 
615 }/* print_status */
616 
617 #endif /* !defined(AMIGA) */
618 
619 #if !defined(AMIGA)
620 
621 /*
622  * set_font
623  *
624  * Set a new character font. Font can be either be:
625  *
626  *    TEXT_FONT (1)     = normal text character font
627  *    GRAPHICS_FONT (3) = graphical character font
628  *
629  * Returns TRUE if succeeded, FALSE if failed.
630  */
631 
632 #ifdef __STDC__
set_font(int font_type)633 int set_font (int font_type)
634 #else
635 int set_font (font_type)
636 int font_type;
637 #endif
638 {
639     if (font_type == 1)
640         return TRUE;
641     else
642         return FALSE;
643 }/* set_font */
644 
645 #endif /* !defined(AMIGA) */
646 
647 #if !defined(MSDOS) && !defined(AMIGA)
648 
649 /*
650  * set_colours
651  *
652  * Sets screen foreground and background colours.
653  *
654  */
655 
656 #ifdef __STDC__
set_colours(int foreground,int background)657 void set_colours (int foreground, int background)
658 #else
659 void set_colours (foreground, background)
660 int foreground;
661 int background;
662 #endif
663 {
664 
665 }/* set_colours */
666 
667 #endif /* !defined(MSDOS) && !defined(AMIGA) */
668 
669 #if !defined(VMS) && !defined(MSDOS)
670 
671 /*
672  * codes_to_text
673  *
674  * Translate Z-code characters to machine specific characters. These characters
675  * include line drawing characters and international characters.
676  *
677  * The routine takes one of the Z-code characters from the following table and
678  * writes the machine specific text replacement. The target replacement buffer
679  * is defined by MAX_TEXT_SIZE in ztypes.h. The replacement text should be in a
680  * normal C, zero terminated, string.
681  *
682  * Return 0 if a translation was available, otherwise 1.
683  *
684  *  Arrow characters (0x18 - 0x1b):
685  *
686  *  0x18 Up arrow
687  *  0x19 Down arrow
688  *  0x1a Right arrow
689  *  0x1b Left arrow
690  *
691  *  International characters (0x9b - 0xa3):
692  *
693  *  0x9b a umlaut (ae)
694  *  0x9c o umlaut (oe)
695  *  0x9d u umlaut (ue)
696  *  0x9e A umlaut (Ae)
697  *  0x9f O umlaut (Oe)
698  *  0xa0 U umlaut (Ue)
699  *  0xa1 sz (ss)
700  *  0xa2 open quote (>>)
701  *  0xa3 close quota (<<)
702  *
703  *  Line drawing characters (0xb3 - 0xda):
704  *
705  *  0xb3 vertical line (|)
706  *  0xba double vertical line (#)
707  *  0xc4 horizontal line (-)
708  *  0xcd double horizontal line (=)
709  *  all other are corner pieces (+)
710  *
711  */
712 
713 #ifdef __STDC__
codes_to_text(int c,char * s)714 int codes_to_text (int c, char *s)
715 #else
716 int codes_to_text (c, s)
717 int c;
718 char *s;
719 #endif
720 {
721     if (c >= 220 && c <= 221) {
722         if (c == 220) {
723 	    s[0] = 'o';
724 	    s[1] = 'e';
725 	}
726 	else {
727 	    s[0] = 'O';
728 	    s[1] = 'E';
729 	}
730 	s[2] = '\0';
731 	return 0;
732     }
733 
734     if (c >= 155 && c <= 223) {
735         static char xlat[] = "\344\366\374\304\326\334\337\273\253\353\357\377\313\317\341\351\355\363\372\375\301\311\315\323\332\335\340\350\354\362\371\300\310\314\322\331\342\352\356\364\373\302\312\316\324\333\345\305\370\330\343\361\365\303\321\325\346\306\347\307\376\360\336\320\243oO\241\277";
736 	s[0] = xlat[c - 155];
737 	s[1] = '\0';
738 	return 0;
739     }
740 
741     return 1;
742 
743 }/* codes_to_text */
744 
745 #endif /* !defined(VMS) && !defined(MSDOS) */
746