Added basic infocom IF games emulator backend
This commit is contained in:
parent
90aacd41d9
commit
51c03d82ea
32 changed files with 11869 additions and 19 deletions
|
@ -7,4 +7,6 @@ if (PROTOBUF_FOUND)
|
|||
ADD_SUBDIRECTORY(libircclient-qt)
|
||||
endif()
|
||||
|
||||
ADD_SUBDIRECTORY(frotz)
|
||||
|
||||
endif()
|
||||
|
|
9
backends/frotz/CMakeLists.txt
Normal file
9
backends/frotz/CMakeLists.txt
Normal 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
152
backends/frotz/buffer.c
Normal 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;
|
||||
}
|
||||
|
41
backends/frotz/dumb_frotz.h
Normal file
41
backends/frotz/dumb_frotz.h
Normal 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
237
backends/frotz/dumb_init.c
Normal 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
417
backends/frotz/dumb_input.c
Normal 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 */
|
||||
}
|
554
backends/frotz/dumb_output.c
Normal file
554
backends/frotz/dumb_output.c
Normal 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
153
backends/frotz/dumb_pic.c
Normal 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
154
backends/frotz/err.c
Normal 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
1115
backends/frotz/fastmem.c
Normal file
File diff suppressed because it is too large
Load diff
566
backends/frotz/files.c
Normal file
566
backends/frotz/files.c
Normal 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
636
backends/frotz/frotz.h
Normal 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
72
backends/frotz/getopt.c
Normal 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
258
backends/frotz/hotkey.c
Normal 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
323
backends/frotz/input.c
Normal 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
288
backends/frotz/main.cpp
Normal 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
261
backends/frotz/math.c
Normal 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
1003
backends/frotz/object.c
Normal file
File diff suppressed because it is too large
Load diff
798
backends/frotz/process.c
Normal file
798
backends/frotz/process.c
Normal 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
562
backends/frotz/quetzal.c
Normal 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
82
backends/frotz/random.c
Normal 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
172
backends/frotz/redirect.c
Normal 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
1743
backends/frotz/screen.c
Normal file
File diff suppressed because it is too large
Load diff
67
backends/frotz/setup.h
Normal file
67
backends/frotz/setup.h
Normal 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
204
backends/frotz/sound.c
Normal 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 */
|
10
backends/frotz/soundcard.h
Normal file
10
backends/frotz/soundcard.h
Normal 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
365
backends/frotz/stream.c
Normal 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
193
backends/frotz/table.c
Normal 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
1110
backends/frotz/text.c
Normal file
File diff suppressed because it is too large
Load diff
304
backends/frotz/variable.c
Normal file
304
backends/frotz/variable.c
Normal 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 */
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Add table
Reference in a new issue