2 * Copyright 2009-2012 Chris Spiegel.
4 * This file is part of Bocfel.
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.
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.
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/>.
42 #include <libchimara/garglk.h>
47 static schanid_t sound_channel = NULL;
54 #define ZTERP_VERSION "0.6.1"
56 const char *game_file;
57 struct options options = {
58 .eval_stack_size = DEFAULT_STACK_SIZE,
59 .call_stack_size = DEFAULT_CALL_DEPTH,
65 .escape_string = NULL,
68 .disable_graphics_font = 0,
69 .enable_alt_graphics = 0,
71 .disable_term_keys = 0,
74 .disable_meta_commands = 0,
75 .int_number = 1, /* DEC */
82 .transcript_name = NULL,
84 .disable_undo_compression = 0,
86 .disable_abbreviations = 0,
87 .enable_censorship = 0,
88 .overwrite_transcript = 0,
90 .random_device = NULL,
93 static char story_id[64];
97 /* zversion stores the Z-machine version of the story: 1–6.
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.
102 * zwhich stores the actual version (1–8) for the few rare times where
103 * this knowledge is necessary.
108 struct header header;
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
123 uint8_t atable[26 * 3] =
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',
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',
134 0x0, 0xd, '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '.',
135 ',', '!', '?', '_', '#', '\'','"', '/', '\\','-', ':', '(', ')',
149 uint16_t checksum = 0;
150 uint32_t remaining = header.file_length - 0x40;
152 if(zterp_io_seek(story.io, story.offset + 0x40, SEEK_SET) == -1)
158 while(remaining != 0)
161 uint32_t to_read = remaining < sizeof buf ? remaining : sizeof buf;
163 if(zterp_io_read(story.io, buf, to_read) != to_read)
169 for(uint32_t i = 0; i < to_read; i++) checksum += buf[i];
171 remaining -= to_read;
174 branch_if(checksum == header.checksum);
177 uint32_t unpack(uint16_t addr, int string)
181 case 1: case 2: case 3:
186 return (addr * 4UL) + (string ? header.S_O : header.R_O);
190 die("unhandled z-machine version: %d", zwhich);
194 void store(uint16_t v)
196 store_variable(BYTE(pc++), v);
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);
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]);
221 zterp_io_close(savefile);
223 store(n == zargs[1]);
238 savefile = zterp_io_open(NULL, ZTERP_IO_RDONLY | ZTERP_IO_SAVE);
245 buf = malloc(zargs[1]);
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]);
257 zterp_io_close(savefile);
267 void zsound_effect(void)
270 uint8_t repeats, volume;
271 static uint32_t vols[8] = {
272 0x02000, 0x04000, 0x06000, 0x08000,
273 0x0a000, 0x0c000, 0x0e000, 0x10000
276 if(sound_channel == NULL || zargs[0] < 3) return;
278 repeats = zargs[2] >> 8;
279 volume = zargs[2] & 0xff;
281 if(volume == 0) volume = 1;
282 if(volume > 8) volume = 8;
284 glk_schannel_set_volume(sound_channel, vols[volume - 1]);
288 case 1: /* prepare */
289 glk_sound_load_hint(zargs[0], 1);
292 glk_schannel_play_ext(sound_channel, zargs[0], repeats == 255 ? -1 : repeats, 0);
295 glk_schannel_stop(sound_channel);
297 case 4: /* finish with */
298 glk_sound_load_hint(zargs[0], 0);
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.
307 * This does not add a ZCODE- prefix, and will not search for a manually
310 static void find_id(void)
312 char serial[] = "------";
314 for(int i = 0; i < 6; i++)
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.
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];
326 if(strchr("012345679", serial[0]) != NULL && strcmp(serial, "000000") != 0)
328 snprintf(story_id, sizeof story_id, "%d-%s-%04x", header.release, serial, (unsigned)header.checksum);
332 snprintf(story_id, sizeof story_id, "%d-%s", header.release, serial);
336 int is_story(const char *id)
338 return strcmp(story_id, id) == 0;
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.
346 static char freezew_cheat[UINT16_MAX + 1];
347 static uint16_t freezew_val [UINT16_MAX + 1];
349 static void cheat(char *how)
353 p = strtok(how, ":");
354 if(p == NULL) return;
356 if(strcmp(p, "freezew") == 0)
360 p = strtok(NULL, ":");
361 if(p == NULL) return;
365 addr = strtoul(p + 1, NULL, 16);
366 if(addr > 239) return;
368 addr = header.globals + (addr * 2);
372 addr = strtoul(p, NULL, 16);
375 p = strtok(NULL, ":");
376 if(p == NULL) return;
378 freezew_cheat[addr] = 1;
379 freezew_val [addr] = strtoul(p, NULL, 0);
383 int cheat_find_freezew(uint32_t addr, uint16_t *val)
385 if(addr > UINT16_MAX || !freezew_cheat[addr]) return 0;
387 *val = freezew_val[addr];
393 static void read_config(void)
396 char file[MAX_PATH + 1];
400 int story_matches = 1;
402 zterp_os_rcfile(file, sizeof file);
404 fp = fopen(file, "r");
405 if(fp == NULL) return;
407 while(fgets(line, sizeof line, fp) != NULL)
409 line[strcspn(line, "#\n")] = 0;
410 if(line[0] == 0) continue;
414 p = strrchr(line, ']');
415 if(p != NULL && p[1] == 0)
420 for(p = strtok(line + 1, " ,"); p != NULL; p = strtok(NULL, " ,"))
422 if(is_story(p)) story_matches = 1;
429 if(!story_matches) continue;
431 key = strtok(line, " \t=");
432 if(key == NULL) continue;
433 val = strtok(NULL, "=");
434 if(val == NULL) continue;
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;
442 n = strtol(val, NULL, 10);
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]
449 #define COLOR(name, num)else if(strcmp(key, "color_" #name) == 0) update_color(num, strtol(val, NULL, 16))
451 #define COLOR(name, num)else if(0)
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);
465 BOOL (disable_graphics_font);
466 BOOL (enable_alt_graphics);
467 BOOL (disable_term_keys);
470 BOOL (disable_meta_commands);
472 BOOL (disable_undo_compression);
479 BOOL (transcript_on);
480 STRING(transcript_name);
481 BOOL (disable_abbreviations);
482 BOOL (enable_censorship);
483 BOOL (overwrite_transcript);
485 STRING(random_device);
496 #ifndef ZTERP_NO_CHEAT
497 else if(strcmp(key, "cheat") == 0) cheat(val);
510 static int have_statuswin = 0;
511 static int have_upperwin = 0;
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.
518 void write_header(void)
526 flags1 |= FLAGS1_NOSTATUS;
527 flags1 &= ~(FLAGS1_SCREENSPLIT | FLAGS1_VARIABLE);
530 /* Assume that if Gargoyle is being used, the default font is not fixed. */
531 flags1 |= FLAGS1_VARIABLE;
534 if(have_statuswin) flags1 &= ~FLAGS1_NOSTATUS;
535 if(have_upperwin) flags1 |= FLAGS1_SCREENSPLIT;
536 if(options.enable_censorship) flags1 |= FLAGS1_CENSOR;
538 else if(zversion >= 4)
540 flags1 |= (FLAGS1_BOLD | FLAGS1_ITALIC | FLAGS1_FIXED);
541 flags1 &= ~FLAGS1_TIMED;
543 if(zversion >= 5) flags1 &= ~FLAGS1_COLORS;
547 flags1 &= ~(FLAGS1_PICTURES | FLAGS1_SOUND);
549 if(sound_channel != NULL) flags1 |= FLAGS1_SOUND;
554 if(glk_gestalt(gestalt_Timer, 0)) flags1 |= FLAGS1_TIMED;
556 if(zversion >= 5) flags1 |= FLAGS1_COLORS;
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;
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;
569 STORE_BYTE(0x01, flags1);
573 uint16_t flags2 = WORD(0x10);
575 flags2 &= ~FLAGS2_MOUSE;
577 if(sound_channel == NULL) flags2 &= ~FLAGS2_SOUND;
579 flags2 &= ~FLAGS2_SOUND;
581 if(zversion >= 6) flags2 &= ~FLAGS2_MENUS;
583 if(options.disable_graphics_font) flags2 &= ~FLAGS2_PICTURES;
585 if(options.max_saves == 0) flags2 &= ~FLAGS2_UNDO;
587 STORE_WORD(0x10, flags2);
592 unsigned int width, height;
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);
599 get_screen_size(&width, &height);
601 /* Screen height and width.
602 * A height of 255 means infinite, so cap at 254.
604 STORE_BYTE(0x20, height > 254 ? 254 : height);
605 STORE_BYTE(0x21, width > 255 ? 255 : width);
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);
613 /* Font width and height in units. */
617 /* Default background and foreground colors. */
623 /* Standard revision # */
628 void process_story(void)
630 if(zterp_io_seek(story.io, story.offset, SEEK_SET) == -1) die("unable to rewind story");
632 if(zterp_io_read(story.io, memory, memory_size) != memory_size) die("unable to read from story file");
634 zversion = BYTE(0x00);
635 if(zversion < 1 || zversion > 8) die("only z-code versions 1-8 are supported");
638 if(zversion == 7 || zversion == 8) zversion = 5;
641 if(pc >= memory_size) die("corrupted story: initial pc out of range");
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);
650 memcpy(header.serial, &memory[0x12], sizeof header.serial);
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.
655 header.static_end = memory_size < 0xffff ? memory_size : 0xffff;
657 #define PROPSIZE (zversion <= 3 ? 62L : 126L)
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).
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");
666 if(header.dictionary != 0 &&
667 header.dictionary < header.static_start) die("corrupted story: dictionary is not in static memory");
669 if(header.objects < 64 ||
670 header.objects + PROPSIZE > header.static_start)
671 die("corrupted story: object table is not in dynamic memory");
675 if(header.globals < 64 ||
676 header.globals + 480L > header.static_start) die("corrupted story: global variables are not in dynamic memory");
678 if(header.abbr >= memory_size) die("corrupted story: abbreviation table out of range");
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);
683 header.checksum = WORD(0x1c);
685 if(zwhich == 6 || zwhich == 7)
687 header.R_O = WORD(0x28) * 8UL;
688 header.S_O = WORD(0x2a) * 8UL;
691 if(dynamic_memory == NULL)
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);
698 #ifdef GLK_MODULE_LINE_TERMINATORS
699 if(!options.disable_term_keys)
701 if(zversion >= 5 && WORD(0x2e) != 0)
705 for(uint32_t i = WORD(0x2e); i < memory_size && memory[i] != 0; i++)
707 term_keys_add(memory[i]);
715 memcpy(&atable[26 * 2], " 0123456789.,!?_#'\"/\\<-:()", 26);
717 else if(zversion >= 5 && WORD(0x34) != 0)
719 if(WORD(0x34) + 26 * 3 >= memory_size) die("corrupted story: alphabet table out of range");
721 memcpy(atable, &memory[WORD(0x34)], 26 * 3);
723 /* Even with a new alphabet table, characters 6 and 7 from A2 must
724 * remain the same (§3.5.5.1).
730 /* Check for a header extension table. */
733 uint16_t etable = WORD(0x36);
737 uint16_t nentries = user_word(etable);
739 if(etable + (2 * nentries) >= memory_size) die("corrupted story: header extension table out of range");
742 if(nentries >= 3 && WORD(etable + (2 * 3)) != 0)
744 uint16_t utable = WORD(etable + (2 * 3));
746 parse_unicode_table(utable);
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);
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.
763 if(!options.disable_config) read_config();
765 /* Prevent the configuration file from unexpectedly being reread after
766 * @restart or @restore.
768 options.disable_config = 1;
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.
774 if(options.disable_utf8)
777 /* If Glk is not being used, the ZSCII to Unicode table needs to be
778 * aligned with the IO character set.
784 if(options.force_utf8)
792 if(options.escape_string == NULL) options.escape_string = xstrdup("1m");
795 if(options.disable_sound && sound_channel != NULL)
797 glk_schannel_destroy(sound_channel);
798 sound_channel = NULL;
802 /* Now that we have a Unicode table and the user’s Unicode
803 * preferences, build the ZSCII to Unicode and Unicode to ZSCII
808 if(zversion <= 3) have_statuswin = create_statuswin();
809 if(zversion >= 3) have_upperwin = create_upperwin();
812 /* Put everything in a clean state. */
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.
827 if(options.int_number == 6 &&
828 (is_story("47-870915") || is_story("49-870917") ||
829 is_story("51-870923") || is_story("57-871221")))
831 STORE_WORD(0x10, WORD(0x10) | FLAGS2_PICTURES);
834 if(options.transcript_on)
836 STORE_WORD(0x10, WORD(0x10) | FLAGS2_TRANSCRIPT);
837 options.transcript_on = 0;
840 if(options.record_on)
842 output_stream(OSTREAM_RECORD, 0);
843 options.record_on = 0;
846 if(options.replay_on)
848 input_stream(ISTREAM_FILE);
849 options.replay_on = 0;
863 int main(int argc, char **argv)
869 if(!create_mainwin()) return;
870 #ifdef GLK_MODULE_UNICODE
871 have_unicode = glk_gestalt(gestalt_Unicode, 0);
874 have_unicode = zterp_os_have_unicode();
877 use_utf8_io = zterp_os_have_unicode();
880 if(!process_arguments(argc, argv)) exit(EXIT_FAILURE);
882 zterp_os_init_term();
886 #define PRINT(s) do { glk_put_string(s); glk_put_char(UNICODE_LINEFEED); } while(0)
888 #define PRINT(s) puts(s)
891 if(options.show_version)
893 char config[MAX_PATH] = "Configuration file: ";
895 PRINT("Bocfel " ZTERP_VERSION);
896 #ifdef ZTERP_NO_SAFETY_CHECKS
897 PRINT("Runtime assertions disabled");
899 PRINT("Runtime assertions enabled");
901 #ifdef ZTERP_NO_CHEAT
902 PRINT("Cheat support disabled");
904 PRINT("Cheat support enabled");
907 PRINT("The Tandy bit can be set");
909 PRINT("The Tandy bit cannot be set");
912 zterp_os_rcfile(config + strlen(config), sizeof config - strlen(config));
925 if(game_file == NULL)
929 ref = glk_fileref_create_by_prompt(fileusage_Data | fileusage_BinaryMode, filemode_Read, 0);
932 game_file = xstrdup(garglk_fileref_get_name(ref));
933 glk_fileref_destroy(ref);
938 if(game_file == NULL) die("no story provided");
940 story.io = zterp_io_open(game_file, ZTERP_IO_RDONLY);
941 if(story.io == NULL) die("cannot open file %s", game_file);
943 blorb = zterp_blorb_parse(story.io);
946 const zterp_blorb_chunk *chunk;
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)
952 if(strcmp(chunk->name, "GLUL") == 0) die("Glulx stories are not supported (try git or glulxe)");
954 die("unknown story type: %s", chunk->name);
957 if(chunk->offset > LONG_MAX) die("zcode offset too large");
959 memory_size = chunk->size;
960 story.offset = chunk->offset;
962 zterp_blorb_free(blorb);
966 long size = zterp_io_filesize(story.io);
968 if(size == -1) die("unable to determine file size");
969 if(size > UINT32_MAX) die("file too large");
976 if(glk_gestalt(gestalt_Sound, 0))
978 /* 5 for the worst case of needing to add .blb to the end plus the
981 char *blorb_file = malloc(strlen(game_file) + 5);
982 if(blorb_file != NULL)
987 strcpy(blorb_file, game_file);
988 p = strrchr(blorb_file, '.');
989 if(p != NULL) *p = 0;
990 strcat(blorb_file, ".blb");
992 file = glkunix_stream_open_pathname(blorb_file, 0, 0);
995 giblorb_set_resource_map(file);
996 sound_channel = glk_schannel_create(0);
1004 if(memory_size < 64) die("story file too small");
1005 if(memory_size > SIZE_MAX - 22) die("story file too large");
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.
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.
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);
1030 /* If header transcript/fixed bits have been set, either by the
1031 * story or by the user, this will activate them.
1033 user_store_word(0x10, WORD(0x10));
1038 glk_put_string(story_id);
1048 process_instructions();