Add Bocfel interpreter
[projects/chimara/chimara.git] / interpreters / bocfel / zterp.c
1 /*-
2  * Copyright 2009-2012 Chris Spiegel.
3  *
4  * This file is part of Bocfel.
5  *
6  * Bocfel is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License, version
8  * 2 or 3, as published by the Free Software Foundation.
9  *
10  * Bocfel is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with Bocfel.  If not, see <http://www.gnu.org/licenses/>.
17  */
18
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <string.h>
22 #include <stdint.h>
23 #include <ctype.h>
24 #include <signal.h>
25 #include <limits.h>
26
27 #include "zterp.h"
28 #include "blorb.h"
29 #include "branch.h"
30 #include "io.h"
31 #include "memory.h"
32 #include "osdep.h"
33 #include "process.h"
34 #include "random.h"
35 #include "screen.h"
36 #include "stack.h"
37 #include "unicode.h"
38 #include "util.h"
39
40 #ifdef ZTERP_GLK
41 #include <glk.h>
42 #include <libchimara/garglk.h>
43 #ifdef GARGLK
44 #include <glkstart.h>
45 #include <gi_blorb.h>
46
47 static schanid_t sound_channel = NULL;
48 #endif
49 #endif
50
51 #define MAX_LINE        2048
52 #define MAX_PATH        4096
53
54 #define ZTERP_VERSION   "0.6.1"
55
56 const char *game_file;
57 struct options options = {
58   .eval_stack_size = DEFAULT_STACK_SIZE,
59   .call_stack_size = DEFAULT_CALL_DEPTH,
60   .disable_color = 0,
61   .disable_config = 0,
62   .disable_sound = 0,
63   .disable_timed = 0,
64   .enable_escape = 0,
65   .escape_string = NULL,
66   .disable_fixed = 0,
67   .assume_fixed = 0,
68   .disable_graphics_font = 0,
69   .enable_alt_graphics = 0,
70   .show_id = 0,
71   .disable_term_keys = 0,
72   .disable_utf8 = 0,
73   .force_utf8 = 0,
74   .disable_meta_commands = 0,
75   .int_number = 1, /* DEC */
76   .int_version = 'C',
77   .replay_on = 0,
78   .replay_name = NULL,
79   .record_on = 0,
80   .record_name = NULL,
81   .transcript_on = 0,
82   .transcript_name = NULL,
83   .max_saves = 10,
84   .disable_undo_compression = 0,
85   .show_version = 0,
86   .disable_abbreviations = 0,
87   .enable_censorship = 0,
88   .overwrite_transcript = 0,
89   .random_seed = -1,
90   .random_device = NULL,
91 };
92
93 static char story_id[64];
94
95 uint32_t pc;
96
97 /* zversion stores the Z-machine version of the story: 1–6.
98  *
99  * Z-machine versions 7 and 8 are identical to version 5 but for a
100  * couple of tiny details.  They are thus classified as version 5.
101  *
102  * zwhich stores the actual version (1–8) for the few rare times where
103  * this knowledge is necessary.
104  */
105 int zversion;
106 static int zwhich;
107
108 struct header header;
109
110 static struct
111 {
112   zterp_io *io;
113   long offset;
114 } story;
115
116 /* The null character in the alphabet table does not actually signify a
117  * null character: character 6 from A2 is special in that it specifies
118  * that the next two characters form a 10-bit ZSCII character (§3.4).
119  * The code that uses the alphabet table will step around this character
120  * when necessary, so it’s safe to use a null character here to mean
121  * “nothing”.
122  */
123 uint8_t atable[26 * 3] =
124 {
125   /* A0 */
126   'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
127   'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
128
129   /* A1 */
130   'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
131   'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
132
133   /* A2 */
134   0x0, 0xd, '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '.',
135   ',', '!', '?', '_', '#', '\'','"', '/', '\\','-', ':', '(', ')',
136 };
137
138 void znop(void)
139 {
140 }
141
142 void zquit(void)
143 {
144   break_from(0);
145 }
146
147 void zverify(void)
148 {
149   uint16_t checksum = 0;
150   uint32_t remaining = header.file_length - 0x40;
151
152   if(zterp_io_seek(story.io, story.offset + 0x40, SEEK_SET) == -1)
153   {
154     branch_if(0);
155     return;
156   }
157
158   while(remaining != 0)
159   {
160     uint8_t buf[8192];
161     uint32_t to_read = remaining < sizeof buf ? remaining : sizeof buf;
162
163     if(zterp_io_read(story.io, buf, to_read) != to_read)
164     {
165       branch_if(0);
166       return;
167     }
168
169     for(uint32_t i = 0; i < to_read; i++) checksum += buf[i];
170
171     remaining -= to_read;
172   }
173
174   branch_if(checksum == header.checksum);
175 }
176
177 uint32_t unpack(uint16_t addr, int string)
178 {
179   switch(zwhich)
180   {
181     case 1: case 2: case 3:
182       return addr * 2UL;
183     case 4: case 5:
184       return addr * 4UL;
185     case 6: case 7:
186       return (addr * 4UL) + (string ? header.S_O : header.R_O);
187     case 8:
188       return addr * 8UL;
189     default:
190       die("unhandled z-machine version: %d", zwhich);
191   }
192 }
193
194 void store(uint16_t v)
195 {
196   store_variable(BYTE(pc++), v);
197 }
198
199 void zsave5(void)
200 {
201   zterp_io *savefile;
202   size_t n;
203
204   if(znargs == 0)
205   {
206     zsave();
207     return;
208   }
209
210   /* This should be able to suggest a filename, but Glk doesn’t support that. */
211   savefile = zterp_io_open(NULL, ZTERP_IO_WRONLY | ZTERP_IO_SAVE);
212   if(savefile == NULL)
213   {
214     store(0);
215     return;
216   }
217
218   ZASSERT(zargs[0] + zargs[1] < memory_size, "attempt to save beyond the end of memory");
219   n = zterp_io_write(savefile, &memory[zargs[0]], zargs[1]);
220
221   zterp_io_close(savefile);
222
223   store(n == zargs[1]);
224 }
225
226 void zrestore5(void)
227 {
228   zterp_io *savefile;
229   uint8_t *buf;
230   size_t n;
231
232   if(znargs == 0)
233   {
234     zrestore();
235     return;
236   }
237
238   savefile = zterp_io_open(NULL, ZTERP_IO_RDONLY | ZTERP_IO_SAVE);
239   if(savefile == NULL)
240   {
241     store(0);
242     return;
243   }
244
245   buf = malloc(zargs[1]);
246   if(buf == NULL)
247   {
248     store(0);
249     return;
250   }
251
252   n = zterp_io_read(savefile, buf, zargs[1]);
253   for(size_t i = 0; i < n; i++) user_store_byte(zargs[0] + i, buf[i]);
254
255   free(buf);
256
257   zterp_io_close(savefile);
258
259   store(n);
260 }
261
262 void zpiracy(void)
263 {
264   branch_if(1);
265 }
266
267 void zsound_effect(void)
268 {
269 #ifdef GARGLK
270   uint8_t repeats, volume;
271   static uint32_t vols[8] = {
272     0x02000, 0x04000, 0x06000, 0x08000,
273     0x0a000, 0x0c000, 0x0e000, 0x10000
274   };
275
276   if(sound_channel == NULL || zargs[0] < 3) return;
277
278   repeats = zargs[2] >> 8;
279   volume = zargs[2] & 0xff;
280
281   if(volume == 0) volume = 1;
282   if(volume  > 8) volume = 8;
283
284   glk_schannel_set_volume(sound_channel, vols[volume - 1]);
285
286   switch(zargs[1])
287   {
288     case 1: /* prepare */
289       glk_sound_load_hint(zargs[0], 1);
290       break;
291     case 2: /* start */
292       glk_schannel_play_ext(sound_channel, zargs[0], repeats == 255 ? -1 : repeats, 0);
293       break;
294     case 3: /* stop */
295       glk_schannel_stop(sound_channel);
296       break;
297     case 4: /* finish with */
298       glk_sound_load_hint(zargs[0], 0);
299       break;
300   }
301 #endif
302 }
303
304 /* Find a story ID roughly in the form of an IFID according to §2.2.2.1
305  * of draft 7 of the Treaty of Babel.
306  *
307  * This does not add a ZCODE- prefix, and will not search for a manually
308  * created IFID.
309  */
310 static void find_id(void)
311 {
312   char serial[] = "------";
313
314   for(int i = 0; i < 6; i++)
315   {
316     /* isalnum() cannot be used because it is locale-aware, and this
317      * must only check for A–Z, a–z, and 0–9.  Because ASCII (or a
318      * compatible charset) is required, testing against 'a', 'z', etc.
319      * is OK.
320      */
321 #define ALNUM(c) ( ((c) >= 'a' && (c) <= 'z') || ((c) >= 'A' && (c) <= 'Z') || ((c) >= '0' && (c) <= '9') )
322     if(ALNUM(header.serial[i])) serial[i] = header.serial[i];
323 #undef ALNUM
324   }
325
326   if(strchr("012345679", serial[0]) != NULL && strcmp(serial, "000000") != 0)
327   {
328     snprintf(story_id, sizeof story_id, "%d-%s-%04x", header.release, serial, (unsigned)header.checksum);
329   }
330   else
331   {
332     snprintf(story_id, sizeof story_id, "%d-%s", header.release, serial);
333   }
334 }
335
336 int is_story(const char *id)
337 {
338   return strcmp(story_id, id) == 0;
339 }
340
341 #ifndef ZTERP_NO_CHEAT
342 /* The index into these arrays is the address to freeze.
343  * The first array tracks whether the address is frozen, while the
344  * second holds the frozen value.
345  */
346 static char     freezew_cheat[UINT16_MAX + 1];
347 static uint16_t freezew_val  [UINT16_MAX + 1];
348
349 static void cheat(char *how)
350 {
351   char *p;
352
353   p = strtok(how, ":");
354   if(p == NULL) return;
355
356   if(strcmp(p, "freezew") == 0)
357   {
358     uint16_t addr;
359
360     p = strtok(NULL, ":");
361     if(p == NULL) return;
362
363     if(*p == 'G')
364     {
365       addr = strtoul(p + 1, NULL, 16);
366       if(addr > 239) return;
367
368       addr = header.globals + (addr * 2);
369     }
370     else
371     {
372       addr = strtoul(p, NULL, 16);
373     }
374
375     p = strtok(NULL, ":");
376     if(p == NULL) return;
377
378     freezew_cheat[addr] = 1;
379     freezew_val  [addr] = strtoul(p, NULL, 0);
380   }
381 }
382
383 int cheat_find_freezew(uint32_t addr, uint16_t *val)
384 {
385   if(addr > UINT16_MAX || !freezew_cheat[addr]) return 0;
386
387   *val = freezew_val[addr];
388
389   return 1;
390 }
391 #endif
392
393 static void read_config(void)
394 {
395   FILE *fp;
396   char file[MAX_PATH + 1];
397   char line[MAX_LINE];
398   char *key, *val, *p;
399   long n;
400   int story_matches = 1;
401
402   zterp_os_rcfile(file, sizeof file);
403
404   fp = fopen(file, "r");
405   if(fp == NULL) return;
406
407   while(fgets(line, sizeof line, fp) != NULL)
408   {
409     line[strcspn(line, "#\n")] = 0;
410     if(line[0] == 0) continue;
411
412     if(line[0] == '[')
413     {
414       p = strrchr(line, ']');
415       if(p != NULL && p[1] == 0)
416       {
417         *p = 0;
418
419         story_matches = 0;
420         for(p = strtok(line + 1, " ,"); p != NULL; p = strtok(NULL, " ,"))
421         {
422           if(is_story(p)) story_matches = 1;
423         }
424       }
425
426       continue;
427     }
428
429     if(!story_matches) continue;
430
431     key = strtok(line, " \t=");
432     if(key == NULL) continue;
433     val = strtok(NULL, "=");
434     if(val == NULL) continue;
435
436     /* Trim whitespace. */
437     while(isspace((unsigned char)*val)) val++;
438     if(*val == 0) continue;
439     p = val + strlen(val) - 1;
440     while(isspace((unsigned char)*p)) *p-- = 0;
441
442     n = strtol(val, NULL, 10);
443
444 #define BOOL(name)      else if(strcmp(key, #name) == 0) options.name = (n != 0)
445 #define NUMBER(name)    else if(strcmp(key, #name) == 0) options.name = n
446 #define STRING(name)    else if(strcmp(key, #name) == 0) do { free(options.name); options.name = xstrdup(val); } while(0)
447 #define CHAR(name)      else if(strcmp(key, #name) == 0) options.name = val[0]
448 #ifdef GARGLK
449 #define COLOR(name, num)else if(strcmp(key, "color_" #name) == 0) update_color(num, strtol(val, NULL, 16))
450 #else
451 #define COLOR(name, num)else if(0)
452 #endif
453
454     if(0);
455
456     NUMBER(eval_stack_size);
457     NUMBER(call_stack_size);
458     BOOL  (disable_color);
459     BOOL  (disable_timed);
460     BOOL  (disable_sound);
461     BOOL  (enable_escape);
462     STRING(escape_string);
463     BOOL  (disable_fixed);
464     BOOL  (assume_fixed);
465     BOOL  (disable_graphics_font);
466     BOOL  (enable_alt_graphics);
467     BOOL  (disable_term_keys);
468     BOOL  (disable_utf8);
469     BOOL  (force_utf8);
470     BOOL  (disable_meta_commands);
471     NUMBER(max_saves);
472     BOOL  (disable_undo_compression);
473     NUMBER(int_number);
474     CHAR  (int_version);
475     BOOL  (replay_on);
476     STRING(replay_name);
477     BOOL  (record_on);
478     STRING(record_name);
479     BOOL  (transcript_on);
480     STRING(transcript_name);
481     BOOL  (disable_abbreviations);
482     BOOL  (enable_censorship);
483     BOOL  (overwrite_transcript);
484     NUMBER(random_seed);
485     STRING(random_device);
486
487     COLOR(black,   2);
488     COLOR(red,     3);
489     COLOR(green,   4);
490     COLOR(yellow,  5);
491     COLOR(blue,    6);
492     COLOR(magenta, 7);
493     COLOR(cyan,    8);
494     COLOR(white,   9);
495
496 #ifndef ZTERP_NO_CHEAT
497     else if(strcmp(key, "cheat") == 0) cheat(val);
498 #endif
499
500 #undef BOOL
501 #undef NUMBER
502 #undef STRING
503 #undef CHAR
504 #undef COLOR
505   }
506
507   fclose(fp);
508 }
509
510 static int have_statuswin = 0;
511 static int have_upperwin  = 0;
512
513 /* Various parts of the header (those marked “Rst” in §11) should be
514  * updated by the interpreter.  This function does that.  This is also
515  * used when restoring, because the save file might have come from an
516  * interpreter with vastly different settings.
517  */
518 void write_header(void)
519 {
520   uint8_t flags1;
521
522   flags1 = BYTE(0x01);
523
524   if(zversion == 3)
525   {
526     flags1 |= FLAGS1_NOSTATUS;
527     flags1 &= ~(FLAGS1_SCREENSPLIT | FLAGS1_VARIABLE);
528
529 #ifdef GARGLK
530     /* Assume that if Gargoyle is being used, the default font is not fixed. */
531     flags1 |= FLAGS1_VARIABLE;
532 #endif
533
534     if(have_statuswin)            flags1 &= ~FLAGS1_NOSTATUS;
535     if(have_upperwin)             flags1 |= FLAGS1_SCREENSPLIT;
536     if(options.enable_censorship) flags1 |= FLAGS1_CENSOR;
537   }
538   else if(zversion >= 4)
539   {
540     flags1 |= (FLAGS1_BOLD | FLAGS1_ITALIC | FLAGS1_FIXED);
541     flags1 &= ~FLAGS1_TIMED;
542
543     if(zversion >= 5) flags1 &= ~FLAGS1_COLORS;
544
545     if(zversion == 6)
546     {
547       flags1 &= ~(FLAGS1_PICTURES | FLAGS1_SOUND);
548 #ifdef GARGLK
549       if(sound_channel != NULL) flags1 |= FLAGS1_SOUND;
550 #endif
551     }
552
553 #ifdef ZTERP_GLK
554     if(glk_gestalt(gestalt_Timer, 0)) flags1 |= FLAGS1_TIMED;
555 #ifdef GARGLK
556     if(zversion >= 5) flags1 |= FLAGS1_COLORS;
557 #endif
558 #else
559     if(!zterp_os_have_style(STYLE_BOLD)) flags1 &= ~FLAGS1_BOLD;
560     if(!zterp_os_have_style(STYLE_ITALIC)) flags1 &= ~FLAGS1_ITALIC;
561     if(zversion >= 5 && zterp_os_have_colors()) flags1 |= FLAGS1_COLORS;
562 #endif
563
564     if(zversion >= 5 && options.disable_color) flags1 &= ~FLAGS1_COLORS;
565     if(options.disable_timed) flags1 &= ~FLAGS1_TIMED;
566     if(options.disable_fixed) flags1 &= ~FLAGS1_FIXED;
567   }
568
569   STORE_BYTE(0x01, flags1);
570
571   if(zversion >= 5)
572   {
573     uint16_t flags2 = WORD(0x10);
574
575     flags2 &= ~FLAGS2_MOUSE;
576 #ifdef GARGLK
577     if(sound_channel == NULL) flags2 &= ~FLAGS2_SOUND;
578 #else
579     flags2 &= ~FLAGS2_SOUND;
580 #endif
581     if(zversion >= 6) flags2 &= ~FLAGS2_MENUS;
582
583     if(options.disable_graphics_font) flags2 &= ~FLAGS2_PICTURES;
584
585     if(options.max_saves == 0) flags2 &= ~FLAGS2_UNDO;
586
587     STORE_WORD(0x10, flags2);
588   }
589
590   if(zversion >= 4)
591   {
592     unsigned int width, height;
593
594     /* Interpreter number & version. */
595     if(options.int_number < 1 || options.int_number > 11) options.int_number = 1; /* DEC */
596     STORE_BYTE(0x1e, options.int_number);
597     STORE_BYTE(0x1f, options.int_version);
598
599     get_screen_size(&width, &height);
600
601     /* Screen height and width.
602      * A height of 255 means infinite, so cap at 254.
603      */
604     STORE_BYTE(0x20, height > 254 ? 254 : height);
605     STORE_BYTE(0x21, width > 255 ? 255 : width);
606
607     if(zversion >= 5)
608     {
609       /* Screen width and height in units. */
610       STORE_WORD(0x22, width > UINT16_MAX ? UINT16_MAX : width);
611       STORE_WORD(0x24, height > UINT16_MAX ? UINT16_MAX : height);
612
613       /* Font width and height in units. */
614       STORE_BYTE(0x26, 1);
615       STORE_BYTE(0x27, 1);
616
617       /* Default background and foreground colors. */
618       STORE_BYTE(0x2c, 1);
619       STORE_BYTE(0x2d, 1);
620     }
621   }
622
623   /* Standard revision # */
624   STORE_BYTE(0x32, 1);
625   STORE_BYTE(0x33, 1);
626 }
627
628 void process_story(void)
629 {
630   if(zterp_io_seek(story.io, story.offset, SEEK_SET) == -1) die("unable to rewind story");
631
632   if(zterp_io_read(story.io, memory, memory_size) != memory_size) die("unable to read from story file");
633
634   zversion =            BYTE(0x00);
635   if(zversion < 1 || zversion > 8) die("only z-code versions 1-8 are supported");
636
637   zwhich = zversion;
638   if(zversion == 7 || zversion == 8) zversion = 5;
639
640   pc =                  WORD(0x06);
641   if(pc >= memory_size) die("corrupted story: initial pc out of range");
642
643   header.release =      WORD(0x02);
644   header.dictionary =   WORD(0x08);
645   header.objects =      WORD(0x0a);
646   header.globals =      WORD(0x0c);
647   header.static_start = WORD(0x0e);
648   header.abbr =         WORD(0x18);
649
650   memcpy(header.serial, &memory[0x12], sizeof header.serial);
651
652   /* There is no explicit “end of static” tag; but it must end by 0xffff
653    * or the end of the story file, whichever is smaller.
654    */
655   header.static_end = memory_size < 0xffff ? memory_size : 0xffff;
656
657 #define PROPSIZE        (zversion <= 3 ? 62L : 126L)
658
659   /* There must be at least enough room in dynamic memory for the header
660    * (64 bytes), the global variables table (480 bytes), and the
661    * property defaults table (62 or 126 bytes).
662    */
663   if(header.static_start < 64 + 480 + PROPSIZE)   die("corrupted story: dynamic memory too small (%d bytes)", (int)header.static_start);
664   if(header.static_start >= memory_size)          die("corrupted story: static memory out of range");
665
666   if(header.dictionary != 0 &&
667      header.dictionary < header.static_start)     die("corrupted story: dictionary is not in static memory");
668
669   if(header.objects < 64 ||
670      header.objects + PROPSIZE > header.static_start)
671                                                   die("corrupted story: object table is not in dynamic memory");
672
673 #undef PROPSIZE
674
675   if(header.globals < 64 ||
676      header.globals + 480L > header.static_start) die("corrupted story: global variables are not in dynamic memory");
677
678   if(header.abbr >= memory_size)                  die("corrupted story: abbreviation table out of range");
679
680   header.file_length = WORD(0x1a) * (zwhich <= 3 ? 2UL : zwhich <= 5 ? 4UL : 8UL);
681   if(header.file_length > memory_size)            die("story's reported size (%lu) greater than file size (%lu)", (unsigned long)header.file_length, (unsigned long)memory_size);
682
683   header.checksum = WORD(0x1c);
684
685   if(zwhich == 6 || zwhich == 7)
686   {
687     header.R_O = WORD(0x28) * 8UL;
688     header.S_O = WORD(0x2a) * 8UL;
689   }
690
691   if(dynamic_memory == NULL)
692   {
693     dynamic_memory = malloc(header.static_start);
694     if(dynamic_memory == NULL) die("unable to allocate memory for dynamic memory");
695     memcpy(dynamic_memory, memory, header.static_start);
696   }
697
698 #ifdef GLK_MODULE_LINE_TERMINATORS
699   if(!options.disable_term_keys)
700   {
701     if(zversion >= 5 && WORD(0x2e) != 0)
702     {
703       term_keys_reset();
704
705       for(uint32_t i = WORD(0x2e); i < memory_size && memory[i] != 0; i++)
706       {
707         term_keys_add(memory[i]);
708       }
709     }
710   }
711 #endif
712
713   if(zversion == 1)
714   {
715     memcpy(&atable[26 * 2], " 0123456789.,!?_#'\"/\\<-:()", 26);
716   }
717   else if(zversion >= 5 && WORD(0x34) != 0)
718   {
719     if(WORD(0x34) + 26 * 3 >= memory_size) die("corrupted story: alphabet table out of range");
720
721     memcpy(atable, &memory[WORD(0x34)], 26 * 3);
722
723     /* Even with a new alphabet table, characters 6 and 7 from A2 must
724      * remain the same (§3.5.5.1).
725      */
726     atable[52] = 0x00;
727     atable[53] = 0x0d;
728   }
729
730   /* Check for a header extension table. */
731   if(zversion >= 5)
732   {
733     uint16_t etable = WORD(0x36);
734
735     if(etable != 0)
736     {
737       uint16_t nentries = user_word(etable);
738
739       if(etable + (2 * nentries) >= memory_size) die("corrupted story: header extension table out of range");
740
741       /* Unicode table. */
742       if(nentries >= 3 && WORD(etable + (2 * 3)) != 0)
743       {
744         uint16_t utable = WORD(etable + (2 * 3));
745
746         parse_unicode_table(utable);
747       }
748
749       /* Flags3. */
750       if(nentries >= 4) STORE_WORD(etable + (2 * 4), 0);
751       /* True default foreground color. */
752       if(nentries >= 5) STORE_WORD(etable + (2 * 5), 0x0000);
753       /* True default background color. */
754       if(nentries >= 6) STORE_WORD(etable + (2 * 6), 0x7fff);
755     }
756   }
757
758   /* The configuration file cannot be read until the ID of the current
759    * story is known, and the ID of the current story is not known until
760    * the file has been processed; so do both of those here.
761    */
762   find_id();
763   if(!options.disable_config) read_config();
764
765   /* Prevent the configuration file from unexpectedly being reread after
766    * @restart or @restore.
767    */
768   options.disable_config = 1;
769
770   /* Most options directly set their respective variables, but a few
771    * require intervention.  Delay that intervention until here so that
772    * the configuration file is taken into account.
773    */
774   if(options.disable_utf8)
775   {
776 #ifndef ZTERP_GLK
777     /* If Glk is not being used, the ZSCII to Unicode table needs to be
778      * aligned with the IO character set.
779      */
780     have_unicode = 0;
781 #endif
782     use_utf8_io = 0;
783   }
784   if(options.force_utf8)
785   {
786 #ifndef ZTERP_GLK
787     /* See above. */
788     have_unicode = 1;
789 #endif
790     use_utf8_io = 1;
791   }
792   if(options.escape_string == NULL) options.escape_string = xstrdup("1m");
793
794 #ifdef GARGLK
795   if(options.disable_sound && sound_channel != NULL)
796   {
797     glk_schannel_destroy(sound_channel);
798     sound_channel = NULL;
799   }
800 #endif
801
802   /* Now that we have a Unicode table and the user’s Unicode
803    * preferences, build the ZSCII to Unicode and Unicode to ZSCII
804    * tables.
805    */
806   setup_tables();
807
808   if(zversion <= 3) have_statuswin = create_statuswin();
809   if(zversion >= 3) have_upperwin  = create_upperwin();
810
811   write_header();
812   /* Put everything in a clean state. */
813   seed_random(0);
814   init_stack();
815   init_screen();
816
817   /* Unfortunately, Beyond Zork behaves badly when the interpreter
818    * number is set to DOS: it assumes that it can print out IBM PC
819    * character codes and get useful results (e.g. it writes out 0x18
820    * expecting an up arrow); however, if the pictures bit is set, it
821    * uses the character graphics font like a good citizen.  Thus turn
822    * that bit on when Beyond Zork is being used and the interpreter is
823    * set to DOS.  It might make sense to do this generally, not just for
824    * Beyond Zork; but this is such a minor corner of the Z-machine that
825    * it probably doesn’t matter.  For now, peg this to Beyond Zork.
826    */
827   if(options.int_number == 6 &&
828       (is_story("47-870915") || is_story("49-870917") ||
829        is_story("51-870923") || is_story("57-871221")))
830   {
831     STORE_WORD(0x10, WORD(0x10) | FLAGS2_PICTURES);
832   }
833
834   if(options.transcript_on)
835   {
836     STORE_WORD(0x10, WORD(0x10) | FLAGS2_TRANSCRIPT);
837     options.transcript_on = 0;
838   }
839
840   if(options.record_on)
841   {
842     output_stream(OSTREAM_RECORD, 0);
843     options.record_on = 0;
844   }
845
846   if(options.replay_on)
847   {
848     input_stream(ISTREAM_FILE);
849     options.replay_on = 0;
850   }
851
852   if(zversion == 6)
853   {
854     zargs[0] = pc;
855     call(0);
856   }
857 }
858
859 #ifdef ZTERP_GLK
860 zexternally_visible
861 void glk_main(void)
862 #else
863 int main(int argc, char **argv)
864 #endif
865 {
866   zterp_blorb *blorb;
867
868 #ifdef ZTERP_GLK
869   if(!create_mainwin()) return;
870 #ifdef GLK_MODULE_UNICODE
871   have_unicode = glk_gestalt(gestalt_Unicode, 0);
872 #endif
873 #else
874   have_unicode = zterp_os_have_unicode();
875 #endif
876
877   use_utf8_io = zterp_os_have_unicode();
878
879 #ifndef ZTERP_GLK
880   if(!process_arguments(argc, argv)) exit(EXIT_FAILURE);
881
882   zterp_os_init_term();
883 #endif
884
885 #ifdef ZTERP_GLK
886 #define PRINT(s)        do { glk_put_string(s); glk_put_char(UNICODE_LINEFEED); } while(0)
887 #else
888 #define PRINT(s)        puts(s)
889 #endif
890
891   if(options.show_version)
892   {
893     char config[MAX_PATH] = "Configuration file: ";
894
895     PRINT("Bocfel " ZTERP_VERSION);
896 #ifdef ZTERP_NO_SAFETY_CHECKS
897     PRINT("Runtime assertions disabled");
898 #else
899     PRINT("Runtime assertions enabled");
900 #endif
901 #ifdef ZTERP_NO_CHEAT
902     PRINT("Cheat support disabled");
903 #else
904     PRINT("Cheat support enabled");
905 #endif
906 #ifdef ZTERP_TANDY
907     PRINT("The Tandy bit can be set");
908 #else
909     PRINT("The Tandy bit cannot be set");
910 #endif
911
912     zterp_os_rcfile(config + strlen(config), sizeof config - strlen(config));
913     PRINT(config);
914
915 #ifdef ZTERP_GLK
916     glk_exit();
917 #else
918     exit(0);
919 #endif
920   }
921
922 #undef PRINT
923
924 #ifdef GARGLK
925   if(game_file == NULL)
926   {
927     frefid_t ref;
928
929     ref = glk_fileref_create_by_prompt(fileusage_Data | fileusage_BinaryMode, filemode_Read, 0);
930     if(ref != NULL)
931     {
932       game_file = xstrdup(garglk_fileref_get_name(ref));
933       glk_fileref_destroy(ref);
934     }
935   }
936 #endif
937
938   if(game_file == NULL) die("no story provided");
939
940   story.io = zterp_io_open(game_file, ZTERP_IO_RDONLY);
941   if(story.io == NULL) die("cannot open file %s", game_file);
942
943   blorb = zterp_blorb_parse(story.io);
944   if(blorb != NULL)
945   {
946     const zterp_blorb_chunk *chunk;
947
948     chunk = zterp_blorb_find(blorb, BLORB_EXEC, 0);
949     if(chunk == NULL) die("no EXEC resource found");
950     if(strcmp(chunk->name, "ZCOD") != 0)
951     {
952       if(strcmp(chunk->name, "GLUL") == 0) die("Glulx stories are not supported (try git or glulxe)");
953
954       die("unknown story type: %s", chunk->name);
955     }
956
957     if(chunk->offset > LONG_MAX) die("zcode offset too large");
958
959     memory_size = chunk->size;
960     story.offset = chunk->offset;
961
962     zterp_blorb_free(blorb);
963   }
964   else
965   {
966     long size = zterp_io_filesize(story.io);
967
968     if(size == -1) die("unable to determine file size");
969     if(size > UINT32_MAX) die("file too large");
970
971     memory_size = size;
972     story.offset = 0;
973   }
974
975 #ifdef GARGLK
976   if(glk_gestalt(gestalt_Sound, 0))
977   {
978     /* 5 for the worst case of needing to add .blb to the end plus the
979      * null character.
980      */
981     char *blorb_file = malloc(strlen(game_file) + 5);
982     if(blorb_file != NULL)
983     {
984       char *p;
985       strid_t file;
986
987       strcpy(blorb_file, game_file);
988       p = strrchr(blorb_file, '.');
989       if(p != NULL) *p = 0;
990       strcat(blorb_file, ".blb");
991
992       file = glkunix_stream_open_pathname(blorb_file, 0, 0);
993       if(file != NULL)
994       {
995         giblorb_set_resource_map(file);
996         sound_channel = glk_schannel_create(0);
997       }
998
999       free(blorb_file);
1000     }
1001   }
1002 #endif
1003
1004   if(memory_size < 64) die("story file too small");
1005   if(memory_size > SIZE_MAX - 22) die("story file too large");
1006
1007   /* It’s possible for a story to be cut short in the middle of an
1008    * instruction.  If so, the processing loop will run past the end of
1009    * memory.  Either pc needs to be checked each and every time it is
1010    * incremented, or a small guard needs to be placed at the end of
1011    * memory that will trigger an illegal instruction error.  The latter
1012    * is done by filling the end of memory with zeroes, which do not
1013    * represent a valid instruction.
1014    *
1015    * There need to be at least 22 bytes for the worst case: 0xec
1016    * (call_vs2) as the last byte in memory.  The next two bytes, which
1017    * will be zeroes, indicate that 8 large constants, or 16 bytes, will
1018    * be next.  This is a store instruction, so one more byte will be
1019    * read to determine where to store.  Another byte is read to
1020    * determine the next opcode; this will be zero, which is nominally a
1021    * 2OP, requiring two more bytes to be read.  At this point the opcode
1022    * will be looked up, resulting in an illegal instruction error.
1023    */
1024   memory = malloc(memory_size + 22);
1025   if(memory == NULL) die("unable to allocate memory for story file");
1026   memset(memory + memory_size, 0, 22);
1027
1028   process_story();
1029
1030   /* If header transcript/fixed bits have been set, either by the
1031    * story or by the user, this will activate them.
1032    */
1033   user_store_word(0x10, WORD(0x10));
1034
1035   if(options.show_id)
1036   {
1037 #ifdef ZTERP_GLK
1038     glk_put_string(story_id);
1039     glk_exit();
1040 #else
1041     puts(story_id);
1042     exit(0);
1043 #endif
1044   }
1045
1046   setup_opcodes();
1047
1048   process_instructions();
1049
1050 #ifndef ZTERP_GLK
1051   return 0;
1052 #endif
1053 }