Added basic infocom IF games emulator backend

This commit is contained in:
Jan Kaluza 2011-09-15 14:11:24 +02:00
parent 90aacd41d9
commit 51c03d82ea
32 changed files with 11869 additions and 19 deletions

View file

@ -7,4 +7,6 @@ if (PROTOBUF_FOUND)
ADD_SUBDIRECTORY(libircclient-qt)
endif()
ADD_SUBDIRECTORY(frotz)
endif()

View file

@ -0,0 +1,9 @@
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)

152
backends/frotz/buffer.c Normal file
View file

@ -0,0 +1,152 @@
/* 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;
}

View file

@ -0,0 +1,41 @@
/* 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);

237
backends/frotz/dumb_init.c Normal file
View file

@ -0,0 +1,237 @@
/* 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;
}

417
backends/frotz/dumb_input.c Normal file
View file

@ -0,0 +1,417 @@
/* 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 */
}

View file

@ -0,0 +1,554 @@
/* 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);
}

153
backends/frotz/dumb_pic.c Normal file
View file

@ -0,0 +1,153 @@
/* 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; }

154
backends/frotz/err.c Normal file
View file

@ -0,0 +1,154 @@
/* 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 */

1115
backends/frotz/fastmem.c Normal file

File diff suppressed because it is too large Load diff

566
backends/frotz/files.c Normal file
View file

@ -0,0 +1,566 @@
/* 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 */

636
backends/frotz/frotz.h Normal file
View file

@ -0,0 +1,636 @@
/*
* 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

72
backends/frotz/getopt.c Normal file
View file

@ -0,0 +1,72 @@
/*
* 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 */

258
backends/frotz/hotkey.c Normal file
View file

@ -0,0 +1,258 @@
/* 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 */

323
backends/frotz/input.c Normal file
View file

@ -0,0 +1,323 @@
/* 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 */

288
backends/frotz/main.cpp Normal file
View file

@ -0,0 +1,288 @@
/*
* 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());
}
void handleLogoutRequest(const std::string &user, const std::string &legacyName) {
}
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;
private:
Config *config;
};
void send_array() {
np->handleMessage(np->m_user, "zork", frotz_get_array());
std::cout << "sending " << frotz_get_array() << "\n";
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;
}

261
backends/frotz/math.c Normal file
View file

@ -0,0 +1,261 @@
/* 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 */

1003
backends/frotz/object.c Normal file

File diff suppressed because it is too large Load diff

798
backends/frotz/process.c Normal file
View file

@ -0,0 +1,798 @@
/* 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 */

562
backends/frotz/quetzal.c Normal file
View file

@ -0,0 +1,562 @@
/* 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;
}

82
backends/frotz/random.c Normal file
View file

@ -0,0 +1,82 @@
/* 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 */

172
backends/frotz/redirect.c Normal file
View file

@ -0,0 +1,172 @@
/* 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 */

1743
backends/frotz/screen.c Normal file

File diff suppressed because it is too large Load diff

67
backends/frotz/setup.h Normal file
View file

@ -0,0 +1,67 @@
/*
* 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;

204
backends/frotz/sound.c Normal file
View file

@ -0,0 +1,204 @@
/* 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 */

View file

@ -0,0 +1,10 @@
/*
* 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>

365
backends/frotz/stream.c Normal file
View file

@ -0,0 +1,365 @@
/* 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 */

193
backends/frotz/table.c Normal file
View file

@ -0,0 +1,193 @@
/* 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 */

1110
backends/frotz/text.c Normal file

File diff suppressed because it is too large Load diff

304
backends/frotz/variable.c Normal file
View file

@ -0,0 +1,304 @@
/* variable.c - Variable and stack related 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_dec, decrement a variable.
*
* zargs[0] = variable to decrement
*
*/
void z_dec (void)
{
zword value;
if (zargs[0] == 0)
(*sp)--;
else if (zargs[0] < 16)
(*(fp - zargs[0]))--;
else {
zword addr = h_globals + 2 * (zargs[0] - 16);
LOW_WORD (addr, value)
value--;
SET_WORD (addr, value)
}
}/* z_dec */
/*
* z_dec_chk, decrement a variable and branch if now less than value.
*
* zargs[0] = variable to decrement
* zargs[1] = value to check variable against
*
*/
void z_dec_chk (void)
{
zword value;
if (zargs[0] == 0)
value = --(*sp);
else if (zargs[0] < 16)
value = --(*(fp - zargs[0]));
else {
zword addr = h_globals + 2 * (zargs[0] - 16);
LOW_WORD (addr, value)
value--;
SET_WORD (addr, value)
}
branch ((short) value < (short) zargs[1]);
}/* z_dec_chk */
/*
* z_inc, increment a variable.
*
* zargs[0] = variable to increment
*
*/
void z_inc (void)
{
zword value;
if (zargs[0] == 0)
(*sp)++;
else if (zargs[0] < 16)
(*(fp - zargs[0]))++;
else {
zword addr = h_globals + 2 * (zargs[0] - 16);
LOW_WORD (addr, value)
value++;
SET_WORD (addr, value)
}
}/* z_inc */
/*
* z_inc_chk, increment a variable and branch if now greater than value.
*
* zargs[0] = variable to increment
* zargs[1] = value to check variable against
*
*/
void z_inc_chk (void)
{
zword value;
if (zargs[0] == 0)
value = ++(*sp);
else if (zargs[0] < 16)
value = ++(*(fp - zargs[0]));
else {
zword addr = h_globals + 2 * (zargs[0] - 16);
LOW_WORD (addr, value)
value++;
SET_WORD (addr, value)
}
branch ((short) value > (short) zargs[1]);
}/* z_inc_chk */
/*
* z_load, store the value of a variable.
*
* zargs[0] = variable to store
*
*/
void z_load (void)
{
zword value;
if (zargs[0] == 0)
value = *sp;
else if (zargs[0] < 16)
value = *(fp - zargs[0]);
else {
zword addr = h_globals + 2 * (zargs[0] - 16);
LOW_WORD (addr, value)
}
store (value);
}/* z_load */
/*
* z_pop, pop a value off the game stack and discard it.
*
* no zargs used
*
*/
void z_pop (void)
{
sp++;
}/* z_pop */
/*
* z_pop_stack, pop n values off the game or user stack and discard them.
*
* zargs[0] = number of values to discard
* zargs[1] = address of user stack (optional)
*
*/
void z_pop_stack (void)
{
if (zargc == 2) { /* it's a user stack */
zword size;
zword addr = zargs[1];
LOW_WORD (addr, size)
size += zargs[0];
storew (addr, size);
} else sp += zargs[0]; /* it's the game stack */
}/* z_pop_stack */
/*
* z_pull, pop a value off...
*
* a) ...the game or a user stack and store it (V6)
*
* zargs[0] = address of user stack (optional)
*
* b) ...the game stack and write it to a variable (other than V6)
*
* zargs[0] = variable to write value to
*
*/
void z_pull (void)
{
zword value;
if (h_version != V6) { /* not a V6 game, pop stack and write */
value = *sp++;
if (zargs[0] == 0)
*sp = value;
else if (zargs[0] < 16)
*(fp - zargs[0]) = value;
else {
zword addr = h_globals + 2 * (zargs[0] - 16);
SET_WORD (addr, value)
}
} else { /* it's V6, but is there a user stack? */
if (zargc == 1) { /* it's a user stack */
zword size;
zword addr = zargs[0];
LOW_WORD (addr, size)
size++;
storew (addr, size);
addr += 2 * size;
LOW_WORD (addr, value)
} else value = *sp++; /* it's the game stack */
store (value);
}
}/* z_pull */
/*
* z_push, push a value onto the game stack.
*
* zargs[0] = value to push onto the stack
*
*/
void z_push (void)
{
*--sp = zargs[0];
}/* z_push */
/*
* z_push_stack, push a value onto a user stack then branch if successful.
*
* zargs[0] = value to push onto the stack
* zargs[1] = address of user stack
*
*/
void z_push_stack (void)
{
zword size;
zword addr = zargs[1];
LOW_WORD (addr, size)
if (size != 0) {
storew ((zword) (addr + 2 * size), zargs[0]);
size--;
storew (addr, size);
}
branch (size);
}/* z_push_stack */
/*
* z_store, write a value to a variable.
*
* zargs[0] = variable to be written to
* zargs[1] = value to write
*
*/
void z_store (void)
{
zword value = zargs[1];
if (zargs[0] == 0)
*sp = value;
else if (zargs[0] < 16)
*(fp - zargs[0]) = value;
else {
zword addr = h_globals + 2 * (zargs[0] - 16);
SET_WORD (addr, value)
}
}/* z_store */

View file

@ -8,16 +8,14 @@ backend_host=localhost # < this option doesn't work yet
backend_port=10001
admin_username=admin
admin_password=test
#idle_reconnect_time=10
#cert=server.pfx #patch to PKCS#12 certificate
#cert_password=test #password to that certificate if any
users_per_backend=10
backend=/home/hanzz/code/libtransport/backends/libpurple/spectrum_libpurple_backend
backend=/home/hanzz/code/libtransport/backends/frotz/spectrum_frotz_backend
#backend=../../backends/libircclient-qt/spectrum_libircclient-qt_backend
#protocol=prpl-msn
protocol=any
#protocol=prpl-icq
#idle_reconnect_time=10
[backend]
#default_avatar=catmelonhead.jpg

View file

@ -672,27 +672,28 @@ void NetworkPluginServer::pingTimeout() {
time_t now = time(NULL);
std::vector<User *> usersToMove;
unsigned long diff = CONFIG_INT(m_config, "service.idle_reconnect_time");
for (std::list<Backend *>::const_iterator it = m_clients.begin(); it != m_clients.end(); it++) {
// Users from long-running backends can't be moved
if ((*it)->longRun) {
continue;
}
if (diff != 0) {
for (std::list<Backend *>::const_iterator it = m_clients.begin(); it != m_clients.end(); it++) {
// Users from long-running backends can't be moved
if ((*it)->longRun) {
continue;
}
// Find users which are inactive for more than 'diff'
BOOST_FOREACH(User *u, (*it)->users) {
if (now - u->getLastActivity() > diff) {
usersToMove.push_back(u);
// Find users which are inactive for more than 'diff'
BOOST_FOREACH(User *u, (*it)->users) {
if (now - u->getLastActivity() > diff) {
usersToMove.push_back(u);
}
}
}
}
// Move inactive users to long-running backend.
BOOST_FOREACH(User *u, usersToMove) {
LOG4CXX_INFO(logger, "Moving user " << u->getJID().toString() << " to long-running backend");
if (!moveToLongRunBackend(u))
break;
// Move inactive users to long-running backend.
BOOST_FOREACH(User *u, usersToMove) {
LOG4CXX_INFO(logger, "Moving user " << u->getJID().toString() << " to long-running backend");
if (!moveToLongRunBackend(u))
break;
}
}
// check ping responses