Changeset - 174e3c560923
backends/CMakeLists.txt
Show inline comments
 
@@ -4,7 +4,9 @@ if (PROTOBUF_FOUND)
 
	endif()
 

	
 
	if (IRC_FOUND)
 
		ADD_SUBDIRECTORY(libircclient-qt)
 
	endif()
 

	
 
	ADD_SUBDIRECTORY(frotz)
 

	
 
endif()
backends/frotz/CMakeLists.txt
Show inline comments
 
new file 100644
 
cmake_minimum_required(VERSION 2.6)
 
FILE(GLOB SRC *.c *.cpp)
 
 
ADD_EXECUTABLE(spectrum_frotz_backend ${SRC})
 
 
target_link_libraries(spectrum_frotz_backend transport pthread)
 
 
INSTALL(TARGETS spectrum_frotz_backend RUNTIME DESTINATION bin)
 
backends/frotz/buffer.c
Show inline comments
 
new file 100644
 
/* buffer.c - Text buffering and word wrapping
 
 *	Copyright (c) 1995-1997 Stefan Jokisch
 
 *
 
 * This file is part of Frotz.
 
 *
 
 * Frotz is free software; you can redistribute it and/or modify
 
 * it under the terms of the GNU General Public License as published by
 
 * the Free Software Foundation; either version 2 of the License, or
 
 * (at your option) any later version.
 
 *
 
 * Frotz is distributed in the hope that it will be useful,
 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
 * GNU General Public License for more details.
 
 *
 
 * You should have received a copy of the GNU General Public License
 
 * along with this program; if not, write to the Free Software
 
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
 
 */
 

	
 
#include "frotz.h"
 

	
 
extern void stream_char (zchar);
 
extern void stream_word (const zchar *);
 
extern void stream_new_line (void);
 

	
 
static zchar buffer[TEXT_BUFFER_SIZE];
 
static int bufpos = 0;
 

	
 
static zchar prev_c = 0;
 

	
 
/*
 
 * flush_buffer
 
 *
 
 * Copy the contents of the text buffer to the output streams.
 
 *
 
 */
 

	
 
void flush_buffer (void)
 
{
 
    static int locked = FALSE;
 

	
 
    /* Make sure we stop when flush_buffer is called from flush_buffer.
 
       Note that this is difficult to avoid as we might print a newline
 
       during flush_buffer, which might cause a newline interrupt, that
 
       might execute any arbitrary opcode, which might flush the buffer. */
 

	
 
    if (locked || bufpos == 0)
 
	return;
 

	
 
    /* Send the buffer to the output streams */
 

	
 
    buffer[bufpos] = 0;
 

	
 

	
 
    locked = TRUE;
 

	
 
    stream_word (buffer); 
 

	
 
#ifdef SPEECH_OUTPUT
 
    os_speech_output(buffer);
 
#endif
 

	
 
    locked = FALSE;
 

	
 
    /* Reset the buffer */
 

	
 
    bufpos = 0;
 
    prev_c = 0;
 

	
 
}/* flush_buffer */
 

	
 
/*
 
 * print_char
 
 *
 
 * High level output function.
 
 *
 
 */
 

	
 
void print_char (zchar c)
 
{
 
    static int flag = FALSE;
 

	
 
    if (message || ostream_memory || enable_buffering) {
 

	
 
	if (!flag) {
 

	
 
	    /* Characters 0 and ZC_RETURN are special cases */
 

	
 
	    if (c == ZC_RETURN)
 
		{ new_line (); return; }
 
	    if (c == 0)
 
		return;
 

	
 
	    /* Flush the buffer before a whitespace or after a hyphen */
 

	
 
	    if (c == ' ' || c == ZC_INDENT || c == ZC_GAP || (prev_c == '-' && c != '-'))
 

	
 

	
 
		flush_buffer ();
 

	
 
	    /* Set the flag if this is part one of a style or font change */
 

	
 
	    if (c == ZC_NEW_FONT || c == ZC_NEW_STYLE)
 
		flag = TRUE;
 

	
 
	    /* Remember the current character code */
 

	
 
	    prev_c = c;
 

	
 
	} else flag = FALSE;
 

	
 
	/* Insert the character into the buffer */
 

	
 
	buffer[bufpos++] = c;
 

	
 
	if (bufpos == TEXT_BUFFER_SIZE)
 
	    runtime_error (ERR_TEXT_BUF_OVF);
 

	
 
    } else stream_char (c);
 

	
 
}/* print_char */
 

	
 
/*
 
 * new_line
 
 *
 
 * High level newline function.
 
 *
 
 */
 

	
 
void new_line (void)
 
{
 

	
 
    flush_buffer (); stream_new_line ();
 

	
 
}/* new_line */
 

	
 

	
 
/*
 
 * init_buffer
 
 *
 
 * Initialize buffer variables.
 
 *
 
 */
 

	
 
void init_buffer(void)
 
{
 
    memset(buffer, 0, sizeof (zchar) * TEXT_BUFFER_SIZE);
 
    bufpos = 0;
 
    prev_c = 0;
 
}
 

	
backends/frotz/dumb_frotz.h
Show inline comments
 
new file 100644
 
/* dumb-frotz.h
 
 * $Id: dumb-frotz.h,v 1.1.1.1 2002/03/26 22:38:34 feedle Exp $
 
 * Frotz os functions for a standard C library and a dumb terminal.
 
 * Now you can finally play Zork Zero on your Teletype.
 
 *
 
 * Copyright 1997, 1998 Alembic Petrofsky <alembic@petrofsky.berkeley.ca.us>.
 
 * Any use permitted provided this notice stays intact.
 
 */
 
#include "frotz.h"
 
#include <stdio.h>
 
#include <stdlib.h>
 
#include <errno.h>
 
#include <string.h>
 
#include <ctype.h>
 
#include <time.h>
 

	
 
/* from ../common/setup.h */
 
extern f_setup_t f_setup;
 
extern void spectrum_get_line(char *s);
 

	
 
/* From input.c.  */
 
int is_terminator (zchar);
 

	
 
/* dumb-input.c */
 
int dumb_handle_setting(const char *setting, int show_cursor, int startup);
 
void dumb_init_input(void);
 

	
 
/* dumb-output.c */
 
void dumb_init_output(void);
 
int dumb_output_handle_setting(const char *setting, int show_cursor,
 
				int startup);
 
void dumb_show_screen(int show_cursor);
 
void dumb_show_prompt(int show_cursor, char line_type);
 
void dumb_dump_screen(void);
 
void dumb_display_user_input(char *);
 
void dumb_discard_old_input(int num_chars);
 
void dumb_elide_more_prompt(void);
 
void dumb_set_picture_cell(int row, int col, char c);
 

	
 
/* dumb-pic.c */
 
void dumb_init_pictures(char *graphics_filename);
backends/frotz/dumb_init.c
Show inline comments
 
new file 100644
 
/* dumb-init.c
 
 * $Id: dumb-init.c,v 1.1.1.1 2002/03/26 22:38:34 feedle Exp $
 
 *
 
 * Copyright 1997,1998 Alva Petrofsky <alva@petrofsky.berkeley.ca.us>.
 
 * Any use permitted provided this notice stays intact.
 
 */
 

	
 
#include "dumb_frotz.h"
 

	
 
#define VERSION 1.0
 

	
 
f_setup_t f_setup;
 

	
 
#define INFORMATION "\
 
An interpreter for all Infocom and other Z-Machine games.\n\
 
Complies with standard 1.0 of Graham Nelson's specification.\n\
 
\n\
 
Syntax: dfrotz [options] story-file\n\
 
  -a   watch attribute setting \t -Q   use old-style save format\n\
 
  -A   watch attribute testing \t -R xxx  do runtime setting \\xxx\n\
 
  -h # screen height           \t    before starting (can be used repeatedly)\n\
 
  -i   ignore fatal errors     \t -s # random number seed value\n\
 
  -I # interpreter number      \t -S # transscript width\n\
 
  -o   watch object movement   \t -t   set Tandy bit\n\
 
  -O   watch object locating   \t -u # slots for multiple undo\n\
 
  -p   plain ASCII output only \t -w # screen width\n\
 
  -P   alter piracy opcode     \t -x   expand abbreviations g/x/z"
 

	
 
/*
 
static char usage[] = "\
 
\n\
 
FROTZ V2.32 - interpreter for all Infocom games. Complies with standard\n\
 
1.0 of Graham Nelson's specification. Written by Stefan Jokisch in 1995-7.\n\
 
\n\
 
DUMB-FROTZ V2.32R1 - port for all platforms.  Somewhat complies with standard\n\
 
9899 of ISO's specification.  Written by Alembic Petrofsky in 1997-8.\n\
 
\n\
 
Syntax: frotz [options] story-file [graphics-file]\n\
 
\n\
 
  -a      watch attribute setting\n\
 
  -A      watch attribute testing\n\
 
  -h #    screen height\n\
 
  -i      ignore runtime errors\n\
 
  -I #    interpreter number to report to game\n\
 
  -o      watch object movement\n\
 
  -O      watch object locating\n\
 
  -p      alter piracy opcode\n\
 
  -P      transliterate latin1 to plain ASCII\n\
 
  -R xxx  do runtime setting \\xxx before starting\n\
 
            (this option can be used multiple times)\n\
 
  -s #    random number seed value\n\
 
  -S #    transscript width\n\
 
  -t      set Tandy bit\n\
 
  -u #    slots for multiple undo\n\
 
  -w #    screen width\n\
 
  -x      expand abbreviations g/x/z\n\
 
\n\
 
While running, enter \"\\help\" to list the runtime escape sequences.\n\
 
";
 
*/
 

	
 

	
 
/* A unix-like getopt, but with the names changed to avoid any problems.  */
 
static int zoptind = 1;
 
static int zoptopt = 0;
 
static char *zoptarg = NULL;
 
static int zgetopt (int argc, char *argv[], const char *options)
 
{
 
    static pos = 1;
 
    const char *p;
 
    if (zoptind >= argc || argv[zoptind][0] != '-' || argv[zoptind][1] == 0)
 
	return EOF;
 
    zoptopt = argv[zoptind][pos++];
 
    zoptarg = NULL;
 
    if (argv[zoptind][pos] == 0)
 
	{ pos = 1; zoptind++; }
 
    p = strchr (options, zoptopt);
 
    if (zoptopt == ':' || p == NULL) {
 
	fputs ("illegal option -- ", stderr);
 
	goto error;
 
    } else if (p[1] == ':')
 
	if (zoptind >= argc) {
 
	    fputs ("option requires an argument -- ", stderr);
 
	    goto error;
 
	} else {
 
	    zoptarg = argv[zoptind];
 
	    if (pos != 1)
 
		zoptarg += pos;
 
	    pos = 1; zoptind++;
 
	}
 
    return zoptopt;
 
error:
 
    fputc (zoptopt, stderr);
 
    fputc ('\n', stderr);
 
    return '?';
 
}/* zgetopt */
 

	
 
static int user_screen_width = 75;
 
static int user_screen_height = 100;
 
static int user_interpreter_number = -1;
 
static int user_random_seed = -1;
 
static int user_tandy_bit = 0;
 
static char *graphics_filename = NULL;
 
static int plain_ascii = FALSE;
 

	
 
void os_process_arguments(int argc, char *argv[]) 
 
{
 
	return;
 
    int c;
 

	
 
    /* Parse the options */
 
    do {
 
	c = zgetopt(argc, argv, "aAh:iI:oOpPQs:R:S:tu:w:xZ:");
 
	switch(c) {
 
	  case 'a': f_setup.attribute_assignment = 1; break;
 
	  case 'A': f_setup.attribute_testing = 1; break;
 
	case 'h': user_screen_height = atoi(zoptarg); break;
 
	  case 'i': f_setup.ignore_errors = 1; break;
 
	  case 'I': f_setup.interpreter_number = atoi(zoptarg); break;
 
	  case 'o': f_setup.object_movement = 1; break;
 
	  case 'O': f_setup.object_locating = 1; break;
 
	  case 'P': f_setup.piracy = 1; break;
 
	case 'p': plain_ascii = 1; break;
 
	  case 'Q': f_setup.save_quetzal = 0; break;
 
	case 'R': dumb_handle_setting(zoptarg, FALSE, TRUE); break;
 
	case 's': user_random_seed = atoi(zoptarg); break;
 
	  case 'S': f_setup.script_cols = atoi(zoptarg); break;
 
	case 't': user_tandy_bit = 1; break;
 
	  case 'u': f_setup.undo_slots = atoi(zoptarg); break;
 
	case 'w': user_screen_width = atoi(zoptarg); break;
 
	  case 'x': f_setup.expand_abbreviations = 1; break;
 
	  case 'Z': f_setup.err_report_mode = atoi(zoptarg);
 
		if ((f_setup.err_report_mode < ERR_REPORT_NEVER) ||
 
		(f_setup.err_report_mode > ERR_REPORT_FATAL))
 
			f_setup.err_report_mode = ERR_DEFAULT_REPORT_MODE;
 
		break;
 
	}
 
    } while (c != EOF);
 

	
 
    if (((argc - zoptind) != 1) && ((argc - zoptind) != 2)) {
 
	printf("FROTZ V%s\tdumb interface.\n", VERSION);
 
	puts(INFORMATION);
 
	printf("\t-Z # error checking mode (default = %d)\n"
 
	    "\t     %d = don't report errors   %d = report first error\n"
 
	    "\t     %d = report all errors     %d = exit after any error\n\n",
 
	    ERR_DEFAULT_REPORT_MODE, ERR_REPORT_NEVER,
 
	    ERR_REPORT_ONCE, ERR_REPORT_ALWAYS, ERR_REPORT_FATAL);
 
	exit(1);
 
    }
 
/*
 
    if (((argc - zoptind) != 1) && ((argc - zoptind) != 2)) {
 
	puts(usage);
 
	exit(1);
 
    }
 
*/
 
    story_name = argv[zoptind++];
 
    if (zoptind < argc)
 
      graphics_filename = argv[zoptind++];
 

	
 
}
 

	
 
void os_init_screen(void)
 
{
 
  if (h_version == V3 && user_tandy_bit)
 
      h_config |= CONFIG_TANDY;
 

	
 
  if (h_version >= V5 && f_setup.undo_slots == 0)
 
      h_flags &= ~UNDO_FLAG;
 

	
 
  h_screen_rows = user_screen_height;
 
  h_screen_cols = user_screen_width;
 

	
 
  if (user_interpreter_number > 0)
 
    h_interpreter_number = user_interpreter_number;
 
  else {
 
    /* Use ms-dos for v6 (because that's what most people have the
 
     * graphics files for), but don't use it for v5 (or Beyond Zork
 
     * will try to use funky characters).  */
 
    h_interpreter_number = h_version == 6 ? INTERP_MSDOS : INTERP_DEC_20;
 
  }
 
  h_interpreter_version = 'F';
 

	
 
  dumb_init_input();
 
  dumb_init_output();
 
  dumb_init_pictures(graphics_filename);
 
}
 

	
 
int os_random_seed (void)
 
{
 
  if (user_random_seed == -1)
 
    /* Use the epoch as seed value */
 
    return (time(0) & 0x7fff);
 
  else return user_random_seed;
 
}
 

	
 
void os_restart_game (int stage) {}
 

	
 
void os_fatal (const char *s)
 
{
 
  fprintf(stderr, "\nFatal error: %s\n", s);
 
//   exit(1);
 
}
 

	
 
FILE *os_path_open(const char *name, const char *mode)
 
{
 
	FILE *fp;
 
	char buf[FILENAME_MAX + 1];
 
	char *p;
 

	
 
	/* Let's see if the file is in the currect directory */
 
	/* or if the user gave us a full path. */
 
	if ((fp = fopen(name, mode))) {
 
		return fp;
 
	}
 
	/* Sorry, but dumb frotz is too dumb to care about searching paths. */
 
	return NULL;
 
}
 

	
 
void os_init_setup(void)
 
{
 
	f_setup.attribute_assignment = 0;
 
	f_setup.attribute_testing = 0;
 
	f_setup.context_lines = 0;
 
	f_setup.object_locating = 0;
 
	f_setup.object_movement = 0;
 
	f_setup.left_margin = 0;
 
	f_setup.right_margin = 0;
 
	f_setup.ignore_errors = 0;
 
	f_setup.piracy = 0;
 
	f_setup.undo_slots = MAX_UNDO_SLOTS;
 
	f_setup.expand_abbreviations = 0;
 
	f_setup.script_cols = 80;
 
	f_setup.save_quetzal = 1;
 
	f_setup.sound = 1;
 
	f_setup.err_report_mode = ERR_DEFAULT_REPORT_MODE;
 

	
 
}
backends/frotz/dumb_input.c
Show inline comments
 
new file 100644
 
/* dumb-input.c
 
 * $Id: dumb-input.c,v 1.1.1.1 2002/03/26 22:38:34 feedle Exp $
 
 * Copyright 1997,1998 Alpine Petrofsky <alpine@petrofsky.berkeley.ca.us>.
 
 * Any use permitted provided this notice stays intact.
 
 */
 

	
 
#include "dumb_frotz.h"
 
f_setup_t f_setup;
 

	
 
static char runtime_usage[] =
 
  "DUMB-FROTZ runtime help:\n"
 
  "  General Commands:\n"
 
  "    \\help    Show this message.\n"
 
  "    \\set     Show the current values of runtime settings.\n"
 
  "    \\s       Show the current contents of the whole screen.\n"
 
  "    \\d       Discard the part of the input before the cursor.\n"
 
  "    \\wN      Advance clock N/10 seconds, possibly causing the current\n"
 
  "                and subsequent inputs to timeout.\n"
 
  "    \\w       Advance clock by the amount of real time since this input\n"
 
  "                started (times the current speed factor).\n"
 
  "    \\t       Advance clock just enough to timeout the current input\n"
 
  "  Reverse-Video Display Method Settings:\n"
 
  "    \\rn   none    \\rc   CAPS    \\rd   doublestrike    \\ru   underline\n"
 
  "    \\rbC  show rv blanks as char C (orthogonal to above modes)\n"
 
  "  Output Compression Settings:\n"
 
  "    \\cn      none: show whole screen before every input.\n"
 
  "    \\cm      max: show only lines that have new nonblank characters.\n"
 
  "    \\cs      spans: like max, but emit a blank line between each span of\n"
 
  "                screen lines shown.\n"
 
  "    \\chN     Hide top N lines (orthogonal to above modes).\n"
 
  "  Misc Settings:\n"
 
  "    \\sfX     Set speed factor to X.  (0 = never timeout automatically).\n"
 
  "    \\mp      Toggle use of MORE prompts\n"
 
  "    \\ln      Toggle display of line numbers.\n"
 
  "    \\lt      Toggle display of the line type identification chars.\n"
 
  "    \\vb      Toggle visual bell.\n"
 
  "    \\pb      Toggle display of picture outline boxes.\n"
 
  "    (Toggle commands can be followed by a 1 or 0 to set value ON or OFF.)\n"
 
  "  Character Escapes:\n"
 
  "    \\\\  backslash    \\#  backspace    \\[  escape    \\_  return\n"
 
  "    \\< \\> \\^ \\.  cursor motion        \\1 ..\\0  f1..f10\n"
 
  "    \\D ..\\X   Standard Frotz hotkeys.  Use \\H (help) to see the list.\n"
 
  "  Line Type Identification Characters:\n"
 
  "    Input lines:\n"
 
  "      untimed  timed\n"
 
  "      >        T      A regular line-oriented input\n"
 
  "      )        t      A single-character input\n"
 
  "      }        D      A line input with some input before the cursor.\n"
 
  "                         (Use \\d to discard it.)\n"
 
  "    Output lines:\n"
 
  "      ]     Output line that contains the cursor.\n"
 
  "      .     A blank line emitted as part of span compression.\n"
 
  "            (blank) Any other output line.\n"
 
;
 

	
 
static float speed = 1;
 
static int do_more_prompts = FALSE;
 

	
 
enum input_type {
 
  INPUT_CHAR,
 
  INPUT_LINE,
 
  INPUT_LINE_CONTINUED,
 
};
 

	
 
/* get a character.  Exit with no fuss on EOF.  */
 
static int xgetchar(void)
 
{
 
  int c = getchar();
 
  if (c == EOF) {
 
    if (feof(stdin)) {
 
      fprintf(stderr, "\nEOT\n");
 
      exit(0);
 
    }
 
    os_fatal(strerror(errno));
 
  }
 
  return c;
 
}
 

	
 
/* Read one line, including the newline, into s.  Safely avoids buffer
 
 * overruns (but that's kind of pointless because there are several
 
 * other places where I'm not so careful).  */
 
static void getline_(char *s)
 
{
 
 spectrum_get_line(s);
 
}
 

	
 
/* Translate in place all the escape characters in s.  */
 
static void translate_special_chars(char *s)
 
{
 
  char *src = s, *dest = s;
 
  while (*src)
 
    switch(*src++) {
 
    default: *dest++ = src[-1]; break;
 
    case '\n': *dest++ = ZC_RETURN; break;
 
    case '\\':
 
      switch (*src++) {
 
      case '\n': *dest++ = ZC_RETURN; break;
 
      case '\\': *dest++ = '\\'; break;
 
      case '?': *dest++ = ZC_BACKSPACE; break;
 
      case '[': *dest++ = ZC_ESCAPE; break;
 
      case '_': *dest++ = ZC_RETURN; break;
 
      case '^': *dest++ = ZC_ARROW_UP; break;
 
      case '.': *dest++ = ZC_ARROW_DOWN; break;
 
      case '<': *dest++ = ZC_ARROW_LEFT; break;
 
      case '>': *dest++ = ZC_ARROW_RIGHT; break;
 
      case 'R': *dest++ = ZC_HKEY_RECORD; break;
 
      case 'P': *dest++ = ZC_HKEY_PLAYBACK; break;
 
      case 'S': *dest++ = ZC_HKEY_SEED; break;
 
      case 'U': *dest++ = ZC_HKEY_UNDO; break;
 
      case 'N': *dest++ = ZC_HKEY_RESTART; break;
 
      case 'X': *dest++ = ZC_HKEY_QUIT; break;
 
      case 'D': *dest++ = ZC_HKEY_DEBUG; break;
 
      case 'H': *dest++ = ZC_HKEY_HELP; break;
 
      case '1': case '2': case '3': case '4':
 
      case '5': case '6': case '7': case '8': case '9':
 
	*dest++ = ZC_FKEY_MIN + src[-1] - '0' - 1; break;
 
      case '0': *dest++ = ZC_FKEY_MIN + 9; break;
 
      default:
 
	fprintf(stderr, "DUMB-FROTZ: unknown escape char: %c\n", src[-1]);
 
	fprintf(stderr, "Enter \\help to see the list\n");
 
      }
 
    }
 
  *dest = '\0';
 
}
 

	
 

	
 
/* The time in tenths of seconds that the user is ahead of z time.  */
 
static int time_ahead = 0;
 

	
 
/* Called from os_read_key and os_read_line if they have input from
 
 * a previous call to dumb_read_line.
 
 * Returns TRUE if we should timeout rather than use the read-ahead.
 
 * (because the user is further ahead than the timeout).  */
 
static int check_timeout(int timeout)
 
{
 
  if ((timeout == 0) || (timeout > time_ahead))
 
    time_ahead = 0;
 
  else
 
    time_ahead -= timeout;
 
  return time_ahead != 0;
 
}
 

	
 
/* If val is '0' or '1', set *var accordingly, otherwise toggle it.  */
 
static void toggle(int *var, char val)
 
{
 
  *var = val == '1' || (val != '0' && !*var);
 
}
 

	
 
/* Handle input-related user settings and call dumb_output_handle_setting.  */
 
int dumb_handle_setting(const char *setting, int show_cursor, int startup)
 
{
 
  if (!strncmp(setting, "sf", 2)) {
 
    speed = atof(&setting[2]);
 
    printf("Speed Factor %g\n", speed);
 
  } else if (!strncmp(setting, "mp", 2)) {
 
    toggle(&do_more_prompts, setting[2]);
 
    printf("More prompts %s\n", do_more_prompts ? "ON" : "OFF");
 
  } else {
 
    if (!strcmp(setting, "set")) {
 
      printf("Speed Factor %g\n", speed);
 
      printf("More Prompts %s\n", do_more_prompts ? "ON" : "OFF");
 
    }
 
    return dumb_output_handle_setting(setting, show_cursor, startup);
 
  }
 
  return TRUE;
 
}
 

	
 
/* Read a line, processing commands (lines that start with a backslash
 
 * (that isn't the start of a special character)), and write the
 
 * first non-command to s.
 
 * Return true if timed-out.  */
 
static int dumb_read_line(char *s, char *prompt, int show_cursor,
 
			   int timeout, enum input_type type,
 
			   zchar *continued_line_chars)
 
{
 
  time_t start_time;
 
  
 
  if (timeout) {
 
    if (time_ahead >= timeout) {
 
      time_ahead -= timeout;
 
      return TRUE;
 
    }
 
    timeout -= time_ahead;
 
    start_time = time(0);
 
  }
 
  time_ahead = 0;
 

	
 
  for (;;) {
 
    char *command;
 
    if (prompt)
 
      fputs(prompt, stdout);
 
    else
 
      dumb_show_prompt(show_cursor, (timeout ? "tTD" : ")>}")[type]);
 
    getline_(s);
 
    if ((s[0] != '\\') || ((s[1] != '\0') && !islower(s[1]))) {
 
      /* Is not a command line.  */
 
      translate_special_chars(s);
 
      if (timeout) {
 
	int elapsed = (time(0) - start_time) * 10 * speed;
 
	if (elapsed > timeout) {
 
	  time_ahead = elapsed - timeout;
 
	  return TRUE;
 
	}
 
      }
 
      return FALSE;
 
    }
 
    /* Commands.  */
 

	
 
    /* Remove the \ and the terminating newline.  */
 
    command = s + 1;
 
    command[strlen(command) - 1] = '\0';
 
    
 
    if (!strcmp(command, "t")) {
 
      if (timeout) {
 
	time_ahead = 0;
 
	s[0] = '\0';
 
	return TRUE;
 
      }
 
    } else if (*command == 'w') {
 
      if (timeout) {
 
	int elapsed = atoi(&command[1]);
 
	time_t now = time(0);
 
	if (elapsed == 0)
 
	  elapsed = (now - start_time) * 10 * speed;
 
	if (elapsed >= timeout) {
 
	  time_ahead = elapsed - timeout;
 
	  s[0] = '\0';
 
	  return TRUE;
 
	}
 
	timeout -= elapsed;
 
	start_time = now;
 
      }
 
    } else if (!strcmp(command, "d")) {
 
      if (type != INPUT_LINE_CONTINUED)
 
	fprintf(stderr, "DUMB-FROTZ: No input to discard\n");
 
      else {
 
	dumb_discard_old_input(strlen(continued_line_chars));
 
	continued_line_chars[0] = '\0';
 
	type = INPUT_LINE;
 
      }
 
    } else if (!strcmp(command, "help")) {
 
      if (!do_more_prompts)
 
	fputs(runtime_usage, stdout);
 
      else {
 
	char *current_page, *next_page;
 
	current_page = next_page = runtime_usage;
 
	for (;;) {
 
	  int i;
 
	  for (i = 0; (i < h_screen_rows - 2) && *next_page; i++)
 
	    next_page = strchr(next_page, '\n') + 1;
 
	  printf("%.*s", next_page - current_page, current_page);
 
	  current_page = next_page;
 
	  if (!*current_page)
 
	    break;
 
	  printf("HELP: Type <return> for more, or q <return> to stop: ");
 
	  getline_(s);
 
	  if (!strcmp(s, "q\n"))
 
	    break;
 
	}
 
      }
 
    } else if (!strcmp(command, "s")) {
 
	dumb_dump_screen();
 
    } else if (!dumb_handle_setting(command, show_cursor, FALSE)) {
 
      fprintf(stderr, "DUMB-FROTZ: unknown command: %s\n", s);
 
      fprintf(stderr, "Enter \\help to see the list of commands\n");
 
    }
 
  }
 
}
 

	
 
/* Read a line that is not part of z-machine input (more prompts and
 
 * filename requests).  */
 
static void dumb_read_misc_line(char *s, char *prompt)
 
{
 
  dumb_read_line(s, prompt, 0, 0, 0, 0);
 
  /* Remove terminating newline */
 
  s[strlen(s) - 1] = '\0';
 
}
 

	
 
/* For allowing the user to input in a single line keys to be returned
 
 * for several consecutive calls to read_char, with no screen update
 
 * in between.  Useful for traversing menus.  */
 
static char read_key_buffer[INPUT_BUFFER_SIZE];
 

	
 
/* Similar.  Useful for using function key abbreviations.  */
 
static char read_line_buffer[INPUT_BUFFER_SIZE];
 

	
 
zchar os_read_key (int timeout, int show_cursor)
 
{
 
  char c;
 
  int timed_out;
 

	
 
  /* Discard any keys read for line input.  */
 
  read_line_buffer[0] = '\0';
 

	
 
  if (read_key_buffer[0] == '\0') {
 
    timed_out = dumb_read_line(read_key_buffer, NULL, show_cursor, timeout,
 
			       INPUT_CHAR, NULL);
 
    /* An empty input line is reported as a single CR.
 
     * If there's anything else in the line, we report only the line's
 
     * contents and not the terminating CR.  */
 
    if (strlen(read_key_buffer) > 1)
 
      read_key_buffer[strlen(read_key_buffer) - 1] = '\0';
 
  } else
 
    timed_out = check_timeout(timeout);
 
    
 
  if (timed_out)
 
    return ZC_TIME_OUT;
 

	
 
  c = read_key_buffer[0];
 
  memmove(read_key_buffer, read_key_buffer + 1, strlen(read_key_buffer));
 

	
 
  /* TODO: error messages for invalid special chars.  */
 

	
 
  return c;
 
}
 

	
 
zchar os_read_line (int max, zchar *buf, int timeout, int width, int continued)
 
{
 
  char *p;
 
  int terminator;
 
  static int timed_out_last_time;
 
  int timed_out;
 

	
 
  /* Discard any keys read for single key input.  */
 
  read_key_buffer[0] = '\0';
 

	
 
  /* After timing out, discard any further input unless we're continuing.  */
 
  if (timed_out_last_time && !continued)
 
    read_line_buffer[0] = '\0';
 

	
 
  if (read_line_buffer[0] == '\0')
 
    timed_out = dumb_read_line(read_line_buffer, NULL, TRUE, timeout,
 
			       buf[0] ? INPUT_LINE_CONTINUED : INPUT_LINE,
 
			       buf);
 
  else
 
    timed_out = check_timeout(timeout);
 
  
 
  if (timed_out) {
 
    timed_out_last_time = TRUE;
 
    return ZC_TIME_OUT;
 
  }
 
    
 
  /* find the terminating character.  */
 
  for (p = read_line_buffer;; p++) {
 
    if (is_terminator(*p)) {
 
      terminator = *p;
 
      *p++ = '\0';
 
      break;
 
    }
 
  }
 

	
 
  /* TODO: Truncate to width and max.  */
 

	
 
  /* copy to screen */
 
  dumb_display_user_input(read_line_buffer);
 

	
 
  /* copy to the buffer and save the rest for next time.  */
 
  strcat(buf, read_line_buffer);
 
  p = read_line_buffer + strlen(read_line_buffer) + 1;
 
  memmove(read_line_buffer, p, strlen(p) + 1);
 
    
 
  /* If there was just a newline after the terminating character,
 
   * don't save it.  */
 
  if ((read_line_buffer[0] == '\r') && (read_line_buffer[1] == '\0'))
 
    read_line_buffer[0] = '\0';
 

	
 
  timed_out_last_time = FALSE;
 
  return terminator;
 
}
 

	
 
int os_read_file_name (char *file_name, const char *default_name, int flag)
 
{
 
	return FALSE;
 
  char buf[INPUT_BUFFER_SIZE], prompt[INPUT_BUFFER_SIZE];
 
  FILE *fp;
 

	
 
  sprintf(prompt, "Please enter a filename [%s]: ", default_name);
 
  dumb_read_misc_line(buf, prompt);
 
  if (strlen(buf) > MAX_FILE_NAME) {
 
    printf("Filename too long\n");
 
    return FALSE;
 
  }
 

	
 
  strcpy (file_name, buf[0] ? buf : default_name);
 

	
 
  /* Warn if overwriting a file.  */
 
  if ((flag == FILE_SAVE || flag == FILE_SAVE_AUX || flag == FILE_RECORD)
 
      && ((fp = fopen(file_name, "rb")) != NULL)) {
 
    fclose (fp);
 
    dumb_read_misc_line(buf, "Overwrite existing file? ");
 
    return(tolower(buf[0]) == 'y');
 
  }
 
  return TRUE;
 
}
 

	
 
void os_more_prompt (void)
 
{
 
  if (do_more_prompts) {
 
    char buf[INPUT_BUFFER_SIZE];
 
    dumb_read_misc_line(buf, "***MORE***");
 
  } else
 
    dumb_elide_more_prompt();
 
}
 

	
 
void dumb_init_input(void)
 
{
 
  if ((h_version >= V4) && (speed != 0))
 
    h_config |= CONFIG_TIMEDINPUT;
 

	
 
  if (h_version >= V5)
 
    h_flags &= ~(MOUSE_FLAG | MENU_FLAG);
 
}
 

	
 
zword os_read_mouse(void)
 
{
 
	/* NOT IMPLEMENTED */
 
}
backends/frotz/dumb_output.c
Show inline comments
 
new file 100644
 
/* dumb-output.c
 
 * $Id: dumb-output.c,v 1.2 2002/03/26 22:52:31 feedle Exp $
 
 *
 
 * Copyright 1997,1998 Alfresco Petrofsky <alfresco@petrofsky.berkeley.edu>.
 
 * Any use permitted provided this notice stays intact.
 
 */
 

	
 
#include "dumb_frotz.h"
 

	
 
f_setup_t f_setup;
 

	
 
static int show_line_numbers = FALSE;
 
static int show_line_types = -1;
 
static int show_pictures = TRUE;
 
static int visual_bell = TRUE;
 
static int plain_ascii = FALSE;
 

	
 
static char latin1_to_ascii[] =
 
  "    !   c   L   >o< Y   |   S   ''  C   a   <<  not -   R   _   "
 
  "^0  +/- ^2  ^3  '   my  P   .   ,   ^1  o   >>  1/4 1/2 3/4 ?   "
 
  "A   A   A   A   Ae  A   AE  C   E   E   E   E   I   I   I   I   "
 
  "Th  N   O   O   O   O   Oe  *   O   U   U   U   Ue  Y   Th  ss  "
 
  "a   a   a   a   ae  a   ae  c   e   e   e   e   i   i   i   i   "
 
  "th  n   o   o   o   o   oe  :   o   u   u   u   ue  y   th  y   "
 
;
 

	
 
/* h_screen_rows * h_screen_cols */
 
static int screen_cells;
 

	
 
/* The in-memory state of the screen.  */
 
/* Each cell contains a style in the upper byte and a char in the lower. */
 
typedef unsigned short cell;
 
static cell *screen_data;
 

	
 
static cell make_cell(int style, char c) {return (style << 8) | (0xff & c);}
 
static char cell_char(cell c) {return c & 0xff;}
 
static int cell_style(cell c) {return c >> 8;}
 

	
 

	
 
/* A cell's style is REVERSE_STYLE, normal (0), or PICTURE_STYLE.
 
 * PICTURE_STYLE means the character is part of an ascii image outline
 
 * box.  (This just buys us the ability to turn box display on and off
 
 * with immediate effect.  No, not very useful, but I wanted to give
 
 * the rv bit some company in that huge byte I allocated for it.)  */
 
#define PICTURE_STYLE 16
 

	
 
static int current_style = 0;
 

	
 
/* Which cells have changed (1 byte per cell).  */
 
static char *screen_changes;
 

	
 
static int cursor_row = 0, cursor_col = 0;
 

	
 
/* Compression styles.  */
 
static enum {
 
  COMPRESSION_NONE, COMPRESSION_SPANS, COMPRESSION_MAX,
 
} compression_mode = COMPRESSION_SPANS;
 
static char *compression_names[] = {"NONE", "SPANS", "MAX"};
 
static int hide_lines = 0;
 

	
 
/* Reverse-video display styles.  */
 
static enum {
 
  RV_NONE, RV_DOUBLESTRIKE, RV_UNDERLINE, RV_CAPS,
 
} rv_mode = RV_NONE;
 
static char *rv_names[] = {"NONE", "DOUBLESTRIKE", "UNDERLINE", "CAPS"};
 
static char rv_blank_char = ' ';
 

	
 
static cell *dumb_row(int r) {return screen_data + r * h_screen_cols;}
 

	
 
static char *dumb_changes_row(int r)
 
{
 
  return screen_changes + r * h_screen_cols;
 
}
 

	
 
static char array[15000];
 
static char *array_ptr = &array;
 

	
 
char *frotz_get_array() {
 
	return &array;
 
}
 

	
 
void frotz_reset_array() {
 
	array_ptr = &array;
 
	*array_ptr = 0;
 
}
 

	
 
static void myputchar(char c) {
 
	*array_ptr = c;
 
	array_ptr++;
 
	*array_ptr = 0;
 
}
 

	
 
int os_char_width (zchar z)
 
{
 
  if (plain_ascii && z >= ZC_LATIN1_MIN && z <= ZC_LATIN1_MAX) {
 
    char *p = latin1_to_ascii + 4 * (z - ZC_LATIN1_MIN);
 
    return strchr(p, ' ') - p;
 
  }
 
  return 1;
 
}
 

	
 
int os_string_width (const zchar *s)
 
{
 
  int width = 0;
 
  zchar c;
 

	
 
  while ((c = *s++) != 0)
 
    if (c == ZC_NEW_STYLE || c == ZC_NEW_FONT)
 
      s++;
 
    else
 
      width += os_char_width(c);
 

	
 
  return width;
 
}
 

	
 
void os_set_cursor(int row, int col)
 
{
 
  cursor_row = row - 1; cursor_col = col - 1;
 
  if (cursor_row >= h_screen_rows)
 
    cursor_row = h_screen_rows - 1;
 
}
 

	
 
/* Set a cell and update screen_changes.  */
 
static void dumb_set_cell(int row, int col, cell c)
 
{
 
  dumb_changes_row(row)[col] = (c != dumb_row(row)[col]);
 
  dumb_row(row)[col] = c;
 
}
 

	
 
void dumb_set_picture_cell(int row, int col, char c)
 
{
 
  dumb_set_cell(row, col, make_cell(PICTURE_STYLE, c));
 
}
 

	
 
/* Copy a cell and copy its changedness state.
 
 * This is used for scrolling.  */
 
static void dumb_copy_cell(int dest_row, int dest_col,
 
			   int src_row, int src_col)
 
{
 
  dumb_row(dest_row)[dest_col] = dumb_row(src_row)[src_col];
 
  dumb_changes_row(dest_row)[dest_col] = dumb_changes_row(src_row)[src_col];
 
}
 

	
 
void os_set_text_style(int x)
 
{
 
  current_style = x & REVERSE_STYLE;
 
}
 

	
 
/* put a character in the cell at the cursor and advance the cursor.  */
 
static void dumb_display_char(char c)
 
{
 
  dumb_set_cell(cursor_row, cursor_col, make_cell(current_style, c));
 
  if (++cursor_col == h_screen_cols)
 
    if (cursor_row == h_screen_rows - 1)
 
      cursor_col--;
 
    else {
 
      cursor_row++;
 
      cursor_col = 0;
 
    }
 
}
 

	
 
void dumb_display_user_input(char *s)
 
{
 
  /* copy to screen without marking it as a change.  */
 
  while (*s)
 
    dumb_row(cursor_row)[cursor_col++] = make_cell(0, *s++);
 
}
 

	
 
void dumb_discard_old_input(int num_chars)
 
{
 
  /* Weird discard stuff.  Grep spec for 'pain in my butt'.  */
 
  /* The old characters should be on the screen just before the cursor.
 
   * Erase them.  */
 
  cursor_col -= num_chars;
 
  if (cursor_col < 0)
 
    cursor_col = 0;
 
  os_erase_area(cursor_row + 1, cursor_col + 1,
 
		cursor_row + 1, cursor_col + num_chars);
 
}
 

	
 
void os_display_char (zchar c)
 
{
 
  if (c >= ZC_LATIN1_MIN && c <= ZC_LATIN1_MAX) {
 
    if (plain_ascii) {
 
      char *ptr = latin1_to_ascii + 4 * (c - ZC_LATIN1_MIN);
 
      do
 
	dumb_display_char(*ptr++);
 
      while (*ptr != ' ');
 
    } else
 
      dumb_display_char(c);
 
  } else if (c >= 32 && c <= 126) {
 
    dumb_display_char(c);
 
  } else if (c == ZC_GAP) {
 
    dumb_display_char(' '); dumb_display_char(' ');
 
  } else if (c == ZC_INDENT) {
 
    dumb_display_char(' '); dumb_display_char(' '); dumb_display_char(' ');
 
  }
 
  return;
 
}
 

	
 

	
 
/* Haxor your boxor? */
 
void os_display_string (const zchar *s)
 
{
 
  zchar c;
 

	
 
  while ((c = *s++) != 0)
 
    if (c == ZC_NEW_FONT)
 
      s++;
 
    else if (c == ZC_NEW_STYLE)
 
      os_set_text_style(*s++);
 
    else {
 
     os_display_char (c); 
 
     }
 
}
 

	
 
void os_erase_area (int top, int left, int bottom, int right)
 
{
 
  int row, col;
 
  top--; left--; bottom--; right--;
 
  for (row = top; row <= bottom; row++)
 
    for (col = left; col <= right; col++)
 
      dumb_set_cell(row, col, make_cell(current_style, ' '));
 
}
 

	
 
void os_scroll_area (int top, int left, int bottom, int right, int units)
 
{
 
  int row, col;
 
  top--; left--; bottom--; right--;
 
  if (units > 0) {
 
    for (row = top; row <= bottom - units; row++)
 
      for (col = left; col <= right; col++)
 
	dumb_copy_cell(row, col, row + units, col);
 
    os_erase_area(bottom - units + 2, left + 1, bottom + 1, right + 1);
 
  } else if (units < 0) {
 
    for (row = bottom; row >= top - units; row--)
 
      for (col = left; col <= right; col++)
 
	dumb_copy_cell(row, col, row + units, col);
 
    os_erase_area(top + 1, left + 1, top - units, right + 1);
 
  }
 
}
 

	
 
int os_font_data(int font, int *height, int *width)
 
{
 
    if (font == TEXT_FONT) {
 
      *height = 1; *width = 1; return 1;
 
    }
 
    return 0;
 
}
 

	
 
void os_set_colour (int x, int y) {}
 
void os_set_font (int x) {}
 

	
 
/* Print a cell to stdout.  */
 
static void show_cell(cell cel)
 
{
 
  char c = cell_char(cel);
 
  switch (cell_style(cel)) {
 
  case 0:
 
    myputchar(c);
 
    break;
 
  case PICTURE_STYLE:
 
    myputchar(show_pictures ? c : ' ');
 
    break;
 
  case REVERSE_STYLE:
 
    if (c == ' ')
 
      myputchar(rv_blank_char);
 
    else
 
      switch (rv_mode) {
 
      case RV_NONE: myputchar(c); break;
 
      case RV_CAPS: myputchar(toupper(c)); break;
 
      case RV_UNDERLINE: myputchar('_'); myputchar('\b'); myputchar(c); break;
 
      case RV_DOUBLESTRIKE: myputchar(c); myputchar('\b'); myputchar(c); break;
 
      }
 
    break;
 
  }
 
}
 

	
 
static int will_print_blank(cell c)
 
{
 
  return (((cell_style(c) == PICTURE_STYLE) && !show_pictures)
 
	  || ((cell_char(c) == ' ')
 
	      && ((cell_style(c) != REVERSE_STYLE) || (rv_blank_char == ' '))));
 
}
 

	
 
static void show_line_prefix(int row, char c)
 
{
 
  if (show_line_numbers)
 
    printf((row == -1) ? ".." : "%02d", (row + 1) % 100);
 
  if (show_line_types)
 
    myputchar(c);
 
  /* Add a separator char (unless there's nothing to separate).  */
 
  if (show_line_numbers || show_line_types)
 
    myputchar(' ');
 
}
 

	
 
/* Print a row to stdout.  */
 
static void show_row(int r)
 
{
 
  if (r == -1) {
 
    show_line_prefix(-1, '.');
 
  } else {
 
    int c, last;
 
    show_line_prefix(r, (r == cursor_row) ? ']' : ' ');
 
    /* Don't print spaces at end of line.  */
 
    /* (Saves bandwidth and printhead wear.)  */
 
    /* TODO: compress spaces to tabs.  */
 
    for (last = h_screen_cols - 1; last >= 0; last--)
 
      if (!will_print_blank(dumb_row(r)[last]))
 
	  break;
 
    for (c = 0; c <= last; c++)
 
      show_cell(dumb_row(r)[c]);
 
  }
 
  myputchar('\n');
 
}
 

	
 
/* Print the part of the cursor row before the cursor.  */
 
void dumb_show_prompt(int show_cursor, char line_type)
 
{
 
return;
 
  int i;
 
  show_line_prefix(show_cursor ? cursor_row : -1, line_type);
 
  if (show_cursor)
 
    for (i = 0; i < cursor_col; i++)
 
      show_cell(dumb_row(cursor_row)[i]);
 
}
 

	
 
static void mark_all_unchanged(void)
 
{
 
  memset(screen_changes, 0, screen_cells);
 
}
 

	
 
/* Check if a cell is a blank or will display as one.
 
 * (Used to help decide if contents are worth printing.)  */
 
static int is_blank(cell c)
 
{
 
  return ((cell_char(c) == ' ')
 
	  || ((cell_style(c) == PICTURE_STYLE) && !show_pictures));
 
}
 

	
 
/* Show the current screen contents, or what's changed since the last
 
 * call.
 
 *
 
 * If compressing, and show_cursor is true, and the cursor is past the
 
 * last nonblank character on the last line that would be shown, then
 
 * don't show that line (because it will be redundant with the prompt
 
 * line just below it).  */
 
void dumb_show_screen(int show_cursor)
 
{
 
  int r, c, first, last;
 
  char changed_rows[0x100]; 
 
  printf("show_screen\n");
 
  /* Easy case */
 
  if (compression_mode == COMPRESSION_NONE) {
 
    for (r = hide_lines; r < h_screen_rows; r++)
 
      show_row(r);
 
    mark_all_unchanged();
 
    return;
 
  }
 

	
 
  /* Check which rows changed, and where the first and last change is.  */
 
  first = last = -1;
 
  memset(changed_rows, 0, h_screen_rows);
 
  for (r = hide_lines; r < h_screen_rows; r++) { 
 
    for (c = 0; c < h_screen_cols; c++)
 
      if (dumb_changes_row(r)[c] && !is_blank(dumb_row(r)[c]))
 
	break;
 
    changed_rows[r] = (c != h_screen_cols);
 
    if (changed_rows[r]) {
 
      first = (first != -1) ? first : r;
 
      last = r;
 
    }
 
  }
 

	
 
  if (first == -1)
 
    return;
 

	
 
  /* The show_cursor rule described above */
 
  if (show_cursor && (cursor_row == last)) {
 
    for (c = cursor_col; c < h_screen_cols; c++)
 
      if (!is_blank(dumb_row(last)[c]))
 
	break;
 
    if (c == h_screen_cols)
 
      last--;
 
  }
 

	
 
  /* Display the appropriate rows.  */
 
  if (compression_mode == COMPRESSION_MAX) {
 
    for (r = first; r <= last; r++) 
 
      if (changed_rows[r])
 
	show_row(r);
 
  } else {
 
    /* COMPRESSION_SPANS */
 
    for (r = first; r <= last; r++) {
 
      if (changed_rows[r] || changed_rows[r + 1])
 
	show_row(r);
 
      else {
 
	while (!changed_rows[r + 1])
 
	  r++;
 
	show_row(-1);
 
      }
 
    }
 
    if (show_cursor && (cursor_row > last + 1))
 
      show_row((cursor_row == last + 2) ? (last + 1) : -1);
 
  }
 

	
 
  mark_all_unchanged();
 
}
 

	
 
/* Unconditionally show whole screen.  For \s user command.  */
 
void dumb_dump_screen(void)
 
{
 
  int r;
 
  for (r = 0; r < h_screen_height; r++)
 
    show_row(r);
 
}
 

	
 
/* Called when it's time for a more prompt but user has them turned off.  */
 
void dumb_elide_more_prompt(void)
 
{
 
  dumb_show_screen(FALSE);
 
  if (compression_mode == COMPRESSION_SPANS && hide_lines == 0) {
 
    show_row(-1);
 
  }
 
}
 

	
 
void os_reset_screen(void)
 
{
 
  dumb_show_screen(FALSE);
 
}
 

	
 
void os_beep (int volume)
 
{
 
  if (visual_bell)
 
    printf("[%s-PITCHED BEEP]\n", (volume == 1) ? "HIGH" : "LOW");
 
  else
 
    myputchar('\a'); /* so much for dumb.  */
 
}
 

	
 

	
 
/* To make the common code happy */
 

	
 
void os_prepare_sample (int a) {}
 
void os_finish_with_sample (int a) {}
 
void os_start_sample (int a, int b, int c, zword d) {}
 
void os_stop_sample (int a) {}
 

	
 

	
 
/* if val is '0' or '1', set *var accordingly, else toggle it.  */
 
static void toggle(int *var, char val)
 
{
 
  *var = val == '1' || (val != '0' && !*var);
 
}
 

	
 
int dumb_output_handle_setting(const char *setting, int show_cursor,
 
				int startup)
 
{
 
  char *p;
 
  int i;
 

	
 
  if (!strncmp(setting, "pb", 2)) {
 
    toggle(&show_pictures, setting[2]);
 
    printf("Picture outlines display %s\n", show_pictures ? "ON" : "OFF");
 
    if (startup)
 
      return TRUE;
 
    for (i = 0; i < screen_cells; i++)
 
      screen_changes[i] = (cell_style(screen_data[i]) == PICTURE_STYLE);
 
    dumb_show_screen(show_cursor);
 

	
 
  } else if (!strncmp(setting, "vb", 2)) {
 
    toggle(&visual_bell, setting[2]);
 
    printf("Visual bell %s\n", visual_bell ? "ON" : "OFF");
 
    os_beep(1); os_beep(2);
 

	
 
  } else if (!strncmp(setting, "ln", 2)) {
 
    toggle(&show_line_numbers, setting[2]);
 
    printf("Line numbering %s\n", show_line_numbers ? "ON" : "OFF");
 

	
 
  } else if (!strncmp(setting, "lt", 2)) {
 
    toggle(&show_line_types, setting[2]);
 
    printf("Line-type display %s\n", show_line_types ? "ON" : "OFF");
 

	
 
  } else if (*setting == 'c') {
 
    switch (setting[1]) {
 
    case 'm': compression_mode = COMPRESSION_MAX; break;
 
    case 's': compression_mode = COMPRESSION_SPANS; break;
 
    case 'n': compression_mode = COMPRESSION_NONE; break;
 
    case 'h': hide_lines = atoi(&setting[2]); break;
 
    default: return FALSE;
 
    }
 
    printf("Compression mode %s, hiding top %d lines\n",
 
	   compression_names[compression_mode], hide_lines);
 

	
 
  } else if (*setting == 'r') {
 
    switch (setting[1]) {
 
    case 'n': rv_mode = RV_NONE; break;
 
    case 'o': rv_mode = RV_DOUBLESTRIKE; break;
 
    case 'u': rv_mode = RV_UNDERLINE; break;
 
    case 'c': rv_mode = RV_CAPS; break;
 
    case 'b': rv_blank_char = setting[2] ? setting[2] : ' '; break;
 
    default: return FALSE;
 
    }
 
    printf("Reverse-video mode %s, blanks reverse to '%c': ",
 
	   rv_names[rv_mode], rv_blank_char);
 
    for (p = "sample reverse text"; *p; p++)
 
      show_cell(make_cell(REVERSE_STYLE, *p));
 
    myputchar('\n');
 
    for (i = 0; i < screen_cells; i++)
 
      screen_changes[i] = (cell_style(screen_data[i]) == REVERSE_STYLE);
 
    dumb_show_screen(show_cursor);
 

	
 
  } else if (!strcmp(setting, "set")) {
 
    printf("Compression Mode %s, hiding top %d lines\n",
 
	   compression_names[compression_mode], hide_lines);
 
    printf("Picture Boxes display %s\n", show_pictures ? "ON" : "OFF");
 
    printf("Visual Bell %s\n", visual_bell ? "ON" : "OFF");
 
    os_beep(1); os_beep(2);
 
    printf("Line Numbering %s\n", show_line_numbers ? "ON" : "OFF");
 
    printf("Line-Type display %s\n", show_line_types ? "ON" : "OFF");
 
    printf("Reverse-Video mode %s, Blanks reverse to '%c': ",
 
	   rv_names[rv_mode], rv_blank_char);
 
    for (p = "sample reverse text"; *p; p++)
 
      show_cell(make_cell(REVERSE_STYLE, *p));
 
    myputchar('\n');
 
  } else
 
    return FALSE;
 
  return TRUE;
 
}
 

	
 
void dumb_init_output(void)
 
{
 
  if (h_version == V3) {
 
    h_config |= CONFIG_SPLITSCREEN;
 
    h_flags &= ~OLD_SOUND_FLAG;
 
  }
 

	
 
  if (h_version >= V5) {
 
    h_flags &= ~SOUND_FLAG;
 
  }
 

	
 
  h_screen_height = h_screen_rows;
 
  h_screen_width = h_screen_cols;
 
  screen_cells = h_screen_rows * h_screen_cols;
 

	
 
  h_font_width = 1; h_font_height = 1;
 

	
 
  if (show_line_types == -1)
 
    show_line_types = h_version > 3;
 

	
 
  screen_data = malloc(screen_cells * sizeof(cell));
 
  screen_changes = malloc(screen_cells);
 
  os_erase_area(1, 1, h_screen_rows, h_screen_cols);
 
  memset(screen_changes, 0, screen_cells);
 
}
backends/frotz/dumb_pic.c
Show inline comments
 
new file 100644
 
/* dumb-pic.c
 
 * $Id: dumb-pic.c,v 1.1.1.1 2002/03/26 22:38:34 feedle Exp $
 
 *
 
 * Copyright 1997,1998 Alcibiades Petrofsky
 
 * <alcibiades@petrofsky.berkeley.ca.us>.
 
 * Any use permitted provided this notice stays intact.
 
 */
 
#include "dumb_frotz.h"
 

	
 
f_setup_t f_setup;
 

	
 
#define PIC_FILE_HEADER_FLAGS 1
 
#define PIC_FILE_HEADER_NUM_IMAGES 4
 
#define PIC_FILE_HEADER_ENTRY_SIZE 8
 
#define PIC_FILE_HEADER_VERSION 14
 

	
 
#define PIC_HEADER_NUMBER 0
 
#define PIC_HEADER_WIDTH 2
 
#define PIC_HEADER_HEIGHT 4
 

	
 
static struct {
 
  int z_num;
 
  int width;
 
  int height;
 
  int orig_width;
 
  int orig_height;
 
} *pict_info;
 
static int num_pictures = 0;
 

	
 
static unsigned char lookupb(unsigned char *p, int n) {return p[n];}
 
static unsigned short lookupw(unsigned char *p, int n)
 
{
 
  return (p[n + 1] << 8) | p[n];
 
}
 

	
 
void dumb_init_pictures (char *filename)
 
{
 
  FILE *file = NULL;
 
  int success = FALSE;
 
  unsigned char gheader[16];
 
  unsigned char *raw_info = NULL;
 
  int i, entry_size, flags;
 
  float x_scaler, y_scaler;
 

	
 
  do {
 
    if ((h_version != V6)
 
	|| !filename
 
	|| ((file = fopen (filename, "rb")) == NULL)
 
	|| (fread(&gheader, sizeof (gheader), 1, file) != 1))
 
      break;
 

	
 
    num_pictures = lookupw(gheader, PIC_FILE_HEADER_NUM_IMAGES);
 
    entry_size = lookupb(gheader, PIC_FILE_HEADER_ENTRY_SIZE);
 
    flags = lookupb(gheader, PIC_FILE_HEADER_FLAGS);
 

	
 
    raw_info = malloc(num_pictures * entry_size);
 

	
 
    if (fread(raw_info, num_pictures * entry_size, 1, file) != 1)
 
      break;
 

	
 
    pict_info = malloc((num_pictures + 1) * sizeof(*pict_info));
 
    pict_info[0].z_num = 0;
 
    pict_info[0].height = num_pictures;
 
    pict_info[0].width = lookupw(gheader, PIC_FILE_HEADER_VERSION);
 

	
 
    y_scaler = h_screen_rows / 200.0;
 
    x_scaler = h_screen_cols / ((flags & 0x08) ? 640.0 : 320.0);
 

	
 
    /* Copy and scale.  */
 
    for (i = 1; i <= num_pictures; i++) {
 
      unsigned char *p = raw_info + entry_size * (i - 1);
 
      pict_info[i].z_num = lookupw(p, PIC_HEADER_NUMBER);
 
      pict_info[i].orig_height = lookupw(p, PIC_HEADER_HEIGHT);
 
      pict_info[i].orig_width = lookupw(p, PIC_HEADER_WIDTH);
 
      pict_info[i].height = pict_info[i].orig_height * y_scaler + .5;
 
      pict_info[i].width = pict_info[i].orig_width * x_scaler + .5;
 
    }
 
    success = TRUE;
 
  } while (0);
 
  if (file)
 
    fclose(file);
 
  if (raw_info)
 
    free(raw_info);
 
  if (success)
 
    h_config |= CONFIG_PICTURES;
 
  else
 
    {
 
      h_flags &= ~GRAPHICS_FLAG;
 
      if (filename)
 
	fprintf(stderr, "Warning: could not read graphics file %s\n", filename);
 
    }
 
}
 

	
 
/* Convert a Z picture number to an index into pict_info.  */
 
static int z_num_to_index(int n)
 
{
 
  int i;
 
  for (i = 0; i <= num_pictures; i++)
 
    if (pict_info[i].z_num == n)
 
      return i;
 
  return -1;
 
}
 

	
 
int os_picture_data(int num, int *height, int *width)
 
{
 
  int index;
 

	
 
  *height = 0;
 
  *width = 0;
 

	
 
  if (!pict_info)
 
    return FALSE;
 

	
 
  if ((index = z_num_to_index(num)) == -1)
 
    return FALSE;
 

	
 
  *height = pict_info[index].height;
 
  *width = pict_info[index].width;
 

	
 
  return TRUE;
 
}
 

	
 
void os_draw_picture (int num, int row, int col)
 
{
 
  int width, height, r, c;
 
  if (!os_picture_data(num, &height, &width) || !width || !height)
 
    return;
 
  col--, row--;
 
  /* Draw corners */
 
  dumb_set_picture_cell(row, col, '+');
 
  dumb_set_picture_cell(row, col + width - 1, '+');
 
  dumb_set_picture_cell(row + height - 1, col, '+');
 
  dumb_set_picture_cell(row + height - 1, col + width - 1, '+');
 
  /* sides */
 
  for (c = col + 1; c < col + width - 1; c++) {
 
    dumb_set_picture_cell(row, c, '-');
 
    dumb_set_picture_cell(row + height - 1, c, '-');
 
  }
 
  for (r = row + 1; r < row + height - 1; r++) {
 
    dumb_set_picture_cell(r, col, '|');
 
    dumb_set_picture_cell(r, col + width - 1, '|');
 
  }
 
  /* body, but for last line */
 
  for (r = row + 1; r < row + height - 2; r++)
 
    for (c = col + 1; c < col + width - 1; c++)
 
      dumb_set_picture_cell(r, c, ':');
 
  /* Last line of body, including picture number.  */
 
  if (height >= 3)
 
    for (c = col + width - 2; c > col; c--, (num /= 10))
 
      dumb_set_picture_cell(row + height - 2, c, num ? (num % 10 + '0') : ':');
 
}
 

	
 
int os_peek_colour (void) {return BLACK_COLOUR; }
backends/frotz/err.c
Show inline comments
 
new file 100644
 
/* err.c - Runtime error reporting functions
 
 *	Written by Jim Dunleavy <jim.dunleavy@erha.ie>
 
 *
 
 * This file is part of Frotz.
 
 *
 
 * Frotz is free software; you can redistribute it and/or modify
 
 * it under the terms of the GNU General Public License as published by
 
 * the Free Software Foundation; either version 2 of the License, or
 
 * (at your option) any later version.
 
 *
 
 * Frotz is distributed in the hope that it will be useful,
 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
 * GNU General Public License for more details.
 
 *
 
 * You should have received a copy of the GNU General Public License
 
 * along with this program; if not, write to the Free Software
 
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
 
 */
 

	
 
#include "frotz.h"
 

	
 
/* Define stuff for stricter Z-code error checking, for the generic
 
   Unix/DOS/etc terminal-window interface. Feel free to change the way
 
   player prefs are specified, or replace report_zstrict_error() 
 
   completely if you want to change the way errors are reported. */
 

	
 
/* int err_report_mode = ERR_DEFAULT_REPORT_MODE; */
 

	
 
static int error_count[ERR_NUM_ERRORS];
 

	
 
static char *err_messages[] = {
 
    "Text buffer overflow",
 
    "Store out of dynamic memory",
 
    "Division by zero",
 
    "Illegal object",
 
    "Illegal attribute",
 
    "No such property",
 
    "Stack overflow",
 
    "Call to illegal address",
 
    "Call to non-routine",
 
    "Stack underflow",
 
    "Illegal opcode",
 
    "Bad stack frame",
 
    "Jump to illegal address",
 
    "Can't save while in interrupt",
 
    "Nesting stream #3 too deep",
 
    "Illegal window",
 
    "Illegal window property",
 
    "Print at illegal address",
 
    "@jin called with object 0",
 
    "@get_child called with object 0",
 
    "@get_parent called with object 0",
 
    "@get_sibling called with object 0",
 
    "@get_prop_addr called with object 0",
 
    "@get_prop called with object 0",
 
    "@put_prop called with object 0",
 
    "@clear_attr called with object 0",
 
    "@set_attr called with object 0",
 
    "@test_attr called with object 0",
 
    "@move_object called moving object 0",
 
    "@move_object called moving into object 0",
 
    "@remove_object called with object 0",
 
    "@get_next_prop called with object 0"
 
};
 

	
 
static void print_long (unsigned long value, int base);
 

	
 
/*
 
 * init_err
 
 *
 
 * Initialise error reporting.
 
 *
 
 */
 

	
 
void init_err (void)
 
{
 
    int i;
 

	
 
    /* Initialize the counters. */
 
    
 
    for (i = 0; i < ERR_NUM_ERRORS; i++)
 
        error_count[i] = 0;
 
}
 

	
 
/*
 
 * runtime_error
 
 *
 
 * An error has occurred. Ignore it, pass it to os_fatal or report
 
 * it according to err_report_mode.
 
 *
 
 * errnum : Numeric code for error (1 to ERR_NUM_ERRORS)
 
 *
 
 */
 

	
 
void runtime_error (int errnum)
 
{
 
    int wasfirst;
 
    
 
    if (errnum <= 0 || errnum > ERR_NUM_ERRORS)
 
	return;
 

	
 
    if (f_setup.err_report_mode == ERR_REPORT_FATAL
 
	|| (!f_setup.ignore_errors && errnum <= ERR_MAX_FATAL)) {
 
	flush_buffer ();
 
	os_fatal (err_messages[errnum - 1]);
 
	return;
 
    }
 

	
 
    wasfirst = (error_count[errnum - 1] == 0);
 
    error_count[errnum - 1]++;
 
    
 
    if ((f_setup.err_report_mode == ERR_REPORT_ALWAYS)
 
	|| (f_setup.err_report_mode == ERR_REPORT_ONCE && wasfirst)) {
 
	long pc;
 

	
 
	GET_PC (pc);
 
	print_string ("Warning: ");
 
	print_string (err_messages[errnum - 1]);
 
	print_string (" (PC = ");
 
	print_long (pc, 16);
 
	print_char (')');
 
        
 
	if (f_setup.err_report_mode == ERR_REPORT_ONCE) {
 
	    print_string (" (will ignore further occurrences)");
 
	} else {
 
	    print_string (" (occurence ");
 
	    print_long (error_count[errnum - 1], 10);
 
	    print_char (')');
 
	}
 
	new_line ();
 
    }
 

	
 
} /* report_error */
 

	
 
/*
 
 * print_long
 
 *
 
 * Print an unsigned 32bit number in decimal or hex.
 
 *
 
 */
 

	
 
static void print_long (unsigned long value, int base)
 
{
 
    unsigned long i;
 
    char c;
 

	
 
    for (i = (base == 10 ? 1000000000 : 0x10000000); i != 0; i /= base)
 
	if (value >= i || i == 1) {
 
	    c = (value / i) % base;
 
	    print_char (c + (c <= 9 ? '0' : 'a' - 10));
 
	}
 

	
 
}/* print_long */
backends/frotz/fastmem.c
Show inline comments
 
new file 100644
 
/* fastmem.c - Memory related functions (fast version without virtual memory)
 
 *	Copyright (c) 1995-1997 Stefan Jokisch
 
 *
 
 * This file is part of Frotz.
 
 *
 
 * Frotz is free software; you can redistribute it and/or modify
 
 * it under the terms of the GNU General Public License as published by
 
 * the Free Software Foundation; either version 2 of the License, or
 
 * (at your option) any later version.
 
 *
 
 * Frotz is distributed in the hope that it will be useful,
 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
 * GNU General Public License for more details.
 
 *
 
 * You should have received a copy of the GNU General Public License
 
 * along with this program; if not, write to the Free Software
 
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
 
 */
 

	
 
/*
 
 * New undo mechanism added by Jim Dunleavy <jim.dunleavy@erha.ie>
 
 */
 

	
 
#include <stdio.h>
 
#include <string.h>
 
#include "frotz.h"
 

	
 
#ifdef MSDOS_16BIT
 

	
 
#include <alloc.h>
 

	
 
#define malloc(size)	farmalloc (size)
 
#define realloc(size,p)	farrealloc (size,p)
 
#define free(size)	farfree (size)
 
#define memcpy(d,s,n)	_fmemcpy (d,s,n)
 

	
 
#else
 

	
 
#include <stdlib.h>
 

	
 
#ifndef SEEK_SET
 
#define SEEK_SET 0
 
#define SEEK_CUR 1
 
#define SEEK_END 2
 
#endif
 

	
 
#define far
 

	
 
#endif
 

	
 
extern void seed_random (int);
 
extern void restart_screen (void);
 
extern void refresh_text_style (void);
 
extern void call (zword, int, zword *, int);
 
extern void split_window (zword);
 
extern void script_open (void);
 
extern void script_close (void);
 

	
 
extern FILE *os_path_open (const char *, const char *);
 

	
 
extern zword save_quetzal (FILE *, FILE *);
 
extern zword restore_quetzal (FILE *, FILE *);
 

	
 
extern void erase_window (zword);
 

	
 
extern void (*op0_opcodes[]) (void);
 
extern void (*op1_opcodes[]) (void);
 
extern void (*op2_opcodes[]) (void);
 
extern void (*var_opcodes[]) (void);
 

	
 
char save_name[MAX_FILE_NAME + 1] = DEFAULT_SAVE_NAME;
 
char auxilary_name[MAX_FILE_NAME + 1] = DEFAULT_AUXILARY_NAME;
 

	
 
zbyte far *zmp = NULL;
 
zbyte far *pcp = NULL;
 

	
 
static FILE *story_fp = NULL;
 

	
 
/*
 
 * Data for the undo mechanism.
 
 * This undo mechanism is based on the scheme used in Evin Robertson's
 
 * Nitfol interpreter.
 
 * Undo blocks are stored as differences between states.
 
 */
 

	
 
typedef struct undo_struct undo_t;
 
struct undo_struct {
 
    undo_t *next;
 
    undo_t *prev;
 
    long pc;
 
    long diff_size;
 
    zword frame_count;
 
    zword stack_size;
 
    zword frame_offset;
 
    /* undo diff and stack data follow */
 
};
 

	
 
static undo_t *first_undo = NULL, *last_undo = NULL, *curr_undo = NULL;
 
static zbyte *undo_mem = NULL, *prev_zmp, *undo_diff;
 

	
 
static int undo_count = 0;
 

	
 
/*
 
 * get_header_extension
 
 *
 
 * Read a value from the header extension (former mouse table).
 
 *
 
 */
 

	
 
zword get_header_extension (int entry)
 
{
 
    zword addr;
 
    zword val;
 

	
 
    if (h_extension_table == 0 || entry > hx_table_size)
 
	return 0;
 

	
 
    addr = h_extension_table + 2 * entry;
 
    LOW_WORD (addr, val)
 

	
 
    return val;
 

	
 
}/* get_header_extension */
 

	
 
/*
 
 * set_header_extension
 
 *
 
 * Set an entry in the header extension (former mouse table).
 
 *
 
 */
 

	
 
void set_header_extension (int entry, zword val)
 
{
 
    zword addr;
 

	
 
    if (h_extension_table == 0 || entry > hx_table_size)
 
	return;
 

	
 
    addr = h_extension_table + 2 * entry;
 
    SET_WORD (addr, val)
 

	
 
}/* set_header_extension */
 

	
 
/*
 
 * restart_header
 
 *
 
 * Set all header fields which hold information about the interpreter.
 
 *
 
 */
 

	
 
void restart_header (void)
 
{
 
    zword screen_x_size;
 
    zword screen_y_size;
 
    zbyte font_x_size;
 
    zbyte font_y_size;
 

	
 
    int i;
 

	
 
    SET_BYTE (H_CONFIG, h_config)
 
    SET_WORD (H_FLAGS, h_flags)
 

	
 
    if (h_version >= V4) {
 
	SET_BYTE (H_INTERPRETER_NUMBER, h_interpreter_number)
 
	SET_BYTE (H_INTERPRETER_VERSION, h_interpreter_version)
 
	SET_BYTE (H_SCREEN_ROWS, h_screen_rows)
 
	SET_BYTE (H_SCREEN_COLS, h_screen_cols)
 
    }
 

	
 
    /* It's less trouble to use font size 1x1 for V5 games, especially
 
       because of a bug in the unreleased German version of "Zork 1" */
 

	
 
    if (h_version != V6) {
 
	screen_x_size = (zword) h_screen_cols;
 
	screen_y_size = (zword) h_screen_rows;
 
	font_x_size = 1;
 
	font_y_size = 1;
 
    } else {
 
	screen_x_size = h_screen_width;
 
	screen_y_size = h_screen_height;
 
	font_x_size = h_font_width;
 
	font_y_size = h_font_height;
 
    }
 

	
 
    if (h_version >= V5) {
 
	SET_WORD (H_SCREEN_WIDTH, screen_x_size)
 
	SET_WORD (H_SCREEN_HEIGHT, screen_y_size)
 
	SET_BYTE (H_FONT_HEIGHT, font_y_size)
 
	SET_BYTE (H_FONT_WIDTH, font_x_size)
 
	SET_BYTE (H_DEFAULT_BACKGROUND, h_default_background)
 
	SET_BYTE (H_DEFAULT_FOREGROUND, h_default_foreground)
 
    }
 

	
 
    if (h_version == V6)
 
	for (i = 0; i < 8; i++)
 
	    storeb ((zword) (H_USER_NAME + i), h_user_name[i]);
 

	
 
    SET_BYTE (H_STANDARD_HIGH, h_standard_high)
 
    SET_BYTE (H_STANDARD_LOW, h_standard_low)
 

	
 
}/* restart_header */
 

	
 
/*
 
 * init_memory
 
 *
 
 * Allocate memory and load the story file.
 
 *
 
 */
 

	
 
void init_memory (void)
 
{
 
    long size;
 
    zword addr;
 
    unsigned n;
 
    int i, j;
 

	
 
    static struct {
 
	enum story story_id;
 
	zword release;
 
	zbyte serial[6];
 
    } records[] = {
 
	{       SHERLOCK,  21, "871214" },
 
	{       SHERLOCK,  26, "880127" },
 
	{    BEYOND_ZORK,  47, "870915" },
 
	{    BEYOND_ZORK,  49, "870917" },
 
	{    BEYOND_ZORK,  51, "870923" },
 
	{    BEYOND_ZORK,  57, "871221" },
 
	{      ZORK_ZERO, 296, "881019" },
 
	{      ZORK_ZERO, 366, "890323" },
 
	{      ZORK_ZERO, 383, "890602" },
 
	{      ZORK_ZERO, 393, "890714" },
 
	{         SHOGUN, 292, "890314" },
 
	{         SHOGUN, 295, "890321" },
 
	{         SHOGUN, 311, "890510" },
 
	{         SHOGUN, 322, "890706" },
 
	{         ARTHUR,  54, "890606" },
 
	{         ARTHUR,  63, "890622" },
 
	{         ARTHUR,  74, "890714" },
 
	{        JOURNEY,  26, "890316" },
 
	{        JOURNEY,  30, "890322" },
 
	{        JOURNEY,  77, "890616" },
 
	{        JOURNEY,  83, "890706" },
 
	{ LURKING_HORROR, 203, "870506" },
 
	{ LURKING_HORROR, 219, "870912" },
 
	{ LURKING_HORROR, 221, "870918" },
 
	{        UNKNOWN,   0, "------" }
 
    };
 

	
 
    /* Open story file */
 

	
 
    if ((story_fp = os_path_open(story_name, "rb")) == NULL)
 
	os_fatal ("Cannot open story file");
 

	
 
    /* Allocate memory for story header */
 

	
 
    if ((zmp = (zbyte far *) malloc (64)) == NULL)
 
	os_fatal ("Out of memory");
 

	
 
    /* Load header into memory */
 

	
 
    if (fread (zmp, 1, 64, story_fp) != 64)
 
	os_fatal ("Story file read error");
 

	
 
    /* Copy header fields to global variables */
 

	
 
    LOW_BYTE (H_VERSION, h_version)
 

	
 
    if (h_version < V1 || h_version > V8)
 
	os_fatal ("Unknown Z-code version");
 

	
 
    LOW_BYTE (H_CONFIG, h_config)
 

	
 
    if (h_version == V3 && (h_config & CONFIG_BYTE_SWAPPED))
 
	os_fatal ("Byte swapped story file");
 

	
 
    LOW_WORD (H_RELEASE, h_release)
 
    LOW_WORD (H_RESIDENT_SIZE, h_resident_size)
 
    LOW_WORD (H_START_PC, h_start_pc)
 
    LOW_WORD (H_DICTIONARY, h_dictionary)
 
    LOW_WORD (H_OBJECTS, h_objects)
 
    LOW_WORD (H_GLOBALS, h_globals)
 
    LOW_WORD (H_DYNAMIC_SIZE, h_dynamic_size)
 
    LOW_WORD (H_FLAGS, h_flags)
 

	
 
    for (i = 0, addr = H_SERIAL; i < 6; i++, addr++)
 
	LOW_BYTE (addr, h_serial[i])
 

	
 
    /* Auto-detect buggy story files that need special fixes */
 

	
 
    story_id = UNKNOWN;
 

	
 
    for (i = 0; records[i].story_id != UNKNOWN; i++) {
 

	
 
	if (h_release == records[i].release) {
 

	
 
	    for (j = 0; j < 6; j++)
 
		if (h_serial[j] != records[i].serial[j])
 
		    goto no_match;
 

	
 
	    story_id = records[i].story_id;
 

	
 
	}
 

	
 
    no_match: ; /* null statement */
 

	
 
    }
 

	
 
    LOW_WORD (H_ABBREVIATIONS, h_abbreviations)
 
    LOW_WORD (H_FILE_SIZE, h_file_size)
 

	
 
    /* Calculate story file size in bytes */
 

	
 
    if (h_file_size != 0) {
 

	
 
	story_size = (long) 2 * h_file_size;
 

	
 
	if (h_version >= V4)
 
	    story_size *= 2;
 
	if (h_version >= V6)
 
	    story_size *= 2;
 

	
 
    } else {		/* some old games lack the file size entry */
 

	
 
	fseek (story_fp, 0, SEEK_END);
 
	story_size = ftell (story_fp);
 
	fseek (story_fp, 64, SEEK_SET);
 

	
 
    }
 

	
 
    LOW_WORD (H_CHECKSUM, h_checksum)
 
    LOW_WORD (H_ALPHABET, h_alphabet)
 
    LOW_WORD (H_FUNCTIONS_OFFSET, h_functions_offset)
 
    LOW_WORD (H_STRINGS_OFFSET, h_strings_offset)
 
    LOW_WORD (H_TERMINATING_KEYS, h_terminating_keys)
 
    LOW_WORD (H_EXTENSION_TABLE, h_extension_table)
 

	
 
    /* Zork Zero Macintosh doesn't have the graphics flag set */
 

	
 
    if (story_id == ZORK_ZERO && h_release == 296)
 
	h_flags |= GRAPHICS_FLAG;
 

	
 
    /* Adjust opcode tables */
 

	
 
    if (h_version <= V4) {
 
	op0_opcodes[0x09] = z_pop;
 
	op1_opcodes[0x0f] = z_not;
 
    } else {
 
	op0_opcodes[0x09] = z_catch;
 
	op1_opcodes[0x0f] = z_call_n;
 
    }
 

	
 
    /* Allocate memory for story data */
 

	
 
    if ((zmp = (zbyte far *) realloc (zmp, story_size)) == NULL)
 
	os_fatal ("Out of memory");
 

	
 
    /* Load story file in chunks of 32KB */
 

	
 
    n = 0x8000;
 

	
 
    for (size = 64; size < story_size; size += n) {
 

	
 
	if (story_size - size < 0x8000)
 
	    n = (unsigned) (story_size - size);
 

	
 
	SET_PC (size)
 

	
 
	if (fread (pcp, 1, n, story_fp) != n)
 
	    os_fatal ("Story file read error");
 

	
 
    }
 

	
 
    /* Read header extension table */
 

	
 
    hx_table_size = get_header_extension (HX_TABLE_SIZE);
 
    hx_unicode_table = get_header_extension (HX_UNICODE_TABLE);
 

	
 
}/* init_memory */
 

	
 
/*
 
 * init_undo
 
 *
 
 * Allocate memory for multiple undo. It is important not to occupy
 
 * all the memory available, since the IO interface may need memory
 
 * during the game, e.g. for loading sounds or pictures.
 
 *
 
 */
 

	
 
void init_undo (void)
 
{
 
    void far *reserved;
 

	
 
    reserved = NULL;	/* makes compilers shut up */
 

	
 
    if (reserve_mem != 0) {
 
	if ((reserved = malloc (reserve_mem)) == NULL)
 
	    return;
 
    }
 

	
 
    /* Allocate h_dynamic_size bytes for previous dynamic zmp state
 
       + 1.5 h_dynamic_size for Quetzal diff + 2. */
 
    undo_mem = malloc ((h_dynamic_size * 5) / 2 + 2);
 
    if (undo_mem != NULL) {
 
	prev_zmp = undo_mem;
 
	undo_diff = undo_mem + h_dynamic_size;
 
	memcpy (prev_zmp, zmp, h_dynamic_size);
 
    } else
 
	f_setup.undo_slots = 0;
 

	
 
    if (reserve_mem != 0)
 
	free (reserved);
 

	
 
}/* init_undo */
 

	
 
/*
 
 * free_undo
 
 *
 
 * Free count undo blocks from the beginning of the undo list.
 
 *
 
 */
 

	
 
static void free_undo (int count)
 
{
 
    undo_t *p;
 

	
 
    if (count > undo_count)
 
	count = undo_count;
 
    while (count--) {
 
	p = first_undo;
 
	if (curr_undo == first_undo)
 
	    curr_undo = curr_undo->next;
 
	first_undo = first_undo->next;
 
	free (p);
 
	undo_count--;
 
    }
 
    if (first_undo)
 
	first_undo->prev = NULL;
 
    else
 
	last_undo = NULL;
 
}/* free_undo */
 

	
 
/*
 
 * reset_memory
 
 *
 
 * Close the story file and deallocate memory.
 
 *
 
 */
 

	
 
void reset_memory (void)
 
{
 
    if (story_fp) 
 
	fclose (story_fp);
 
    story_fp = NULL;
 

	
 
    if (undo_mem) {
 
	free_undo (undo_count);
 
	free (undo_mem);
 
    }
 

	
 
    undo_mem = NULL;
 
    undo_count = 0;
 

	
 
    if (zmp)
 
	free (zmp);
 
    zmp = NULL;
 
}/* reset_memory */
 

	
 
/*
 
 * storeb
 
 *
 
 * Write a byte value to the dynamic Z-machine memory.
 
 *
 
 */
 

	
 
void storeb (zword addr, zbyte value)
 
{
 

	
 
    if (addr >= h_dynamic_size)
 
	runtime_error (ERR_STORE_RANGE);
 

	
 
    if (addr == H_FLAGS + 1) {	/* flags register is modified */
 

	
 
	h_flags &= ~(SCRIPTING_FLAG | FIXED_FONT_FLAG);
 
	h_flags |= value & (SCRIPTING_FLAG | FIXED_FONT_FLAG);
 

	
 
	if (value & SCRIPTING_FLAG) {
 
	    if (!ostream_script)
 
		script_open ();
 
	} else {
 
	    if (ostream_script)
 
		script_close ();
 
	}
 

	
 
	refresh_text_style ();
 

	
 
    }
 

	
 
    SET_BYTE (addr, value)
 

	
 
}/* storeb */
 

	
 
/*
 
 * storew
 
 *
 
 * Write a word value to the dynamic Z-machine memory.
 
 *
 
 */
 

	
 
void storew (zword addr, zword value)
 
{
 

	
 
    storeb ((zword) (addr + 0), hi (value));
 
    storeb ((zword) (addr + 1), lo (value));
 

	
 
}/* storew */
 

	
 
/*
 
 * z_restart, re-load dynamic area, clear the stack and set the PC.
 
 *
 
 * 	no zargs used
 
 *
 
 */
 

	
 
void z_restart (void)
 
{
 
    static int first_restart = TRUE;
 

	
 
    flush_buffer ();
 

	
 
    os_restart_game (RESTART_BEGIN);
 

	
 
    seed_random (0);
 

	
 
    if (!first_restart) {
 

	
 
	fseek (story_fp, 0, SEEK_SET);
 

	
 
	if (fread (zmp, 1, h_dynamic_size, story_fp) != h_dynamic_size)
 
	    os_fatal ("Story file read error");
 

	
 
    } else first_restart = FALSE;
 

	
 
    restart_header ();
 
    restart_screen ();
 

	
 
    sp = fp = stack + STACK_SIZE;
 
    frame_count = 0;
 

	
 
    if (h_version != V6) {
 

	
 
	long pc = (long) h_start_pc;
 
	SET_PC (pc)
 

	
 
    } else call (h_start_pc, 0, NULL, 0);
 

	
 
    os_restart_game (RESTART_END);
 

	
 
}/* z_restart */
 

	
 
/*
 
 * get_default_name
 
 *
 
 * Read a default file name from the memory of the Z-machine and
 
 * copy it to a string.
 
 *
 
 */
 

	
 
static void get_default_name (char *default_name, zword addr)
 
{
 

	
 
    if (addr != 0) {
 

	
 
	zbyte len;
 
	int i;
 

	
 
	LOW_BYTE (addr, len)
 
	addr++;
 

	
 
	for (i = 0; i < len; i++) {
 

	
 
	    zbyte c;
 

	
 
	    LOW_BYTE (addr, c)
 
	    addr++;
 

	
 
	    if (c >= 'A' && c <= 'Z')
 
		c += 'a' - 'A';
 

	
 
	    default_name[i] = c;
 

	
 
	}
 

	
 
	default_name[i] = 0;
 

	
 
	if (strchr (default_name, '.') == NULL)
 
	    strcpy (default_name + i, ".AUX");
 

	
 
    } else strcpy (default_name, auxilary_name);
 

	
 
}/* get_default_name */
 

	
 
/*
 
 * z_restore, restore [a part of] a Z-machine state from disk
 
 *
 
 *	zargs[0] = address of area to restore (optional)
 
 *	zargs[1] = number of bytes to restore
 
 *	zargs[2] = address of suggested file name
 
 *
 
 */
 

	
 
void z_restore (void)
 
{
 
    char new_name[MAX_FILE_NAME + 1];
 
    char default_name[MAX_FILE_NAME + 1];
 
    FILE *gfp;
 

	
 
    zword success = 0;
 

	
 
    if (zargc != 0) {
 

	
 
	/* Get the file name */
 

	
 
	get_default_name (default_name, (zargc >= 3) ? zargs[2] : 0);
 

	
 
	if (os_read_file_name (new_name, default_name, FILE_LOAD_AUX) == 0)
 
	    goto finished;
 

	
 
	strcpy (auxilary_name, default_name);
 

	
 
	/* Open auxilary file */
 

	
 
	if ((gfp = fopen (new_name, "rb")) == NULL)
 
	    goto finished;
 

	
 
	/* Load auxilary file */
 

	
 
	success = fread (zmp + zargs[0], 1, zargs[1], gfp);
 

	
 
	/* Close auxilary file */
 

	
 
	fclose (gfp);
 

	
 
    } else {
 

	
 
	long pc;
 
	zword release;
 
	zword addr;
 
	int i;
 

	
 
	/* Get the file name */
 

	
 
	if (os_read_file_name (new_name, save_name, FILE_RESTORE) == 0)
 
	    goto finished;
 

	
 
	strcpy (save_name, new_name);
 

	
 
	/* Open game file */
 

	
 
	if ((gfp = fopen (new_name, "rb")) == NULL)
 
	    goto finished;
 

	
 
	if (f_setup.save_quetzal) {
 
	    success = restore_quetzal (gfp, story_fp);
 

	
 
	} else {
 
	    /* Load game file */
 

	
 
	    release = (unsigned) fgetc (gfp) << 8;
 
	    release |= fgetc (gfp);
 

	
 
	    (void) fgetc (gfp);
 
	    (void) fgetc (gfp);
 

	
 
	    /* Check the release number */
 

	
 
	    if (release == h_release) {
 

	
 
		pc = (long) fgetc (gfp) << 16;
 
		pc |= (unsigned) fgetc (gfp) << 8;
 
		pc |= fgetc (gfp);
 

	
 
		SET_PC (pc)
 

	
 
		sp = stack + (fgetc (gfp) << 8);
 
		sp += fgetc (gfp);
 
		fp = stack + (fgetc (gfp) << 8);
 
		fp += fgetc (gfp);
 

	
 
		for (i = (int) (sp - stack); i < STACK_SIZE; i++) {
 
		    stack[i] = (unsigned) fgetc (gfp) << 8;
 
		    stack[i] |= fgetc (gfp);
 
		}
 

	
 
		fseek (story_fp, 0, SEEK_SET);
 

	
 
		for (addr = 0; addr < h_dynamic_size; addr++) {
 
		    int skip = fgetc (gfp);
 
		    for (i = 0; i < skip; i++)
 
			zmp[addr++] = fgetc (story_fp);
 
		    zmp[addr] = fgetc (gfp);
 
		    (void) fgetc (story_fp);
 
		}
 

	
 
		/* Check for errors */
 

	
 
		if (ferror (gfp) || ferror (story_fp) || addr != h_dynamic_size)
 
		    success = -1;
 
		else
 

	
 
		    /* Success */
 

	
 
		    success = 2;
 

	
 
	    } else print_string ("Invalid save file\n");
 
	}
 

	
 
	if ((short) success >= 0) {
 

	
 
	    /* Close game file */
 

	
 
	    fclose (gfp);
 

	
 
	    if ((short) success > 0) {
 
		zbyte old_screen_rows;
 
		zbyte old_screen_cols;
 

	
 
		/* In V3, reset the upper window. */
 
		if (h_version == V3)
 
		    split_window (0);
 

	
 
		LOW_BYTE (H_SCREEN_ROWS, old_screen_rows);
 
		LOW_BYTE (H_SCREEN_COLS, old_screen_cols);
 

	
 
		/* Reload cached header fields. */
 
		restart_header ();
 

	
 
		/*
 
		 * Since QUETZAL files may be saved on many different machines,
 
		 * the screen sizes may vary a lot. Erasing the status window
 
		 * seems to cover up most of the resulting badness.
 
		 */
 
		if (h_version > V3 && h_version != V6
 
		    && (h_screen_rows != old_screen_rows
 
		    || h_screen_cols != old_screen_cols))
 
		    erase_window (1);
 
	    }
 
	} else
 
	    os_fatal ("Error reading save file");
 
    }
 

	
 
finished:
 

	
 
    if (h_version <= V3)
 
	branch (success);
 
    else
 
	store (success);
 

	
 
}/* z_restore */
 

	
 
/*
 
 * mem_diff
 
 *
 
 * Set diff to a Quetzal-like difference between a and b,
 
 * copying a to b as we go.  It is assumed that diff points to a
 
 * buffer which is large enough to hold the diff.
 
 * mem_size is the number of bytes to compare.
 
 * Returns the number of bytes copied to diff.
 
 *
 
 */
 

	
 
static long mem_diff (zbyte *a, zbyte *b, zword mem_size, zbyte *diff)
 
{
 
    unsigned size = mem_size;
 
    zbyte *p = diff;
 
    unsigned j;
 
    zbyte c;
 

	
 
    for (;;) {
 
	for (j = 0; size > 0 && (c = *a++ ^ *b++) == 0; j++)
 
	    size--;
 
	if (size == 0) break;
 
	size--;
 
	if (j > 0x8000) {
 
	    *p++ = 0;
 
	    *p++ = 0xff;
 
	    *p++ = 0xff;
 
	    j -= 0x8000;
 
	}
 
	if (j > 0) {
 
	    *p++ = 0;
 
	    j--;
 
	    if (j <= 0x7f) {
 
		*p++ = j;
 
	    } else {
 
		*p++ = (j & 0x7f) | 0x80;
 
		*p++ = (j & 0x7f80) >> 7;
 
	    }
 
	}
 
	*p++ = c;
 
	*(b - 1) ^= c;
 
    }
 
    return p - diff;
 
}/* mem_diff */
 

	
 
/*
 
 * mem_undiff
 
 *
 
 * Applies a quetzal-like diff to dest
 
 *
 
 */
 

	
 
static void mem_undiff (zbyte *diff, long diff_length, zbyte *dest)
 
{
 
    zbyte c;
 

	
 
    while (diff_length) {
 
	c = *diff++;
 
	diff_length--;
 
	if (c == 0) {
 
	    unsigned runlen;
 

	
 
	    if (!diff_length)
 
		return;  /* Incomplete run */
 
	    runlen = *diff++;
 
	    diff_length--;
 
	    if (runlen & 0x80) {
 
		if (!diff_length)
 
		    return; /* Incomplete extended run */
 
		c = *diff++;
 
		diff_length--;
 
		runlen = (runlen & 0x7f) | (((unsigned) c) << 7);
 
	    }
 

	
 
	    dest += runlen + 1;
 
	} else {
 
	    *dest++ ^= c;
 
	}
 
    }
 
}/* mem_undiff */
 

	
 
/*
 
 * restore_undo
 
 *
 
 * This function does the dirty work for z_restore_undo.
 
 *
 
 */
 

	
 
int restore_undo (void)
 
{
 

	
 
    if (f_setup.undo_slots == 0)	/* undo feature unavailable */
 

	
 
	return -1;
 

	
 
    if (curr_undo == NULL)		/* no saved game state */
 

	
 
	return 0;
 

	
 
    /* undo possible */
 

	
 
    memcpy (zmp, prev_zmp, h_dynamic_size);
 
    SET_PC (curr_undo->pc)
 
    sp = stack + STACK_SIZE - curr_undo->stack_size;
 
    fp = stack + curr_undo->frame_offset;
 
    frame_count = curr_undo->frame_count;
 
    mem_undiff ((zbyte *) (curr_undo + 1), curr_undo->diff_size, prev_zmp);
 
    memcpy (sp, (zbyte *)(curr_undo + 1) + curr_undo->diff_size,
 
	    curr_undo->stack_size * sizeof (*sp));
 

	
 
    curr_undo = curr_undo->prev;
 

	
 
    restart_header ();
 

	
 
    return 2;
 

	
 
}/* restore_undo */
 

	
 
/*
 
 * z_restore_undo, restore a Z-machine state from memory.
 
 *
 
 *	no zargs used
 
 *
 
 */
 

	
 
void z_restore_undo (void)
 
{
 

	
 
    store ((zword) restore_undo ());
 

	
 
}/* z_restore_undo */
 

	
 
/*
 
 * z_save, save [a part of] the Z-machine state to disk.
 
 *
 
 *	zargs[0] = address of memory area to save (optional)
 
 *	zargs[1] = number of bytes to save
 
 *	zargs[2] = address of suggested file name
 
 *
 
 */
 

	
 
void z_save (void)
 
{
 
    char new_name[MAX_FILE_NAME + 1];
 
    char default_name[MAX_FILE_NAME + 1];
 
    FILE *gfp;
 

	
 
    zword success = 0;
 

	
 
    if (zargc != 0) {
 

	
 
	/* Get the file name */
 

	
 
	get_default_name (default_name, (zargc >= 3) ? zargs[2] : 0);
 

	
 
	if (os_read_file_name (new_name, default_name, FILE_SAVE_AUX) == 0)
 
	    goto finished;
 

	
 
	strcpy (auxilary_name, default_name);
 

	
 
	/* Open auxilary file */
 

	
 
	if ((gfp = fopen (new_name, "wb")) == NULL)
 
	    goto finished;
 

	
 
	/* Write auxilary file */
 

	
 
	success = fwrite (zmp + zargs[0], zargs[1], 1, gfp);
 

	
 
	/* Close auxilary file */
 

	
 
	fclose (gfp);
 

	
 
    } else {
 

	
 
	long pc;
 
	zword addr;
 
	zword nsp, nfp;
 
	int skip;
 
	int i;
 

	
 
	/* Get the file name */
 

	
 
	if (os_read_file_name (new_name, save_name, FILE_SAVE) == 0)
 
	    goto finished;
 

	
 
	strcpy (save_name, new_name);
 

	
 
	/* Open game file */
 

	
 
	if ((gfp = fopen (new_name, "wb")) == NULL)
 
	    goto finished;
 

	
 
	if (f_setup.save_quetzal) {
 
	    success = save_quetzal (gfp, story_fp);
 
	} else {
 
	    /* Write game file */
 

	
 
	    fputc ((int) hi (h_release), gfp);
 
	    fputc ((int) lo (h_release), gfp);
 
	    fputc ((int) hi (h_checksum), gfp);
 
	    fputc ((int) lo (h_checksum), gfp);
 

	
 
	    GET_PC (pc)
 

	
 
	    fputc ((int) (pc >> 16) & 0xff, gfp);
 
	    fputc ((int) (pc >> 8) & 0xff, gfp);
 
	    fputc ((int) (pc) & 0xff, gfp);
 

	
 
	    nsp = (int) (sp - stack);
 
	    nfp = (int) (fp - stack);
 

	
 
	    fputc ((int) hi (nsp), gfp);
 
	    fputc ((int) lo (nsp), gfp);
 
	    fputc ((int) hi (nfp), gfp);
 
	    fputc ((int) lo (nfp), gfp);
 

	
 
	    for (i = nsp; i < STACK_SIZE; i++) {
 
		fputc ((int) hi (stack[i]), gfp);
 
		fputc ((int) lo (stack[i]), gfp);
 
	    }
 

	
 
	    fseek (story_fp, 0, SEEK_SET);
 

	
 
	    for (addr = 0, skip = 0; addr < h_dynamic_size; addr++)
 
		if (zmp[addr] != fgetc (story_fp) || skip == 255 || addr + 1 == h_dynamic_size) {
 
		    fputc (skip, gfp);
 
		    fputc (zmp[addr], gfp);
 
		    skip = 0;
 
		} else skip++;
 
	}
 

	
 
	/* Close game file and check for errors */
 

	
 
	if (fclose (gfp) == EOF || ferror (story_fp)) {
 
	    print_string ("Error writing save file\n");
 
	    goto finished;
 
	}
 

	
 
	/* Success */
 

	
 
	success = 1;
 

	
 
    }
 

	
 
finished:
 

	
 
    if (h_version <= V3)
 
	branch (success);
 
    else
 
	store (success);
 

	
 
}/* z_save */
 

	
 
/*
 
 * save_undo
 
 *
 
 * This function does the dirty work for z_save_undo.
 
 *
 
 */
 

	
 
int save_undo (void)
 
{
 
    long diff_size;
 
    zword stack_size;
 
    undo_t *p;
 

	
 
    if (f_setup.undo_slots == 0)	/* undo feature unavailable */
 
	return -1;
 

	
 
    /* save undo possible */
 

	
 
    while (last_undo != curr_undo) {
 
	p = last_undo;
 
	last_undo = last_undo->prev;
 
	free (p);
 
	undo_count--;
 
    }
 
    if (last_undo)
 
	last_undo->next = NULL;
 
    else
 
	first_undo = NULL;
 

	
 
    if (undo_count == f_setup.undo_slots)
 
	free_undo (1);
 

	
 
    diff_size = mem_diff (zmp, prev_zmp, h_dynamic_size, undo_diff);
 
    stack_size = stack + STACK_SIZE - sp;
 
    do {
 
	p = malloc (sizeof (undo_t) + diff_size + stack_size * sizeof (*sp));
 
	if (p == NULL)
 
	    free_undo (1);
 
    } while (!p && undo_count);
 
    if (p == NULL)
 
	return -1;
 
    GET_PC (p->pc)
 
    p->frame_count = frame_count;
 
    p->diff_size = diff_size;
 
    p->stack_size = stack_size;
 
    p->frame_offset = fp - stack;
 
    memcpy (p + 1, undo_diff, diff_size);
 
    memcpy ((zbyte *)(p + 1) + diff_size, sp, stack_size * sizeof (*sp));
 

	
 
    if (!first_undo) {
 
	p->prev = NULL;
 
	first_undo = p;
 
    } else {
 
	last_undo->next = p;
 
	p->prev = last_undo;
 
    }
 
    p->next = NULL;
 
    curr_undo = last_undo = p;
 
    undo_count++;
 
    return 1;
 

	
 
}/* save_undo */
 

	
 
/*
 
 * z_save_undo, save the current Z-machine state for a future undo.
 
 *
 
 *	no zargs used
 
 *
 
 */
 

	
 
void z_save_undo (void)
 
{
 

	
 
    store ((zword) save_undo ());
 

	
 
}/* z_save_undo */
 

	
 
/*
 
 * z_verify, check the story file integrity.
 
 *
 
 *	no zargs used
 
 *
 
 */
 

	
 
void z_verify (void)
 
{
 
    zword checksum = 0;
 
    long i;
 

	
 
    /* Sum all bytes in story file except header bytes */
 

	
 
    fseek (story_fp, 64, SEEK_SET);
 

	
 
    for (i = 64; i < story_size; i++)
 
	checksum += fgetc (story_fp);
 

	
 
    /* Branch if the checksums are equal */
 

	
 
    branch (checksum == h_checksum);
 

	
 
}/* z_verify */
backends/frotz/files.c
Show inline comments
 
new file 100644
 
/* files.c - Transscription, recording and playback
 
 *	Copyright (c) 1995-1997 Stefan Jokisch
 
 *
 
 * This file is part of Frotz.
 
 *
 
 * Frotz is free software; you can redistribute it and/or modify
 
 * it under the terms of the GNU General Public License as published by
 
 * the Free Software Foundation; either version 2 of the License, or
 
 * (at your option) any later version.
 
 *
 
 * Frotz is distributed in the hope that it will be useful,
 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
 * GNU General Public License for more details.
 
 *
 
 * You should have received a copy of the GNU General Public License
 
 * along with this program; if not, write to the Free Software
 
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
 
 */
 

	
 
#include <stdio.h>
 
#include <string.h>
 
#include "frotz.h"
 

	
 
#ifndef SEEK_SET
 
#define SEEK_SET 0
 
#define SEEK_CUR 1
 
#define SEEK_END 2
 
#endif
 

	
 
extern void set_more_prompts (int);
 

	
 
extern int is_terminator (zchar);
 

	
 
extern int read_yes_or_no (const char *);
 

	
 
char script_name[MAX_FILE_NAME + 1] = DEFAULT_SCRIPT_NAME;
 
char command_name[MAX_FILE_NAME + 1] = DEFAULT_COMMAND_NAME;
 

	
 
#ifdef __MSDOS__
 
extern char latin1_to_ibm[];
 
#endif
 

	
 
static int script_width = 0;
 

	
 
static FILE *sfp = NULL;
 
static FILE *rfp = NULL;
 
static FILE *pfp = NULL;
 

	
 
/*
 
 * script_open
 
 *
 
 * Open the transscript file. 'AMFV' makes this more complicated as it
 
 * turns transscription on/off several times to exclude some text from
 
 * the transscription file. This wasn't a problem for the original V4
 
 * interpreters which always sent transscription to the printer, but it
 
 * means a problem to modern interpreters that offer to open a new file
 
 * every time transscription is turned on. Our solution is to append to
 
 * the old transscription file in V1 to V4, and to ask for a new file
 
 * name in V5+.
 
 *
 
 */
 

	
 
void script_open (void)
 
{
 
    static int script_valid = FALSE;
 

	
 
    char new_name[MAX_FILE_NAME + 1];
 

	
 
    h_flags &= ~SCRIPTING_FLAG;
 

	
 
    if (h_version >= V5 || !script_valid) {
 

	
 
	if (!os_read_file_name (new_name, script_name, FILE_SCRIPT))
 
	    goto done;
 

	
 
	strcpy (script_name, new_name);
 

	
 
    }
 

	
 
    /* Opening in "at" mode doesn't work for script_erase_input... */
 

	
 
    if ((sfp = fopen (script_name, "r+t")) != NULL || (sfp = fopen (script_name, "w+t")) != NULL) {
 

	
 
	fseek (sfp, 0, SEEK_END);
 

	
 
	h_flags |= SCRIPTING_FLAG;
 

	
 
	script_valid = TRUE;
 
	ostream_script = TRUE;
 

	
 
	script_width = 0;
 

	
 
    } else print_string ("Cannot open file\n");
 

	
 
done:
 

	
 
    SET_WORD (H_FLAGS, h_flags)
 

	
 
}/* script_open */
 

	
 
/*
 
 * script_close
 
 *
 
 * Stop transscription.
 
 *
 
 */
 

	
 
void script_close (void)
 
{
 

	
 
    h_flags &= ~SCRIPTING_FLAG;
 
    SET_WORD (H_FLAGS, h_flags)
 

	
 
    fclose (sfp); ostream_script = FALSE;
 

	
 
}/* script_close */
 

	
 
/*
 
 * script_new_line
 
 *
 
 * Write a newline to the transscript file.
 
 *
 
 */
 

	
 
void script_new_line (void)
 
{
 

	
 
    if (fputc ('\n', sfp) == EOF)
 
	script_close ();
 

	
 
    script_width = 0;
 

	
 
}/* script_new_line */
 

	
 
/*
 
 * script_char
 
 *
 
 * Write a single character to the transscript file.
 
 *
 
 */
 

	
 
void script_char (zchar c)
 
{
 

	
 
    if (c == ZC_INDENT && script_width != 0)
 
	c = ' ';
 

	
 
    if (c == ZC_INDENT)
 
	{ script_char (' '); script_char (' '); script_char (' '); return; }
 
    if (c == ZC_GAP)
 
	{ script_char (' '); script_char (' '); return; }
 

	
 
#ifdef __MSDOS__
 
    if (c >= ZC_LATIN1_MIN)
 
	c = latin1_to_ibm[c - ZC_LATIN1_MIN];
 
#endif
 

	
 
    fputc (c, sfp); script_width++;
 

	
 
}/* script_char */
 

	
 
/*
 
 * script_word
 
 *
 
 * Write a string to the transscript file.
 
 *
 
 */
 

	
 
void script_word (const zchar *s)
 
{
 
    int width;
 
    int i;
 

	
 
    if (*s == ZC_INDENT && script_width != 0)
 
	script_char (*s++);
 

	
 
    for (i = 0, width = 0; s[i] != 0; i++)
 

	
 
	if (s[i] == ZC_NEW_STYLE || s[i] == ZC_NEW_FONT)
 
	    i++;
 
	else if (s[i] == ZC_GAP)
 
	    width += 3;
 
	else if (s[i] == ZC_INDENT)
 
	    width += 2;
 
	else
 
	    width += 1;
 

	
 
    if (f_setup.script_cols != 0 && script_width + width > f_setup.script_cols) {
 

	
 
	if (*s == ' ' || *s == ZC_INDENT || *s == ZC_GAP)
 
	    s++;
 

	
 
	script_new_line ();
 

	
 
    }
 

	
 
    for (i = 0; s[i] != 0; i++)
 

	
 
	if (s[i] == ZC_NEW_FONT || s[i] == ZC_NEW_STYLE)
 
	    i++;
 
	else
 
	    script_char (s[i]);
 

	
 
}/* script_word */
 

	
 
/*
 
 * script_write_input
 
 *
 
 * Send an input line to the transscript file.
 
 *
 
 */
 

	
 
void script_write_input (const zchar *buf, zchar key)
 
{
 
    int width;
 
    int i;
 

	
 
    for (i = 0, width = 0; buf[i] != 0; i++)
 
	width++;
 

	
 
    if (f_setup.script_cols != 0 && script_width + width > f_setup.script_cols)
 
	script_new_line ();
 

	
 
    for (i = 0; buf[i] != 0; i++)
 
	script_char (buf[i]);
 

	
 
    if (key == ZC_RETURN)
 
	script_new_line ();
 

	
 
}/* script_write_input */
 

	
 
/*
 
 * script_erase_input
 
 *
 
 * Remove an input line from the transscript file.
 
 *
 
 */
 

	
 
void script_erase_input (const zchar *buf)
 
{
 
    int width;
 
    int i;
 

	
 
    for (i = 0, width = 0; buf[i] != 0; i++)
 
	width++;
 

	
 
    fseek (sfp, -width, SEEK_CUR); script_width -= width;
 

	
 
}/* script_erase_input */
 

	
 
/*
 
 * script_mssg_on
 
 *
 
 * Start sending a "debugging" message to the transscript file.
 
 *
 
 */
 

	
 
void script_mssg_on (void)
 
{
 

	
 
    if (script_width != 0)
 
	script_new_line ();
 

	
 
    script_char (ZC_INDENT);
 

	
 
}/* script_mssg_on */
 

	
 
/*
 
 * script_mssg_off
 
 *
 
 * Stop writing a "debugging" message.
 
 *
 
 */
 

	
 
void script_mssg_off (void)
 
{
 

	
 
    script_new_line ();
 

	
 
}/* script_mssg_off */
 

	
 
/*
 
 * record_open
 
 *
 
 * Open a file to record the player's input.
 
 *
 
 */
 

	
 
void record_open (void)
 
{
 
    char new_name[MAX_FILE_NAME + 1];
 

	
 
    if (os_read_file_name (new_name, command_name, FILE_RECORD)) {
 

	
 
	strcpy (command_name, new_name);
 

	
 
	if ((rfp = fopen (new_name, "wt")) != NULL)
 
	    ostream_record = TRUE;
 
	else
 
	    print_string ("Cannot open file\n");
 

	
 
    }
 

	
 
}/* record_open */
 

	
 
/*
 
 * record_close
 
 *
 
 * Stop recording the player's input.
 
 *
 
 */
 

	
 
void record_close (void)
 
{
 

	
 
    fclose (rfp); ostream_record = FALSE;
 

	
 
}/* record_close */
 

	
 
/*
 
 * record_code
 
 *
 
 * Helper function for record_char.
 
 *
 
 */
 

	
 
static void record_code (int c, int force_encoding)
 
{
 

	
 
    if (force_encoding || c == '[' || c < 0x20 || c > 0x7e) {
 

	
 
	int i;
 

	
 
	fputc ('[', rfp);
 

	
 
	for (i = 10000; i != 0; i /= 10)
 
	    if (c >= i || i == 1)
 
		fputc ('0' + (c / i) % 10, rfp);
 

	
 
	fputc (']', rfp);
 

	
 
    } else fputc (c, rfp);
 

	
 
}/* record_code */
 

	
 
/*
 
 * record_char
 
 *
 
 * Write a character to the command file.
 
 *
 
 */
 

	
 
static void record_char (zchar c)
 
{
 

	
 
    if (c != ZC_RETURN) {
 
	if (c < ZC_HKEY_MIN || c > ZC_HKEY_MAX) {
 
	    record_code (translate_to_zscii (c), FALSE);
 
	    if (c == ZC_SINGLE_CLICK || c == ZC_DOUBLE_CLICK) {
 
		record_code (mouse_x, TRUE);
 
		record_code (mouse_y, TRUE);
 
	    }
 
	} else record_code (1000 + c - ZC_HKEY_MIN, TRUE);
 
    }
 

	
 
}/* record_char */
 

	
 
/*
 
 * record_write_key
 
 *
 
 * Copy a keystroke to the command file.
 
 *
 
 */
 

	
 
void record_write_key (zchar key)
 
{
 

	
 
    record_char (key);
 

	
 
    if (fputc ('\n', rfp) == EOF)
 
	record_close ();
 

	
 
}/* record_write_key */
 

	
 
/*
 
 * record_write_input
 
 *
 
 * Copy a line of input to a command file.
 
 *
 
 */
 

	
 
void record_write_input (const zchar *buf, zchar key)
 
{
 
    zchar c;
 

	
 
    while ((c = *buf++) != 0)
 
	record_char (c);
 

	
 
    record_char (key);
 

	
 
    if (fputc ('\n', rfp) == EOF)
 
	record_close ();
 

	
 
}/* record_write_input */
 

	
 
/*
 
 * replay_open
 
 *
 
 * Open a file of commands for playback.
 
 *
 
 */
 

	
 
void replay_open (void)
 
{
 
    char new_name[MAX_FILE_NAME + 1];
 

	
 
    if (os_read_file_name (new_name, command_name, FILE_PLAYBACK)) {
 

	
 
	strcpy (command_name, new_name);
 

	
 
	if ((pfp = fopen (new_name, "rt")) != NULL) {
 

	
 
	    set_more_prompts (read_yes_or_no ("Do you want MORE prompts"));
 

	
 
	    istream_replay = TRUE;
 

	
 
	} else print_string ("Cannot open file\n");
 

	
 
    }
 

	
 
}/* replay_open */
 

	
 
/*
 
 * replay_close
 
 *
 
 * Stop playback of commands.
 
 *
 
 */
 

	
 
void replay_close (void)
 
{
 

	
 
    set_more_prompts (TRUE);
 

	
 
    fclose (pfp); istream_replay = FALSE;
 

	
 
}/* replay_close */
 

	
 
/*
 
 * replay_code
 
 *
 
 * Helper function for replay_key and replay_line.
 
 *
 
 */
 

	
 
static int replay_code (void)
 
{
 
    int c;
 

	
 
    if ((c = fgetc (pfp)) == '[') {
 

	
 
	int c2;
 

	
 
	c = 0;
 

	
 
	while ((c2 = fgetc (pfp)) != EOF && c2 >= '0' && c2 <= '9')
 
	    c = 10 * c + c2 - '0';
 

	
 
	return (c2 == ']') ? c : EOF;
 

	
 
    } else return c;
 

	
 
}/* replay_code */
 

	
 
/*
 
 * replay_char
 
 *
 
 * Read a character from the command file.
 
 *
 
 */
 

	
 
static zchar replay_char (void)
 
{
 
    int c;
 

	
 
    if ((c = replay_code ()) != EOF) {
 

	
 
	if (c != '\n') {
 

	
 
	    if (c < 1000) {
 

	
 
		c = translate_from_zscii (c);
 

	
 
		if (c == ZC_SINGLE_CLICK || c == ZC_DOUBLE_CLICK) {
 
		    mouse_x = replay_code ();
 
		    mouse_y = replay_code ();
 
		}
 

	
 
		return c;
 

	
 
	    } else return ZC_HKEY_MIN + c - 1000;
 
	}
 

	
 
	ungetc ('\n', pfp);
 

	
 
	return ZC_RETURN;
 

	
 
    } else return ZC_BAD;
 

	
 
}/* replay_char */
 

	
 
/*
 
 * replay_read_key
 
 *
 
 * Read a keystroke from a command file.
 
 *
 
 */
 

	
 
zchar replay_read_key (void)
 
{
 
    zchar key;
 

	
 
    key = replay_char ();
 

	
 
    if (fgetc (pfp) != '\n') {
 

	
 
	replay_close ();
 
	return ZC_BAD;
 

	
 
    } else return key;
 

	
 
}/* replay_read_key */
 

	
 
/*
 
 * replay_read_input
 
 *
 
 * Read a line of input from a command file.
 
 *
 
 */
 

	
 
zchar replay_read_input (zchar *buf)
 
{
 
    zchar c;
 

	
 
    for (;;) {
 

	
 
	c = replay_char ();
 

	
 
	if (c == ZC_BAD || is_terminator (c))
 
	    break;
 

	
 
	*buf++ = c;
 

	
 
    }
 

	
 
    *buf = 0;
 

	
 
    if (fgetc (pfp) != '\n') {
 

	
 
	replay_close ();
 
	return ZC_BAD;
 

	
 
    } else return c;
 

	
 
}/* replay_read_input */
backends/frotz/frotz.h
Show inline comments
 
new file 100644
 
/*
 
 * frotz.h
 
 *
 
 * Global declarations and definitions
 
 *
 
 */
 

	
 
/* Unfortunately, frotz's int definition conflicts with that of curses.
 
   But since no os_* function uses it, it's safe to let the frotz core see
 
   this definition, but have the unix port see the curses version. */
 

	
 
/* #include "../config.h" */
 

	
 
#ifndef __UNIX_PORT_FILE
 
#include <signal.h>
 

	
 
#ifdef __cplusplus
 
extern "C" {
 
#endif
 

	
 
#ifndef TRUE
 
#define TRUE 1
 
#endif
 

	
 
#ifndef FALSE
 
#define FALSE 0
 
#endif
 

	
 
#endif /* __UNIX_PORT_FILE */
 

	
 

	
 
#include <stdio.h>
 

	
 

	
 
typedef unsigned char zbyte;
 
typedef unsigned short zword;
 

	
 
enum story {
 
    BEYOND_ZORK,
 
    SHERLOCK,
 
    ZORK_ZERO,
 
    SHOGUN,
 
    ARTHUR,
 
    JOURNEY,
 
    LURKING_HORROR,
 
    UNKNOWN
 
};
 

	
 
typedef unsigned char zchar;
 

	
 
/*** Constants that may be set at compile time ***/
 

	
 
#ifndef MAX_UNDO_SLOTS
 
#define MAX_UNDO_SLOTS 500
 
#endif
 
#ifndef MAX_FILE_NAME
 
#define MAX_FILE_NAME 80
 
#endif
 
#ifndef TEXT_BUFFER_SIZE
 
#define TEXT_BUFFER_SIZE 200
 
#endif
 
#ifndef INPUT_BUFFER_SIZE
 
#define INPUT_BUFFER_SIZE 200
 
#endif
 
#ifndef STACK_SIZE
 
#define STACK_SIZE 1024
 
#endif
 

	
 
#ifndef DEFAULT_SAVE_NAME
 
#define DEFAULT_SAVE_NAME "story.sav"
 
#endif
 
#ifndef DEFAULT_SCRIPT_NAME
 
#define DEFAULT_SCRIPT_NAME "story.scr"
 
#endif
 
#ifndef DEFAULT_COMMAND_NAME
 
#define DEFAULT_COMMAND_NAME "story.rec"
 
#endif
 
#ifndef DEFAULT_AUXILARY_NAME
 
#define DEFAULT_AUXILARY_NAME "story.aux"
 
#endif
 
#ifndef DEFAULT_SAVE_DIR	/* DG */
 
#define DEFAULT_SAVE_DIR ".frotz-saves"
 
#endif
 

	
 
/*** Story file header format ***/
 

	
 
#define H_VERSION 0
 
#define H_CONFIG 1
 
#define H_RELEASE 2
 
#define H_RESIDENT_SIZE 4
 
#define H_START_PC 6
 
#define H_DICTIONARY 8
 
#define H_OBJECTS 10
 
#define H_GLOBALS 12
 
#define H_DYNAMIC_SIZE 14
 
#define H_FLAGS 16
 
#define H_SERIAL 18
 
#define H_ABBREVIATIONS 24
 
#define H_FILE_SIZE 26
 
#define H_CHECKSUM 28
 
#define H_INTERPRETER_NUMBER 30
 
#define H_INTERPRETER_VERSION 31
 
#define H_SCREEN_ROWS 32
 
#define H_SCREEN_COLS 33
 
#define H_SCREEN_WIDTH 34
 
#define H_SCREEN_HEIGHT 36
 
#define H_FONT_HEIGHT 38 /* this is the font width in V5 */
 
#define H_FONT_WIDTH 39 /* this is the font height in V5 */
 
#define H_FUNCTIONS_OFFSET 40
 
#define H_STRINGS_OFFSET 42
 
#define H_DEFAULT_BACKGROUND 44
 
#define H_DEFAULT_FOREGROUND 45
 
#define H_TERMINATING_KEYS 46
 
#define H_LINE_WIDTH 48
 
#define H_STANDARD_HIGH 50
 
#define H_STANDARD_LOW 51
 
#define H_ALPHABET 52
 
#define H_EXTENSION_TABLE 54
 
#define H_USER_NAME 56
 

	
 
#define HX_TABLE_SIZE 0
 
#define HX_MOUSE_X 1
 
#define HX_MOUSE_Y 2
 
#define HX_UNICODE_TABLE 3
 

	
 
/*** Various Z-machine constants ***/
 

	
 
#define V1 1
 
#define V2 2
 
#define V3 3
 
#define V4 4
 
#define V5 5
 
#define V6 6
 
#define V7 7
 
#define V8 8
 

	
 
#define CONFIG_BYTE_SWAPPED 0x01 /* Story file is byte swapped         - V3  */
 
#define CONFIG_TIME         0x02 /* Status line displays time          - V3  */
 
#define CONFIG_TWODISKS     0x04 /* Story file occupied two disks      - V3  */
 
#define CONFIG_TANDY        0x08 /* Tandy licensed game                - V3  */
 
#define CONFIG_NOSTATUSLINE 0x10 /* Interpr can't support status lines - V3  */
 
#define CONFIG_SPLITSCREEN  0x20 /* Interpr supports split screen mode - V3  */
 
#define CONFIG_PROPORTIONAL 0x40 /* Interpr uses proportional font     - V3  */
 

	
 
#define CONFIG_COLOUR       0x01 /* Interpr supports colour            - V5+ */
 
#define CONFIG_PICTURES	    0x02 /* Interpr supports pictures	       - V6  */
 
#define CONFIG_BOLDFACE     0x04 /* Interpr supports boldface style    - V4+ */
 
#define CONFIG_EMPHASIS     0x08 /* Interpr supports emphasis style    - V4+ */
 
#define CONFIG_FIXED        0x10 /* Interpr supports fixed width style - V4+ */
 
#define CONFIG_SOUND	    0x20 /* Interpr supports sound             - V6  */
 

	
 
#define CONFIG_TIMEDINPUT   0x80 /* Interpr supports timed input       - V4+ */
 

	
 
#define SCRIPTING_FLAG	  0x0001 /* Outputting to transscription file  - V1+ */
 
#define FIXED_FONT_FLAG   0x0002 /* Use fixed width font               - V3+ */
 
#define REFRESH_FLAG 	  0x0004 /* Refresh the screen                 - V6  */
 
#define GRAPHICS_FLAG	  0x0008 /* Game wants to use graphics         - V5+ */
 
#define OLD_SOUND_FLAG	  0x0010 /* Game wants to use sound effects    - V3  */
 
#define UNDO_FLAG	  0x0010 /* Game wants to use UNDO feature     - V5+ */
 
#define MOUSE_FLAG	  0x0020 /* Game wants to use a mouse          - V5+ */
 
#define COLOUR_FLAG	  0x0040 /* Game wants to use colours          - V5+ */
 
#define SOUND_FLAG	  0x0080 /* Game wants to use sound effects    - V5+ */
 
#define MENU_FLAG	  0x0100 /* Game wants to use menus            - V6  */
 

	
 
#define INTERP_DEFAULT 0
 
#define INTERP_DEC_20 1
 
#define INTERP_APPLE_IIE 2
 
#define INTERP_MACINTOSH 3
 
#define INTERP_AMIGA 4
 
#define INTERP_ATARI_ST 5
 
#define INTERP_MSDOS 6
 
#define INTERP_CBM_128 7
 
#define INTERP_CBM_64 8
 
#define INTERP_APPLE_IIC 9
 
#define INTERP_APPLE_IIGS 10
 
#define INTERP_TANDY 11
 

	
 
#define BLACK_COLOUR 2
 
#define RED_COLOUR 3
 
#define GREEN_COLOUR 4
 
#define YELLOW_COLOUR 5
 
#define BLUE_COLOUR 6
 
#define MAGENTA_COLOUR 7
 
#define CYAN_COLOUR 8
 
#define WHITE_COLOUR 9
 
#define GREY_COLOUR 10		/* INTERP_MSDOS only */
 
#define LIGHTGREY_COLOUR 10 	/* INTERP_AMIGA only */
 
#define MEDIUMGREY_COLOUR 11 	/* INTERP_AMIGA only */
 
#define DARKGREY_COLOUR 12 	/* INTERP_AMIGA only */
 

	
 
#define REVERSE_STYLE 1
 
#define BOLDFACE_STYLE 2
 
#define EMPHASIS_STYLE 4
 
#define FIXED_WIDTH_STYLE 8
 

	
 
#define TEXT_FONT 1
 
#define PICTURE_FONT 2
 
#define GRAPHICS_FONT 3
 
#define FIXED_WIDTH_FONT 4
 

	
 
#define BEEP_HIGH	1
 
#define BEEP_LOW	2
 

	
 
/*** Constants for os_restart_game */
 

	
 
#define RESTART_BEGIN 0
 
#define RESTART_WPROP_SET 1
 
#define RESTART_END 2
 

	
 
/*** Character codes ***/
 

	
 
#define ZC_TIME_OUT 0x00
 
#define ZC_NEW_STYLE 0x01
 
#define ZC_NEW_FONT 0x02
 
#define ZC_BACKSPACE 0x08
 
#define ZC_INDENT 0x09
 
#define ZC_GAP 0x0b
 
#define ZC_RETURN 0x0d
 
#define ZC_HKEY_MIN 0x0e
 
#define ZC_HKEY_RECORD 0x0e
 
#define ZC_HKEY_PLAYBACK 0x0f
 
#define ZC_HKEY_SEED 0x10
 
#define ZC_HKEY_UNDO 0x11
 
#define ZC_HKEY_RESTART 0x12
 
#define ZC_HKEY_QUIT 0x13
 
#define ZC_HKEY_DEBUG 0x14
 
#define ZC_HKEY_HELP 0x15
 
#define ZC_HKEY_MAX 0x15
 
#define ZC_ESCAPE 0x1b
 
#define ZC_ASCII_MIN 0x20
 
#define ZC_ASCII_MAX 0x7e
 
#define ZC_BAD 0x7f
 
#define ZC_ARROW_MIN 0x81
 
#define ZC_ARROW_UP 0x81
 
#define ZC_ARROW_DOWN 0x82
 
#define ZC_ARROW_LEFT 0x83
 
#define ZC_ARROW_RIGHT 0x84
 
#define ZC_ARROW_MAX 0x84
 
#define ZC_FKEY_MIN 0x85
 
#define ZC_FKEY_MAX 0x90
 
#define ZC_NUMPAD_MIN 0x91
 
#define ZC_NUMPAD_MAX 0x9a
 
#define ZC_SINGLE_CLICK 0x9b
 
#define ZC_DOUBLE_CLICK 0x9c
 
#define ZC_MENU_CLICK 0x9d
 
#define ZC_LATIN1_MIN 0xa0
 
#define ZC_LATIN1_MAX 0xff
 

	
 
/*** File types ***/
 

	
 
#define FILE_RESTORE 0
 
#define FILE_SAVE 1
 
#define FILE_SCRIPT 2
 
#define FILE_PLAYBACK 3
 
#define FILE_RECORD 4
 
#define FILE_LOAD_AUX 5
 
#define FILE_SAVE_AUX 6
 

	
 
/*** Data access macros ***/
 

	
 
#define SET_BYTE(addr,v)  { zmp[addr] = v; }
 
#define LOW_BYTE(addr,v)  { v = zmp[addr]; }
 
#define CODE_BYTE(v)	  { v = *pcp++;    }
 

	
 
#if defined (AMIGA)
 

	
 
extern zbyte *pcp;
 
extern zbyte *zmp;
 

	
 
#define lo(v)	((zbyte *)&v)[1]
 
#define hi(v)	((zbyte *)&v)[0]
 

	
 
#define SET_WORD(addr,v)  { zmp[addr] = hi(v); zmp[addr+1] = lo(v); }
 
#define LOW_WORD(addr,v)  { hi(v) = zmp[addr]; lo(v) = zmp[addr+1]; }
 
#define HIGH_WORD(addr,v) { hi(v) = zmp[addr]; lo(v) = zmp[addr+1]; }
 
#define CODE_WORD(v)      { hi(v) = *pcp++; lo(v) = *pcp++; }
 
#define GET_PC(v)         { v = pcp - zmp; }
 
#define SET_PC(v)         { pcp = zmp + v; }
 

	
 
#endif
 

	
 
/* A bunch of x86 assembly code previously appeared here. */
 

	
 
#if !defined (AMIGA) && !defined (MSDOS_16BIT)
 

	
 
extern zbyte *pcp;
 
extern zbyte *zmp;
 

	
 
#define lo(v)	(v & 0xff)
 
#define hi(v)	(v >> 8)
 

	
 
#define SET_WORD(addr,v)  { zmp[addr] = hi(v); zmp[addr+1] = lo(v); }
 
#define LOW_WORD(addr,v)  { v = ((zword) zmp[addr] << 8) | zmp[addr+1]; }
 
#define HIGH_WORD(addr,v) { v = ((zword) zmp[addr] << 8) | zmp[addr+1]; }
 
#define CODE_WORD(v)      { v = ((zword) pcp[0] << 8) | pcp[1]; pcp += 2; }
 
#define GET_PC(v)         { v = pcp - zmp; }
 
#define SET_PC(v)         { pcp = zmp + v; }
 

	
 
#endif
 

	
 

	
 
/*** Story file header data ***/
 

	
 
extern zbyte h_version;
 
extern zbyte h_config;
 
extern zword h_release;
 
extern zword h_resident_size;
 
extern zword h_start_pc;
 
extern zword h_dictionary;
 
extern zword h_objects;
 
extern zword h_globals;
 
extern zword h_dynamic_size;
 
extern zword h_flags;
 
extern zbyte h_serial[6];
 
extern zword h_abbreviations;
 
extern zword h_file_size;
 
extern zword h_checksum;
 
extern zbyte h_interpreter_number;
 
extern zbyte h_interpreter_version;
 
extern zbyte h_screen_rows;
 
extern zbyte h_screen_cols;
 
extern zword h_screen_width;
 
extern zword h_screen_height;
 
extern zbyte h_font_height;
 
extern zbyte h_font_width;
 
extern zword h_functions_offset;
 
extern zword h_strings_offset;
 
extern zbyte h_default_background;
 
extern zbyte h_default_foreground;
 
extern zword h_terminating_keys;
 
extern zword h_line_width;
 
extern zbyte h_standard_high;
 
extern zbyte h_standard_low;
 
extern zword h_alphabet;
 
extern zword h_extension_table;
 
extern zbyte h_user_name[8];
 

	
 
extern zword hx_table_size;
 
extern zword hx_mouse_x;
 
extern zword hx_mouse_y;
 
extern zword hx_unicode_table;
 

	
 
/*** Various data ***/
 

	
 
extern char *story_name;
 

	
 
extern enum story story_id;
 
extern long story_size;
 

	
 
extern zword stack[STACK_SIZE];
 
extern zword *sp;
 
extern zword *fp;
 
extern zword frame_count;
 

	
 
extern zword zargs[8];
 
extern int zargc;
 

	
 
extern int ostream_screen;
 
extern int ostream_script;
 
extern int ostream_memory;
 
extern int ostream_record;
 
extern int istream_replay;
 
extern int message;
 

	
 
extern int cwin;
 
extern int mwin;
 

	
 
extern int mouse_x;
 
extern int mouse_y;
 

	
 
extern int enable_wrapping;
 
extern int enable_scripting;
 
extern int enable_scrolling;
 
extern int enable_buffering;
 

	
 

	
 
extern char *option_zcode_path;	/* dg */
 

	
 
extern long reserve_mem;
 

	
 

	
 
/*** Blorb stuff ***/
 
/*
 
bb_err_t       blorb_err;
 
bb_map_t       *blorb_map;
 
*/
 

	
 
/*** Z-machine opcodes ***/
 

	
 
void 	z_add (void);
 
void 	z_and (void);
 
void 	z_art_shift (void);
 
void 	z_buffer_mode (void);
 
void 	z_call_n (void);
 
void 	z_call_s (void);
 
void 	z_catch (void);
 
void 	z_check_arg_count (void);
 
void	z_check_unicode (void);
 
void 	z_clear_attr (void);
 
void 	z_copy_table (void);
 
void 	z_dec (void);
 
void 	z_dec_chk (void);
 
void 	z_div (void);
 
void 	z_draw_picture (void);
 
void 	z_encode_text (void);
 
void 	z_erase_line (void);
 
void 	z_erase_picture (void);
 
void 	z_erase_window (void);
 
void 	z_get_child (void);
 
void 	z_get_cursor (void);
 
void 	z_get_next_prop (void);
 
void 	z_get_parent (void);
 
void 	z_get_prop (void);
 
void 	z_get_prop_addr (void);
 
void 	z_get_prop_len (void);
 
void 	z_get_sibling (void);
 
void 	z_get_wind_prop (void);
 
void 	z_inc (void);
 
void 	z_inc_chk (void);
 
void 	z_input_stream (void);
 
void 	z_insert_obj (void);
 
void 	z_je (void);
 
void 	z_jg (void);
 
void 	z_jin (void);
 
void 	z_jl (void);
 
void 	z_jump (void);
 
void 	z_jz (void);
 
void 	z_load (void);
 
void 	z_loadb (void);
 
void 	z_loadw (void);
 
void 	z_log_shift (void);
 
void 	z_make_menu (void);
 
void 	z_mod (void);
 
void 	z_mouse_window (void);
 
void 	z_move_window (void);
 
void 	z_mul (void);
 
void 	z_new_line (void);
 
void 	z_nop (void);
 
void 	z_not (void);
 
void 	z_or (void);
 
void 	z_output_stream (void);
 
void 	z_picture_data (void);
 
void 	z_picture_table (void);
 
void 	z_piracy (void);
 
void 	z_pop (void);
 
void 	z_pop_stack (void);
 
void 	z_print (void);
 
void 	z_print_addr (void);
 
void 	z_print_char (void);
 
void 	z_print_form (void);
 
void 	z_print_num (void);
 
void 	z_print_obj (void);
 
void 	z_print_paddr (void);
 
void 	z_print_ret (void);
 
void 	z_print_table (void);
 
void	z_print_unicode (void);
 
void 	z_pull (void);
 
void 	z_push (void);
 
void 	z_push_stack (void);
 
void 	z_put_prop (void);
 
void 	z_put_wind_prop (void);
 
void 	z_quit (void);
 
void 	z_random (void);
 
void 	z_read (void);
 
void 	z_read_char (void);
 
void 	z_read_mouse (void);
 
void 	z_remove_obj (void);
 
void 	z_restart (void);
 
void 	z_restore (void);
 
void 	z_restore_undo (void);
 
void 	z_ret (void);
 
void 	z_ret_popped (void);
 
void 	z_rfalse (void);
 
void 	z_rtrue (void);
 
void 	z_save (void);
 
void 	z_save_undo (void);
 
void 	z_scan_table (void);
 
void 	z_scroll_window (void);
 
void 	z_set_attr (void);
 
void 	z_set_font (void);
 
void 	z_set_colour (void);
 
void 	z_set_cursor (void);
 
void 	z_set_margins (void);
 
void 	z_set_window (void);
 
void 	z_set_text_style (void);
 
void 	z_show_status (void);
 
void 	z_sound_effect (void);
 
void 	z_split_window (void);
 
void 	z_store (void);
 
void 	z_storeb (void);
 
void 	z_storew (void);
 
void 	z_sub (void);
 
void 	z_test (void);
 
void 	z_test_attr (void);
 
void 	z_throw (void);
 
void 	z_tokenise (void);
 
void 	z_verify (void);
 
void 	z_window_size (void);
 
void 	z_window_style (void);
 

	
 
/* Definitions for error handling functions and error codes. */
 

	
 
/* extern int err_report_mode; */
 

	
 
void	init_err (void);
 
void	runtime_error (int);
 
 
 
/* Error codes */
 
#define ERR_TEXT_BUF_OVF 1	/* Text buffer overflow */
 
#define ERR_STORE_RANGE 2	/* Store out of dynamic memory */
 
#define ERR_DIV_ZERO 3		/* Division by zero */
 
#define ERR_ILL_OBJ 4		/* Illegal object */
 
#define ERR_ILL_ATTR 5		/* Illegal attribute */
 
#define ERR_NO_PROP 6		/* No such property */
 
#define ERR_STK_OVF 7		/* Stack overflow */
 
#define ERR_ILL_CALL_ADDR 8	/* Call to illegal address */
 
#define ERR_CALL_NON_RTN 9	/* Call to non-routine */
 
#define ERR_STK_UNDF 10		/* Stack underflow */
 
#define ERR_ILL_OPCODE 11	/* Illegal opcode */
 
#define ERR_BAD_FRAME 12	/* Bad stack frame */
 
#define ERR_ILL_JUMP_ADDR 13	/* Jump to illegal address */
 
#define ERR_SAVE_IN_INTER 14	/* Can't save while in interrupt */
 
#define ERR_STR3_NESTING 15	/* Nesting stream #3 too deep */
 
#define ERR_ILL_WIN 16		/* Illegal window */
 
#define ERR_ILL_WIN_PROP 17	/* Illegal window property */
 
#define ERR_ILL_PRINT_ADDR 18	/* Print at illegal address */
 
#define ERR_MAX_FATAL 18
 

	
 
/* Less serious errors */
 
#define ERR_JIN_0 19		/* @jin called with object 0 */
 
#define ERR_GET_CHILD_0 20	/* @get_child called with object 0 */
 
#define ERR_GET_PARENT_0 21	/* @get_parent called with object 0 */
 
#define ERR_GET_SIBLING_0 22	/* @get_sibling called with object 0 */
 
#define ERR_GET_PROP_ADDR_0 23	/* @get_prop_addr called with object 0 */
 
#define ERR_GET_PROP_0 24	/* @get_prop called with object 0 */
 
#define ERR_PUT_PROP_0 25	/* @put_prop called with object 0 */
 
#define ERR_CLEAR_ATTR_0 26	/* @clear_attr called with object 0 */
 
#define ERR_SET_ATTR_0 27	/* @set_attr called with object 0 */
 
#define ERR_TEST_ATTR_0 28	/* @test_attr called with object 0 */
 
#define ERR_MOVE_OBJECT_0 29	/* @move_object called moving object 0 */
 
#define ERR_MOVE_OBJECT_TO_0 30	/* @move_object called moving into object 0 */
 
#define ERR_REMOVE_OBJECT_0 31	/* @remove_object called with object 0 */
 
#define ERR_GET_NEXT_PROP_0 32	/* @get_next_prop called with object 0 */
 
#define ERR_NUM_ERRORS (32)
 
 
 
/* There are four error reporting modes: never report errors;
 
  report only the first time a given error type occurs; report
 
  every time an error occurs; or treat all errors as fatal
 
  errors, killing the interpreter. I strongly recommend
 
  "report once" as the default. But you can compile in a
 
  different default by changing the definition of
 
  ERR_DEFAULT_REPORT_MODE. In any case, the player can
 
  specify a report mode on the command line by typing "-Z 0"
 
  through "-Z 3". */
 

	
 
#define ERR_REPORT_NEVER (0)
 
#define ERR_REPORT_ONCE (1)
 
#define ERR_REPORT_ALWAYS (2)
 
#define ERR_REPORT_FATAL (3)
 

	
 
#define ERR_DEFAULT_REPORT_MODE ERR_REPORT_ONCE
 

	
 

	
 
/*** Various global functions ***/
 

	
 
zchar	translate_from_zscii (zbyte);
 
zbyte	translate_to_zscii (zchar);
 

	
 
void 	flush_buffer (void);
 
void	new_line (void);
 
void	print_char (zchar);
 
void	print_num (zword);
 
void	print_object (zword);
 
void 	print_string (const char *);
 

	
 
void 	stream_mssg_on (void);
 
void 	stream_mssg_off (void);
 

	
 
void	ret (zword);
 
void 	store (zword);
 
void 	branch (int);
 

	
 
void	storeb (zword, zbyte);
 
void	storew (zword, zword);
 

	
 
/*** Interface functions ***/
 

	
 
void 	os_beep (int);
 
int  	os_char_width (zchar);
 
void 	os_display_char (zchar);
 
void 	os_display_string (const zchar *);
 
void 	os_draw_picture (int, int, int);
 
void 	os_erase_area (int, int, int, int);
 
void 	os_fatal (const char *);
 
void 	os_finish_with_sample (int);
 
int  	os_font_data (int, int *, int *);
 
void 	os_init_screen (void);
 
void 	os_more_prompt (void);
 
int  	os_peek_colour (void);
 
int  	os_picture_data (int, int *, int *);
 
void 	os_prepare_sample (int);
 
void 	os_process_arguments (int, char *[]);
 
int	os_random_seed (void);
 
int  	os_read_file_name (char *, const char *, int);
 
zchar	os_read_key (int, int);
 
zchar	os_read_line (int, zchar *, int, int, int);
 
void 	os_reset_screen (void);
 
void 	os_restart_game (int);
 
void 	os_scroll_area (int, int, int, int, int);
 
void 	os_set_colour (int, int);
 
void 	os_set_cursor (int, int);
 
void 	os_set_font (int);
 
void 	os_set_text_style (int);
 
void 	os_start_sample (int, int, int, zword);
 
void 	os_stop_sample (int);
 
int  	os_string_width (const zchar *);
 
void	os_init_setup (void);
 
int	os_speech_output(const zchar *);
 

	
 
void init_buffer(void);
 
void init_process(void);
 
void init_sound(void);
 
void init_memory(void);
 
void init_undo(void);
 
void interpret(void);
 
void reset_memory(void);
 

	
 
char *frotz_get_array();
 
void frotz_reset_array();
 
void dumb_show_screen(int show_cursor);
 

	
 
#include "setup.h"
 

	
 
#ifdef __cplusplus
 
}
 
#endif
backends/frotz/getopt.c
Show inline comments
 
new file 100644
 
/*
 
 * getopt.c
 
 *
 
 * Replacement for a Unix style getopt function
 
 *
 
 * Quick, clean, and portable to funky systems that don't have getopt() 
 
 * for whatever reason.
 
 *
 
 */
 

	
 
#include <stdio.h>
 
#include <string.h>
 

	
 
#ifndef MSDOS_16BIT
 
#define cdecl
 
#endif
 

	
 
int optind = 1;
 
int optopt = 0;
 

	
 
const char *optarg = NULL;
 

	
 
int cdecl getopt (int argc, char *argv[], const char *options)
 
{
 
    static int pos = 1;
 

	
 
    const char *p;
 

	
 
    if (optind >= argc || argv[optind][0] != '-' || argv[optind][1] == 0)
 
	return EOF;
 

	
 
    optopt = argv[optind][pos++];
 
    optarg = NULL;
 

	
 
    if (argv[optind][pos] == 0)
 
	{ pos = 1; optind++; }
 

	
 
    p = strchr (options, optopt);
 

	
 
    if (optopt == ':' || p == NULL) {
 

	
 
	fputs ("illegal option -- ", stdout);
 
	goto error;
 

	
 
    } else if (p[1] == ':') {
 

	
 
	if (optind >= argc) {
 

	
 
	    fputs ("option requires an argument -- ", stdout);
 
	    goto error;
 

	
 
	} else {
 

	
 
	    optarg = argv[optind];
 

	
 
	    if (pos != 1)
 
		optarg += pos;
 

	
 
	    pos = 1; optind++;
 

	
 
	}
 
    }
 
    return optopt;
 

	
 
error:
 

	
 
    fputc (optopt, stdout);
 
    fputc ('\n', stdout);
 

	
 
    return '?';
 

	
 
}/* getopt */
backends/frotz/hotkey.c
Show inline comments
 
new file 100644
 
/* hotkey.c - Hot key functions
 
 *	Copyright (c) 1995-1997 Stefan Jokisch
 
 *
 
 * This file is part of Frotz.
 
 *
 
 * Frotz is free software; you can redistribute it and/or modify
 
 * it under the terms of the GNU General Public License as published by
 
 * the Free Software Foundation; either version 2 of the License, or
 
 * (at your option) any later version.
 
 *
 
 * Frotz is distributed in the hope that it will be useful,
 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
 * GNU General Public License for more details.
 
 *
 
 * You should have received a copy of the GNU General Public License
 
 * along with this program; if not, write to the Free Software
 
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
 
 */
 

	
 
#include "frotz.h"
 

	
 
extern int restore_undo (void);
 

	
 
extern int read_number (void);
 

	
 
extern int read_yes_or_no (const char *);
 

	
 
extern void replay_open (void);
 
extern void replay_close (void);
 
extern void record_open (void);
 
extern void record_close (void);
 

	
 
extern void seed_random (int);
 

	
 
/*
 
 * hot_key_debugging
 
 *
 
 * ...allows user to toggle cheating options on/off.
 
 *
 
 */
 

	
 
static int hot_key_debugging (void)
 
{
 

	
 
    print_string ("Debugging options\n");
 

	
 
    f_setup.attribute_assignment = read_yes_or_no ("Watch attribute assignment");
 
    f_setup.attribute_testing = read_yes_or_no ("Watch attribute testing");
 

	
 
    f_setup.object_movement = read_yes_or_no ("Watch object movement");
 
    f_setup.object_locating = read_yes_or_no ("Watch object locating");
 

	
 
    return FALSE;
 

	
 
}/* hot_key_debugging */
 

	
 
/*
 
 * hot_key_help
 
 *
 
 * ...displays a list of all hot keys.
 
 *
 
 */
 

	
 
static int hot_key_help (void) {
 

	
 
    print_string ("Help\n");
 

	
 
    print_string (
 
	"\n"
 
	"Alt-D  debugging options\n"
 
	"Alt-H  help\n"
 
	"Alt-N  new game\n"
 
	"Alt-P  playback on\n"
 
	"Alt-R  recording on/off\n"
 
	"Alt-S  seed random numbers\n"
 
	"Alt-U  undo one turn\n"
 
	"Alt-X  exit game\n");
 

	
 
    return FALSE;
 

	
 
}/* hot_key_help */
 

	
 
/*
 
 * hot_key_playback
 
 *
 
 * ...allows user to turn playback on.
 
 *
 
 */
 

	
 
static int hot_key_playback (void)
 
{
 

	
 
    print_string ("Playback on\n");
 

	
 
    if (!istream_replay)
 
	replay_open ();
 

	
 
    return FALSE;
 

	
 
}/* hot_key_playback */
 

	
 
/*
 
 * hot_key_recording
 
 *
 
 * ...allows user to turn recording on/off.
 
 *
 
 */
 

	
 
static int hot_key_recording (void)
 
{
 

	
 
    if (istream_replay) {
 
	print_string ("Playback off\n");
 
	replay_close ();
 
    } else if (ostream_record) {
 
	print_string ("Recording off\n");
 
	record_close ();
 
    } else {
 
	print_string ("Recording on\n");
 
	record_open ();
 
    }
 

	
 
    return FALSE;
 

	
 
}/* hot_key_recording */
 

	
 
/*
 
 * hot_key_seed
 
 *
 
 * ...allows user to seed the random number seed.
 
 *
 
 */
 

	
 
static int hot_key_seed (void)
 
{
 

	
 
    print_string ("Seed random numbers\n");
 

	
 
    print_string ("Enter seed value (or return to randomize): ");
 
    seed_random (read_number ());
 

	
 
    return FALSE;
 

	
 
}/* hot_key_seed */
 

	
 
/*
 
 * hot_key_undo
 
 *
 
 * ...allows user to undo the previous turn.
 
 *
 
 */
 

	
 
static int hot_key_undo (void)
 
{
 

	
 
    print_string ("Undo one turn\n");
 

	
 
    if (restore_undo ()) {
 

	
 
	if (h_version >= V5) {		/* for V5+ games we must */
 
	    store (2);			/* store 2 (for success) */
 
	    return TRUE;		/* and abort the input   */
 
	}
 

	
 
	if (h_version <= V3) {		/* for V3- games we must */
 
	    z_show_status ();		/* draw the status line  */
 
	    return FALSE;		/* and continue input    */
 
	}
 

	
 
    } else print_string ("No more undo information available.\n");
 

	
 
    return FALSE;
 

	
 
}/* hot_key_undo */
 

	
 
/*
 
 * hot_key_restart
 
 *
 
 * ...allows user to start a new game.
 
 *
 
 */
 

	
 
static int hot_key_restart (void)
 
{
 

	
 
    print_string ("New game\n");
 

	
 
    if (read_yes_or_no ("Do you wish to restart")) {
 

	
 
	z_restart ();
 
	return TRUE;
 

	
 
    } else return FALSE;
 

	
 
}/* hot_key_restart */
 

	
 
/*
 
 * hot_key_quit
 
 *
 
 * ...allows user to exit the game.
 
 *
 
 */
 

	
 
static int hot_key_quit (void)
 
{
 

	
 
    print_string ("Exit game\n");
 

	
 
    if (read_yes_or_no ("Do you wish to quit")) {
 

	
 
	z_quit ();
 
	return TRUE;
 

	
 
    } else return FALSE;
 

	
 
}/* hot_key_quit */
 

	
 
/*
 
 * handle_hot_key
 
 *
 
 * Perform the action associated with a so-called hot key. Return
 
 * true to abort the current input action.
 
 *
 
 */
 

	
 
int handle_hot_key (zchar key)
 
{
 

	
 
    if (cwin == 0) {
 

	
 
	int aborting;
 

	
 
	aborting = FALSE;
 

	
 
	print_string ("\nHot key -- ");
 

	
 
	switch (key) {
 
	    case ZC_HKEY_RECORD: aborting = hot_key_recording (); break;
 
	    case ZC_HKEY_PLAYBACK: aborting = hot_key_playback (); break;
 
	    case ZC_HKEY_SEED: aborting = hot_key_seed (); break;
 
	    case ZC_HKEY_UNDO: aborting = hot_key_undo (); break;
 
	    case ZC_HKEY_RESTART: aborting = hot_key_restart (); break;
 
	    case ZC_HKEY_QUIT: aborting = hot_key_quit (); break;
 
	    case ZC_HKEY_DEBUG: aborting = hot_key_debugging (); break;
 
	    case ZC_HKEY_HELP: aborting = hot_key_help (); break;
 
	}
 

	
 
	if (aborting)
 
	    return TRUE;
 

	
 
	print_string ("\nContinue input...\n");
 

	
 
    }
 

	
 
    return FALSE;
 

	
 
}/* handle_hot_key */
backends/frotz/input.c
Show inline comments
 
new file 100644
 
/* input.c - High level input functions
 
 *	Copyright (c) 1995-1997 Stefan Jokisch
 
 *
 
 * This file is part of Frotz.
 
 *
 
 * Frotz is free software; you can redistribute it and/or modify
 
 * it under the terms of the GNU General Public License as published by
 
 * the Free Software Foundation; either version 2 of the License, or
 
 * (at your option) any later version.
 
 *
 
 * Frotz is distributed in the hope that it will be useful,
 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
 * GNU General Public License for more details.
 
 *
 
 * You should have received a copy of the GNU General Public License
 
 * along with this program; if not, write to the Free Software
 
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
 
 */
 

	
 
#include "frotz.h"
 

	
 
extern int save_undo (void);
 

	
 
extern zchar stream_read_key (zword, zword, int);
 
extern zchar stream_read_input (int, zchar *, zword, zword, int, int);
 

	
 
extern void tokenise_line (zword, zword, zword, int);
 

	
 
/*
 
 * is_terminator
 
 *
 
 * Check if the given key is an input terminator.
 
 *
 
 */
 

	
 
int is_terminator (zchar key)
 
{
 

	
 
    if (key == ZC_TIME_OUT)
 
	return TRUE;
 
    if (key == ZC_RETURN)
 
	return TRUE;
 
    if (key >= ZC_HKEY_MIN && key <= ZC_HKEY_MAX)
 
	return TRUE;
 

	
 
    if (h_terminating_keys != 0)
 

	
 
	if (key >= ZC_ARROW_MIN && key <= ZC_MENU_CLICK) {
 

	
 
	    zword addr = h_terminating_keys;
 
	    zbyte c;
 

	
 
	    do {
 
		LOW_BYTE (addr, c)
 
		if (c == 255 || key == translate_from_zscii (c))
 
		    return TRUE;
 
		addr++;
 
	    } while (c != 0);
 

	
 
	}
 

	
 
    return FALSE;
 

	
 
}/* is_terminator */
 

	
 
/*
 
 * z_make_menu, add or remove a menu and branch if successful.
 
 *
 
 * 	zargs[0] = number of menu
 
 *	zargs[1] = table of menu entries or 0 to remove menu
 
 *
 
 */
 

	
 
void z_make_menu (void)
 
{
 

	
 
    /* This opcode was only used for the Macintosh version of Journey.
 
       It controls menus with numbers greater than 2 (menus 0, 1 and 2
 
       are system menus). Frotz doesn't implement menus yet. */
 

	
 
    branch (FALSE);
 

	
 
}/* z_make_menu */
 

	
 
/*
 
 * read_yes_or_no
 
 *
 
 * Ask the user a question; return true if the answer is yes.
 
 *
 
 */
 

	
 
int read_yes_or_no (const char *s)
 
{
 
    zchar key;
 

	
 
    print_string (s);
 
    print_string ("? (y/n) >");
 

	
 
    key = stream_read_key (0, 0, FALSE);
 

	
 
    if (key == 'y' || key == 'Y') {
 
	print_string ("y\n");
 
	return TRUE;
 
    } else {
 
	print_string ("n\n");
 
	return FALSE;
 
    }
 

	
 
}/* read_yes_or_no */
 

	
 
/*
 
 * read_string
 
 *
 
 * Read a string from the current input stream.
 
 *
 
 */
 

	
 
void read_string (int max, zchar *buffer)
 
{
 
    zchar key;
 

	
 
    buffer[0] = 0;
 

	
 
    do {
 

	
 
	key = stream_read_input (max, buffer, 0, 0, FALSE, FALSE);
 

	
 
    } while (key != ZC_RETURN);
 

	
 
}/* read_string */
 

	
 
/*
 
 * read_number
 
 *
 
 * Ask the user to type in a number and return it.
 
 *
 
 */
 

	
 
int read_number (void)
 
{
 
    zchar buffer[6];
 
    int value = 0;
 
    int i;
 

	
 
    read_string (5, buffer);
 

	
 
    for (i = 0; buffer[i] != 0; i++)
 
	if (buffer[i] >= '0' && buffer[i] <= '9')
 
	    value = 10 * value + buffer[i] - '0';
 

	
 
    return value;
 

	
 
}/* read_number */
 

	
 
/*
 
 * z_read, read a line of input and (in V5+) store the terminating key.
 
 *
 
 *	zargs[0] = address of text buffer
 
 *	zargs[1] = address of token buffer
 
 *	zargs[2] = timeout in tenths of a second (optional)
 
 *	zargs[3] = packed address of routine to be called on timeout
 
 *
 
 */
 

	
 
void z_read (void)
 
{
 
    zchar buffer[INPUT_BUFFER_SIZE];
 
    zword addr;
 
    zchar key;
 
    zbyte max, size;
 
    zbyte c;
 
    int i;
 

	
 
    /* Supply default arguments */
 

	
 
    if (zargc < 3)
 
	zargs[2] = 0;
 

	
 
    /* Get maximum input size */
 

	
 
    addr = zargs[0];
 

	
 
    LOW_BYTE (addr, max)
 

	
 
    if (h_version <= V4)
 
	max--;
 

	
 
    if (max >= INPUT_BUFFER_SIZE)
 
	max = INPUT_BUFFER_SIZE - 1;
 

	
 
    /* Get initial input size */
 

	
 
    if (h_version >= V5) {
 
	addr++;
 
	LOW_BYTE (addr, size)
 
    } else size = 0;
 

	
 
    /* Copy initial input to local buffer */
 

	
 
    for (i = 0; i < size; i++) {
 
	addr++;
 
	LOW_BYTE (addr, c)
 
	buffer[i] = translate_from_zscii (c);
 
    }
 

	
 
    buffer[i] = 0;
 

	
 
    /* Draw status line for V1 to V3 games */
 

	
 
    if (h_version <= V3)
 
	z_show_status ();
 

	
 
    /* Read input from current input stream */
 

	
 
    key = stream_read_input (
 
	max, buffer,		/* buffer and size */
 
	zargs[2],		/* timeout value   */
 
	zargs[3],		/* timeout routine */
 
	TRUE,	        	/* enable hot keys */
 
	h_version == V6);	/* no script in V6 */
 

	
 
    if (key == ZC_BAD)
 
	return;
 

	
 
    /* Perform save_undo for V1 to V4 games */
 

	
 
    if (h_version <= V4)
 
	save_undo ();
 

	
 
    /* Copy local buffer back to dynamic memory */
 

	
 
    for (i = 0; buffer[i] != 0; i++) {
 

	
 
	if (key == ZC_RETURN) {
 

	
 
	    if (buffer[i] >= 'A' && buffer[i] <= 'Z')
 
		buffer[i] += 'a' - 'A';
 
	    if (buffer[i] >= 0xc0 && buffer[i] <= 0xde && buffer[i] != 0xd7)
 
		buffer[i] += 0x20;
 

	
 
	}
 

	
 
	storeb ((zword) (zargs[0] + ((h_version <= V4) ? 1 : 2) + i), translate_to_zscii (buffer[i]));
 

	
 
    }
 

	
 
    /* Add null character (V1-V4) or write input length into 2nd byte */
 

	
 
    if (h_version <= V4)
 
	storeb ((zword) (zargs[0] + 1 + i), 0);
 
    else
 
	storeb ((zword) (zargs[0] + 1), i);
 

	
 
    /* Tokenise line if a token buffer is present */
 

	
 
    if (key == ZC_RETURN && zargs[1] != 0)
 
	tokenise_line (zargs[0], zargs[1], 0, FALSE);
 

	
 
    /* Store key */
 

	
 
    if (h_version >= V5)
 
	store (translate_to_zscii (key));
 

	
 
}/* z_read */
 

	
 
/*
 
 * z_read_char, read and store a key.
 
 *
 
 *	zargs[0] = input device (must be 1)
 
 *	zargs[1] = timeout in tenths of a second (optional)
 
 *	zargs[2] = packed address of routine to be called on timeout
 
 *
 
 */
 

	
 
void z_read_char (void)
 
{
 
    zchar key;
 

	
 
    /* Supply default arguments */
 

	
 
    if (zargc < 2)
 
	zargs[1] = 0;
 

	
 
    /* Read input from the current input stream */
 

	
 
    key = stream_read_key (
 
	zargs[1],	/* timeout value   */
 
	zargs[2],	/* timeout routine */
 
	TRUE);  	/* enable hot keys */
 

	
 
    if (key == ZC_BAD)
 
	return;
 

	
 
    /* Store key */
 

	
 
    store (translate_to_zscii (key));
 

	
 
}/* z_read_char */
 

	
 
/*
 
 * z_read_mouse, write the current mouse status into a table.
 
 *
 
 *	zargs[0] = address of table
 
 *
 
 */
 

	
 
void z_read_mouse (void)
 
{
 
    zword btn;
 

	
 
    /* Read the mouse position and which buttons are down */
 

	
 
    btn = os_read_mouse ();
 
    hx_mouse_y = mouse_y;
 
    hx_mouse_x = mouse_x;
 

	
 
    storew ((zword) (zargs[0] + 0), hx_mouse_y);
 
    storew ((zword) (zargs[0] + 2), hx_mouse_x);
 
    storew ((zword) (zargs[0] + 4), btn);	/* mouse button bits */
 
    storew ((zword) (zargs[0] + 6), 0);		/* menu selection */
 

	
 
}/* z_read_mouse */
backends/frotz/main.cpp
Show inline comments
 
new file 100644
 
/*
 
 * Copyright (C) 2008-2009 J-P Nurmi jpnurmi@gmail.com
 
 *
 
 * This example is free, and not covered by LGPL license. There is no
 
 * restriction applied to their modification, redistribution, using and so on.
 
 * You can study them, modify them, use them in your own program - either
 
 * completely or partially. By using it you may give me some credits in your
 
 * program, but you don't have to.
 
 */
 

	
 
#include "transport/config.h"
 
#include "transport/networkplugin.h"
 
#include "frotz.h"
 

	
 
#ifndef MSDOS_16BIT
 
#define cdecl
 
#endif
 

	
 
Swift::SimpleEventLoop *loop_;
 

	
 
extern "C" void spectrum_get_line(char *s);
 

	
 
char input[15000];
 

	
 
void send_array();
 

	
 
void spectrum_get_line(char *s) {
 
	std::cout << "running event loop\n";
 
	dumb_show_screen(FALSE);
 
	send_array();
 
// 	while(strlen(input) == 0) {
 
		loop_->run();
 
// 	}
 
	strcpy(s, input);
 
	strcpy(input, "");
 
	std::cout << "got message " << s << "\n";
 
}
 

	
 
using namespace boost::program_options;
 
using namespace Transport;
 

	
 
extern void interpret (void);
 
extern void init_memory (void);
 
extern void init_undo (void);
 
extern void reset_memory (void);
 

	
 
/* Story file name, id number and size */
 

	
 
char *story_name = "zork.z5";
 

	
 
enum story story_id = UNKNOWN;
 
long story_size = 0;
 

	
 
/* Story file header data */
 

	
 
zbyte h_version = 0;
 
zbyte h_config = 0;
 
zword h_release = 0;
 
zword h_resident_size = 0;
 
zword h_start_pc = 0;
 
zword h_dictionary = 0;
 
zword h_objects = 0;
 
zword h_globals = 0;
 
zword h_dynamic_size = 0;
 
zword h_flags = 0;
 
zbyte h_serial[6] = { 0, 0, 0, 0, 0, 0 };
 
zword h_abbreviations = 0;
 
zword h_file_size = 0;
 
zword h_checksum = 0;
 
zbyte h_interpreter_number = 0;
 
zbyte h_interpreter_version = 0;
 
zbyte h_screen_rows = 0;
 
zbyte h_screen_cols = 0;
 
zword h_screen_width = 0;
 
zword h_screen_height = 0;
 
zbyte h_font_height = 1;
 
zbyte h_font_width = 1;
 
zword h_functions_offset = 0;
 
zword h_strings_offset = 0;
 
zbyte h_default_background = 0;
 
zbyte h_default_foreground = 0;
 
zword h_terminating_keys = 0;
 
zword h_line_width = 0;
 
zbyte h_standard_high = 1;
 
zbyte h_standard_low = 0;
 
zword h_alphabet = 0;
 
zword h_extension_table = 0;
 
zbyte h_user_name[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
 

	
 
zword hx_table_size = 0;
 
zword hx_mouse_x = 0;
 
zword hx_mouse_y = 0;
 
zword hx_unicode_table = 0;
 

	
 
/* Stack data */
 

	
 
zword stack[STACK_SIZE];
 
zword *sp = 0;
 
zword *fp = 0;
 
zword frame_count = 0;
 

	
 
/* IO streams */
 

	
 
int ostream_screen = TRUE;
 
int ostream_script = FALSE;
 
int ostream_memory = FALSE;
 
int ostream_record = FALSE;
 
int istream_replay = FALSE;
 
int message = FALSE;
 

	
 
/* Current window and mouse data */
 

	
 
int cwin = 0;
 
int mwin = 0;
 

	
 
int mouse_y = 0;
 
int mouse_x = 0;
 

	
 
/* Window attributes */
 

	
 
int enable_wrapping = FALSE;
 
int enable_scripting = FALSE;
 
int enable_scrolling = FALSE;
 
int enable_buffering = FALSE;
 

	
 
/* User options */
 

	
 
/*
 
int option_attribute_assignment = 0;
 
int option_attribute_testing = 0;
 
int option_context_lines = 0;
 
int option_object_locating = 0;
 
int option_object_movement = 0;
 
int option_left_margin = 0;
 
int option_right_margin = 0;
 
int option_ignore_errors = 0;
 
int option_piracy = 0;
 
int option_undo_slots = MAX_UNDO_SLOTS;
 
int option_expand_abbreviations = 0;
 
int option_script_cols = 80;
 
int option_save_quetzal = 1;
 
*/
 

	
 
int option_sound = 1;
 
char *option_zcode_path;
 

	
 

	
 
/* Size of memory to reserve (in bytes) */
 

	
 
long reserve_mem = 0;
 

	
 
/*
 
 * z_piracy, branch if the story file is a legal copy.
 
 *
 
 *	no zargs used
 
 *
 
 */
 

	
 
void z_piracy (void)
 
{
 

	
 
    branch (!f_setup.piracy);
 

	
 
}/* z_piracy */
 

	
 
class FrotzNetworkPlugin;
 
FrotzNetworkPlugin * np = NULL;
 

	
 
class FrotzNetworkPlugin : public NetworkPlugin {
 
	public:
 
		FrotzNetworkPlugin(Config *config, Swift::SimpleEventLoop *loop, const std::string &host, int port) : NetworkPlugin(loop, host, port) {
 
			this->config = config;
 
		}
 

	
 
		void handleLoginRequest(const std::string &user, const std::string &legacyName, const std::string &password) {
 
			m_user = user;
 
			np->handleConnected(user);
 
			Swift::StatusShow status;
 
			np->handleBuddyChanged(user, "zork", "Zork", "Games", status.getType());
 
			sleep(1);
 
			np->handleMessage(np->m_user, "zork", first_msg);
 
		}
 

	
 
		void handleLogoutRequest(const std::string &user, const std::string &legacyName) {
 
			exit(0);
 
		}
 

	
 
		void handleMessageSendRequest(const std::string &user, const std::string &legacyName, const std::string &message, const std::string &/*xhtml*/) {
 
			std::string msg = message + "\n";
 
			strcpy(input, msg.c_str());
 
			loop_->stop();
 
		}
 

	
 
		void handleJoinRoomRequest(const std::string &user, const std::string &room, const std::string &nickname, const std::string &password) {
 
		}
 

	
 
		void handleLeaveRoomRequest(const std::string &user, const std::string &room) {
 
		}
 

	
 
		std::string m_user;
 
		std::string first_msg;
 
	private:
 
		
 
		Config *config;
 
};
 

	
 
void send_array() {
 
	if (np->first_msg.empty())
 
		np->first_msg = frotz_get_array();
 
	else
 
		np->handleMessage(np->m_user, "zork", frotz_get_array());
 
	frotz_reset_array();
 
}
 

	
 
int main (int argc, char* argv[]) {
 
	std::string host;
 
	int port;
 

	
 

	
 
	boost::program_options::options_description desc("Usage: spectrum [OPTIONS] <config_file.cfg>\nAllowed options");
 
	desc.add_options()
 
		("host,h", value<std::string>(&host), "host")
 
		("port,p", value<int>(&port), "port")
 
		;
 
	try
 
	{
 
		boost::program_options::variables_map vm;
 
		boost::program_options::store(boost::program_options::parse_command_line(argc, argv, desc), vm);
 
		boost::program_options::notify(vm);
 
	}
 
	catch (std::runtime_error& e)
 
	{
 
		std::cout << desc << "\n";
 
		exit(1);
 
	}
 
	catch (...)
 
	{
 
		std::cout << desc << "\n";
 
		exit(1);
 
	}
 

	
 

	
 
	if (argc < 5) {
 
		return 1;
 
	}
 

	
 
// 	QStringList channels;
 
// 	for (int i = 3; i < argc; ++i)
 
// 	{
 
// 		channels.append(argv[i]);
 
// 	}
 
// 
 
// 	MyIrcSession session;
 
// 	session.setNick(argv[2]);
 
// 	session.setAutoJoinChannels(channels);
 
// 	session.connectToServer(argv[1], 6667);
 

	
 
	Config config;
 
	if (!config.load(argv[5])) {
 
		std::cerr << "Can't open " << argv[1] << " configuration file.\n";
 
		return 1;
 
	}
 

	
 
	Swift::SimpleEventLoop eventLoop;
 
	loop_ = &eventLoop;
 
	np = new FrotzNetworkPlugin(&config, &eventLoop, host, port);
 

	
 
	os_init_setup ();
 
	os_process_arguments (argc, argv);
 
	init_buffer ();
 
	init_err ();
 
	init_memory ();
 
	init_process ();
 
	init_sound ();
 
	os_init_screen ();
 
	init_undo ();
 
	z_restart ();
 
	interpret ();
 
	reset_memory ();
 
	os_reset_screen ();
 

	
 
	return 0;
 
}
backends/frotz/math.c
Show inline comments
 
new file 100644
 
/* math.c - Arithmetic, compare and logical opcodes
 
 *	Copyright (c) 1995-1997 Stefan Jokisch
 
 *
 
 * This file is part of Frotz.
 
 *
 
 * Frotz is free software; you can redistribute it and/or modify
 
 * it under the terms of the GNU General Public License as published by
 
 * the Free Software Foundation; either version 2 of the License, or
 
 * (at your option) any later version.
 
 *
 
 * Frotz is distributed in the hope that it will be useful,
 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
 * GNU General Public License for more details.
 
 *
 
 * You should have received a copy of the GNU General Public License
 
 * along with this program; if not, write to the Free Software
 
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
 
 */
 

	
 
#include "frotz.h"
 

	
 
/*
 
 * z_add, 16bit addition.
 
 *
 
 *	zargs[0] = first value
 
 *	zargs[1] = second value
 
 *
 
 */
 

	
 
void z_add (void)
 
{
 

	
 
    store ((zword) ((short) zargs[0] + (short) zargs[1]));
 

	
 
}/* z_add */
 

	
 
/*
 
 * z_and, bitwise AND operation.
 
 *
 
 *	zargs[0] = first value
 
 *	zargs[1] = second value
 
 *
 
 */
 

	
 
void z_and (void)
 
{
 

	
 
    store ((zword) (zargs[0] & zargs[1]));
 

	
 
}/* z_and */
 

	
 
/*
 
 * z_art_shift, arithmetic SHIFT operation.
 
 *
 
 *	zargs[0] = value
 
 *	zargs[1] = #positions to shift left (positive) or right
 
 *
 
 */
 

	
 
void z_art_shift (void)
 
{
 

	
 
    if ((short) zargs[1] > 0)
 
	store ((zword) ((short) zargs[0] << (short) zargs[1]));
 
    else
 
	store ((zword) ((short) zargs[0] >> - (short) zargs[1]));
 

	
 
}/* z_art_shift */
 

	
 
/*
 
 * z_div, signed 16bit division.
 
 *
 
 *	zargs[0] = first value
 
 *	zargs[1] = second value
 
 *
 
 */
 

	
 
void z_div (void)
 
{
 

	
 
    if (zargs[1] == 0)
 
	runtime_error (ERR_DIV_ZERO);
 

	
 
    store ((zword) ((short) zargs[0] / (short) zargs[1]));
 

	
 
}/* z_div */
 

	
 
/*
 
 * z_je, branch if the first value equals any of the following.
 
 *
 
 *	zargs[0] = first value
 
 *	zargs[1] = second value (optional)
 
 *	...
 
 *	zargs[3] = fourth value (optional)
 
 *
 
 */
 

	
 
void z_je (void)
 
{
 

	
 
    branch (
 
	zargc > 1 && (zargs[0] == zargs[1] || (
 
	zargc > 2 && (zargs[0] == zargs[2] || (
 
	zargc > 3 && (zargs[0] == zargs[3]))))));
 

	
 
}/* z_je */
 

	
 
/*
 
 * z_jg, branch if the first value is greater than the second.
 
 *
 
 *	zargs[0] = first value
 
 *	zargs[1] = second value
 
 *
 
 */
 

	
 
void z_jg (void)
 
{
 

	
 
    branch ((short) zargs[0] > (short) zargs[1]);
 

	
 
}/* z_jg */
 

	
 
/*
 
 * z_jl, branch if the first value is less than the second.
 
 *
 
 *	zargs[0] = first value
 
 *	zargs[1] = second value
 
 *
 
 */
 

	
 
void z_jl (void)
 
{
 

	
 
    branch ((short) zargs[0] < (short) zargs[1]);
 

	
 
}/* z_jl */
 

	
 
/*
 
 * z_jz, branch if value is zero.
 
 *
 
 * 	zargs[0] = value
 
 *
 
 */
 

	
 
void z_jz (void)
 
{
 

	
 
    branch ((short) zargs[0] == 0);
 

	
 
}/* z_jz */
 

	
 
/*
 
 * z_log_shift, logical SHIFT operation.
 
 *
 
 * 	zargs[0] = value
 
 *	zargs[1] = #positions to shift left (positive) or right (negative)
 
 *
 
 */
 

	
 
void z_log_shift (void)
 
{
 

	
 
    if ((short) zargs[1] > 0)
 
	store ((zword) (zargs[0] << (short) zargs[1]));
 
    else
 
	store ((zword) (zargs[0] >> - (short) zargs[1]));
 

	
 
}/* z_log_shift */
 

	
 
/*
 
 * z_mod, remainder after signed 16bit division.
 
 *
 
 * 	zargs[0] = first value
 
 *	zargs[1] = second value
 
 *
 
 */
 

	
 
void z_mod (void)
 
{
 

	
 
    if (zargs[1] == 0)
 
	runtime_error (ERR_DIV_ZERO);
 

	
 
    store ((zword) ((short) zargs[0] % (short) zargs[1]));
 

	
 
}/* z_mod */
 

	
 
/*
 
 * z_mul, 16bit multiplication.
 
 *
 
 * 	zargs[0] = first value
 
 *	zargs[1] = second value
 
 *
 
 */
 

	
 
void z_mul (void)
 
{
 

	
 
    store ((zword) ((short) zargs[0] * (short) zargs[1]));
 

	
 
}/* z_mul */
 

	
 
/*
 
 * z_not, bitwise NOT operation.
 
 *
 
 * 	zargs[0] = value
 
 *
 
 */
 

	
 
void z_not (void)
 
{
 

	
 
    store ((zword) ~zargs[0]);
 

	
 
}/* z_not */
 

	
 
/*
 
 * z_or, bitwise OR operation.
 
 *
 
 *	zargs[0] = first value
 
 *	zargs[1] = second value
 
 *
 
 */
 

	
 
void z_or (void)
 
{
 

	
 
    store ((zword) (zargs[0] | zargs[1]));
 

	
 
}/* z_or */
 

	
 
/*
 
 * z_sub, 16bit substraction.
 
 *
 
 *	zargs[0] = first value
 
 *	zargs[1] = second value
 
 *
 
 */
 

	
 
void z_sub (void)
 
{
 

	
 
    store ((zword) ((short) zargs[0] - (short) zargs[1]));
 

	
 
}/* z_sub */
 

	
 
/*
 
 * z_test, branch if all the flags of a bit mask are set in a value.
 
 *
 
 *	zargs[0] = value to be examined
 
 *	zargs[1] = bit mask
 
 *
 
 */
 

	
 
void z_test (void)
 
{
 

	
 
    branch ((zargs[0] & zargs[1]) == zargs[1]);
 

	
 
}/* z_test */
backends/frotz/object.c
Show inline comments
 
new file 100644
 
/* object.c - Object manipulation opcodes
 
 *	Copyright (c) 1995-1997 Stefan Jokisch
 
 *
 
 * This file is part of Frotz.
 
 *
 
 * Frotz is free software; you can redistribute it and/or modify
 
 * it under the terms of the GNU General Public License as published by
 
 * the Free Software Foundation; either version 2 of the License, or
 
 * (at your option) any later version.
 
 *
 
 * Frotz is distributed in the hope that it will be useful,
 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
 * GNU General Public License for more details.
 
 *
 
 * You should have received a copy of the GNU General Public License
 
 * along with this program; if not, write to the Free Software
 
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
 
 */
 

	
 
#include "frotz.h"
 

	
 
#define MAX_OBJECT 2000
 

	
 
#define O1_PARENT 4
 
#define O1_SIBLING 5
 
#define O1_CHILD 6
 
#define O1_PROPERTY_OFFSET 7
 
#define O1_SIZE 9
 

	
 
#define O4_PARENT 6
 
#define O4_SIBLING 8
 
#define O4_CHILD 10
 
#define O4_PROPERTY_OFFSET 12
 
#define O4_SIZE 14
 

	
 
/*
 
 * object_address
 
 *
 
 * Calculate the address of an object.
 
 *
 
 */
 

	
 
static zword object_address (zword obj)
 
{
 
/*    zchar obj_num[10]; */
 

	
 
    /* Check object number */
 

	
 
    if (obj > ((h_version <= V3) ? 255 : MAX_OBJECT)) {
 
	print_string("@Attempt to address illegal object ");
 
	print_num(obj);
 
	print_string(".  This is normally fatal.");
 
	new_line();
 
	runtime_error (ERR_ILL_OBJ);
 
    }
 

	
 
    /* Return object address */
 

	
 
    if (h_version <= V3)
 
	return h_objects + ((obj - 1) * O1_SIZE + 62);
 
    else
 
	return h_objects + ((obj - 1) * O4_SIZE + 126);
 

	
 
}/* object_address */
 

	
 
/*
 
 * object_name
 
 *
 
 * Return the address of the given object's name.
 
 *
 
 */
 

	
 
zword object_name (zword object)
 
{
 
    zword obj_addr;
 
    zword name_addr;
 

	
 
    obj_addr = object_address (object);
 

	
 
    /* The object name address is found at the start of the properties */
 

	
 
    if (h_version <= V3)
 
	obj_addr += O1_PROPERTY_OFFSET;
 
    else
 
	obj_addr += O4_PROPERTY_OFFSET;
 

	
 
    LOW_WORD (obj_addr, name_addr)
 

	
 
    return name_addr;
 

	
 
}/* object_name */
 

	
 
/*
 
 * first_property
 
 *
 
 * Calculate the start address of the property list associated with
 
 * an object.
 
 *
 
 */
 

	
 
static zword first_property (zword obj)
 
{
 
    zword prop_addr;
 
    zbyte size;
 

	
 
    /* Fetch address of object name */
 

	
 
    prop_addr = object_name (obj);
 

	
 
    /* Get length of object name */
 

	
 
    LOW_BYTE (prop_addr, size)
 

	
 
    /* Add name length to pointer */
 

	
 
    return prop_addr + 1 + 2 * size;
 

	
 
}/* first_property */
 

	
 
/*
 
 * next_property
 
 *
 
 * Calculate the address of the next property in a property list.
 
 *
 
 */
 

	
 
static zword next_property (zword prop_addr)
 
{
 
    zbyte value;
 

	
 
    /* Load the current property id */
 

	
 
    LOW_BYTE (prop_addr, value)
 
    prop_addr++;
 

	
 
    /* Calculate the length of this property */
 

	
 
    if (h_version <= V3)
 
	value >>= 5;
 
    else if (!(value & 0x80))
 
	value >>= 6;
 
    else {
 

	
 
	LOW_BYTE (prop_addr, value)
 
	value &= 0x3f;
 

	
 
	if (value == 0) value = 64;	/* demanded by Spec 1.0 */
 

	
 
    }
 

	
 
    /* Add property length to current property pointer */
 

	
 
    return prop_addr + value + 1;
 

	
 
}/* next_property */
 

	
 
/*
 
 * unlink_object
 
 *
 
 * Unlink an object from its parent and siblings.
 
 *
 
 */
 

	
 
static void unlink_object (zword object)
 
{
 
    zword obj_addr;
 
    zword parent_addr;
 
    zword sibling_addr;
 

	
 
    if (object == 0) {
 
	runtime_error (ERR_REMOVE_OBJECT_0);
 
	return;
 
    }
 

	
 
    obj_addr = object_address (object);
 

	
 
    if (h_version <= V3) {
 

	
 
	zbyte parent;
 
	zbyte younger_sibling;
 
	zbyte older_sibling;
 
	zbyte zero = 0;
 

	
 
	/* Get parent of object, and return if no parent */
 

	
 
	obj_addr += O1_PARENT;
 
	LOW_BYTE (obj_addr, parent)
 
	if (!parent)
 
	    return;
 

	
 
	/* Get (older) sibling of object and set both parent and sibling
 
	   pointers to 0 */
 

	
 
	SET_BYTE (obj_addr, zero)
 
	obj_addr += O1_SIBLING - O1_PARENT;
 
	LOW_BYTE (obj_addr, older_sibling)
 
	SET_BYTE (obj_addr, zero)
 

	
 
	/* Get first child of parent (the youngest sibling of the object) */
 

	
 
	parent_addr = object_address (parent) + O1_CHILD;
 
	LOW_BYTE (parent_addr, younger_sibling)
 

	
 
	/* Remove object from the list of siblings */
 

	
 
	if (younger_sibling == object)
 
	    SET_BYTE (parent_addr, older_sibling)
 
	else {
 
	    do {
 
		sibling_addr = object_address (younger_sibling) + O1_SIBLING;
 
		LOW_BYTE (sibling_addr, younger_sibling)
 
	    } while (younger_sibling != object);
 
	    SET_BYTE (sibling_addr, older_sibling)
 
	}
 

	
 
    } else {
 

	
 
	zword parent;
 
	zword younger_sibling;
 
	zword older_sibling;
 
	zword zero = 0;
 

	
 
	/* Get parent of object, and return if no parent */
 

	
 
	obj_addr += O4_PARENT;
 
	LOW_WORD (obj_addr, parent)
 
	if (!parent)
 
	    return;
 

	
 
	/* Get (older) sibling of object and set both parent and sibling
 
	   pointers to 0 */
 

	
 
	SET_WORD (obj_addr, zero)
 
	obj_addr += O4_SIBLING - O4_PARENT;
 
	LOW_WORD (obj_addr, older_sibling)
 
	SET_WORD (obj_addr, zero)
 

	
 
	/* Get first child of parent (the youngest sibling of the object) */
 

	
 
	parent_addr = object_address (parent) + O4_CHILD;
 
	LOW_WORD (parent_addr, younger_sibling)
 

	
 
	/* Remove object from the list of siblings */
 

	
 
	if (younger_sibling == object)
 
	    SET_WORD (parent_addr, older_sibling)
 
	else {
 
	    do {
 
		sibling_addr = object_address (younger_sibling) + O4_SIBLING;
 
		LOW_WORD (sibling_addr, younger_sibling)
 
	    } while (younger_sibling != object);
 
	    SET_WORD (sibling_addr, older_sibling)
 
	}
 

	
 
    }
 

	
 
}/* unlink_object */
 

	
 
/*
 
 * z_clear_attr, clear an object attribute.
 
 *
 
 *	zargs[0] = object
 
 *	zargs[1] = number of attribute to be cleared
 
 *
 
 */
 

	
 
void z_clear_attr (void)
 
{
 
    zword obj_addr;
 
    zbyte value;
 

	
 
    if (story_id == SHERLOCK)
 
	if (zargs[1] == 48)
 
	    return;
 

	
 
    if (zargs[1] > ((h_version <= V3) ? 31 : 47))
 
	runtime_error (ERR_ILL_ATTR);
 

	
 
    /* If we are monitoring attribute assignment display a short note */
 

	
 
    if (f_setup.attribute_assignment) {
 
	stream_mssg_on ();
 
	print_string ("@clear_attr ");
 
	print_object (zargs[0]);
 
	print_string (" ");
 
	print_num (zargs[1]);
 
	stream_mssg_off ();
 
    }
 

	
 
    if (zargs[0] == 0) {
 
	runtime_error (ERR_CLEAR_ATTR_0);
 
	return;
 
    }
 

	
 
    /* Get attribute address */
 

	
 
    obj_addr = object_address (zargs[0]) + zargs[1] / 8;
 

	
 
    /* Clear attribute bit */
 

	
 
    LOW_BYTE (obj_addr, value)
 
    value &= ~(0x80 >> (zargs[1] & 7));
 
    SET_BYTE (obj_addr, value)
 

	
 
}/* z_clear_attr */
 

	
 
/*
 
 * z_jin, branch if the first object is inside the second.
 
 *
 
 *	zargs[0] = first object
 
 *	zargs[1] = second object
 
 *
 
 */
 

	
 
void z_jin (void)
 
{
 
    zword obj_addr;
 

	
 
    /* If we are monitoring object locating display a short note */
 

	
 
    if (f_setup.object_locating) {
 
	stream_mssg_on ();
 
	print_string ("@jin ");
 
	print_object (zargs[0]);
 
	print_string (" ");
 
	print_object (zargs[1]);
 
	stream_mssg_off ();
 
    }
 

	
 
    if (zargs[0] == 0) {
 
	runtime_error (ERR_JIN_0);
 
	branch (0 == zargs[1]);
 
	return;
 
    }
 

	
 
    obj_addr = object_address (zargs[0]);
 

	
 
    if (h_version <= V3) {
 

	
 
	zbyte parent;
 

	
 
	/* Get parent id from object */
 

	
 
	obj_addr += O1_PARENT;
 
	LOW_BYTE (obj_addr, parent)
 

	
 
	/* Branch if the parent is obj2 */
 

	
 
	branch (parent == zargs[1]);
 

	
 
    } else {
 

	
 
	zword parent;
 

	
 
	/* Get parent id from object */
 

	
 
	obj_addr += O4_PARENT;
 
	LOW_WORD (obj_addr, parent)
 

	
 
	/* Branch if the parent is obj2 */
 

	
 
	branch (parent == zargs[1]);
 

	
 
    }
 

	
 
}/* z_jin */
 

	
 
/*
 
 * z_get_child, store the child of an object.
 
 *
 
 *	zargs[0] = object
 
 *
 
 */
 

	
 
void z_get_child (void)
 
{
 
    zword obj_addr;
 

	
 
    /* If we are monitoring object locating display a short note */
 

	
 
    if (f_setup.object_locating) {
 
	stream_mssg_on ();
 
	print_string ("@get_child ");
 
	print_object (zargs[0]);
 
	stream_mssg_off ();
 
    }
 

	
 
    if (zargs[0] == 0) {
 
	runtime_error (ERR_GET_CHILD_0);
 
	store (0);
 
	branch (FALSE);
 
	return;
 
    }
 

	
 
    obj_addr = object_address (zargs[0]);
 

	
 
    if (h_version <= V3) {
 

	
 
	zbyte child;
 

	
 
	/* Get child id from object */
 

	
 
	obj_addr += O1_CHILD;
 
	LOW_BYTE (obj_addr, child)
 

	
 
	/* Store child id and branch */
 

	
 
	store (child);
 
	branch (child);
 

	
 
    } else {
 

	
 
	zword child;
 

	
 
	/* Get child id from object */
 

	
 
	obj_addr += O4_CHILD;
 
	LOW_WORD (obj_addr, child)
 

	
 
	/* Store child id and branch */
 

	
 
	store (child);
 
	branch (child);
 

	
 
    }
 

	
 
}/* z_get_child */
 

	
 
/*
 
 * z_get_next_prop, store the number of the first or next property.
 
 *
 
 *	zargs[0] = object
 
 *	zargs[1] = address of current property (0 gets the first property)
 
 *
 
 */
 

	
 
void z_get_next_prop (void)
 
{
 
    zword prop_addr;
 
    zbyte value;
 
    zbyte mask;
 

	
 
    if (zargs[0] == 0) {
 
	runtime_error (ERR_GET_NEXT_PROP_0);
 
	store (0);
 
	return;
 
    }
 

	
 
    /* Property id is in bottom five (six) bits */
 

	
 
    mask = (h_version <= V3) ? 0x1f : 0x3f;
 

	
 
    /* Load address of first property */
 

	
 
    prop_addr = first_property (zargs[0]);
 

	
 
    if (zargs[1] != 0) {
 

	
 
	/* Scan down the property list */
 

	
 
	do {
 
	    LOW_BYTE (prop_addr, value)
 
	    prop_addr = next_property (prop_addr);
 
	} while ((value & mask) > zargs[1]);
 

	
 
	/* Exit if the property does not exist */
 

	
 
	if ((value & mask) != zargs[1])
 
	    runtime_error (ERR_NO_PROP);
 

	
 
    }
 

	
 
    /* Return the property id */
 

	
 
    LOW_BYTE (prop_addr, value)
 
    store ((zword) (value & mask));
 

	
 
}/* z_get_next_prop */
 

	
 
/*
 
 * z_get_parent, store the parent of an object.
 
 *
 
 *	zargs[0] = object
 
 *
 
 */
 

	
 
void z_get_parent (void)
 
{
 
    zword obj_addr;
 

	
 
    /* If we are monitoring object locating display a short note */
 

	
 
    if (f_setup.object_locating) {
 
	stream_mssg_on ();
 
	print_string ("@get_parent ");
 
	print_object (zargs[0]);
 
	stream_mssg_off ();
 
    }
 

	
 
    if (zargs[0] == 0) {
 
	runtime_error (ERR_GET_PARENT_0);
 
	store (0);
 
	return;
 
    }
 

	
 
    obj_addr = object_address (zargs[0]);
 

	
 
    if (h_version <= V3) {
 

	
 
	zbyte parent;
 

	
 
	/* Get parent id from object */
 

	
 
	obj_addr += O1_PARENT;
 
	LOW_BYTE (obj_addr, parent)
 

	
 
	/* Store parent */
 

	
 
	store (parent);
 

	
 
    } else {
 

	
 
	zword parent;
 

	
 
	/* Get parent id from object */
 

	
 
	obj_addr += O4_PARENT;
 
	LOW_WORD (obj_addr, parent)
 

	
 
	/* Store parent */
 

	
 
	store (parent);
 

	
 
    }
 

	
 
}/* z_get_parent */
 

	
 
/*
 
 * z_get_prop, store the value of an object property.
 
 *
 
 *	zargs[0] = object
 
 *	zargs[1] = number of property to be examined
 
 *
 
 */
 

	
 
void z_get_prop (void)
 
{
 
    zword prop_addr;
 
    zword wprop_val;
 
    zbyte bprop_val;
 
    zbyte value;
 
    zbyte mask;
 

	
 
    if (zargs[0] == 0) {
 
	runtime_error (ERR_GET_PROP_0);
 
	store (0);
 
	return;
 
    }
 

	
 
    /* Property id is in bottom five (six) bits */
 

	
 
    mask = (h_version <= V3) ? 0x1f : 0x3f;
 

	
 
    /* Load address of first property */
 

	
 
    prop_addr = first_property (zargs[0]);
 

	
 
    /* Scan down the property list */
 

	
 
    for (;;) {
 
	LOW_BYTE (prop_addr, value)
 
	if ((value & mask) <= zargs[1])
 
	    break;
 
	prop_addr = next_property (prop_addr);
 
    }
 

	
 
    if ((value & mask) == zargs[1]) {	/* property found */
 

	
 
	/* Load property (byte or word sized) */
 

	
 
	prop_addr++;
 

	
 
	if ((h_version <= V3 && !(value & 0xe0)) || (h_version >= V4 && !(value & 0xc0))) {
 

	
 
	    LOW_BYTE (prop_addr, bprop_val)
 
	    wprop_val = bprop_val;
 

	
 
	} else LOW_WORD (prop_addr, wprop_val)
 

	
 
    } else {	/* property not found */
 

	
 
	/* Load default value */
 

	
 
	prop_addr = h_objects + 2 * (zargs[1] - 1);
 
	LOW_WORD (prop_addr, wprop_val)
 

	
 
    }
 

	
 
    /* Store the property value */
 

	
 
    store (wprop_val);
 

	
 
}/* z_get_prop */
 

	
 
/*
 
 * z_get_prop_addr, store the address of an object property.
 
 *
 
 *	zargs[0] = object
 
 *	zargs[1] = number of property to be examined
 
 *
 
 */
 

	
 
void z_get_prop_addr (void)
 
{
 
    zword prop_addr;
 
    zbyte value;
 
    zbyte mask;
 

	
 
    if (zargs[0] == 0) {
 
	runtime_error (ERR_GET_PROP_ADDR_0);
 
	store (0);
 
	return;
 
    }
 

	
 
    if (story_id == BEYOND_ZORK)
 
	if (zargs[0] > MAX_OBJECT)
 
	    { store (0); return; }
 

	
 
    /* Property id is in bottom five (six) bits */
 

	
 
    mask = (h_version <= V3) ? 0x1f : 0x3f;
 

	
 
    /* Load address of first property */
 

	
 
    prop_addr = first_property (zargs[0]);
 

	
 
    /* Scan down the property list */
 

	
 
    for (;;) {
 
	LOW_BYTE (prop_addr, value)
 
	if ((value & mask) <= zargs[1])
 
	    break;
 
	prop_addr = next_property (prop_addr);
 
    }
 

	
 
    /* Calculate the property address or return zero */
 

	
 
    if ((value & mask) == zargs[1]) {
 

	
 
	if (h_version >= V4 && (value & 0x80))
 
	    prop_addr++;
 
	store ((zword) (prop_addr + 1));
 

	
 
    } else store (0);
 

	
 
}/* z_get_prop_addr */
 

	
 
/*
 
 * z_get_prop_len, store the length of an object property.
 
 *
 
 * 	zargs[0] = address of property to be examined
 
 *
 
 */
 

	
 
void z_get_prop_len (void)
 
{
 
    zword addr;
 
    zbyte value;
 

	
 
    /* Back up the property pointer to the property id */
 

	
 
    addr = zargs[0] - 1;
 
    LOW_BYTE (addr, value)
 

	
 
    /* Calculate length of property */
 

	
 
    if (h_version <= V3)
 
	value = (value >> 5) + 1;
 
    else if (!(value & 0x80))
 
	value = (value >> 6) + 1;
 
    else {
 

	
 
	value &= 0x3f;
 

	
 
	if (value == 0) value = 64;	/* demanded by Spec 1.0 */
 

	
 
    }
 

	
 
    /* Store length of property */
 

	
 
    store (value);
 

	
 
}/* z_get_prop_len */
 

	
 
/*
 
 * z_get_sibling, store the sibling of an object.
 
 *
 
 *	zargs[0] = object
 
 *
 
 */
 

	
 
void z_get_sibling (void)
 
{
 
    zword obj_addr;
 

	
 
    if (zargs[0] == 0) {
 
	runtime_error (ERR_GET_SIBLING_0);
 
	store (0);
 
	branch (FALSE);
 
	return;
 
    }
 

	
 
    obj_addr = object_address (zargs[0]);
 

	
 
    if (h_version <= V3) {
 

	
 
	zbyte sibling;
 

	
 
	/* Get sibling id from object */
 

	
 
	obj_addr += O1_SIBLING;
 
	LOW_BYTE (obj_addr, sibling)
 

	
 
	/* Store sibling and branch */
 

	
 
	store (sibling);
 
	branch (sibling);
 

	
 
    } else {
 

	
 
	zword sibling;
 

	
 
	/* Get sibling id from object */
 

	
 
	obj_addr += O4_SIBLING;
 
	LOW_WORD (obj_addr, sibling)
 

	
 
	/* Store sibling and branch */
 

	
 
	store (sibling);
 
	branch (sibling);
 

	
 
    }
 

	
 
}/* z_get_sibling */
 

	
 
/*
 
 * z_insert_obj, make an object the first child of another object.
 
 *
 
 *	zargs[0] = object to be moved
 
 *	zargs[1] = destination object
 
 *
 
 */
 

	
 
void z_insert_obj (void)
 
{
 
    zword obj1 = zargs[0];
 
    zword obj2 = zargs[1];
 
    zword obj1_addr;
 
    zword obj2_addr;
 

	
 
    /* If we are monitoring object movements display a short note */
 

	
 
    if (f_setup.object_movement) {
 
	stream_mssg_on ();
 
	print_string ("@move_obj ");
 
	print_object (obj1);
 
	print_string (" ");
 
	print_object (obj2);
 
	stream_mssg_off ();
 
    }
 

	
 
    if (obj1 == 0) {
 
	runtime_error (ERR_MOVE_OBJECT_0);
 
	return;
 
    }
 

	
 
    if (obj2 == 0) {
 
	runtime_error (ERR_MOVE_OBJECT_TO_0);
 
	return;
 
    }
 

	
 
    /* Get addresses of both objects */
 

	
 
    obj1_addr = object_address (obj1);
 
    obj2_addr = object_address (obj2);
 

	
 
    /* Remove object 1 from current parent */
 

	
 
    unlink_object (obj1);
 

	
 
    /* Make object 1 first child of object 2 */
 

	
 
    if (h_version <= V3) {
 

	
 
	zbyte child;
 

	
 
	obj1_addr += O1_PARENT;
 
	SET_BYTE (obj1_addr, obj2)
 
	obj2_addr += O1_CHILD;
 
	LOW_BYTE (obj2_addr, child)
 
	SET_BYTE (obj2_addr, obj1)
 
	obj1_addr += O1_SIBLING - O1_PARENT;
 
	SET_BYTE (obj1_addr, child)
 

	
 
    } else {
 

	
 
	zword child;
 

	
 
	obj1_addr += O4_PARENT;
 
	SET_WORD (obj1_addr, obj2)
 
	obj2_addr += O4_CHILD;
 
	LOW_WORD (obj2_addr, child)
 
	SET_WORD (obj2_addr, obj1)
 
	obj1_addr += O4_SIBLING - O4_PARENT;
 
	SET_WORD (obj1_addr, child)
 

	
 
    }
 

	
 
}/* z_insert_obj */
 

	
 
/*
 
 * z_put_prop, set the value of an object property.
 
 *
 
 *	zargs[0] = object
 
 *	zargs[1] = number of property to set
 
 *	zargs[2] = value to set property to
 
 *
 
 */
 

	
 
void z_put_prop (void)
 
{
 
    zword prop_addr;
 
    zword value;
 
    zbyte mask;
 

	
 
    if (zargs[0] == 0) {
 
	runtime_error (ERR_PUT_PROP_0);
 
	return;
 
    }
 

	
 
    /* Property id is in bottom five or six bits */
 

	
 
    mask = (h_version <= V3) ? 0x1f : 0x3f;
 

	
 
    /* Load address of first property */
 

	
 
    prop_addr = first_property (zargs[0]);
 

	
 
    /* Scan down the property list */
 

	
 
    for (;;) {
 
	LOW_BYTE (prop_addr, value)
 
	if ((value & mask) <= zargs[1])
 
	    break;
 
	prop_addr = next_property (prop_addr);
 
    }
 

	
 
    /* Exit if the property does not exist */
 

	
 
    if ((value & mask) != zargs[1])
 
	runtime_error (ERR_NO_PROP);
 

	
 
    /* Store the new property value (byte or word sized) */
 

	
 
    prop_addr++;
 

	
 
    if ((h_version <= V3 && !(value & 0xe0)) || (h_version >= V4 && !(value & 0xc0))) {
 
	zbyte v = zargs[2];
 
	SET_BYTE (prop_addr, v)
 
    } else {
 
	zword v = zargs[2];
 
	SET_WORD (prop_addr, v)
 
    }
 

	
 
}/* z_put_prop */
 

	
 
/*
 
 * z_remove_obj, unlink an object from its parent and siblings.
 
 *
 
 *	zargs[0] = object
 
 *
 
 */
 

	
 
void z_remove_obj (void)
 
{
 

	
 
    /* If we are monitoring object movements display a short note */
 

	
 
    if (f_setup.object_movement) {
 
	stream_mssg_on ();
 
	print_string ("@remove_obj ");
 
	print_object (zargs[0]);
 
	stream_mssg_off ();
 
    }
 

	
 
    /* Call unlink_object to do the job */
 

	
 
    unlink_object (zargs[0]);
 

	
 
}/* z_remove_obj */
 

	
 
/*
 
 * z_set_attr, set an object attribute.
 
 *
 
 *	zargs[0] = object
 
 *	zargs[1] = number of attribute to set
 
 *
 
 */
 

	
 
void z_set_attr (void)
 
{
 
    zword obj_addr;
 
    zbyte value;
 

	
 
    if (story_id == SHERLOCK)
 
	if (zargs[1] == 48)
 
	    return;
 

	
 
    if (zargs[1] > ((h_version <= V3) ? 31 : 47))
 
	runtime_error (ERR_ILL_ATTR);
 

	
 
    /* If we are monitoring attribute assignment display a short note */
 

	
 
    if (f_setup.attribute_assignment) {
 
	stream_mssg_on ();
 
	print_string ("@set_attr ");
 
	print_object (zargs[0]);
 
	print_string (" ");
 
	print_num (zargs[1]);
 
	stream_mssg_off ();
 
    }
 

	
 
    if (zargs[0] == 0) {
 
	runtime_error (ERR_SET_ATTR_0);
 
	return;
 
    }
 

	
 
    /* Get attribute address */
 

	
 
    obj_addr = object_address (zargs[0]) + zargs[1] / 8;
 

	
 
    /* Load attribute byte */
 

	
 
    LOW_BYTE (obj_addr, value)
 

	
 
    /* Set attribute bit */
 

	
 
    value |= 0x80 >> (zargs[1] & 7);
 

	
 
    /* Store attribute byte */
 

	
 
    SET_BYTE (obj_addr, value)
 

	
 
}/* z_set_attr */
 

	
 
/*
 
 * z_test_attr, branch if an object attribute is set.
 
 *
 
 *	zargs[0] = object
 
 *	zargs[1] = number of attribute to test
 
 *
 
 */
 

	
 
void z_test_attr (void)
 
{
 
    zword obj_addr;
 
    zbyte value;
 

	
 
    if (zargs[1] > ((h_version <= V3) ? 31 : 47))
 
	runtime_error (ERR_ILL_ATTR);
 

	
 
    /* If we are monitoring attribute testing display a short note */
 

	
 
    if (f_setup.attribute_testing) {
 
	stream_mssg_on ();
 
	print_string ("@test_attr ");
 
	print_object (zargs[0]);
 
	print_string (" ");
 
	print_num (zargs[1]);
 
	stream_mssg_off ();
 
    }
 

	
 
    if (zargs[0] == 0) {
 
	runtime_error (ERR_TEST_ATTR_0);
 
	branch (FALSE);
 
	return;
 
    }
 

	
 
    /* Get attribute address */
 

	
 
    obj_addr = object_address (zargs[0]) + zargs[1] / 8;
 

	
 
    /* Load attribute byte */
 

	
 
    LOW_BYTE (obj_addr, value)
 

	
 
    /* Test attribute */
 

	
 
    branch (value & (0x80 >> (zargs[1] & 7)));
 

	
 
}/* z_test_attr */
backends/frotz/process.c
Show inline comments
 
new file 100644
 
/* process.c - Interpreter loop and program control
 
 *	Copyright (c) 1995-1997 Stefan Jokisch
 
 *
 
 * This file is part of Frotz.
 
 *
 
 * Frotz is free software; you can redistribute it and/or modify
 
 * it under the terms of the GNU General Public License as published by
 
 * the Free Software Foundation; either version 2 of the License, or
 
 * (at your option) any later version.
 
 *
 
 * Frotz is distributed in the hope that it will be useful,
 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
 * GNU General Public License for more details.
 
 *
 
 * You should have received a copy of the GNU General Public License
 
 * along with this program; if not, write to the Free Software
 
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
 
 */
 

	
 
#include "frotz.h"
 

	
 
#ifdef DJGPP
 
#include "djfrotz.h"
 
#endif
 

	
 

	
 
zword zargs[8];
 
int zargc;
 

	
 
static int finished = 0;
 

	
 
static void __extended__ (void);
 
static void __illegal__ (void);
 

	
 
void (*op0_opcodes[0x10]) (void) = {
 
    z_rtrue,
 
    z_rfalse,
 
    z_print,
 
    z_print_ret,
 
    z_nop,
 
    z_save,
 
    z_restore,
 
    z_restart,
 
    z_ret_popped,
 
    z_catch,
 
    z_quit,
 
    z_new_line,
 
    z_show_status,
 
    z_verify,
 
    __extended__,
 
    z_piracy
 
};
 

	
 
void (*op1_opcodes[0x10]) (void) = {
 
    z_jz,
 
    z_get_sibling,
 
    z_get_child,
 
    z_get_parent,
 
    z_get_prop_len,
 
    z_inc,
 
    z_dec,
 
    z_print_addr,
 
    z_call_s,
 
    z_remove_obj,
 
    z_print_obj,
 
    z_ret,
 
    z_jump,
 
    z_print_paddr,
 
    z_load,
 
    z_call_n
 
};
 

	
 
void (*var_opcodes[0x40]) (void) = {
 
    __illegal__,
 
    z_je,
 
    z_jl,
 
    z_jg,
 
    z_dec_chk,
 
    z_inc_chk,
 
    z_jin,
 
    z_test,
 
    z_or,
 
    z_and,
 
    z_test_attr,
 
    z_set_attr,
 
    z_clear_attr,
 
    z_store,
 
    z_insert_obj,
 
    z_loadw,
 
    z_loadb,
 
    z_get_prop,
 
    z_get_prop_addr,
 
    z_get_next_prop,
 
    z_add,
 
    z_sub,
 
    z_mul,
 
    z_div,
 
    z_mod,
 
    z_call_s,
 
    z_call_n,
 
    z_set_colour,
 
    z_throw,
 
    __illegal__,
 
    __illegal__,
 
    __illegal__,
 
    z_call_s,
 
    z_storew,
 
    z_storeb,
 
    z_put_prop,
 
    z_read,
 
    z_print_char,
 
    z_print_num,
 
    z_random,
 
    z_push,
 
    z_pull,
 
    z_split_window,
 
    z_set_window,
 
    z_call_s,
 
    z_erase_window,
 
    z_erase_line,
 
    z_set_cursor,
 
    z_get_cursor,
 
    z_set_text_style,
 
    z_buffer_mode,
 
    z_output_stream,
 
    z_input_stream,
 
    z_sound_effect,
 
    z_read_char,
 
    z_scan_table,
 
    z_not,
 
    z_call_n,
 
    z_call_n,
 
    z_tokenise,
 
    z_encode_text,
 
    z_copy_table,
 
    z_print_table,
 
    z_check_arg_count
 
};
 

	
 
void (*ext_opcodes[0x1d]) (void) = {
 
    z_save,
 
    z_restore,
 
    z_log_shift,
 
    z_art_shift,
 
    z_set_font,
 
    z_draw_picture,
 
    z_picture_data,
 
    z_erase_picture,
 
    z_set_margins,
 
    z_save_undo,
 
    z_restore_undo,
 
    z_print_unicode,
 
    z_check_unicode,
 
    __illegal__,
 
    __illegal__,
 
    __illegal__,
 
    z_move_window,
 
    z_window_size,
 
    z_window_style,
 
    z_get_wind_prop,
 
    z_scroll_window,
 
    z_pop_stack,
 
    z_read_mouse,
 
    z_mouse_window,
 
    z_push_stack,
 
    z_put_wind_prop,
 
    z_print_form,
 
    z_make_menu,
 
    z_picture_table
 
};
 

	
 

	
 
/*
 
 * init_process
 
 *
 
 * Initialize process variables.
 
 *
 
 */
 

	
 
void init_process (void)
 
{
 
    finished = 0;
 
} /* init_process */
 

	
 

	
 
/*
 
 * load_operand
 
 *
 
 * Load an operand, either a variable or a constant.
 
 *
 
 */
 

	
 
static void load_operand (zbyte type)
 
{
 
    zword value;
 

	
 
    if (type & 2) { 			/* variable */
 

	
 
	zbyte variable;
 

	
 
	CODE_BYTE (variable)
 

	
 
	if (variable == 0)
 
	    value = *sp++;
 
	else if (variable < 16)
 
	    value = *(fp - variable);
 
	else {
 
	    zword addr = h_globals + 2 * (variable - 16);
 
	    LOW_WORD (addr, value)
 
	}
 

	
 
    } else if (type & 1) { 		/* small constant */
 

	
 
	zbyte bvalue;
 

	
 
	CODE_BYTE (bvalue)
 
	value = bvalue;
 

	
 
    } else CODE_WORD (value) 		/* large constant */
 

	
 
    zargs[zargc++] = value;
 

	
 
}/* load_operand */
 

	
 
/*
 
 * load_all_operands
 
 *
 
 * Given the operand specifier byte, load all (up to four) operands
 
 * for a VAR or EXT opcode.
 
 *
 
 */
 

	
 
static void load_all_operands (zbyte specifier)
 
{
 
    int i;
 

	
 
    for (i = 6; i >= 0; i -= 2) {
 

	
 
	zbyte type = (specifier >> i) & 0x03;
 

	
 
	if (type == 3)
 
	    break;
 

	
 
	load_operand (type);
 

	
 
    }
 

	
 
}/* load_all_operands */
 

	
 
/*
 
 * interpret
 
 *
 
 * Z-code interpreter main loop
 
 *
 
 */
 

	
 
void interpret (void)
 
{
 

	
 
    do {
 

	
 
	zbyte opcode;
 

	
 
	CODE_BYTE (opcode)
 

	
 
	zargc = 0;
 

	
 
	if (opcode < 0x80) {			/* 2OP opcodes */
 

	
 
	    load_operand ((zbyte) (opcode & 0x40) ? 2 : 1);
 
	    load_operand ((zbyte) (opcode & 0x20) ? 2 : 1);
 

	
 
	    var_opcodes[opcode & 0x1f] ();
 

	
 
	} else if (opcode < 0xb0) {		/* 1OP opcodes */
 

	
 
	    load_operand ((zbyte) (opcode >> 4));
 

	
 
	    op1_opcodes[opcode & 0x0f] ();
 

	
 
	} else if (opcode < 0xc0) {		/* 0OP opcodes */
 

	
 
	    op0_opcodes[opcode - 0xb0] ();
 

	
 
	} else {				/* VAR opcodes */
 

	
 
	    zbyte specifier1;
 
	    zbyte specifier2;
 

	
 
	    if (opcode == 0xec || opcode == 0xfa) {	/* opcodes 0xec */
 
		CODE_BYTE (specifier1)                  /* and 0xfa are */
 
		CODE_BYTE (specifier2)                  /* call opcodes */
 
		load_all_operands (specifier1);		/* with up to 8 */
 
		load_all_operands (specifier2);         /* arguments    */
 
	    } else {
 
		CODE_BYTE (specifier1)
 
		load_all_operands (specifier1);
 
	    }
 

	
 
	    var_opcodes[opcode - 0xc0] ();
 

	
 
	}
 

	
 
#if defined(DJGPP) && defined(SOUND_SUPPORT)
 
    if (end_of_sound_flag)
 
	end_of_sound ();
 
#endif
 

	
 
    } while (finished == 0);
 

	
 
    finished--;
 

	
 
}/* interpret */
 

	
 
/*
 
 * call
 
 *
 
 * Call a subroutine. Save PC and FP then load new PC and initialise
 
 * new stack frame. Note that the caller may legally provide less or
 
 * more arguments than the function actually has. The call type "ct"
 
 * can be 0 (z_call_s), 1 (z_call_n) or 2 (direct call).
 
 *
 
 */
 

	
 
void call (zword routine, int argc, zword *args, int ct)
 
{
 
    long pc;
 
    zword value;
 
    zbyte count;
 
    int i;
 

	
 
    if (sp - stack < 4)
 
	runtime_error (ERR_STK_OVF);
 

	
 
    GET_PC (pc)
 

	
 
    *--sp = (zword) (pc >> 9);
 
    *--sp = (zword) (pc & 0x1ff);
 
    *--sp = (zword) (fp - stack - 1);
 
    *--sp = (zword) (argc | (ct << (f_setup.save_quetzal ? 12 : 8)));
 

	
 
    fp = sp;
 
    frame_count++;
 

	
 
    /* Calculate byte address of routine */
 

	
 
    if (h_version <= V3)
 
	pc = (long) routine << 1;
 
    else if (h_version <= V5)
 
	pc = (long) routine << 2;
 
    else if (h_version <= V7)
 
	pc = ((long) routine << 2) + ((long) h_functions_offset << 3);
 
    else /* h_version == V8 */
 
	pc = (long) routine << 3;
 

	
 
    if (pc >= story_size)
 
	runtime_error (ERR_ILL_CALL_ADDR);
 

	
 
    SET_PC (pc)
 

	
 
    /* Initialise local variables */
 

	
 
    CODE_BYTE (count)
 

	
 
    if (count > 15)
 
	runtime_error (ERR_CALL_NON_RTN);
 
    if (sp - stack < count)
 
	runtime_error (ERR_STK_OVF);
 

	
 
    if (f_setup.save_quetzal)
 
	fp[0] |= (zword) count << 8;	/* Save local var count for Quetzal. */
 

	
 
    value = 0;
 

	
 
    for (i = 0; i < count; i++) {
 

	
 
	if (h_version <= V4)		/* V1 to V4 games provide default */
 
	    CODE_WORD (value)		/* values for all local variables */
 

	
 
	*--sp = (zword) ((argc-- > 0) ? args[i] : value);
 

	
 
    }
 

	
 
    /* Start main loop for direct calls */
 

	
 
    if (ct == 2)
 
	interpret ();
 

	
 
}/* call */
 

	
 
/*
 
 * ret
 
 *
 
 * Return from the current subroutine and restore the previous stack
 
 * frame. The result may be stored (0), thrown away (1) or pushed on
 
 * the stack (2). In the latter case a direct call has been finished
 
 * and we must exit the interpreter loop.
 
 *
 
 */
 

	
 
void ret (zword value)
 
{
 
    long pc;
 
    int ct;
 

	
 
    if (sp > fp)
 
	runtime_error (ERR_STK_UNDF);
 

	
 
    sp = fp;
 

	
 
    ct = *sp++ >> (f_setup.save_quetzal ? 12 : 8);
 
    frame_count--;
 
    fp = stack + 1 + *sp++;
 
    pc = *sp++;
 
    pc = ((long) *sp++ << 9) | pc;
 

	
 
    SET_PC (pc)
 

	
 
    /* Handle resulting value */
 

	
 
    if (ct == 0)
 
	store (value);
 
    if (ct == 2)
 
	*--sp = value;
 

	
 
    /* Stop main loop for direct calls */
 

	
 
    if (ct == 2)
 
	finished++;
 

	
 
}/* ret */
 

	
 
/*
 
 * branch
 
 *
 
 * Take a jump after an instruction based on the flag, either true or
 
 * false. The branch can be short or long; it is encoded in one or two
 
 * bytes respectively. When bit 7 of the first byte is set, the jump
 
 * takes place if the flag is true; otherwise it is taken if the flag
 
 * is false. When bit 6 of the first byte is set, the branch is short;
 
 * otherwise it is long. The offset occupies the bottom 6 bits of the
 
 * first byte plus all the bits in the second byte for long branches.
 
 * Uniquely, an offset of 0 means return false, and an offset of 1 is
 
 * return true.
 
 *
 
 */
 

	
 
void branch (int flag)
 
{
 
    long pc;
 
    zword offset;
 
    zbyte specifier;
 
    zbyte off1;
 
    zbyte off2;
 

	
 
    CODE_BYTE (specifier)
 

	
 
    off1 = specifier & 0x3f;
 

	
 
    if (!flag)
 
	specifier ^= 0x80;
 

	
 
    if (!(specifier & 0x40)) {		/* it's a long branch */
 

	
 
	if (off1 & 0x20)		/* propagate sign bit */
 
	    off1 |= 0xc0;
 

	
 
	CODE_BYTE (off2)
 

	
 
	offset = (off1 << 8) | off2;
 

	
 
    } else offset = off1;		/* it's a short branch */
 

	
 
    if (specifier & 0x80) {
 

	
 
	if (offset > 1) {		/* normal branch */
 

	
 
	    GET_PC (pc)
 
	    pc += (short) offset - 2;
 
	    SET_PC (pc)
 

	
 
	} else ret (offset);		/* special case, return 0 or 1 */
 
    }
 

	
 
}/* branch */
 

	
 
/*
 
 * store
 
 *
 
 * Store an operand, either as a variable or pushed on the stack.
 
 *
 
 */
 

	
 
void store (zword value)
 
{
 
    zbyte variable;
 

	
 
    CODE_BYTE (variable)
 

	
 
    if (variable == 0)
 
	*--sp = value;
 
    else if (variable < 16)
 
	*(fp - variable) = value;
 
    else {
 
	zword addr = h_globals + 2 * (variable - 16);
 
	SET_WORD (addr, value)
 
    }
 

	
 
}/* store */
 

	
 
/*
 
 * direct_call
 
 *
 
 * Call the interpreter loop directly. This is necessary when
 
 *
 
 * - a sound effect has been finished
 
 * - a read instruction has timed out
 
 * - a newline countdown has hit zero
 
 *
 
 * The interpreter returns the result value on the stack.
 
 *
 
 */
 

	
 
int direct_call (zword addr)
 
{
 
    zword saved_zargs[8];
 
    int saved_zargc;
 
    int i;
 

	
 
    /* Calls to address 0 return false */
 

	
 
    if (addr == 0)
 
	return 0;
 

	
 
    /* Save operands and operand count */
 

	
 
    for (i = 0; i < 8; i++)
 
	saved_zargs[i] = zargs[i];
 

	
 
    saved_zargc = zargc;
 

	
 
    /* Call routine directly */
 

	
 
    call (addr, 0, 0, 2);
 

	
 
    /* Restore operands and operand count */
 

	
 
    for (i = 0; i < 8; i++)
 
	zargs[i] = saved_zargs[i];
 

	
 
    zargc = saved_zargc;
 

	
 
    /* Resulting value lies on top of the stack */
 

	
 
    return (short) *sp++;
 

	
 
}/* direct_call */
 

	
 
/*
 
 * __extended__
 
 *
 
 * Load and execute an extended opcode.
 
 *
 
 */
 

	
 
static void __extended__ (void)
 
{
 
    zbyte opcode;
 
    zbyte specifier;
 

	
 
    CODE_BYTE (opcode)
 
    CODE_BYTE (specifier)
 

	
 
    load_all_operands (specifier);
 

	
 
    if (opcode < 0x1d)			/* extended opcodes from 0x1d on */
 
	ext_opcodes[opcode] ();		/* are reserved for future spec' */
 

	
 
}/* __extended__ */
 

	
 
/*
 
 * __illegal__
 
 *
 
 * Exit game because an unknown opcode has been hit.
 
 *
 
 */
 

	
 
static void __illegal__ (void)
 
{
 

	
 
    runtime_error (ERR_ILL_OPCODE);
 

	
 
}/* __illegal__ */
 

	
 
/*
 
 * z_catch, store the current stack frame for later use with z_throw.
 
 *
 
 *	no zargs used
 
 *
 
 */
 

	
 
void z_catch (void)
 
{
 

	
 
    store (f_setup.save_quetzal ? frame_count : (zword) (fp - stack));
 

	
 
}/* z_catch */
 

	
 
/*
 
 * z_throw, go back to the given stack frame and return the given value.
 
 *
 
 *	zargs[0] = value to return
 
 *	zargs[1] = stack frame
 
 *
 
 */
 

	
 
void z_throw (void)
 
{
 

	
 
    if (f_setup.save_quetzal) {
 
	if (zargs[1] > frame_count)
 
	    runtime_error (ERR_BAD_FRAME);
 

	
 
	/* Unwind the stack a frame at a time. */
 
	for (; frame_count > zargs[1]; --frame_count)
 
	    fp = stack + 1 + fp[1];
 
    } else {
 
	if (zargs[1] > STACK_SIZE)
 
	    runtime_error (ERR_BAD_FRAME);
 

	
 
	fp = stack + zargs[1];
 
    }
 

	
 
    ret (zargs[0]);
 

	
 
}/* z_throw */
 

	
 
/*
 
 * z_call_n, call a subroutine and discard its result.
 
 *
 
 * 	zargs[0] = packed address of subroutine
 
 *	zargs[1] = first argument (optional)
 
 *	...
 
 *	zargs[7] = seventh argument (optional)
 
 *
 
 */
 

	
 
void z_call_n (void)
 
{
 

	
 
    if (zargs[0] != 0)
 
	call (zargs[0], zargc - 1, zargs + 1, 1);
 

	
 
}/* z_call_n */
 

	
 
/*
 
 * z_call_s, call a subroutine and store its result.
 
 *
 
 * 	zargs[0] = packed address of subroutine
 
 *	zargs[1] = first argument (optional)
 
 *	...
 
 *	zargs[7] = seventh argument (optional)
 
 *
 
 */
 

	
 
void z_call_s (void)
 
{
 

	
 
    if (zargs[0] != 0)
 
	call (zargs[0], zargc - 1, zargs + 1, 0);
 
    else
 
	store (0);
 

	
 
}/* z_call_s */
 

	
 
/*
 
 * z_check_arg_count, branch if subroutine was called with >= n arg's.
 
 *
 
 * 	zargs[0] = number of arguments
 
 *
 
 */
 

	
 
void z_check_arg_count (void)
 
{
 

	
 
    if (fp == stack + STACK_SIZE)
 
	branch (zargs[0] == 0);
 
    else
 
	branch (zargs[0] <= (*fp & 0xff));
 

	
 
}/* z_check_arg_count */
 

	
 
/*
 
 * z_jump, jump unconditionally to the given address.
 
 *
 
 *	zargs[0] = PC relative address
 
 *
 
 */
 

	
 
void z_jump (void)
 
{
 
    long pc;
 

	
 
    GET_PC (pc)
 

	
 
    pc += (short) zargs[0] - 2;
 

	
 
    if (pc >= story_size)
 
	runtime_error (ERR_ILL_JUMP_ADDR);
 

	
 
    SET_PC (pc)
 

	
 
}/* z_jump */
 

	
 
/*
 
 * z_nop, no operation.
 
 *
 
 *	no zargs used
 
 *
 
 */
 

	
 
void z_nop (void)
 
{
 

	
 
    /* Do nothing */
 

	
 
}/* z_nop */
 

	
 
/*
 
 * z_quit, stop game and exit interpreter.
 
 *
 
 *	no zargs used
 
 *
 
 */
 

	
 
void z_quit (void)
 
{
 

	
 
    finished = 9999;
 

	
 
}/* z_quit */
 

	
 
/*
 
 * z_ret, return from a subroutine with the given value.
 
 *
 
 *	zargs[0] = value to return
 
 *
 
 */
 

	
 
void z_ret (void)
 
{
 

	
 
    ret (zargs[0]);
 

	
 
}/* z_ret */
 

	
 
/*
 
 * z_ret_popped, return from a subroutine with a value popped off the stack.
 
 *
 
 *	no zargs used
 
 *
 
 */
 

	
 
void z_ret_popped (void)
 
{
 

	
 
    ret (*sp++);
 

	
 
}/* z_ret_popped */
 

	
 
/*
 
 * z_rfalse, return from a subroutine with false (0).
 
 *
 
 * 	no zargs used
 
 *
 
 */
 

	
 
void z_rfalse (void)
 
{
 

	
 
    ret (0);
 

	
 
}/* z_rfalse */
 

	
 
/*
 
 * z_rtrue, return from a subroutine with true (1).
 
 *
 
 * 	no zargs used
 
 *
 
 */
 

	
 
void z_rtrue (void)
 
{
 

	
 
    ret (1);
 

	
 
}/* z_rtrue */
backends/frotz/quetzal.c
Show inline comments
 
new file 100644
 
/* quetzal.c  - Saving and restoring of Quetzal files.
 
 *	Written by Martin Frost <mdf@doc.ic.ac.uk>
 
 *
 
 * This file is part of Frotz.
 
 *
 
 * Frotz is free software; you can redistribute it and/or modify
 
 * it under the terms of the GNU General Public License as published by
 
 * the Free Software Foundation; either version 2 of the License, or
 
 * (at your option) any later version.
 
 *
 
 * Frotz is distributed in the hope that it will be useful,
 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
 * GNU General Public License for more details.
 
 *
 
 * You should have received a copy of the GNU General Public License
 
 * along with this program; if not, write to the Free Software
 
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
 
 */
 

	
 
#include <stdio.h>
 
#include <string.h>
 
#include "frotz.h"
 

	
 
#ifdef MSDOS_16BIT
 

	
 
#include <alloc.h>
 

	
 
#define malloc(size)	farmalloc (size)
 
#define realloc(size,p)	farrealloc (size,p)
 
#define free(size)	farfree (size)
 
#define memcpy(d,s,n)	_fmemcpy (d,s,n)
 

	
 
#else
 

	
 
#include <stdlib.h>
 

	
 
#ifndef SEEK_SET
 
#define SEEK_SET 0
 
#define SEEK_CUR 1
 
#define SEEK_END 2
 
#endif
 

	
 
#define far
 

	
 
#endif
 

	
 
#define get_c fgetc
 
#define put_c fputc
 

	
 
typedef unsigned long zlong;
 

	
 
/*
 
 * This is used only by save_quetzal. It probably should be allocated
 
 * dynamically rather than statically.
 
 */
 

	
 
static zword frames[STACK_SIZE/4+1];
 

	
 
/*
 
 * ID types.
 
 */
 

	
 
#define makeid(a,b,c,d) ((zlong) (((a)<<24) | ((b)<<16) | ((c)<<8) | (d)))
 

	
 
#define ID_FORM makeid ('F','O','R','M')
 
#define ID_IFZS makeid ('I','F','Z','S')
 
#define ID_IFhd makeid ('I','F','h','d')
 
#define ID_UMem makeid ('U','M','e','m')
 
#define ID_CMem makeid ('C','M','e','m')
 
#define ID_Stks makeid ('S','t','k','s')
 
#define ID_ANNO makeid ('A','N','N','O')
 

	
 
/*
 
 * Various parsing states within restoration.
 
 */
 

	
 
#define GOT_HEADER	0x01
 
#define GOT_STACK	0x02
 
#define GOT_MEMORY	0x04
 
#define GOT_NONE	0x00
 
#define GOT_ALL		0x07
 
#define GOT_ERROR	0x80
 

	
 
/*
 
 * Macros used to write the files.
 
 */
 

	
 
#define write_byte(fp,b) (put_c (b, fp) != EOF)
 
#define write_bytx(fp,b) write_byte (fp, (b) & 0xFF)
 
#define write_word(fp,w) \
 
    (write_bytx (fp, (w) >>  8) && write_bytx (fp, (w)))
 
#define write_long(fp,l) \
 
    (write_bytx (fp, (l) >> 24) && write_bytx (fp, (l) >> 16) && \
 
     write_bytx (fp, (l) >>  8) && write_bytx (fp, (l)))
 
#define write_chnk(fp,id,len) \
 
    (write_long (fp, (id))      && write_long (fp, (len)))
 
#define write_run(fp,run) \
 
    (write_byte (fp, 0)         && write_byte (fp, (run)))
 

	
 
/* Read one word from file; return TRUE if OK. */
 
static int read_word (FILE *f, zword *result)
 
{
 
    int a, b;
 

	
 
    if ((a = get_c (f)) == EOF) return FALSE;
 
    if ((b = get_c (f)) == EOF) return FALSE;
 

	
 
    *result = ((zword) a << 8) | (zword) b;
 
    return TRUE;
 
}
 

	
 
/* Read one long from file; return TRUE if OK. */
 
static int read_long (FILE *f, zlong *result)
 
{
 
    int a, b, c, d;
 

	
 
    if ((a = get_c (f)) == EOF) return FALSE;
 
    if ((b = get_c (f)) == EOF) return FALSE;
 
    if ((c = get_c (f)) == EOF) return FALSE;
 
    if ((d = get_c (f)) == EOF) return FALSE;
 

	
 
    *result = ((zlong) a << 24) | ((zlong) b << 16) |
 
	      ((zlong) c <<  8) |  (zlong) d;
 
    return TRUE;
 
}
 

	
 
/*
 
 * Restore a saved game using Quetzal format. Return 2 if OK, 0 if an error
 
 * occurred before any damage was done, -1 on a fatal error.
 
 */
 

	
 
zword restore_quetzal (FILE *svf, FILE *stf)
 
{
 
    zlong ifzslen, currlen, tmpl;
 
    zlong pc;
 
    zword i, tmpw;
 
    zword fatal = 0;	/* Set to -1 when errors must be fatal. */
 
    zbyte skip, progress = GOT_NONE;
 
    int x, y;
 

	
 
    /* Check it's really an `IFZS' file. */
 
    if (!read_long (svf, &tmpl)
 
	|| !read_long (svf, &ifzslen)
 
	|| !read_long (svf, &currlen))				return 0;
 
    if (tmpl != ID_FORM || currlen != ID_IFZS)
 
    {
 
	print_string ("This is not a saved game file!\n");
 
	return 0;
 
    }
 
    if ((ifzslen & 1) || ifzslen<4) /* Sanity checks. */	return 0;
 
    ifzslen -= 4;
 

	
 
    /* Read each chunk and process it. */
 
    while (ifzslen > 0)
 
    {
 
	/* Read chunk header. */
 
	if (ifzslen < 8) /* Couldn't contain a chunk. */	return 0;
 
	if (!read_long (svf, &tmpl)
 
	    || !read_long (svf, &currlen))			return 0;
 
	ifzslen -= 8;	/* Reduce remaining by size of header. */
 

	
 
	/* Handle chunk body. */
 
	if (ifzslen < currlen) /* Chunk goes past EOF?! */	return 0;
 
	skip = currlen & 1;
 
	ifzslen -= currlen + (zlong) skip;
 

	
 
	switch (tmpl)
 
	{
 
	    /* `IFhd' header chunk; must be first in file. */
 
	    case ID_IFhd:
 
		if (progress & GOT_HEADER)
 
		{
 
		    print_string ("Save file has two IFZS chunks!\n");
 
		    return fatal;
 
		}
 
		progress |= GOT_HEADER;
 
		if (currlen < 13
 
		    || !read_word (svf, &tmpw))			return fatal;
 
		if (tmpw != h_release)
 
		    progress = GOT_ERROR;
 

	
 
		for (i=H_SERIAL; i<H_SERIAL+6; ++i)
 
		{
 
		    if ((x = get_c (svf)) == EOF)		return fatal;
 
		    if (x != zmp[i])
 
			progress = GOT_ERROR;
 
		}
 

	
 
		if (!read_word (svf, &tmpw))			return fatal;
 
		if (tmpw != h_checksum)
 
		    progress = GOT_ERROR;
 

	
 
		if (progress & GOT_ERROR)
 
		{
 
		    print_string ("File was not saved from this story!\n");
 
		    return fatal;
 
		}
 
		if ((x = get_c (svf)) == EOF)			return fatal;
 
		pc = (zlong) x << 16;
 
		if ((x = get_c (svf)) == EOF)			return fatal;
 
		pc |= (zlong) x << 8;
 
		if ((x = get_c (svf)) == EOF)			return fatal;
 
		pc |= (zlong) x;
 
		fatal = -1;	/* Setting PC means errors must be fatal. */
 
		SET_PC (pc);
 

	
 
		for (i=13; i<currlen; ++i)
 
		    (void) get_c (svf);	/* Skip rest of chunk. */
 
		break;
 
	    /* `Stks' stacks chunk; restoring this is quite complex. ;) */
 
	    case ID_Stks:
 
		if (progress & GOT_STACK)
 
		{
 
		    print_string ("File contains two stack chunks!\n");
 
		    break;
 
		}
 
		progress |= GOT_STACK;
 

	
 
		fatal = -1;	/* Setting SP means errors must be fatal. */
 
		sp = stack + STACK_SIZE;
 

	
 
		/*
 
		 * All versions other than V6 may use evaluation stack outside
 
		 * any function context. As a result a faked function context
 
		 * will be present in the file here. We skip this context, but
 
		 * load the associated stack onto the stack proper...
 
		 */
 
		if (h_version != V6)
 
		{
 
		    if (currlen < 8)				return fatal;
 
		    for (i=0; i<6; ++i)
 
			if (get_c (svf) != 0)			return fatal;
 
		    if (!read_word (svf, &tmpw))		return fatal;
 
		    if (tmpw > STACK_SIZE)
 
		    {
 
			print_string ("Save-file has too much stack (and I can't cope).\n");
 
			return fatal;
 
		    }
 
		    currlen -= 8;
 
		    if (currlen < tmpw*2)			return fatal;
 
		    for (i=0; i<tmpw; ++i)
 
			if (!read_word (svf, --sp))		return fatal;
 
		    currlen -= tmpw*2;
 
		}
 

	
 
		/* We now proceed to load the main block of stack frames. */
 
		for (fp = stack+STACK_SIZE, frame_count = 0;
 
		     currlen > 0;
 
		     currlen -= 8, ++frame_count)
 
		{
 
		    if (currlen < 8)				return fatal;
 
		    if (sp - stack < 4)	/* No space for frame. */
 
		    {
 
			print_string ("Save-file has too much stack (and I can't cope).\n");
 
			return fatal;
 
		    }
 

	
 
		    /* Read PC, procedure flag and formal param count. */
 
		    if (!read_long (svf, &tmpl))		return fatal;
 
		    y = (int) (tmpl & 0x0F);	/* Number of formals. */
 
		    tmpw = y << 8;
 

	
 
		    /* Read result variable. */
 
		    if ((x = get_c (svf)) == EOF)		return fatal;
 

	
 
		    /* Check the procedure flag... */
 
		    if (tmpl & 0x10)
 
		    {
 
			tmpw |= 0x1000;	/* It's a procedure. */
 
			tmpl >>= 8;	/* Shift to get PC value. */
 
		    }
 
		    else
 
		    {
 
			/* Functions have type 0, so no need to or anything. */
 
			tmpl >>= 8;	/* Shift to get PC value. */
 
			--tmpl;		/* Point at result byte. */
 
			/* Sanity check on result variable... */
 
			if (zmp[tmpl] != (zbyte) x)
 
			{
 
			    print_string ("Save-file has wrong variable number on stack (possibly wrong game version?)\n");
 
			    return fatal;
 
			}
 
		    }
 
		    *--sp = (zword) (tmpl >> 9);	/* High part of PC */
 
		    *--sp = (zword) (tmpl & 0x1FF);	/* Low part of PC */
 
		    *--sp = (zword) (fp - stack - 1);	/* FP */
 

	
 
		    /* Read and process argument mask. */
 
		    if ((x = get_c (svf)) == EOF)		return fatal;
 
		    ++x;	/* Should now be a power of 2 */
 
		    for (i=0; i<8; ++i)
 
			if (x & (1<<i))
 
			    break;
 
		    if (x ^ (1<<i))	/* Not a power of 2 */
 
		    {
 
			print_string ("Save-file uses incomplete argument lists (which I can't handle)\n");
 
			return fatal;
 
		    }
 
		    *--sp = tmpw | i;
 
		    fp = sp;	/* FP for next frame. */
 

	
 
		    /* Read amount of eval stack used. */
 
		    if (!read_word (svf, &tmpw))		return fatal;
 

	
 
		    tmpw += y;	/* Amount of stack + number of locals. */
 
		    if (sp - stack <= tmpw)
 
		    {
 
			print_string ("Save-file has too much stack (and I can't cope).\n");
 
			return fatal;
 
		    }
 
		    if (currlen < tmpw*2)			return fatal;
 
		    for (i=0; i<tmpw; ++i)
 
			if (!read_word (svf, --sp))		return fatal;
 
		    currlen -= tmpw*2;
 
		}
 
		/* End of `Stks' processing... */
 
		break;
 
	    /* Any more special chunk types must go in HERE or ABOVE. */
 
	    /* `CMem' compressed memory chunk; uncompress it. */
 
	    case ID_CMem:
 
		if (!(progress & GOT_MEMORY))	/* Don't complain if two. */
 
		{
 
		    (void) fseek (stf, 0, SEEK_SET);
 
		    i=0;	/* Bytes written to data area. */
 
		    for (; currlen > 0; --currlen)
 
		    {
 
			if ((x = get_c (svf)) == EOF)		return fatal;
 
			if (x == 0)	/* Start run. */
 
			{
 
			    /* Check for bogus run. */
 
			    if (currlen < 2)
 
			    {
 
				print_string ("File contains bogus `CMem' chunk.\n");
 
				for (; currlen > 0; --currlen)
 
				    (void) get_c (svf);	/* Skip rest. */
 
				currlen = 1;
 
				i = 0xFFFF;
 
				break; /* Keep going; may be a `UMem' too. */
 
			    }
 
			    /* Copy story file to memory during the run. */
 
			    --currlen;
 
			    if ((x = get_c (svf)) == EOF)	return fatal;
 
			    for (; x >= 0 && i<h_dynamic_size; --x, ++i)
 
				if ((y = get_c (stf)) == EOF)	return fatal;
 
				else
 
				    zmp[i] = (zbyte) y;
 
			}
 
			else	/* Not a run. */
 
			{
 
			    if ((y = get_c (stf)) == EOF)	return fatal;
 
			    zmp[i] = (zbyte) (x ^ y);
 
			    ++i;
 
			}
 
			/* Make sure we don't load too much. */
 
			if (i > h_dynamic_size)
 
			{
 
			    print_string ("warning: `CMem' chunk too long!\n");
 
			    for (; currlen > 1; --currlen)
 
				(void) get_c (svf);	/* Skip rest. */
 
			    break;	/* Keep going; there may be a `UMem' too. */
 
			}
 
		    }
 
		    /* If chunk is short, assume a run. */
 
		    for (; i<h_dynamic_size; ++i)
 
			if ((y = get_c (stf)) == EOF)		return fatal;
 
			else
 
			    zmp[i] = (zbyte) y;
 
		    if (currlen == 0)
 
			progress |= GOT_MEMORY;	/* Only if succeeded. */
 
		    break;
 
	    }
 
		/* Fall right thru (to default) if already GOT_MEMORY */
 
	    /* `UMem' uncompressed memory chunk; load it. */
 
	    case ID_UMem:
 
		if (!(progress & GOT_MEMORY))	/* Don't complain if two. */
 
		{
 
		    /* Must be exactly the right size. */
 
		    if (currlen == h_dynamic_size)
 
		    {
 
			if (fread (zmp, currlen, 1, svf) == 1)
 
			{
 
			    progress |= GOT_MEMORY;	/* Only on success. */
 
			    break;
 
			}
 
		    }
 
		    else
 
			print_string ("`UMem' chunk wrong size!\n");
 
		    /* Fall into default action (skip chunk) on errors. */
 
		}
 
		/* Fall thru (to default) if already GOT_MEMORY */
 
	    /* Unrecognised chunk type; skip it. */
 
	    default:
 
		(void) fseek (svf, currlen, SEEK_CUR);	/* Skip chunk. */
 
		break;
 
	}
 
	if (skip)
 
	    (void) get_c (svf);	/* Skip pad byte. */
 
    }
 

	
 
    /*
 
     * We've reached the end of the file. For the restoration to have been a
 
     * success, we must have had one of each of the required chunks.
 
     */
 
    if (!(progress & GOT_HEADER))
 
	print_string ("error: no valid header (`IFhd') chunk in file.\n");
 
    if (!(progress & GOT_STACK))
 
	print_string ("error: no valid stack (`Stks') chunk in file.\n");
 
    if (!(progress & GOT_MEMORY))
 
	print_string ("error: no valid memory (`CMem' or `UMem') chunk in file.\n");
 

	
 
    return (progress == GOT_ALL ? 2 : fatal);
 
}
 

	
 
/*
 
 * Save a game using Quetzal format. Return 1 if OK, 0 if failed.
 
 */
 

	
 
zword save_quetzal (FILE *svf, FILE *stf)
 
{
 
    zlong ifzslen = 0, cmemlen = 0, stkslen = 0;
 
    zlong pc;
 
    zword i, j, n;
 
    zword nvars, nargs, nstk, *p;
 
    zbyte var;
 
    long cmempos, stkspos;
 
    int c;
 

	
 
    /* Write `IFZS' header. */
 
    if (!write_chnk (svf, ID_FORM, 0))			return 0;
 
    if (!write_long (svf, ID_IFZS))			return 0;
 

	
 
    /* Write `IFhd' chunk. */
 
    GET_PC (pc);
 
    if (!write_chnk (svf, ID_IFhd, 13))			return 0;
 
    if (!write_word (svf, h_release))			return 0;
 
    for (i=H_SERIAL; i<H_SERIAL+6; ++i)
 
	if (!write_byte (svf, zmp[i]))			return 0;
 
    if (!write_word (svf, h_checksum))			return 0;
 
    if (!write_long (svf, pc << 8)) /* Includes pad. */	return 0;
 

	
 
    /* Write `CMem' chunk. */
 
    if ((cmempos = ftell (svf)) < 0)			return 0;
 
    if (!write_chnk (svf, ID_CMem, 0))			return 0;
 
    (void) fseek (stf, 0, SEEK_SET);
 
    /* j holds current run length. */
 
    for (i=0, j=0, cmemlen=0; i < h_dynamic_size; ++i)
 
    {
 
	if ((c = get_c (stf)) == EOF)			return 0;
 
	c ^= (int) zmp[i];
 
	if (c == 0)
 
	    ++j;	/* It's a run of equal bytes. */
 
	else
 
	{
 
	    /* Write out any run there may be. */
 
	    if (j > 0)
 
	    {
 
		for (; j > 0x100; j -= 0x100)
 
		{
 
		    if (!write_run (svf, 0xFF))		return 0;
 
		    cmemlen += 2;
 
		}
 
		if (!write_run (svf, j-1))		return 0;
 
		cmemlen += 2;
 
		j = 0;
 
	    }
 
	    /* Any runs are now written. Write this (nonzero) byte. */
 
	    if (!write_byte (svf, (zbyte) c))		return 0;
 
	    ++cmemlen;
 
	}
 
    }
 
    /*
 
     * Reached end of dynamic memory. We ignore any unwritten run there may be
 
     * at this point.
 
     */
 
    if (cmemlen & 1)	/* Chunk length must be even. */
 
	if (!write_byte (svf, 0))			return 0;
 

	
 
    /* Write `Stks' chunk. You are not expected to understand this. ;) */
 
    if ((stkspos = ftell (svf)) < 0)			return 0;
 
    if (!write_chnk (svf, ID_Stks, 0))			return 0;
 

	
 
    /*
 
     * We construct a list of frame indices, most recent first, in `frames'.
 
     * These indices are the offsets into the `stack' array of the word before
 
     * the first word pushed in each frame.
 
     */
 
    frames[0] = sp - stack;	/* The frame we'd get by doing a call now. */
 
    for (i = fp - stack + 4, n=0; i < STACK_SIZE+4; i = stack[i-3] + 5)
 
	frames[++n] = i;
 

	
 
    /*
 
     * All versions other than V6 can use evaluation stack outside a function
 
     * context. We write a faked stack frame (most fields zero) to cater for
 
     * this.
 
     */
 
    if (h_version != V6)
 
    {
 
	for (i=0; i<6; ++i)
 
	    if (!write_byte (svf, 0))			return 0;
 
	nstk = STACK_SIZE - frames[n];
 
	if (!write_word (svf, nstk))			return 0;
 
	for (j=STACK_SIZE-1; j >= frames[n]; --j)
 
	    if (!write_word (svf, stack[j]))		return 0;
 
	stkslen = 8 + 2*nstk;
 
    }
 

	
 
    /* Write out the rest of the stack frames. */
 
    for (i=n; i>0; --i)
 
    {
 
	p = stack + frames[i] - 4;	/* Points to call frame. */
 
	nvars = (p[0] & 0x0F00) >> 8;
 
	nargs =  p[0] & 0x00FF;
 
	nstk  =  frames[i] - frames[i-1] - nvars - 4;
 
	pc    =  ((zlong) p[3] << 9) | p[2];
 

	
 
	switch (p[0] & 0xF000)	/* Check type of call. */
 
	{
 
	    case 0x0000:	/* Function. */
 
		var = zmp[pc];
 
		pc = ((pc + 1) << 8) | nvars;
 
		break;
 
	    case 0x1000:	/* Procedure. */
 
		var = 0;
 
		pc = (pc << 8) | 0x10 | nvars;	/* Set procedure flag. */
 
		break;
 
	    /* case 0x2000: */
 
	    default:
 
		runtime_error (ERR_SAVE_IN_INTER);
 
		return 0;
 
	}
 
	if (nargs != 0)
 
	    nargs = (1 << nargs) - 1;	/* Make args into bitmap. */
 

	
 
	/* Write the main part of the frame... */
 
	if (!write_long (svf, pc)
 
	    || !write_byte (svf, var)
 
	    || !write_byte (svf, nargs)
 
	    || !write_word (svf, nstk))			return 0;
 

	
 
	/* Write the variables and eval stack. */
 
	for (j=0, ++p; j<nvars+nstk; ++j, --p)
 
	    if (!write_word (svf, *p))			return 0;
 

	
 
	/* Calculate length written thus far. */
 
	stkslen += 8 + 2 * (nvars + nstk);
 
    }
 

	
 
    /* Fill in variable chunk lengths. */
 
    ifzslen = 3*8 + 4 + 14 + cmemlen + stkslen;
 
    if (cmemlen & 1)
 
	++ifzslen;
 
    (void) fseek (svf,         4, SEEK_SET);
 
    if (!write_long (svf, ifzslen))			return 0;
 
    (void) fseek (svf, cmempos+4, SEEK_SET);
 
    if (!write_long (svf, cmemlen))			return 0;
 
    (void) fseek (svf, stkspos+4, SEEK_SET);
 
    if (!write_long (svf, stkslen))			return 0;
 

	
 
    /* After all that, still nothing went wrong! */
 
    return 1;
 
}
backends/frotz/random.c
Show inline comments
 
new file 100644
 
/* random.c - Z-machine random number generator
 
 *	Copyright (c) 1995-1997 Stefan Jokisch
 
 *
 
 * This file is part of Frotz.
 
 *
 
 * Frotz is free software; you can redistribute it and/or modify
 
 * it under the terms of the GNU General Public License as published by
 
 * the Free Software Foundation; either version 2 of the License, or
 
 * (at your option) any later version.
 
 *
 
 * Frotz is distributed in the hope that it will be useful,
 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
 * GNU General Public License for more details.
 
 *
 
 * You should have received a copy of the GNU General Public License
 
 * along with this program; if not, write to the Free Software
 
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
 
 */
 

	
 
#include "frotz.h"
 

	
 
static long A = 1;
 

	
 
static int interval = 0;
 
static int counter = 0;
 

	
 
/*
 
 * seed_random
 
 *
 
 * Set the seed value for the random number generator.
 
 *
 
 */
 

	
 
void seed_random (int value)
 
{
 

	
 
    if (value == 0) {		/* ask interface for seed value */
 
	A = os_random_seed ();
 
	interval = 0;
 
    } else if (value < 1000) {	/* special seed value */
 
	counter = 0;
 
	interval = value;
 
    } else {			/* standard seed value */
 
	A = value;
 
	interval = 0;
 
    }
 

	
 
}/* seed_random */
 

	
 
/*
 
 * z_random, store a random number or set the random number seed.
 
 *
 
 *	zargs[0] = range (positive) or seed value (negative)
 
 *
 
 */
 

	
 
void z_random ()
 
{
 

	
 
    if ((short) zargs[0] <= 0) {	/* set random seed */
 

	
 
	seed_random (- (short) zargs[0]);
 
	store (0);
 

	
 
    } else {				/* generate random number */
 

	
 
	zword result;
 

	
 
	if (interval != 0) {		/* ...in special mode */
 
	    result = counter++;
 
	    if (counter == interval) counter = 0;
 
	} else {			/* ...in standard mode */
 
	    A = 0x015a4e35L * A + 1;
 
	    result = (A >> 16) & 0x7fff;
 
	}
 

	
 
	store ((zword) (result % zargs[0] + 1));
 

	
 
    }
 

	
 
}/* z_random */
backends/frotz/redirect.c
Show inline comments
 
new file 100644
 
/* redirect.c - Output redirection to Z-machine memory
 
 *	Copyright (c) 1995-1997 Stefan Jokisch
 
 *
 
 * This file is part of Frotz.
 
 *
 
 * Frotz is free software; you can redistribute it and/or modify
 
 * it under the terms of the GNU General Public License as published by
 
 * the Free Software Foundation; either version 2 of the License, or
 
 * (at your option) any later version.
 
 *
 
 * Frotz is distributed in the hope that it will be useful,
 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
 * GNU General Public License for more details.
 
 *
 
 * You should have received a copy of the GNU General Public License
 
 * along with this program; if not, write to the Free Software
 
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
 
 */
 

	
 
#include "frotz.h"
 

	
 
#define MAX_NESTING 16
 

	
 
extern zword get_max_width (zword);
 

	
 
static int depth = -1;
 

	
 
static struct {
 
    zword xsize;
 
    zword table;
 
    zword width;
 
    zword total;
 
} redirect[MAX_NESTING];
 

	
 
/*
 
 * memory_open
 
 *
 
 * Begin output redirection to the memory of the Z-machine.
 
 *
 
 */
 

	
 
void memory_open (zword table, zword xsize, int buffering)
 
{
 

	
 
    if (++depth < MAX_NESTING) {
 

	
 
	if (!buffering)
 
	    xsize = 0xffff;
 
	if (buffering && (short) xsize <= 0)
 
	    xsize = get_max_width ((zword) (- (short) xsize));
 

	
 
	storew (table, 0);
 

	
 
	redirect[depth].table = table;
 
	redirect[depth].width = 0;
 
	redirect[depth].total = 0;
 
	redirect[depth].xsize = xsize;
 

	
 
	ostream_memory = TRUE;
 

	
 
   } else runtime_error (ERR_STR3_NESTING);
 

	
 
}/* memory_open */
 

	
 
/*
 
 * memory_new_line
 
 *
 
 * Redirect a newline to the memory of the Z-machine.
 
 *
 
 */
 

	
 
void memory_new_line (void)
 
{
 
    zword size;
 
    zword addr;
 

	
 
    redirect[depth].total += redirect[depth].width;
 
    redirect[depth].width = 0;
 

	
 
    addr = redirect[depth].table;
 

	
 
    LOW_WORD (addr, size)
 
    addr += 2;
 

	
 
    if (redirect[depth].xsize != 0xffff) {
 

	
 
	redirect[depth].table = addr + size;
 
	size = 0;
 

	
 
    } else storeb ((zword) (addr + (size++)), 13);
 

	
 
    storew (redirect[depth].table, size);
 

	
 
}/* memory_new_line */
 

	
 
/*
 
 * memory_word
 
 *
 
 * Redirect a string of characters to the memory of the Z-machine.
 
 *
 
 */
 

	
 
void memory_word (const zchar *s)
 
{
 
    zword size;
 
    zword addr;
 
    zchar c;
 

	
 
    if (h_version == V6) {
 

	
 
	int width = os_string_width (s);
 

	
 
	if (redirect[depth].xsize != 0xffff)
 

	
 
	    if (redirect[depth].width + width > redirect[depth].xsize) {
 

	
 
		if (*s == ' ' || *s == ZC_INDENT || *s == ZC_GAP)
 
		    width = os_string_width (++s);
 

	
 
		memory_new_line ();
 

	
 
	    }
 

	
 
	redirect[depth].width += width;
 

	
 
    }
 

	
 
    addr = redirect[depth].table;
 

	
 
    LOW_WORD (addr, size)
 
    addr += 2;
 

	
 
    while ((c = *s++) != 0)
 
	storeb ((zword) (addr + (size++)), translate_to_zscii (c));
 

	
 
    storew (redirect[depth].table, size);
 

	
 
}/* memory_word */
 

	
 
/*
 
 * memory_close
 
 *
 
 * End of output redirection.
 
 *
 
 */
 

	
 
void memory_close (void)
 
{
 

	
 
    if (depth >= 0) {
 

	
 
	if (redirect[depth].xsize != 0xffff)
 
	    memory_new_line ();
 

	
 
	if (h_version == V6) {
 

	
 
	    h_line_width = (redirect[depth].xsize != 0xffff) ?
 
		redirect[depth].total : redirect[depth].width;
 

	
 
	    SET_WORD (H_LINE_WIDTH, h_line_width)
 

	
 
	}
 

	
 
	if (depth == 0)
 
	    ostream_memory = FALSE;
 

	
 
	depth--;
 

	
 
    }
 

	
 
}/* memory_close */
backends/frotz/screen.c
Show inline comments
 
new file 100644
 
/* screen.c - Generic screen manipulation
 
 *	Copyright (c) 1995-1997 Stefan Jokisch
 
 *
 
 * This file is part of Frotz.
 
 *
 
 * Frotz is free software; you can redistribute it and/or modify
 
 * it under the terms of the GNU General Public License as published by
 
 * the Free Software Foundation; either version 2 of the License, or
 
 * (at your option) any later version.
 
 *
 
 * Frotz is distributed in the hope that it will be useful,
 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
 * GNU General Public License for more details.
 
 *
 
 * You should have received a copy of the GNU General Public License
 
 * along with this program; if not, write to the Free Software
 
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
 
 */
 

	
 
#include "frotz.h"
 

	
 
extern void set_header_extension (int, zword);
 

	
 
extern int direct_call (zword);
 

	
 
static struct {
 
    enum story story_id;
 
    int pic;
 
    int pic1;
 
    int pic2;
 
} mapper[] = {
 
    { ZORK_ZERO,  5, 497, 498 },
 
    { ZORK_ZERO,  6, 501, 502 },
 
    { ZORK_ZERO,  7, 499, 500 },
 
    { ZORK_ZERO,  8, 503, 504 },
 
    {    ARTHUR, 54, 170, 171 },
 
    {    SHOGUN, 50,  61,  62 },
 
    {   UNKNOWN,  0,   0,   0 }
 
};
 

	
 
static int font_height = 1;
 
static int font_width = 1;
 

	
 
static int input_redraw = FALSE;
 
static int more_prompts = TRUE;
 
static int discarding = FALSE;
 
static int cursor = TRUE;
 

	
 
static int input_window = 0;
 

	
 
static struct {
 
    zword y_pos;
 
    zword x_pos;
 
    zword y_size;
 
    zword x_size;
 
    zword y_cursor;
 
    zword x_cursor;
 
    zword left;
 
    zword right;
 
    zword nl_routine;
 
    zword nl_countdown;
 
    zword style;
 
    zword colour;
 
    zword font;
 
    zword font_size;
 
    zword attribute;
 
    zword line_count;
 
} wp[8], *cwp;
 

	
 

	
 
/*
 
 * winarg0
 
 *
 
 * Return the window number in zargs[0]. In V6 only, -3 refers to the
 
 * current window.
 
 *
 
 */
 

	
 
static zword winarg0 (void)
 
{
 

	
 
    if (h_version == V6 && (short) zargs[0] == -3)
 
	return cwin;
 

	
 
    if (zargs[0] >= ((h_version == V6) ? 8 : 2))
 
	runtime_error (ERR_ILL_WIN);
 

	
 
    return zargs[0];
 

	
 
}/* winarg0 */
 

	
 
/*
 
 * winarg2
 
 *
 
 * Return the (optional) window number in zargs[2]. -3 refers to the
 
 * current window. This optional window number was only used by some
 
 * V6 opcodes: set_cursor, set_margins, set_colour.
 
 *
 
 */
 

	
 
static zword winarg2 (void)
 
{
 

	
 
    if (zargc < 3 || (short) zargs[2] == -3)
 
	return cwin;
 

	
 
    if (zargs[2] >= 8)
 
	runtime_error (ERR_ILL_WIN);
 

	
 
    return zargs[2];
 

	
 
}/* winarg2 */
 

	
 
/*
 
 * update_cursor
 
 *
 
 * Move the hardware cursor to make it match the window properties.
 
 *
 
 */
 

	
 
static void update_cursor (void)
 
{
 

	
 
    os_set_cursor (
 
	cwp->y_pos + cwp->y_cursor - 1,
 
	cwp->x_pos + cwp->x_cursor - 1);
 

	
 
}/* update_cursor */
 

	
 
/*
 
 * reset_cursor
 
 *
 
 * Reset the cursor of a given window to its initial position.
 
 *
 
 */
 

	
 
static void reset_cursor (zword win)
 
{
 
    int lines = 0;
 

	
 
    if (h_version <= V4 && win == 0)
 
	lines = wp[0].y_size / hi (wp[0].font_size) - 1;
 

	
 
    wp[win].y_cursor = hi (wp[0].font_size) * lines + 1;
 
    wp[win].x_cursor = wp[win].left + 1;
 

	
 
    if (win == cwin)
 
	update_cursor ();
 

	
 
}/* reset_cursor */
 

	
 
/*
 
 * set_more_prompts
 
 *
 
 * Turn more prompts on/off.
 
 *
 
 */
 

	
 
void set_more_prompts (int flag)
 
{
 

	
 
    if (flag && !more_prompts)
 
	cwp->line_count = 0;
 

	
 
    more_prompts = flag;
 

	
 
}/* set_more_prompts */
 

	
 
/*
 
 * units_left
 
 *
 
 * Return the #screen units from the cursor to the end of the line.
 
 *
 
 */
 

	
 
static int units_left (void)
 
{
 

	
 
    return cwp->x_size - cwp->right - cwp->x_cursor + 1;
 

	
 
}/* units_left */
 

	
 
/*
 
 * get_max_width
 
 *
 
 * Return maximum width of a line in the given window. This is used in
 
 * connection with the extended output stream #3 call in V6.
 
 *
 
 */
 

	
 
zword get_max_width (zword win)
 
{
 

	
 
    if (h_version == V6) {
 

	
 
	if (win >= 8)
 
	    runtime_error (ERR_ILL_WIN);
 

	
 
	return wp[win].x_size - wp[win].left - wp[win].right;
 

	
 
    } else return 0xffff;
 

	
 
}/* get_max_width */
 

	
 
/*
 
 * countdown
 
 *
 
 * Decrement the newline counter. Call the newline interrupt when the
 
 * counter hits zero. This is a helper function for screen_new_line.
 
 *
 
 */
 

	
 
static void countdown (void)
 
{
 

	
 
    if (cwp->nl_countdown != 0)
 
	if (--cwp->nl_countdown == 0)
 
	    direct_call (cwp->nl_routine);
 

	
 
}/* countdown */
 

	
 
/*
 
 * screen_new_line
 
 *
 
 * Print a newline to the screen.
 
 *
 
 */
 

	
 
void screen_new_line (void)
 
{
 

	
 
    if (discarding) return;
 

	
 
    /* Handle newline interrupts at the start (for most cases) */
 

	
 
    if (h_interpreter_number != INTERP_MSDOS || story_id != ZORK_ZERO || h_release != 393)
 
	countdown ();
 

	
 
    /* Check whether the last input line gets destroyed */
 

	
 
    if (input_window == cwin)
 
	input_redraw = TRUE;
 

	
 
    /* If the cursor has not reached the bottom line, then move it to
 
       the next line; otherwise scroll the window or reset the cursor
 
       to the top left. */
 

	
 
    cwp->x_cursor = cwp->left + 1;
 

	
 
    if (cwp->y_cursor + 2 * font_height - 1 > cwp->y_size)
 

	
 
	if (enable_scrolling) {
 

	
 
	    zword y = cwp->y_pos;
 
	    zword x = cwp->x_pos;
 

	
 
	    os_scroll_area (y,
 
			    x,
 
			    y + cwp->y_size - 1,
 
			    x + cwp->x_size - 1,
 
			    font_height);
 

	
 
	} else cwp->y_cursor = 1;
 

	
 
    else cwp->y_cursor += font_height;
 

	
 
    update_cursor ();
 

	
 
    /* See if we need to print a more prompt (unless the game has set
 
       the line counter to -999 in order to suppress more prompts). */
 

	
 
    if (enable_scrolling && (short) cwp->line_count != -999) {
 

	
 
	zword above = (cwp->y_cursor - 1) / font_height;
 
	zword below = (cwp->y_size - cwp->y_cursor + 1) / font_height;
 

	
 
	cwp->line_count++;
 

	
 
	if ((short) cwp->line_count >= (short) above + below - 1) {
 

	
 
	    if (more_prompts)
 
		os_more_prompt ();
 

	
 
	    cwp->line_count = f_setup.context_lines;
 

	
 
	}
 

	
 
    }
 

	
 
    /* Handle newline interrupts at the end for Zork Zero under DOS */
 

	
 
    if (h_interpreter_number == INTERP_MSDOS && story_id == ZORK_ZERO && h_release == 393)
 
	countdown ();
 

	
 
}/* screen_new_line */
 

	
 
/*
 
 * screen_char
 
 *
 
 * Display a single character on the screen.
 
 *
 
 */
 

	
 
void screen_char (zchar c)
 
{
 
    int width;
 

	
 
    if (discarding) return;
 

	
 
    if (c == ZC_INDENT && cwp->x_cursor != cwp->left + 1)
 
	c = ' ';
 

	
 
    if (units_left () < (width = os_char_width (c))) {
 

	
 
	if (!enable_wrapping)
 
	    { cwp->x_cursor = cwp->x_size - cwp->right; return; }
 

	
 
	screen_new_line ();
 

	
 
    }
 

	
 
    os_display_char (c); cwp->x_cursor += width;
 

	
 
}/* screen_char */
 

	
 
/*
 
 * screen_word
 
 *
 
 * Display a string of characters on the screen. If the word doesn't fit
 
 * then use wrapping or clipping depending on the current setting of the
 
 * enable_wrapping flag.
 
 *
 
 */
 

	
 
void screen_word (const zchar *s)
 
{
 
    int width;
 

	
 
    if (discarding) return;
 

	
 
    if (*s == ZC_INDENT && cwp->x_cursor != cwp->left + 1)
 
	screen_char (*s++);
 

	
 
    if (units_left () < (width = os_string_width (s))) {
 

	
 
	if (!enable_wrapping) {
 

	
 
	    zchar c;
 

	
 
	    while ((c = *s++) != 0)
 

	
 
		if (c == ZC_NEW_FONT || c == ZC_NEW_STYLE) {
 

	
 
		    int arg = (int) *s++;
 

	
 
		    if (c == ZC_NEW_FONT)
 
			os_set_font (arg);
 
		    if (c == ZC_NEW_STYLE)
 
			os_set_text_style (arg);
 

	
 
		} else screen_char (c);
 

	
 
	    return;
 

	
 
	}
 

	
 
	if (*s == ' ' || *s == ZC_INDENT || *s == ZC_GAP)
 
	    width = os_string_width (++s);
 

	
 
#ifdef AMIGA
 
	if (cwin == 0) Justifiable ();
 
#endif
 

	
 
	screen_new_line ();
 

	
 
    }
 

	
 
    os_display_string (s); cwp->x_cursor += width;
 

	
 
}/* screen_word */
 

	
 
/*
 
 * screen_write_input
 
 *
 
 * Display an input line on the screen. This is required during playback.
 
 *
 
 */
 

	
 
void screen_write_input (const zchar *buf, zchar key)
 
{
 
    int width;
 

	
 
    if (units_left () < (width = os_string_width (buf)))
 
	screen_new_line ();
 

	
 
    os_display_string (buf); cwp->x_cursor += width;
 

	
 
    if (key == ZC_RETURN)
 
	screen_new_line ();
 

	
 
}/* screen_write_input */
 

	
 
/*
 
 * screen_erase_input
 
 *
 
 * Remove an input line that has already been printed from the screen
 
 * as if it was deleted by the player. This could be necessary during
 
 * playback.
 
 *
 
 */
 

	
 
void screen_erase_input (const zchar *buf)
 
{
 

	
 
    if (buf[0] != 0) {
 

	
 
	int width = os_string_width (buf);
 

	
 
	zword y;
 
	zword x;
 

	
 
	cwp->x_cursor -= width;
 

	
 
	y = cwp->y_pos + cwp->y_cursor - 1;
 
	x = cwp->x_pos + cwp->x_cursor - 1;
 

	
 
	os_erase_area (y, x, y + font_height - 1, x + width - 1);
 
	os_set_cursor (y, x);
 

	
 
    }
 

	
 
}/* screen_erase_input */
 

	
 
/*
 
 * console_read_input
 
 *
 
 * Read an input line from the keyboard and return the terminating key.
 
 *
 
 */
 

	
 
zchar console_read_input (int max, zchar *buf, zword timeout, int continued)
 
{
 
    zchar key;
 
    int i;
 

	
 
    /* Make sure there is some space for input */
 

	
 
    if (cwin == 0 && units_left () + os_string_width (buf) < 10 * font_width)
 
	screen_new_line ();
 

	
 
    /* Make sure the input line is visible */
 

	
 
    if (continued && input_redraw)
 
	screen_write_input (buf, -1);
 

	
 
    input_window = cwin;
 
    input_redraw = FALSE;
 

	
 
    /* Get input line from IO interface */
 

	
 
    cwp->x_cursor -= os_string_width (buf);
 
    key = os_read_line (max, buf, timeout, units_left (), continued);
 
    cwp->x_cursor += os_string_width (buf);
 

	
 
    if (key != ZC_TIME_OUT)
 
	for (i = 0; i < 8; i++)
 
	    wp[i].line_count = 0;
 

	
 
    /* Add a newline if the input was terminated normally */
 

	
 
    if (key == ZC_RETURN)
 
	screen_new_line ();
 

	
 
    return key;
 

	
 
}/* console_read_input */
 

	
 
/*
 
 * console_read_key
 
 *
 
 * Read a single keystroke and return it.
 
 *
 
 */
 

	
 
zchar console_read_key (zword timeout)
 
{
 
    zchar key;
 
    int i;
 

	
 
    key = os_read_key (timeout, cursor);
 

	
 
    if (key != ZC_TIME_OUT)
 
	for (i = 0; i < 8; i++)
 
	    wp[i].line_count = 0;
 

	
 
    return key;
 

	
 
}/* console_read_key */
 

	
 
/*
 
 * update_attributes
 
 *
 
 * Set the three enable_*** variables to make them match the attributes
 
 * of the current window.
 
 *
 
 */
 

	
 
static void update_attributes (void)
 
{
 
    zword attr = cwp->attribute;
 

	
 
    enable_wrapping = attr & 1;
 
    enable_scrolling = attr & 2;
 
    enable_scripting = attr & 4;
 
    enable_buffering = attr & 8;
 

	
 
    /* Some story files forget to select wrapping for printing hints */
 

	
 
    if (story_id == ZORK_ZERO && h_release == 366)
 
	if (cwin == 0)
 
	    enable_wrapping = TRUE;
 
    if (story_id == SHOGUN && h_release <= 295)
 
	if (cwin == 0)
 
	    enable_wrapping = TRUE;
 

	
 
}/* update_attributes */
 

	
 
/*
 
 * refresh_text_style
 
 *
 
 * Set the right text style. This can be necessary when the fixed font
 
 * flag is changed, or when a new window is selected, or when the game
 
 * uses the set_text_style opcode.
 
 *
 
 */
 

	
 
void refresh_text_style (void)
 
{
 
    zword style;
 

	
 
    if (h_version != V6) {
 

	
 
	style = wp[0].style;
 

	
 
	if (cwin != 0 || h_flags & FIXED_FONT_FLAG)
 
	    style |= FIXED_WIDTH_STYLE;
 

	
 
    } else style = cwp->style;
 

	
 
    if (!ostream_memory && ostream_screen && enable_buffering) {
 

	
 
	print_char (ZC_NEW_STYLE);
 
	print_char (style);
 

	
 
    } else os_set_text_style (style);
 

	
 
}/* refresh_text_style */
 

	
 
/*
 
 * set_window
 
 *
 
 * Set the current window. In V6 every window has its own set of window
 
 * properties such as colours, text style, cursor position and size.
 
 *
 
 */
 

	
 
static void set_window (zword win)
 
{
 

	
 
    flush_buffer ();
 

	
 
    cwin = win; cwp = wp + win;
 

	
 
    update_attributes ();
 

	
 
    if (h_version == V6) {
 

	
 
	os_set_colour (lo (cwp->colour), hi (cwp->colour));
 

	
 
	if (os_font_data (cwp->font, &font_height, &font_width))
 
	    os_set_font (cwp->font);
 

	
 
	os_set_text_style (cwp->style);
 

	
 
    } else refresh_text_style ();
 

	
 
    if (h_version != V6 && win != 0) {
 
	wp[win].y_cursor = 1;
 
	wp[win].x_cursor = 1;
 
    }
 

	
 
    update_cursor ();
 

	
 
}/* set_window */
 

	
 
/*
 
 * erase_window
 
 *
 
 * Erase a window to background colour.
 
 *
 
 */
 

	
 
void erase_window (zword win)
 
{
 
    zword y = wp[win].y_pos;
 
    zword x = wp[win].x_pos;
 

	
 
    if (h_version == V6 && win != cwin && h_interpreter_number != INTERP_AMIGA)
 
	os_set_colour (lo (wp[win].colour), hi (wp[win].colour));
 

	
 
    os_erase_area (y,
 
		   x,
 
		   y + wp[win].y_size - 1,
 
		   x + wp[win].x_size - 1);
 

	
 
    if (h_version == V6 && win != cwin && h_interpreter_number != INTERP_AMIGA)
 
	os_set_colour (lo (cwp->colour), hi (cwp->colour));
 

	
 
    reset_cursor (win);
 

	
 
    wp[win].line_count = 0;
 

	
 
}/* erase_window */
 

	
 
/*
 
 * split_window
 
 *
 
 * Divide the screen into upper (1) and lower (0) windows. In V3 the upper
 
 * window appears below the status line.
 
 *
 
 */
 

	
 
void split_window (zword height)
 
{
 
    zword stat_height = 0;
 

	
 
    flush_buffer ();
 

	
 
    /* Calculate height of status line and upper window */
 

	
 
    if (h_version != V6)
 
	height *= hi (wp[1].font_size);
 

	
 
    if (h_version <= V3)
 
	stat_height = hi (wp[7].font_size);
 

	
 
    /* Cursor of upper window mustn't be swallowed by the lower window */
 

	
 
    wp[1].y_cursor += wp[1].y_pos - 1 - stat_height;
 

	
 
    wp[1].y_pos = 1 + stat_height;
 
    wp[1].y_size = height;
 

	
 
    if ((short) wp[1].y_cursor > (short) wp[1].y_size)
 
	reset_cursor (1);
 

	
 
    /* Cursor of lower window mustn't be swallowed by the upper window */
 

	
 
    wp[0].y_cursor += wp[0].y_pos - 1 - stat_height - height;
 

	
 
    wp[0].y_pos = 1 + stat_height + height;
 
    wp[0].y_size = h_screen_height - stat_height - height;
 

	
 
    if ((short) wp[0].y_cursor < 1)
 
	reset_cursor (0);
 

	
 
    /* Erase the upper window in V3 only */
 

	
 
    if (h_version == V3 && height != 0)
 
	erase_window (1);
 

	
 
}/* split_window */
 

	
 
/*
 
 * erase_screen
 
 *
 
 * Erase the entire screen to background colour.
 
 *
 
 */
 

	
 
static void erase_screen (zword win)
 
{
 
    int i;
 

	
 
    os_erase_area (1, 1, h_screen_height, h_screen_width);
 

	
 
    if ((short) win == -1) {
 
	split_window (0);
 
	set_window (0);
 
	reset_cursor (0);
 
    }
 

	
 
    for (i = 0; i < 8; i++)
 
	wp[i].line_count = 0;
 

	
 
}/* erase_screen */
 

	
 
/* #ifdef AMIGA */
 

	
 
/*
 
 * resize_screen
 
 *
 
 * Try to adapt the window properties to a new screen size.
 
 *
 
 */
 

	
 
void resize_screen (void)
 
{
 

	
 
    if (h_version != V6) {
 

	
 
	wp[0].x_size = h_screen_width;
 
	wp[1].x_size = h_screen_width;
 
	wp[7].x_size = h_screen_width;
 

	
 
	wp[0].y_size = h_screen_height - wp[1].y_size - wp[7].y_size;
 

	
 
    }
 

	
 
}/* resize_screen */
 

	
 
/* #endif */
 

	
 
/*
 
 * restart_screen
 
 *
 
 * Prepare the screen for a new game.
 
 *
 
 */
 

	
 
void restart_screen (void)
 
{
 

	
 
    /* Use default settings */
 

	
 
    os_set_colour (h_default_foreground, h_default_background);
 

	
 
    if (os_font_data (TEXT_FONT, &font_height, &font_width))
 
	os_set_font (TEXT_FONT);
 

	
 
    os_set_text_style (0);
 

	
 
    cursor = TRUE;
 

	
 
    /* Initialise window properties */
 

	
 
    mwin = 1;
 

	
 
    for (cwp = wp; cwp < wp + 8; cwp++) {
 
	cwp->y_pos = 1;
 
	cwp->x_pos = 1;
 
	cwp->y_size = 0;
 
	cwp->x_size = 0;
 
	cwp->y_cursor = 1;
 
	cwp->x_cursor = 1;
 
	cwp->left = 0;
 
	cwp->right = 0;
 
	cwp->nl_routine = 0;
 
	cwp->nl_countdown = 0;
 
	cwp->style = 0;
 
	cwp->colour = (h_default_background << 8) | h_default_foreground;
 
	cwp->font = TEXT_FONT;
 
	cwp->font_size = (font_height << 8) | font_width;
 
	cwp->attribute = 8;
 
    }
 

	
 
    /* Prepare lower/upper windows and status line */
 

	
 
    wp[0].attribute = 15;
 

	
 
    wp[0].left = f_setup.left_margin;
 
    wp[0].right = f_setup.right_margin;
 

	
 
    wp[0].x_size = h_screen_width;
 
    wp[1].x_size = h_screen_width;
 

	
 
    if (h_version <= V3)
 
	wp[7].x_size = h_screen_width;
 

	
 
    os_restart_game (RESTART_WPROP_SET);
 

	
 
    /* Clear the screen, unsplit it and select window 0 */
 

	
 
    erase_screen ((zword) (-1));
 

	
 
}/* restart_screen */
 

	
 
/*
 
 * validate_click
 
 *
 
 * Return false if the last mouse click occured outside the current
 
 * mouse window; otherwise write the mouse arrow coordinates to the
 
 * memory of the header extension table and return true.
 
 *
 
 */
 

	
 
int validate_click (void)
 
{
 

	
 
    if (mwin >= 0) {
 

	
 
	if (mouse_y < wp[mwin].y_pos || mouse_y >= wp[mwin].y_pos + wp[mwin].y_size)
 
	    return FALSE;
 
	if (mouse_x < wp[mwin].x_pos || mouse_x >= wp[mwin].x_pos + wp[mwin].x_size)
 
	    return FALSE;
 

	
 
	hx_mouse_y = mouse_y - wp[mwin].y_pos + 1;
 
	hx_mouse_x = mouse_x - wp[mwin].x_pos + 1;
 

	
 
    } else {
 

	
 
	if (mouse_y < 1 || mouse_y > h_screen_height)
 
	    return FALSE;
 
	if (mouse_x < 1 || mouse_x > h_screen_width)
 
	    return FALSE;
 

	
 
	hx_mouse_y = mouse_y;
 
	hx_mouse_x = mouse_x;
 

	
 
    }
 

	
 
    if (h_version != V6) {
 
	hx_mouse_y = (hx_mouse_y - 1) / h_font_height + 1;
 
	hx_mouse_x = (hx_mouse_x - 1) / h_font_width + 1;
 
    }
 

	
 
    set_header_extension (HX_MOUSE_Y, hx_mouse_y);
 
    set_header_extension (HX_MOUSE_X, hx_mouse_x);
 

	
 
    return TRUE;
 

	
 
}/* validate_click */
 

	
 
/*
 
 * screen_mssg_on
 
 *
 
 * Start printing a so-called debugging message. The contents of the
 
 * message are passed to the message stream, a Frotz specific output
 
 * stream with maximum priority.
 
 *
 
 */
 

	
 
void screen_mssg_on (void)
 
{
 

	
 
    if (cwin == 0) {		/* messages in window 0 only */
 

	
 
	os_set_text_style (0);
 

	
 
	if (cwp->x_cursor != cwp->left + 1)
 
	    screen_new_line ();
 

	
 
	screen_char (ZC_INDENT);
 

	
 
    } else discarding = TRUE; 	/* discard messages in other windows */
 

	
 
}/* screen_mssg_on */
 

	
 
/*
 
 * screen_mssg_off
 
 *
 
 * Stop printing a "debugging" message.
 
 *
 
 */
 

	
 
void screen_mssg_off (void)
 
{
 

	
 
    if (cwin == 0) {		/* messages in window 0 only */
 

	
 
	screen_new_line ();
 

	
 
	refresh_text_style ();
 

	
 
    } else discarding = FALSE; 	/* message has been discarded */
 

	
 
}/* screen_mssg_off */
 

	
 
/*
 
 * z_buffer_mode, turn text buffering on/off.
 
 *
 
 *	zargs[0] = new text buffering flag (0 or 1)
 
 *
 
 */
 

	
 
void z_buffer_mode (void)
 
{
 

	
 
    /* Infocom's V6 games rarely use the buffer_mode opcode. If they do
 
       then only to print text immediately, without any delay. This was
 
       used to give the player some sign of life while the game was
 
       spending much time on parsing a complicated input line. (To turn
 
       off word wrapping, V6 games use the window_style opcode instead.)
 
       Today we can afford to ignore buffer_mode in V6. */
 

	
 
    if (h_version != V6) {
 

	
 
	flush_buffer ();
 

	
 
	wp[0].attribute &= ~8;
 

	
 
	if (zargs[0] != 0)
 
	    wp[0].attribute |= 8;
 

	
 
	update_attributes ();
 

	
 
    }
 

	
 
}/* z_buffer_mode */
 

	
 
/*
 
 * z_draw_picture, draw a picture.
 
 *
 
 *	zargs[0] = number of picture to draw
 
 *	zargs[1] = y-coordinate of top left corner
 
 *	zargs[2] = x-coordinate of top left corner
 
 *
 
 */
 

	
 
void z_draw_picture (void)
 
{
 
    zword pic = zargs[0];
 

	
 
    zword y = zargs[1];
 
    zword x = zargs[2];
 

	
 
    int i;
 

	
 
    flush_buffer ();
 

	
 
    if (y == 0)			/* use cursor line if y-coordinate is 0 */
 
	y = cwp->y_cursor;
 
    if (x == 0)    		/* use cursor column if x-coordinate is 0 */
 
	x = cwp->x_cursor;
 

	
 
    y += cwp->y_pos - 1;
 
    x += cwp->x_pos - 1;
 

	
 
    /* The following is necessary to make Amiga and Macintosh story
 
       files work with MCGA graphics files.  Some screen-filling
 
       pictures of the original Amiga release like the borders of
 
       Zork Zero were split into several MCGA pictures (left, right
 
       and top borders).  We pretend this has not happened. */
 

	
 
    for (i = 0; mapper[i].story_id != UNKNOWN; i++)
 

	
 
	if (story_id == mapper[i].story_id && pic == mapper[i].pic) {
 

	
 
	    int height1, width1;
 
	    int height2, width2;
 

	
 
	    int delta = 0;
 

	
 
	    os_picture_data (pic, &height1, &width1);
 
	    os_picture_data (mapper[i].pic2, &height2, &width2);
 

	
 
	    if (story_id == ARTHUR && pic == 54)
 
		delta = h_screen_width / 160;
 

	
 
	    os_draw_picture (mapper[i].pic1, y + height1, x + delta);
 
	    os_draw_picture (mapper[i].pic2, y + height1, x + width1 - width2 - delta);
 

	
 
	}
 

	
 
    os_draw_picture (pic, y, x);
 

	
 
    if (story_id == SHOGUN)
 

	
 
	if (pic == 3) {
 

	
 
	    int height, width;
 

	
 
	    os_picture_data (59, &height, &width);
 
	    os_draw_picture (59, y, h_screen_width - width + 1);
 

	
 
	}
 

	
 
}/* z_draw_picture */
 

	
 
/*
 
 * z_erase_line, erase the line starting at the cursor position.
 
 *
 
 *	zargs[0] = 1 + #units to erase (1 clears to the end of the line)
 
 *
 
 */
 

	
 
void z_erase_line (void)
 
{
 
    zword pixels = zargs[0];
 
    zword y, x;
 

	
 
    flush_buffer ();
 

	
 
    /* Clipping at the right margin of the current window */
 

	
 
    if (--pixels == 0 || pixels > units_left ())
 
	pixels = units_left ();
 

	
 
    /* Erase from cursor position */
 

	
 
    y = cwp->y_pos + cwp->y_cursor - 1;
 
    x = cwp->x_pos + cwp->x_cursor - 1;
 

	
 
    os_erase_area (y, x, y + font_height - 1, x + pixels - 1);
 

	
 
}/* z_erase_line */
 

	
 
/*
 
 * z_erase_picture, erase a picture with background colour.
 
 *
 
 *	zargs[0] = number of picture to erase
 
 *	zargs[1] = y-coordinate of top left corner (optional)
 
 *	zargs[2] = x-coordinate of top left corner (optional)
 
 *
 
 */
 

	
 
void z_erase_picture (void)
 
{
 
    int height, width;
 

	
 
    zword y = zargs[1];
 
    zword x = zargs[2];
 

	
 
    flush_buffer ();
 

	
 
    if (y == 0)		/* use cursor line if y-coordinate is 0 */
 
	y = cwp->y_cursor;
 
    if (x == 0)    	/* use cursor column if x-coordinate is 0 */
 
	x = cwp->x_cursor;
 

	
 
    os_picture_data (zargs[0], &height, &width);
 

	
 
    y += cwp->y_pos - 1;
 
    x += cwp->x_pos - 1;
 

	
 
    os_erase_area (y, x, y + height - 1, x + width - 1);
 

	
 
}/* z_erase_picture */
 

	
 
/*
 
 * z_erase_window, erase a window or the screen to background colour.
 
 *
 
 *	zargs[0] = window (-3 current, -2 screen, -1 screen & unsplit)
 
 *
 
 */
 

	
 
void z_erase_window (void)
 
{
 

	
 
    flush_buffer ();
 

	
 
    if ((short) zargs[0] == -1 || (short) zargs[0] == -2)
 
	erase_screen (zargs[0]);
 
    else
 
	erase_window (winarg0 ());
 

	
 
}/* z_erase_window */
 

	
 
/*
 
 * z_get_cursor, write the cursor coordinates into a table.
 
 *
 
 *	zargs[0] = address to write information to
 
 *
 
 */
 

	
 
void z_get_cursor (void)
 
{
 
    zword y, x;
 

	
 
    flush_buffer ();
 

	
 
    y = cwp->y_cursor;
 
    x = cwp->x_cursor;
 

	
 
    if (h_version != V6) {	/* convert to grid positions */
 
	y = (y - 1) / h_font_height + 1;
 
	x = (x - 1) / h_font_width + 1;
 
    }
 

	
 
    storew ((zword) (zargs[0] + 0), y);
 
    storew ((zword) (zargs[0] + 2), x);
 

	
 
}/* z_get_cursor */
 

	
 
/*
 
 * z_get_wind_prop, store the value of a window property.
 
 *
 
 *	zargs[0] = window (-3 is the current one)
 
 *	zargs[1] = number of window property to be stored
 
 *
 
 */
 

	
 
void z_get_wind_prop (void)
 
{
 

	
 
    flush_buffer ();
 

	
 
    if (zargs[1] >= 16)
 
	runtime_error (ERR_ILL_WIN_PROP);
 

	
 
    store (((zword *) (wp + winarg0 ())) [zargs[1]]);
 

	
 
}/* z_get_wind_prop */
 

	
 
/*
 
 * z_mouse_window, select a window as mouse window.
 
 *
 
 *	zargs[0] = window number (-3 is the current) or -1 for the screen
 
 *
 
 */
 

	
 
void z_mouse_window (void)
 
{
 

	
 
    mwin = ((short) zargs[0] == -1) ? -1 : winarg0 ();
 

	
 
}/* z_mouse_window */
 

	
 
/*
 
 * z_move_window, place a window on the screen.
 
 *
 
 *	zargs[0] = window (-3 is the current one)
 
 *	zargs[1] = y-coordinate
 
 *	zargs[2] = x-coordinate
 
 *
 
 */
 

	
 
void z_move_window (void)
 
{
 
    zword win = winarg0 ();
 

	
 
    flush_buffer ();
 

	
 
    wp[win].y_pos = zargs[1];
 
    wp[win].x_pos = zargs[2];
 

	
 
    if (win == cwin)
 
	update_cursor ();
 

	
 
}/* z_move_window */
 

	
 
/*
 
 * z_picture_data, get information on a picture or the graphics file.
 
 *
 
 *	zargs[0] = number of picture or 0 for the graphics file
 
 *	zargs[1] = address to write information to
 
 *
 
 */
 

	
 
void z_picture_data (void)
 
{
 
    zword pic = zargs[0];
 
    zword table = zargs[1];
 

	
 
    int height, width;
 
    int i;
 

	
 
    int avail = os_picture_data (pic, &height, &width);
 

	
 
    for (i = 0; mapper[i].story_id != UNKNOWN; i++)
 

	
 
	if (story_id == mapper[i].story_id) {
 

	
 
	    if (pic == mapper[i].pic) {
 

	
 
		int height2, width2;
 

	
 
		avail &= os_picture_data (mapper[i].pic1, &height2, &width2);
 
		avail &= os_picture_data (mapper[i].pic2, &height2, &width2);
 

	
 
		height += height2;
 

	
 
	    } else if (pic == mapper[i].pic1 || pic == mapper[i].pic2)
 

	
 
		avail = FALSE;
 
	}
 

	
 
    storew ((zword) (table + 0), (zword) (height));
 
    storew ((zword) (table + 2), (zword) (width));
 

	
 
    branch (avail);
 

	
 
}/* z_picture_data */
 

	
 
/*
 
 * z_picture_table, prepare a group of pictures for faster display.
 
 *
 
 *	zargs[0] = address of table holding the picture numbers
 
 *
 
 */
 

	
 
void z_picture_table (void)
 
{
 

	
 
    /* This opcode is used by Shogun and Zork Zero when the player
 
       encounters built-in games such as Peggleboz. Nowadays it is
 
       not very helpful to hold the picture data in memory because
 
       even a small disk cache avoids re-loading of data. */
 

	
 
}/* z_picture_table */
 

	
 
/*
 
 * z_print_table, print ASCII text in a rectangular area.
 
 *
 
 *	zargs[0] = address of text to be printed
 
 *	zargs[1] = width of rectangular area
 
 *	zargs[2] = height of rectangular area (optional)
 
 *	zargs[3] = number of char's to skip between lines (optional)
 
 *
 
 */
 

	
 
void z_print_table (void)
 
{
 
    zword addr = zargs[0];
 
    zword x;
 
    int i, j;
 

	
 
    flush_buffer ();
 

	
 
    /* Supply default arguments */
 

	
 
    if (zargc < 3)
 
	zargs[2] = 1;
 
    if (zargc < 4)
 
	zargs[3] = 0;
 

	
 
    /* Write text in width x height rectangle */
 

	
 
    x = cwp->x_cursor;
 

	
 
    for (i = 0; i < zargs[2]; i++) {
 

	
 
	if (i != 0) {
 

	
 
	    flush_buffer ();
 

	
 
	    cwp->y_cursor += font_height;
 
	    cwp->x_cursor = x;
 

	
 
	    update_cursor ();
 

	
 
	}
 

	
 
	for (j = 0; j < zargs[1]; j++) {
 

	
 
	    zbyte c;
 

	
 
	    LOW_BYTE (addr, c)
 
	    addr++;
 

	
 
	    print_char (c);
 

	
 
	}
 

	
 
	addr += zargs[3];
 

	
 
    }
 

	
 
}/* z_print_table */
 

	
 
/*
 
 * z_put_wind_prop, set the value of a window property.
 
 *
 
 *	zargs[0] = window (-3 is the current one)
 
 *	zargs[1] = number of window property to set
 
 *	zargs[2] = value to set window property to
 
 *
 
 */
 

	
 
void z_put_wind_prop (void)
 
{
 

	
 
    flush_buffer ();
 

	
 
    if (zargs[1] >= 16)
 
	runtime_error (ERR_ILL_WIN_PROP);
 

	
 
    ((zword *) (wp + winarg0 ())) [zargs[1]] = zargs[2];
 

	
 
}/* z_put_wind_prop */
 

	
 
/*
 
 * z_scroll_window, scroll a window up or down.
 
 *
 
 *	zargs[0] = window (-3 is the current one)
 
 *	zargs[1] = #screen units to scroll up (positive) or down (negative)
 
 *
 
 */
 

	
 
void z_scroll_window (void)
 
{
 
    zword win = winarg0 ();
 
    zword y, x;
 

	
 
    flush_buffer ();
 

	
 
    /* Use the correct set of colours when scrolling the window */
 

	
 
    if (win != cwin && h_interpreter_number != INTERP_AMIGA)
 
	os_set_colour (lo (wp[win].colour), hi (wp[win].colour));
 

	
 
    y = wp[win].y_pos;
 
    x = wp[win].x_pos;
 

	
 
    os_scroll_area (y,
 
		    x,
 
		    y + wp[win].y_size - 1,
 
		    x + wp[win].x_size - 1,
 
		    (short) zargs[1]);
 

	
 
    if (win != cwin && h_interpreter_number != INTERP_AMIGA)
 
	os_set_colour (lo (cwp->colour), hi (cwp->colour));
 

	
 
}/* z_scroll_window */
 

	
 
/*
 
 * z_set_colour, set the foreground and background colours.
 
 *
 
 *	zargs[0] = foreground colour
 
 *	zargs[1] = background colour
 
 *	zargs[2] = window (-3 is the current one, optional)
 
 *
 
 */
 

	
 
void z_set_colour (void)
 
{
 
    zword win = (h_version == V6) ? winarg2 () : 0;
 

	
 
    zword fg = zargs[0];
 
    zword bg = zargs[1];
 

	
 
    flush_buffer ();
 

	
 
    if ((short) fg == -1)	/* colour -1 is the colour at the cursor */
 
	fg = os_peek_colour ();
 
    if ((short) bg == -1)
 
	bg = os_peek_colour ();
 

	
 
    if (fg == 0)		/* colour 0 means keep current colour */
 
	fg = lo (wp[win].colour);
 
    if (bg == 0)
 
	bg = hi (wp[win].colour);
 

	
 
    if (fg == 1)		/* colour 1 is the system default colour */
 
	fg = h_default_foreground;
 
    if (bg == 1)
 
	bg = h_default_background;
 

	
 
    if (h_version == V6 && h_interpreter_number == INTERP_AMIGA)
 

	
 
	/* Changing colours of window 0 affects the entire screen */
 

	
 
	if (win == 0) {
 

	
 
	    int i;
 

	
 
	    for (i = 1; i < 8; i++) {
 

	
 
		zword bg2 = hi (wp[i].colour);
 
		zword fg2 = lo (wp[i].colour);
 

	
 
		if (bg2 < 16)
 
		    bg2 = (bg2 == lo (wp[0].colour)) ? fg : bg;
 
		if (fg2 < 16)
 
		    fg2 = (fg2 == lo (wp[0].colour)) ? fg : bg;
 

	
 
		wp[i].colour = (bg2 << 8) | fg2;
 

	
 
	    }
 

	
 
	}
 

	
 
    wp[win].colour = (bg << 8) | fg;
 

	
 
    if (win == cwin || h_version != V6)
 
	os_set_colour (fg, bg);
 

	
 
}/* z_set_colour */
 

	
 
/*
 
 * z_set_font, set the font for text output and store the previous font.
 
 *
 
 * 	zargs[0] = number of font or 0 to keep current font
 
 *
 
 */
 

	
 
void z_set_font (void)
 
{
 
    zword win = (h_version == V6) ? cwin : 0;
 
    zword font = zargs[0];
 

	
 
    if (font != 0) {
 

	
 
	if (os_font_data (font, &font_height, &font_width)) {
 

	
 
	    store (wp[win].font);
 

	
 
	    wp[win].font = font;
 
	    wp[win].font_size = (font_height << 8) | font_width;
 

	
 
	    if (!ostream_memory && ostream_screen && enable_buffering) {
 

	
 
		print_char (ZC_NEW_FONT);
 
		print_char (font);
 

	
 
	    } else os_set_font (font);
 

	
 
	} else store (0);
 

	
 
    } else store (wp[win].font);
 

	
 
}/* z_set_font */
 

	
 
/*
 
 * z_set_cursor, set the cursor position or turn the cursor on/off.
 
 *
 
 *	zargs[0] = y-coordinate or -2/-1 for cursor on/off
 
 *	zargs[1] = x-coordinate
 
 *	zargs[2] = window (-3 is the current one, optional)
 
 *
 
 */
 

	
 
void z_set_cursor (void)
 
{
 
    zword win = (h_version == V6) ? winarg2 () : 1;
 

	
 
    zword y = zargs[0];
 
    zword x = zargs[1];
 

	
 
    flush_buffer ();
 

	
 
    /* Supply default arguments */
 

	
 
    if (zargc < 3)
 
	zargs[2] = -3;
 

	
 
    /* Handle cursor on/off */
 

	
 
    if ((short) y < 0) {
 

	
 
	if ((short) y == -2)
 
	    cursor = TRUE;
 
	if ((short) y == -1)
 
	    cursor = FALSE;
 

	
 
	return;
 

	
 
    }
 

	
 
    /* Convert grid positions to screen units if this is not V6 */
 

	
 
    if (h_version != V6) {
 

	
 
	if (cwin == 0)
 
	    return;
 

	
 
	y = (y - 1) * h_font_height + 1;
 
	x = (x - 1) * h_font_width + 1;
 

	
 
    }
 

	
 
    /* Protect the margins */
 

	
 
    if (y == 0)			/* use cursor line if y-coordinate is 0 */
 
	y = wp[win].y_cursor;
 
    if (x == 0)			/* use cursor column if x-coordinate is 0 */
 
	x = wp[win].x_cursor;
 
    if (x <= wp[win].left || x > wp[win].x_size - wp[win].right)
 
	x = wp[win].left + 1;
 

	
 
    /* Move the cursor */
 

	
 
    wp[win].y_cursor = y;
 
    wp[win].x_cursor = x;
 

	
 
    if (win == cwin)
 
	update_cursor ();
 

	
 
}/* z_set_cursor */
 

	
 
/*
 
 * z_set_margins, set the left and right margins of a window.
 
 *
 
 *	zargs[0] = left margin in pixels
 
 *	zargs[1] = right margin in pixels
 
 *	zargs[2] = window (-3 is the current one, optional)
 
 *
 
 */
 

	
 
void z_set_margins (void)
 
{
 
    zword win = winarg2 ();
 

	
 
    flush_buffer ();
 

	
 
    wp[win].left = zargs[0];
 
    wp[win].right = zargs[1];
 

	
 
    /* Protect the margins */
 

	
 
    if (wp[win].x_cursor <= zargs[0] || wp[win].x_cursor > wp[win].x_size - zargs[1]) {
 

	
 
	wp[win].x_cursor = zargs[0] + 1;
 

	
 
	if (win == cwin)
 
	    update_cursor ();
 

	
 
    }
 

	
 
}/* z_set_margins */
 

	
 
/*
 
 * z_set_text_style, set the style for text output.
 
 *
 
 * 	zargs[0] = style flags to set or 0 to reset text style
 
 *
 
 */
 

	
 
void z_set_text_style (void)
 
{
 
    zword win = (h_version == V6) ? cwin : 0;
 
    zword style = zargs[0];
 

	
 
    wp[win].style |= style;
 

	
 
    if (style == 0)
 
	wp[win].style = 0;
 

	
 
    refresh_text_style ();
 

	
 
}/* z_set_text_style */
 

	
 
/*
 
 * z_set_window, select the current window.
 
 *
 
 *	zargs[0] = window to be selected (-3 is the current one)
 
 *
 
 */
 

	
 
void z_set_window (void)
 
{
 

	
 
    set_window (winarg0 ());
 

	
 
}/* z_set_window */
 

	
 
/*
 
 * pad_status_line
 
 *
 
 * Pad the status line with spaces up to the given position.
 
 *
 
 */
 

	
 
static void pad_status_line (int column)
 
{
 
    int spaces;
 

	
 
    flush_buffer ();
 

	
 
    spaces = units_left () / os_char_width (' ') - column;
 

	
 
    /* while (spaces--) */
 
    /* Justin Wesley's fix for narrow displays (Agenda PDA) */
 
    while (spaces-- > 0)
 
	screen_char (' ');
 

	
 
}/* pad_status_line */
 

	
 
/*
 
 * z_show_status, display the status line for V1 to V3 games.
 
 *
 
 *	no zargs used
 
 *
 
 */
 

	
 
void z_show_status (void)
 
{
 
    zword global0;
 
    zword global1;
 
    zword global2;
 
    zword addr;
 

	
 
    int brief = FALSE;
 

	
 
    /* One V5 game (Wishbringer Solid Gold) contains this opcode by
 
       accident, so just return if the version number does not fit */
 

	
 
    if (h_version >= V4)
 
	return;
 

	
 
    /* Read all relevant global variables from the memory of the
 
       Z-machine into local variables */
 

	
 
    addr = h_globals;
 
    LOW_WORD (addr, global0)
 
    addr += 2;
 
    LOW_WORD (addr, global1)
 
    addr += 2;
 
    LOW_WORD (addr, global2)
 

	
 
    /* Frotz uses window 7 for the status line. Don't forget to select
 
       reverse and fixed width text style */
 

	
 
    set_window (7);
 

	
 
    print_char (ZC_NEW_STYLE);
 
    print_char (REVERSE_STYLE | FIXED_WIDTH_STYLE);
 

	
 
    /* If the screen width is below 55 characters then we have to use
 
       the brief status line format */
 

	
 
    if (h_screen_cols < 55)
 
	brief = TRUE;
 

	
 
    /* Print the object description for the global variable 0 */
 

	
 
    print_char (' ');
 
    print_object (global0);
 

	
 
    /* A header flag tells us whether we have to display the current
 
       time or the score/moves information */
 

	
 
    if (h_config & CONFIG_TIME) {	/* print hours and minutes */
 

	
 
	zword hours = (global1 + 11) % 12 + 1;
 

	
 
	pad_status_line (brief ? 15 : 20);
 

	
 
	print_string ("Time: ");
 

	
 
	if (hours < 10)
 
	    print_char (' ');
 
	print_num (hours);
 

	
 
	print_char (':');
 

	
 
	if (global2 < 10)
 
	    print_char ('0');
 
	print_num (global2);
 

	
 
	print_char (' ');
 

	
 
	print_char ((global1 >= 12) ? 'p' : 'a');
 
	print_char ('m');
 

	
 
    } else {				/* print score and moves */
 

	
 
	pad_status_line (brief ? 15 : 30);
 

	
 
	print_string (brief ? "S: " : "Score: ");
 
	print_num (global1);
 

	
 
	pad_status_line (brief ? 8 : 14);
 

	
 
	print_string (brief ? "M: " : "Moves: ");
 
	print_num (global2);
 

	
 
    }
 

	
 
    /* Pad the end of the status line with spaces */
 

	
 
    pad_status_line (0);
 

	
 
    /* Return to the lower window */
 

	
 
    set_window (0);
 

	
 
}/* z_show_status */
 

	
 
/*
 
 * z_split_window, split the screen into an upper (1) and lower (0) window.
 
 *
 
 *	zargs[0] = height of upper window in screen units (V6) or #lines
 
 *
 
 */
 

	
 
void z_split_window (void)
 
{
 

	
 
    split_window (zargs[0]);
 

	
 
}/* z_split_window */
 

	
 
/*
 
 * z_window_size, change the width and height of a window.
 
 *
 
 *	zargs[0] = window (-3 is the current one)
 
 *	zargs[1] = new height in screen units
 
 *	zargs[2] = new width in screen units
 
 *
 
 */
 

	
 
void z_window_size (void)
 
{
 
    zword win = winarg0 ();
 

	
 
    flush_buffer ();
 

	
 
    wp[win].y_size = zargs[1];
 
    wp[win].x_size = zargs[2];
 

	
 
    /* Keep the cursor within the window */
 

	
 
    if (wp[win].y_cursor > zargs[1] || wp[win].x_cursor > zargs[2])
 
	reset_cursor (win);
 

	
 
}/* z_window_size */
 

	
 
/*
 
 * z_window_style, set / clear / toggle window attributes.
 
 *
 
 *	zargs[0] = window (-3 is the current one)
 
 *	zargs[1] = window attribute flags
 
 *	zargs[2] = operation to perform (optional, defaults to 0)
 
 *
 
 */
 

	
 
void z_window_style (void)
 
{
 
    zword win = winarg0 ();
 
    zword flags = zargs[1];
 

	
 
    flush_buffer ();
 

	
 
    /* Supply default arguments */
 

	
 
    if (zargc < 3)
 
	zargs[2] = 0;
 

	
 
    /* Set window style */
 

	
 
    switch (zargs[2]) {
 
	case 0: wp[win].attribute = flags; break;
 
	case 1: wp[win].attribute |= flags; break;
 
	case 2: wp[win].attribute &= ~flags; break;
 
	case 3: wp[win].attribute ^= flags; break;
 
    }
 

	
 
    if (cwin == win)
 
	update_attributes ();
 

	
 
}/* z_window_style */
backends/frotz/setup.h
Show inline comments
 
new file 100644
 
/*
 
 * Various status thingies for the interpreter and interface.
 
 *
 
 */
 

	
 
typedef struct frotz_setup_struct {
 
	int attribute_assignment;	/* done */
 
	int attribute_testing;		/* done */
 
	int context_lines;		/* done */
 
	int object_locating;		/* done */
 
	int object_movement;		/* done */
 
	int left_margin;		/* done */
 
	int right_margin;		/* done */
 
	int ignore_errors;		/* done */
 
	int interpreter_number;		/* Just dumb frotz now */
 
	int piracy;			/* done */
 
	int undo_slots;			/* done */
 
	int expand_abbreviations;	/* done */
 
	int script_cols;		/* done */
 
	int save_quetzal;		/* done */
 
	int sound;			/* done */
 
	int err_report_mode;		/* done */
 
} f_setup_t;
 

	
 
extern f_setup_t f_setup;
 

	
 

	
 
typedef struct zcode_header_struct {
 
	zbyte h_version;
 
	zbyte h_config;
 
	zword h_release;
 
	zword h_resident_size;
 
	zword h_start_pc;
 
	zword h_dictionary;
 
	zword h_objects;
 
	zword h_globals;
 
	zword h_dynamic_size;
 
	zword h_flags;
 
	zbyte h_serial[6];
 
	zword h_abbreviations;
 
	zword h_file_size;
 
	zword h_checksum;
 
	zbyte h_interpreter_number;
 
	zbyte h_interpreter_version;
 
	zbyte h_screen_rows;
 
	zbyte h_screen_cols;
 
	zword h_screen_width;
 
	zword h_screen_height;
 
	zbyte h_font_height;
 
	zbyte h_font_width;
 
	zword h_functions_offset;
 
	zword h_strings_offset;
 
	zbyte h_default_background;
 
	zbyte h_default_foreground;
 
	zword h_terminating_keys;
 
	zword h_line_width;
 
	zbyte h_standard_high;
 
	zbyte h_standard_low;
 
	zword h_alphabet;
 
	zword h_extension_table;
 
	zbyte h_user_name[8];
 

	
 
	zword hx_table_size;
 
	zword hx_mouse_x;
 
	zword hx_mouse_y;
 
	zword hx_unicode_table;
 
} z_header_t;
backends/frotz/sound.c
Show inline comments
 
new file 100644
 
/* sound.c - Sound effect function
 
 *	Copyright (c) 1995-1997 Stefan Jokisch
 
 *
 
 * This file is part of Frotz.
 
 *
 
 * Frotz is free software; you can redistribute it and/or modify
 
 * it under the terms of the GNU General Public License as published by
 
 * the Free Software Foundation; either version 2 of the License, or
 
 * (at your option) any later version.
 
 *
 
 * Frotz is distributed in the hope that it will be useful,
 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
 * GNU General Public License for more details.
 
 *
 
 * You should have received a copy of the GNU General Public License
 
 * along with this program; if not, write to the Free Software
 
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
 
 */
 

	
 
#include "frotz.h"
 

	
 
#ifdef DJGPP
 
#include "djfrotz.h"
 
#endif
 

	
 
#define EFFECT_PREPARE 1
 
#define EFFECT_PLAY 2
 
#define EFFECT_STOP 3
 
#define EFFECT_FINISH_WITH 4
 

	
 
extern int direct_call (zword);
 

	
 
static zword routine = 0;
 

	
 
static int next_sample = 0;
 
static int next_volume = 0;
 

	
 
static int locked = FALSE;
 
static int playing = FALSE;
 

	
 
/*
 
 * init_sound
 
 *
 
 * Initialize sound variables.
 
 *
 
 */
 

	
 
void init_sound (void)
 
{
 
    locked = FALSE;
 
    playing = FALSE;
 
} /* init_sound */
 

	
 

	
 
/*
 
 * start_sample
 
 *
 
 * Call the IO interface to play a sample.
 
 *
 
 */
 

	
 
static void start_sample (int number, int volume, int repeats, zword eos)
 
{
 

	
 
    static zbyte lh_repeats[] = {
 
	0x00, 0x00, 0x00, 0x01, 0xff,
 
	0x00, 0x01, 0x01, 0x01, 0x01,
 
	0xff, 0x01, 0x01, 0xff, 0x00,
 
	0xff, 0xff, 0xff, 0xff, 0xff
 
    };
 

	
 
    if (story_id == LURKING_HORROR)
 
	repeats = lh_repeats[number];
 

	
 
    os_start_sample (number, volume, repeats, eos);
 

	
 
    routine = eos;
 
    playing = TRUE;
 

	
 
}/* start_sample */
 

	
 
/*
 
 * start_next_sample
 
 *
 
 * Play a sample that has been delayed until the previous sound effect has
 
 * finished.  This is necessary for two samples in The Lurking Horror that
 
 * immediately follow other samples.
 
 *
 
 */
 

	
 
static void start_next_sample (void)
 
{
 

	
 
    if (next_sample != 0)
 
	start_sample (next_sample, next_volume, 0, 0);
 

	
 
    next_sample = 0;
 
    next_volume = 0;
 

	
 
}/* start_next_sample */
 

	
 
/*
 
 * end_of_sound
 
 *
 
 * Call the Z-code routine which was given as the last parameter of
 
 * a sound_effect call. This function may be called from a hardware
 
 * interrupt (which requires extremely careful programming).
 
 *
 
 */
 

	
 
void end_of_sound (void)
 
{
 

	
 
#if defined(DJGPP) && defined(SOUND_SUPPORT)
 
    end_of_sound_flag = 0;
 
#endif
 

	
 
    playing = FALSE;
 

	
 
    if (!locked) {
 

	
 
	if (story_id == LURKING_HORROR)
 
	    start_next_sample ();
 

	
 
	direct_call (routine);
 

	
 
    }
 

	
 
}/* end_of_sound */
 

	
 
/*
 
 * z_sound_effect, load / play / stop / discard a sound effect.
 
 *
 
 *   	zargs[0] = number of bleep (1 or 2) or sample
 
 *	zargs[1] = operation to perform (samples only)
 
 *	zargs[2] = repeats and volume (play sample only)
 
 *	zargs[3] = end-of-sound routine (play sample only, optional)
 
 *
 
 * Note: Volumes range from 1 to 8, volume 255 is the default volume.
 
 *	 Repeats are stored in the high byte, 255 is infinite loop.
 
 *
 
 */
 

	
 
void z_sound_effect (void)
 
{
 
    zword number = zargs[0];
 
    zword effect = zargs[1];
 
    zword volume = zargs[2];
 

	
 
    /* By default play sound 1 at volume 8 */
 
    if (zargc < 1)
 
	number = 1;
 
    if (zargc < 2)
 
	effect = EFFECT_PLAY;
 
    if (zargc < 3)
 
	volume = 8;
 

	
 
    if (number >= 3 || number == 0) {
 

	
 
	locked = TRUE;
 

	
 
	if (story_id == LURKING_HORROR && (number == 9 || number == 16)) {
 

	
 
	    if (effect == EFFECT_PLAY) {
 

	
 
		next_sample = number;
 
		next_volume = volume;
 

	
 
		locked = FALSE;
 

	
 
		if (!playing)
 
		    start_next_sample ();
 

	
 
	    } else locked = FALSE;
 

	
 
	    return;
 

	
 
	}
 

	
 
	playing = FALSE;
 

	
 
	switch (effect) {
 

	
 
	case EFFECT_PREPARE:
 
	    os_prepare_sample (number);
 
	    break;
 
	case EFFECT_PLAY:
 
	    start_sample (number, lo (volume), hi (volume), (zargc == 4) ? zargs[3] : 0);
 
	    break;
 
	case EFFECT_STOP:
 
	    os_stop_sample (number);
 
	    break;
 
	case EFFECT_FINISH_WITH:
 
	    os_finish_with_sample (number);
 
	    break;
 

	
 
	}
 

	
 
	locked = FALSE;
 

	
 
    } else os_beep (number);
 

	
 
}/* z_sound_effect */
backends/frotz/soundcard.h
Show inline comments
 
new file 100644
 
/*
 
 * This file automatically generated by findsound.sh which run by the
 
 * Makefile found in the Unix Frotz 2.43 source distribution.
 
 * Copying this nasty hack to find headers which may be in any of several
 
 * places is not recommended.  I don't want to use autoconf just yet for
 
 * this project.
 
 *
 
 */
 

	
 
#include <sys/soundcard.h>
backends/frotz/stream.c
Show inline comments
 
new file 100644
 
/* stream.c - IO stream implementation
 
 *	Copyright (c) 1995-1997 Stefan Jokisch
 
 *
 
 * This file is part of Frotz.
 
 *
 
 * Frotz is free software; you can redistribute it and/or modify
 
 * it under the terms of the GNU General Public License as published by
 
 * the Free Software Foundation; either version 2 of the License, or
 
 * (at your option) any later version.
 
 *
 
 * Frotz is distributed in the hope that it will be useful,
 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
 * GNU General Public License for more details.
 
 *
 
 * You should have received a copy of the GNU General Public License
 
 * along with this program; if not, write to the Free Software
 
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
 
 */
 

	
 
#include "frotz.h"
 

	
 
extern int handle_hot_key (zchar);
 

	
 
extern int validate_click (void);
 

	
 
extern void replay_open (void);
 
extern void replay_close (void);
 
extern void memory_open (zword, zword, int);
 
extern void memory_close (void);
 
extern void record_open (void);
 
extern void record_close (void);
 
extern void script_open (void);
 
extern void script_close (void);
 

	
 
extern void memory_word (const zchar *);
 
extern void memory_new_line (void);
 
extern void record_write_key (zchar);
 
extern void record_write_input (const zchar *, zchar);
 
extern void script_char (zchar);
 
extern void script_word (const zchar *);
 
extern void script_new_line (void);
 
extern void script_write_input (const zchar *, zchar);
 
extern void script_erase_input (const zchar *);
 
extern void script_mssg_on (void);
 
extern void script_mssg_off (void);
 
extern void screen_char (zchar);
 
extern void screen_word (const zchar *);
 
extern void screen_new_line (void);
 
extern void screen_write_input (const zchar *, zchar);
 
extern void screen_erase_input (const zchar *);
 
extern void screen_mssg_on (void);
 
extern void screen_mssg_off (void);
 

	
 
extern zchar replay_read_key (void);
 
extern zchar replay_read_input (zchar *);
 
extern zchar console_read_key (zword);
 
extern zchar console_read_input (int, zchar *, zword, int);
 

	
 
extern int direct_call (zword);
 

	
 
/*
 
 * stream_mssg_on
 
 *
 
 * Start printing a "debugging" message.
 
 *
 
 */
 

	
 
void stream_mssg_on (void)
 
{
 

	
 
    flush_buffer ();
 

	
 
    if (ostream_screen)
 
	screen_mssg_on ();
 
    if (ostream_script && enable_scripting)
 
	script_mssg_on ();
 

	
 
    message = TRUE;
 

	
 
}/* stream_mssg_on */
 

	
 
/*
 
 * stream_mssg_off
 
 *
 
 * Stop printing a "debugging" message.
 
 *
 
 */
 

	
 
void stream_mssg_off (void)
 
{
 

	
 
    flush_buffer ();
 

	
 
    if (ostream_screen)
 
	screen_mssg_off ();
 
    if (ostream_script && enable_scripting)
 
	script_mssg_off ();
 

	
 
    message = FALSE;
 

	
 
}/* stream_mssg_off */
 

	
 
/*
 
 * z_output_stream, open or close an output stream.
 
 *
 
 *	zargs[0] = stream to open (positive) or close (negative)
 
 *	zargs[1] = address to redirect output to (stream 3 only)
 
 *	zargs[2] = width of redirected output (stream 3 only, optional)
 
 *
 
 */
 

	
 
void z_output_stream (void)
 
{
 

	
 
    flush_buffer ();
 

	
 
    switch ((short) zargs[0]) {
 

	
 
    case  1: ostream_screen = TRUE;
 
	     break;
 
    case -1: ostream_screen = FALSE;
 
	     break;
 
    case  2: if (!ostream_script) script_open ();
 
	     break;
 
    case -2: if (ostream_script) script_close ();
 
	     break;
 
    case  3: memory_open (zargs[1], zargs[2], zargc >= 3);
 
	     break;
 
    case -3: memory_close ();
 
	     break;
 
    case  4: if (!ostream_record) record_open ();
 
	     break;
 
    case -4: if (ostream_record) record_close ();
 
	     break;
 

	
 
    }
 

	
 
}/* z_output_stream */
 

	
 
/*
 
 * stream_char
 
 *
 
 * Send a single character to the output stream.
 
 *
 
 */
 

	
 
void stream_char (zchar c)
 
{
 

	
 
    if (ostream_screen)
 
	screen_char (c);
 
    if (ostream_script && enable_scripting)
 
	script_char (c);
 

	
 
}/* stream_char */
 

	
 
/*
 
 * stream_word
 
 *
 
 * Send a string of characters to the output streams.
 
 *
 
 */
 

	
 
void stream_word (const zchar *s)
 
{
 

	
 
    if (ostream_memory && !message)
 

	
 
	memory_word (s);
 

	
 
    else {
 

	
 
	if (ostream_screen)
 
	    screen_word (s);
 
	if (ostream_script && enable_scripting)
 
	    script_word (s);
 

	
 
    }
 

	
 
}/* stream_word */
 

	
 
/*
 
 * stream_new_line
 
 *
 
 * Send a newline to the output streams.
 
 *
 
 */
 

	
 
void stream_new_line (void)
 
{
 

	
 
    if (ostream_memory && !message)
 

	
 
	memory_new_line ();
 

	
 
    else {
 

	
 
	if (ostream_screen)
 
	    screen_new_line ();
 
	if (ostream_script && enable_scripting)
 
	    script_new_line ();
 

	
 
    }
 

	
 
}/* stream_new_line */
 

	
 
/*
 
 * z_input_stream, select an input stream.
 
 *
 
 *	zargs[0] = input stream to be selected
 
 *
 
 */
 

	
 
void z_input_stream (void)
 
{
 

	
 
    flush_buffer ();
 

	
 
    if (zargs[0] == 0 && istream_replay)
 
	replay_close ();
 
    if (zargs[0] == 1 && !istream_replay)
 
	replay_open ();
 

	
 
}/* z_input_stream */
 

	
 
/*
 
 * stream_read_key
 
 *
 
 * Read a single keystroke from the current input stream.
 
 *
 
 */
 

	
 
zchar stream_read_key ( zword timeout, zword routine,
 
			int hot_keys )
 
{
 
    zchar key = ZC_BAD;
 

	
 
    flush_buffer ();
 

	
 
    /* Read key from current input stream */
 

	
 
continue_input:
 

	
 
    do {
 

	
 
	if (istream_replay)
 
	    key = replay_read_key ();
 
	else
 
	    key = console_read_key (timeout);
 

	
 
    } while (key == ZC_BAD);
 

	
 
    /* Verify mouse clicks */
 

	
 
    if (key == ZC_SINGLE_CLICK || key == ZC_DOUBLE_CLICK)
 
	if (!validate_click ())
 
	    goto continue_input;
 

	
 
    /* Copy key to the command file */
 

	
 
    if (ostream_record && !istream_replay)
 
	record_write_key (key);
 

	
 
    /* Handle timeouts */
 

	
 
    if (key == ZC_TIME_OUT)
 
	if (direct_call (routine) == 0)
 
	    goto continue_input;
 

	
 
    /* Handle hot keys */
 

	
 
    if (hot_keys && key >= ZC_HKEY_MIN && key <= ZC_HKEY_MAX) {
 

	
 
	if (h_version == V4 && key == ZC_HKEY_UNDO)
 
	    goto continue_input;
 
	if (!handle_hot_key (key))
 
	    goto continue_input;
 

	
 
	return ZC_BAD;
 

	
 
    }
 

	
 
    /* Return key */
 

	
 
    return key;
 

	
 
}/* stream_read_key */
 

	
 
/*
 
 * stream_read_input
 
 *
 
 * Read a line of input from the current input stream.
 
 *
 
 */
 

	
 
zchar stream_read_input ( int max, zchar *buf,
 
			  zword timeout, zword routine,
 
			  int hot_keys,
 
			  int no_scripting )
 
{
 
    zchar key = ZC_BAD;
 

	
 
    flush_buffer ();
 

	
 
    /* Remove initial input from the transscript file or from the screen */
 

	
 
    if (ostream_script && enable_scripting && !no_scripting)
 
	script_erase_input (buf);
 
    if (istream_replay)
 
	screen_erase_input (buf);
 

	
 
    /* Read input line from current input stream */
 

	
 
continue_input:
 

	
 
    do {
 

	
 
	if (istream_replay)
 
	    key = replay_read_input (buf);
 
	else
 
	    key = console_read_input (max, buf, timeout, key != ZC_BAD);
 

	
 
    } while (key == ZC_BAD);
 

	
 
    /* Verify mouse clicks */
 

	
 
    if (key == ZC_SINGLE_CLICK || key == ZC_DOUBLE_CLICK)
 
	if (!validate_click ())
 
	    goto continue_input;
 

	
 
    /* Copy input line to the command file */
 

	
 
    if (ostream_record && !istream_replay)
 
	record_write_input (buf, key);
 

	
 
    /* Handle timeouts */
 

	
 
    if (key == ZC_TIME_OUT)
 
	if (direct_call (routine) == 0)
 
	    goto continue_input;
 

	
 
    /* Handle hot keys */
 

	
 
    if (hot_keys && key >= ZC_HKEY_MIN && key <= ZC_HKEY_MAX) {
 

	
 
	if (!handle_hot_key (key))
 
	    goto continue_input;
 

	
 
	return ZC_BAD;
 

	
 
    }
 

	
 
    /* Copy input line to transscript file or to the screen */
 

	
 
    if (ostream_script && enable_scripting && !no_scripting)
 
	script_write_input (buf, key);
 
    if (istream_replay)
 
	screen_write_input (buf, key);
 

	
 
    /* Return terminating key */
 

	
 
    return key;
 

	
 
}/* stream_read_input */
backends/frotz/table.c
Show inline comments
 
new file 100644
 
/* table.c - Table handling opcodes
 
 *	Copyright (c) 1995-1997 Stefan Jokisch
 
 *
 
 * This file is part of Frotz.
 
 *
 
 * Frotz is free software; you can redistribute it and/or modify
 
 * it under the terms of the GNU General Public License as published by
 
 * the Free Software Foundation; either version 2 of the License, or
 
 * (at your option) any later version.
 
 *
 
 * Frotz is distributed in the hope that it will be useful,
 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
 * GNU General Public License for more details.
 
 *
 
 * You should have received a copy of the GNU General Public License
 
 * along with this program; if not, write to the Free Software
 
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
 
 */
 

	
 
#include "frotz.h"
 

	
 
/*
 
 * z_copy_table, copy a table or fill it with zeroes.
 
 *
 
 *	zargs[0] = address of table
 
 * 	zargs[1] = destination address or 0 for fill
 
 *	zargs[2] = size of table
 
 *
 
 * Note: Copying is safe even when source and destination overlap; but
 
 *       if zargs[1] is negative the table _must_ be copied forwards.
 
 *
 
 */
 

	
 
void z_copy_table (void)
 
{
 
    zword addr;
 
    zword size = zargs[2];
 
    zbyte value;
 
    int i;
 

	
 
    if (zargs[1] == 0)      				/* zero table */
 

	
 
	for (i = 0; i < size; i++)
 
	    storeb ((zword) (zargs[0] + i), 0);
 

	
 
    else if ((short) size < 0 || zargs[0] > zargs[1])	/* copy forwards */
 

	
 
	for (i = 0; i < (((short) size < 0) ? - (short) size : size); i++) {
 
	    addr = zargs[0] + i;
 
	    LOW_BYTE (addr, value)
 
	    storeb ((zword) (zargs[1] + i), value);
 
	}
 

	
 
    else						/* copy backwards */
 

	
 
	for (i = size - 1; i >= 0; i--) {
 
	    addr = zargs[0] + i;
 
	    LOW_BYTE (addr, value)
 
	    storeb ((zword) (zargs[1] + i), value);
 
	}
 

	
 
}/* z_copy_table */
 

	
 
/*
 
 * z_loadb, store a value from a table of bytes.
 
 *
 
 *	zargs[0] = address of table
 
 *	zargs[1] = index of table entry to store
 
 *
 
 */
 

	
 
void z_loadb (void)
 
{
 
    zword addr = zargs[0] + zargs[1];
 
    zbyte value;
 

	
 
    LOW_BYTE (addr, value)
 

	
 
    store (value);
 

	
 
}/* z_loadb */
 

	
 
/*
 
 * z_loadw, store a value from a table of words.
 
 *
 
 *	zargs[0] = address of table
 
 *	zargs[1] = index of table entry to store
 
 *
 
 */
 

	
 
void z_loadw (void)
 
{
 
    zword addr = zargs[0] + 2 * zargs[1];
 
    zword value;
 

	
 
    LOW_WORD (addr, value)
 

	
 
    store (value);
 

	
 
}/* z_loadw */
 

	
 
/*
 
 * z_scan_table, find and store the address of a target within a table.
 
 *
 
 *	zargs[0] = target value to be searched for
 
 *	zargs[1] = address of table
 
 *	zargs[2] = number of table entries to check value against
 
 *	zargs[3] = type of table (optional, defaults to 0x82)
 
 *
 
 * Note: The table is a word array if bit 7 of zargs[3] is set; otherwise
 
 *       it's a byte array. The lower bits hold the address step.
 
 *
 
 */
 

	
 
void z_scan_table (void)
 
{
 
    zword addr = zargs[1];
 
    int i;
 

	
 
    /* Supply default arguments */
 

	
 
    if (zargc < 4)
 
	zargs[3] = 0x82;
 

	
 
    /* Scan byte or word array */
 

	
 
    for (i = 0; i < zargs[2]; i++) {
 

	
 
	if (zargs[3] & 0x80) {	/* scan word array */
 

	
 
	    zword wvalue;
 

	
 
	    LOW_WORD (addr, wvalue)
 

	
 
	    if (wvalue == zargs[0])
 
		goto finished;
 

	
 
	} else {		/* scan byte array */
 

	
 
	    zbyte bvalue;
 

	
 
	    LOW_BYTE (addr, bvalue)
 

	
 
	    if (bvalue == zargs[0])
 
		goto finished;
 

	
 
	}
 

	
 
	addr += zargs[3] & 0x7f;
 

	
 
    }
 

	
 
    addr = 0;
 

	
 
finished:
 

	
 
    store (addr);
 
    branch (addr);
 

	
 
}/* z_scan_table */
 

	
 
/*
 
 * z_storeb, write a byte into a table of bytes.
 
 *
 
 *	zargs[0] = address of table
 
 *	zargs[1] = index of table entry
 
 *	zargs[2] = value to be written
 
 *
 
 */
 

	
 
void z_storeb (void)
 
{
 

	
 
    storeb ((zword) (zargs[0] + zargs[1]), zargs[2]);
 

	
 
}/* z_storeb */
 

	
 
/*
 
 * z_storew, write a word into a table of words.
 
 *
 
 *	zargs[0] = address of table
 
 *	zargs[1] = index of table entry
 
 *	zargs[2] = value to be written
 
 *
 
 */
 

	
 
void z_storew (void)
 
{
 

	
 
    storew ((zword) (zargs[0] + 2 * zargs[1]), zargs[2]);
 

	
 
}/* z_storew */

Changeset was too big and was cut off... Show full diff anyway

0 comments (0 inline, 0 general)