New plugin: frotz, a Z-machine interpreter, for playing interactive fiction.

The interpreter more or less passes all the tests in the z-machine test suite.
It should build for every target except Archos (for which it is disabled).


git-svn-id: svn://svn.rockbox.org/rockbox/trunk@24267 a1c6a512-1295-4272-9138-f99709370657
This commit is contained in:
Torne Wuff 2010-01-17 22:15:13 +00:00
parent 563f2602f4
commit 7f28c94eda
34 changed files with 10936 additions and 0 deletions

View file

@ -28,6 +28,7 @@ fire,demos
fireworks,demos
firmware_flash,apps
flipit,games
frotz,viewers
goban,games
greyscale,demos
helloworld,demos

View file

@ -25,6 +25,10 @@ jpeg
sudoku
reversi
goban
/* setjmp/longjmp are not implemented on sh */
#if (CONFIG_CPU != SH7034)
frotz
#endif
#ifdef HAVE_LCD_COLOR
png
#endif

View file

@ -0,0 +1,22 @@
frotz.c
buffer.c
err.c
fastmem.c
files.c
hotkey.c
input.c
main.c
math.c
object.c
process.c
quetzal.c
random.c
redirect.c
screen.c
sound.c
stream.c
table.c
text.c
variable.c
dumb_init.c
dumb_output.c

45
apps/plugins/frotz/STATUS Normal file
View file

@ -0,0 +1,45 @@
frotz is quite portable and is divided into 'common' and os-specific files.
The common files are included here with minimal modifications. For the
os-specific files I have started with dumbfrotz, the port intended for a plain
C stdio system - it has its own screen buffering which is needed for rockbox.
Things that work
----------------
Games, mostly! If the game is too large to fit in the plugin buffer it will
stop playback and steal the audio buffer.
Saving and restoring (/path/to/story.sav, no filename selection).
Transcripts, command records and replays (likewise).
Undo, up to the limit of available memory (the rest of the plugin buffer if
the game fit there, or the rest of the audio buffer if not).
Timed input, though it resets the timer when you enter the menu and only
counts until you enter the keyboard.
Input line preloading, though the actual displayed line after editing looks
wrong.
Things that don't work because I've not implemented it
------------------------------------------------------
Reading buttons that don't appear on the rockbox keyboard.
Audible beeps (just a splash for now).
Setting the random seed.
Things which don't work in the original frotz anyway
----------------------------------------------------
Mouse and menus.
Pictures.
Non-beep sound samples.
Unicode.
Colours.

152
apps/plugins/frotz/buffer.c Normal file
View file

@ -0,0 +1,152 @@
/* buffer.c - Text buffering and word wrapping
* Copyright (c) 1995-1997 Stefan Jokisch
*
* This file is part of Frotz.
*
* Frotz is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* Frotz is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
*/
#include "frotz.h"
extern void stream_char (zchar);
extern void stream_word (const zchar *);
extern void stream_new_line (void);
static zchar buffer[TEXT_BUFFER_SIZE];
static int bufpos = 0;
static zchar prev_c = 0;
/*
* flush_buffer
*
* Copy the contents of the text buffer to the output streams.
*
*/
void flush_buffer (void)
{
static bool 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 bool flag = FALSE;
if (message || ostream_memory || enable_buffering) {
if (!flag) {
/* Characters 0 and ZC_RETURN are special cases */
if (c == ZC_RETURN)
{ new_line (); return; }
if (c == 0)
return;
/* Flush the buffer before a whitespace or after a hyphen */
if (c == ' ' || c == ZC_INDENT || c == ZC_GAP || (prev_c == '-' && c != '-'))
flush_buffer ();
/* Set the flag if this is part one of a style or font change */
if (c == ZC_NEW_FONT || c == ZC_NEW_STYLE)
flag = TRUE;
/* Remember the current character code */
prev_c = c;
} else flag = FALSE;
/* Insert the character into the buffer */
buffer[bufpos++] = c;
if (bufpos == TEXT_BUFFER_SIZE)
runtime_error (ERR_TEXT_BUF_OVF);
} else stream_char (c);
}/* print_char */
/*
* new_line
*
* High level newline function.
*
*/
void new_line (void)
{
flush_buffer (); stream_new_line ();
}/* new_line */
/*
* init_buffer
*
* Initialize buffer variables.
*
*/
void init_buffer(void)
{
memset(buffer, 0, sizeof (zchar) * TEXT_BUFFER_SIZE);
bufpos = 0;
prev_c = 0;
}

View file

@ -0,0 +1,19 @@
/* 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.
*
* Changes for Rockbox copyright 2009 Torne Wuff
*/
#include "frotz.h"
/* from ../common/setup.h */
extern f_setup_t f_setup;
/* dumb-output.c */
void dumb_init_output(void);
void dumb_show_screen(bool show_cursor);
void dumb_dump_screen(void);

View file

@ -0,0 +1,86 @@
/* 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.
*
* Changes for Rockbox copyright 2009 Torne Wuff
*/
#include "dumb_frotz.h"
#include "lib/pluginlib_exit.h"
static int user_screen_width = LCD_WIDTH / SYSFONT_WIDTH;
static int user_screen_height = LCD_HEIGHT / SYSFONT_HEIGHT;
static int user_interpreter_number = -1;
static int user_random_seed = -1;
static int user_tandy_bit = 0;
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';
if (h_version >= V4)
h_config |= CONFIG_TIMEDINPUT;
if (h_version >= V5)
h_flags &= ~(MOUSE_FLAG | MENU_FLAG);
dumb_init_output();
h_flags &= ~GRAPHICS_FLAG;
}
int os_random_seed (void)
{
if (user_random_seed == -1)
/* Use the rockbox tick as seed value */
return ((int)rb->current_tick) & 0x7fff;
else return user_random_seed;
}
void os_restart_game (int stage) { (void)stage; }
void os_fatal (const char *s)
{
rb->splash(HZ*10, s);
exit(1);
}
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;
}

View file

@ -0,0 +1,256 @@
/* 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.
*
* Changes for Rockbox copyright 2009 Torne Wuff
*
* Rockbox is not really a dumb terminal (it supports printing text wherever
* you like) but it doesn't implement a terminal type buffer, so this is
* close enough to be a good starting point. Keeping a copy of the graphical
* framebuffer would be too expensive, text+attributes is much smaller.
*/
#include "dumb_frotz.h"
/* 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[(LCD_WIDTH/SYSFONT_WIDTH) * (LCD_HEIGHT/SYSFONT_HEIGHT)];
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;}
static int current_style = 0;
static int cursor_row = 0, cursor_col = 0;
static cell *dumb_row(int r) {return screen_data + r * h_screen_cols;}
int os_char_width (zchar z)
{
(void)z;
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 */
static void dumb_set_cell(int row, int col, cell c)
{
dumb_row(row)[col] = c;
}
void os_set_text_style(int x)
{
current_style = x & REVERSE_STYLE;
}
static void dumb_display_cell(int row, int col)
{
cell cel = dumb_row(row)[col];
char c = cell_char(cel);
if (!c)
c = ' ';
char style = cell_style(cel);
char s[5];
char *end = rb->utf8encode(c, s);
*end = 0;
if (style & REVERSE_STYLE)
rb->lcd_set_drawmode(DRMODE_INVERSEVID);
rb->lcd_putsxy(col * SYSFONT_WIDTH, row * SYSFONT_HEIGHT, s);
rb->lcd_set_drawmode(DRMODE_SOLID);
}
/* 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 os_display_char (zchar c)
{
if (c >= ZC_LATIN1_MIN /*&& c <= ZC_LATIN1_MAX*/) {
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;
top--; left--; bottom--; right--;
if (left == 0 && right == h_screen_cols - 1)
rb->memset(dumb_row(top), 0, (bottom - top + 1) * h_screen_cols * sizeof(cell));
else
for (row = top; row <= bottom; row++)
rb->memset(dumb_row(row) + left, 0, (right - left + 1) * sizeof(cell));
}
void os_scroll_area (int top, int left, int bottom, int right, int units)
{
int row;
top--; left--; bottom--; right--;
if (units > 0) {
for (row = top; row <= bottom - units; row++)
rb->memcpy(dumb_row(row) + left,
dumb_row(row + units) + left,
(right - left + 1) * sizeof(cell));
os_erase_area(bottom - units + 2, left + 1, bottom + 1, right + 1);
} else if (units < 0) {
for (row = bottom; row >= top - units; row--)
rb->memcpy(dumb_row(row) + left,
dumb_row(row + units) + left,
(right - left + 1) * sizeof(cell));
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)x; (void)y; }
void os_set_font (int x) { (void)x; }
/* Unconditionally show whole screen. */
void dumb_dump_screen(void)
{
int r, c;
rb->lcd_clear_display();
for (r = 0; r < h_screen_height; r++)
for (c = 0; c < h_screen_width; c++)
dumb_display_cell(r, c);
rb->lcd_update();
}
/* Show the current screen contents. */
void dumb_show_screen(bool show_cursor)
{
(void)show_cursor;
dumb_dump_screen();
}
void os_more_prompt(void)
{
int old_row = cursor_row;
int old_col = cursor_col;
int old_style = current_style;
current_style = REVERSE_STYLE;
os_display_string("[MORE]");
wait_for_key();
cursor_row = old_row;
cursor_col = old_col;
current_style = old_style;
os_erase_area(cursor_row+1, cursor_col+1, cursor_row+1, cursor_col+7);
}
void os_reset_screen(void)
{
current_style = REVERSE_STYLE;
os_display_string("[Press key to exit]");
wait_for_key();
}
/* To make the common code happy */
void os_prepare_sample (int a) { (void)a; }
void os_finish_with_sample (int a) { (void)a; }
void os_start_sample (int a, int b, int c, zword d)
{
(void)a; (void)b; (void)c; (void)d;
}
void os_stop_sample (int a) { (void)a; }
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;
h_font_width = 1; h_font_height = 1;
os_erase_area(1, 1, h_screen_rows, h_screen_cols);
}
bool os_picture_data(int num, int *height, int *width)
{
(void)num;
*height = 0;
*width = 0;
return FALSE;
}
void os_draw_picture (int num, int row, int col)
{
(void)num;
(void)row;
(void)col;
}
int os_peek_colour (void) {return BLACK_COLOUR; }

154
apps/plugins/frotz/err.c Normal file
View file

@ -0,0 +1,154 @@
/* err.c - Runtime error reporting functions
* Written by Jim Dunleavy <jim.dunleavy@erha.ie>
*
* This file is part of Frotz.
*
* Frotz is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* Frotz is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
*/
#include "frotz.h"
/* Define stuff for stricter Z-code error checking, for the generic
Unix/DOS/etc terminal-window interface. Feel free to change the way
player prefs are specified, or replace report_zstrict_error()
completely if you want to change the way errors are reported. */
/* int err_report_mode = ERR_DEFAULT_REPORT_MODE; */
static int error_count[ERR_NUM_ERRORS];
static char *err_messages[] = {
"Text buffer overflow",
"Store out of dynamic memory",
"Division by zero",
"Illegal object",
"Illegal attribute",
"No such property",
"Stack overflow",
"Call to illegal address",
"Call to non-routine",
"Stack underflow",
"Illegal opcode",
"Bad stack frame",
"Jump to illegal address",
"Can't save while in interrupt",
"Nesting stream #3 too deep",
"Illegal window",
"Illegal window property",
"Print at illegal address",
"@jin called with object 0",
"@get_child called with object 0",
"@get_parent called with object 0",
"@get_sibling called with object 0",
"@get_prop_addr called with object 0",
"@get_prop called with object 0",
"@put_prop called with object 0",
"@clear_attr called with object 0",
"@set_attr called with object 0",
"@test_attr called with object 0",
"@move_object called moving object 0",
"@move_object called moving into object 0",
"@remove_object called with object 0",
"@get_next_prop called with object 0"
};
static void print_long (unsigned long value, int base);
/*
* init_err
*
* Initialise error reporting.
*
*/
void init_err (void)
{
int i;
/* Initialize the counters. */
for (i = 0; i < ERR_NUM_ERRORS; i++)
error_count[i] = 0;
}
/*
* runtime_error
*
* An error has occurred. Ignore it, pass it to os_fatal or report
* it according to err_report_mode.
*
* errnum : Numeric code for error (1 to ERR_NUM_ERRORS)
*
*/
void runtime_error (int errnum)
{
int wasfirst;
if (errnum <= 0 || errnum > ERR_NUM_ERRORS)
return;
if (f_setup.err_report_mode == ERR_REPORT_FATAL
|| (!f_setup.ignore_errors && errnum <= ERR_MAX_FATAL)) {
flush_buffer ();
os_fatal (err_messages[errnum - 1]);
return;
}
wasfirst = (error_count[errnum - 1] == 0);
error_count[errnum - 1]++;
if ((f_setup.err_report_mode == ERR_REPORT_ALWAYS)
|| (f_setup.err_report_mode == ERR_REPORT_ONCE && wasfirst)) {
long pc;
GET_PC (pc);
print_string ("Warning: ");
print_string (err_messages[errnum - 1]);
print_string (" (PC = ");
print_long (pc, 16);
print_char (')');
if (f_setup.err_report_mode == ERR_REPORT_ONCE) {
print_string (" (will ignore further occurrences)");
} else {
print_string (" (occurence ");
print_long (error_count[errnum - 1], 10);
print_char (')');
}
new_line ();
}
} /* report_error */
/*
* print_long
*
* Print an unsigned 32bit number in decimal or hex.
*
*/
static void print_long (unsigned long value, int base)
{
unsigned long i;
char c;
for (i = (base == 10 ? 1000000000 : 0x10000000); i != 0; i /= base)
if (value >= i || i == 1) {
c = (value / i) % base;
print_char (c + (c <= 9 ? '0' : 'a' - 10));
}
}/* print_long */

1013
apps/plugins/frotz/fastmem.c Normal file

File diff suppressed because it is too large Load diff

563
apps/plugins/frotz/files.c Normal file
View file

@ -0,0 +1,563 @@
/* files.c - Transscription, recording and playback
* Copyright (c) 1995-1997 Stefan Jokisch
*
* Changes for Rockbox copyright 2009 Torne Wuff
*
* This file is part of Frotz.
*
* Frotz is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* Frotz is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
*/
#include "frotz.h"
extern void set_more_prompts (bool);
extern bool is_terminator (zchar);
extern bool read_yes_or_no (const char *);
char script_name[MAX_PATH];
char command_name[MAX_PATH];
#ifdef __MSDOS__
extern char latin1_to_ibm[];
#endif
static int script_width = 0;
int sfp = -1;
int rfp = -1;
int pfp = -1;
/*
* 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 bool script_valid = FALSE;
char new_name[MAX_PATH];
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 = rb->open (script_name, O_RDWR|O_CREAT)) != -1) {
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;
sfp = -1;
}/* 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_PATH];
if (os_read_file_name (new_name, command_name, FILE_RECORD)) {
strcpy (command_name, new_name);
if ((rfp = rb->open (new_name, O_WRONLY|O_CREAT|O_TRUNC)) != -1)
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;
rfp = -1;
}/* record_close */
/*
* record_code
*
* Helper function for record_char.
*
*/
static void record_code (int c, bool 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_PATH];
if (os_read_file_name (new_name, command_name, FILE_PLAYBACK)) {
strcpy (command_name, new_name);
if ((pfp = rb->open (new_name, O_RDONLY)) != -1) {
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;
pfp = -1;
}/* 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 */

314
apps/plugins/frotz/frotz.c Normal file
View file

@ -0,0 +1,314 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2009 Torne Wuff
*
* This program 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.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#include "plugin.h"
#include "dumb_frotz.h"
#include "lib/pluginlib_exit.h"
#include "lib/pluginlib_actions.h"
PLUGIN_HEADER
extern int frotz_main(void);
extern bool hot_key_quit(void);
f_setup_t f_setup;
extern char save_name[], auxilary_name[], script_name[], command_name[];
extern int story_fp, sfp, rfp, pfp;
static struct viewport vp[NB_SCREENS];
static void atexit_cleanup(void);
enum plugin_status plugin_start(const void* parameter)
{
int i;
char* ext;
PLUGINLIB_EXIT_INIT_ATEXIT(atexit_cleanup);
if (!parameter)
return PLUGIN_ERROR;
rb->lcd_setfont(FONT_SYSFIXED);
#if LCD_DEPTH > 1
rb->lcd_set_backdrop(NULL);
#endif
rb->lcd_clear_display();
FOR_NB_SCREENS(i)
rb->viewport_set_defaults(&vp[i], i);
story_name = parameter;
strcpy(save_name, story_name);
ext = rb->strrchr(save_name, '.');
if (ext)
*ext = '\0';
strcpy(auxilary_name, save_name);
strcpy(script_name, save_name);
strcpy(command_name, save_name);
rb->strlcat(save_name, ".sav", MAX_PATH);
rb->strlcat(auxilary_name, ".aux", MAX_PATH);
rb->strlcat(script_name, ".scr", MAX_PATH);
rb->strlcat(command_name, ".rec", MAX_PATH);
frotz_main();
return PLUGIN_OK;
}
void atexit_cleanup()
{
if (story_fp != -1)
fclose(story_fp);
if (sfp != -1)
fclose(sfp);
if (rfp != -1)
fclose(rfp);
if (pfp != -1)
fclose(pfp);
}
MENUITEM_STRINGLIST(ingame_menu, "Frotz", NULL, "Resume", "Undo", "Restart",
"Toggle input recording", "Play back input",
"Debug options", "Exit");
zchar menu(void)
{
switch (rb->do_menu(&ingame_menu, NULL, vp, true))
{
case 0:
default:
dumb_dump_screen();
return ZC_BAD;
case 1:
return ZC_HKEY_UNDO;
case 2:
return ZC_HKEY_RESTART;
case 3:
return ZC_HKEY_RECORD;
case 4:
return ZC_HKEY_PLAYBACK;
case 5:
return ZC_HKEY_DEBUG;
case 6:
return ZC_HKEY_QUIT;
}
}
const struct button_mapping* plugin_contexts[]={generic_actions};
void wait_for_key()
{
int action;
dumb_show_screen(false);
for (;;)
{
action = pluginlib_getaction(TIMEOUT_BLOCK,
plugin_contexts, 1);
switch (action)
{
case PLA_QUIT:
hot_key_quit();
break;
case PLA_FIRE:
return;
}
}
}
zchar do_input(int timeout, bool show_cursor)
{
int action;
long timeout_at;
zchar menu_ret;
dumb_show_screen(show_cursor);
/* Convert timeout (tenths of a second) to ticks */
if (timeout > 0)
timeout = (timeout * HZ) / 10;
else
timeout = TIMEOUT_BLOCK;
timeout_at = *rb->current_tick + timeout;
for (;;)
{
action = pluginlib_getaction(timeout,
plugin_contexts, 1);
switch (action)
{
case PLA_QUIT:
return ZC_HKEY_QUIT;
case PLA_MENU:
menu_ret = menu();
if (menu_ret != ZC_BAD)
return menu_ret;
timeout_at = *rb->current_tick + timeout;
break;
case PLA_FIRE:
return ZC_RETURN;
case PLA_START:
return ZC_BAD;
default:
if (timeout != TIMEOUT_BLOCK &&
!TIME_BEFORE(*rb->current_tick, timeout_at))
return ZC_TIME_OUT;
}
}
}
zchar os_read_key(int timeout, bool show_cursor)
{
int r;
char inputbuf[5];
short key;
zchar zkey;
for(;;)
{
zkey = do_input(timeout, show_cursor);
if (zkey != ZC_BAD)
return zkey;
inputbuf[0] = '\0';
r = rb->kbd_input(inputbuf, 5);
rb->lcd_setfont(FONT_SYSFIXED);
dumb_dump_screen();
if (!r)
{
rb->utf8decode(inputbuf, &key);
if (key > 0 && key < 256)
return (zchar)key;
}
}
}
zchar os_read_line(int max, zchar *buf, int timeout, int width, int continued)
{
(void)continued;
int r;
char inputbuf[256];
const char *in;
char *out;
short key;
zchar zkey;
for(;;)
{
zkey = do_input(timeout, true);
if (zkey != ZC_BAD)
return zkey;
if (max > width)
max = width;
strcpy(inputbuf, buf);
r = rb->kbd_input(inputbuf, 256);
rb->lcd_setfont(FONT_SYSFIXED);
dumb_dump_screen();
if (!r)
{
in = inputbuf;
out = buf;
while (*in && max)
{
in = rb->utf8decode(in, &key);
if (key > 0 && key < 256)
{
*out++ = key;
max--;
}
}
*out = '\0';
os_display_string(buf);
return ZC_RETURN;
}
}
}
bool read_yes_or_no(const char *s)
{
char message_line[50];
const char *message_lines[] = {message_line};
const struct text_message message = {message_lines, 1};
rb->strlcpy(message_line, s, 49);
rb->strcat(message_line, "?");
if (rb->gui_syncyesno_run(&message, NULL, NULL) == YESNO_YES)
return TRUE;
else
return FALSE;
}
zchar os_read_mouse(void)
{
return 0;
}
int os_read_file_name(char *file_name, const char *default_name, int flag)
{
(void)flag;
strcpy(file_name, default_name);
return TRUE;
}
void os_beep(int volume)
{
rb->splashf(HZ/2, "[%s-PITCHED BEEP]", (volume==1) ? "HIGH" : "LOW");
}
static unsigned char unget_buf;
static int unget_file;
int ungetc(int c, int f)
{
unget_file = f;
unget_buf = c;
return c;
}
int fgetc(int f)
{
unsigned char cb;
if (unget_file == f)
{
unget_file = -1;
return unget_buf;
}
if (rb->read(f, &cb, 1) != 1)
return EOF;
return cb;
}
int fputc(int c, int f)
{
unsigned char cb = c;
if (rb->write(f, &cb, 1) != 1)
return EOF;
return cb;
}

583
apps/plugins/frotz/frotz.h Normal file
View file

@ -0,0 +1,583 @@
/*
* frotz.h
*
* Global declarations and definitions
*
*/
#include "frotzplugin.h"
#ifndef TRUE
#define TRUE 1
#endif
#ifndef FALSE
#define FALSE 0
#endif
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 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
/*** 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 const 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 bool ostream_screen;
extern bool ostream_script;
extern bool ostream_memory;
extern bool ostream_record;
extern bool istream_replay;
extern bool message;
extern int cwin;
extern int mwin;
extern int mouse_x;
extern int mouse_y;
extern bool enable_wrapping;
extern bool enable_scripting;
extern bool enable_scrolling;
extern bool enable_buffering;
extern char *option_zcode_path; /* dg */
/*** 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 (bool);
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 *) __attribute__((noreturn));
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);
bool 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, bool);
zchar os_read_line (int, zchar *, int, int, int);
zchar os_read_mouse (void);
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 *);
#include "setup.h"

View file

@ -0,0 +1,21 @@
# __________ __ ___.
# Open \______ \ ____ ____ | | _\_ |__ _______ ___
# Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
# Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
# Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
# \/ \/ \/ \/ \/
# $Id$
#
FROTZSRCDIR := $(APPSDIR)/plugins/frotz
FROTZBUILDDIR := $(BUILDDIR)/apps/plugins/frotz
ROCKS += $(FROTZBUILDDIR)/frotz.rock
FROTZ_SRC := $(call preprocess, $(FROTZSRCDIR)/SOURCES)
FROTZ_OBJ := $(call c2obj, $(FROTZ_SRC))
# add source files to OTHER_SRC to get automatic dependencies
OTHER_SRC += $(FROTZ_SRC)
$(FROTZBUILDDIR)/frotz.rock: $(FROTZ_OBJ)

View file

@ -0,0 +1,56 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2009 Torne Wuff
*
* This program 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.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#ifndef _FROTZPLUGIN_H_
#define _FROTZPLUGIN_H_
#include "plugin.h"
/*
* pretend stdio.h is implemented. references to FILE * still have to be
* changed to int, and references to NULL into -1, but there are less of those
*/
#define fread(ptr, size, nmemb, stream) rb->read(stream, ptr, size*nmemb)
#define fwrite(ptr, size, nmemb, stream) rb->write(stream, ptr, size*nmemb)
#define fclose(stream) rb->close(stream)
#define fseek(stream, offset, whence) rb->lseek(stream, offset, whence)
#define ftell(stream) rb->lseek(stream, 0, SEEK_CUR)
#define ferror(stream) 0
/*
* we need functions for character io
*/
extern int ungetc(int c, int f);
extern int fgetc(int f);
extern int fputc(int c, int f);
/*
* this is used instead of os_read_key for more prompts and the like
* since the menu can't be used there.
*/
extern void wait_for_key(void);
/*
* wrappers
*/
#define strchr(s, c) rb->strchr(s, c)
#define strcpy(dest, src) rb->strcpy(dest, src)
#endif /* _FROTZPLUGIN_H_ */

221
apps/plugins/frotz/hotkey.c Normal file
View file

@ -0,0 +1,221 @@
/* hotkey.c - Hot key functions
* Copyright (c) 1995-1997 Stefan Jokisch
*
* Changes for Rockbox copyright 2009 Torne Wuff
*
* 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"
#include "lib/pluginlib_exit.h"
extern int restore_undo (void);
extern int read_number (void);
extern bool 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 bool hot_key_debugging (void)
{
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_playback
*
* ...allows user to turn playback on.
*
*/
static bool hot_key_playback (void)
{
rb->splash(HZ, "Playback on");
if (!istream_replay)
replay_open ();
return FALSE;
}/* hot_key_playback */
/*
* hot_key_recording
*
* ...allows user to turn recording on/off.
*
*/
static bool hot_key_recording (void)
{
if (istream_replay) {
rb->splash(HZ, "Playback off");
replay_close ();
} else if (ostream_record) {
rb->splash(HZ, "Recording off");
record_close ();
} else {
rb->splash(HZ, "Recording on");
record_open ();
}
return FALSE;
}/* hot_key_recording */
/*
* hot_key_seed
*
* ...allows user to seed the random number seed.
*
*/
static bool hot_key_seed (void)
{
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 bool hot_key_undo (void)
{
if (restore_undo ()) {
print_string ("undo\n");
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 rb->splash(HZ, "No undo information available.");
return FALSE;
}/* hot_key_undo */
/*
* hot_key_restart
*
* ...allows user to start a new game.
*
*/
static bool hot_key_restart (void)
{
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.
*
*/
bool hot_key_quit (void)
{
if (read_yes_or_no ("Do you wish to quit")) {
exit(0);
} 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.
*
*/
bool handle_hot_key (zchar key)
{
if (cwin == 0) {
bool aborting;
aborting = FALSE;
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;
}
if (aborting)
return TRUE;
}
return FALSE;
}/* handle_hot_key */

301
apps/plugins/frotz/input.c Normal file
View file

@ -0,0 +1,301 @@
/* input.c - High level input functions
* Copyright (c) 1995-1997 Stefan Jokisch
*
* Changes for Rockbox copyright 2009 Torne Wuff
*
* 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, bool);
extern zchar stream_read_input (int, zchar *, zword, zword, bool, bool);
extern void tokenise_line (zword, zword, zword, bool);
/*
* is_terminator
*
* Check if the given key is an input terminator.
*
*/
bool 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 */
extern bool read_yes_or_no (const char *s);
/*
* 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 */

205
apps/plugins/frotz/main.c Normal file
View file

@ -0,0 +1,205 @@
/* main.c - Frotz V2.40 main function
* Copyright (c) 1995-1997 Stefan Jokisch
*
* Changes for Rockbox copyright 2009 Torne Wuff
*
* 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
*/
/*
* This is an interpreter for Infocom V1 to V6 games. It also supports
* the recently defined V7 and V8 games. Please report bugs to
*
* s.jokisch@avu.de
*
*/
#include "frotz.h"
#ifndef MSDOS_16BIT
#define cdecl
#endif
extern void interpret (void);
extern void init_memory (void);
extern void init_undo (void);
extern void reset_memory (void);
extern void init_buffer (void);
extern void init_sound (void);
extern void init_process (void);
extern void script_close (void);
extern void record_close (void);
extern void replay_close (void);
/* Story file name, id number and size */
const char *story_name = 0;
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 */
bool ostream_screen = TRUE;
bool ostream_script = FALSE;
bool ostream_memory = FALSE;
bool ostream_record = FALSE;
bool istream_replay = FALSE;
bool message = FALSE;
/* Current window and mouse data */
int cwin = 0;
int mwin = 0;
int mouse_y = 0;
int mouse_x = 0;
/* Window attributes */
bool enable_wrapping = FALSE;
bool enable_scripting = FALSE;
bool enable_scrolling = FALSE;
bool 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;
/*
* z_piracy, branch if the story file is a legal copy.
*
* no zargs used
*
*/
void z_piracy (void)
{
branch (!f_setup.piracy);
}/* z_piracy */
/*
* main
*
* Prepare and run the game.
*
*/
int cdecl frotz_main (void)
{
os_init_setup ();
init_buffer ();
init_err ();
init_memory ();
init_process ();
init_sound ();
os_init_screen ();
init_undo ();
z_restart ();
interpret ();
script_close ();
record_close ();
replay_close ();
reset_memory ();
os_reset_screen ();
return 0;
}/* main */

261
apps/plugins/frotz/math.c Normal file
View file

@ -0,0 +1,261 @@
/* math.c - Arithmetic, compare and logical opcodes
* Copyright (c) 1995-1997 Stefan Jokisch
*
* This file is part of Frotz.
*
* Frotz is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* Frotz is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
*/
#include "frotz.h"
/*
* z_add, 16bit addition.
*
* zargs[0] = first value
* zargs[1] = second value
*
*/
void z_add (void)
{
store ((zword) ((short) zargs[0] + (short) zargs[1]));
}/* z_add */
/*
* z_and, bitwise AND operation.
*
* zargs[0] = first value
* zargs[1] = second value
*
*/
void z_and (void)
{
store ((zword) (zargs[0] & zargs[1]));
}/* z_and */
/*
* z_art_shift, arithmetic SHIFT operation.
*
* zargs[0] = value
* zargs[1] = #positions to shift left (positive) or right
*
*/
void z_art_shift (void)
{
if ((short) zargs[1] > 0)
store ((zword) ((short) zargs[0] << (short) zargs[1]));
else
store ((zword) ((short) zargs[0] >> - (short) zargs[1]));
}/* z_art_shift */
/*
* z_div, signed 16bit division.
*
* zargs[0] = first value
* zargs[1] = second value
*
*/
void z_div (void)
{
if (zargs[1] == 0)
runtime_error (ERR_DIV_ZERO);
store ((zword) ((short) zargs[0] / (short) zargs[1]));
}/* z_div */
/*
* z_je, branch if the first value equals any of the following.
*
* zargs[0] = first value
* zargs[1] = second value (optional)
* ...
* zargs[3] = fourth value (optional)
*
*/
void z_je (void)
{
branch (
zargc > 1 && (zargs[0] == zargs[1] || (
zargc > 2 && (zargs[0] == zargs[2] || (
zargc > 3 && (zargs[0] == zargs[3]))))));
}/* z_je */
/*
* z_jg, branch if the first value is greater than the second.
*
* zargs[0] = first value
* zargs[1] = second value
*
*/
void z_jg (void)
{
branch ((short) zargs[0] > (short) zargs[1]);
}/* z_jg */
/*
* z_jl, branch if the first value is less than the second.
*
* zargs[0] = first value
* zargs[1] = second value
*
*/
void z_jl (void)
{
branch ((short) zargs[0] < (short) zargs[1]);
}/* z_jl */
/*
* z_jz, branch if value is zero.
*
* zargs[0] = value
*
*/
void z_jz (void)
{
branch ((short) zargs[0] == 0);
}/* z_jz */
/*
* z_log_shift, logical SHIFT operation.
*
* zargs[0] = value
* zargs[1] = #positions to shift left (positive) or right (negative)
*
*/
void z_log_shift (void)
{
if ((short) zargs[1] > 0)
store ((zword) (zargs[0] << (short) zargs[1]));
else
store ((zword) (zargs[0] >> - (short) zargs[1]));
}/* z_log_shift */
/*
* z_mod, remainder after signed 16bit division.
*
* zargs[0] = first value
* zargs[1] = second value
*
*/
void z_mod (void)
{
if (zargs[1] == 0)
runtime_error (ERR_DIV_ZERO);
store ((zword) ((short) zargs[0] % (short) zargs[1]));
}/* z_mod */
/*
* z_mul, 16bit multiplication.
*
* zargs[0] = first value
* zargs[1] = second value
*
*/
void z_mul (void)
{
store ((zword) ((short) zargs[0] * (short) zargs[1]));
}/* z_mul */
/*
* z_not, bitwise NOT operation.
*
* zargs[0] = value
*
*/
void z_not (void)
{
store ((zword) ~zargs[0]);
}/* z_not */
/*
* z_or, bitwise OR operation.
*
* zargs[0] = first value
* zargs[1] = second value
*
*/
void z_or (void)
{
store ((zword) (zargs[0] | zargs[1]));
}/* z_or */
/*
* z_sub, 16bit substraction.
*
* zargs[0] = first value
* zargs[1] = second value
*
*/
void z_sub (void)
{
store ((zword) ((short) zargs[0] - (short) zargs[1]));
}/* z_sub */
/*
* z_test, branch if all the flags of a bit mask are set in a value.
*
* zargs[0] = value to be examined
* zargs[1] = bit mask
*
*/
void z_test (void)
{
branch ((zargs[0] & zargs[1]) == zargs[1]);
}/* z_test */

1003
apps/plugins/frotz/object.c Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,798 @@
/* process.c - Interpreter loop and program control
* Copyright (c) 1995-1997 Stefan Jokisch
*
* This file is part of Frotz.
*
* Frotz is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* Frotz is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
*/
#include "frotz.h"
#ifdef DJGPP
#include "djfrotz.h"
#endif
zword zargs[8];
int zargc;
static int finished = 0;
static void __extended__ (void);
static void __illegal__ (void);
void (*op0_opcodes[0x10]) (void) = {
z_rtrue,
z_rfalse,
z_print,
z_print_ret,
z_nop,
z_save,
z_restore,
z_restart,
z_ret_popped,
z_catch,
z_quit,
z_new_line,
z_show_status,
z_verify,
__extended__,
z_piracy
};
void (*op1_opcodes[0x10]) (void) = {
z_jz,
z_get_sibling,
z_get_child,
z_get_parent,
z_get_prop_len,
z_inc,
z_dec,
z_print_addr,
z_call_s,
z_remove_obj,
z_print_obj,
z_ret,
z_jump,
z_print_paddr,
z_load,
z_call_n
};
void (*var_opcodes[0x40]) (void) = {
__illegal__,
z_je,
z_jl,
z_jg,
z_dec_chk,
z_inc_chk,
z_jin,
z_test,
z_or,
z_and,
z_test_attr,
z_set_attr,
z_clear_attr,
z_store,
z_insert_obj,
z_loadw,
z_loadb,
z_get_prop,
z_get_prop_addr,
z_get_next_prop,
z_add,
z_sub,
z_mul,
z_div,
z_mod,
z_call_s,
z_call_n,
z_set_colour,
z_throw,
__illegal__,
__illegal__,
__illegal__,
z_call_s,
z_storew,
z_storeb,
z_put_prop,
z_read,
z_print_char,
z_print_num,
z_random,
z_push,
z_pull,
z_split_window,
z_set_window,
z_call_s,
z_erase_window,
z_erase_line,
z_set_cursor,
z_get_cursor,
z_set_text_style,
z_buffer_mode,
z_output_stream,
z_input_stream,
z_sound_effect,
z_read_char,
z_scan_table,
z_not,
z_call_n,
z_call_n,
z_tokenise,
z_encode_text,
z_copy_table,
z_print_table,
z_check_arg_count
};
void (*ext_opcodes[0x1d]) (void) = {
z_save,
z_restore,
z_log_shift,
z_art_shift,
z_set_font,
z_draw_picture,
z_picture_data,
z_erase_picture,
z_set_margins,
z_save_undo,
z_restore_undo,
z_print_unicode,
z_check_unicode,
__illegal__,
__illegal__,
__illegal__,
z_move_window,
z_window_size,
z_window_style,
z_get_wind_prop,
z_scroll_window,
z_pop_stack,
z_read_mouse,
z_mouse_window,
z_push_stack,
z_put_wind_prop,
z_print_form,
z_make_menu,
z_picture_table
};
/*
* init_process
*
* Initialize process variables.
*
*/
void init_process (void)
{
finished = 0;
} /* init_process */
/*
* load_operand
*
* Load an operand, either a variable or a constant.
*
*/
static void load_operand (zbyte type)
{
zword value;
if (type & 2) { /* variable */
zbyte variable;
CODE_BYTE (variable)
if (variable == 0)
value = *sp++;
else if (variable < 16)
value = *(fp - variable);
else {
zword addr = h_globals + 2 * (variable - 16);
LOW_WORD (addr, value)
}
} else if (type & 1) { /* small constant */
zbyte bvalue;
CODE_BYTE (bvalue)
value = bvalue;
} else CODE_WORD (value) /* large constant */
zargs[zargc++] = value;
}/* load_operand */
/*
* load_all_operands
*
* Given the operand specifier byte, load all (up to four) operands
* for a VAR or EXT opcode.
*
*/
static void load_all_operands (zbyte specifier)
{
int i;
for (i = 6; i >= 0; i -= 2) {
zbyte type = (specifier >> i) & 0x03;
if (type == 3)
break;
load_operand (type);
}
}/* load_all_operands */
/*
* interpret
*
* Z-code interpreter main loop
*
*/
void interpret (void)
{
do {
zbyte opcode;
CODE_BYTE (opcode)
zargc = 0;
if (opcode < 0x80) { /* 2OP opcodes */
load_operand ((zbyte) (opcode & 0x40) ? 2 : 1);
load_operand ((zbyte) (opcode & 0x20) ? 2 : 1);
var_opcodes[opcode & 0x1f] ();
} else if (opcode < 0xb0) { /* 1OP opcodes */
load_operand ((zbyte) (opcode >> 4));
op1_opcodes[opcode & 0x0f] ();
} else if (opcode < 0xc0) { /* 0OP opcodes */
op0_opcodes[opcode - 0xb0] ();
} else { /* VAR opcodes */
zbyte specifier1;
zbyte specifier2;
if (opcode == 0xec || opcode == 0xfa) { /* opcodes 0xec */
CODE_BYTE (specifier1) /* and 0xfa are */
CODE_BYTE (specifier2) /* call opcodes */
load_all_operands (specifier1); /* with up to 8 */
load_all_operands (specifier2); /* arguments */
} else {
CODE_BYTE (specifier1)
load_all_operands (specifier1);
}
var_opcodes[opcode - 0xc0] ();
}
#if defined(DJGPP) && defined(SOUND_SUPPORT)
if (end_of_sound_flag)
end_of_sound ();
#endif
} while (finished == 0);
finished--;
}/* interpret */
/*
* call
*
* Call a subroutine. Save PC and FP then load new PC and initialise
* new stack frame. Note that the caller may legally provide less or
* more arguments than the function actually has. The call type "ct"
* can be 0 (z_call_s), 1 (z_call_n) or 2 (direct call).
*
*/
void call (zword routine, int argc, zword *args, int ct)
{
long pc;
zword value;
zbyte count;
int i;
if (sp - stack < 4)
runtime_error (ERR_STK_OVF);
GET_PC (pc)
*--sp = (zword) (pc >> 9);
*--sp = (zword) (pc & 0x1ff);
*--sp = (zword) (fp - stack - 1);
*--sp = (zword) (argc | (ct << (f_setup.save_quetzal ? 12 : 8)));
fp = sp;
frame_count++;
/* Calculate byte address of routine */
if (h_version <= V3)
pc = (long) routine << 1;
else if (h_version <= V5)
pc = (long) routine << 2;
else if (h_version <= V7)
pc = ((long) routine << 2) + ((long) h_functions_offset << 3);
else /* h_version == V8 */
pc = (long) routine << 3;
if (pc >= story_size)
runtime_error (ERR_ILL_CALL_ADDR);
SET_PC (pc)
/* Initialise local variables */
CODE_BYTE (count)
if (count > 15)
runtime_error (ERR_CALL_NON_RTN);
if (sp - stack < count)
runtime_error (ERR_STK_OVF);
if (f_setup.save_quetzal)
fp[0] |= (zword) count << 8; /* Save local var count for Quetzal. */
value = 0;
for (i = 0; i < count; i++) {
if (h_version <= V4) /* V1 to V4 games provide default */
CODE_WORD (value) /* values for all local variables */
*--sp = (zword) ((argc-- > 0) ? args[i] : value);
}
/* Start main loop for direct calls */
if (ct == 2)
interpret ();
}/* call */
/*
* ret
*
* Return from the current subroutine and restore the previous stack
* frame. The result may be stored (0), thrown away (1) or pushed on
* the stack (2). In the latter case a direct call has been finished
* and we must exit the interpreter loop.
*
*/
void ret (zword value)
{
long pc;
int ct;
if (sp > fp)
runtime_error (ERR_STK_UNDF);
sp = fp;
ct = *sp++ >> (f_setup.save_quetzal ? 12 : 8);
frame_count--;
fp = stack + 1 + *sp++;
pc = *sp++;
pc = ((long) *sp++ << 9) | pc;
SET_PC (pc)
/* Handle resulting value */
if (ct == 0)
store (value);
if (ct == 2)
*--sp = value;
/* Stop main loop for direct calls */
if (ct == 2)
finished++;
}/* ret */
/*
* branch
*
* Take a jump after an instruction based on the flag, either true or
* false. The branch can be short or long; it is encoded in one or two
* bytes respectively. When bit 7 of the first byte is set, the jump
* takes place if the flag is true; otherwise it is taken if the flag
* is false. When bit 6 of the first byte is set, the branch is short;
* otherwise it is long. The offset occupies the bottom 6 bits of the
* first byte plus all the bits in the second byte for long branches.
* Uniquely, an offset of 0 means return false, and an offset of 1 is
* return true.
*
*/
void branch (bool 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 */

View file

@ -0,0 +1,541 @@
/* quetzal.c - Saving and restoring of Quetzal files.
* Written by Martin Frost <mdf@doc.ic.ac.uk>
*
* Changes for Rockbox copyright 2009 Torne Wuff
*
* 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 far
#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 bool read_word (int 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 bool read_long (int 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 (int svf, int 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 ((signed)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 ((signed)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 (int svf, int 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;
}

View file

@ -0,0 +1,82 @@
/* random.c - Z-machine random number generator
* Copyright (c) 1995-1997 Stefan Jokisch
*
* This file is part of Frotz.
*
* Frotz is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* Frotz is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
*/
#include "frotz.h"
static long A = 1;
static int interval = 0;
static int counter = 0;
/*
* seed_random
*
* Set the seed value for the random number generator.
*
*/
void seed_random (int value)
{
if (value == 0) { /* ask interface for seed value */
A = os_random_seed ();
interval = 0;
} else if (value < 1000) { /* special seed value */
counter = 0;
interval = value;
} else { /* standard seed value */
A = value;
interval = 0;
}
}/* seed_random */
/*
* z_random, store a random number or set the random number seed.
*
* zargs[0] = range (positive) or seed value (negative)
*
*/
void z_random ()
{
if ((short) zargs[0] <= 0) { /* set random seed */
seed_random (- (short) zargs[0]);
store (0);
} else { /* generate random number */
zword result;
if (interval != 0) { /* ...in special mode */
result = counter++;
if (counter == interval) counter = 0;
} else { /* ...in standard mode */
A = 0x015a4e35L * A + 1;
result = (A >> 16) & 0x7fff;
}
store ((zword) (result % zargs[0] + 1));
}
}/* z_random */

View file

@ -0,0 +1,172 @@
/* redirect.c - Output redirection to Z-machine memory
* Copyright (c) 1995-1997 Stefan Jokisch
*
* This file is part of Frotz.
*
* Frotz is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* Frotz is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
*/
#include "frotz.h"
#define MAX_NESTING 16
extern zword get_max_width (zword);
static int depth = -1;
static struct {
zword xsize;
zword table;
zword width;
zword total;
} redirect[MAX_NESTING];
/*
* memory_open
*
* Begin output redirection to the memory of the Z-machine.
*
*/
void memory_open (zword table, zword xsize, bool 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
apps/plugins/frotz/screen.c Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,67 @@
/*
* Various status thingies for the interpreter and interface.
*
*/
typedef struct frotz_setup_struct {
int attribute_assignment; /* done */
int attribute_testing; /* done */
int context_lines; /* done */
int object_locating; /* done */
int object_movement; /* done */
int left_margin; /* done */
int right_margin; /* done */
int ignore_errors; /* done */
int interpreter_number; /* Just dumb frotz now */
int piracy; /* done */
int undo_slots; /* done */
int expand_abbreviations; /* done */
int script_cols; /* done */
int save_quetzal; /* done */
int sound; /* done */
int err_report_mode; /* done */
} f_setup_t;
extern f_setup_t f_setup;
typedef struct zcode_header_struct {
zbyte h_version;
zbyte h_config;
zword h_release;
zword h_resident_size;
zword h_start_pc;
zword h_dictionary;
zword h_objects;
zword h_globals;
zword h_dynamic_size;
zword h_flags;
zbyte h_serial[6];
zword h_abbreviations;
zword h_file_size;
zword h_checksum;
zbyte h_interpreter_number;
zbyte h_interpreter_version;
zbyte h_screen_rows;
zbyte h_screen_cols;
zword h_screen_width;
zword h_screen_height;
zbyte h_font_height;
zbyte h_font_width;
zword h_functions_offset;
zword h_strings_offset;
zbyte h_default_background;
zbyte h_default_foreground;
zword h_terminating_keys;
zword h_line_width;
zbyte h_standard_high;
zbyte h_standard_low;
zword h_alphabet;
zword h_extension_table;
zbyte h_user_name[8];
zword hx_table_size;
zword hx_mouse_x;
zword hx_mouse_y;
zword hx_unicode_table;
} z_header_t;

204
apps/plugins/frotz/sound.c Normal file
View file

@ -0,0 +1,204 @@
/* sound.c - Sound effect function
* Copyright (c) 1995-1997 Stefan Jokisch
*
* This file is part of Frotz.
*
* Frotz is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* Frotz is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
*/
#include "frotz.h"
#ifdef DJGPP
#include "djfrotz.h"
#endif
#define EFFECT_PREPARE 1
#define EFFECT_PLAY 2
#define EFFECT_STOP 3
#define EFFECT_FINISH_WITH 4
extern int direct_call (zword);
static zword routine = 0;
static int next_sample = 0;
static int next_volume = 0;
static bool locked = FALSE;
static bool 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 */

365
apps/plugins/frotz/stream.c Normal file
View file

@ -0,0 +1,365 @@
/* stream.c - IO stream implementation
* Copyright (c) 1995-1997 Stefan Jokisch
*
* This file is part of Frotz.
*
* Frotz is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* Frotz is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
*/
#include "frotz.h"
extern bool handle_hot_key (zchar);
extern bool validate_click (void);
extern void replay_open (void);
extern void replay_close (void);
extern void memory_open (zword, zword, bool);
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, bool);
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,
bool 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,
bool hot_keys,
bool 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
apps/plugins/frotz/table.c Normal file
View file

@ -0,0 +1,193 @@
/* table.c - Table handling opcodes
* Copyright (c) 1995-1997 Stefan Jokisch
*
* This file is part of Frotz.
*
* Frotz is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* Frotz is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
*/
#include "frotz.h"
/*
* z_copy_table, copy a table or fill it with zeroes.
*
* zargs[0] = address of table
* zargs[1] = destination address or 0 for fill
* zargs[2] = size of table
*
* Note: Copying is safe even when source and destination overlap; but
* if zargs[1] is negative the table _must_ be copied forwards.
*
*/
void z_copy_table (void)
{
zword addr;
zword size = zargs[2];
zbyte value;
int i;
if (zargs[1] == 0) /* zero table */
for (i = 0; i < size; i++)
storeb ((zword) (zargs[0] + i), 0);
else if ((short) size < 0 || zargs[0] > zargs[1]) /* copy forwards */
for (i = 0; i < (((short) size < 0) ? - (short) size : size); i++) {
addr = zargs[0] + i;
LOW_BYTE (addr, value)
storeb ((zword) (zargs[1] + i), value);
}
else /* copy backwards */
for (i = size - 1; i >= 0; i--) {
addr = zargs[0] + i;
LOW_BYTE (addr, value)
storeb ((zword) (zargs[1] + i), value);
}
}/* z_copy_table */
/*
* z_loadb, store a value from a table of bytes.
*
* zargs[0] = address of table
* zargs[1] = index of table entry to store
*
*/
void z_loadb (void)
{
zword addr = zargs[0] + zargs[1];
zbyte value;
LOW_BYTE (addr, value)
store (value);
}/* z_loadb */
/*
* z_loadw, store a value from a table of words.
*
* zargs[0] = address of table
* zargs[1] = index of table entry to store
*
*/
void z_loadw (void)
{
zword addr = zargs[0] + 2 * zargs[1];
zword value;
LOW_WORD (addr, value)
store (value);
}/* z_loadw */
/*
* z_scan_table, find and store the address of a target within a table.
*
* zargs[0] = target value to be searched for
* zargs[1] = address of table
* zargs[2] = number of table entries to check value against
* zargs[3] = type of table (optional, defaults to 0x82)
*
* Note: The table is a word array if bit 7 of zargs[3] is set; otherwise
* it's a byte array. The lower bits hold the address step.
*
*/
void z_scan_table (void)
{
zword addr = zargs[1];
int i;
/* Supply default arguments */
if (zargc < 4)
zargs[3] = 0x82;
/* Scan byte or word array */
for (i = 0; i < zargs[2]; i++) {
if (zargs[3] & 0x80) { /* scan word array */
zword wvalue;
LOW_WORD (addr, wvalue)
if (wvalue == zargs[0])
goto finished;
} else { /* scan byte array */
zbyte bvalue;
LOW_BYTE (addr, bvalue)
if (bvalue == zargs[0])
goto finished;
}
addr += zargs[3] & 0x7f;
}
addr = 0;
finished:
store (addr);
branch (addr);
}/* z_scan_table */
/*
* z_storeb, write a byte into a table of bytes.
*
* zargs[0] = address of table
* zargs[1] = index of table entry
* zargs[2] = value to be written
*
*/
void z_storeb (void)
{
storeb ((zword) (zargs[0] + zargs[1]), zargs[2]);
}/* z_storeb */
/*
* z_storew, write a word into a table of words.
*
* zargs[0] = address of table
* zargs[1] = index of table entry
* zargs[2] = value to be written
*
*/
void z_storew (void)
{
storew ((zword) (zargs[0] + 2 * zargs[1]), zargs[2]);
}/* z_storew */

1109
apps/plugins/frotz/text.c Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,304 @@
/* variable.c - Variable and stack related opcodes
* Copyright (c) 1995-1997 Stefan Jokisch
*
* This file is part of Frotz.
*
* Frotz is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* Frotz is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
*/
#include "frotz.h"
/*
* z_dec, decrement a variable.
*
* zargs[0] = variable to decrement
*
*/
void z_dec (void)
{
zword value;
if (zargs[0] == 0)
(*sp)--;
else if (zargs[0] < 16)
(*(fp - zargs[0]))--;
else {
zword addr = h_globals + 2 * (zargs[0] - 16);
LOW_WORD (addr, value)
value--;
SET_WORD (addr, value)
}
}/* z_dec */
/*
* z_dec_chk, decrement a variable and branch if now less than value.
*
* zargs[0] = variable to decrement
* zargs[1] = value to check variable against
*
*/
void z_dec_chk (void)
{
zword value;
if (zargs[0] == 0)
value = --(*sp);
else if (zargs[0] < 16)
value = --(*(fp - zargs[0]));
else {
zword addr = h_globals + 2 * (zargs[0] - 16);
LOW_WORD (addr, value)
value--;
SET_WORD (addr, value)
}
branch ((short) value < (short) zargs[1]);
}/* z_dec_chk */
/*
* z_inc, increment a variable.
*
* zargs[0] = variable to increment
*
*/
void z_inc (void)
{
zword value;
if (zargs[0] == 0)
(*sp)++;
else if (zargs[0] < 16)
(*(fp - zargs[0]))++;
else {
zword addr = h_globals + 2 * (zargs[0] - 16);
LOW_WORD (addr, value)
value++;
SET_WORD (addr, value)
}
}/* z_inc */
/*
* z_inc_chk, increment a variable and branch if now greater than value.
*
* zargs[0] = variable to increment
* zargs[1] = value to check variable against
*
*/
void z_inc_chk (void)
{
zword value;
if (zargs[0] == 0)
value = ++(*sp);
else if (zargs[0] < 16)
value = ++(*(fp - zargs[0]));
else {
zword addr = h_globals + 2 * (zargs[0] - 16);
LOW_WORD (addr, value)
value++;
SET_WORD (addr, value)
}
branch ((short) value > (short) zargs[1]);
}/* z_inc_chk */
/*
* z_load, store the value of a variable.
*
* zargs[0] = variable to store
*
*/
void z_load (void)
{
zword value;
if (zargs[0] == 0)
value = *sp;
else if (zargs[0] < 16)
value = *(fp - zargs[0]);
else {
zword addr = h_globals + 2 * (zargs[0] - 16);
LOW_WORD (addr, value)
}
store (value);
}/* z_load */
/*
* z_pop, pop a value off the game stack and discard it.
*
* no zargs used
*
*/
void z_pop (void)
{
sp++;
}/* z_pop */
/*
* z_pop_stack, pop n values off the game or user stack and discard them.
*
* zargs[0] = number of values to discard
* zargs[1] = address of user stack (optional)
*
*/
void z_pop_stack (void)
{
if (zargc == 2) { /* it's a user stack */
zword size;
zword addr = zargs[1];
LOW_WORD (addr, size)
size += zargs[0];
storew (addr, size);
} else sp += zargs[0]; /* it's the game stack */
}/* z_pop_stack */
/*
* z_pull, pop a value off...
*
* a) ...the game or a user stack and store it (V6)
*
* zargs[0] = address of user stack (optional)
*
* b) ...the game stack and write it to a variable (other than V6)
*
* zargs[0] = variable to write value to
*
*/
void z_pull (void)
{
zword value;
if (h_version != V6) { /* not a V6 game, pop stack and write */
value = *sp++;
if (zargs[0] == 0)
*sp = value;
else if (zargs[0] < 16)
*(fp - zargs[0]) = value;
else {
zword addr = h_globals + 2 * (zargs[0] - 16);
SET_WORD (addr, value)
}
} else { /* it's V6, but is there a user stack? */
if (zargc == 1) { /* it's a user stack */
zword size;
zword addr = zargs[0];
LOW_WORD (addr, size)
size++;
storew (addr, size);
addr += 2 * size;
LOW_WORD (addr, value)
} else value = *sp++; /* it's the game stack */
store (value);
}
}/* z_pull */
/*
* z_push, push a value onto the game stack.
*
* zargs[0] = value to push onto the stack
*
*/
void z_push (void)
{
*--sp = zargs[0];
}/* z_push */
/*
* z_push_stack, push a value onto a user stack then branch if successful.
*
* zargs[0] = value to push onto the stack
* zargs[1] = address of user stack
*
*/
void z_push_stack (void)
{
zword size;
zword addr = zargs[1];
LOW_WORD (addr, size)
if (size != 0) {
storew ((zword) (addr + 2 * size), zargs[0]);
size--;
storew (addr, size);
}
branch (size);
}/* z_push_stack */
/*
* z_store, write a value to a variable.
*
* zargs[0] = variable to be written to
* zargs[1] = value to write
*
*/
void z_store (void)
{
zword value = zargs[1];
if (zargs[0] == 0)
*sp = value;
else if (zargs[0] < 16)
*(fp - zargs[0]) = value;
else {
zword addr = h_globals + 2 * (zargs[0] - 16);
SET_WORD (addr, value)
}
}/* z_store */

View file

@ -61,3 +61,11 @@ link,viewers/shortcuts_view,-
lua,viewers/lua,-
ipod,viewers/crypt_firmware,-
ipodx,viewers/crypt_firmware,-
z1,viewers/frotz,-
z2,viewers/frotz,-
z3,viewers/frotz,-
z4,viewers/frotz,-
z5,viewers/frotz,-
z6,viewers/frotz,-
z7,viewers/frotz,-
z8,viewers/frotz,-

67
manual/plugins/frotz.tex Normal file
View file

@ -0,0 +1,67 @@
% $Id$ %
\subsection{Frotz}
Frotz is a Z-Machine interpreter for playing Infocom's interactive fiction
games, and newer games using the same format. To start a game open a
\fname{.z1 - .z8} file in the \setting{File Browser}. Most modern games are
in the \fname{.z5} or \fname{.z8} format but the older formats used by
Infocom are supported.
Z-Machine games are text based and most depend heavily on typed commands.
The virtual keyboard is used for text entry, both for typing entire lines
and for typing single characters when the game requires single character
input.
Sounds, pictures, colour and Unicode are not currently supported, but
the interpreter informs the game of this and almost all games will
adapt so that they are still playable. This port of Frotz attempts to be
compliant with the Z-Machine Specification version 1.0.
Some places where you can find Z-Machine games, and information about
interactive fiction:
\begin{itemize}
\item The Interactive Fiction Archive, where many free modern works
can be downloaded:
\url{http://www.ifarchive.org/}
\item The specific folder on the if-archive containing Z-Machine games:
\url{http://www.ifarchive.org/indexes/if-archiveXgamesXzcode.html}
\item The Infocom homepage, with information about how to get the
classic commercial Infocom games:
\url{http://www.csd.uwo.ca/Infocom/}
\item The Frotz homepage (for the original Unix port):
\url{http://frotz.sourceforge.net/}
\item A Beginner's Guide to Playing Interactive Fiction:
\url{http://www.microheaven.com/IFGuide/}
\end{itemize}
\begin{table}
\begin{btnmap}{}{}
\opt{RECORDER_PAD,IRIVER_H100_PAD,IRIVER_H300_PAD}{\ButtonOn}
\opt{IPOD_4G_PAD,IPOD_3G_PAD,IRIVER_H10_PAD,GIGABEAT_S_PAD}{\ButtonPlay}
\opt{ONDIO_PAD}{\ButtonMenu}
\opt{IAUDIO_X5_PAD,MROBE100_PAD}{\ButtonPower}
\opt{SANSA_E200_PAD,SANSA_C200_PAD}{\ButtonUp}
\opt{GIGABEAT_PAD}{\ButtonA}
\opt{HAVEREMOTEKEYMAP}{&
\opt{IRIVER_RC_H100_PAD}{\ButtonRCOn}
}
& Display keyboard to enter text\\
\opt{IRIVER_H100_PAD,IRIVER_H300_PAD,IAUDIO_X5_PAD,IPOD_4G_PAD,IPOD_3G_PAD%
,SANSA_E200_PAD,SANSA_C200_PAD,GIGABEAT_PAD,GIGABEAT_S_PAD,MROBE100_PAD}{\ButtonSelect}
\opt{RECORDER_PAD}{\ButtonPlay}
\opt{ONDIO_PAD}{\ButtonUp}
\opt{IRIVER_H10_PAD}{\ButtonRew}
\opt{COWON_D2_PAD}{\ButtonMenu{}, \TouchCenter{} or \TouchBottomMiddle}
\opt{HAVEREMOTEKEYMAP}{& }
& Press enter\\
\opt{RECORDER_PAD,ONDIO_PAD,IRIVER_H100_PAD,IRIVER_H300_PAD}{\ButtonOff}
\opt{IPOD_4G_PAD,IPOD_3G_PAD}{\ButtonMenu}
\opt{IAUDIO_X5_PAD,IRIVER_H10_PAD,SANSA_E200_PAD,SANSA_C200_PAD,GIGABEAT_PAD%
,MROBE100_PAD}{\ButtonPower}
\opt{GIGABEAT_S_PAD}{\ButtonBack}
\opt{COWON_D2_PAD}{\ButtonPower{} or \TouchBottomRight}
\opt{HAVEREMOTEKEYMAP}{&
\opt{IRIVER_RC_H100_PAD}{\ButtonRCStop}
}
& Open Frotz menu (not available at MORE prompts)\\
\end{btnmap}
\end{table}

View file

@ -135,6 +135,7 @@ option from the \setting{Context Menu} (see \reference{ref:Contextmenu}).}
{}{}
Shortcuts & \fname{.link} & \\
Chip-8 Emulator & \fname{.ch8} & \\
Frotz & \fname{.z1 - .z8} & \\
JPEG Viewer & \fname{.jpg, .jpeg} & \\
Lua scripting language& \fname{.lua} & \\
Midiplay & \fname{.mid, .midi} & \\
@ -160,6 +161,8 @@ option from the \setting{Context Menu} (see \reference{ref:Contextmenu}).}
\opt{lcd_bitmap}{\input{plugins/chip8emulator.tex}}
\opt{lcd_bitmap}{\input{plugins/frotz.tex}}
\opt{lcd_bitmap}{\input{plugins/jpegviewer.tex}}
\opt{large_plugin_buffer}{\input{plugins/lua.tex}}