summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTorne Wuff <torne@wolfpuppy.org.uk>2010-01-17 22:15:13 +0000
committerTorne Wuff <torne@wolfpuppy.org.uk>2010-01-17 22:15:13 +0000
commit7f28c94eda576e3f972fc05468188986f2e45885 (patch)
treee03b94613028d16855a5d3df0f4853e077931214
parent563f2602f471208cb8544a36539a79dcceaad643 (diff)
downloadrockbox-7f28c94eda576e3f972fc05468188986f2e45885.tar.gz
rockbox-7f28c94eda576e3f972fc05468188986f2e45885.zip
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
-rw-r--r--apps/plugins/CATEGORIES1
-rw-r--r--apps/plugins/SUBDIRS4
-rw-r--r--apps/plugins/frotz/SOURCES22
-rw-r--r--apps/plugins/frotz/STATUS45
-rw-r--r--apps/plugins/frotz/buffer.c152
-rw-r--r--apps/plugins/frotz/dumb_frotz.h19
-rw-r--r--apps/plugins/frotz/dumb_init.c86
-rw-r--r--apps/plugins/frotz/dumb_output.c256
-rw-r--r--apps/plugins/frotz/err.c154
-rw-r--r--apps/plugins/frotz/fastmem.c1013
-rw-r--r--apps/plugins/frotz/files.c563
-rw-r--r--apps/plugins/frotz/frotz.c314
-rw-r--r--apps/plugins/frotz/frotz.h583
-rw-r--r--apps/plugins/frotz/frotz.make21
-rw-r--r--apps/plugins/frotz/frotzplugin.h56
-rw-r--r--apps/plugins/frotz/hotkey.c221
-rw-r--r--apps/plugins/frotz/input.c301
-rw-r--r--apps/plugins/frotz/main.c205
-rw-r--r--apps/plugins/frotz/math.c261
-rw-r--r--apps/plugins/frotz/object.c1003
-rw-r--r--apps/plugins/frotz/process.c798
-rw-r--r--apps/plugins/frotz/quetzal.c541
-rw-r--r--apps/plugins/frotz/random.c82
-rw-r--r--apps/plugins/frotz/redirect.c172
-rw-r--r--apps/plugins/frotz/screen.c1743
-rw-r--r--apps/plugins/frotz/setup.h67
-rw-r--r--apps/plugins/frotz/sound.c204
-rw-r--r--apps/plugins/frotz/stream.c365
-rw-r--r--apps/plugins/frotz/table.c193
-rw-r--r--apps/plugins/frotz/text.c1109
-rw-r--r--apps/plugins/frotz/variable.c304
-rw-r--r--apps/plugins/viewers.config8
-rw-r--r--manual/plugins/frotz.tex67
-rw-r--r--manual/plugins/main.tex3
34 files changed, 10936 insertions, 0 deletions
diff --git a/apps/plugins/CATEGORIES b/apps/plugins/CATEGORIES
index a41514f93d..cf6e2663fa 100644
--- a/apps/plugins/CATEGORIES
+++ b/apps/plugins/CATEGORIES
@@ -28,6 +28,7 @@ fire,demos
28fireworks,demos 28fireworks,demos
29firmware_flash,apps 29firmware_flash,apps
30flipit,games 30flipit,games
31frotz,viewers
31goban,games 32goban,games
32greyscale,demos 33greyscale,demos
33helloworld,demos 34helloworld,demos
diff --git a/apps/plugins/SUBDIRS b/apps/plugins/SUBDIRS
index 0993e8221c..5cb9356317 100644
--- a/apps/plugins/SUBDIRS
+++ b/apps/plugins/SUBDIRS
@@ -25,6 +25,10 @@ jpeg
25sudoku 25sudoku
26reversi 26reversi
27goban 27goban
28/* setjmp/longjmp are not implemented on sh */
29#if (CONFIG_CPU != SH7034)
30frotz
31#endif
28#ifdef HAVE_LCD_COLOR 32#ifdef HAVE_LCD_COLOR
29png 33png
30#endif 34#endif
diff --git a/apps/plugins/frotz/SOURCES b/apps/plugins/frotz/SOURCES
new file mode 100644
index 0000000000..6a73e0c602
--- /dev/null
+++ b/apps/plugins/frotz/SOURCES
@@ -0,0 +1,22 @@
1frotz.c
2buffer.c
3err.c
4fastmem.c
5files.c
6hotkey.c
7input.c
8main.c
9math.c
10object.c
11process.c
12quetzal.c
13random.c
14redirect.c
15screen.c
16sound.c
17stream.c
18table.c
19text.c
20variable.c
21dumb_init.c
22dumb_output.c
diff --git a/apps/plugins/frotz/STATUS b/apps/plugins/frotz/STATUS
new file mode 100644
index 0000000000..3aa0f673ee
--- /dev/null
+++ b/apps/plugins/frotz/STATUS
@@ -0,0 +1,45 @@
1frotz is quite portable and is divided into 'common' and os-specific files.
2The common files are included here with minimal modifications. For the
3os-specific files I have started with dumbfrotz, the port intended for a plain
4C stdio system - it has its own screen buffering which is needed for rockbox.
5
6Things that work
7----------------
8
9Games, mostly! If the game is too large to fit in the plugin buffer it will
10stop playback and steal the audio buffer.
11
12Saving and restoring (/path/to/story.sav, no filename selection).
13
14Transcripts, command records and replays (likewise).
15
16Undo, up to the limit of available memory (the rest of the plugin buffer if
17the game fit there, or the rest of the audio buffer if not).
18
19Timed input, though it resets the timer when you enter the menu and only
20counts until you enter the keyboard.
21
22Input line preloading, though the actual displayed line after editing looks
23wrong.
24
25Things that don't work because I've not implemented it
26------------------------------------------------------
27
28Reading buttons that don't appear on the rockbox keyboard.
29
30Audible beeps (just a splash for now).
31
32Setting the random seed.
33
34Things which don't work in the original frotz anyway
35----------------------------------------------------
36
37Mouse and menus.
38
39Pictures.
40
41Non-beep sound samples.
42
43Unicode.
44
45Colours.
diff --git a/apps/plugins/frotz/buffer.c b/apps/plugins/frotz/buffer.c
new file mode 100644
index 0000000000..298ac69c20
--- /dev/null
+++ b/apps/plugins/frotz/buffer.c
@@ -0,0 +1,152 @@
1/* buffer.c - Text buffering and word wrapping
2 * Copyright (c) 1995-1997 Stefan Jokisch
3 *
4 * This file is part of Frotz.
5 *
6 * Frotz is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * Frotz is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
19 */
20
21#include "frotz.h"
22
23extern void stream_char (zchar);
24extern void stream_word (const zchar *);
25extern void stream_new_line (void);
26
27static zchar buffer[TEXT_BUFFER_SIZE];
28static int bufpos = 0;
29
30static zchar prev_c = 0;
31
32/*
33 * flush_buffer
34 *
35 * Copy the contents of the text buffer to the output streams.
36 *
37 */
38
39void flush_buffer (void)
40{
41 static bool locked = FALSE;
42
43 /* Make sure we stop when flush_buffer is called from flush_buffer.
44 Note that this is difficult to avoid as we might print a newline
45 during flush_buffer, which might cause a newline interrupt, that
46 might execute any arbitrary opcode, which might flush the buffer. */
47
48 if (locked || bufpos == 0)
49 return;
50
51 /* Send the buffer to the output streams */
52
53 buffer[bufpos] = 0;
54
55
56 locked = TRUE;
57
58 stream_word (buffer);
59
60#ifdef SPEECH_OUTPUT
61 os_speech_output(buffer);
62#endif
63
64 locked = FALSE;
65
66 /* Reset the buffer */
67
68 bufpos = 0;
69 prev_c = 0;
70
71}/* flush_buffer */
72
73/*
74 * print_char
75 *
76 * High level output function.
77 *
78 */
79
80void print_char (zchar c)
81{
82 static bool flag = FALSE;
83
84 if (message || ostream_memory || enable_buffering) {
85
86 if (!flag) {
87
88 /* Characters 0 and ZC_RETURN are special cases */
89
90 if (c == ZC_RETURN)
91 { new_line (); return; }
92 if (c == 0)
93 return;
94
95 /* Flush the buffer before a whitespace or after a hyphen */
96
97 if (c == ' ' || c == ZC_INDENT || c == ZC_GAP || (prev_c == '-' && c != '-'))
98
99
100 flush_buffer ();
101
102 /* Set the flag if this is part one of a style or font change */
103
104 if (c == ZC_NEW_FONT || c == ZC_NEW_STYLE)
105 flag = TRUE;
106
107 /* Remember the current character code */
108
109 prev_c = c;
110
111 } else flag = FALSE;
112
113 /* Insert the character into the buffer */
114
115 buffer[bufpos++] = c;
116
117 if (bufpos == TEXT_BUFFER_SIZE)
118 runtime_error (ERR_TEXT_BUF_OVF);
119
120 } else stream_char (c);
121
122}/* print_char */
123
124/*
125 * new_line
126 *
127 * High level newline function.
128 *
129 */
130
131void new_line (void)
132{
133
134 flush_buffer (); stream_new_line ();
135
136}/* new_line */
137
138
139/*
140 * init_buffer
141 *
142 * Initialize buffer variables.
143 *
144 */
145
146void init_buffer(void)
147{
148 memset(buffer, 0, sizeof (zchar) * TEXT_BUFFER_SIZE);
149 bufpos = 0;
150 prev_c = 0;
151}
152
diff --git a/apps/plugins/frotz/dumb_frotz.h b/apps/plugins/frotz/dumb_frotz.h
new file mode 100644
index 0000000000..0b8415e4ae
--- /dev/null
+++ b/apps/plugins/frotz/dumb_frotz.h
@@ -0,0 +1,19 @@
1/* dumb-frotz.h
2 * $Id: dumb-frotz.h,v 1.1.1.1 2002/03/26 22:38:34 feedle Exp $
3 * Frotz os functions for a standard C library and a dumb terminal.
4 * Now you can finally play Zork Zero on your Teletype.
5 *
6 * Copyright 1997, 1998 Alembic Petrofsky <alembic@petrofsky.berkeley.ca.us>.
7 * Any use permitted provided this notice stays intact.
8 *
9 * Changes for Rockbox copyright 2009 Torne Wuff
10 */
11#include "frotz.h"
12
13/* from ../common/setup.h */
14extern f_setup_t f_setup;
15
16/* dumb-output.c */
17void dumb_init_output(void);
18void dumb_show_screen(bool show_cursor);
19void dumb_dump_screen(void);
diff --git a/apps/plugins/frotz/dumb_init.c b/apps/plugins/frotz/dumb_init.c
new file mode 100644
index 0000000000..ea08447c0c
--- /dev/null
+++ b/apps/plugins/frotz/dumb_init.c
@@ -0,0 +1,86 @@
1/* dumb-init.c
2 * $Id: dumb-init.c,v 1.1.1.1 2002/03/26 22:38:34 feedle Exp $
3 *
4 * Copyright 1997,1998 Alva Petrofsky <alva@petrofsky.berkeley.ca.us>.
5 * Any use permitted provided this notice stays intact.
6 *
7 * Changes for Rockbox copyright 2009 Torne Wuff
8 */
9
10#include "dumb_frotz.h"
11#include "lib/pluginlib_exit.h"
12
13static int user_screen_width = LCD_WIDTH / SYSFONT_WIDTH;
14static int user_screen_height = LCD_HEIGHT / SYSFONT_HEIGHT;
15static int user_interpreter_number = -1;
16static int user_random_seed = -1;
17static int user_tandy_bit = 0;
18
19
20void os_init_screen(void)
21{
22 if (h_version == V3 && user_tandy_bit)
23 h_config |= CONFIG_TANDY;
24
25 if (h_version >= V5 && f_setup.undo_slots == 0)
26 h_flags &= ~UNDO_FLAG;
27
28 h_screen_rows = user_screen_height;
29 h_screen_cols = user_screen_width;
30
31 if (user_interpreter_number > 0)
32 h_interpreter_number = user_interpreter_number;
33 else {
34 /* Use ms-dos for v6 (because that's what most people have the
35 * graphics files for), but don't use it for v5 (or Beyond Zork
36 * will try to use funky characters). */
37 h_interpreter_number = h_version == 6 ? INTERP_MSDOS : INTERP_DEC_20;
38 }
39 h_interpreter_version = 'F';
40
41 if (h_version >= V4)
42 h_config |= CONFIG_TIMEDINPUT;
43
44 if (h_version >= V5)
45 h_flags &= ~(MOUSE_FLAG | MENU_FLAG);
46
47 dumb_init_output();
48
49 h_flags &= ~GRAPHICS_FLAG;
50}
51
52int os_random_seed (void)
53{
54 if (user_random_seed == -1)
55 /* Use the rockbox tick as seed value */
56 return ((int)rb->current_tick) & 0x7fff;
57 else return user_random_seed;
58}
59
60void os_restart_game (int stage) { (void)stage; }
61
62void os_fatal (const char *s)
63{
64 rb->splash(HZ*10, s);
65 exit(1);
66}
67
68void os_init_setup(void)
69{
70 f_setup.attribute_assignment = 0;
71 f_setup.attribute_testing = 0;
72 f_setup.context_lines = 0;
73 f_setup.object_locating = 0;
74 f_setup.object_movement = 0;
75 f_setup.left_margin = 0;
76 f_setup.right_margin = 0;
77 f_setup.ignore_errors = 0;
78 f_setup.piracy = 0;
79 f_setup.undo_slots = MAX_UNDO_SLOTS;
80 f_setup.expand_abbreviations = 0;
81 f_setup.script_cols = 80;
82 f_setup.save_quetzal = 1;
83 f_setup.sound = 1;
84 f_setup.err_report_mode = ERR_DEFAULT_REPORT_MODE;
85
86}
diff --git a/apps/plugins/frotz/dumb_output.c b/apps/plugins/frotz/dumb_output.c
new file mode 100644
index 0000000000..eb61419195
--- /dev/null
+++ b/apps/plugins/frotz/dumb_output.c
@@ -0,0 +1,256 @@
1/* dumb-output.c
2 * $Id: dumb-output.c,v 1.2 2002/03/26 22:52:31 feedle Exp $
3 *
4 * Copyright 1997,1998 Alfresco Petrofsky <alfresco@petrofsky.berkeley.edu>.
5 * Any use permitted provided this notice stays intact.
6 *
7 * Changes for Rockbox copyright 2009 Torne Wuff
8 *
9 * Rockbox is not really a dumb terminal (it supports printing text wherever
10 * you like) but it doesn't implement a terminal type buffer, so this is
11 * close enough to be a good starting point. Keeping a copy of the graphical
12 * framebuffer would be too expensive, text+attributes is much smaller.
13 */
14
15#include "dumb_frotz.h"
16
17/* The in-memory state of the screen. */
18/* Each cell contains a style in the upper byte and a char in the lower. */
19typedef unsigned short cell;
20static cell screen_data[(LCD_WIDTH/SYSFONT_WIDTH) * (LCD_HEIGHT/SYSFONT_HEIGHT)];
21
22static cell make_cell(int style, char c) {return (style << 8) | (0xff & c);}
23static char cell_char(cell c) {return c & 0xff;}
24static int cell_style(cell c) {return c >> 8;}
25
26static int current_style = 0;
27static int cursor_row = 0, cursor_col = 0;
28
29static cell *dumb_row(int r) {return screen_data + r * h_screen_cols;}
30
31int os_char_width (zchar z)
32{
33 (void)z;
34 return 1;
35}
36
37int os_string_width (const zchar *s)
38{
39 int width = 0;
40 zchar c;
41
42 while ((c = *s++) != 0)
43 if (c == ZC_NEW_STYLE || c == ZC_NEW_FONT)
44 s++;
45 else
46 width += os_char_width(c);
47
48 return width;
49}
50
51void os_set_cursor(int row, int col)
52{
53 cursor_row = row - 1; cursor_col = col - 1;
54 if (cursor_row >= h_screen_rows)
55 cursor_row = h_screen_rows - 1;
56}
57
58/* Set a cell */
59static void dumb_set_cell(int row, int col, cell c)
60{
61 dumb_row(row)[col] = c;
62}
63
64void os_set_text_style(int x)
65{
66 current_style = x & REVERSE_STYLE;
67}
68
69static void dumb_display_cell(int row, int col)
70{
71 cell cel = dumb_row(row)[col];
72 char c = cell_char(cel);
73 if (!c)
74 c = ' ';
75 char style = cell_style(cel);
76 char s[5];
77 char *end = rb->utf8encode(c, s);
78 *end = 0;
79 if (style & REVERSE_STYLE)
80 rb->lcd_set_drawmode(DRMODE_INVERSEVID);
81 rb->lcd_putsxy(col * SYSFONT_WIDTH, row * SYSFONT_HEIGHT, s);
82 rb->lcd_set_drawmode(DRMODE_SOLID);
83}
84
85/* put a character in the cell at the cursor and advance the cursor. */
86static void dumb_display_char(char c)
87{
88 dumb_set_cell(cursor_row, cursor_col, make_cell(current_style, c));
89 if (++cursor_col == h_screen_cols) {
90 if (cursor_row == h_screen_rows - 1)
91 cursor_col--;
92 else {
93 cursor_row++;
94 cursor_col = 0;
95 }
96 }
97}
98
99void os_display_char (zchar c)
100{
101 if (c >= ZC_LATIN1_MIN /*&& c <= ZC_LATIN1_MAX*/) {
102 dumb_display_char(c);
103 } else if (c >= 32 && c <= 126) {
104 dumb_display_char(c);
105 } else if (c == ZC_GAP) {
106 dumb_display_char(' '); dumb_display_char(' ');
107 } else if (c == ZC_INDENT) {
108 dumb_display_char(' '); dumb_display_char(' '); dumb_display_char(' ');
109 }
110 return;
111}
112
113
114/* Haxor your boxor? */
115void os_display_string (const zchar *s)
116{
117 zchar c;
118
119 while ((c = *s++) != 0)
120 if (c == ZC_NEW_FONT)
121 s++;
122 else if (c == ZC_NEW_STYLE)
123 os_set_text_style(*s++);
124 else {
125 os_display_char (c);
126 }
127}
128
129void os_erase_area (int top, int left, int bottom, int right)
130{
131 int row;
132 top--; left--; bottom--; right--;
133 if (left == 0 && right == h_screen_cols - 1)
134 rb->memset(dumb_row(top), 0, (bottom - top + 1) * h_screen_cols * sizeof(cell));
135 else
136 for (row = top; row <= bottom; row++)
137 rb->memset(dumb_row(row) + left, 0, (right - left + 1) * sizeof(cell));
138}
139
140void os_scroll_area (int top, int left, int bottom, int right, int units)
141{
142 int row;
143 top--; left--; bottom--; right--;
144 if (units > 0) {
145 for (row = top; row <= bottom - units; row++)
146 rb->memcpy(dumb_row(row) + left,
147 dumb_row(row + units) + left,
148 (right - left + 1) * sizeof(cell));
149 os_erase_area(bottom - units + 2, left + 1, bottom + 1, right + 1);
150 } else if (units < 0) {
151 for (row = bottom; row >= top - units; row--)
152 rb->memcpy(dumb_row(row) + left,
153 dumb_row(row + units) + left,
154 (right - left + 1) * sizeof(cell));
155 os_erase_area(top + 1, left + 1, top - units, right + 1);
156 }
157}
158
159int os_font_data(int font, int *height, int *width)
160{
161 if (font == TEXT_FONT) {
162 *height = 1; *width = 1; return 1;
163 }
164 return 0;
165}
166
167void os_set_colour (int x, int y) { (void)x; (void)y; }
168void os_set_font (int x) { (void)x; }
169
170/* Unconditionally show whole screen. */
171void dumb_dump_screen(void)
172{
173 int r, c;
174 rb->lcd_clear_display();
175 for (r = 0; r < h_screen_height; r++)
176 for (c = 0; c < h_screen_width; c++)
177 dumb_display_cell(r, c);
178 rb->lcd_update();
179}
180
181/* Show the current screen contents. */
182void dumb_show_screen(bool show_cursor)
183{
184 (void)show_cursor;
185 dumb_dump_screen();
186}
187
188void os_more_prompt(void)
189{
190 int old_row = cursor_row;
191 int old_col = cursor_col;
192 int old_style = current_style;
193
194 current_style = REVERSE_STYLE;
195 os_display_string("[MORE]");
196 wait_for_key();
197
198 cursor_row = old_row;
199 cursor_col = old_col;
200 current_style = old_style;
201 os_erase_area(cursor_row+1, cursor_col+1, cursor_row+1, cursor_col+7);
202}
203
204void os_reset_screen(void)
205{
206 current_style = REVERSE_STYLE;
207 os_display_string("[Press key to exit]");
208 wait_for_key();
209}
210
211
212/* To make the common code happy */
213
214void os_prepare_sample (int a) { (void)a; }
215void os_finish_with_sample (int a) { (void)a; }
216void os_start_sample (int a, int b, int c, zword d)
217{
218 (void)a; (void)b; (void)c; (void)d;
219}
220void os_stop_sample (int a) { (void)a; }
221
222void dumb_init_output(void)
223{
224 if (h_version == V3) {
225 h_config |= CONFIG_SPLITSCREEN;
226 h_flags &= ~OLD_SOUND_FLAG;
227 }
228
229 if (h_version >= V5) {
230 h_flags &= ~SOUND_FLAG;
231 }
232
233 h_screen_height = h_screen_rows;
234 h_screen_width = h_screen_cols;
235
236 h_font_width = 1; h_font_height = 1;
237
238 os_erase_area(1, 1, h_screen_rows, h_screen_cols);
239}
240
241bool os_picture_data(int num, int *height, int *width)
242{
243 (void)num;
244 *height = 0;
245 *width = 0;
246 return FALSE;
247}
248
249void os_draw_picture (int num, int row, int col)
250{
251 (void)num;
252 (void)row;
253 (void)col;
254}
255
256int os_peek_colour (void) {return BLACK_COLOUR; }
diff --git a/apps/plugins/frotz/err.c b/apps/plugins/frotz/err.c
new file mode 100644
index 0000000000..61ca78ce3b
--- /dev/null
+++ b/apps/plugins/frotz/err.c
@@ -0,0 +1,154 @@
1/* err.c - Runtime error reporting functions
2 * Written by Jim Dunleavy <jim.dunleavy@erha.ie>
3 *
4 * This file is part of Frotz.
5 *
6 * Frotz is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * Frotz is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
19 */
20
21#include "frotz.h"
22
23/* Define stuff for stricter Z-code error checking, for the generic
24 Unix/DOS/etc terminal-window interface. Feel free to change the way
25 player prefs are specified, or replace report_zstrict_error()
26 completely if you want to change the way errors are reported. */
27
28/* int err_report_mode = ERR_DEFAULT_REPORT_MODE; */
29
30static int error_count[ERR_NUM_ERRORS];
31
32static char *err_messages[] = {
33 "Text buffer overflow",
34 "Store out of dynamic memory",
35 "Division by zero",
36 "Illegal object",
37 "Illegal attribute",
38 "No such property",
39 "Stack overflow",
40 "Call to illegal address",
41 "Call to non-routine",
42 "Stack underflow",
43 "Illegal opcode",
44 "Bad stack frame",
45 "Jump to illegal address",
46 "Can't save while in interrupt",
47 "Nesting stream #3 too deep",
48 "Illegal window",
49 "Illegal window property",
50 "Print at illegal address",
51 "@jin called with object 0",
52 "@get_child called with object 0",
53 "@get_parent called with object 0",
54 "@get_sibling called with object 0",
55 "@get_prop_addr called with object 0",
56 "@get_prop called with object 0",
57 "@put_prop called with object 0",
58 "@clear_attr called with object 0",
59 "@set_attr called with object 0",
60 "@test_attr called with object 0",
61 "@move_object called moving object 0",
62 "@move_object called moving into object 0",
63 "@remove_object called with object 0",
64 "@get_next_prop called with object 0"
65};
66
67static void print_long (unsigned long value, int base);
68
69/*
70 * init_err
71 *
72 * Initialise error reporting.
73 *
74 */
75
76void init_err (void)
77{
78 int i;
79
80 /* Initialize the counters. */
81
82 for (i = 0; i < ERR_NUM_ERRORS; i++)
83 error_count[i] = 0;
84}
85
86/*
87 * runtime_error
88 *
89 * An error has occurred. Ignore it, pass it to os_fatal or report
90 * it according to err_report_mode.
91 *
92 * errnum : Numeric code for error (1 to ERR_NUM_ERRORS)
93 *
94 */
95
96void runtime_error (int errnum)
97{
98 int wasfirst;
99
100 if (errnum <= 0 || errnum > ERR_NUM_ERRORS)
101 return;
102
103 if (f_setup.err_report_mode == ERR_REPORT_FATAL
104 || (!f_setup.ignore_errors && errnum <= ERR_MAX_FATAL)) {
105 flush_buffer ();
106 os_fatal (err_messages[errnum - 1]);
107 return;
108 }
109
110 wasfirst = (error_count[errnum - 1] == 0);
111 error_count[errnum - 1]++;
112
113 if ((f_setup.err_report_mode == ERR_REPORT_ALWAYS)
114 || (f_setup.err_report_mode == ERR_REPORT_ONCE && wasfirst)) {
115 long pc;
116
117 GET_PC (pc);
118 print_string ("Warning: ");
119 print_string (err_messages[errnum - 1]);
120 print_string (" (PC = ");
121 print_long (pc, 16);
122 print_char (')');
123
124 if (f_setup.err_report_mode == ERR_REPORT_ONCE) {
125 print_string (" (will ignore further occurrences)");
126 } else {
127 print_string (" (occurence ");
128 print_long (error_count[errnum - 1], 10);
129 print_char (')');
130 }
131 new_line ();
132 }
133
134} /* report_error */
135
136/*
137 * print_long
138 *
139 * Print an unsigned 32bit number in decimal or hex.
140 *
141 */
142
143static void print_long (unsigned long value, int base)
144{
145 unsigned long i;
146 char c;
147
148 for (i = (base == 10 ? 1000000000 : 0x10000000); i != 0; i /= base)
149 if (value >= i || i == 1) {
150 c = (value / i) % base;
151 print_char (c + (c <= 9 ? '0' : 'a' - 10));
152 }
153
154}/* print_long */
diff --git a/apps/plugins/frotz/fastmem.c b/apps/plugins/frotz/fastmem.c
new file mode 100644
index 0000000000..cdb4bce602
--- /dev/null
+++ b/apps/plugins/frotz/fastmem.c
@@ -0,0 +1,1013 @@
1/* fastmem.c - Memory related functions (fast version without virtual memory)
2 * Copyright (c) 1995-1997 Stefan Jokisch
3 *
4 * Changes for Rockbox copyright 2009 Torne Wuff
5 *
6 * This file is part of Frotz.
7 *
8 * Frotz is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * Frotz is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
21 */
22
23/*
24 * New undo mechanism added by Jim Dunleavy <jim.dunleavy@erha.ie>
25 */
26
27#include "frotz.h"
28
29#define far
30
31extern void seed_random (int);
32extern void restart_screen (void);
33extern void refresh_text_style (void);
34extern void call (zword, int, zword *, int);
35extern void split_window (zword);
36extern void script_open (void);
37extern void script_close (void);
38
39extern zword save_quetzal (int, int);
40extern zword restore_quetzal (int, int);
41
42extern void erase_window (zword);
43
44extern void (*op0_opcodes[]) (void);
45extern void (*op1_opcodes[]) (void);
46extern void (*op2_opcodes[]) (void);
47extern void (*var_opcodes[]) (void);
48
49char save_name[MAX_PATH];
50char auxilary_name[MAX_PATH];
51
52zbyte far *zmp = NULL;
53zbyte far *pcp = NULL;
54
55int story_fp = -1;
56
57/*
58 * Data for the undo mechanism.
59 * This undo mechanism is based on the scheme used in Evin Robertson's
60 * Nitfol interpreter.
61 * Undo blocks are stored as differences between states.
62 */
63
64typedef struct undo_struct undo_t;
65struct undo_struct {
66 undo_t *next;
67 undo_t *prev;
68 long pc;
69 long diff_size;
70 zword frame_count;
71 zword stack_size;
72 zword frame_offset;
73 /* undo diff and stack data follow */
74};
75
76static undo_t *first_undo = NULL, *last_undo = NULL, *curr_undo = NULL;
77static zbyte *prev_zmp, *undo_diff;
78
79static int undo_count = 0;
80
81static void *arena_start = NULL, *arena_end = NULL, *arena_next = NULL;
82
83/*
84 * get_header_extension
85 *
86 * Read a value from the header extension (former mouse table).
87 *
88 */
89
90zword get_header_extension (int entry)
91{
92 zword addr;
93 zword val;
94
95 if (h_extension_table == 0 || entry > hx_table_size)
96 return 0;
97
98 addr = h_extension_table + 2 * entry;
99 LOW_WORD (addr, val)
100
101 return val;
102
103}/* get_header_extension */
104
105/*
106 * set_header_extension
107 *
108 * Set an entry in the header extension (former mouse table).
109 *
110 */
111
112void set_header_extension (int entry, zword val)
113{
114 zword addr;
115
116 if (h_extension_table == 0 || entry > hx_table_size)
117 return;
118
119 addr = h_extension_table + 2 * entry;
120 SET_WORD (addr, val)
121
122}/* set_header_extension */
123
124/*
125 * restart_header
126 *
127 * Set all header fields which hold information about the interpreter.
128 *
129 */
130
131void restart_header (void)
132{
133 zword screen_x_size;
134 zword screen_y_size;
135 zbyte font_x_size;
136 zbyte font_y_size;
137
138 int i;
139
140 SET_BYTE (H_CONFIG, h_config)
141 SET_WORD (H_FLAGS, h_flags)
142
143 if (h_version >= V4) {
144 SET_BYTE (H_INTERPRETER_NUMBER, h_interpreter_number)
145 SET_BYTE (H_INTERPRETER_VERSION, h_interpreter_version)
146 SET_BYTE (H_SCREEN_ROWS, h_screen_rows)
147 SET_BYTE (H_SCREEN_COLS, h_screen_cols)
148 }
149
150 /* It's less trouble to use font size 1x1 for V5 games, especially
151 because of a bug in the unreleased German version of "Zork 1" */
152
153 if (h_version != V6) {
154 screen_x_size = (zword) h_screen_cols;
155 screen_y_size = (zword) h_screen_rows;
156 font_x_size = 1;
157 font_y_size = 1;
158 } else {
159 screen_x_size = h_screen_width;
160 screen_y_size = h_screen_height;
161 font_x_size = h_font_width;
162 font_y_size = h_font_height;
163 }
164
165 if (h_version >= V5) {
166 SET_WORD (H_SCREEN_WIDTH, screen_x_size)
167 SET_WORD (H_SCREEN_HEIGHT, screen_y_size)
168 SET_BYTE (H_FONT_HEIGHT, font_y_size)
169 SET_BYTE (H_FONT_WIDTH, font_x_size)
170 SET_BYTE (H_DEFAULT_BACKGROUND, h_default_background)
171 SET_BYTE (H_DEFAULT_FOREGROUND, h_default_foreground)
172 }
173
174 if (h_version == V6)
175 for (i = 0; i < 8; i++)
176 storeb ((zword) (H_USER_NAME + i), h_user_name[i]);
177
178 SET_BYTE (H_STANDARD_HIGH, h_standard_high)
179 SET_BYTE (H_STANDARD_LOW, h_standard_low)
180
181}/* restart_header */
182
183/*
184 * init_memory
185 *
186 * Allocate memory and load the story file.
187 *
188 */
189
190void init_memory (void)
191{
192 long size;
193 zword addr;
194 unsigned n;
195 int i, j;
196 zbyte *audiobuf;
197 size_t buf_size;
198
199 static struct {
200 enum story story_id;
201 zword release;
202 zbyte serial[6];
203 } records[] = {
204 { SHERLOCK, 21, "871214" },
205 { SHERLOCK, 26, "880127" },
206 { BEYOND_ZORK, 47, "870915" },
207 { BEYOND_ZORK, 49, "870917" },
208 { BEYOND_ZORK, 51, "870923" },
209 { BEYOND_ZORK, 57, "871221" },
210 { ZORK_ZERO, 296, "881019" },
211 { ZORK_ZERO, 366, "890323" },
212 { ZORK_ZERO, 383, "890602" },
213 { ZORK_ZERO, 393, "890714" },
214 { SHOGUN, 292, "890314" },
215 { SHOGUN, 295, "890321" },
216 { SHOGUN, 311, "890510" },
217 { SHOGUN, 322, "890706" },
218 { ARTHUR, 54, "890606" },
219 { ARTHUR, 63, "890622" },
220 { ARTHUR, 74, "890714" },
221 { JOURNEY, 26, "890316" },
222 { JOURNEY, 30, "890322" },
223 { JOURNEY, 77, "890616" },
224 { JOURNEY, 83, "890706" },
225 { LURKING_HORROR, 203, "870506" },
226 { LURKING_HORROR, 219, "870912" },
227 { LURKING_HORROR, 221, "870918" },
228 { UNKNOWN, 0, "------" }
229 };
230
231 /* Open story file */
232
233 if ((story_fp = rb->open(story_name, O_RDONLY)) < 0)
234 os_fatal ("Cannot open story file");
235
236 /* Allocate memory for story header */
237
238 zmp = rb->plugin_get_buffer(&buf_size);
239
240 /* Load header into memory */
241
242 if (fread (zmp, 1, 64, story_fp) != 64)
243 os_fatal ("Story file read error");
244
245 /* Copy header fields to global variables */
246
247 LOW_BYTE (H_VERSION, h_version)
248
249 if (h_version < V1 || h_version > V8)
250 os_fatal ("Unknown Z-code version");
251
252 LOW_BYTE (H_CONFIG, h_config)
253
254 if (h_version == V3 && (h_config & CONFIG_BYTE_SWAPPED))
255 os_fatal ("Byte swapped story file");
256
257 LOW_WORD (H_RELEASE, h_release)
258 LOW_WORD (H_RESIDENT_SIZE, h_resident_size)
259 LOW_WORD (H_START_PC, h_start_pc)
260 LOW_WORD (H_DICTIONARY, h_dictionary)
261 LOW_WORD (H_OBJECTS, h_objects)
262 LOW_WORD (H_GLOBALS, h_globals)
263 LOW_WORD (H_DYNAMIC_SIZE, h_dynamic_size)
264 LOW_WORD (H_FLAGS, h_flags)
265
266 for (i = 0, addr = H_SERIAL; i < 6; i++, addr++)
267 LOW_BYTE (addr, h_serial[i])
268
269 /* Auto-detect buggy story files that need special fixes */
270
271 story_id = UNKNOWN;
272
273 for (i = 0; records[i].story_id != UNKNOWN; i++) {
274
275 if (h_release == records[i].release) {
276
277 for (j = 0; j < 6; j++)
278 if (h_serial[j] != records[i].serial[j])
279 goto no_match;
280
281 story_id = records[i].story_id;
282
283 }
284
285 no_match: ; /* null statement */
286
287 }
288
289 LOW_WORD (H_ABBREVIATIONS, h_abbreviations)
290 LOW_WORD (H_FILE_SIZE, h_file_size)
291
292 /* Calculate story file size in bytes */
293
294 if (h_file_size != 0) {
295
296 story_size = (long) 2 * h_file_size;
297
298 if (h_version >= V4)
299 story_size *= 2;
300 if (h_version >= V6)
301 story_size *= 2;
302
303 } else { /* some old games lack the file size entry */
304
305 fseek (story_fp, 0, SEEK_END);
306 story_size = ftell (story_fp);
307 fseek (story_fp, 64, SEEK_SET);
308
309 }
310
311 LOW_WORD (H_CHECKSUM, h_checksum)
312 LOW_WORD (H_ALPHABET, h_alphabet)
313 LOW_WORD (H_FUNCTIONS_OFFSET, h_functions_offset)
314 LOW_WORD (H_STRINGS_OFFSET, h_strings_offset)
315 LOW_WORD (H_TERMINATING_KEYS, h_terminating_keys)
316 LOW_WORD (H_EXTENSION_TABLE, h_extension_table)
317
318 /* Zork Zero Macintosh doesn't have the graphics flag set */
319
320 if (story_id == ZORK_ZERO && h_release == 296)
321 h_flags |= GRAPHICS_FLAG;
322
323 /* Adjust opcode tables */
324
325 if (h_version <= V4) {
326 op0_opcodes[0x09] = z_pop;
327 op1_opcodes[0x0f] = z_not;
328 } else {
329 op0_opcodes[0x09] = z_catch;
330 op1_opcodes[0x0f] = z_call_n;
331 }
332
333 /* Allocate memory for story data */
334
335 if ((size_t)story_size > buf_size)
336 {
337 audiobuf = rb->plugin_get_audio_buffer(&buf_size);
338 if ((size_t)story_size > buf_size)
339 os_fatal ("Out of memory");
340 rb->memcpy(audiobuf, zmp, 64);
341 zmp = audiobuf;
342 }
343
344 /* Assign left over memory as the arena for undo alloc */
345 arena_start = arena_next = (void*)((int)(zmp + story_size + 3) & ~3);
346 arena_end = zmp + buf_size;
347
348 /* Load story file in chunks of 32KB */
349
350 n = 0x8000;
351
352 for (size = 64; size < story_size; size += n) {
353
354 if (story_size - size < 0x8000)
355 n = (unsigned) (story_size - size);
356
357 SET_PC (size)
358
359 if (fread (pcp, 1, n, story_fp) != (signed)n)
360 os_fatal ("Story file read error");
361
362 }
363
364 /* Read header extension table */
365
366 hx_table_size = get_header_extension (HX_TABLE_SIZE);
367 hx_unicode_table = get_header_extension (HX_UNICODE_TABLE);
368
369}/* init_memory */
370
371/*
372 * init_undo
373 *
374 * Allocate memory for multiple undo. It is important not to occupy
375 * all the memory available, since the IO interface may need memory
376 * during the game, e.g. for loading sounds or pictures.
377 *
378 */
379
380void init_undo (void)
381{
382 /* Allocate h_dynamic_size bytes for previous dynamic zmp state
383 + 1.5 h_dynamic_size for Quetzal diff + 2. */
384 int size = (h_dynamic_size * 5) / 2 + 2;
385 if ((arena_end - arena_start) >= size) {
386 prev_zmp = arena_start;
387 undo_diff = arena_start + h_dynamic_size;
388 arena_start = (void*)((int)(arena_start + size + 3) & ~3);
389 arena_next = arena_start;
390 memcpy (prev_zmp, zmp, h_dynamic_size);
391 } else
392 f_setup.undo_slots = 0;
393
394}/* init_undo */
395
396/*
397 * free_undo
398 *
399 * Free count undo blocks from the beginning of the undo list.
400 *
401 */
402
403static void free_undo (int count)
404{
405 undo_t *p;
406
407 if (count > undo_count)
408 count = undo_count;
409 while (count--) {
410 p = first_undo;
411 if (curr_undo == first_undo)
412 curr_undo = curr_undo->next;
413 first_undo = first_undo->next;
414 undo_count--;
415 }
416 if (first_undo)
417 first_undo->prev = NULL;
418 else
419 last_undo = NULL;
420}/* free_undo */
421
422/*
423 * reset_memory
424 *
425 * Close the story file and deallocate memory.
426 *
427 */
428
429void reset_memory (void)
430{
431 if (story_fp != -1)
432 fclose (story_fp);
433 story_fp = -1;
434
435 free_undo (undo_count);
436 undo_count = 0;
437
438 arena_start = NULL;
439 arena_end = NULL;
440 arena_next = NULL;
441 zmp = NULL;
442}/* reset_memory */
443
444/*
445 * storeb
446 *
447 * Write a byte value to the dynamic Z-machine memory.
448 *
449 */
450
451void storeb (zword addr, zbyte value)
452{
453
454 if (addr >= h_dynamic_size)
455 runtime_error (ERR_STORE_RANGE);
456
457 if (addr == H_FLAGS + 1) { /* flags register is modified */
458
459 h_flags &= ~(SCRIPTING_FLAG | FIXED_FONT_FLAG);
460 h_flags |= value & (SCRIPTING_FLAG | FIXED_FONT_FLAG);
461
462 if (value & SCRIPTING_FLAG) {
463 if (!ostream_script)
464 script_open ();
465 } else {
466 if (ostream_script)
467 script_close ();
468 }
469
470 refresh_text_style ();
471
472 }
473
474 SET_BYTE (addr, value)
475
476}/* storeb */
477
478/*
479 * storew
480 *
481 * Write a word value to the dynamic Z-machine memory.
482 *
483 */
484
485void storew (zword addr, zword value)
486{
487
488 storeb ((zword) (addr + 0), hi (value));
489 storeb ((zword) (addr + 1), lo (value));
490
491}/* storew */
492
493/*
494 * z_restart, re-load dynamic area, clear the stack and set the PC.
495 *
496 * no zargs used
497 *
498 */
499
500void z_restart (void)
501{
502 static bool first_restart = TRUE;
503
504 flush_buffer ();
505
506 os_restart_game (RESTART_BEGIN);
507
508 seed_random (0);
509
510 if (!first_restart) {
511
512 fseek (story_fp, 0, SEEK_SET);
513
514 if (fread (zmp, 1, h_dynamic_size, story_fp) != h_dynamic_size)
515 os_fatal ("Story file read error");
516
517 } else first_restart = FALSE;
518
519 restart_header ();
520 restart_screen ();
521
522 sp = fp = stack + STACK_SIZE;
523 frame_count = 0;
524
525 if (h_version != V6) {
526
527 long pc = (long) h_start_pc;
528 SET_PC (pc)
529
530 } else call (h_start_pc, 0, NULL, 0);
531
532 os_restart_game (RESTART_END);
533
534}/* z_restart */
535
536/*
537 * get_default_name
538 *
539 * Read a default file name from the memory of the Z-machine and
540 * copy it to a string.
541 *
542 */
543
544static void get_default_name (char *default_name, zword addr)
545{
546
547 if (addr != 0) {
548
549 zbyte len;
550 int i;
551
552 LOW_BYTE (addr, len)
553 addr++;
554
555 for (i = 0; i < len; i++) {
556
557 zbyte c;
558
559 LOW_BYTE (addr, c)
560 addr++;
561
562 if (c >= 'A' && c <= 'Z')
563 c += 'a' - 'A';
564
565 default_name[i] = c;
566
567 }
568
569 default_name[i] = 0;
570
571 if (strchr (default_name, '.') == NULL)
572 strcpy (default_name + i, ".AUX");
573
574 } else strcpy (default_name, auxilary_name);
575
576}/* get_default_name */
577
578/*
579 * z_restore, restore [a part of] a Z-machine state from disk
580 *
581 * zargs[0] = address of area to restore (optional)
582 * zargs[1] = number of bytes to restore
583 * zargs[2] = address of suggested file name
584 *
585 */
586
587void z_restore (void)
588{
589 char new_name[MAX_PATH];
590 char default_name[MAX_PATH];
591 int gfp;
592
593 zword success = 0;
594
595 if (zargc != 0) {
596
597 /* Get the file name */
598
599 get_default_name (default_name, (zargc >= 3) ? zargs[2] : 0);
600
601 if (os_read_file_name (new_name, default_name, FILE_LOAD_AUX) == 0)
602 goto finished;
603
604 strcpy (auxilary_name, default_name);
605
606 /* Open auxilary file */
607
608 if ((gfp = rb->open (new_name, O_RDONLY)) < 0)
609 goto finished;
610
611 /* Load auxilary file */
612
613 success = fread (zmp + zargs[0], 1, zargs[1], gfp);
614
615 /* Close auxilary file */
616
617 fclose (gfp);
618
619 } else {
620
621 /* Get the file name */
622
623 if (os_read_file_name (new_name, save_name, FILE_RESTORE) == 0)
624 goto finished;
625
626 strcpy (save_name, new_name);
627
628 /* Open game file */
629
630 if ((gfp = rb->open (new_name, O_RDONLY)) < 0)
631 goto finished;
632
633 success = restore_quetzal (gfp, story_fp);
634
635 /* Close game file */
636
637 fclose (gfp);
638
639 if ((short) success >= 0) {
640
641 if ((short) success > 0) {
642 zbyte old_screen_rows;
643 zbyte old_screen_cols;
644
645 /* In V3, reset the upper window. */
646 if (h_version == V3)
647 split_window (0);
648
649 LOW_BYTE (H_SCREEN_ROWS, old_screen_rows);
650 LOW_BYTE (H_SCREEN_COLS, old_screen_cols);
651
652 /* Reload cached header fields. */
653 restart_header ();
654
655 /*
656 * Since QUETZAL files may be saved on many different machines,
657 * the screen sizes may vary a lot. Erasing the status window
658 * seems to cover up most of the resulting badness.
659 */
660 if (h_version > V3 && h_version != V6
661 && (h_screen_rows != old_screen_rows
662 || h_screen_cols != old_screen_cols))
663 erase_window (1);
664 }
665 } else
666 os_fatal ("Error reading save file");
667 }
668
669finished:
670
671 if (h_version <= V3)
672 branch (success);
673 else
674 store (success);
675
676}/* z_restore */
677
678/*
679 * mem_diff
680 *
681 * Set diff to a Quetzal-like difference between a and b,
682 * copying a to b as we go. It is assumed that diff points to a
683 * buffer which is large enough to hold the diff.
684 * mem_size is the number of bytes to compare.
685 * Returns the number of bytes copied to diff.
686 *
687 */
688
689static long mem_diff (zbyte *a, zbyte *b, zword mem_size, zbyte *diff)
690{
691 unsigned size = mem_size;
692 zbyte *p = diff;
693 unsigned j;
694 zbyte c;
695
696 for (;;) {
697 for (j = 0; size > 0 && (c = *a++ ^ *b++) == 0; j++)
698 size--;
699 if (size == 0) break;
700 size--;
701 if (j > 0x8000) {
702 *p++ = 0;
703 *p++ = 0xff;
704 *p++ = 0xff;
705 j -= 0x8000;
706 }
707 if (j > 0) {
708 *p++ = 0;
709 j--;
710 if (j <= 0x7f) {
711 *p++ = j;
712 } else {
713 *p++ = (j & 0x7f) | 0x80;
714 *p++ = (j & 0x7f80) >> 7;
715 }
716 }
717 *p++ = c;
718 *(b - 1) ^= c;
719 }
720 return p - diff;
721}/* mem_diff */
722
723/*
724 * mem_undiff
725 *
726 * Applies a quetzal-like diff to dest
727 *
728 */
729
730static void mem_undiff (zbyte *diff, long diff_length, zbyte *dest)
731{
732 zbyte c;
733
734 while (diff_length) {
735 c = *diff++;
736 diff_length--;
737 if (c == 0) {
738 unsigned runlen;
739
740 if (!diff_length)
741 return; /* Incomplete run */
742 runlen = *diff++;
743 diff_length--;
744 if (runlen & 0x80) {
745 if (!diff_length)
746 return; /* Incomplete extended run */
747 c = *diff++;
748 diff_length--;
749 runlen = (runlen & 0x7f) | (((unsigned) c) << 7);
750 }
751
752 dest += runlen + 1;
753 } else {
754 *dest++ ^= c;
755 }
756 }
757}/* mem_undiff */
758
759/*
760 * restore_undo
761 *
762 * This function does the dirty work for z_restore_undo.
763 *
764 */
765
766int restore_undo (void)
767{
768
769 if (f_setup.undo_slots == 0) /* undo feature unavailable */
770
771 return -1;
772
773 if (curr_undo == NULL) /* no saved game state */
774
775 return 0;
776
777 /* undo possible */
778
779 memcpy (zmp, prev_zmp, h_dynamic_size);
780 SET_PC (curr_undo->pc)
781 sp = stack + STACK_SIZE - curr_undo->stack_size;
782 fp = stack + curr_undo->frame_offset;
783 frame_count = curr_undo->frame_count;
784 mem_undiff ((zbyte *) (curr_undo + 1), curr_undo->diff_size, prev_zmp);
785 memcpy (sp, (zbyte *)(curr_undo + 1) + curr_undo->diff_size,
786 curr_undo->stack_size * sizeof (*sp));
787
788 curr_undo = curr_undo->prev;
789
790 restart_header ();
791
792 return 2;
793
794}/* restore_undo */
795
796/*
797 * z_restore_undo, restore a Z-machine state from memory.
798 *
799 * no zargs used
800 *
801 */
802
803void z_restore_undo (void)
804{
805
806 store ((zword) restore_undo ());
807
808}/* z_restore_undo */
809
810/*
811 * z_save, save [a part of] the Z-machine state to disk.
812 *
813 * zargs[0] = address of memory area to save (optional)
814 * zargs[1] = number of bytes to save
815 * zargs[2] = address of suggested file name
816 *
817 */
818
819void z_save (void)
820{
821 char new_name[MAX_PATH];
822 char default_name[MAX_PATH];
823 int gfp;
824
825 zword success = 0;
826
827 if (zargc != 0) {
828
829 /* Get the file name */
830
831 get_default_name (default_name, (zargc >= 3) ? zargs[2] : 0);
832
833 if (os_read_file_name (new_name, default_name, FILE_SAVE_AUX) == 0)
834 goto finished;
835
836 strcpy (auxilary_name, default_name);
837
838 /* Open auxilary file */
839
840 if ((gfp = rb->open (new_name, O_WRONLY|O_CREAT|O_TRUNC)) < 0)
841 goto finished;
842
843 /* Write auxilary file */
844
845 success = fwrite (zmp + zargs[0], zargs[1], 1, gfp);
846
847 /* Close auxilary file */
848
849 fclose (gfp);
850
851 } else {
852
853 /* Get the file name */
854
855 if (os_read_file_name (new_name, save_name, FILE_SAVE) == 0)
856 goto finished;
857
858 strcpy (save_name, new_name);
859
860 /* Open game file */
861
862 if ((gfp = rb->open (new_name, O_WRONLY|O_CREAT|O_TRUNC)) < 0)
863 goto finished;
864
865 success = save_quetzal (gfp, story_fp);
866
867 /* Close game file and check for errors */
868
869 if (fclose (gfp) != 0 || ferror (story_fp)) {
870 print_string ("Error writing save file\n");
871 goto finished;
872 }
873
874 /* Success */
875
876 success = 1;
877
878 }
879
880finished:
881
882 if (h_version <= V3)
883 branch (success);
884 else
885 store (success);
886
887}/* z_save */
888
889/*
890 * save_undo
891 *
892 * This function does the dirty work for z_save_undo.
893 *
894 */
895
896int save_undo (void)
897{
898 long diff_size;
899 zword stack_size;
900 int size;
901 undo_t *p;
902
903 if (f_setup.undo_slots == 0) /* undo feature unavailable */
904 return -1;
905
906 /* save undo possible */
907
908 while (last_undo != curr_undo) {
909 p = last_undo;
910 last_undo = last_undo->prev;
911 arena_next = p;
912 undo_count--;
913 }
914 if (last_undo)
915 last_undo->next = NULL;
916 else
917 first_undo = NULL;
918
919 if (undo_count == f_setup.undo_slots)
920 free_undo (1);
921
922 diff_size = mem_diff (zmp, prev_zmp, h_dynamic_size, undo_diff);
923 stack_size = stack + STACK_SIZE - sp;
924 do {
925 size = sizeof (undo_t) + diff_size + stack_size * sizeof (*sp);
926 if (arena_next > (void*)first_undo) {
927 /* Free space is all at the end */
928 if ((arena_end - arena_next) >= size) {
929 /* Trivial: enough room at the end */
930 p = arena_next;
931 arena_next = (void*)((int)(arena_next + size + 3) & ~3);
932 } else {
933 /* Need to wrap */
934 arena_next = arena_start;
935 p = NULL;
936 }
937 } else {
938 /* Free space is somewhere else */
939 if (((void*)first_undo - arena_next) >= size) {
940 /* There is room before the "first" undo */
941 p = arena_next;
942 arena_next = (void*)((int)(arena_next + size + 3) & ~3);
943 } else {
944 /* Not enough room, just need to free some */
945 p = NULL;
946 }
947 }
948
949 if (p == NULL)
950 free_undo (1);
951 } while (!p && undo_count);
952 if (p == NULL)
953 return -1;
954 GET_PC (p->pc)
955 p->frame_count = frame_count;
956 p->diff_size = diff_size;
957 p->stack_size = stack_size;
958 p->frame_offset = fp - stack;
959 memcpy (p + 1, undo_diff, diff_size);
960 memcpy ((zbyte *)(p + 1) + diff_size, sp, stack_size * sizeof (*sp));
961
962 if (!first_undo) {
963 p->prev = NULL;
964 first_undo = p;
965 } else {
966 last_undo->next = p;
967 p->prev = last_undo;
968 }
969 p->next = NULL;
970 curr_undo = last_undo = p;
971 undo_count++;
972 return 1;
973
974}/* save_undo */
975
976/*
977 * z_save_undo, save the current Z-machine state for a future undo.
978 *
979 * no zargs used
980 *
981 */
982
983void z_save_undo (void)
984{
985
986 store ((zword) save_undo ());
987
988}/* z_save_undo */
989
990/*
991 * z_verify, check the story file integrity.
992 *
993 * no zargs used
994 *
995 */
996
997void z_verify (void)
998{
999 zword checksum = 0;
1000 long i;
1001
1002 /* Sum all bytes in story file except header bytes */
1003
1004 fseek (story_fp, 64, SEEK_SET);
1005
1006 for (i = 64; i < story_size; i++)
1007 checksum += fgetc (story_fp);
1008
1009 /* Branch if the checksums are equal */
1010
1011 branch (checksum == h_checksum);
1012
1013}/* z_verify */
diff --git a/apps/plugins/frotz/files.c b/apps/plugins/frotz/files.c
new file mode 100644
index 0000000000..1baaa4073f
--- /dev/null
+++ b/apps/plugins/frotz/files.c
@@ -0,0 +1,563 @@
1/* files.c - Transscription, recording and playback
2 * Copyright (c) 1995-1997 Stefan Jokisch
3 *
4 * Changes for Rockbox copyright 2009 Torne Wuff
5 *
6 * This file is part of Frotz.
7 *
8 * Frotz is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * Frotz is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
21 */
22
23#include "frotz.h"
24
25extern void set_more_prompts (bool);
26
27extern bool is_terminator (zchar);
28
29extern bool read_yes_or_no (const char *);
30
31char script_name[MAX_PATH];
32char command_name[MAX_PATH];
33
34#ifdef __MSDOS__
35extern char latin1_to_ibm[];
36#endif
37
38static int script_width = 0;
39
40int sfp = -1;
41int rfp = -1;
42int pfp = -1;
43
44/*
45 * script_open
46 *
47 * Open the transscript file. 'AMFV' makes this more complicated as it
48 * turns transscription on/off several times to exclude some text from
49 * the transscription file. This wasn't a problem for the original V4
50 * interpreters which always sent transscription to the printer, but it
51 * means a problem to modern interpreters that offer to open a new file
52 * every time transscription is turned on. Our solution is to append to
53 * the old transscription file in V1 to V4, and to ask for a new file
54 * name in V5+.
55 *
56 */
57
58void script_open (void)
59{
60 static bool script_valid = FALSE;
61
62 char new_name[MAX_PATH];
63
64 h_flags &= ~SCRIPTING_FLAG;
65
66 if (h_version >= V5 || !script_valid) {
67
68 if (!os_read_file_name (new_name, script_name, FILE_SCRIPT))
69 goto done;
70
71 strcpy (script_name, new_name);
72
73 }
74
75 /* Opening in "at" mode doesn't work for script_erase_input... */
76
77 if ((sfp = rb->open (script_name, O_RDWR|O_CREAT)) != -1) {
78
79 fseek (sfp, 0, SEEK_END);
80
81 h_flags |= SCRIPTING_FLAG;
82
83 script_valid = TRUE;
84 ostream_script = TRUE;
85
86 script_width = 0;
87
88 } else print_string ("Cannot open file\n");
89
90done:
91
92 SET_WORD (H_FLAGS, h_flags)
93
94}/* script_open */
95
96/*
97 * script_close
98 *
99 * Stop transscription.
100 *
101 */
102
103void script_close (void)
104{
105
106 h_flags &= ~SCRIPTING_FLAG;
107 SET_WORD (H_FLAGS, h_flags)
108
109 fclose (sfp); ostream_script = FALSE;
110 sfp = -1;
111
112}/* script_close */
113
114/*
115 * script_new_line
116 *
117 * Write a newline to the transscript file.
118 *
119 */
120
121void script_new_line (void)
122{
123
124 if (fputc ('\n', sfp) == EOF)
125 script_close ();
126
127 script_width = 0;
128
129}/* script_new_line */
130
131/*
132 * script_char
133 *
134 * Write a single character to the transscript file.
135 *
136 */
137
138void script_char (zchar c)
139{
140
141 if (c == ZC_INDENT && script_width != 0)
142 c = ' ';
143
144 if (c == ZC_INDENT)
145 { script_char (' '); script_char (' '); script_char (' '); return; }
146 if (c == ZC_GAP)
147 { script_char (' '); script_char (' '); return; }
148
149#ifdef __MSDOS__
150 if (c >= ZC_LATIN1_MIN)
151 c = latin1_to_ibm[c - ZC_LATIN1_MIN];
152#endif
153
154 fputc (c, sfp); script_width++;
155
156}/* script_char */
157
158/*
159 * script_word
160 *
161 * Write a string to the transscript file.
162 *
163 */
164
165void script_word (const zchar *s)
166{
167 int width;
168 int i;
169
170 if (*s == ZC_INDENT && script_width != 0)
171 script_char (*s++);
172
173 for (i = 0, width = 0; s[i] != 0; i++)
174
175 if (s[i] == ZC_NEW_STYLE || s[i] == ZC_NEW_FONT)
176 i++;
177 else if (s[i] == ZC_GAP)
178 width += 3;
179 else if (s[i] == ZC_INDENT)
180 width += 2;
181 else
182 width += 1;
183
184 if (f_setup.script_cols != 0 && script_width + width > f_setup.script_cols) {
185
186 if (*s == ' ' || *s == ZC_INDENT || *s == ZC_GAP)
187 s++;
188
189 script_new_line ();
190
191 }
192
193 for (i = 0; s[i] != 0; i++)
194
195 if (s[i] == ZC_NEW_FONT || s[i] == ZC_NEW_STYLE)
196 i++;
197 else
198 script_char (s[i]);
199
200}/* script_word */
201
202/*
203 * script_write_input
204 *
205 * Send an input line to the transscript file.
206 *
207 */
208
209void script_write_input (const zchar *buf, zchar key)
210{
211 int width;
212 int i;
213
214 for (i = 0, width = 0; buf[i] != 0; i++)
215 width++;
216
217 if (f_setup.script_cols != 0 && script_width + width > f_setup.script_cols)
218 script_new_line ();
219
220 for (i = 0; buf[i] != 0; i++)
221 script_char (buf[i]);
222
223 if (key == ZC_RETURN)
224 script_new_line ();
225
226}/* script_write_input */
227
228/*
229 * script_erase_input
230 *
231 * Remove an input line from the transscript file.
232 *
233 */
234
235void script_erase_input (const zchar *buf)
236{
237 int width;
238 int i;
239
240 for (i = 0, width = 0; buf[i] != 0; i++)
241 width++;
242
243 fseek (sfp, -width, SEEK_CUR); script_width -= width;
244
245}/* script_erase_input */
246
247/*
248 * script_mssg_on
249 *
250 * Start sending a "debugging" message to the transscript file.
251 *
252 */
253
254void script_mssg_on (void)
255{
256
257 if (script_width != 0)
258 script_new_line ();
259
260 script_char (ZC_INDENT);
261
262}/* script_mssg_on */
263
264/*
265 * script_mssg_off
266 *
267 * Stop writing a "debugging" message.
268 *
269 */
270
271void script_mssg_off (void)
272{
273
274 script_new_line ();
275
276}/* script_mssg_off */
277
278/*
279 * record_open
280 *
281 * Open a file to record the player's input.
282 *
283 */
284
285void record_open (void)
286{
287 char new_name[MAX_PATH];
288
289 if (os_read_file_name (new_name, command_name, FILE_RECORD)) {
290
291 strcpy (command_name, new_name);
292
293 if ((rfp = rb->open (new_name, O_WRONLY|O_CREAT|O_TRUNC)) != -1)
294 ostream_record = TRUE;
295 else
296 print_string ("Cannot open file\n");
297
298 }
299
300}/* record_open */
301
302/*
303 * record_close
304 *
305 * Stop recording the player's input.
306 *
307 */
308
309void record_close (void)
310{
311
312 fclose (rfp); ostream_record = FALSE;
313 rfp = -1;
314
315}/* record_close */
316
317/*
318 * record_code
319 *
320 * Helper function for record_char.
321 *
322 */
323
324static void record_code (int c, bool force_encoding)
325{
326
327 if (force_encoding || c == '[' || c < 0x20 || c > 0x7e) {
328
329 int i;
330
331 fputc ('[', rfp);
332
333 for (i = 10000; i != 0; i /= 10)
334 if (c >= i || i == 1)
335 fputc ('0' + (c / i) % 10, rfp);
336
337 fputc (']', rfp);
338
339 } else fputc (c, rfp);
340
341}/* record_code */
342
343/*
344 * record_char
345 *
346 * Write a character to the command file.
347 *
348 */
349
350static void record_char (zchar c)
351{
352
353 if (c != ZC_RETURN) {
354 if (c < ZC_HKEY_MIN || c > ZC_HKEY_MAX) {
355 record_code (translate_to_zscii (c), FALSE);
356 if (c == ZC_SINGLE_CLICK || c == ZC_DOUBLE_CLICK) {
357 record_code (mouse_x, TRUE);
358 record_code (mouse_y, TRUE);
359 }
360 } else record_code (1000 + c - ZC_HKEY_MIN, TRUE);
361 }
362
363}/* record_char */
364
365/*
366 * record_write_key
367 *
368 * Copy a keystroke to the command file.
369 *
370 */
371
372void record_write_key (zchar key)
373{
374
375 record_char (key);
376
377 if (fputc ('\n', rfp) == EOF)
378 record_close ();
379
380}/* record_write_key */
381
382/*
383 * record_write_input
384 *
385 * Copy a line of input to a command file.
386 *
387 */
388
389void record_write_input (const zchar *buf, zchar key)
390{
391 zchar c;
392
393 while ((c = *buf++) != 0)
394 record_char (c);
395
396 record_char (key);
397
398 if (fputc ('\n', rfp) == EOF)
399 record_close ();
400
401}/* record_write_input */
402
403/*
404 * replay_open
405 *
406 * Open a file of commands for playback.
407 *
408 */
409
410void replay_open (void)
411{
412 char new_name[MAX_PATH];
413
414 if (os_read_file_name (new_name, command_name, FILE_PLAYBACK)) {
415
416 strcpy (command_name, new_name);
417
418 if ((pfp = rb->open (new_name, O_RDONLY)) != -1) {
419
420 set_more_prompts (read_yes_or_no ("Do you want MORE prompts"));
421
422 istream_replay = TRUE;
423
424 } else print_string ("Cannot open file\n");
425
426 }
427
428}/* replay_open */
429
430/*
431 * replay_close
432 *
433 * Stop playback of commands.
434 *
435 */
436
437void replay_close (void)
438{
439
440 set_more_prompts (TRUE);
441
442 fclose (pfp); istream_replay = FALSE;
443 pfp = -1;
444
445}/* replay_close */
446
447/*
448 * replay_code
449 *
450 * Helper function for replay_key and replay_line.
451 *
452 */
453
454static int replay_code (void)
455{
456 int c;
457
458 if ((c = fgetc (pfp)) == '[') {
459
460 int c2;
461
462 c = 0;
463
464 while ((c2 = fgetc (pfp)) != EOF && c2 >= '0' && c2 <= '9')
465 c = 10 * c + c2 - '0';
466
467 return (c2 == ']') ? c : EOF;
468
469 } else return c;
470
471}/* replay_code */
472
473/*
474 * replay_char
475 *
476 * Read a character from the command file.
477 *
478 */
479
480static zchar replay_char (void)
481{
482 int c;
483
484 if ((c = replay_code ()) != EOF) {
485
486 if (c != '\n') {
487
488 if (c < 1000) {
489
490 c = translate_from_zscii (c);
491
492 if (c == ZC_SINGLE_CLICK || c == ZC_DOUBLE_CLICK) {
493 mouse_x = replay_code ();
494 mouse_y = replay_code ();
495 }
496
497 return c;
498
499 } else return ZC_HKEY_MIN + c - 1000;
500 }
501
502 ungetc ('\n', pfp);
503
504 return ZC_RETURN;
505
506 } else return ZC_BAD;
507
508}/* replay_char */
509
510/*
511 * replay_read_key
512 *
513 * Read a keystroke from a command file.
514 *
515 */
516
517zchar replay_read_key (void)
518{
519 zchar key;
520
521 key = replay_char ();
522
523 if (fgetc (pfp) != '\n') {
524
525 replay_close ();
526 return ZC_BAD;
527
528 } else return key;
529
530}/* replay_read_key */
531
532/*
533 * replay_read_input
534 *
535 * Read a line of input from a command file.
536 *
537 */
538
539zchar replay_read_input (zchar *buf)
540{
541 zchar c;
542
543 for (;;) {
544
545 c = replay_char ();
546
547 if (c == ZC_BAD || is_terminator (c))
548 break;
549
550 *buf++ = c;
551
552 }
553
554 *buf = 0;
555
556 if (fgetc (pfp) != '\n') {
557
558 replay_close ();
559 return ZC_BAD;
560
561 } else return c;
562
563}/* replay_read_input */
diff --git a/apps/plugins/frotz/frotz.c b/apps/plugins/frotz/frotz.c
new file mode 100644
index 0000000000..96029b85cb
--- /dev/null
+++ b/apps/plugins/frotz/frotz.c
@@ -0,0 +1,314 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2009 Torne Wuff
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
16 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ****************************************************************************/
21#include "plugin.h"
22#include "dumb_frotz.h"
23#include "lib/pluginlib_exit.h"
24#include "lib/pluginlib_actions.h"
25
26PLUGIN_HEADER
27
28extern int frotz_main(void);
29extern bool hot_key_quit(void);
30
31f_setup_t f_setup;
32extern char save_name[], auxilary_name[], script_name[], command_name[];
33extern int story_fp, sfp, rfp, pfp;
34
35static struct viewport vp[NB_SCREENS];
36
37static void atexit_cleanup(void);
38
39enum plugin_status plugin_start(const void* parameter)
40{
41 int i;
42 char* ext;
43
44 PLUGINLIB_EXIT_INIT_ATEXIT(atexit_cleanup);
45
46 if (!parameter)
47 return PLUGIN_ERROR;
48
49 rb->lcd_setfont(FONT_SYSFIXED);
50#if LCD_DEPTH > 1
51 rb->lcd_set_backdrop(NULL);
52#endif
53 rb->lcd_clear_display();
54
55 FOR_NB_SCREENS(i)
56 rb->viewport_set_defaults(&vp[i], i);
57
58 story_name = parameter;
59 strcpy(save_name, story_name);
60 ext = rb->strrchr(save_name, '.');
61 if (ext)
62 *ext = '\0';
63 strcpy(auxilary_name, save_name);
64 strcpy(script_name, save_name);
65 strcpy(command_name, save_name);
66 rb->strlcat(save_name, ".sav", MAX_PATH);
67 rb->strlcat(auxilary_name, ".aux", MAX_PATH);
68 rb->strlcat(script_name, ".scr", MAX_PATH);
69 rb->strlcat(command_name, ".rec", MAX_PATH);
70
71 frotz_main();
72
73 return PLUGIN_OK;
74}
75
76void atexit_cleanup()
77{
78 if (story_fp != -1)
79 fclose(story_fp);
80 if (sfp != -1)
81 fclose(sfp);
82 if (rfp != -1)
83 fclose(rfp);
84 if (pfp != -1)
85 fclose(pfp);
86}
87
88MENUITEM_STRINGLIST(ingame_menu, "Frotz", NULL, "Resume", "Undo", "Restart",
89 "Toggle input recording", "Play back input",
90 "Debug options", "Exit");
91
92zchar menu(void)
93{
94 switch (rb->do_menu(&ingame_menu, NULL, vp, true))
95 {
96 case 0:
97 default:
98 dumb_dump_screen();
99 return ZC_BAD;
100 case 1:
101 return ZC_HKEY_UNDO;
102 case 2:
103 return ZC_HKEY_RESTART;
104 case 3:
105 return ZC_HKEY_RECORD;
106 case 4:
107 return ZC_HKEY_PLAYBACK;
108 case 5:
109 return ZC_HKEY_DEBUG;
110 case 6:
111 return ZC_HKEY_QUIT;
112 }
113}
114
115const struct button_mapping* plugin_contexts[]={generic_actions};
116
117void wait_for_key()
118{
119 int action;
120
121 dumb_show_screen(false);
122
123 for (;;)
124 {
125 action = pluginlib_getaction(TIMEOUT_BLOCK,
126 plugin_contexts, 1);
127 switch (action)
128 {
129 case PLA_QUIT:
130 hot_key_quit();
131 break;
132
133 case PLA_FIRE:
134 return;
135 }
136 }
137}
138
139zchar do_input(int timeout, bool show_cursor)
140{
141 int action;
142 long timeout_at;
143 zchar menu_ret;
144
145 dumb_show_screen(show_cursor);
146
147 /* Convert timeout (tenths of a second) to ticks */
148 if (timeout > 0)
149 timeout = (timeout * HZ) / 10;
150 else
151 timeout = TIMEOUT_BLOCK;
152
153 timeout_at = *rb->current_tick + timeout;
154
155 for (;;)
156 {
157 action = pluginlib_getaction(timeout,
158 plugin_contexts, 1);
159 switch (action)
160 {
161 case PLA_QUIT:
162 return ZC_HKEY_QUIT;
163
164 case PLA_MENU:
165 menu_ret = menu();
166 if (menu_ret != ZC_BAD)
167 return menu_ret;
168 timeout_at = *rb->current_tick + timeout;
169 break;
170
171 case PLA_FIRE:
172 return ZC_RETURN;
173
174 case PLA_START:
175 return ZC_BAD;
176
177 default:
178 if (timeout != TIMEOUT_BLOCK &&
179 !TIME_BEFORE(*rb->current_tick, timeout_at))
180 return ZC_TIME_OUT;
181 }
182 }
183}
184
185zchar os_read_key(int timeout, bool show_cursor)
186{
187 int r;
188 char inputbuf[5];
189 short key;
190 zchar zkey;
191
192 for(;;)
193 {
194 zkey = do_input(timeout, show_cursor);
195 if (zkey != ZC_BAD)
196 return zkey;
197
198 inputbuf[0] = '\0';
199 r = rb->kbd_input(inputbuf, 5);
200 rb->lcd_setfont(FONT_SYSFIXED);
201 dumb_dump_screen();
202 if (!r)
203 {
204 rb->utf8decode(inputbuf, &key);
205 if (key > 0 && key < 256)
206 return (zchar)key;
207 }
208 }
209}
210
211zchar os_read_line(int max, zchar *buf, int timeout, int width, int continued)
212{
213 (void)continued;
214 int r;
215 char inputbuf[256];
216 const char *in;
217 char *out;
218 short key;
219 zchar zkey;
220
221 for(;;)
222 {
223 zkey = do_input(timeout, true);
224 if (zkey != ZC_BAD)
225 return zkey;
226
227 if (max > width)
228 max = width;
229 strcpy(inputbuf, buf);
230 r = rb->kbd_input(inputbuf, 256);
231 rb->lcd_setfont(FONT_SYSFIXED);
232 dumb_dump_screen();
233 if (!r)
234 {
235 in = inputbuf;
236 out = buf;
237 while (*in && max)
238 {
239 in = rb->utf8decode(in, &key);
240 if (key > 0 && key < 256)
241 {
242 *out++ = key;
243 max--;
244 }
245 }
246 *out = '\0';
247 os_display_string(buf);
248 return ZC_RETURN;
249 }
250 }
251}
252
253bool read_yes_or_no(const char *s)
254{
255 char message_line[50];
256 const char *message_lines[] = {message_line};
257 const struct text_message message = {message_lines, 1};
258
259 rb->strlcpy(message_line, s, 49);
260 rb->strcat(message_line, "?");
261
262 if (rb->gui_syncyesno_run(&message, NULL, NULL) == YESNO_YES)
263 return TRUE;
264 else
265 return FALSE;
266}
267
268zchar os_read_mouse(void)
269{
270 return 0;
271}
272
273int os_read_file_name(char *file_name, const char *default_name, int flag)
274{
275 (void)flag;
276 strcpy(file_name, default_name);
277 return TRUE;
278}
279
280void os_beep(int volume)
281{
282 rb->splashf(HZ/2, "[%s-PITCHED BEEP]", (volume==1) ? "HIGH" : "LOW");
283}
284
285static unsigned char unget_buf;
286static int unget_file;
287
288int ungetc(int c, int f)
289{
290 unget_file = f;
291 unget_buf = c;
292 return c;
293}
294
295int fgetc(int f)
296{
297 unsigned char cb;
298 if (unget_file == f)
299 {
300 unget_file = -1;
301 return unget_buf;
302 }
303 if (rb->read(f, &cb, 1) != 1)
304 return EOF;
305 return cb;
306}
307
308int fputc(int c, int f)
309{
310 unsigned char cb = c;
311 if (rb->write(f, &cb, 1) != 1)
312 return EOF;
313 return cb;
314}
diff --git a/apps/plugins/frotz/frotz.h b/apps/plugins/frotz/frotz.h
new file mode 100644
index 0000000000..bc8869cd24
--- /dev/null
+++ b/apps/plugins/frotz/frotz.h
@@ -0,0 +1,583 @@
1/*
2 * frotz.h
3 *
4 * Global declarations and definitions
5 *
6 */
7
8#include "frotzplugin.h"
9
10#ifndef TRUE
11#define TRUE 1
12#endif
13
14#ifndef FALSE
15#define FALSE 0
16#endif
17
18typedef unsigned char zbyte;
19typedef unsigned short zword;
20
21enum story {
22 BEYOND_ZORK,
23 SHERLOCK,
24 ZORK_ZERO,
25 SHOGUN,
26 ARTHUR,
27 JOURNEY,
28 LURKING_HORROR,
29 UNKNOWN
30};
31
32typedef unsigned char zchar;
33
34/*** Constants that may be set at compile time ***/
35
36#ifndef MAX_UNDO_SLOTS
37#define MAX_UNDO_SLOTS 500
38#endif
39#ifndef TEXT_BUFFER_SIZE
40#define TEXT_BUFFER_SIZE 200
41#endif
42#ifndef INPUT_BUFFER_SIZE
43#define INPUT_BUFFER_SIZE 200
44#endif
45#ifndef STACK_SIZE
46#define STACK_SIZE 1024
47#endif
48
49/*** Story file header format ***/
50
51#define H_VERSION 0
52#define H_CONFIG 1
53#define H_RELEASE 2
54#define H_RESIDENT_SIZE 4
55#define H_START_PC 6
56#define H_DICTIONARY 8
57#define H_OBJECTS 10
58#define H_GLOBALS 12
59#define H_DYNAMIC_SIZE 14
60#define H_FLAGS 16
61#define H_SERIAL 18
62#define H_ABBREVIATIONS 24
63#define H_FILE_SIZE 26
64#define H_CHECKSUM 28
65#define H_INTERPRETER_NUMBER 30
66#define H_INTERPRETER_VERSION 31
67#define H_SCREEN_ROWS 32
68#define H_SCREEN_COLS 33
69#define H_SCREEN_WIDTH 34
70#define H_SCREEN_HEIGHT 36
71#define H_FONT_HEIGHT 38 /* this is the font width in V5 */
72#define H_FONT_WIDTH 39 /* this is the font height in V5 */
73#define H_FUNCTIONS_OFFSET 40
74#define H_STRINGS_OFFSET 42
75#define H_DEFAULT_BACKGROUND 44
76#define H_DEFAULT_FOREGROUND 45
77#define H_TERMINATING_KEYS 46
78#define H_LINE_WIDTH 48
79#define H_STANDARD_HIGH 50
80#define H_STANDARD_LOW 51
81#define H_ALPHABET 52
82#define H_EXTENSION_TABLE 54
83#define H_USER_NAME 56
84
85#define HX_TABLE_SIZE 0
86#define HX_MOUSE_X 1
87#define HX_MOUSE_Y 2
88#define HX_UNICODE_TABLE 3
89
90/*** Various Z-machine constants ***/
91
92#define V1 1
93#define V2 2
94#define V3 3
95#define V4 4
96#define V5 5
97#define V6 6
98#define V7 7
99#define V8 8
100
101#define CONFIG_BYTE_SWAPPED 0x01 /* Story file is byte swapped - V3 */
102#define CONFIG_TIME 0x02 /* Status line displays time - V3 */
103#define CONFIG_TWODISKS 0x04 /* Story file occupied two disks - V3 */
104#define CONFIG_TANDY 0x08 /* Tandy licensed game - V3 */
105#define CONFIG_NOSTATUSLINE 0x10 /* Interpr can't support status lines - V3 */
106#define CONFIG_SPLITSCREEN 0x20 /* Interpr supports split screen mode - V3 */
107#define CONFIG_PROPORTIONAL 0x40 /* Interpr uses proportional font - V3 */
108
109#define CONFIG_COLOUR 0x01 /* Interpr supports colour - V5+ */
110#define CONFIG_PICTURES 0x02 /* Interpr supports pictures - V6 */
111#define CONFIG_BOLDFACE 0x04 /* Interpr supports boldface style - V4+ */
112#define CONFIG_EMPHASIS 0x08 /* Interpr supports emphasis style - V4+ */
113#define CONFIG_FIXED 0x10 /* Interpr supports fixed width style - V4+ */
114#define CONFIG_SOUND 0x20 /* Interpr supports sound - V6 */
115
116#define CONFIG_TIMEDINPUT 0x80 /* Interpr supports timed input - V4+ */
117
118#define SCRIPTING_FLAG 0x0001 /* Outputting to transscription file - V1+ */
119#define FIXED_FONT_FLAG 0x0002 /* Use fixed width font - V3+ */
120#define REFRESH_FLAG 0x0004 /* Refresh the screen - V6 */
121#define GRAPHICS_FLAG 0x0008 /* Game wants to use graphics - V5+ */
122#define OLD_SOUND_FLAG 0x0010 /* Game wants to use sound effects - V3 */
123#define UNDO_FLAG 0x0010 /* Game wants to use UNDO feature - V5+ */
124#define MOUSE_FLAG 0x0020 /* Game wants to use a mouse - V5+ */
125#define COLOUR_FLAG 0x0040 /* Game wants to use colours - V5+ */
126#define SOUND_FLAG 0x0080 /* Game wants to use sound effects - V5+ */
127#define MENU_FLAG 0x0100 /* Game wants to use menus - V6 */
128
129#define INTERP_DEFAULT 0
130#define INTERP_DEC_20 1
131#define INTERP_APPLE_IIE 2
132#define INTERP_MACINTOSH 3
133#define INTERP_AMIGA 4
134#define INTERP_ATARI_ST 5
135#define INTERP_MSDOS 6
136#define INTERP_CBM_128 7
137#define INTERP_CBM_64 8
138#define INTERP_APPLE_IIC 9
139#define INTERP_APPLE_IIGS 10
140#define INTERP_TANDY 11
141
142#define BLACK_COLOUR 2
143#define RED_COLOUR 3
144#define GREEN_COLOUR 4
145#define YELLOW_COLOUR 5
146#define BLUE_COLOUR 6
147#define MAGENTA_COLOUR 7
148#define CYAN_COLOUR 8
149#define WHITE_COLOUR 9
150#define GREY_COLOUR 10 /* INTERP_MSDOS only */
151#define LIGHTGREY_COLOUR 10 /* INTERP_AMIGA only */
152#define MEDIUMGREY_COLOUR 11 /* INTERP_AMIGA only */
153#define DARKGREY_COLOUR 12 /* INTERP_AMIGA only */
154
155#define REVERSE_STYLE 1
156#define BOLDFACE_STYLE 2
157#define EMPHASIS_STYLE 4
158#define FIXED_WIDTH_STYLE 8
159
160#define TEXT_FONT 1
161#define PICTURE_FONT 2
162#define GRAPHICS_FONT 3
163#define FIXED_WIDTH_FONT 4
164
165#define BEEP_HIGH 1
166#define BEEP_LOW 2
167
168/*** Constants for os_restart_game */
169
170#define RESTART_BEGIN 0
171#define RESTART_WPROP_SET 1
172#define RESTART_END 2
173
174/*** Character codes ***/
175
176#define ZC_TIME_OUT 0x00
177#define ZC_NEW_STYLE 0x01
178#define ZC_NEW_FONT 0x02
179#define ZC_BACKSPACE 0x08
180#define ZC_INDENT 0x09
181#define ZC_GAP 0x0b
182#define ZC_RETURN 0x0d
183#define ZC_HKEY_MIN 0x0e
184#define ZC_HKEY_RECORD 0x0e
185#define ZC_HKEY_PLAYBACK 0x0f
186#define ZC_HKEY_SEED 0x10
187#define ZC_HKEY_UNDO 0x11
188#define ZC_HKEY_RESTART 0x12
189#define ZC_HKEY_QUIT 0x13
190#define ZC_HKEY_DEBUG 0x14
191#define ZC_HKEY_HELP 0x15
192#define ZC_HKEY_MAX 0x15
193#define ZC_ESCAPE 0x1b
194#define ZC_ASCII_MIN 0x20
195#define ZC_ASCII_MAX 0x7e
196#define ZC_BAD 0x7f
197#define ZC_ARROW_MIN 0x81
198#define ZC_ARROW_UP 0x81
199#define ZC_ARROW_DOWN 0x82
200#define ZC_ARROW_LEFT 0x83
201#define ZC_ARROW_RIGHT 0x84
202#define ZC_ARROW_MAX 0x84
203#define ZC_FKEY_MIN 0x85
204#define ZC_FKEY_MAX 0x90
205#define ZC_NUMPAD_MIN 0x91
206#define ZC_NUMPAD_MAX 0x9a
207#define ZC_SINGLE_CLICK 0x9b
208#define ZC_DOUBLE_CLICK 0x9c
209#define ZC_MENU_CLICK 0x9d
210#define ZC_LATIN1_MIN 0xa0
211#define ZC_LATIN1_MAX 0xff
212
213/*** File types ***/
214
215#define FILE_RESTORE 0
216#define FILE_SAVE 1
217#define FILE_SCRIPT 2
218#define FILE_PLAYBACK 3
219#define FILE_RECORD 4
220#define FILE_LOAD_AUX 5
221#define FILE_SAVE_AUX 6
222
223/*** Data access macros ***/
224
225#define SET_BYTE(addr,v) { zmp[addr] = v; }
226#define LOW_BYTE(addr,v) { v = zmp[addr]; }
227#define CODE_BYTE(v) { v = *pcp++; }
228
229#if defined (AMIGA)
230
231extern zbyte *pcp;
232extern zbyte *zmp;
233
234#define lo(v) ((zbyte *)&v)[1]
235#define hi(v) ((zbyte *)&v)[0]
236
237#define SET_WORD(addr,v) { zmp[addr] = hi(v); zmp[addr+1] = lo(v); }
238#define LOW_WORD(addr,v) { hi(v) = zmp[addr]; lo(v) = zmp[addr+1]; }
239#define HIGH_WORD(addr,v) { hi(v) = zmp[addr]; lo(v) = zmp[addr+1]; }
240#define CODE_WORD(v) { hi(v) = *pcp++; lo(v) = *pcp++; }
241#define GET_PC(v) { v = pcp - zmp; }
242#define SET_PC(v) { pcp = zmp + v; }
243
244#endif
245
246/* A bunch of x86 assembly code previously appeared here. */
247
248#if !defined (AMIGA) && !defined (MSDOS_16BIT)
249
250extern zbyte *pcp;
251extern zbyte *zmp;
252
253#define lo(v) (v & 0xff)
254#define hi(v) (v >> 8)
255
256#define SET_WORD(addr,v) { zmp[addr] = hi(v); zmp[addr+1] = lo(v); }
257#define LOW_WORD(addr,v) { v = ((zword) zmp[addr] << 8) | zmp[addr+1]; }
258#define HIGH_WORD(addr,v) { v = ((zword) zmp[addr] << 8) | zmp[addr+1]; }
259#define CODE_WORD(v) { v = ((zword) pcp[0] << 8) | pcp[1]; pcp += 2; }
260#define GET_PC(v) { v = pcp - zmp; }
261#define SET_PC(v) { pcp = zmp + v; }
262
263#endif
264
265
266/*** Story file header data ***/
267
268extern zbyte h_version;
269extern zbyte h_config;
270extern zword h_release;
271extern zword h_resident_size;
272extern zword h_start_pc;
273extern zword h_dictionary;
274extern zword h_objects;
275extern zword h_globals;
276extern zword h_dynamic_size;
277extern zword h_flags;
278extern zbyte h_serial[6];
279extern zword h_abbreviations;
280extern zword h_file_size;
281extern zword h_checksum;
282extern zbyte h_interpreter_number;
283extern zbyte h_interpreter_version;
284extern zbyte h_screen_rows;
285extern zbyte h_screen_cols;
286extern zword h_screen_width;
287extern zword h_screen_height;
288extern zbyte h_font_height;
289extern zbyte h_font_width;
290extern zword h_functions_offset;
291extern zword h_strings_offset;
292extern zbyte h_default_background;
293extern zbyte h_default_foreground;
294extern zword h_terminating_keys;
295extern zword h_line_width;
296extern zbyte h_standard_high;
297extern zbyte h_standard_low;
298extern zword h_alphabet;
299extern zword h_extension_table;
300extern zbyte h_user_name[8];
301
302extern zword hx_table_size;
303extern zword hx_mouse_x;
304extern zword hx_mouse_y;
305extern zword hx_unicode_table;
306
307/*** Various data ***/
308
309extern const char *story_name;
310
311extern enum story story_id;
312extern long story_size;
313
314extern zword stack[STACK_SIZE];
315extern zword *sp;
316extern zword *fp;
317extern zword frame_count;
318
319extern zword zargs[8];
320extern int zargc;
321
322extern bool ostream_screen;
323extern bool ostream_script;
324extern bool ostream_memory;
325extern bool ostream_record;
326extern bool istream_replay;
327extern bool message;
328
329extern int cwin;
330extern int mwin;
331
332extern int mouse_x;
333extern int mouse_y;
334
335extern bool enable_wrapping;
336extern bool enable_scripting;
337extern bool enable_scrolling;
338extern bool enable_buffering;
339
340
341extern char *option_zcode_path; /* dg */
342
343
344/*** Blorb stuff ***/
345/*
346bb_err_t blorb_err;
347bb_map_t *blorb_map;
348*/
349
350/*** Z-machine opcodes ***/
351
352void z_add (void);
353void z_and (void);
354void z_art_shift (void);
355void z_buffer_mode (void);
356void z_call_n (void);
357void z_call_s (void);
358void z_catch (void);
359void z_check_arg_count (void);
360void z_check_unicode (void);
361void z_clear_attr (void);
362void z_copy_table (void);
363void z_dec (void);
364void z_dec_chk (void);
365void z_div (void);
366void z_draw_picture (void);
367void z_encode_text (void);
368void z_erase_line (void);
369void z_erase_picture (void);
370void z_erase_window (void);
371void z_get_child (void);
372void z_get_cursor (void);
373void z_get_next_prop (void);
374void z_get_parent (void);
375void z_get_prop (void);
376void z_get_prop_addr (void);
377void z_get_prop_len (void);
378void z_get_sibling (void);
379void z_get_wind_prop (void);
380void z_inc (void);
381void z_inc_chk (void);
382void z_input_stream (void);
383void z_insert_obj (void);
384void z_je (void);
385void z_jg (void);
386void z_jin (void);
387void z_jl (void);
388void z_jump (void);
389void z_jz (void);
390void z_load (void);
391void z_loadb (void);
392void z_loadw (void);
393void z_log_shift (void);
394void z_make_menu (void);
395void z_mod (void);
396void z_mouse_window (void);
397void z_move_window (void);
398void z_mul (void);
399void z_new_line (void);
400void z_nop (void);
401void z_not (void);
402void z_or (void);
403void z_output_stream (void);
404void z_picture_data (void);
405void z_picture_table (void);
406void z_piracy (void);
407void z_pop (void);
408void z_pop_stack (void);
409void z_print (void);
410void z_print_addr (void);
411void z_print_char (void);
412void z_print_form (void);
413void z_print_num (void);
414void z_print_obj (void);
415void z_print_paddr (void);
416void z_print_ret (void);
417void z_print_table (void);
418void z_print_unicode (void);
419void z_pull (void);
420void z_push (void);
421void z_push_stack (void);
422void z_put_prop (void);
423void z_put_wind_prop (void);
424void z_quit (void);
425void z_random (void);
426void z_read (void);
427void z_read_char (void);
428void z_read_mouse (void);
429void z_remove_obj (void);
430void z_restart (void);
431void z_restore (void);
432void z_restore_undo (void);
433void z_ret (void);
434void z_ret_popped (void);
435void z_rfalse (void);
436void z_rtrue (void);
437void z_save (void);
438void z_save_undo (void);
439void z_scan_table (void);
440void z_scroll_window (void);
441void z_set_attr (void);
442void z_set_font (void);
443void z_set_colour (void);
444void z_set_cursor (void);
445void z_set_margins (void);
446void z_set_window (void);
447void z_set_text_style (void);
448void z_show_status (void);
449void z_sound_effect (void);
450void z_split_window (void);
451void z_store (void);
452void z_storeb (void);
453void z_storew (void);
454void z_sub (void);
455void z_test (void);
456void z_test_attr (void);
457void z_throw (void);
458void z_tokenise (void);
459void z_verify (void);
460void z_window_size (void);
461void z_window_style (void);
462
463/* Definitions for error handling functions and error codes. */
464
465/* extern int err_report_mode; */
466
467void init_err (void);
468void runtime_error (int);
469
470/* Error codes */
471#define ERR_TEXT_BUF_OVF 1 /* Text buffer overflow */
472#define ERR_STORE_RANGE 2 /* Store out of dynamic memory */
473#define ERR_DIV_ZERO 3 /* Division by zero */
474#define ERR_ILL_OBJ 4 /* Illegal object */
475#define ERR_ILL_ATTR 5 /* Illegal attribute */
476#define ERR_NO_PROP 6 /* No such property */
477#define ERR_STK_OVF 7 /* Stack overflow */
478#define ERR_ILL_CALL_ADDR 8 /* Call to illegal address */
479#define ERR_CALL_NON_RTN 9 /* Call to non-routine */
480#define ERR_STK_UNDF 10 /* Stack underflow */
481#define ERR_ILL_OPCODE 11 /* Illegal opcode */
482#define ERR_BAD_FRAME 12 /* Bad stack frame */
483#define ERR_ILL_JUMP_ADDR 13 /* Jump to illegal address */
484#define ERR_SAVE_IN_INTER 14 /* Can't save while in interrupt */
485#define ERR_STR3_NESTING 15 /* Nesting stream #3 too deep */
486#define ERR_ILL_WIN 16 /* Illegal window */
487#define ERR_ILL_WIN_PROP 17 /* Illegal window property */
488#define ERR_ILL_PRINT_ADDR 18 /* Print at illegal address */
489#define ERR_MAX_FATAL 18
490
491/* Less serious errors */
492#define ERR_JIN_0 19 /* @jin called with object 0 */
493#define ERR_GET_CHILD_0 20 /* @get_child called with object 0 */
494#define ERR_GET_PARENT_0 21 /* @get_parent called with object 0 */
495#define ERR_GET_SIBLING_0 22 /* @get_sibling called with object 0 */
496#define ERR_GET_PROP_ADDR_0 23 /* @get_prop_addr called with object 0 */
497#define ERR_GET_PROP_0 24 /* @get_prop called with object 0 */
498#define ERR_PUT_PROP_0 25 /* @put_prop called with object 0 */
499#define ERR_CLEAR_ATTR_0 26 /* @clear_attr called with object 0 */
500#define ERR_SET_ATTR_0 27 /* @set_attr called with object 0 */
501#define ERR_TEST_ATTR_0 28 /* @test_attr called with object 0 */
502#define ERR_MOVE_OBJECT_0 29 /* @move_object called moving object 0 */
503#define ERR_MOVE_OBJECT_TO_0 30 /* @move_object called moving into object 0 */
504#define ERR_REMOVE_OBJECT_0 31 /* @remove_object called with object 0 */
505#define ERR_GET_NEXT_PROP_0 32 /* @get_next_prop called with object 0 */
506#define ERR_NUM_ERRORS (32)
507
508/* There are four error reporting modes: never report errors;
509 report only the first time a given error type occurs; report
510 every time an error occurs; or treat all errors as fatal
511 errors, killing the interpreter. I strongly recommend
512 "report once" as the default. But you can compile in a
513 different default by changing the definition of
514 ERR_DEFAULT_REPORT_MODE. In any case, the player can
515 specify a report mode on the command line by typing "-Z 0"
516 through "-Z 3". */
517
518#define ERR_REPORT_NEVER (0)
519#define ERR_REPORT_ONCE (1)
520#define ERR_REPORT_ALWAYS (2)
521#define ERR_REPORT_FATAL (3)
522
523#define ERR_DEFAULT_REPORT_MODE ERR_REPORT_ONCE
524
525
526/*** Various global functions ***/
527
528zchar translate_from_zscii (zbyte);
529zbyte translate_to_zscii (zchar);
530
531void flush_buffer (void);
532void new_line (void);
533void print_char (zchar);
534void print_num (zword);
535void print_object (zword);
536void print_string (const char *);
537
538void stream_mssg_on (void);
539void stream_mssg_off (void);
540
541void ret (zword);
542void store (zword);
543void branch (bool);
544
545void storeb (zword, zbyte);
546void storew (zword, zword);
547
548/*** Interface functions ***/
549
550void os_beep (int);
551int os_char_width (zchar);
552void os_display_char (zchar);
553void os_display_string (const zchar *);
554void os_draw_picture (int, int, int);
555void os_erase_area (int, int, int, int);
556void os_fatal (const char *) __attribute__((noreturn));
557void os_finish_with_sample (int);
558int os_font_data (int, int *, int *);
559void os_init_screen (void);
560void os_more_prompt (void);
561int os_peek_colour (void);
562bool os_picture_data (int, int *, int *);
563void os_prepare_sample (int);
564void os_process_arguments (int, char *[]);
565int os_random_seed (void);
566int os_read_file_name (char *, const char *, int);
567zchar os_read_key (int, bool);
568zchar os_read_line (int, zchar *, int, int, int);
569zchar os_read_mouse (void);
570void os_reset_screen (void);
571void os_restart_game (int);
572void os_scroll_area (int, int, int, int, int);
573void os_set_colour (int, int);
574void os_set_cursor (int, int);
575void os_set_font (int);
576void os_set_text_style (int);
577void os_start_sample (int, int, int, zword);
578void os_stop_sample (int);
579int os_string_width (const zchar *);
580void os_init_setup (void);
581int os_speech_output(const zchar *);
582
583#include "setup.h"
diff --git a/apps/plugins/frotz/frotz.make b/apps/plugins/frotz/frotz.make
new file mode 100644
index 0000000000..0bf4370cca
--- /dev/null
+++ b/apps/plugins/frotz/frotz.make
@@ -0,0 +1,21 @@
1# __________ __ ___.
2# Open \______ \ ____ ____ | | _\_ |__ _______ ___
3# Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
4# Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
5# Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
6# \/ \/ \/ \/ \/
7# $Id$
8#
9
10FROTZSRCDIR := $(APPSDIR)/plugins/frotz
11FROTZBUILDDIR := $(BUILDDIR)/apps/plugins/frotz
12
13ROCKS += $(FROTZBUILDDIR)/frotz.rock
14
15FROTZ_SRC := $(call preprocess, $(FROTZSRCDIR)/SOURCES)
16FROTZ_OBJ := $(call c2obj, $(FROTZ_SRC))
17
18# add source files to OTHER_SRC to get automatic dependencies
19OTHER_SRC += $(FROTZ_SRC)
20
21$(FROTZBUILDDIR)/frotz.rock: $(FROTZ_OBJ)
diff --git a/apps/plugins/frotz/frotzplugin.h b/apps/plugins/frotz/frotzplugin.h
new file mode 100644
index 0000000000..8caddb470d
--- /dev/null
+++ b/apps/plugins/frotz/frotzplugin.h
@@ -0,0 +1,56 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2009 Torne Wuff
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
16 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ****************************************************************************/
21#ifndef _FROTZPLUGIN_H_
22#define _FROTZPLUGIN_H_
23
24#include "plugin.h"
25
26/*
27 * pretend stdio.h is implemented. references to FILE * still have to be
28 * changed to int, and references to NULL into -1, but there are less of those
29 */
30#define fread(ptr, size, nmemb, stream) rb->read(stream, ptr, size*nmemb)
31#define fwrite(ptr, size, nmemb, stream) rb->write(stream, ptr, size*nmemb)
32#define fclose(stream) rb->close(stream)
33#define fseek(stream, offset, whence) rb->lseek(stream, offset, whence)
34#define ftell(stream) rb->lseek(stream, 0, SEEK_CUR)
35#define ferror(stream) 0
36
37/*
38 * we need functions for character io
39 */
40extern int ungetc(int c, int f);
41extern int fgetc(int f);
42extern int fputc(int c, int f);
43
44/*
45 * this is used instead of os_read_key for more prompts and the like
46 * since the menu can't be used there.
47 */
48extern void wait_for_key(void);
49
50/*
51 * wrappers
52 */
53#define strchr(s, c) rb->strchr(s, c)
54#define strcpy(dest, src) rb->strcpy(dest, src)
55
56#endif /* _FROTZPLUGIN_H_ */
diff --git a/apps/plugins/frotz/hotkey.c b/apps/plugins/frotz/hotkey.c
new file mode 100644
index 0000000000..d214f1f322
--- /dev/null
+++ b/apps/plugins/frotz/hotkey.c
@@ -0,0 +1,221 @@
1/* hotkey.c - Hot key functions
2 * Copyright (c) 1995-1997 Stefan Jokisch
3 *
4 * Changes for Rockbox copyright 2009 Torne Wuff
5 *
6 * This file is part of Frotz.
7 *
8 * Frotz is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * Frotz is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
21 */
22
23#include "frotz.h"
24#include "lib/pluginlib_exit.h"
25
26extern int restore_undo (void);
27
28extern int read_number (void);
29
30extern bool read_yes_or_no (const char *);
31
32extern void replay_open (void);
33extern void replay_close (void);
34extern void record_open (void);
35extern void record_close (void);
36
37extern void seed_random (int);
38
39/*
40 * hot_key_debugging
41 *
42 * ...allows user to toggle cheating options on/off.
43 *
44 */
45
46static bool hot_key_debugging (void)
47{
48
49 f_setup.attribute_assignment = read_yes_or_no ("Watch attribute assignment");
50 f_setup.attribute_testing = read_yes_or_no ("Watch attribute testing");
51
52 f_setup.object_movement = read_yes_or_no ("Watch object movement");
53 f_setup.object_locating = read_yes_or_no ("Watch object locating");
54
55 return FALSE;
56
57}/* hot_key_debugging */
58
59/*
60 * hot_key_playback
61 *
62 * ...allows user to turn playback on.
63 *
64 */
65
66static bool hot_key_playback (void)
67{
68
69 rb->splash(HZ, "Playback on");
70
71 if (!istream_replay)
72 replay_open ();
73
74 return FALSE;
75
76}/* hot_key_playback */
77
78/*
79 * hot_key_recording
80 *
81 * ...allows user to turn recording on/off.
82 *
83 */
84
85static bool hot_key_recording (void)
86{
87
88 if (istream_replay) {
89 rb->splash(HZ, "Playback off");
90 replay_close ();
91 } else if (ostream_record) {
92 rb->splash(HZ, "Recording off");
93 record_close ();
94 } else {
95 rb->splash(HZ, "Recording on");
96 record_open ();
97 }
98
99 return FALSE;
100
101}/* hot_key_recording */
102
103/*
104 * hot_key_seed
105 *
106 * ...allows user to seed the random number seed.
107 *
108 */
109
110static bool hot_key_seed (void)
111{
112
113 print_string ("Enter seed value (or return to randomize): ");
114 seed_random (read_number ());
115
116 return FALSE;
117
118}/* hot_key_seed */
119
120/*
121 * hot_key_undo
122 *
123 * ...allows user to undo the previous turn.
124 *
125 */
126
127static bool hot_key_undo (void)
128{
129
130 if (restore_undo ()) {
131
132 print_string ("undo\n");
133
134 if (h_version >= V5) { /* for V5+ games we must */
135 store (2); /* store 2 (for success) */
136 return TRUE; /* and abort the input */
137 }
138
139 if (h_version <= V3) { /* for V3- games we must */
140 z_show_status (); /* draw the status line */
141 return FALSE; /* and continue input */
142 }
143
144 } else rb->splash(HZ, "No undo information available.");
145
146 return FALSE;
147
148}/* hot_key_undo */
149
150/*
151 * hot_key_restart
152 *
153 * ...allows user to start a new game.
154 *
155 */
156
157static bool hot_key_restart (void)
158{
159
160 if (read_yes_or_no ("Do you wish to restart")) {
161
162 z_restart ();
163 return TRUE;
164
165 } else return FALSE;
166
167}/* hot_key_restart */
168
169/*
170 * hot_key_quit
171 *
172 * ...allows user to exit the game.
173 *
174 */
175
176bool hot_key_quit (void)
177{
178
179 if (read_yes_or_no ("Do you wish to quit")) {
180
181 exit(0);
182
183 } else return FALSE;
184
185}/* hot_key_quit */
186
187/*
188 * handle_hot_key
189 *
190 * Perform the action associated with a so-called hot key. Return
191 * true to abort the current input action.
192 *
193 */
194
195bool handle_hot_key (zchar key)
196{
197
198 if (cwin == 0) {
199
200 bool aborting;
201
202 aborting = FALSE;
203
204 switch (key) {
205 case ZC_HKEY_RECORD: aborting = hot_key_recording (); break;
206 case ZC_HKEY_PLAYBACK: aborting = hot_key_playback (); break;
207 case ZC_HKEY_SEED: aborting = hot_key_seed (); break;
208 case ZC_HKEY_UNDO: aborting = hot_key_undo (); break;
209 case ZC_HKEY_RESTART: aborting = hot_key_restart (); break;
210 case ZC_HKEY_QUIT: aborting = hot_key_quit (); break;
211 case ZC_HKEY_DEBUG: aborting = hot_key_debugging (); break;
212 }
213
214 if (aborting)
215 return TRUE;
216
217 }
218
219 return FALSE;
220
221}/* handle_hot_key */
diff --git a/apps/plugins/frotz/input.c b/apps/plugins/frotz/input.c
new file mode 100644
index 0000000000..296bffc529
--- /dev/null
+++ b/apps/plugins/frotz/input.c
@@ -0,0 +1,301 @@
1/* input.c - High level input functions
2 * Copyright (c) 1995-1997 Stefan Jokisch
3 *
4 * Changes for Rockbox copyright 2009 Torne Wuff
5 *
6 * This file is part of Frotz.
7 *
8 * Frotz is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * Frotz is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
21 */
22
23#include "frotz.h"
24
25extern int save_undo (void);
26
27extern zchar stream_read_key (zword, zword, bool);
28extern zchar stream_read_input (int, zchar *, zword, zword, bool, bool);
29
30extern void tokenise_line (zword, zword, zword, bool);
31
32/*
33 * is_terminator
34 *
35 * Check if the given key is an input terminator.
36 *
37 */
38
39bool is_terminator (zchar key)
40{
41
42 if (key == ZC_TIME_OUT)
43 return TRUE;
44 if (key == ZC_RETURN)
45 return TRUE;
46 if (key >= ZC_HKEY_MIN && key <= ZC_HKEY_MAX)
47 return TRUE;
48
49 if (h_terminating_keys != 0)
50
51 if (key >= ZC_ARROW_MIN && key <= ZC_MENU_CLICK) {
52
53 zword addr = h_terminating_keys;
54 zbyte c;
55
56 do {
57 LOW_BYTE (addr, c)
58 if (c == 255 || key == translate_from_zscii (c))
59 return TRUE;
60 addr++;
61 } while (c != 0);
62
63 }
64
65 return FALSE;
66
67}/* is_terminator */
68
69/*
70 * z_make_menu, add or remove a menu and branch if successful.
71 *
72 * zargs[0] = number of menu
73 * zargs[1] = table of menu entries or 0 to remove menu
74 *
75 */
76
77void z_make_menu (void)
78{
79
80 /* This opcode was only used for the Macintosh version of Journey.
81 It controls menus with numbers greater than 2 (menus 0, 1 and 2
82 are system menus). Frotz doesn't implement menus yet. */
83
84 branch (FALSE);
85
86}/* z_make_menu */
87
88extern bool read_yes_or_no (const char *s);
89
90/*
91 * read_string
92 *
93 * Read a string from the current input stream.
94 *
95 */
96
97void read_string (int max, zchar *buffer)
98{
99 zchar key;
100
101 buffer[0] = 0;
102
103 do {
104
105 key = stream_read_input (max, buffer, 0, 0, FALSE, FALSE);
106
107 } while (key != ZC_RETURN);
108
109}/* read_string */
110
111/*
112 * read_number
113 *
114 * Ask the user to type in a number and return it.
115 *
116 */
117
118int read_number (void)
119{
120 zchar buffer[6];
121 int value = 0;
122 int i;
123
124 read_string (5, buffer);
125
126 for (i = 0; buffer[i] != 0; i++)
127 if (buffer[i] >= '0' && buffer[i] <= '9')
128 value = 10 * value + buffer[i] - '0';
129
130 return value;
131
132}/* read_number */
133
134/*
135 * z_read, read a line of input and (in V5+) store the terminating key.
136 *
137 * zargs[0] = address of text buffer
138 * zargs[1] = address of token buffer
139 * zargs[2] = timeout in tenths of a second (optional)
140 * zargs[3] = packed address of routine to be called on timeout
141 *
142 */
143
144void z_read (void)
145{
146 zchar buffer[INPUT_BUFFER_SIZE];
147 zword addr;
148 zchar key;
149 zbyte max, size;
150 zbyte c;
151 int i;
152
153 /* Supply default arguments */
154
155 if (zargc < 3)
156 zargs[2] = 0;
157
158 /* Get maximum input size */
159
160 addr = zargs[0];
161
162 LOW_BYTE (addr, max)
163
164 if (h_version <= V4)
165 max--;
166
167 if (max >= INPUT_BUFFER_SIZE)
168 max = INPUT_BUFFER_SIZE - 1;
169
170 /* Get initial input size */
171
172 if (h_version >= V5) {
173 addr++;
174 LOW_BYTE (addr, size)
175 } else size = 0;
176
177 /* Copy initial input to local buffer */
178
179 for (i = 0; i < size; i++) {
180 addr++;
181 LOW_BYTE (addr, c)
182 buffer[i] = translate_from_zscii (c);
183 }
184
185 buffer[i] = 0;
186
187 /* Draw status line for V1 to V3 games */
188
189 if (h_version <= V3)
190 z_show_status ();
191
192 /* Read input from current input stream */
193
194 key = stream_read_input (
195 max, buffer, /* buffer and size */
196 zargs[2], /* timeout value */
197 zargs[3], /* timeout routine */
198 TRUE, /* enable hot keys */
199 h_version == V6); /* no script in V6 */
200
201 if (key == ZC_BAD)
202 return;
203
204 /* Perform save_undo for V1 to V4 games */
205
206 if (h_version <= V4)
207 save_undo ();
208
209 /* Copy local buffer back to dynamic memory */
210
211 for (i = 0; buffer[i] != 0; i++) {
212
213 if (key == ZC_RETURN) {
214
215 if (buffer[i] >= 'A' && buffer[i] <= 'Z')
216 buffer[i] += 'a' - 'A';
217 if (buffer[i] >= 0xc0 && buffer[i] <= 0xde && buffer[i] != 0xd7)
218 buffer[i] += 0x20;
219
220 }
221
222 storeb ((zword) (zargs[0] + ((h_version <= V4) ? 1 : 2) + i), translate_to_zscii (buffer[i]));
223
224 }
225
226 /* Add null character (V1-V4) or write input length into 2nd byte */
227
228 if (h_version <= V4)
229 storeb ((zword) (zargs[0] + 1 + i), 0);
230 else
231 storeb ((zword) (zargs[0] + 1), i);
232
233 /* Tokenise line if a token buffer is present */
234
235 if (key == ZC_RETURN && zargs[1] != 0)
236 tokenise_line (zargs[0], zargs[1], 0, FALSE);
237
238 /* Store key */
239
240 if (h_version >= V5)
241 store (translate_to_zscii (key));
242
243}/* z_read */
244
245/*
246 * z_read_char, read and store a key.
247 *
248 * zargs[0] = input device (must be 1)
249 * zargs[1] = timeout in tenths of a second (optional)
250 * zargs[2] = packed address of routine to be called on timeout
251 *
252 */
253
254void z_read_char (void)
255{
256 zchar key;
257
258 /* Supply default arguments */
259
260 if (zargc < 2)
261 zargs[1] = 0;
262
263 /* Read input from the current input stream */
264
265 key = stream_read_key (
266 zargs[1], /* timeout value */
267 zargs[2], /* timeout routine */
268 TRUE); /* enable hot keys */
269
270 if (key == ZC_BAD)
271 return;
272
273 /* Store key */
274
275 store (translate_to_zscii (key));
276
277}/* z_read_char */
278
279/*
280 * z_read_mouse, write the current mouse status into a table.
281 *
282 * zargs[0] = address of table
283 *
284 */
285
286void z_read_mouse (void)
287{
288 zword btn;
289
290 /* Read the mouse position and which buttons are down */
291
292 btn = os_read_mouse ();
293 hx_mouse_y = mouse_y;
294 hx_mouse_x = mouse_x;
295
296 storew ((zword) (zargs[0] + 0), hx_mouse_y);
297 storew ((zword) (zargs[0] + 2), hx_mouse_x);
298 storew ((zword) (zargs[0] + 4), btn); /* mouse button bits */
299 storew ((zword) (zargs[0] + 6), 0); /* menu selection */
300
301}/* z_read_mouse */
diff --git a/apps/plugins/frotz/main.c b/apps/plugins/frotz/main.c
new file mode 100644
index 0000000000..22c021bfd1
--- /dev/null
+++ b/apps/plugins/frotz/main.c
@@ -0,0 +1,205 @@
1/* main.c - Frotz V2.40 main function
2 * Copyright (c) 1995-1997 Stefan Jokisch
3 *
4 * Changes for Rockbox copyright 2009 Torne Wuff
5 *
6 * This file is part of Frotz.
7 *
8 * Frotz is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * Frotz is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
21 */
22
23/*
24 * This is an interpreter for Infocom V1 to V6 games. It also supports
25 * the recently defined V7 and V8 games. Please report bugs to
26 *
27 * s.jokisch@avu.de
28 *
29 */
30
31#include "frotz.h"
32
33#ifndef MSDOS_16BIT
34#define cdecl
35#endif
36
37extern void interpret (void);
38extern void init_memory (void);
39extern void init_undo (void);
40extern void reset_memory (void);
41extern void init_buffer (void);
42extern void init_sound (void);
43extern void init_process (void);
44extern void script_close (void);
45extern void record_close (void);
46extern void replay_close (void);
47
48/* Story file name, id number and size */
49
50const char *story_name = 0;
51
52enum story story_id = UNKNOWN;
53long story_size = 0;
54
55/* Story file header data */
56
57zbyte h_version = 0;
58zbyte h_config = 0;
59zword h_release = 0;
60zword h_resident_size = 0;
61zword h_start_pc = 0;
62zword h_dictionary = 0;
63zword h_objects = 0;
64zword h_globals = 0;
65zword h_dynamic_size = 0;
66zword h_flags = 0;
67zbyte h_serial[6] = { 0, 0, 0, 0, 0, 0 };
68zword h_abbreviations = 0;
69zword h_file_size = 0;
70zword h_checksum = 0;
71zbyte h_interpreter_number = 0;
72zbyte h_interpreter_version = 0;
73zbyte h_screen_rows = 0;
74zbyte h_screen_cols = 0;
75zword h_screen_width = 0;
76zword h_screen_height = 0;
77zbyte h_font_height = 1;
78zbyte h_font_width = 1;
79zword h_functions_offset = 0;
80zword h_strings_offset = 0;
81zbyte h_default_background = 0;
82zbyte h_default_foreground = 0;
83zword h_terminating_keys = 0;
84zword h_line_width = 0;
85zbyte h_standard_high = 1;
86zbyte h_standard_low = 0;
87zword h_alphabet = 0;
88zword h_extension_table = 0;
89zbyte h_user_name[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
90
91zword hx_table_size = 0;
92zword hx_mouse_x = 0;
93zword hx_mouse_y = 0;
94zword hx_unicode_table = 0;
95
96/* Stack data */
97
98zword stack[STACK_SIZE];
99zword *sp = 0;
100zword *fp = 0;
101zword frame_count = 0;
102
103/* IO streams */
104
105bool ostream_screen = TRUE;
106bool ostream_script = FALSE;
107bool ostream_memory = FALSE;
108bool ostream_record = FALSE;
109bool istream_replay = FALSE;
110bool message = FALSE;
111
112/* Current window and mouse data */
113
114int cwin = 0;
115int mwin = 0;
116
117int mouse_y = 0;
118int mouse_x = 0;
119
120/* Window attributes */
121
122bool enable_wrapping = FALSE;
123bool enable_scripting = FALSE;
124bool enable_scrolling = FALSE;
125bool enable_buffering = FALSE;
126
127/* User options */
128
129/*
130int option_attribute_assignment = 0;
131int option_attribute_testing = 0;
132int option_context_lines = 0;
133int option_object_locating = 0;
134int option_object_movement = 0;
135int option_left_margin = 0;
136int option_right_margin = 0;
137int option_ignore_errors = 0;
138int option_piracy = 0;
139int option_undo_slots = MAX_UNDO_SLOTS;
140int option_expand_abbreviations = 0;
141int option_script_cols = 80;
142int option_save_quetzal = 1;
143*/
144
145int option_sound = 1;
146char *option_zcode_path;
147
148
149/*
150 * z_piracy, branch if the story file is a legal copy.
151 *
152 * no zargs used
153 *
154 */
155
156void z_piracy (void)
157{
158
159 branch (!f_setup.piracy);
160
161}/* z_piracy */
162
163/*
164 * main
165 *
166 * Prepare and run the game.
167 *
168 */
169
170int cdecl frotz_main (void)
171{
172
173 os_init_setup ();
174
175 init_buffer ();
176
177 init_err ();
178
179 init_memory ();
180
181 init_process ();
182
183 init_sound ();
184
185 os_init_screen ();
186
187 init_undo ();
188
189 z_restart ();
190
191 interpret ();
192
193 script_close ();
194
195 record_close ();
196
197 replay_close ();
198
199 reset_memory ();
200
201 os_reset_screen ();
202
203 return 0;
204
205}/* main */
diff --git a/apps/plugins/frotz/math.c b/apps/plugins/frotz/math.c
new file mode 100644
index 0000000000..5ff5163230
--- /dev/null
+++ b/apps/plugins/frotz/math.c
@@ -0,0 +1,261 @@
1/* math.c - Arithmetic, compare and logical opcodes
2 * Copyright (c) 1995-1997 Stefan Jokisch
3 *
4 * This file is part of Frotz.
5 *
6 * Frotz is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * Frotz is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
19 */
20
21#include "frotz.h"
22
23/*
24 * z_add, 16bit addition.
25 *
26 * zargs[0] = first value
27 * zargs[1] = second value
28 *
29 */
30
31void z_add (void)
32{
33
34 store ((zword) ((short) zargs[0] + (short) zargs[1]));
35
36}/* z_add */
37
38/*
39 * z_and, bitwise AND operation.
40 *
41 * zargs[0] = first value
42 * zargs[1] = second value
43 *
44 */
45
46void z_and (void)
47{
48
49 store ((zword) (zargs[0] & zargs[1]));
50
51}/* z_and */
52
53/*
54 * z_art_shift, arithmetic SHIFT operation.
55 *
56 * zargs[0] = value
57 * zargs[1] = #positions to shift left (positive) or right
58 *
59 */
60
61void z_art_shift (void)
62{
63
64 if ((short) zargs[1] > 0)
65 store ((zword) ((short) zargs[0] << (short) zargs[1]));
66 else
67 store ((zword) ((short) zargs[0] >> - (short) zargs[1]));
68
69}/* z_art_shift */
70
71/*
72 * z_div, signed 16bit division.
73 *
74 * zargs[0] = first value
75 * zargs[1] = second value
76 *
77 */
78
79void z_div (void)
80{
81
82 if (zargs[1] == 0)
83 runtime_error (ERR_DIV_ZERO);
84
85 store ((zword) ((short) zargs[0] / (short) zargs[1]));
86
87}/* z_div */
88
89/*
90 * z_je, branch if the first value equals any of the following.
91 *
92 * zargs[0] = first value
93 * zargs[1] = second value (optional)
94 * ...
95 * zargs[3] = fourth value (optional)
96 *
97 */
98
99void z_je (void)
100{
101
102 branch (
103 zargc > 1 && (zargs[0] == zargs[1] || (
104 zargc > 2 && (zargs[0] == zargs[2] || (
105 zargc > 3 && (zargs[0] == zargs[3]))))));
106
107}/* z_je */
108
109/*
110 * z_jg, branch if the first value is greater than the second.
111 *
112 * zargs[0] = first value
113 * zargs[1] = second value
114 *
115 */
116
117void z_jg (void)
118{
119
120 branch ((short) zargs[0] > (short) zargs[1]);
121
122}/* z_jg */
123
124/*
125 * z_jl, branch if the first value is less than the second.
126 *
127 * zargs[0] = first value
128 * zargs[1] = second value
129 *
130 */
131
132void z_jl (void)
133{
134
135 branch ((short) zargs[0] < (short) zargs[1]);
136
137}/* z_jl */
138
139/*
140 * z_jz, branch if value is zero.
141 *
142 * zargs[0] = value
143 *
144 */
145
146void z_jz (void)
147{
148
149 branch ((short) zargs[0] == 0);
150
151}/* z_jz */
152
153/*
154 * z_log_shift, logical SHIFT operation.
155 *
156 * zargs[0] = value
157 * zargs[1] = #positions to shift left (positive) or right (negative)
158 *
159 */
160
161void z_log_shift (void)
162{
163
164 if ((short) zargs[1] > 0)
165 store ((zword) (zargs[0] << (short) zargs[1]));
166 else
167 store ((zword) (zargs[0] >> - (short) zargs[1]));
168
169}/* z_log_shift */
170
171/*
172 * z_mod, remainder after signed 16bit division.
173 *
174 * zargs[0] = first value
175 * zargs[1] = second value
176 *
177 */
178
179void z_mod (void)
180{
181
182 if (zargs[1] == 0)
183 runtime_error (ERR_DIV_ZERO);
184
185 store ((zword) ((short) zargs[0] % (short) zargs[1]));
186
187}/* z_mod */
188
189/*
190 * z_mul, 16bit multiplication.
191 *
192 * zargs[0] = first value
193 * zargs[1] = second value
194 *
195 */
196
197void z_mul (void)
198{
199
200 store ((zword) ((short) zargs[0] * (short) zargs[1]));
201
202}/* z_mul */
203
204/*
205 * z_not, bitwise NOT operation.
206 *
207 * zargs[0] = value
208 *
209 */
210
211void z_not (void)
212{
213
214 store ((zword) ~zargs[0]);
215
216}/* z_not */
217
218/*
219 * z_or, bitwise OR operation.
220 *
221 * zargs[0] = first value
222 * zargs[1] = second value
223 *
224 */
225
226void z_or (void)
227{
228
229 store ((zword) (zargs[0] | zargs[1]));
230
231}/* z_or */
232
233/*
234 * z_sub, 16bit substraction.
235 *
236 * zargs[0] = first value
237 * zargs[1] = second value
238 *
239 */
240
241void z_sub (void)
242{
243
244 store ((zword) ((short) zargs[0] - (short) zargs[1]));
245
246}/* z_sub */
247
248/*
249 * z_test, branch if all the flags of a bit mask are set in a value.
250 *
251 * zargs[0] = value to be examined
252 * zargs[1] = bit mask
253 *
254 */
255
256void z_test (void)
257{
258
259 branch ((zargs[0] & zargs[1]) == zargs[1]);
260
261}/* z_test */
diff --git a/apps/plugins/frotz/object.c b/apps/plugins/frotz/object.c
new file mode 100644
index 0000000000..676b6c93c5
--- /dev/null
+++ b/apps/plugins/frotz/object.c
@@ -0,0 +1,1003 @@
1/* object.c - Object manipulation opcodes
2 * Copyright (c) 1995-1997 Stefan Jokisch
3 *
4 * This file is part of Frotz.
5 *
6 * Frotz is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * Frotz is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
19 */
20
21#include "frotz.h"
22
23#define MAX_OBJECT 2000
24
25#define O1_PARENT 4
26#define O1_SIBLING 5
27#define O1_CHILD 6
28#define O1_PROPERTY_OFFSET 7
29#define O1_SIZE 9
30
31#define O4_PARENT 6
32#define O4_SIBLING 8
33#define O4_CHILD 10
34#define O4_PROPERTY_OFFSET 12
35#define O4_SIZE 14
36
37/*
38 * object_address
39 *
40 * Calculate the address of an object.
41 *
42 */
43
44static zword object_address (zword obj)
45{
46/* zchar obj_num[10]; */
47
48 /* Check object number */
49
50 if (obj > ((h_version <= V3) ? 255 : MAX_OBJECT)) {
51 print_string("@Attempt to address illegal object ");
52 print_num(obj);
53 print_string(". This is normally fatal.");
54 new_line();
55 runtime_error (ERR_ILL_OBJ);
56 }
57
58 /* Return object address */
59
60 if (h_version <= V3)
61 return h_objects + ((obj - 1) * O1_SIZE + 62);
62 else
63 return h_objects + ((obj - 1) * O4_SIZE + 126);
64
65}/* object_address */
66
67/*
68 * object_name
69 *
70 * Return the address of the given object's name.
71 *
72 */
73
74zword object_name (zword object)
75{
76 zword obj_addr;
77 zword name_addr;
78
79 obj_addr = object_address (object);
80
81 /* The object name address is found at the start of the properties */
82
83 if (h_version <= V3)
84 obj_addr += O1_PROPERTY_OFFSET;
85 else
86 obj_addr += O4_PROPERTY_OFFSET;
87
88 LOW_WORD (obj_addr, name_addr)
89
90 return name_addr;
91
92}/* object_name */
93
94/*
95 * first_property
96 *
97 * Calculate the start address of the property list associated with
98 * an object.
99 *
100 */
101
102static zword first_property (zword obj)
103{
104 zword prop_addr;
105 zbyte size;
106
107 /* Fetch address of object name */
108
109 prop_addr = object_name (obj);
110
111 /* Get length of object name */
112
113 LOW_BYTE (prop_addr, size)
114
115 /* Add name length to pointer */
116
117 return prop_addr + 1 + 2 * size;
118
119}/* first_property */
120
121/*
122 * next_property
123 *
124 * Calculate the address of the next property in a property list.
125 *
126 */
127
128static zword next_property (zword prop_addr)
129{
130 zbyte value;
131
132 /* Load the current property id */
133
134 LOW_BYTE (prop_addr, value)
135 prop_addr++;
136
137 /* Calculate the length of this property */
138
139 if (h_version <= V3)
140 value >>= 5;
141 else if (!(value & 0x80))
142 value >>= 6;
143 else {
144
145 LOW_BYTE (prop_addr, value)
146 value &= 0x3f;
147
148 if (value == 0) value = 64; /* demanded by Spec 1.0 */
149
150 }
151
152 /* Add property length to current property pointer */
153
154 return prop_addr + value + 1;
155
156}/* next_property */
157
158/*
159 * unlink_object
160 *
161 * Unlink an object from its parent and siblings.
162 *
163 */
164
165static void unlink_object (zword object)
166{
167 zword obj_addr;
168 zword parent_addr;
169 zword sibling_addr;
170
171 if (object == 0) {
172 runtime_error (ERR_REMOVE_OBJECT_0);
173 return;
174 }
175
176 obj_addr = object_address (object);
177
178 if (h_version <= V3) {
179
180 zbyte parent;
181 zbyte younger_sibling;
182 zbyte older_sibling;
183 zbyte zero = 0;
184
185 /* Get parent of object, and return if no parent */
186
187 obj_addr += O1_PARENT;
188 LOW_BYTE (obj_addr, parent)
189 if (!parent)
190 return;
191
192 /* Get (older) sibling of object and set both parent and sibling
193 pointers to 0 */
194
195 SET_BYTE (obj_addr, zero)
196 obj_addr += O1_SIBLING - O1_PARENT;
197 LOW_BYTE (obj_addr, older_sibling)
198 SET_BYTE (obj_addr, zero)
199
200 /* Get first child of parent (the youngest sibling of the object) */
201
202 parent_addr = object_address (parent) + O1_CHILD;
203 LOW_BYTE (parent_addr, younger_sibling)
204
205 /* Remove object from the list of siblings */
206
207 if (younger_sibling == object)
208 SET_BYTE (parent_addr, older_sibling)
209 else {
210 do {
211 sibling_addr = object_address (younger_sibling) + O1_SIBLING;
212 LOW_BYTE (sibling_addr, younger_sibling)
213 } while (younger_sibling != object);
214 SET_BYTE (sibling_addr, older_sibling)
215 }
216
217 } else {
218
219 zword parent;
220 zword younger_sibling;
221 zword older_sibling;
222 zword zero = 0;
223
224 /* Get parent of object, and return if no parent */
225
226 obj_addr += O4_PARENT;
227 LOW_WORD (obj_addr, parent)
228 if (!parent)
229 return;
230
231 /* Get (older) sibling of object and set both parent and sibling
232 pointers to 0 */
233
234 SET_WORD (obj_addr, zero)
235 obj_addr += O4_SIBLING - O4_PARENT;
236 LOW_WORD (obj_addr, older_sibling)
237 SET_WORD (obj_addr, zero)
238
239 /* Get first child of parent (the youngest sibling of the object) */
240
241 parent_addr = object_address (parent) + O4_CHILD;
242 LOW_WORD (parent_addr, younger_sibling)
243
244 /* Remove object from the list of siblings */
245
246 if (younger_sibling == object)
247 SET_WORD (parent_addr, older_sibling)
248 else {
249 do {
250 sibling_addr = object_address (younger_sibling) + O4_SIBLING;
251 LOW_WORD (sibling_addr, younger_sibling)
252 } while (younger_sibling != object);
253 SET_WORD (sibling_addr, older_sibling)
254 }
255
256 }
257
258}/* unlink_object */
259
260/*
261 * z_clear_attr, clear an object attribute.
262 *
263 * zargs[0] = object
264 * zargs[1] = number of attribute to be cleared
265 *
266 */
267
268void z_clear_attr (void)
269{
270 zword obj_addr;
271 zbyte value;
272
273 if (story_id == SHERLOCK)
274 if (zargs[1] == 48)
275 return;
276
277 if (zargs[1] > ((h_version <= V3) ? 31 : 47))
278 runtime_error (ERR_ILL_ATTR);
279
280 /* If we are monitoring attribute assignment display a short note */
281
282 if (f_setup.attribute_assignment) {
283 stream_mssg_on ();
284 print_string ("@clear_attr ");
285 print_object (zargs[0]);
286 print_string (" ");
287 print_num (zargs[1]);
288 stream_mssg_off ();
289 }
290
291 if (zargs[0] == 0) {
292 runtime_error (ERR_CLEAR_ATTR_0);
293 return;
294 }
295
296 /* Get attribute address */
297
298 obj_addr = object_address (zargs[0]) + zargs[1] / 8;
299
300 /* Clear attribute bit */
301
302 LOW_BYTE (obj_addr, value)
303 value &= ~(0x80 >> (zargs[1] & 7));
304 SET_BYTE (obj_addr, value)
305
306}/* z_clear_attr */
307
308/*
309 * z_jin, branch if the first object is inside the second.
310 *
311 * zargs[0] = first object
312 * zargs[1] = second object
313 *
314 */
315
316void z_jin (void)
317{
318 zword obj_addr;
319
320 /* If we are monitoring object locating display a short note */
321
322 if (f_setup.object_locating) {
323 stream_mssg_on ();
324 print_string ("@jin ");
325 print_object (zargs[0]);
326 print_string (" ");
327 print_object (zargs[1]);
328 stream_mssg_off ();
329 }
330
331 if (zargs[0] == 0) {
332 runtime_error (ERR_JIN_0);
333 branch (0 == zargs[1]);
334 return;
335 }
336
337 obj_addr = object_address (zargs[0]);
338
339 if (h_version <= V3) {
340
341 zbyte parent;
342
343 /* Get parent id from object */
344
345 obj_addr += O1_PARENT;
346 LOW_BYTE (obj_addr, parent)
347
348 /* Branch if the parent is obj2 */
349
350 branch (parent == zargs[1]);
351
352 } else {
353
354 zword parent;
355
356 /* Get parent id from object */
357
358 obj_addr += O4_PARENT;
359 LOW_WORD (obj_addr, parent)
360
361 /* Branch if the parent is obj2 */
362
363 branch (parent == zargs[1]);
364
365 }
366
367}/* z_jin */
368
369/*
370 * z_get_child, store the child of an object.
371 *
372 * zargs[0] = object
373 *
374 */
375
376void z_get_child (void)
377{
378 zword obj_addr;
379
380 /* If we are monitoring object locating display a short note */
381
382 if (f_setup.object_locating) {
383 stream_mssg_on ();
384 print_string ("@get_child ");
385 print_object (zargs[0]);
386 stream_mssg_off ();
387 }
388
389 if (zargs[0] == 0) {
390 runtime_error (ERR_GET_CHILD_0);
391 store (0);
392 branch (FALSE);
393 return;
394 }
395
396 obj_addr = object_address (zargs[0]);
397
398 if (h_version <= V3) {
399
400 zbyte child;
401
402 /* Get child id from object */
403
404 obj_addr += O1_CHILD;
405 LOW_BYTE (obj_addr, child)
406
407 /* Store child id and branch */
408
409 store (child);
410 branch (child);
411
412 } else {
413
414 zword child;
415
416 /* Get child id from object */
417
418 obj_addr += O4_CHILD;
419 LOW_WORD (obj_addr, child)
420
421 /* Store child id and branch */
422
423 store (child);
424 branch (child);
425
426 }
427
428}/* z_get_child */
429
430/*
431 * z_get_next_prop, store the number of the first or next property.
432 *
433 * zargs[0] = object
434 * zargs[1] = address of current property (0 gets the first property)
435 *
436 */
437
438void z_get_next_prop (void)
439{
440 zword prop_addr;
441 zbyte value;
442 zbyte mask;
443
444 if (zargs[0] == 0) {
445 runtime_error (ERR_GET_NEXT_PROP_0);
446 store (0);
447 return;
448 }
449
450 /* Property id is in bottom five (six) bits */
451
452 mask = (h_version <= V3) ? 0x1f : 0x3f;
453
454 /* Load address of first property */
455
456 prop_addr = first_property (zargs[0]);
457
458 if (zargs[1] != 0) {
459
460 /* Scan down the property list */
461
462 do {
463 LOW_BYTE (prop_addr, value)
464 prop_addr = next_property (prop_addr);
465 } while ((value & mask) > zargs[1]);
466
467 /* Exit if the property does not exist */
468
469 if ((value & mask) != zargs[1])
470 runtime_error (ERR_NO_PROP);
471
472 }
473
474 /* Return the property id */
475
476 LOW_BYTE (prop_addr, value)
477 store ((zword) (value & mask));
478
479}/* z_get_next_prop */
480
481/*
482 * z_get_parent, store the parent of an object.
483 *
484 * zargs[0] = object
485 *
486 */
487
488void z_get_parent (void)
489{
490 zword obj_addr;
491
492 /* If we are monitoring object locating display a short note */
493
494 if (f_setup.object_locating) {
495 stream_mssg_on ();
496 print_string ("@get_parent ");
497 print_object (zargs[0]);
498 stream_mssg_off ();
499 }
500
501 if (zargs[0] == 0) {
502 runtime_error (ERR_GET_PARENT_0);
503 store (0);
504 return;
505 }
506
507 obj_addr = object_address (zargs[0]);
508
509 if (h_version <= V3) {
510
511 zbyte parent;
512
513 /* Get parent id from object */
514
515 obj_addr += O1_PARENT;
516 LOW_BYTE (obj_addr, parent)
517
518 /* Store parent */
519
520 store (parent);
521
522 } else {
523
524 zword parent;
525
526 /* Get parent id from object */
527
528 obj_addr += O4_PARENT;
529 LOW_WORD (obj_addr, parent)
530
531 /* Store parent */
532
533 store (parent);
534
535 }
536
537}/* z_get_parent */
538
539/*
540 * z_get_prop, store the value of an object property.
541 *
542 * zargs[0] = object
543 * zargs[1] = number of property to be examined
544 *
545 */
546
547void z_get_prop (void)
548{
549 zword prop_addr;
550 zword wprop_val;
551 zbyte bprop_val;
552 zbyte value;
553 zbyte mask;
554
555 if (zargs[0] == 0) {
556 runtime_error (ERR_GET_PROP_0);
557 store (0);
558 return;
559 }
560
561 /* Property id is in bottom five (six) bits */
562
563 mask = (h_version <= V3) ? 0x1f : 0x3f;
564
565 /* Load address of first property */
566
567 prop_addr = first_property (zargs[0]);
568
569 /* Scan down the property list */
570
571 for (;;) {
572 LOW_BYTE (prop_addr, value)
573 if ((value & mask) <= zargs[1])
574 break;
575 prop_addr = next_property (prop_addr);
576 }
577
578 if ((value & mask) == zargs[1]) { /* property found */
579
580 /* Load property (byte or word sized) */
581
582 prop_addr++;
583
584 if ((h_version <= V3 && !(value & 0xe0)) || (h_version >= V4 && !(value & 0xc0))) {
585
586 LOW_BYTE (prop_addr, bprop_val)
587 wprop_val = bprop_val;
588
589 } else LOW_WORD (prop_addr, wprop_val)
590
591 } else { /* property not found */
592
593 /* Load default value */
594
595 prop_addr = h_objects + 2 * (zargs[1] - 1);
596 LOW_WORD (prop_addr, wprop_val)
597
598 }
599
600 /* Store the property value */
601
602 store (wprop_val);
603
604}/* z_get_prop */
605
606/*
607 * z_get_prop_addr, store the address of an object property.
608 *
609 * zargs[0] = object
610 * zargs[1] = number of property to be examined
611 *
612 */
613
614void z_get_prop_addr (void)
615{
616 zword prop_addr;
617 zbyte value;
618 zbyte mask;
619
620 if (zargs[0] == 0) {
621 runtime_error (ERR_GET_PROP_ADDR_0);
622 store (0);
623 return;
624 }
625
626 if (story_id == BEYOND_ZORK)
627 if (zargs[0] > MAX_OBJECT)
628 { store (0); return; }
629
630 /* Property id is in bottom five (six) bits */
631
632 mask = (h_version <= V3) ? 0x1f : 0x3f;
633
634 /* Load address of first property */
635
636 prop_addr = first_property (zargs[0]);
637
638 /* Scan down the property list */
639
640 for (;;) {
641 LOW_BYTE (prop_addr, value)
642 if ((value & mask) <= zargs[1])
643 break;
644 prop_addr = next_property (prop_addr);
645 }
646
647 /* Calculate the property address or return zero */
648
649 if ((value & mask) == zargs[1]) {
650
651 if (h_version >= V4 && (value & 0x80))
652 prop_addr++;
653 store ((zword) (prop_addr + 1));
654
655 } else store (0);
656
657}/* z_get_prop_addr */
658
659/*
660 * z_get_prop_len, store the length of an object property.
661 *
662 * zargs[0] = address of property to be examined
663 *
664 */
665
666void z_get_prop_len (void)
667{
668 zword addr;
669 zbyte value;
670
671 /* Back up the property pointer to the property id */
672
673 addr = zargs[0] - 1;
674 LOW_BYTE (addr, value)
675
676 /* Calculate length of property */
677
678 if (h_version <= V3)
679 value = (value >> 5) + 1;
680 else if (!(value & 0x80))
681 value = (value >> 6) + 1;
682 else {
683
684 value &= 0x3f;
685
686 if (value == 0) value = 64; /* demanded by Spec 1.0 */
687
688 }
689
690 /* Store length of property */
691
692 store (value);
693
694}/* z_get_prop_len */
695
696/*
697 * z_get_sibling, store the sibling of an object.
698 *
699 * zargs[0] = object
700 *
701 */
702
703void z_get_sibling (void)
704{
705 zword obj_addr;
706
707 if (zargs[0] == 0) {
708 runtime_error (ERR_GET_SIBLING_0);
709 store (0);
710 branch (FALSE);
711 return;
712 }
713
714 obj_addr = object_address (zargs[0]);
715
716 if (h_version <= V3) {
717
718 zbyte sibling;
719
720 /* Get sibling id from object */
721
722 obj_addr += O1_SIBLING;
723 LOW_BYTE (obj_addr, sibling)
724
725 /* Store sibling and branch */
726
727 store (sibling);
728 branch (sibling);
729
730 } else {
731
732 zword sibling;
733
734 /* Get sibling id from object */
735
736 obj_addr += O4_SIBLING;
737 LOW_WORD (obj_addr, sibling)
738
739 /* Store sibling and branch */
740
741 store (sibling);
742 branch (sibling);
743
744 }
745
746}/* z_get_sibling */
747
748/*
749 * z_insert_obj, make an object the first child of another object.
750 *
751 * zargs[0] = object to be moved
752 * zargs[1] = destination object
753 *
754 */
755
756void z_insert_obj (void)
757{
758 zword obj1 = zargs[0];
759 zword obj2 = zargs[1];
760 zword obj1_addr;
761 zword obj2_addr;
762
763 /* If we are monitoring object movements display a short note */
764
765 if (f_setup.object_movement) {
766 stream_mssg_on ();
767 print_string ("@move_obj ");
768 print_object (obj1);
769 print_string (" ");
770 print_object (obj2);
771 stream_mssg_off ();
772 }
773
774 if (obj1 == 0) {
775 runtime_error (ERR_MOVE_OBJECT_0);
776 return;
777 }
778
779 if (obj2 == 0) {
780 runtime_error (ERR_MOVE_OBJECT_TO_0);
781 return;
782 }
783
784 /* Get addresses of both objects */
785
786 obj1_addr = object_address (obj1);
787 obj2_addr = object_address (obj2);
788
789 /* Remove object 1 from current parent */
790
791 unlink_object (obj1);
792
793 /* Make object 1 first child of object 2 */
794
795 if (h_version <= V3) {
796
797 zbyte child;
798
799 obj1_addr += O1_PARENT;
800 SET_BYTE (obj1_addr, obj2)
801 obj2_addr += O1_CHILD;
802 LOW_BYTE (obj2_addr, child)
803 SET_BYTE (obj2_addr, obj1)
804 obj1_addr += O1_SIBLING - O1_PARENT;
805 SET_BYTE (obj1_addr, child)
806
807 } else {
808
809 zword child;
810
811 obj1_addr += O4_PARENT;
812 SET_WORD (obj1_addr, obj2)
813 obj2_addr += O4_CHILD;
814 LOW_WORD (obj2_addr, child)
815 SET_WORD (obj2_addr, obj1)
816 obj1_addr += O4_SIBLING - O4_PARENT;
817 SET_WORD (obj1_addr, child)
818
819 }
820
821}/* z_insert_obj */
822
823/*
824 * z_put_prop, set the value of an object property.
825 *
826 * zargs[0] = object
827 * zargs[1] = number of property to set
828 * zargs[2] = value to set property to
829 *
830 */
831
832void z_put_prop (void)
833{
834 zword prop_addr;
835 zword value;
836 zbyte mask;
837
838 if (zargs[0] == 0) {
839 runtime_error (ERR_PUT_PROP_0);
840 return;
841 }
842
843 /* Property id is in bottom five or six bits */
844
845 mask = (h_version <= V3) ? 0x1f : 0x3f;
846
847 /* Load address of first property */
848
849 prop_addr = first_property (zargs[0]);
850
851 /* Scan down the property list */
852
853 for (;;) {
854 LOW_BYTE (prop_addr, value)
855 if ((value & mask) <= zargs[1])
856 break;
857 prop_addr = next_property (prop_addr);
858 }
859
860 /* Exit if the property does not exist */
861
862 if ((value & mask) != zargs[1])
863 runtime_error (ERR_NO_PROP);
864
865 /* Store the new property value (byte or word sized) */
866
867 prop_addr++;
868
869 if ((h_version <= V3 && !(value & 0xe0)) || (h_version >= V4 && !(value & 0xc0))) {
870 zbyte v = zargs[2];
871 SET_BYTE (prop_addr, v)
872 } else {
873 zword v = zargs[2];
874 SET_WORD (prop_addr, v)
875 }
876
877}/* z_put_prop */
878
879/*
880 * z_remove_obj, unlink an object from its parent and siblings.
881 *
882 * zargs[0] = object
883 *
884 */
885
886void z_remove_obj (void)
887{
888
889 /* If we are monitoring object movements display a short note */
890
891 if (f_setup.object_movement) {
892 stream_mssg_on ();
893 print_string ("@remove_obj ");
894 print_object (zargs[0]);
895 stream_mssg_off ();
896 }
897
898 /* Call unlink_object to do the job */
899
900 unlink_object (zargs[0]);
901
902}/* z_remove_obj */
903
904/*
905 * z_set_attr, set an object attribute.
906 *
907 * zargs[0] = object
908 * zargs[1] = number of attribute to set
909 *
910 */
911
912void z_set_attr (void)
913{
914 zword obj_addr;
915 zbyte value;
916
917 if (story_id == SHERLOCK)
918 if (zargs[1] == 48)
919 return;
920
921 if (zargs[1] > ((h_version <= V3) ? 31 : 47))
922 runtime_error (ERR_ILL_ATTR);
923
924 /* If we are monitoring attribute assignment display a short note */
925
926 if (f_setup.attribute_assignment) {
927 stream_mssg_on ();
928 print_string ("@set_attr ");
929 print_object (zargs[0]);
930 print_string (" ");
931 print_num (zargs[1]);
932 stream_mssg_off ();
933 }
934
935 if (zargs[0] == 0) {
936 runtime_error (ERR_SET_ATTR_0);
937 return;
938 }
939
940 /* Get attribute address */
941
942 obj_addr = object_address (zargs[0]) + zargs[1] / 8;
943
944 /* Load attribute byte */
945
946 LOW_BYTE (obj_addr, value)
947
948 /* Set attribute bit */
949
950 value |= 0x80 >> (zargs[1] & 7);
951
952 /* Store attribute byte */
953
954 SET_BYTE (obj_addr, value)
955
956}/* z_set_attr */
957
958/*
959 * z_test_attr, branch if an object attribute is set.
960 *
961 * zargs[0] = object
962 * zargs[1] = number of attribute to test
963 *
964 */
965
966void z_test_attr (void)
967{
968 zword obj_addr;
969 zbyte value;
970
971 if (zargs[1] > ((h_version <= V3) ? 31 : 47))
972 runtime_error (ERR_ILL_ATTR);
973
974 /* If we are monitoring attribute testing display a short note */
975
976 if (f_setup.attribute_testing) {
977 stream_mssg_on ();
978 print_string ("@test_attr ");
979 print_object (zargs[0]);
980 print_string (" ");
981 print_num (zargs[1]);
982 stream_mssg_off ();
983 }
984
985 if (zargs[0] == 0) {
986 runtime_error (ERR_TEST_ATTR_0);
987 branch (FALSE);
988 return;
989 }
990
991 /* Get attribute address */
992
993 obj_addr = object_address (zargs[0]) + zargs[1] / 8;
994
995 /* Load attribute byte */
996
997 LOW_BYTE (obj_addr, value)
998
999 /* Test attribute */
1000
1001 branch (value & (0x80 >> (zargs[1] & 7)));
1002
1003}/* z_test_attr */
diff --git a/apps/plugins/frotz/process.c b/apps/plugins/frotz/process.c
new file mode 100644
index 0000000000..99ad82ca85
--- /dev/null
+++ b/apps/plugins/frotz/process.c
@@ -0,0 +1,798 @@
1/* process.c - Interpreter loop and program control
2 * Copyright (c) 1995-1997 Stefan Jokisch
3 *
4 * This file is part of Frotz.
5 *
6 * Frotz is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * Frotz is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
19 */
20
21#include "frotz.h"
22
23#ifdef DJGPP
24#include "djfrotz.h"
25#endif
26
27
28zword zargs[8];
29int zargc;
30
31static int finished = 0;
32
33static void __extended__ (void);
34static void __illegal__ (void);
35
36void (*op0_opcodes[0x10]) (void) = {
37 z_rtrue,
38 z_rfalse,
39 z_print,
40 z_print_ret,
41 z_nop,
42 z_save,
43 z_restore,
44 z_restart,
45 z_ret_popped,
46 z_catch,
47 z_quit,
48 z_new_line,
49 z_show_status,
50 z_verify,
51 __extended__,
52 z_piracy
53};
54
55void (*op1_opcodes[0x10]) (void) = {
56 z_jz,
57 z_get_sibling,
58 z_get_child,
59 z_get_parent,
60 z_get_prop_len,
61 z_inc,
62 z_dec,
63 z_print_addr,
64 z_call_s,
65 z_remove_obj,
66 z_print_obj,
67 z_ret,
68 z_jump,
69 z_print_paddr,
70 z_load,
71 z_call_n
72};
73
74void (*var_opcodes[0x40]) (void) = {
75 __illegal__,
76 z_je,
77 z_jl,
78 z_jg,
79 z_dec_chk,
80 z_inc_chk,
81 z_jin,
82 z_test,
83 z_or,
84 z_and,
85 z_test_attr,
86 z_set_attr,
87 z_clear_attr,
88 z_store,
89 z_insert_obj,
90 z_loadw,
91 z_loadb,
92 z_get_prop,
93 z_get_prop_addr,
94 z_get_next_prop,
95 z_add,
96 z_sub,
97 z_mul,
98 z_div,
99 z_mod,
100 z_call_s,
101 z_call_n,
102 z_set_colour,
103 z_throw,
104 __illegal__,
105 __illegal__,
106 __illegal__,
107 z_call_s,
108 z_storew,
109 z_storeb,
110 z_put_prop,
111 z_read,
112 z_print_char,
113 z_print_num,
114 z_random,
115 z_push,
116 z_pull,
117 z_split_window,
118 z_set_window,
119 z_call_s,
120 z_erase_window,
121 z_erase_line,
122 z_set_cursor,
123 z_get_cursor,
124 z_set_text_style,
125 z_buffer_mode,
126 z_output_stream,
127 z_input_stream,
128 z_sound_effect,
129 z_read_char,
130 z_scan_table,
131 z_not,
132 z_call_n,
133 z_call_n,
134 z_tokenise,
135 z_encode_text,
136 z_copy_table,
137 z_print_table,
138 z_check_arg_count
139};
140
141void (*ext_opcodes[0x1d]) (void) = {
142 z_save,
143 z_restore,
144 z_log_shift,
145 z_art_shift,
146 z_set_font,
147 z_draw_picture,
148 z_picture_data,
149 z_erase_picture,
150 z_set_margins,
151 z_save_undo,
152 z_restore_undo,
153 z_print_unicode,
154 z_check_unicode,
155 __illegal__,
156 __illegal__,
157 __illegal__,
158 z_move_window,
159 z_window_size,
160 z_window_style,
161 z_get_wind_prop,
162 z_scroll_window,
163 z_pop_stack,
164 z_read_mouse,
165 z_mouse_window,
166 z_push_stack,
167 z_put_wind_prop,
168 z_print_form,
169 z_make_menu,
170 z_picture_table
171};
172
173
174/*
175 * init_process
176 *
177 * Initialize process variables.
178 *
179 */
180
181void init_process (void)
182{
183 finished = 0;
184} /* init_process */
185
186
187/*
188 * load_operand
189 *
190 * Load an operand, either a variable or a constant.
191 *
192 */
193
194static void load_operand (zbyte type)
195{
196 zword value;
197
198 if (type & 2) { /* variable */
199
200 zbyte variable;
201
202 CODE_BYTE (variable)
203
204 if (variable == 0)
205 value = *sp++;
206 else if (variable < 16)
207 value = *(fp - variable);
208 else {
209 zword addr = h_globals + 2 * (variable - 16);
210 LOW_WORD (addr, value)
211 }
212
213 } else if (type & 1) { /* small constant */
214
215 zbyte bvalue;
216
217 CODE_BYTE (bvalue)
218 value = bvalue;
219
220 } else CODE_WORD (value) /* large constant */
221
222 zargs[zargc++] = value;
223
224}/* load_operand */
225
226/*
227 * load_all_operands
228 *
229 * Given the operand specifier byte, load all (up to four) operands
230 * for a VAR or EXT opcode.
231 *
232 */
233
234static void load_all_operands (zbyte specifier)
235{
236 int i;
237
238 for (i = 6; i >= 0; i -= 2) {
239
240 zbyte type = (specifier >> i) & 0x03;
241
242 if (type == 3)
243 break;
244
245 load_operand (type);
246
247 }
248
249}/* load_all_operands */
250
251/*
252 * interpret
253 *
254 * Z-code interpreter main loop
255 *
256 */
257
258void interpret (void)
259{
260
261 do {
262
263 zbyte opcode;
264
265 CODE_BYTE (opcode)
266
267 zargc = 0;
268
269 if (opcode < 0x80) { /* 2OP opcodes */
270
271 load_operand ((zbyte) (opcode & 0x40) ? 2 : 1);
272 load_operand ((zbyte) (opcode & 0x20) ? 2 : 1);
273
274 var_opcodes[opcode & 0x1f] ();
275
276 } else if (opcode < 0xb0) { /* 1OP opcodes */
277
278 load_operand ((zbyte) (opcode >> 4));
279
280 op1_opcodes[opcode & 0x0f] ();
281
282 } else if (opcode < 0xc0) { /* 0OP opcodes */
283
284 op0_opcodes[opcode - 0xb0] ();
285
286 } else { /* VAR opcodes */
287
288 zbyte specifier1;
289 zbyte specifier2;
290
291 if (opcode == 0xec || opcode == 0xfa) { /* opcodes 0xec */
292 CODE_BYTE (specifier1) /* and 0xfa are */
293 CODE_BYTE (specifier2) /* call opcodes */
294 load_all_operands (specifier1); /* with up to 8 */
295 load_all_operands (specifier2); /* arguments */
296 } else {
297 CODE_BYTE (specifier1)
298 load_all_operands (specifier1);
299 }
300
301 var_opcodes[opcode - 0xc0] ();
302
303 }
304
305#if defined(DJGPP) && defined(SOUND_SUPPORT)
306 if (end_of_sound_flag)
307 end_of_sound ();
308#endif
309
310 } while (finished == 0);
311
312 finished--;
313
314}/* interpret */
315
316/*
317 * call
318 *
319 * Call a subroutine. Save PC and FP then load new PC and initialise
320 * new stack frame. Note that the caller may legally provide less or
321 * more arguments than the function actually has. The call type "ct"
322 * can be 0 (z_call_s), 1 (z_call_n) or 2 (direct call).
323 *
324 */
325
326void call (zword routine, int argc, zword *args, int ct)
327{
328 long pc;
329 zword value;
330 zbyte count;
331 int i;
332
333 if (sp - stack < 4)
334 runtime_error (ERR_STK_OVF);
335
336 GET_PC (pc)
337
338 *--sp = (zword) (pc >> 9);
339 *--sp = (zword) (pc & 0x1ff);
340 *--sp = (zword) (fp - stack - 1);
341 *--sp = (zword) (argc | (ct << (f_setup.save_quetzal ? 12 : 8)));
342
343 fp = sp;
344 frame_count++;
345
346 /* Calculate byte address of routine */
347
348 if (h_version <= V3)
349 pc = (long) routine << 1;
350 else if (h_version <= V5)
351 pc = (long) routine << 2;
352 else if (h_version <= V7)
353 pc = ((long) routine << 2) + ((long) h_functions_offset << 3);
354 else /* h_version == V8 */
355 pc = (long) routine << 3;
356
357 if (pc >= story_size)
358 runtime_error (ERR_ILL_CALL_ADDR);
359
360 SET_PC (pc)
361
362 /* Initialise local variables */
363
364 CODE_BYTE (count)
365
366 if (count > 15)
367 runtime_error (ERR_CALL_NON_RTN);
368 if (sp - stack < count)
369 runtime_error (ERR_STK_OVF);
370
371 if (f_setup.save_quetzal)
372 fp[0] |= (zword) count << 8; /* Save local var count for Quetzal. */
373
374 value = 0;
375
376 for (i = 0; i < count; i++) {
377
378 if (h_version <= V4) /* V1 to V4 games provide default */
379 CODE_WORD (value) /* values for all local variables */
380
381 *--sp = (zword) ((argc-- > 0) ? args[i] : value);
382
383 }
384
385 /* Start main loop for direct calls */
386
387 if (ct == 2)
388 interpret ();
389
390}/* call */
391
392/*
393 * ret
394 *
395 * Return from the current subroutine and restore the previous stack
396 * frame. The result may be stored (0), thrown away (1) or pushed on
397 * the stack (2). In the latter case a direct call has been finished
398 * and we must exit the interpreter loop.
399 *
400 */
401
402void ret (zword value)
403{
404 long pc;
405 int ct;
406
407 if (sp > fp)
408 runtime_error (ERR_STK_UNDF);
409
410 sp = fp;
411
412 ct = *sp++ >> (f_setup.save_quetzal ? 12 : 8);
413 frame_count--;
414 fp = stack + 1 + *sp++;
415 pc = *sp++;
416 pc = ((long) *sp++ << 9) | pc;
417
418 SET_PC (pc)
419
420 /* Handle resulting value */
421
422 if (ct == 0)
423 store (value);
424 if (ct == 2)
425 *--sp = value;
426
427 /* Stop main loop for direct calls */
428
429 if (ct == 2)
430 finished++;
431
432}/* ret */
433
434/*
435 * branch
436 *
437 * Take a jump after an instruction based on the flag, either true or
438 * false. The branch can be short or long; it is encoded in one or two
439 * bytes respectively. When bit 7 of the first byte is set, the jump
440 * takes place if the flag is true; otherwise it is taken if the flag
441 * is false. When bit 6 of the first byte is set, the branch is short;
442 * otherwise it is long. The offset occupies the bottom 6 bits of the
443 * first byte plus all the bits in the second byte for long branches.
444 * Uniquely, an offset of 0 means return false, and an offset of 1 is
445 * return true.
446 *
447 */
448
449void branch (bool flag)
450{
451 long pc;
452 zword offset;
453 zbyte specifier;
454 zbyte off1;
455 zbyte off2;
456
457 CODE_BYTE (specifier)
458
459 off1 = specifier & 0x3f;
460
461 if (!flag)
462 specifier ^= 0x80;
463
464 if (!(specifier & 0x40)) { /* it's a long branch */
465
466 if (off1 & 0x20) /* propagate sign bit */
467 off1 |= 0xc0;
468
469 CODE_BYTE (off2)
470
471 offset = (off1 << 8) | off2;
472
473 } else offset = off1; /* it's a short branch */
474
475 if (specifier & 0x80) {
476
477 if (offset > 1) { /* normal branch */
478
479 GET_PC (pc)
480 pc += (short) offset - 2;
481 SET_PC (pc)
482
483 } else ret (offset); /* special case, return 0 or 1 */
484 }
485
486}/* branch */
487
488/*
489 * store
490 *
491 * Store an operand, either as a variable or pushed on the stack.
492 *
493 */
494
495void store (zword value)
496{
497 zbyte variable;
498
499 CODE_BYTE (variable)
500
501 if (variable == 0)
502 *--sp = value;
503 else if (variable < 16)
504 *(fp - variable) = value;
505 else {
506 zword addr = h_globals + 2 * (variable - 16);
507 SET_WORD (addr, value)
508 }
509
510}/* store */
511
512/*
513 * direct_call
514 *
515 * Call the interpreter loop directly. This is necessary when
516 *
517 * - a sound effect has been finished
518 * - a read instruction has timed out
519 * - a newline countdown has hit zero
520 *
521 * The interpreter returns the result value on the stack.
522 *
523 */
524
525int direct_call (zword addr)
526{
527 zword saved_zargs[8];
528 int saved_zargc;
529 int i;
530
531 /* Calls to address 0 return false */
532
533 if (addr == 0)
534 return 0;
535
536 /* Save operands and operand count */
537
538 for (i = 0; i < 8; i++)
539 saved_zargs[i] = zargs[i];
540
541 saved_zargc = zargc;
542
543 /* Call routine directly */
544
545 call (addr, 0, 0, 2);
546
547 /* Restore operands and operand count */
548
549 for (i = 0; i < 8; i++)
550 zargs[i] = saved_zargs[i];
551
552 zargc = saved_zargc;
553
554 /* Resulting value lies on top of the stack */
555
556 return (short) *sp++;
557
558}/* direct_call */
559
560/*
561 * __extended__
562 *
563 * Load and execute an extended opcode.
564 *
565 */
566
567static void __extended__ (void)
568{
569 zbyte opcode;
570 zbyte specifier;
571
572 CODE_BYTE (opcode)
573 CODE_BYTE (specifier)
574
575 load_all_operands (specifier);
576
577 if (opcode < 0x1d) /* extended opcodes from 0x1d on */
578 ext_opcodes[opcode] (); /* are reserved for future spec' */
579
580}/* __extended__ */
581
582/*
583 * __illegal__
584 *
585 * Exit game because an unknown opcode has been hit.
586 *
587 */
588
589static void __illegal__ (void)
590{
591
592 runtime_error (ERR_ILL_OPCODE);
593
594}/* __illegal__ */
595
596/*
597 * z_catch, store the current stack frame for later use with z_throw.
598 *
599 * no zargs used
600 *
601 */
602
603void z_catch (void)
604{
605
606 store (f_setup.save_quetzal ? frame_count : (zword) (fp - stack));
607
608}/* z_catch */
609
610/*
611 * z_throw, go back to the given stack frame and return the given value.
612 *
613 * zargs[0] = value to return
614 * zargs[1] = stack frame
615 *
616 */
617
618void z_throw (void)
619{
620
621 if (f_setup.save_quetzal) {
622 if (zargs[1] > frame_count)
623 runtime_error (ERR_BAD_FRAME);
624
625 /* Unwind the stack a frame at a time. */
626 for (; frame_count > zargs[1]; --frame_count)
627 fp = stack + 1 + fp[1];
628 } else {
629 if (zargs[1] > STACK_SIZE)
630 runtime_error (ERR_BAD_FRAME);
631
632 fp = stack + zargs[1];
633 }
634
635 ret (zargs[0]);
636
637}/* z_throw */
638
639/*
640 * z_call_n, call a subroutine and discard its result.
641 *
642 * zargs[0] = packed address of subroutine
643 * zargs[1] = first argument (optional)
644 * ...
645 * zargs[7] = seventh argument (optional)
646 *
647 */
648
649void z_call_n (void)
650{
651
652 if (zargs[0] != 0)
653 call (zargs[0], zargc - 1, zargs + 1, 1);
654
655}/* z_call_n */
656
657/*
658 * z_call_s, call a subroutine and store its result.
659 *
660 * zargs[0] = packed address of subroutine
661 * zargs[1] = first argument (optional)
662 * ...
663 * zargs[7] = seventh argument (optional)
664 *
665 */
666
667void z_call_s (void)
668{
669
670 if (zargs[0] != 0)
671 call (zargs[0], zargc - 1, zargs + 1, 0);
672 else
673 store (0);
674
675}/* z_call_s */
676
677/*
678 * z_check_arg_count, branch if subroutine was called with >= n arg's.
679 *
680 * zargs[0] = number of arguments
681 *
682 */
683
684void z_check_arg_count (void)
685{
686
687 if (fp == stack + STACK_SIZE)
688 branch (zargs[0] == 0);
689 else
690 branch (zargs[0] <= (*fp & 0xff));
691
692}/* z_check_arg_count */
693
694/*
695 * z_jump, jump unconditionally to the given address.
696 *
697 * zargs[0] = PC relative address
698 *
699 */
700
701void z_jump (void)
702{
703 long pc;
704
705 GET_PC (pc)
706
707 pc += (short) zargs[0] - 2;
708
709 if (pc >= story_size)
710 runtime_error (ERR_ILL_JUMP_ADDR);
711
712 SET_PC (pc)
713
714}/* z_jump */
715
716/*
717 * z_nop, no operation.
718 *
719 * no zargs used
720 *
721 */
722
723void z_nop (void)
724{
725
726 /* Do nothing */
727
728}/* z_nop */
729
730/*
731 * z_quit, stop game and exit interpreter.
732 *
733 * no zargs used
734 *
735 */
736
737void z_quit (void)
738{
739
740 finished = 9999;
741
742}/* z_quit */
743
744/*
745 * z_ret, return from a subroutine with the given value.
746 *
747 * zargs[0] = value to return
748 *
749 */
750
751void z_ret (void)
752{
753
754 ret (zargs[0]);
755
756}/* z_ret */
757
758/*
759 * z_ret_popped, return from a subroutine with a value popped off the stack.
760 *
761 * no zargs used
762 *
763 */
764
765void z_ret_popped (void)
766{
767
768 ret (*sp++);
769
770}/* z_ret_popped */
771
772/*
773 * z_rfalse, return from a subroutine with false (0).
774 *
775 * no zargs used
776 *
777 */
778
779void z_rfalse (void)
780{
781
782 ret (0);
783
784}/* z_rfalse */
785
786/*
787 * z_rtrue, return from a subroutine with true (1).
788 *
789 * no zargs used
790 *
791 */
792
793void z_rtrue (void)
794{
795
796 ret (1);
797
798}/* z_rtrue */
diff --git a/apps/plugins/frotz/quetzal.c b/apps/plugins/frotz/quetzal.c
new file mode 100644
index 0000000000..a75ade856e
--- /dev/null
+++ b/apps/plugins/frotz/quetzal.c
@@ -0,0 +1,541 @@
1/* quetzal.c - Saving and restoring of Quetzal files.
2 * Written by Martin Frost <mdf@doc.ic.ac.uk>
3 *
4 * Changes for Rockbox copyright 2009 Torne Wuff
5 *
6 * This file is part of Frotz.
7 *
8 * Frotz is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * Frotz is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
21 */
22
23#include "frotz.h"
24
25#define far
26
27#define get_c fgetc
28#define put_c fputc
29
30typedef unsigned long zlong;
31
32/*
33 * This is used only by save_quetzal. It probably should be allocated
34 * dynamically rather than statically.
35 */
36
37static zword frames[STACK_SIZE/4+1];
38
39/*
40 * ID types.
41 */
42
43#define makeid(a,b,c,d) ((zlong) (((a)<<24) | ((b)<<16) | ((c)<<8) | (d)))
44
45#define ID_FORM makeid ('F','O','R','M')
46#define ID_IFZS makeid ('I','F','Z','S')
47#define ID_IFhd makeid ('I','F','h','d')
48#define ID_UMem makeid ('U','M','e','m')
49#define ID_CMem makeid ('C','M','e','m')
50#define ID_Stks makeid ('S','t','k','s')
51#define ID_ANNO makeid ('A','N','N','O')
52
53/*
54 * Various parsing states within restoration.
55 */
56
57#define GOT_HEADER 0x01
58#define GOT_STACK 0x02
59#define GOT_MEMORY 0x04
60#define GOT_NONE 0x00
61#define GOT_ALL 0x07
62#define GOT_ERROR 0x80
63
64/*
65 * Macros used to write the files.
66 */
67
68#define write_byte(fp,b) (put_c (b, fp) != EOF)
69#define write_bytx(fp,b) write_byte (fp, (b) & 0xFF)
70#define write_word(fp,w) \
71 (write_bytx (fp, (w) >> 8) && write_bytx (fp, (w)))
72#define write_long(fp,l) \
73 (write_bytx (fp, (l) >> 24) && write_bytx (fp, (l) >> 16) && \
74 write_bytx (fp, (l) >> 8) && write_bytx (fp, (l)))
75#define write_chnk(fp,id,len) \
76 (write_long (fp, (id)) && write_long (fp, (len)))
77#define write_run(fp,run) \
78 (write_byte (fp, 0) && write_byte (fp, (run)))
79
80/* Read one word from file; return TRUE if OK. */
81static bool read_word (int f, zword *result)
82{
83 int a, b;
84
85 if ((a = get_c (f)) == EOF) return FALSE;
86 if ((b = get_c (f)) == EOF) return FALSE;
87
88 *result = ((zword) a << 8) | (zword) b;
89 return TRUE;
90}
91
92/* Read one long from file; return TRUE if OK. */
93static bool read_long (int f, zlong *result)
94{
95 int a, b, c, d;
96
97 if ((a = get_c (f)) == EOF) return FALSE;
98 if ((b = get_c (f)) == EOF) return FALSE;
99 if ((c = get_c (f)) == EOF) return FALSE;
100 if ((d = get_c (f)) == EOF) return FALSE;
101
102 *result = ((zlong) a << 24) | ((zlong) b << 16) |
103 ((zlong) c << 8) | (zlong) d;
104 return TRUE;
105}
106
107/*
108 * Restore a saved game using Quetzal format. Return 2 if OK, 0 if an error
109 * occurred before any damage was done, -1 on a fatal error.
110 */
111
112zword restore_quetzal (int svf, int stf)
113{
114 zlong ifzslen, currlen, tmpl;
115 zlong pc;
116 zword i, tmpw;
117 zword fatal = 0; /* Set to -1 when errors must be fatal. */
118 zbyte skip, progress = GOT_NONE;
119 int x, y;
120
121 /* Check it's really an `IFZS' file. */
122 if (!read_long (svf, &tmpl)
123 || !read_long (svf, &ifzslen)
124 || !read_long (svf, &currlen)) return 0;
125 if (tmpl != ID_FORM || currlen != ID_IFZS)
126 {
127 print_string ("This is not a saved game file!\n");
128 return 0;
129 }
130 if ((ifzslen & 1) || ifzslen<4) /* Sanity checks. */ return 0;
131 ifzslen -= 4;
132
133 /* Read each chunk and process it. */
134 while (ifzslen > 0)
135 {
136 /* Read chunk header. */
137 if (ifzslen < 8) /* Couldn't contain a chunk. */ return 0;
138 if (!read_long (svf, &tmpl)
139 || !read_long (svf, &currlen)) return 0;
140 ifzslen -= 8; /* Reduce remaining by size of header. */
141
142 /* Handle chunk body. */
143 if (ifzslen < currlen) /* Chunk goes past EOF?! */ return 0;
144 skip = currlen & 1;
145 ifzslen -= currlen + (zlong) skip;
146
147 switch (tmpl)
148 {
149 /* `IFhd' header chunk; must be first in file. */
150 case ID_IFhd:
151 if (progress & GOT_HEADER)
152 {
153 print_string ("Save file has two IFZS chunks!\n");
154 return fatal;
155 }
156 progress |= GOT_HEADER;
157 if (currlen < 13
158 || !read_word (svf, &tmpw)) return fatal;
159 if (tmpw != h_release)
160 progress = GOT_ERROR;
161
162 for (i=H_SERIAL; i<H_SERIAL+6; ++i)
163 {
164 if ((x = get_c (svf)) == EOF) return fatal;
165 if (x != zmp[i])
166 progress = GOT_ERROR;
167 }
168
169 if (!read_word (svf, &tmpw)) return fatal;
170 if (tmpw != h_checksum)
171 progress = GOT_ERROR;
172
173 if (progress & GOT_ERROR)
174 {
175 print_string ("File was not saved from this story!\n");
176 return fatal;
177 }
178 if ((x = get_c (svf)) == EOF) return fatal;
179 pc = (zlong) x << 16;
180 if ((x = get_c (svf)) == EOF) return fatal;
181 pc |= (zlong) x << 8;
182 if ((x = get_c (svf)) == EOF) return fatal;
183 pc |= (zlong) x;
184 fatal = -1; /* Setting PC means errors must be fatal. */
185 SET_PC (pc);
186
187 for (i=13; i<currlen; ++i)
188 (void) get_c (svf); /* Skip rest of chunk. */
189 break;
190 /* `Stks' stacks chunk; restoring this is quite complex. ;) */
191 case ID_Stks:
192 if (progress & GOT_STACK)
193 {
194 print_string ("File contains two stack chunks!\n");
195 break;
196 }
197 progress |= GOT_STACK;
198
199 fatal = -1; /* Setting SP means errors must be fatal. */
200 sp = stack + STACK_SIZE;
201
202 /*
203 * All versions other than V6 may use evaluation stack outside
204 * any function context. As a result a faked function context
205 * will be present in the file here. We skip this context, but
206 * load the associated stack onto the stack proper...
207 */
208 if (h_version != V6)
209 {
210 if (currlen < 8) return fatal;
211 for (i=0; i<6; ++i)
212 if (get_c (svf) != 0) return fatal;
213 if (!read_word (svf, &tmpw)) return fatal;
214 if (tmpw > STACK_SIZE)
215 {
216 print_string ("Save-file has too much stack (and I can't cope).\n");
217 return fatal;
218 }
219 currlen -= 8;
220 if ((signed)currlen < tmpw*2) return fatal;
221 for (i=0; i<tmpw; ++i)
222 if (!read_word (svf, --sp)) return fatal;
223 currlen -= tmpw*2;
224 }
225
226 /* We now proceed to load the main block of stack frames. */
227 for (fp = stack+STACK_SIZE, frame_count = 0;
228 currlen > 0;
229 currlen -= 8, ++frame_count)
230 {
231 if (currlen < 8) return fatal;
232 if (sp - stack < 4) /* No space for frame. */
233 {
234 print_string ("Save-file has too much stack (and I can't cope).\n");
235 return fatal;
236 }
237
238 /* Read PC, procedure flag and formal param count. */
239 if (!read_long (svf, &tmpl)) return fatal;
240 y = (int) (tmpl & 0x0F); /* Number of formals. */
241 tmpw = y << 8;
242
243 /* Read result variable. */
244 if ((x = get_c (svf)) == EOF) return fatal;
245
246 /* Check the procedure flag... */
247 if (tmpl & 0x10)
248 {
249 tmpw |= 0x1000; /* It's a procedure. */
250 tmpl >>= 8; /* Shift to get PC value. */
251 }
252 else
253 {
254 /* Functions have type 0, so no need to or anything. */
255 tmpl >>= 8; /* Shift to get PC value. */
256 --tmpl; /* Point at result byte. */
257 /* Sanity check on result variable... */
258 if (zmp[tmpl] != (zbyte) x)
259 {
260 print_string ("Save-file has wrong variable number on stack (possibly wrong game version?)\n");
261 return fatal;
262 }
263 }
264 *--sp = (zword) (tmpl >> 9); /* High part of PC */
265 *--sp = (zword) (tmpl & 0x1FF); /* Low part of PC */
266 *--sp = (zword) (fp - stack - 1); /* FP */
267
268 /* Read and process argument mask. */
269 if ((x = get_c (svf)) == EOF) return fatal;
270 ++x; /* Should now be a power of 2 */
271 for (i=0; i<8; ++i)
272 if (x & (1<<i))
273 break;
274 if (x ^ (1<<i)) /* Not a power of 2 */
275 {
276 print_string ("Save-file uses incomplete argument lists (which I can't handle)\n");
277 return fatal;
278 }
279 *--sp = tmpw | i;
280 fp = sp; /* FP for next frame. */
281
282 /* Read amount of eval stack used. */
283 if (!read_word (svf, &tmpw)) return fatal;
284
285 tmpw += y; /* Amount of stack + number of locals. */
286 if (sp - stack <= tmpw)
287 {
288 print_string ("Save-file has too much stack (and I can't cope).\n");
289 return fatal;
290 }
291 if ((signed)currlen < tmpw*2) return fatal;
292 for (i=0; i<tmpw; ++i)
293 if (!read_word (svf, --sp)) return fatal;
294 currlen -= tmpw*2;
295 }
296 /* End of `Stks' processing... */
297 break;
298 /* Any more special chunk types must go in HERE or ABOVE. */
299 /* `CMem' compressed memory chunk; uncompress it. */
300 case ID_CMem:
301 if (!(progress & GOT_MEMORY)) /* Don't complain if two. */
302 {
303 (void) fseek (stf, 0, SEEK_SET);
304 i=0; /* Bytes written to data area. */
305 for (; currlen > 0; --currlen)
306 {
307 if ((x = get_c (svf)) == EOF) return fatal;
308 if (x == 0) /* Start run. */
309 {
310 /* Check for bogus run. */
311 if (currlen < 2)
312 {
313 print_string ("File contains bogus `CMem' chunk.\n");
314 for (; currlen > 0; --currlen)
315 (void) get_c (svf); /* Skip rest. */
316 currlen = 1;
317 i = 0xFFFF;
318 break; /* Keep going; may be a `UMem' too. */
319 }
320 /* Copy story file to memory during the run. */
321 --currlen;
322 if ((x = get_c (svf)) == EOF) return fatal;
323 for (; x >= 0 && i<h_dynamic_size; --x, ++i)
324 if ((y = get_c (stf)) == EOF) return fatal;
325 else
326 zmp[i] = (zbyte) y;
327 }
328 else /* Not a run. */
329 {
330 if ((y = get_c (stf)) == EOF) return fatal;
331 zmp[i] = (zbyte) (x ^ y);
332 ++i;
333 }
334 /* Make sure we don't load too much. */
335 if (i > h_dynamic_size)
336 {
337 print_string ("warning: `CMem' chunk too long!\n");
338 for (; currlen > 1; --currlen)
339 (void) get_c (svf); /* Skip rest. */
340 break; /* Keep going; there may be a `UMem' too. */
341 }
342 }
343 /* If chunk is short, assume a run. */
344 for (; i<h_dynamic_size; ++i)
345 if ((y = get_c (stf)) == EOF) return fatal;
346 else
347 zmp[i] = (zbyte) y;
348 if (currlen == 0)
349 progress |= GOT_MEMORY; /* Only if succeeded. */
350 break;
351 }
352 /* Fall right thru (to default) if already GOT_MEMORY */
353 /* `UMem' uncompressed memory chunk; load it. */
354 case ID_UMem:
355 if (!(progress & GOT_MEMORY)) /* Don't complain if two. */
356 {
357 /* Must be exactly the right size. */
358 if (currlen == h_dynamic_size)
359 {
360 if (fread (zmp, currlen, 1, svf) == 1)
361 {
362 progress |= GOT_MEMORY; /* Only on success. */
363 break;
364 }
365 }
366 else
367 print_string ("`UMem' chunk wrong size!\n");
368 /* Fall into default action (skip chunk) on errors. */
369 }
370 /* Fall thru (to default) if already GOT_MEMORY */
371 /* Unrecognised chunk type; skip it. */
372 default:
373 (void) fseek (svf, currlen, SEEK_CUR); /* Skip chunk. */
374 break;
375 }
376 if (skip)
377 (void) get_c (svf); /* Skip pad byte. */
378 }
379
380 /*
381 * We've reached the end of the file. For the restoration to have been a
382 * success, we must have had one of each of the required chunks.
383 */
384 if (!(progress & GOT_HEADER))
385 print_string ("error: no valid header (`IFhd') chunk in file.\n");
386 if (!(progress & GOT_STACK))
387 print_string ("error: no valid stack (`Stks') chunk in file.\n");
388 if (!(progress & GOT_MEMORY))
389 print_string ("error: no valid memory (`CMem' or `UMem') chunk in file.\n");
390
391 return (progress == GOT_ALL ? 2 : fatal);
392}
393
394/*
395 * Save a game using Quetzal format. Return 1 if OK, 0 if failed.
396 */
397
398zword save_quetzal (int svf, int stf)
399{
400 zlong ifzslen = 0, cmemlen = 0, stkslen = 0;
401 zlong pc;
402 zword i, j, n;
403 zword nvars, nargs, nstk, *p;
404 zbyte var;
405 long cmempos, stkspos;
406 int c;
407
408 /* Write `IFZS' header. */
409 if (!write_chnk (svf, ID_FORM, 0)) return 0;
410 if (!write_long (svf, ID_IFZS)) return 0;
411
412 /* Write `IFhd' chunk. */
413 GET_PC (pc);
414 if (!write_chnk (svf, ID_IFhd, 13)) return 0;
415 if (!write_word (svf, h_release)) return 0;
416 for (i=H_SERIAL; i<H_SERIAL+6; ++i)
417 if (!write_byte (svf, zmp[i])) return 0;
418 if (!write_word (svf, h_checksum)) return 0;
419 if (!write_long (svf, pc << 8)) /* Includes pad. */ return 0;
420
421 /* Write `CMem' chunk. */
422 if ((cmempos = ftell (svf)) < 0) return 0;
423 if (!write_chnk (svf, ID_CMem, 0)) return 0;
424 (void) fseek (stf, 0, SEEK_SET);
425 /* j holds current run length. */
426 for (i=0, j=0, cmemlen=0; i < h_dynamic_size; ++i)
427 {
428 if ((c = get_c (stf)) == EOF) return 0;
429 c ^= (int) zmp[i];
430 if (c == 0)
431 ++j; /* It's a run of equal bytes. */
432 else
433 {
434 /* Write out any run there may be. */
435 if (j > 0)
436 {
437 for (; j > 0x100; j -= 0x100)
438 {
439 if (!write_run (svf, 0xFF)) return 0;
440 cmemlen += 2;
441 }
442 if (!write_run (svf, j-1)) return 0;
443 cmemlen += 2;
444 j = 0;
445 }
446 /* Any runs are now written. Write this (nonzero) byte. */
447 if (!write_byte (svf, (zbyte) c)) return 0;
448 ++cmemlen;
449 }
450 }
451 /*
452 * Reached end of dynamic memory. We ignore any unwritten run there may be
453 * at this point.
454 */
455 if (cmemlen & 1) /* Chunk length must be even. */
456 if (!write_byte (svf, 0)) return 0;
457
458 /* Write `Stks' chunk. You are not expected to understand this. ;) */
459 if ((stkspos = ftell (svf)) < 0) return 0;
460 if (!write_chnk (svf, ID_Stks, 0)) return 0;
461
462 /*
463 * We construct a list of frame indices, most recent first, in `frames'.
464 * These indices are the offsets into the `stack' array of the word before
465 * the first word pushed in each frame.
466 */
467 frames[0] = sp - stack; /* The frame we'd get by doing a call now. */
468 for (i = fp - stack + 4, n=0; i < STACK_SIZE+4; i = stack[i-3] + 5)
469 frames[++n] = i;
470
471 /*
472 * All versions other than V6 can use evaluation stack outside a function
473 * context. We write a faked stack frame (most fields zero) to cater for
474 * this.
475 */
476 if (h_version != V6)
477 {
478 for (i=0; i<6; ++i)
479 if (!write_byte (svf, 0)) return 0;
480 nstk = STACK_SIZE - frames[n];
481 if (!write_word (svf, nstk)) return 0;
482 for (j=STACK_SIZE-1; j >= frames[n]; --j)
483 if (!write_word (svf, stack[j])) return 0;
484 stkslen = 8 + 2*nstk;
485 }
486
487 /* Write out the rest of the stack frames. */
488 for (i=n; i>0; --i)
489 {
490 p = stack + frames[i] - 4; /* Points to call frame. */
491 nvars = (p[0] & 0x0F00) >> 8;
492 nargs = p[0] & 0x00FF;
493 nstk = frames[i] - frames[i-1] - nvars - 4;
494 pc = ((zlong) p[3] << 9) | p[2];
495
496 switch (p[0] & 0xF000) /* Check type of call. */
497 {
498 case 0x0000: /* Function. */
499 var = zmp[pc];
500 pc = ((pc + 1) << 8) | nvars;
501 break;
502 case 0x1000: /* Procedure. */
503 var = 0;
504 pc = (pc << 8) | 0x10 | nvars; /* Set procedure flag. */
505 break;
506 /* case 0x2000: */
507 default:
508 runtime_error (ERR_SAVE_IN_INTER);
509 return 0;
510 }
511 if (nargs != 0)
512 nargs = (1 << nargs) - 1; /* Make args into bitmap. */
513
514 /* Write the main part of the frame... */
515 if (!write_long (svf, pc)
516 || !write_byte (svf, var)
517 || !write_byte (svf, nargs)
518 || !write_word (svf, nstk)) return 0;
519
520 /* Write the variables and eval stack. */
521 for (j=0, ++p; j<nvars+nstk; ++j, --p)
522 if (!write_word (svf, *p)) return 0;
523
524 /* Calculate length written thus far. */
525 stkslen += 8 + 2 * (nvars + nstk);
526 }
527
528 /* Fill in variable chunk lengths. */
529 ifzslen = 3*8 + 4 + 14 + cmemlen + stkslen;
530 if (cmemlen & 1)
531 ++ifzslen;
532 (void) fseek (svf, 4, SEEK_SET);
533 if (!write_long (svf, ifzslen)) return 0;
534 (void) fseek (svf, cmempos+4, SEEK_SET);
535 if (!write_long (svf, cmemlen)) return 0;
536 (void) fseek (svf, stkspos+4, SEEK_SET);
537 if (!write_long (svf, stkslen)) return 0;
538
539 /* After all that, still nothing went wrong! */
540 return 1;
541}
diff --git a/apps/plugins/frotz/random.c b/apps/plugins/frotz/random.c
new file mode 100644
index 0000000000..6ea14d89c9
--- /dev/null
+++ b/apps/plugins/frotz/random.c
@@ -0,0 +1,82 @@
1/* random.c - Z-machine random number generator
2 * Copyright (c) 1995-1997 Stefan Jokisch
3 *
4 * This file is part of Frotz.
5 *
6 * Frotz is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * Frotz is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
19 */
20
21#include "frotz.h"
22
23static long A = 1;
24
25static int interval = 0;
26static int counter = 0;
27
28/*
29 * seed_random
30 *
31 * Set the seed value for the random number generator.
32 *
33 */
34
35void seed_random (int value)
36{
37
38 if (value == 0) { /* ask interface for seed value */
39 A = os_random_seed ();
40 interval = 0;
41 } else if (value < 1000) { /* special seed value */
42 counter = 0;
43 interval = value;
44 } else { /* standard seed value */
45 A = value;
46 interval = 0;
47 }
48
49}/* seed_random */
50
51/*
52 * z_random, store a random number or set the random number seed.
53 *
54 * zargs[0] = range (positive) or seed value (negative)
55 *
56 */
57
58void z_random ()
59{
60
61 if ((short) zargs[0] <= 0) { /* set random seed */
62
63 seed_random (- (short) zargs[0]);
64 store (0);
65
66 } else { /* generate random number */
67
68 zword result;
69
70 if (interval != 0) { /* ...in special mode */
71 result = counter++;
72 if (counter == interval) counter = 0;
73 } else { /* ...in standard mode */
74 A = 0x015a4e35L * A + 1;
75 result = (A >> 16) & 0x7fff;
76 }
77
78 store ((zword) (result % zargs[0] + 1));
79
80 }
81
82}/* z_random */
diff --git a/apps/plugins/frotz/redirect.c b/apps/plugins/frotz/redirect.c
new file mode 100644
index 0000000000..d81776dbcd
--- /dev/null
+++ b/apps/plugins/frotz/redirect.c
@@ -0,0 +1,172 @@
1/* redirect.c - Output redirection to Z-machine memory
2 * Copyright (c) 1995-1997 Stefan Jokisch
3 *
4 * This file is part of Frotz.
5 *
6 * Frotz is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * Frotz is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
19 */
20
21#include "frotz.h"
22
23#define MAX_NESTING 16
24
25extern zword get_max_width (zword);
26
27static int depth = -1;
28
29static struct {
30 zword xsize;
31 zword table;
32 zword width;
33 zword total;
34} redirect[MAX_NESTING];
35
36/*
37 * memory_open
38 *
39 * Begin output redirection to the memory of the Z-machine.
40 *
41 */
42
43void memory_open (zword table, zword xsize, bool buffering)
44{
45
46 if (++depth < MAX_NESTING) {
47
48 if (!buffering)
49 xsize = 0xffff;
50 if (buffering && (short) xsize <= 0)
51 xsize = get_max_width ((zword) (- (short) xsize));
52
53 storew (table, 0);
54
55 redirect[depth].table = table;
56 redirect[depth].width = 0;
57 redirect[depth].total = 0;
58 redirect[depth].xsize = xsize;
59
60 ostream_memory = TRUE;
61
62 } else runtime_error (ERR_STR3_NESTING);
63
64}/* memory_open */
65
66/*
67 * memory_new_line
68 *
69 * Redirect a newline to the memory of the Z-machine.
70 *
71 */
72
73void memory_new_line (void)
74{
75 zword size;
76 zword addr;
77
78 redirect[depth].total += redirect[depth].width;
79 redirect[depth].width = 0;
80
81 addr = redirect[depth].table;
82
83 LOW_WORD (addr, size)
84 addr += 2;
85
86 if (redirect[depth].xsize != 0xffff) {
87
88 redirect[depth].table = addr + size;
89 size = 0;
90
91 } else storeb ((zword) (addr + (size++)), 13);
92
93 storew (redirect[depth].table, size);
94
95}/* memory_new_line */
96
97/*
98 * memory_word
99 *
100 * Redirect a string of characters to the memory of the Z-machine.
101 *
102 */
103
104void memory_word (const zchar *s)
105{
106 zword size;
107 zword addr;
108 zchar c;
109
110 if (h_version == V6) {
111
112 int width = os_string_width (s);
113
114 if (redirect[depth].xsize != 0xffff)
115
116 if (redirect[depth].width + width > redirect[depth].xsize) {
117
118 if (*s == ' ' || *s == ZC_INDENT || *s == ZC_GAP)
119 width = os_string_width (++s);
120
121 memory_new_line ();
122
123 }
124
125 redirect[depth].width += width;
126
127 }
128
129 addr = redirect[depth].table;
130
131 LOW_WORD (addr, size)
132 addr += 2;
133
134 while ((c = *s++) != 0)
135 storeb ((zword) (addr + (size++)), translate_to_zscii (c));
136
137 storew (redirect[depth].table, size);
138
139}/* memory_word */
140
141/*
142 * memory_close
143 *
144 * End of output redirection.
145 *
146 */
147
148void memory_close (void)
149{
150
151 if (depth >= 0) {
152
153 if (redirect[depth].xsize != 0xffff)
154 memory_new_line ();
155
156 if (h_version == V6) {
157
158 h_line_width = (redirect[depth].xsize != 0xffff) ?
159 redirect[depth].total : redirect[depth].width;
160
161 SET_WORD (H_LINE_WIDTH, h_line_width)
162
163 }
164
165 if (depth == 0)
166 ostream_memory = FALSE;
167
168 depth--;
169
170 }
171
172}/* memory_close */
diff --git a/apps/plugins/frotz/screen.c b/apps/plugins/frotz/screen.c
new file mode 100644
index 0000000000..713ec2921c
--- /dev/null
+++ b/apps/plugins/frotz/screen.c
@@ -0,0 +1,1743 @@
1/* screen.c - Generic screen manipulation
2 * Copyright (c) 1995-1997 Stefan Jokisch
3 *
4 * This file is part of Frotz.
5 *
6 * Frotz is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * Frotz is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
19 */
20
21#include "frotz.h"
22
23extern void set_header_extension (int, zword);
24
25extern int direct_call (zword);
26
27static struct {
28 enum story story_id;
29 int pic;
30 int pic1;
31 int pic2;
32} mapper[] = {
33 { ZORK_ZERO, 5, 497, 498 },
34 { ZORK_ZERO, 6, 501, 502 },
35 { ZORK_ZERO, 7, 499, 500 },
36 { ZORK_ZERO, 8, 503, 504 },
37 { ARTHUR, 54, 170, 171 },
38 { SHOGUN, 50, 61, 62 },
39 { UNKNOWN, 0, 0, 0 }
40};
41
42static int font_height = 1;
43static int font_width = 1;
44
45static bool input_redraw = FALSE;
46static bool more_prompts = TRUE;
47static bool discarding = FALSE;
48static bool cursor = TRUE;
49
50static int input_window = 0;
51
52static struct {
53 zword y_pos;
54 zword x_pos;
55 zword y_size;
56 zword x_size;
57 zword y_cursor;
58 zword x_cursor;
59 zword left;
60 zword right;
61 zword nl_routine;
62 zword nl_countdown;
63 zword style;
64 zword colour;
65 zword font;
66 zword font_size;
67 zword attribute;
68 zword line_count;
69} wp[8], *cwp;
70
71
72/*
73 * winarg0
74 *
75 * Return the window number in zargs[0]. In V6 only, -3 refers to the
76 * current window.
77 *
78 */
79
80static zword winarg0 (void)
81{
82
83 if (h_version == V6 && (short) zargs[0] == -3)
84 return cwin;
85
86 if (zargs[0] >= ((h_version == V6) ? 8 : 2))
87 runtime_error (ERR_ILL_WIN);
88
89 return zargs[0];
90
91}/* winarg0 */
92
93/*
94 * winarg2
95 *
96 * Return the (optional) window number in zargs[2]. -3 refers to the
97 * current window. This optional window number was only used by some
98 * V6 opcodes: set_cursor, set_margins, set_colour.
99 *
100 */
101
102static zword winarg2 (void)
103{
104
105 if (zargc < 3 || (short) zargs[2] == -3)
106 return cwin;
107
108 if (zargs[2] >= 8)
109 runtime_error (ERR_ILL_WIN);
110
111 return zargs[2];
112
113}/* winarg2 */
114
115/*
116 * update_cursor
117 *
118 * Move the hardware cursor to make it match the window properties.
119 *
120 */
121
122static void update_cursor (void)
123{
124
125 os_set_cursor (
126 cwp->y_pos + cwp->y_cursor - 1,
127 cwp->x_pos + cwp->x_cursor - 1);
128
129}/* update_cursor */
130
131/*
132 * reset_cursor
133 *
134 * Reset the cursor of a given window to its initial position.
135 *
136 */
137
138static void reset_cursor (zword win)
139{
140 int lines = 0;
141
142 if (h_version <= V4 && win == 0)
143 lines = wp[0].y_size / hi (wp[0].font_size) - 1;
144
145 wp[win].y_cursor = hi (wp[0].font_size) * lines + 1;
146 wp[win].x_cursor = wp[win].left + 1;
147
148 if (win == cwin)
149 update_cursor ();
150
151}/* reset_cursor */
152
153/*
154 * set_more_prompts
155 *
156 * Turn more prompts on/off.
157 *
158 */
159
160void set_more_prompts (bool flag)
161{
162
163 if (flag && !more_prompts)
164 cwp->line_count = 0;
165
166 more_prompts = flag;
167
168}/* set_more_prompts */
169
170/*
171 * units_left
172 *
173 * Return the #screen units from the cursor to the end of the line.
174 *
175 */
176
177static int units_left (void)
178{
179
180 return cwp->x_size - cwp->right - cwp->x_cursor + 1;
181
182}/* units_left */
183
184/*
185 * get_max_width
186 *
187 * Return maximum width of a line in the given window. This is used in
188 * connection with the extended output stream #3 call in V6.
189 *
190 */
191
192zword get_max_width (zword win)
193{
194
195 if (h_version == V6) {
196
197 if (win >= 8)
198 runtime_error (ERR_ILL_WIN);
199
200 return wp[win].x_size - wp[win].left - wp[win].right;
201
202 } else return 0xffff;
203
204}/* get_max_width */
205
206/*
207 * countdown
208 *
209 * Decrement the newline counter. Call the newline interrupt when the
210 * counter hits zero. This is a helper function for screen_new_line.
211 *
212 */
213
214static void countdown (void)
215{
216
217 if (cwp->nl_countdown != 0)
218 if (--cwp->nl_countdown == 0)
219 direct_call (cwp->nl_routine);
220
221}/* countdown */
222
223/*
224 * screen_new_line
225 *
226 * Print a newline to the screen.
227 *
228 */
229
230void screen_new_line (void)
231{
232
233 if (discarding) return;
234
235 /* Handle newline interrupts at the start (for most cases) */
236
237 if (h_interpreter_number != INTERP_MSDOS || story_id != ZORK_ZERO || h_release != 393)
238 countdown ();
239
240 /* Check whether the last input line gets destroyed */
241
242 if (input_window == cwin)
243 input_redraw = TRUE;
244
245 /* If the cursor has not reached the bottom line, then move it to
246 the next line; otherwise scroll the window or reset the cursor
247 to the top left. */
248
249 cwp->x_cursor = cwp->left + 1;
250
251 if (cwp->y_cursor + 2 * font_height - 1 > cwp->y_size)
252
253 if (enable_scrolling) {
254
255 zword y = cwp->y_pos;
256 zword x = cwp->x_pos;
257
258 os_scroll_area (y,
259 x,
260 y + cwp->y_size - 1,
261 x + cwp->x_size - 1,
262 font_height);
263
264 } else cwp->y_cursor = 1;
265
266 else cwp->y_cursor += font_height;
267
268 update_cursor ();
269
270 /* See if we need to print a more prompt (unless the game has set
271 the line counter to -999 in order to suppress more prompts). */
272
273 if (enable_scrolling && (short) cwp->line_count != -999) {
274
275 zword above = (cwp->y_cursor - 1) / font_height;
276 zword below = (cwp->y_size - cwp->y_cursor + 1) / font_height;
277
278 cwp->line_count++;
279
280 if ((short) cwp->line_count >= (short) above + below - 1) {
281
282 if (more_prompts)
283 os_more_prompt ();
284
285 cwp->line_count = f_setup.context_lines;
286
287 }
288
289 }
290
291 /* Handle newline interrupts at the end for Zork Zero under DOS */
292
293 if (h_interpreter_number == INTERP_MSDOS && story_id == ZORK_ZERO && h_release == 393)
294 countdown ();
295
296}/* screen_new_line */
297
298/*
299 * screen_char
300 *
301 * Display a single character on the screen.
302 *
303 */
304
305void screen_char (zchar c)
306{
307 int width;
308
309 if (discarding) return;
310
311 if (c == ZC_INDENT && cwp->x_cursor != cwp->left + 1)
312 c = ' ';
313
314 if (units_left () < (width = os_char_width (c))) {
315
316 if (!enable_wrapping)
317 { cwp->x_cursor = cwp->x_size - cwp->right; return; }
318
319 screen_new_line ();
320
321 }
322
323 os_display_char (c); cwp->x_cursor += width;
324
325}/* screen_char */
326
327/*
328 * screen_word
329 *
330 * Display a string of characters on the screen. If the word doesn't fit
331 * then use wrapping or clipping depending on the current setting of the
332 * enable_wrapping flag.
333 *
334 */
335
336void screen_word (const zchar *s)
337{
338 int width;
339
340 if (discarding) return;
341
342 if (*s == ZC_INDENT && cwp->x_cursor != cwp->left + 1)
343 screen_char (*s++);
344
345 if (units_left () < (width = os_string_width (s))) {
346
347 if (!enable_wrapping) {
348
349 zchar c;
350
351 while ((c = *s++) != 0)
352
353 if (c == ZC_NEW_FONT || c == ZC_NEW_STYLE) {
354
355 int arg = (int) *s++;
356
357 if (c == ZC_NEW_FONT)
358 os_set_font (arg);
359 if (c == ZC_NEW_STYLE)
360 os_set_text_style (arg);
361
362 } else screen_char (c);
363
364 return;
365
366 }
367
368 if (*s == ' ' || *s == ZC_INDENT || *s == ZC_GAP)
369 width = os_string_width (++s);
370
371#ifdef AMIGA
372 if (cwin == 0) Justifiable ();
373#endif
374
375 screen_new_line ();
376
377 }
378
379 os_display_string (s); cwp->x_cursor += width;
380
381}/* screen_word */
382
383/*
384 * screen_write_input
385 *
386 * Display an input line on the screen. This is required during playback.
387 *
388 */
389
390void screen_write_input (const zchar *buf, zchar key)
391{
392 int width;
393
394 if (units_left () < (width = os_string_width (buf)))
395 screen_new_line ();
396
397 os_display_string (buf); cwp->x_cursor += width;
398
399 if (key == ZC_RETURN)
400 screen_new_line ();
401
402}/* screen_write_input */
403
404/*
405 * screen_erase_input
406 *
407 * Remove an input line that has already been printed from the screen
408 * as if it was deleted by the player. This could be necessary during
409 * playback.
410 *
411 */
412
413void screen_erase_input (const zchar *buf)
414{
415
416 if (buf[0] != 0) {
417
418 int width = os_string_width (buf);
419
420 zword y;
421 zword x;
422
423 cwp->x_cursor -= width;
424
425 y = cwp->y_pos + cwp->y_cursor - 1;
426 x = cwp->x_pos + cwp->x_cursor - 1;
427
428 os_erase_area (y, x, y + font_height - 1, x + width - 1);
429 os_set_cursor (y, x);
430
431 }
432
433}/* screen_erase_input */
434
435/*
436 * console_read_input
437 *
438 * Read an input line from the keyboard and return the terminating key.
439 *
440 */
441
442zchar console_read_input (int max, zchar *buf, zword timeout, bool continued)
443{
444 zchar key;
445 int i;
446
447 /* Make sure there is some space for input */
448
449 if (cwin == 0 && units_left () + os_string_width (buf) < 10 * font_width)
450 screen_new_line ();
451
452 /* Make sure the input line is visible */
453
454 if (continued && input_redraw)
455 screen_write_input (buf, -1);
456
457 input_window = cwin;
458 input_redraw = FALSE;
459
460 /* Get input line from IO interface */
461
462 cwp->x_cursor -= os_string_width (buf);
463 key = os_read_line (max, buf, timeout, units_left (), continued);
464 cwp->x_cursor += os_string_width (buf);
465
466 if (key != ZC_TIME_OUT)
467 for (i = 0; i < 8; i++)
468 wp[i].line_count = 0;
469
470 /* Add a newline if the input was terminated normally */
471
472 if (key == ZC_RETURN)
473 screen_new_line ();
474
475 return key;
476
477}/* console_read_input */
478
479/*
480 * console_read_key
481 *
482 * Read a single keystroke and return it.
483 *
484 */
485
486zchar console_read_key (zword timeout)
487{
488 zchar key;
489 int i;
490
491 key = os_read_key (timeout, cursor);
492
493 if (key != ZC_TIME_OUT)
494 for (i = 0; i < 8; i++)
495 wp[i].line_count = 0;
496
497 return key;
498
499}/* console_read_key */
500
501/*
502 * update_attributes
503 *
504 * Set the three enable_*** variables to make them match the attributes
505 * of the current window.
506 *
507 */
508
509static void update_attributes (void)
510{
511 zword attr = cwp->attribute;
512
513 enable_wrapping = attr & 1;
514 enable_scrolling = attr & 2;
515 enable_scripting = attr & 4;
516 enable_buffering = attr & 8;
517
518 /* Some story files forget to select wrapping for printing hints */
519
520 if (story_id == ZORK_ZERO && h_release == 366)
521 if (cwin == 0)
522 enable_wrapping = TRUE;
523 if (story_id == SHOGUN && h_release <= 295)
524 if (cwin == 0)
525 enable_wrapping = TRUE;
526
527}/* update_attributes */
528
529/*
530 * refresh_text_style
531 *
532 * Set the right text style. This can be necessary when the fixed font
533 * flag is changed, or when a new window is selected, or when the game
534 * uses the set_text_style opcode.
535 *
536 */
537
538void refresh_text_style (void)
539{
540 zword style;
541
542 if (h_version != V6) {
543
544 style = wp[0].style;
545
546 if (cwin != 0 || h_flags & FIXED_FONT_FLAG)
547 style |= FIXED_WIDTH_STYLE;
548
549 } else style = cwp->style;
550
551 if (!ostream_memory && ostream_screen && enable_buffering) {
552
553 print_char (ZC_NEW_STYLE);
554 print_char (style);
555
556 } else os_set_text_style (style);
557
558}/* refresh_text_style */
559
560/*
561 * set_window
562 *
563 * Set the current window. In V6 every window has its own set of window
564 * properties such as colours, text style, cursor position and size.
565 *
566 */
567
568static void set_window (zword win)
569{
570
571 flush_buffer ();
572
573 cwin = win; cwp = wp + win;
574
575 update_attributes ();
576
577 if (h_version == V6) {
578
579 os_set_colour (lo (cwp->colour), hi (cwp->colour));
580
581 if (os_font_data (cwp->font, &font_height, &font_width))
582 os_set_font (cwp->font);
583
584 os_set_text_style (cwp->style);
585
586 } else refresh_text_style ();
587
588 if (h_version != V6 && win != 0) {
589 wp[win].y_cursor = 1;
590 wp[win].x_cursor = 1;
591 }
592
593 update_cursor ();
594
595}/* set_window */
596
597/*
598 * erase_window
599 *
600 * Erase a window to background colour.
601 *
602 */
603
604void erase_window (zword win)
605{
606 zword y = wp[win].y_pos;
607 zword x = wp[win].x_pos;
608
609 if (h_version == V6 && win != cwin && h_interpreter_number != INTERP_AMIGA)
610 os_set_colour (lo (wp[win].colour), hi (wp[win].colour));
611
612 os_erase_area (y,
613 x,
614 y + wp[win].y_size - 1,
615 x + wp[win].x_size - 1);
616
617 if (h_version == V6 && win != cwin && h_interpreter_number != INTERP_AMIGA)
618 os_set_colour (lo (cwp->colour), hi (cwp->colour));
619
620 reset_cursor (win);
621
622 wp[win].line_count = 0;
623
624}/* erase_window */
625
626/*
627 * split_window
628 *
629 * Divide the screen into upper (1) and lower (0) windows. In V3 the upper
630 * window appears below the status line.
631 *
632 */
633
634void split_window (zword height)
635{
636 zword stat_height = 0;
637
638 flush_buffer ();
639
640 /* Calculate height of status line and upper window */
641
642 if (h_version != V6)
643 height *= hi (wp[1].font_size);
644
645 if (h_version <= V3)
646 stat_height = hi (wp[7].font_size);
647
648 /* Cursor of upper window mustn't be swallowed by the lower window */
649
650 wp[1].y_cursor += wp[1].y_pos - 1 - stat_height;
651
652 wp[1].y_pos = 1 + stat_height;
653 wp[1].y_size = height;
654
655 if ((short) wp[1].y_cursor > (short) wp[1].y_size)
656 reset_cursor (1);
657
658 /* Cursor of lower window mustn't be swallowed by the upper window */
659
660 wp[0].y_cursor += wp[0].y_pos - 1 - stat_height - height;
661
662 wp[0].y_pos = 1 + stat_height + height;
663 wp[0].y_size = h_screen_height - stat_height - height;
664
665 if ((short) wp[0].y_cursor < 1)
666 reset_cursor (0);
667
668 /* Erase the upper window in V3 only */
669
670 if (h_version == V3 && height != 0)
671 erase_window (1);
672
673}/* split_window */
674
675/*
676 * erase_screen
677 *
678 * Erase the entire screen to background colour.
679 *
680 */
681
682static void erase_screen (zword win)
683{
684 int i;
685
686 os_erase_area (1, 1, h_screen_height, h_screen_width);
687
688 if ((short) win == -1) {
689 split_window (0);
690 set_window (0);
691 reset_cursor (0);
692 }
693
694 for (i = 0; i < 8; i++)
695 wp[i].line_count = 0;
696
697}/* erase_screen */
698
699/* #ifdef AMIGA */
700
701/*
702 * resize_screen
703 *
704 * Try to adapt the window properties to a new screen size.
705 *
706 */
707
708void resize_screen (void)
709{
710
711 if (h_version != V6) {
712
713 wp[0].x_size = h_screen_width;
714 wp[1].x_size = h_screen_width;
715 wp[7].x_size = h_screen_width;
716
717 wp[0].y_size = h_screen_height - wp[1].y_size - wp[7].y_size;
718
719 }
720
721}/* resize_screen */
722
723/* #endif */
724
725/*
726 * restart_screen
727 *
728 * Prepare the screen for a new game.
729 *
730 */
731
732void restart_screen (void)
733{
734
735 /* Use default settings */
736
737 os_set_colour (h_default_foreground, h_default_background);
738
739 if (os_font_data (TEXT_FONT, &font_height, &font_width))
740 os_set_font (TEXT_FONT);
741
742 os_set_text_style (0);
743
744 cursor = TRUE;
745
746 /* Initialise window properties */
747
748 mwin = 1;
749
750 for (cwp = wp; cwp < wp + 8; cwp++) {
751 cwp->y_pos = 1;
752 cwp->x_pos = 1;
753 cwp->y_size = 0;
754 cwp->x_size = 0;
755 cwp->y_cursor = 1;
756 cwp->x_cursor = 1;
757 cwp->left = 0;
758 cwp->right = 0;
759 cwp->nl_routine = 0;
760 cwp->nl_countdown = 0;
761 cwp->style = 0;
762 cwp->colour = (h_default_background << 8) | h_default_foreground;
763 cwp->font = TEXT_FONT;
764 cwp->font_size = (font_height << 8) | font_width;
765 cwp->attribute = 8;
766 }
767
768 /* Prepare lower/upper windows and status line */
769
770 wp[0].attribute = 15;
771
772 wp[0].left = f_setup.left_margin;
773 wp[0].right = f_setup.right_margin;
774
775 wp[0].x_size = h_screen_width;
776 wp[1].x_size = h_screen_width;
777
778 if (h_version <= V3)
779 wp[7].x_size = h_screen_width;
780
781 os_restart_game (RESTART_WPROP_SET);
782
783 /* Clear the screen, unsplit it and select window 0 */
784
785 erase_screen ((zword) (-1));
786
787}/* restart_screen */
788
789/*
790 * validate_click
791 *
792 * Return false if the last mouse click occured outside the current
793 * mouse window; otherwise write the mouse arrow coordinates to the
794 * memory of the header extension table and return true.
795 *
796 */
797
798bool validate_click (void)
799{
800
801 if (mwin >= 0) {
802
803 if (mouse_y < wp[mwin].y_pos || mouse_y >= wp[mwin].y_pos + wp[mwin].y_size)
804 return FALSE;
805 if (mouse_x < wp[mwin].x_pos || mouse_x >= wp[mwin].x_pos + wp[mwin].x_size)
806 return FALSE;
807
808 hx_mouse_y = mouse_y - wp[mwin].y_pos + 1;
809 hx_mouse_x = mouse_x - wp[mwin].x_pos + 1;
810
811 } else {
812
813 if (mouse_y < 1 || mouse_y > h_screen_height)
814 return FALSE;
815 if (mouse_x < 1 || mouse_x > h_screen_width)
816 return FALSE;
817
818 hx_mouse_y = mouse_y;
819 hx_mouse_x = mouse_x;
820
821 }
822
823 if (h_version != V6) {
824 hx_mouse_y = (hx_mouse_y - 1) / h_font_height + 1;
825 hx_mouse_x = (hx_mouse_x - 1) / h_font_width + 1;
826 }
827
828 set_header_extension (HX_MOUSE_Y, hx_mouse_y);
829 set_header_extension (HX_MOUSE_X, hx_mouse_x);
830
831 return TRUE;
832
833}/* validate_click */
834
835/*
836 * screen_mssg_on
837 *
838 * Start printing a so-called debugging message. The contents of the
839 * message are passed to the message stream, a Frotz specific output
840 * stream with maximum priority.
841 *
842 */
843
844void screen_mssg_on (void)
845{
846
847 if (cwin == 0) { /* messages in window 0 only */
848
849 os_set_text_style (0);
850
851 if (cwp->x_cursor != cwp->left + 1)
852 screen_new_line ();
853
854 screen_char (ZC_INDENT);
855
856 } else discarding = TRUE; /* discard messages in other windows */
857
858}/* screen_mssg_on */
859
860/*
861 * screen_mssg_off
862 *
863 * Stop printing a "debugging" message.
864 *
865 */
866
867void screen_mssg_off (void)
868{
869
870 if (cwin == 0) { /* messages in window 0 only */
871
872 screen_new_line ();
873
874 refresh_text_style ();
875
876 } else discarding = FALSE; /* message has been discarded */
877
878}/* screen_mssg_off */
879
880/*
881 * z_buffer_mode, turn text buffering on/off.
882 *
883 * zargs[0] = new text buffering flag (0 or 1)
884 *
885 */
886
887void z_buffer_mode (void)
888{
889
890 /* Infocom's V6 games rarely use the buffer_mode opcode. If they do
891 then only to print text immediately, without any delay. This was
892 used to give the player some sign of life while the game was
893 spending much time on parsing a complicated input line. (To turn
894 off word wrapping, V6 games use the window_style opcode instead.)
895 Today we can afford to ignore buffer_mode in V6. */
896
897 if (h_version != V6) {
898
899 flush_buffer ();
900
901 wp[0].attribute &= ~8;
902
903 if (zargs[0] != 0)
904 wp[0].attribute |= 8;
905
906 update_attributes ();
907
908 }
909
910}/* z_buffer_mode */
911
912/*
913 * z_draw_picture, draw a picture.
914 *
915 * zargs[0] = number of picture to draw
916 * zargs[1] = y-coordinate of top left corner
917 * zargs[2] = x-coordinate of top left corner
918 *
919 */
920
921void z_draw_picture (void)
922{
923 zword pic = zargs[0];
924
925 zword y = zargs[1];
926 zword x = zargs[2];
927
928 int i;
929
930 flush_buffer ();
931
932 if (y == 0) /* use cursor line if y-coordinate is 0 */
933 y = cwp->y_cursor;
934 if (x == 0) /* use cursor column if x-coordinate is 0 */
935 x = cwp->x_cursor;
936
937 y += cwp->y_pos - 1;
938 x += cwp->x_pos - 1;
939
940 /* The following is necessary to make Amiga and Macintosh story
941 files work with MCGA graphics files. Some screen-filling
942 pictures of the original Amiga release like the borders of
943 Zork Zero were split into several MCGA pictures (left, right
944 and top borders). We pretend this has not happened. */
945
946 for (i = 0; mapper[i].story_id != UNKNOWN; i++)
947
948 if (story_id == mapper[i].story_id && pic == mapper[i].pic) {
949
950 int height1, width1;
951 int height2, width2;
952
953 int delta = 0;
954
955 os_picture_data (pic, &height1, &width1);
956 os_picture_data (mapper[i].pic2, &height2, &width2);
957
958 if (story_id == ARTHUR && pic == 54)
959 delta = h_screen_width / 160;
960
961 os_draw_picture (mapper[i].pic1, y + height1, x + delta);
962 os_draw_picture (mapper[i].pic2, y + height1, x + width1 - width2 - delta);
963
964 }
965
966 os_draw_picture (pic, y, x);
967
968 if (story_id == SHOGUN)
969
970 if (pic == 3) {
971
972 int height, width;
973
974 os_picture_data (59, &height, &width);
975 os_draw_picture (59, y, h_screen_width - width + 1);
976
977 }
978
979}/* z_draw_picture */
980
981/*
982 * z_erase_line, erase the line starting at the cursor position.
983 *
984 * zargs[0] = 1 + #units to erase (1 clears to the end of the line)
985 *
986 */
987
988void z_erase_line (void)
989{
990 zword pixels = zargs[0];
991 zword y, x;
992
993 flush_buffer ();
994
995 /* Clipping at the right margin of the current window */
996
997 if (--pixels == 0 || pixels > units_left ())
998 pixels = units_left ();
999
1000 /* Erase from cursor position */
1001
1002 y = cwp->y_pos + cwp->y_cursor - 1;
1003 x = cwp->x_pos + cwp->x_cursor - 1;
1004
1005 os_erase_area (y, x, y + font_height - 1, x + pixels - 1);
1006
1007}/* z_erase_line */
1008
1009/*
1010 * z_erase_picture, erase a picture with background colour.
1011 *
1012 * zargs[0] = number of picture to erase
1013 * zargs[1] = y-coordinate of top left corner (optional)
1014 * zargs[2] = x-coordinate of top left corner (optional)
1015 *
1016 */
1017
1018void z_erase_picture (void)
1019{
1020 int height, width;
1021
1022 zword y = zargs[1];
1023 zword x = zargs[2];
1024
1025 flush_buffer ();
1026
1027 if (y == 0) /* use cursor line if y-coordinate is 0 */
1028 y = cwp->y_cursor;
1029 if (x == 0) /* use cursor column if x-coordinate is 0 */
1030 x = cwp->x_cursor;
1031
1032 os_picture_data (zargs[0], &height, &width);
1033
1034 y += cwp->y_pos - 1;
1035 x += cwp->x_pos - 1;
1036
1037 os_erase_area (y, x, y + height - 1, x + width - 1);
1038
1039}/* z_erase_picture */
1040
1041/*
1042 * z_erase_window, erase a window or the screen to background colour.
1043 *
1044 * zargs[0] = window (-3 current, -2 screen, -1 screen & unsplit)
1045 *
1046 */
1047
1048void z_erase_window (void)
1049{
1050
1051 flush_buffer ();
1052
1053 if ((short) zargs[0] == -1 || (short) zargs[0] == -2)
1054 erase_screen (zargs[0]);
1055 else
1056 erase_window (winarg0 ());
1057
1058}/* z_erase_window */
1059
1060/*
1061 * z_get_cursor, write the cursor coordinates into a table.
1062 *
1063 * zargs[0] = address to write information to
1064 *
1065 */
1066
1067void z_get_cursor (void)
1068{
1069 zword y, x;
1070
1071 flush_buffer ();
1072
1073 y = cwp->y_cursor;
1074 x = cwp->x_cursor;
1075
1076 if (h_version != V6) { /* convert to grid positions */
1077 y = (y - 1) / h_font_height + 1;
1078 x = (x - 1) / h_font_width + 1;
1079 }
1080
1081 storew ((zword) (zargs[0] + 0), y);
1082 storew ((zword) (zargs[0] + 2), x);
1083
1084}/* z_get_cursor */
1085
1086/*
1087 * z_get_wind_prop, store the value of a window property.
1088 *
1089 * zargs[0] = window (-3 is the current one)
1090 * zargs[1] = number of window property to be stored
1091 *
1092 */
1093
1094void z_get_wind_prop (void)
1095{
1096
1097 flush_buffer ();
1098
1099 if (zargs[1] >= 16)
1100 runtime_error (ERR_ILL_WIN_PROP);
1101
1102 store (((zword *) (wp + winarg0 ())) [zargs[1]]);
1103
1104}/* z_get_wind_prop */
1105
1106/*
1107 * z_mouse_window, select a window as mouse window.
1108 *
1109 * zargs[0] = window number (-3 is the current) or -1 for the screen
1110 *
1111 */
1112
1113void z_mouse_window (void)
1114{
1115
1116 mwin = ((short) zargs[0] == -1) ? -1 : winarg0 ();
1117
1118}/* z_mouse_window */
1119
1120/*
1121 * z_move_window, place a window on the screen.
1122 *
1123 * zargs[0] = window (-3 is the current one)
1124 * zargs[1] = y-coordinate
1125 * zargs[2] = x-coordinate
1126 *
1127 */
1128
1129void z_move_window (void)
1130{
1131 zword win = winarg0 ();
1132
1133 flush_buffer ();
1134
1135 wp[win].y_pos = zargs[1];
1136 wp[win].x_pos = zargs[2];
1137
1138 if (win == cwin)
1139 update_cursor ();
1140
1141}/* z_move_window */
1142
1143/*
1144 * z_picture_data, get information on a picture or the graphics file.
1145 *
1146 * zargs[0] = number of picture or 0 for the graphics file
1147 * zargs[1] = address to write information to
1148 *
1149 */
1150
1151void z_picture_data (void)
1152{
1153 zword pic = zargs[0];
1154 zword table = zargs[1];
1155
1156 int height, width;
1157 int i;
1158
1159 bool avail = os_picture_data (pic, &height, &width);
1160
1161 for (i = 0; mapper[i].story_id != UNKNOWN; i++)
1162
1163 if (story_id == mapper[i].story_id) {
1164
1165 if (pic == mapper[i].pic) {
1166
1167 int height2, width2;
1168
1169 avail &= os_picture_data (mapper[i].pic1, &height2, &width2);
1170 avail &= os_picture_data (mapper[i].pic2, &height2, &width2);
1171
1172 height += height2;
1173
1174 } else if (pic == mapper[i].pic1 || pic == mapper[i].pic2)
1175
1176 avail = FALSE;
1177 }
1178
1179 storew ((zword) (table + 0), (zword) (height));
1180 storew ((zword) (table + 2), (zword) (width));
1181
1182 branch (avail);
1183
1184}/* z_picture_data */
1185
1186/*
1187 * z_picture_table, prepare a group of pictures for faster display.
1188 *
1189 * zargs[0] = address of table holding the picture numbers
1190 *
1191 */
1192
1193void z_picture_table (void)
1194{
1195
1196 /* This opcode is used by Shogun and Zork Zero when the player
1197 encounters built-in games such as Peggleboz. Nowadays it is
1198 not very helpful to hold the picture data in memory because
1199 even a small disk cache avoids re-loading of data. */
1200
1201}/* z_picture_table */
1202
1203/*
1204 * z_print_table, print ASCII text in a rectangular area.
1205 *
1206 * zargs[0] = address of text to be printed
1207 * zargs[1] = width of rectangular area
1208 * zargs[2] = height of rectangular area (optional)
1209 * zargs[3] = number of char's to skip between lines (optional)
1210 *
1211 */
1212
1213void z_print_table (void)
1214{
1215 zword addr = zargs[0];
1216 zword x;
1217 int i, j;
1218
1219 flush_buffer ();
1220
1221 /* Supply default arguments */
1222
1223 if (zargc < 3)
1224 zargs[2] = 1;
1225 if (zargc < 4)
1226 zargs[3] = 0;
1227
1228 /* Write text in width x height rectangle */
1229
1230 x = cwp->x_cursor;
1231
1232 for (i = 0; i < zargs[2]; i++) {
1233
1234 if (i != 0) {
1235
1236 flush_buffer ();
1237
1238 cwp->y_cursor += font_height;
1239 cwp->x_cursor = x;
1240
1241 update_cursor ();
1242
1243 }
1244
1245 for (j = 0; j < zargs[1]; j++) {
1246
1247 zbyte c;
1248
1249 LOW_BYTE (addr, c)
1250 addr++;
1251
1252 print_char (c);
1253
1254 }
1255
1256 addr += zargs[3];
1257
1258 }
1259
1260}/* z_print_table */
1261
1262/*
1263 * z_put_wind_prop, set the value of a window property.
1264 *
1265 * zargs[0] = window (-3 is the current one)
1266 * zargs[1] = number of window property to set
1267 * zargs[2] = value to set window property to
1268 *
1269 */
1270
1271void z_put_wind_prop (void)
1272{
1273
1274 flush_buffer ();
1275
1276 if (zargs[1] >= 16)
1277 runtime_error (ERR_ILL_WIN_PROP);
1278
1279 ((zword *) (wp + winarg0 ())) [zargs[1]] = zargs[2];
1280
1281}/* z_put_wind_prop */
1282
1283/*
1284 * z_scroll_window, scroll a window up or down.
1285 *
1286 * zargs[0] = window (-3 is the current one)
1287 * zargs[1] = #screen units to scroll up (positive) or down (negative)
1288 *
1289 */
1290
1291void z_scroll_window (void)
1292{
1293 zword win = winarg0 ();
1294 zword y, x;
1295
1296 flush_buffer ();
1297
1298 /* Use the correct set of colours when scrolling the window */
1299
1300 if (win != cwin && h_interpreter_number != INTERP_AMIGA)
1301 os_set_colour (lo (wp[win].colour), hi (wp[win].colour));
1302
1303 y = wp[win].y_pos;
1304 x = wp[win].x_pos;
1305
1306 os_scroll_area (y,
1307 x,
1308 y + wp[win].y_size - 1,
1309 x + wp[win].x_size - 1,
1310 (short) zargs[1]);
1311
1312 if (win != cwin && h_interpreter_number != INTERP_AMIGA)
1313 os_set_colour (lo (cwp->colour), hi (cwp->colour));
1314
1315}/* z_scroll_window */
1316
1317/*
1318 * z_set_colour, set the foreground and background colours.
1319 *
1320 * zargs[0] = foreground colour
1321 * zargs[1] = background colour
1322 * zargs[2] = window (-3 is the current one, optional)
1323 *
1324 */
1325
1326void z_set_colour (void)
1327{
1328 zword win = (h_version == V6) ? winarg2 () : 0;
1329
1330 zword fg = zargs[0];
1331 zword bg = zargs[1];
1332
1333 flush_buffer ();
1334
1335 if ((short) fg == -1) /* colour -1 is the colour at the cursor */
1336 fg = os_peek_colour ();
1337 if ((short) bg == -1)
1338 bg = os_peek_colour ();
1339
1340 if (fg == 0) /* colour 0 means keep current colour */
1341 fg = lo (wp[win].colour);
1342 if (bg == 0)
1343 bg = hi (wp[win].colour);
1344
1345 if (fg == 1) /* colour 1 is the system default colour */
1346 fg = h_default_foreground;
1347 if (bg == 1)
1348 bg = h_default_background;
1349
1350 if (h_version == V6 && h_interpreter_number == INTERP_AMIGA)
1351
1352 /* Changing colours of window 0 affects the entire screen */
1353
1354 if (win == 0) {
1355
1356 int i;
1357
1358 for (i = 1; i < 8; i++) {
1359
1360 zword bg2 = hi (wp[i].colour);
1361 zword fg2 = lo (wp[i].colour);
1362
1363 if (bg2 < 16)
1364 bg2 = (bg2 == lo (wp[0].colour)) ? fg : bg;
1365 if (fg2 < 16)
1366 fg2 = (fg2 == lo (wp[0].colour)) ? fg : bg;
1367
1368 wp[i].colour = (bg2 << 8) | fg2;
1369
1370 }
1371
1372 }
1373
1374 wp[win].colour = (bg << 8) | fg;
1375
1376 if (win == cwin || h_version != V6)
1377 os_set_colour (fg, bg);
1378
1379}/* z_set_colour */
1380
1381/*
1382 * z_set_font, set the font for text output and store the previous font.
1383 *
1384 * zargs[0] = number of font or 0 to keep current font
1385 *
1386 */
1387
1388void z_set_font (void)
1389{
1390 zword win = (h_version == V6) ? cwin : 0;
1391 zword font = zargs[0];
1392
1393 if (font != 0) {
1394
1395 if (os_font_data (font, &font_height, &font_width)) {
1396
1397 store (wp[win].font);
1398
1399 wp[win].font = font;
1400 wp[win].font_size = (font_height << 8) | font_width;
1401
1402 if (!ostream_memory && ostream_screen && enable_buffering) {
1403
1404 print_char (ZC_NEW_FONT);
1405 print_char (font);
1406
1407 } else os_set_font (font);
1408
1409 } else store (0);
1410
1411 } else store (wp[win].font);
1412
1413}/* z_set_font */
1414
1415/*
1416 * z_set_cursor, set the cursor position or turn the cursor on/off.
1417 *
1418 * zargs[0] = y-coordinate or -2/-1 for cursor on/off
1419 * zargs[1] = x-coordinate
1420 * zargs[2] = window (-3 is the current one, optional)
1421 *
1422 */
1423
1424void z_set_cursor (void)
1425{
1426 zword win = (h_version == V6) ? winarg2 () : 1;
1427
1428 zword y = zargs[0];
1429 zword x = zargs[1];
1430
1431 flush_buffer ();
1432
1433 /* Supply default arguments */
1434
1435 if (zargc < 3)
1436 zargs[2] = -3;
1437
1438 /* Handle cursor on/off */
1439
1440 if ((short) y < 0) {
1441
1442 if ((short) y == -2)
1443 cursor = TRUE;
1444 if ((short) y == -1)
1445 cursor = FALSE;
1446
1447 return;
1448
1449 }
1450
1451 /* Convert grid positions to screen units if this is not V6 */
1452
1453 if (h_version != V6) {
1454
1455 if (cwin == 0)
1456 return;
1457
1458 y = (y - 1) * h_font_height + 1;
1459 x = (x - 1) * h_font_width + 1;
1460
1461 }
1462
1463 /* Protect the margins */
1464
1465 if (y == 0) /* use cursor line if y-coordinate is 0 */
1466 y = wp[win].y_cursor;
1467 if (x == 0) /* use cursor column if x-coordinate is 0 */
1468 x = wp[win].x_cursor;
1469 if (x <= wp[win].left || x > wp[win].x_size - wp[win].right)
1470 x = wp[win].left + 1;
1471
1472 /* Move the cursor */
1473
1474 wp[win].y_cursor = y;
1475 wp[win].x_cursor = x;
1476
1477 if (win == cwin)
1478 update_cursor ();
1479
1480}/* z_set_cursor */
1481
1482/*
1483 * z_set_margins, set the left and right margins of a window.
1484 *
1485 * zargs[0] = left margin in pixels
1486 * zargs[1] = right margin in pixels
1487 * zargs[2] = window (-3 is the current one, optional)
1488 *
1489 */
1490
1491void z_set_margins (void)
1492{
1493 zword win = winarg2 ();
1494
1495 flush_buffer ();
1496
1497 wp[win].left = zargs[0];
1498 wp[win].right = zargs[1];
1499
1500 /* Protect the margins */
1501
1502 if (wp[win].x_cursor <= zargs[0] || wp[win].x_cursor > wp[win].x_size - zargs[1]) {
1503
1504 wp[win].x_cursor = zargs[0] + 1;
1505
1506 if (win == cwin)
1507 update_cursor ();
1508
1509 }
1510
1511}/* z_set_margins */
1512
1513/*
1514 * z_set_text_style, set the style for text output.
1515 *
1516 * zargs[0] = style flags to set or 0 to reset text style
1517 *
1518 */
1519
1520void z_set_text_style (void)
1521{
1522 zword win = (h_version == V6) ? cwin : 0;
1523 zword style = zargs[0];
1524
1525 wp[win].style |= style;
1526
1527 if (style == 0)
1528 wp[win].style = 0;
1529
1530 refresh_text_style ();
1531
1532}/* z_set_text_style */
1533
1534/*
1535 * z_set_window, select the current window.
1536 *
1537 * zargs[0] = window to be selected (-3 is the current one)
1538 *
1539 */
1540
1541void z_set_window (void)
1542{
1543
1544 set_window (winarg0 ());
1545
1546}/* z_set_window */
1547
1548/*
1549 * pad_status_line
1550 *
1551 * Pad the status line with spaces up to the given position.
1552 *
1553 */
1554
1555static void pad_status_line (int column)
1556{
1557 int spaces;
1558
1559 flush_buffer ();
1560
1561 spaces = units_left () / os_char_width (' ') - column;
1562
1563 /* while (spaces--) */
1564 /* Justin Wesley's fix for narrow displays (Agenda PDA) */
1565 while (spaces-- > 0)
1566 screen_char (' ');
1567
1568}/* pad_status_line */
1569
1570/*
1571 * z_show_status, display the status line for V1 to V3 games.
1572 *
1573 * no zargs used
1574 *
1575 */
1576
1577void z_show_status (void)
1578{
1579 zword global0;
1580 zword global1;
1581 zword global2;
1582 zword addr;
1583
1584 bool brief = FALSE;
1585
1586 /* One V5 game (Wishbringer Solid Gold) contains this opcode by
1587 accident, so just return if the version number does not fit */
1588
1589 if (h_version >= V4)
1590 return;
1591
1592 /* Read all relevant global variables from the memory of the
1593 Z-machine into local variables */
1594
1595 addr = h_globals;
1596 LOW_WORD (addr, global0)
1597 addr += 2;
1598 LOW_WORD (addr, global1)
1599 addr += 2;
1600 LOW_WORD (addr, global2)
1601
1602 /* Frotz uses window 7 for the status line. Don't forget to select
1603 reverse and fixed width text style */
1604
1605 set_window (7);
1606
1607 print_char (ZC_NEW_STYLE);
1608 print_char (REVERSE_STYLE | FIXED_WIDTH_STYLE);
1609
1610 /* If the screen width is below 55 characters then we have to use
1611 the brief status line format */
1612
1613 if (h_screen_cols < 55)
1614 brief = TRUE;
1615
1616 /* Print the object description for the global variable 0 */
1617
1618 print_char (' ');
1619 print_object (global0);
1620
1621 /* A header flag tells us whether we have to display the current
1622 time or the score/moves information */
1623
1624 if (h_config & CONFIG_TIME) { /* print hours and minutes */
1625
1626 zword hours = (global1 + 11) % 12 + 1;
1627
1628 pad_status_line (brief ? 15 : 20);
1629
1630 print_string ("Time: ");
1631
1632 if (hours < 10)
1633 print_char (' ');
1634 print_num (hours);
1635
1636 print_char (':');
1637
1638 if (global2 < 10)
1639 print_char ('0');
1640 print_num (global2);
1641
1642 print_char (' ');
1643
1644 print_char ((global1 >= 12) ? 'p' : 'a');
1645 print_char ('m');
1646
1647 } else { /* print score and moves */
1648
1649 pad_status_line (brief ? 15 : 30);
1650
1651 print_string (brief ? "S: " : "Score: ");
1652 print_num (global1);
1653
1654 pad_status_line (brief ? 8 : 14);
1655
1656 print_string (brief ? "M: " : "Moves: ");
1657 print_num (global2);
1658
1659 }
1660
1661 /* Pad the end of the status line with spaces */
1662
1663 pad_status_line (0);
1664
1665 /* Return to the lower window */
1666
1667 set_window (0);
1668
1669}/* z_show_status */
1670
1671/*
1672 * z_split_window, split the screen into an upper (1) and lower (0) window.
1673 *
1674 * zargs[0] = height of upper window in screen units (V6) or #lines
1675 *
1676 */
1677
1678void z_split_window (void)
1679{
1680
1681 split_window (zargs[0]);
1682
1683}/* z_split_window */
1684
1685/*
1686 * z_window_size, change the width and height of a window.
1687 *
1688 * zargs[0] = window (-3 is the current one)
1689 * zargs[1] = new height in screen units
1690 * zargs[2] = new width in screen units
1691 *
1692 */
1693
1694void z_window_size (void)
1695{
1696 zword win = winarg0 ();
1697
1698 flush_buffer ();
1699
1700 wp[win].y_size = zargs[1];
1701 wp[win].x_size = zargs[2];
1702
1703 /* Keep the cursor within the window */
1704
1705 if (wp[win].y_cursor > zargs[1] || wp[win].x_cursor > zargs[2])
1706 reset_cursor (win);
1707
1708}/* z_window_size */
1709
1710/*
1711 * z_window_style, set / clear / toggle window attributes.
1712 *
1713 * zargs[0] = window (-3 is the current one)
1714 * zargs[1] = window attribute flags
1715 * zargs[2] = operation to perform (optional, defaults to 0)
1716 *
1717 */
1718
1719void z_window_style (void)
1720{
1721 zword win = winarg0 ();
1722 zword flags = zargs[1];
1723
1724 flush_buffer ();
1725
1726 /* Supply default arguments */
1727
1728 if (zargc < 3)
1729 zargs[2] = 0;
1730
1731 /* Set window style */
1732
1733 switch (zargs[2]) {
1734 case 0: wp[win].attribute = flags; break;
1735 case 1: wp[win].attribute |= flags; break;
1736 case 2: wp[win].attribute &= ~flags; break;
1737 case 3: wp[win].attribute ^= flags; break;
1738 }
1739
1740 if (cwin == win)
1741 update_attributes ();
1742
1743}/* z_window_style */
diff --git a/apps/plugins/frotz/setup.h b/apps/plugins/frotz/setup.h
new file mode 100644
index 0000000000..a9b9360122
--- /dev/null
+++ b/apps/plugins/frotz/setup.h
@@ -0,0 +1,67 @@
1/*
2 * Various status thingies for the interpreter and interface.
3 *
4 */
5
6typedef struct frotz_setup_struct {
7 int attribute_assignment; /* done */
8 int attribute_testing; /* done */
9 int context_lines; /* done */
10 int object_locating; /* done */
11 int object_movement; /* done */
12 int left_margin; /* done */
13 int right_margin; /* done */
14 int ignore_errors; /* done */
15 int interpreter_number; /* Just dumb frotz now */
16 int piracy; /* done */
17 int undo_slots; /* done */
18 int expand_abbreviations; /* done */
19 int script_cols; /* done */
20 int save_quetzal; /* done */
21 int sound; /* done */
22 int err_report_mode; /* done */
23} f_setup_t;
24
25extern f_setup_t f_setup;
26
27
28typedef struct zcode_header_struct {
29 zbyte h_version;
30 zbyte h_config;
31 zword h_release;
32 zword h_resident_size;
33 zword h_start_pc;
34 zword h_dictionary;
35 zword h_objects;
36 zword h_globals;
37 zword h_dynamic_size;
38 zword h_flags;
39 zbyte h_serial[6];
40 zword h_abbreviations;
41 zword h_file_size;
42 zword h_checksum;
43 zbyte h_interpreter_number;
44 zbyte h_interpreter_version;
45 zbyte h_screen_rows;
46 zbyte h_screen_cols;
47 zword h_screen_width;
48 zword h_screen_height;
49 zbyte h_font_height;
50 zbyte h_font_width;
51 zword h_functions_offset;
52 zword h_strings_offset;
53 zbyte h_default_background;
54 zbyte h_default_foreground;
55 zword h_terminating_keys;
56 zword h_line_width;
57 zbyte h_standard_high;
58 zbyte h_standard_low;
59 zword h_alphabet;
60 zword h_extension_table;
61 zbyte h_user_name[8];
62
63 zword hx_table_size;
64 zword hx_mouse_x;
65 zword hx_mouse_y;
66 zword hx_unicode_table;
67} z_header_t;
diff --git a/apps/plugins/frotz/sound.c b/apps/plugins/frotz/sound.c
new file mode 100644
index 0000000000..ea0e3570e6
--- /dev/null
+++ b/apps/plugins/frotz/sound.c
@@ -0,0 +1,204 @@
1/* sound.c - Sound effect function
2 * Copyright (c) 1995-1997 Stefan Jokisch
3 *
4 * This file is part of Frotz.
5 *
6 * Frotz is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * Frotz is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
19 */
20
21#include "frotz.h"
22
23#ifdef DJGPP
24#include "djfrotz.h"
25#endif
26
27#define EFFECT_PREPARE 1
28#define EFFECT_PLAY 2
29#define EFFECT_STOP 3
30#define EFFECT_FINISH_WITH 4
31
32extern int direct_call (zword);
33
34static zword routine = 0;
35
36static int next_sample = 0;
37static int next_volume = 0;
38
39static bool locked = FALSE;
40static bool playing = FALSE;
41
42/*
43 * init_sound
44 *
45 * Initialize sound variables.
46 *
47 */
48
49void init_sound (void)
50{
51 locked = FALSE;
52 playing = FALSE;
53} /* init_sound */
54
55
56/*
57 * start_sample
58 *
59 * Call the IO interface to play a sample.
60 *
61 */
62
63static void start_sample (int number, int volume, int repeats, zword eos)
64{
65
66 static zbyte lh_repeats[] = {
67 0x00, 0x00, 0x00, 0x01, 0xff,
68 0x00, 0x01, 0x01, 0x01, 0x01,
69 0xff, 0x01, 0x01, 0xff, 0x00,
70 0xff, 0xff, 0xff, 0xff, 0xff
71 };
72
73 if (story_id == LURKING_HORROR)
74 repeats = lh_repeats[number];
75
76 os_start_sample (number, volume, repeats, eos);
77
78 routine = eos;
79 playing = TRUE;
80
81}/* start_sample */
82
83/*
84 * start_next_sample
85 *
86 * Play a sample that has been delayed until the previous sound effect has
87 * finished. This is necessary for two samples in The Lurking Horror that
88 * immediately follow other samples.
89 *
90 */
91
92static void start_next_sample (void)
93{
94
95 if (next_sample != 0)
96 start_sample (next_sample, next_volume, 0, 0);
97
98 next_sample = 0;
99 next_volume = 0;
100
101}/* start_next_sample */
102
103/*
104 * end_of_sound
105 *
106 * Call the Z-code routine which was given as the last parameter of
107 * a sound_effect call. This function may be called from a hardware
108 * interrupt (which requires extremely careful programming).
109 *
110 */
111
112void end_of_sound (void)
113{
114
115#if defined(DJGPP) && defined(SOUND_SUPPORT)
116 end_of_sound_flag = 0;
117#endif
118
119 playing = FALSE;
120
121 if (!locked) {
122
123 if (story_id == LURKING_HORROR)
124 start_next_sample ();
125
126 direct_call (routine);
127
128 }
129
130}/* end_of_sound */
131
132/*
133 * z_sound_effect, load / play / stop / discard a sound effect.
134 *
135 * zargs[0] = number of bleep (1 or 2) or sample
136 * zargs[1] = operation to perform (samples only)
137 * zargs[2] = repeats and volume (play sample only)
138 * zargs[3] = end-of-sound routine (play sample only, optional)
139 *
140 * Note: Volumes range from 1 to 8, volume 255 is the default volume.
141 * Repeats are stored in the high byte, 255 is infinite loop.
142 *
143 */
144
145void z_sound_effect (void)
146{
147 zword number = zargs[0];
148 zword effect = zargs[1];
149 zword volume = zargs[2];
150
151 /* By default play sound 1 at volume 8 */
152 if (zargc < 1)
153 number = 1;
154 if (zargc < 2)
155 effect = EFFECT_PLAY;
156 if (zargc < 3)
157 volume = 8;
158
159 if (number >= 3 || number == 0) {
160
161 locked = TRUE;
162
163 if (story_id == LURKING_HORROR && (number == 9 || number == 16)) {
164
165 if (effect == EFFECT_PLAY) {
166
167 next_sample = number;
168 next_volume = volume;
169
170 locked = FALSE;
171
172 if (!playing)
173 start_next_sample ();
174
175 } else locked = FALSE;
176
177 return;
178
179 }
180
181 playing = FALSE;
182
183 switch (effect) {
184
185 case EFFECT_PREPARE:
186 os_prepare_sample (number);
187 break;
188 case EFFECT_PLAY:
189 start_sample (number, lo (volume), hi (volume), (zargc == 4) ? zargs[3] : 0);
190 break;
191 case EFFECT_STOP:
192 os_stop_sample (number);
193 break;
194 case EFFECT_FINISH_WITH:
195 os_finish_with_sample (number);
196 break;
197
198 }
199
200 locked = FALSE;
201
202 } else os_beep (number);
203
204}/* z_sound_effect */
diff --git a/apps/plugins/frotz/stream.c b/apps/plugins/frotz/stream.c
new file mode 100644
index 0000000000..4ccb44451c
--- /dev/null
+++ b/apps/plugins/frotz/stream.c
@@ -0,0 +1,365 @@
1/* stream.c - IO stream implementation
2 * Copyright (c) 1995-1997 Stefan Jokisch
3 *
4 * This file is part of Frotz.
5 *
6 * Frotz is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * Frotz is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
19 */
20
21#include "frotz.h"
22
23extern bool handle_hot_key (zchar);
24
25extern bool validate_click (void);
26
27extern void replay_open (void);
28extern void replay_close (void);
29extern void memory_open (zword, zword, bool);
30extern void memory_close (void);
31extern void record_open (void);
32extern void record_close (void);
33extern void script_open (void);
34extern void script_close (void);
35
36extern void memory_word (const zchar *);
37extern void memory_new_line (void);
38extern void record_write_key (zchar);
39extern void record_write_input (const zchar *, zchar);
40extern void script_char (zchar);
41extern void script_word (const zchar *);
42extern void script_new_line (void);
43extern void script_write_input (const zchar *, zchar);
44extern void script_erase_input (const zchar *);
45extern void script_mssg_on (void);
46extern void script_mssg_off (void);
47extern void screen_char (zchar);
48extern void screen_word (const zchar *);
49extern void screen_new_line (void);
50extern void screen_write_input (const zchar *, zchar);
51extern void screen_erase_input (const zchar *);
52extern void screen_mssg_on (void);
53extern void screen_mssg_off (void);
54
55extern zchar replay_read_key (void);
56extern zchar replay_read_input (zchar *);
57extern zchar console_read_key (zword);
58extern zchar console_read_input (int, zchar *, zword, bool);
59
60extern int direct_call (zword);
61
62/*
63 * stream_mssg_on
64 *
65 * Start printing a "debugging" message.
66 *
67 */
68
69void stream_mssg_on (void)
70{
71
72 flush_buffer ();
73
74 if (ostream_screen)
75 screen_mssg_on ();
76 if (ostream_script && enable_scripting)
77 script_mssg_on ();
78
79 message = TRUE;
80
81}/* stream_mssg_on */
82
83/*
84 * stream_mssg_off
85 *
86 * Stop printing a "debugging" message.
87 *
88 */
89
90void stream_mssg_off (void)
91{
92
93 flush_buffer ();
94
95 if (ostream_screen)
96 screen_mssg_off ();
97 if (ostream_script && enable_scripting)
98 script_mssg_off ();
99
100 message = FALSE;
101
102}/* stream_mssg_off */
103
104/*
105 * z_output_stream, open or close an output stream.
106 *
107 * zargs[0] = stream to open (positive) or close (negative)
108 * zargs[1] = address to redirect output to (stream 3 only)
109 * zargs[2] = width of redirected output (stream 3 only, optional)
110 *
111 */
112
113void z_output_stream (void)
114{
115
116 flush_buffer ();
117
118 switch ((short) zargs[0]) {
119
120 case 1: ostream_screen = TRUE;
121 break;
122 case -1: ostream_screen = FALSE;
123 break;
124 case 2: if (!ostream_script) script_open ();
125 break;
126 case -2: if (ostream_script) script_close ();
127 break;
128 case 3: memory_open (zargs[1], zargs[2], zargc >= 3);
129 break;
130 case -3: memory_close ();
131 break;
132 case 4: if (!ostream_record) record_open ();
133 break;
134 case -4: if (ostream_record) record_close ();
135 break;
136
137 }
138
139}/* z_output_stream */
140
141/*
142 * stream_char
143 *
144 * Send a single character to the output stream.
145 *
146 */
147
148void stream_char (zchar c)
149{
150
151 if (ostream_screen)
152 screen_char (c);
153 if (ostream_script && enable_scripting)
154 script_char (c);
155
156}/* stream_char */
157
158/*
159 * stream_word
160 *
161 * Send a string of characters to the output streams.
162 *
163 */
164
165void stream_word (const zchar *s)
166{
167
168 if (ostream_memory && !message)
169
170 memory_word (s);
171
172 else {
173
174 if (ostream_screen)
175 screen_word (s);
176 if (ostream_script && enable_scripting)
177 script_word (s);
178
179 }
180
181}/* stream_word */
182
183/*
184 * stream_new_line
185 *
186 * Send a newline to the output streams.
187 *
188 */
189
190void stream_new_line (void)
191{
192
193 if (ostream_memory && !message)
194
195 memory_new_line ();
196
197 else {
198
199 if (ostream_screen)
200 screen_new_line ();
201 if (ostream_script && enable_scripting)
202 script_new_line ();
203
204 }
205
206}/* stream_new_line */
207
208/*
209 * z_input_stream, select an input stream.
210 *
211 * zargs[0] = input stream to be selected
212 *
213 */
214
215void z_input_stream (void)
216{
217
218 flush_buffer ();
219
220 if (zargs[0] == 0 && istream_replay)
221 replay_close ();
222 if (zargs[0] == 1 && !istream_replay)
223 replay_open ();
224
225}/* z_input_stream */
226
227/*
228 * stream_read_key
229 *
230 * Read a single keystroke from the current input stream.
231 *
232 */
233
234zchar stream_read_key ( zword timeout, zword routine,
235 bool hot_keys )
236{
237 zchar key = ZC_BAD;
238
239 flush_buffer ();
240
241 /* Read key from current input stream */
242
243continue_input:
244
245 do {
246
247 if (istream_replay)
248 key = replay_read_key ();
249 else
250 key = console_read_key (timeout);
251
252 } while (key == ZC_BAD);
253
254 /* Verify mouse clicks */
255
256 if (key == ZC_SINGLE_CLICK || key == ZC_DOUBLE_CLICK)
257 if (!validate_click ())
258 goto continue_input;
259
260 /* Copy key to the command file */
261
262 if (ostream_record && !istream_replay)
263 record_write_key (key);
264
265 /* Handle timeouts */
266
267 if (key == ZC_TIME_OUT)
268 if (direct_call (routine) == 0)
269 goto continue_input;
270
271 /* Handle hot keys */
272
273 if (hot_keys && key >= ZC_HKEY_MIN && key <= ZC_HKEY_MAX) {
274
275 if (h_version == V4 && key == ZC_HKEY_UNDO)
276 goto continue_input;
277 if (!handle_hot_key (key))
278 goto continue_input;
279
280 return ZC_BAD;
281
282 }
283
284 /* Return key */
285
286 return key;
287
288}/* stream_read_key */
289
290/*
291 * stream_read_input
292 *
293 * Read a line of input from the current input stream.
294 *
295 */
296
297zchar stream_read_input ( int max, zchar *buf,
298 zword timeout, zword routine,
299 bool hot_keys,
300 bool no_scripting )
301{
302 zchar key = ZC_BAD;
303
304 flush_buffer ();
305
306 /* Remove initial input from the transscript file or from the screen */
307
308 if (ostream_script && enable_scripting && !no_scripting)
309 script_erase_input (buf);
310 if (istream_replay)
311 screen_erase_input (buf);
312
313 /* Read input line from current input stream */
314
315continue_input:
316
317 do {
318
319 if (istream_replay)
320 key = replay_read_input (buf);
321 else
322 key = console_read_input (max, buf, timeout, key != ZC_BAD);
323
324 } while (key == ZC_BAD);
325
326 /* Verify mouse clicks */
327
328 if (key == ZC_SINGLE_CLICK || key == ZC_DOUBLE_CLICK)
329 if (!validate_click ())
330 goto continue_input;
331
332 /* Copy input line to the command file */
333
334 if (ostream_record && !istream_replay)
335 record_write_input (buf, key);
336
337 /* Handle timeouts */
338
339 if (key == ZC_TIME_OUT)
340 if (direct_call (routine) == 0)
341 goto continue_input;
342
343 /* Handle hot keys */
344
345 if (hot_keys && key >= ZC_HKEY_MIN && key <= ZC_HKEY_MAX) {
346
347 if (!handle_hot_key (key))
348 goto continue_input;
349
350 return ZC_BAD;
351
352 }
353
354 /* Copy input line to transscript file or to the screen */
355
356 if (ostream_script && enable_scripting && !no_scripting)
357 script_write_input (buf, key);
358 if (istream_replay)
359 screen_write_input (buf, key);
360
361 /* Return terminating key */
362
363 return key;
364
365}/* stream_read_input */
diff --git a/apps/plugins/frotz/table.c b/apps/plugins/frotz/table.c
new file mode 100644
index 0000000000..eb3a16366d
--- /dev/null
+++ b/apps/plugins/frotz/table.c
@@ -0,0 +1,193 @@
1/* table.c - Table handling opcodes
2 * Copyright (c) 1995-1997 Stefan Jokisch
3 *
4 * This file is part of Frotz.
5 *
6 * Frotz is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * Frotz is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
19 */
20
21#include "frotz.h"
22
23/*
24 * z_copy_table, copy a table or fill it with zeroes.
25 *
26 * zargs[0] = address of table
27 * zargs[1] = destination address or 0 for fill
28 * zargs[2] = size of table
29 *
30 * Note: Copying is safe even when source and destination overlap; but
31 * if zargs[1] is negative the table _must_ be copied forwards.
32 *
33 */
34
35void z_copy_table (void)
36{
37 zword addr;
38 zword size = zargs[2];
39 zbyte value;
40 int i;
41
42 if (zargs[1] == 0) /* zero table */
43
44 for (i = 0; i < size; i++)
45 storeb ((zword) (zargs[0] + i), 0);
46
47 else if ((short) size < 0 || zargs[0] > zargs[1]) /* copy forwards */
48
49 for (i = 0; i < (((short) size < 0) ? - (short) size : size); i++) {
50 addr = zargs[0] + i;
51 LOW_BYTE (addr, value)
52 storeb ((zword) (zargs[1] + i), value);
53 }
54
55 else /* copy backwards */
56
57 for (i = size - 1; i >= 0; i--) {
58 addr = zargs[0] + i;
59 LOW_BYTE (addr, value)
60 storeb ((zword) (zargs[1] + i), value);
61 }
62
63}/* z_copy_table */
64
65/*
66 * z_loadb, store a value from a table of bytes.
67 *
68 * zargs[0] = address of table
69 * zargs[1] = index of table entry to store
70 *
71 */
72
73void z_loadb (void)
74{
75 zword addr = zargs[0] + zargs[1];
76 zbyte value;
77
78 LOW_BYTE (addr, value)
79
80 store (value);
81
82}/* z_loadb */
83
84/*
85 * z_loadw, store a value from a table of words.
86 *
87 * zargs[0] = address of table
88 * zargs[1] = index of table entry to store
89 *
90 */
91
92void z_loadw (void)
93{
94 zword addr = zargs[0] + 2 * zargs[1];
95 zword value;
96
97 LOW_WORD (addr, value)
98
99 store (value);
100
101}/* z_loadw */
102
103/*
104 * z_scan_table, find and store the address of a target within a table.
105 *
106 * zargs[0] = target value to be searched for
107 * zargs[1] = address of table
108 * zargs[2] = number of table entries to check value against
109 * zargs[3] = type of table (optional, defaults to 0x82)
110 *
111 * Note: The table is a word array if bit 7 of zargs[3] is set; otherwise
112 * it's a byte array. The lower bits hold the address step.
113 *
114 */
115
116void z_scan_table (void)
117{
118 zword addr = zargs[1];
119 int i;
120
121 /* Supply default arguments */
122
123 if (zargc < 4)
124 zargs[3] = 0x82;
125
126 /* Scan byte or word array */
127
128 for (i = 0; i < zargs[2]; i++) {
129
130 if (zargs[3] & 0x80) { /* scan word array */
131
132 zword wvalue;
133
134 LOW_WORD (addr, wvalue)
135
136 if (wvalue == zargs[0])
137 goto finished;
138
139 } else { /* scan byte array */
140
141 zbyte bvalue;
142
143 LOW_BYTE (addr, bvalue)
144
145 if (bvalue == zargs[0])
146 goto finished;
147
148 }
149
150 addr += zargs[3] & 0x7f;
151
152 }
153
154 addr = 0;
155
156finished:
157
158 store (addr);
159 branch (addr);
160
161}/* z_scan_table */
162
163/*
164 * z_storeb, write a byte into a table of bytes.
165 *
166 * zargs[0] = address of table
167 * zargs[1] = index of table entry
168 * zargs[2] = value to be written
169 *
170 */
171
172void z_storeb (void)
173{
174
175 storeb ((zword) (zargs[0] + zargs[1]), zargs[2]);
176
177}/* z_storeb */
178
179/*
180 * z_storew, write a word into a table of words.
181 *
182 * zargs[0] = address of table
183 * zargs[1] = index of table entry
184 * zargs[2] = value to be written
185 *
186 */
187
188void z_storew (void)
189{
190
191 storew ((zword) (zargs[0] + 2 * zargs[1]), zargs[2]);
192
193}/* z_storew */
diff --git a/apps/plugins/frotz/text.c b/apps/plugins/frotz/text.c
new file mode 100644
index 0000000000..8145cfea29
--- /dev/null
+++ b/apps/plugins/frotz/text.c
@@ -0,0 +1,1109 @@
1/* text.c - Text manipulation functions
2 * Copyright (c) 1995-1997 Stefan Jokisch
3 *
4 * This file is part of Frotz.
5 *
6 * Frotz is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * Frotz is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
19 */
20
21#include "frotz.h"
22
23enum string_type {
24 LOW_STRING, ABBREVIATION, HIGH_STRING, EMBEDDED_STRING, VOCABULARY
25};
26
27extern zword object_name (zword);
28
29static zchar decoded[10];
30static zword encoded[3];
31
32/*
33 * According to Matteo De Luigi <matteo.de.luigi@libero.it>,
34 * 0xab and 0xbb were in each other's proper positions.
35 * Sat Apr 21, 2001
36 */
37static zchar zscii_to_latin1[] = {
38 0xe4, 0xf6, 0xfc, 0xc4, 0xd6, 0xdc, 0xdf, 0xbb,
39 0xab, 0xeb, 0xef, 0xff, 0xcb, 0xcf, 0xe1, 0xe9,
40 0xed, 0xf3, 0xfa, 0xfd, 0xc1, 0xc9, 0xcd, 0xd3,
41 0xda, 0xdd, 0xe0, 0xe8, 0xec, 0xf2, 0xf9, 0xc0,
42 0xc8, 0xcc, 0xd2, 0xd9, 0xe2, 0xea, 0xee, 0xf4,
43 0xfb, 0xc2, 0xca, 0xce, 0xd4, 0xdb, 0xe5, 0xc5,
44 0xf8, 0xd8, 0xe3, 0xf1, 0xf5, 0xc3, 0xd1, 0xd5,
45 0xe6, 0xc6, 0xe7, 0xc7, 0xfe, 0xf0, 0xde, 0xd0,
46 0xa3, 0x00, 0x00, 0xa1, 0xbf
47};
48
49/*
50 * translate_from_zscii
51 *
52 * Map a ZSCII character onto the ISO Latin-1 alphabet.
53 *
54 */
55
56zchar translate_from_zscii (zbyte c)
57{
58
59 if (c == 0xfc)
60 return ZC_MENU_CLICK;
61 if (c == 0xfd)
62 return ZC_DOUBLE_CLICK;
63 if (c == 0xfe)
64 return ZC_SINGLE_CLICK;
65
66 if (c >= 0x9b && story_id != BEYOND_ZORK) {
67
68 if (hx_unicode_table != 0) { /* game has its own Unicode table */
69
70 zbyte N;
71
72 LOW_BYTE (hx_unicode_table, N)
73
74 if (c - 0x9b < N) {
75
76 zword addr = hx_unicode_table + 1 + 2 * (c - 0x9b);
77 zword unicode;
78
79 LOW_WORD (addr, unicode)
80
81 return (unicode < 0x100) ? (zchar) unicode : '?';
82
83 } else return '?';
84
85 } else /* game uses standard set */
86
87 if (c <= 0xdf) {
88
89 if (c == 0xdc || c == 0xdd) /* Oe and oe ligatures */
90 return '?'; /* are not ISO-Latin 1 */
91
92 return zscii_to_latin1[c - 0x9b];
93
94 } else return '?';
95 }
96
97 return c;
98
99}/* translate_from_zscii */
100
101/*
102 * translate_to_zscii
103 *
104 * Map an ISO Latin-1 character onto the ZSCII alphabet.
105 *
106 */
107
108zbyte translate_to_zscii (zchar c)
109{
110 int i;
111
112 if (c == ZC_SINGLE_CLICK)
113 return 0xfe;
114 if (c == ZC_DOUBLE_CLICK)
115 return 0xfd;
116 if (c == ZC_MENU_CLICK)
117 return 0xfc;
118
119 if (c >= ZC_LATIN1_MIN) {
120
121 if (hx_unicode_table != 0) { /* game has its own Unicode table */
122
123 zbyte N;
124 int i;
125
126 LOW_BYTE (hx_unicode_table, N)
127
128 for (i = 0x9b; i < 0x9b + N; i++) {
129
130 zword addr = hx_unicode_table + 1 + 2 * (i - 0x9b);
131 zword unicode;
132
133 LOW_WORD (addr, unicode)
134
135 if (c == unicode)
136 return (zbyte) i;
137
138 }
139
140 return '?';
141
142 } else { /* game uses standard set */
143
144 for (i = 0x9b; i <= 0xdf; i++)
145 if (c == zscii_to_latin1[i - 0x9b])
146 return (zbyte) i;
147
148 return '?';
149
150 }
151 }
152
153 if (c == 0) /* Safety thing from David Kinder */
154 c = '?'; /* regarding his Unicode patches */
155 /* Sept 15, 2002 */
156
157 return c;
158
159}/* translate_to_zscii */
160
161/*
162 * alphabet
163 *
164 * Return a character from one of the three character sets.
165 *
166 */
167
168static zchar alphabet (int set, int index)
169{
170
171 if (h_alphabet != 0) { /* game uses its own alphabet */
172
173 zbyte c;
174
175 zword addr = h_alphabet + 26 * set + index;
176 LOW_BYTE (addr, c)
177
178 return translate_from_zscii (c);
179
180 } else /* game uses default alphabet */
181
182 if (set == 0)
183 return 'a' + index;
184 else if (set == 1)
185 return 'A' + index;
186 else if (h_version == V1)
187 return " 0123456789.,!?_#'\"/\\<-:()"[index];
188 else
189 return " ^0123456789.,!?_#'\"/\\-:()"[index];
190
191}/* alphabet */
192
193/*
194 * load_string
195 *
196 * Copy a ZSCII string from the memory to the global "decoded" string.
197 *
198 */
199
200static void load_string (zword addr, zword length)
201{
202 int resolution = (h_version <= V3) ? 2 : 3;
203 int i = 0;
204
205 while (i < 3 * resolution)
206
207 if (i < length) {
208
209 zbyte c;
210
211 LOW_BYTE (addr, c)
212 addr++;
213
214 decoded[i++] = translate_from_zscii (c);
215
216 } else decoded[i++] = 0;
217
218}/* load_string */
219
220/*
221 * encode_text
222 *
223 * Encode the Unicode text in the global "decoded" string then write
224 * the result to the global "encoded" array. (This is used to look up
225 * words in the dictionary.) Up to V3 the vocabulary resolution is
226 * two, since V4 it is three words. Because each word contains three
227 * Z-characters, that makes six or nine Z-characters respectively.
228 * Longer words are chopped to the proper size, shorter words are are
229 * padded out with 5's. For word completion we pad with 0s and 31s,
230 * the minimum and maximum Z-characters.
231 *
232 */
233
234static void encode_text (int padding)
235{
236 static zchar again[] = { 'a', 'g', 'a', 'i', 'n', 0 };
237 static zchar examine[] = { 'e', 'x', 'a', 'm', 'i', 'n', 'e', 0 };
238 static zchar wait[] = { 'w', 'a', 'i', 't', 0 };
239
240 zbyte zchars[12];
241 const zchar *ptr = decoded;
242 zchar c;
243 int resolution = (h_version <= V3) ? 2 : 3;
244 int i = 0;
245
246 /* Expand abbreviations that some old Infocom games lack */
247
248 if (f_setup.expand_abbreviations)
249
250 if (padding == 0x05 && decoded[1] == 0)
251
252 switch (decoded[0]) {
253 case 'g': ptr = again; break;
254 case 'x': ptr = examine; break;
255 case 'z': ptr = wait; break;
256 }
257
258 /* Translate string to a sequence of Z-characters */
259
260 while (i < 3 * resolution)
261
262 if ((c = *ptr++) != 0) {
263
264 int index, set;
265 zbyte c2;
266
267 /* Search character in the alphabet */
268
269 for (set = 0; set < 3; set++)
270 for (index = 0; index < 26; index++)
271 if (c == alphabet (set, index))
272 goto letter_found;
273
274 /* Character not found, store its ZSCII value */
275
276 c2 = translate_to_zscii (c);
277
278 zchars[i++] = 5;
279 zchars[i++] = 6;
280 zchars[i++] = c2 >> 5;
281 zchars[i++] = c2 & 0x1f;
282
283 continue;
284
285 letter_found:
286
287 /* Character found, store its index */
288
289 if (set != 0)
290 zchars[i++] = ((h_version <= V2) ? 1 : 3) + set;
291
292 zchars[i++] = index + 6;
293
294 } else zchars[i++] = padding;
295
296 /* Three Z-characters make a 16bit word */
297
298 for (i = 0; i < resolution; i++)
299
300 encoded[i] =
301 (zchars[3 * i + 0] << 10) |
302 (zchars[3 * i + 1] << 5) |
303 (zchars[3 * i + 2]);
304
305 encoded[resolution - 1] |= 0x8000;
306
307}/* encode_text */
308
309/*
310 * z_check_unicode, test if a unicode character can be read and printed.
311 *
312 * zargs[0] = Unicode
313 *
314 */
315
316void z_check_unicode (void)
317{
318 zword c = zargs[0];
319
320 if (c >= 0x20 && c <= 0x7e)
321 store (3);
322 else if (c == 0xa0)
323 store (1);
324 else if (c >= 0xa1 && c <= 0xff)
325 store (3);
326 else
327 store (0);
328
329}/* z_check_unicode */
330
331/*
332 * z_encode_text, encode a ZSCII string for use in a dictionary.
333 *
334 * zargs[0] = address of text buffer
335 * zargs[1] = length of ASCII string
336 * zargs[2] = offset of ASCII string within the text buffer
337 * zargs[3] = address to store encoded text in
338 *
339 * This is a V5+ opcode and therefore the dictionary resolution must be
340 * three 16bit words.
341 *
342 */
343
344void z_encode_text (void)
345{
346 int i;
347
348 load_string ((zword) (zargs[0] + zargs[2]), zargs[1]);
349
350 encode_text (0x05);
351
352 for (i = 0; i < 3; i++)
353 storew ((zword) (zargs[3] + 2 * i), encoded[i]);
354
355}/* z_encode_text */
356
357/*
358 * decode_text
359 *
360 * Convert encoded text to Unicode. The encoded text consists of 16bit
361 * words. Every word holds 3 Z-characters (5 bits each) plus a spare
362 * bit to mark the last word. The Z-characters translate to ZSCII by
363 * looking at the current current character set. Some select another
364 * character set, others refer to abbreviations.
365 *
366 * There are several different string types:
367 *
368 * LOW_STRING - from the lower 64KB (byte address)
369 * ABBREVIATION - from the abbreviations table (word address)
370 * HIGH_STRING - from the end of the memory map (packed address)
371 * EMBEDDED_STRING - from the instruction stream (at PC)
372 * VOCABULARY - from the dictionary (byte address)
373 *
374 * The last type is only used for word completion.
375 *
376 */
377
378#define outchar(c) if (st==VOCABULARY) *ptr++=c; else print_char(c)
379
380static void decode_text (enum string_type st, zword addr)
381{
382 zchar *ptr;
383 long byte_addr;
384 zchar c2;
385 zword code;
386 zbyte c, prev_c = 0;
387 int shift_state = 0;
388 int shift_lock = 0;
389 int status = 0;
390
391 ptr = NULL; /* makes compilers shut up */
392 byte_addr = 0;
393
394 /* Calculate the byte address if necessary */
395
396 if (st == ABBREVIATION)
397
398 byte_addr = (long) addr << 1;
399
400 else if (st == HIGH_STRING) {
401
402 if (h_version <= V3)
403 byte_addr = (long) addr << 1;
404 else if (h_version <= V5)
405 byte_addr = (long) addr << 2;
406 else if (h_version <= V7)
407 byte_addr = ((long) addr << 2) + ((long) h_strings_offset << 3);
408 else /* h_version == V8 */
409 byte_addr = (long) addr << 3;
410
411 if (byte_addr >= story_size)
412 runtime_error (ERR_ILL_PRINT_ADDR);
413
414 }
415
416 /* Loop until a 16bit word has the highest bit set */
417
418 if (st == VOCABULARY)
419 ptr = decoded;
420
421 do {
422
423 int i;
424
425 /* Fetch the next 16bit word */
426
427 if (st == LOW_STRING || st == VOCABULARY) {
428 LOW_WORD (addr, code)
429 addr += 2;
430 } else if (st == HIGH_STRING || st == ABBREVIATION) {
431 HIGH_WORD (byte_addr, code)
432 byte_addr += 2;
433 } else
434 CODE_WORD (code)
435
436 /* Read its three Z-characters */
437
438 for (i = 10; i >= 0; i -= 5) {
439
440 zword abbr_addr;
441 zword ptr_addr;
442
443 c = (code >> i) & 0x1f;
444
445 switch (status) {
446
447 case 0: /* normal operation */
448
449 if (shift_state == 2 && c == 6)
450 status = 2;
451
452 else if (h_version == V1 && c == 1)
453 new_line ();
454
455 else if (h_version >= V2 && shift_state == 2 && c == 7)
456 new_line ();
457
458 else if (c >= 6)
459 outchar (alphabet (shift_state, c - 6));
460
461 else if (c == 0)
462 outchar (' ');
463
464 else if (h_version >= V2 && c == 1)
465 status = 1;
466
467 else if (h_version >= V3 && c <= 3)
468 status = 1;
469
470 else {
471
472 shift_state = (shift_lock + (c & 1) + 1) % 3;
473
474 if (h_version <= V2 && c >= 4)
475 shift_lock = shift_state;
476
477 break;
478
479 }
480
481 shift_state = shift_lock;
482
483 break;
484
485 case 1: /* abbreviation */
486
487 ptr_addr = h_abbreviations + 64 * (prev_c - 1) + 2 * c;
488
489 LOW_WORD (ptr_addr, abbr_addr)
490 decode_text (ABBREVIATION, abbr_addr);
491
492 status = 0;
493 break;
494
495 case 2: /* ZSCII character - first part */
496
497 status = 3;
498 break;
499
500 case 3: /* ZSCII character - second part */
501
502 c2 = translate_from_zscii ((prev_c << 5) | c);
503 outchar (c2);
504
505 status = 0;
506 break;
507
508 }
509
510 prev_c = c;
511
512 }
513
514 } while (!(code & 0x8000));
515
516 if (st == VOCABULARY)
517 *ptr = 0;
518
519}/* decode_text */
520
521#undef outchar
522
523/*
524 * z_new_line, print a new line.
525 *
526 * no zargs used
527 *
528 */
529
530void z_new_line (void)
531{
532
533 new_line ();
534
535}/* z_new_line */
536
537/*
538 * z_print, print a string embedded in the instruction stream.
539 *
540 * no zargs used
541 *
542 */
543
544void z_print (void)
545{
546
547 decode_text (EMBEDDED_STRING, 0);
548
549}/* z_print */
550
551/*
552 * z_print_addr, print a string from the lower 64KB.
553 *
554 * zargs[0] = address of string to print
555 *
556 */
557
558void z_print_addr (void)
559{
560
561 decode_text (LOW_STRING, zargs[0]);
562
563}/* z_print_addr */
564
565/*
566 * z_print_char print a single ZSCII character.
567 *
568 * zargs[0] = ZSCII character to be printed
569 *
570 */
571
572void z_print_char (void)
573{
574
575 print_char (translate_from_zscii (zargs[0]));
576
577}/* z_print_char */
578
579/*
580 * z_print_form, print a formatted table.
581 *
582 * zargs[0] = address of formatted table to be printed
583 *
584 */
585
586void z_print_form (void)
587{
588 zword count;
589 zword addr = zargs[0];
590
591 bool first = TRUE;
592
593 for (;;) {
594
595 LOW_WORD (addr, count)
596 addr += 2;
597
598 if (count == 0)
599 break;
600
601 if (!first)
602 new_line ();
603
604 while (count--) {
605
606 zbyte c;
607
608 LOW_BYTE (addr, c)
609 addr++;
610
611 print_char (translate_from_zscii (c));
612
613 }
614
615 first = FALSE;
616
617 }
618
619}/* z_print_form */
620
621/*
622 * print_num
623 *
624 * Print a signed 16bit number.
625 *
626 */
627
628void print_num (zword value)
629{
630 int i;
631
632 /* Print sign */
633
634 if ((short) value < 0) {
635 print_char ('-');
636 value = - (short) value;
637 }
638
639 /* Print absolute value */
640
641 for (i = 10000; i != 0; i /= 10)
642 if (value >= i || i == 1)
643 print_char ('0' + (value / i) % 10);
644
645}/* print_num */
646
647/*
648 * z_print_num, print a signed number.
649 *
650 * zargs[0] = number to print
651 *
652 */
653
654void z_print_num (void)
655{
656
657 print_num (zargs[0]);
658
659}/* z_print_num */
660
661/*
662 * print_object
663 *
664 * Print an object description.
665 *
666 */
667
668void print_object (zword object)
669{
670 zword addr = object_name (object);
671 zword code = 0x94a5;
672 zbyte length;
673
674 LOW_BYTE (addr, length)
675 addr++;
676
677 if (length != 0)
678 LOW_WORD (addr, code)
679
680 if (code == 0x94a5) { /* encoded text 0x94a5 == empty string */
681
682 print_string ("object#"); /* supply a generic name */
683 print_num (object); /* for anonymous objects */
684
685 } else decode_text (LOW_STRING, addr);
686
687}/* print_object */
688
689/*
690 * z_print_obj, print an object description.
691 *
692 * zargs[0] = number of object to be printed
693 *
694 */
695
696void z_print_obj (void)
697{
698
699 print_object (zargs[0]);
700
701}/* z_print_obj */
702
703/*
704 * z_print_paddr, print the string at the given packed address.
705 *
706 * zargs[0] = packed address of string to be printed
707 *
708 */
709
710void z_print_paddr (void)
711{
712
713 decode_text (HIGH_STRING, zargs[0]);
714
715}/* z_print_paddr */
716
717/*
718 * z_print_ret, print the string at PC, print newline then return true.
719 *
720 * no zargs used
721 *
722 */
723
724void z_print_ret (void)
725{
726
727 decode_text (EMBEDDED_STRING, 0);
728 new_line ();
729 ret (1);
730
731}/* z_print_ret */
732
733/*
734 * print_string
735 *
736 * Print a string of ASCII characters.
737 *
738 */
739
740void print_string (const char *s)
741{
742 char c;
743
744 while ((c = *s++) != 0)
745
746 if (c == '\n')
747 new_line ();
748 else
749 print_char (c);
750
751}/* print_string */
752
753/*
754 * z_print_unicode
755 *
756 * zargs[0] = Unicode
757 *
758 */
759
760void z_print_unicode (void)
761{
762
763 print_char ((zargs[0] <= 0xff) ? zargs[0] : '?');
764
765}/* z_print_unicode */
766
767/*
768 * lookup_text
769 *
770 * Scan a dictionary searching for the given word. The first argument
771 * can be
772 *
773 * 0x00 - find the first word which is >= the given one
774 * 0x05 - find the word which exactly matches the given one
775 * 0x1f - find the last word which is <= the given one
776 *
777 * The return value is 0 if the search fails.
778 *
779 */
780
781static zword lookup_text (int padding, zword dct)
782{
783 zword entry_addr;
784 zword entry_count;
785 zword entry;
786 zword addr;
787 zbyte entry_len;
788 zbyte sep_count;
789 int resolution = (h_version <= V3) ? 2 : 3;
790 int entry_number;
791 int lower, upper;
792 int i;
793 bool sorted;
794
795 encode_text (padding);
796
797 LOW_BYTE (dct, sep_count) /* skip word separators */
798 dct += 1 + sep_count;
799 LOW_BYTE (dct, entry_len) /* get length of entries */
800 dct += 1;
801 LOW_WORD (dct, entry_count) /* get number of entries */
802 dct += 2;
803
804 if ((short) entry_count < 0) { /* bad luck, entries aren't sorted */
805
806 entry_count = - (short) entry_count;
807 sorted = FALSE;
808
809 } else sorted = TRUE; /* entries are sorted */
810
811 lower = 0;
812 upper = entry_count - 1;
813
814 while (lower <= upper) {
815
816 if (sorted) /* binary search */
817 entry_number = (lower + upper) / 2;
818 else /* linear search */
819 entry_number = lower;
820
821 entry_addr = dct + entry_number * entry_len;
822
823 /* Compare word to dictionary entry */
824
825 addr = entry_addr;
826
827 for (i = 0; i < resolution; i++) {
828 LOW_WORD (addr, entry)
829 if (encoded[i] != entry)
830 goto continuing;
831 addr += 2;
832 }
833
834 return entry_addr; /* exact match found, return now */
835
836 continuing:
837
838 if (sorted) /* binary search */
839
840 if (encoded[i] > entry)
841 lower = entry_number + 1;
842 else
843 upper = entry_number - 1;
844
845 else lower++; /* linear search */
846
847 }
848
849 /* No exact match has been found */
850
851 if (padding == 0x05)
852 return 0;
853
854 entry_number = (padding == 0x00) ? lower : upper;
855
856 if (entry_number == -1 || entry_number == entry_count)
857 return 0;
858
859 return dct + entry_number * entry_len;
860
861}/* lookup_text */
862
863/*
864 * tokenise_text
865 *
866 * Translate a single word to a token and append it to the token
867 * buffer. Every token consists of the address of the dictionary
868 * entry, the length of the word and the offset of the word from
869 * the start of the text buffer. Unknown words cause empty slots
870 * if the flag is set (such that the text can be scanned several
871 * times with different dictionaries); otherwise they are zero.
872 *
873 */
874
875static void tokenise_text (zword text, zword length, zword from, zword parse, zword dct, bool flag)
876{
877 zword addr;
878 zbyte token_max, token_count;
879
880 LOW_BYTE (parse, token_max)
881 parse++;
882 LOW_BYTE (parse, token_count)
883
884 if (token_count < token_max) { /* sufficient space left for token? */
885
886 storeb (parse++, token_count + 1);
887
888 load_string ((zword) (text + from), length);
889
890 addr = lookup_text (0x05, dct);
891
892 if (addr != 0 || !flag) {
893
894 parse += 4 * token_count;
895
896 storew ((zword) (parse + 0), addr);
897 storeb ((zword) (parse + 2), length);
898 storeb ((zword) (parse + 3), from);
899
900 }
901
902 }
903
904}/* tokenise_text */
905
906/*
907 * tokenise_line
908 *
909 * Split an input line into words and translate the words to tokens.
910 *
911 */
912
913void tokenise_line (zword text, zword token, zword dct, bool flag)
914{
915 zword addr1;
916 zword addr2;
917 zbyte length;
918 zbyte c;
919
920 length = 0; /* makes compilers shut up */
921
922 /* Use standard dictionary if the given dictionary is zero */
923
924 if (dct == 0)
925 dct = h_dictionary;
926
927 /* Remove all tokens before inserting new ones */
928
929 storeb ((zword) (token + 1), 0);
930
931 /* Move the first pointer across the text buffer searching for the
932 beginning of a word. If this succeeds, store the position in a
933 second pointer. Move the first pointer searching for the end of
934 the word. When it is found, "tokenise" the word. Continue until
935 the end of the buffer is reached. */
936
937 addr1 = text;
938 addr2 = 0;
939
940 if (h_version >= V5) {
941 addr1++;
942 LOW_BYTE (addr1, length)
943 }
944
945 do {
946
947 zword sep_addr;
948 zbyte sep_count;
949 zbyte separator;
950
951 /* Fetch next ZSCII character */
952
953 addr1++;
954
955 if (h_version >= V5 && addr1 == text + 2 + length)
956 c = 0;
957 else
958 LOW_BYTE (addr1, c)
959
960 /* Check for separator */
961
962 sep_addr = dct;
963
964 LOW_BYTE (sep_addr, sep_count)
965 sep_addr++;
966
967 do {
968
969 LOW_BYTE (sep_addr, separator)
970 sep_addr++;
971
972 } while (c != separator && --sep_count != 0);
973
974 /* This could be the start or the end of a word */
975
976 if (sep_count == 0 && c != ' ' && c != 0) {
977
978 if (addr2 == 0)
979 addr2 = addr1;
980
981 } else if (addr2 != 0) {
982
983 tokenise_text (
984 text,
985 (zword) (addr1 - addr2),
986 (zword) (addr2 - text),
987 token, dct, flag );
988
989 addr2 = 0;
990
991 }
992
993 /* Translate separator (which is a word in its own right) */
994
995 if (sep_count != 0)
996
997 tokenise_text (
998 text,
999 (zword) (1),
1000 (zword) (addr1 - text),
1001 token, dct, flag );
1002
1003 } while (c != 0);
1004
1005}/* tokenise_line */
1006
1007/*
1008 * z_tokenise, make a lexical analysis of a ZSCII string.
1009 *
1010 * zargs[0] = address of string to analyze
1011 * zargs[1] = address of token buffer
1012 * zargs[2] = address of dictionary (optional)
1013 * zargs[3] = set when unknown words cause empty slots (optional)
1014 *
1015 */
1016
1017void z_tokenise (void)
1018{
1019
1020 /* Supply default arguments */
1021
1022 if (zargc < 3)
1023 zargs[2] = 0;
1024 if (zargc < 4)
1025 zargs[3] = 0;
1026
1027 /* Call tokenise_line to do the real work */
1028
1029 tokenise_line (zargs[0], zargs[1], zargs[2], zargs[3] != 0);
1030
1031}/* z_tokenise */
1032
1033/*
1034 * completion
1035 *
1036 * Scan the vocabulary to complete the last word on the input line
1037 * (similar to "tcsh" under Unix). The return value is
1038 *
1039 * 2 ==> completion is impossible
1040 * 1 ==> completion is ambiguous
1041 * 0 ==> completion is successful
1042 *
1043 * The function also returns a string in its second argument. In case
1044 * of 2, the string is empty; in case of 1, the string is the longest
1045 * extension of the last word on the input line that is common to all
1046 * possible completions (for instance, if the last word on the input
1047 * is "fo" and its only possible completions are "follow" and "folly"
1048 * then the string is "ll"); in case of 0, the string is an extension
1049 * to the last word that results in the only possible completion.
1050 *
1051 */
1052
1053int completion (const zchar *buffer, zchar *result)
1054{
1055 zword minaddr;
1056 zword maxaddr;
1057 zchar *ptr;
1058 zchar c;
1059 int len;
1060 int i;
1061
1062 *result = 0;
1063
1064 /* Copy last word to "decoded" string */
1065
1066 len = 0;
1067
1068 while ((c = *buffer++) != 0)
1069
1070 if (c != ' ') {
1071
1072 if (len < 9)
1073 decoded[len++] = c;
1074
1075 } else len = 0;
1076
1077 decoded[len] = 0;
1078
1079 /* Search the dictionary for first and last possible extensions */
1080
1081 minaddr = lookup_text (0x00, h_dictionary);
1082 maxaddr = lookup_text (0x1f, h_dictionary);
1083
1084 if (minaddr == 0 || maxaddr == 0 || minaddr > maxaddr)
1085 return 2;
1086
1087 /* Copy first extension to "result" string */
1088
1089 decode_text (VOCABULARY, minaddr);
1090
1091 ptr = result;
1092
1093 for (i = len; (c = decoded[i]) != 0; i++)
1094 *ptr++ = c;
1095 *ptr = 0;
1096
1097 /* Merge second extension with "result" string */
1098
1099 decode_text (VOCABULARY, maxaddr);
1100
1101 for (i = len, ptr = result; (c = decoded[i]) != 0; i++, ptr++)
1102 if (*ptr != c) break;
1103 *ptr = 0;
1104
1105 /* Search was ambiguous or successful */
1106
1107 return (minaddr == maxaddr) ? 0 : 1;
1108
1109}/* completion */
diff --git a/apps/plugins/frotz/variable.c b/apps/plugins/frotz/variable.c
new file mode 100644
index 0000000000..3e5c6e0c09
--- /dev/null
+++ b/apps/plugins/frotz/variable.c
@@ -0,0 +1,304 @@
1/* variable.c - Variable and stack related opcodes
2 * Copyright (c) 1995-1997 Stefan Jokisch
3 *
4 * This file is part of Frotz.
5 *
6 * Frotz is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * Frotz is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
19 */
20
21#include "frotz.h"
22
23/*
24 * z_dec, decrement a variable.
25 *
26 * zargs[0] = variable to decrement
27 *
28 */
29
30void z_dec (void)
31{
32 zword value;
33
34 if (zargs[0] == 0)
35 (*sp)--;
36 else if (zargs[0] < 16)
37 (*(fp - zargs[0]))--;
38 else {
39 zword addr = h_globals + 2 * (zargs[0] - 16);
40 LOW_WORD (addr, value)
41 value--;
42 SET_WORD (addr, value)
43 }
44
45}/* z_dec */
46
47/*
48 * z_dec_chk, decrement a variable and branch if now less than value.
49 *
50 * zargs[0] = variable to decrement
51 * zargs[1] = value to check variable against
52 *
53 */
54
55void z_dec_chk (void)
56{
57 zword value;
58
59 if (zargs[0] == 0)
60 value = --(*sp);
61 else if (zargs[0] < 16)
62 value = --(*(fp - zargs[0]));
63 else {
64 zword addr = h_globals + 2 * (zargs[0] - 16);
65 LOW_WORD (addr, value)
66 value--;
67 SET_WORD (addr, value)
68 }
69
70 branch ((short) value < (short) zargs[1]);
71
72}/* z_dec_chk */
73
74/*
75 * z_inc, increment a variable.
76 *
77 * zargs[0] = variable to increment
78 *
79 */
80
81void z_inc (void)
82{
83 zword value;
84
85 if (zargs[0] == 0)
86 (*sp)++;
87 else if (zargs[0] < 16)
88 (*(fp - zargs[0]))++;
89 else {
90 zword addr = h_globals + 2 * (zargs[0] - 16);
91 LOW_WORD (addr, value)
92 value++;
93 SET_WORD (addr, value)
94 }
95
96}/* z_inc */
97
98/*
99 * z_inc_chk, increment a variable and branch if now greater than value.
100 *
101 * zargs[0] = variable to increment
102 * zargs[1] = value to check variable against
103 *
104 */
105
106void z_inc_chk (void)
107{
108 zword value;
109
110 if (zargs[0] == 0)
111 value = ++(*sp);
112 else if (zargs[0] < 16)
113 value = ++(*(fp - zargs[0]));
114 else {
115 zword addr = h_globals + 2 * (zargs[0] - 16);
116 LOW_WORD (addr, value)
117 value++;
118 SET_WORD (addr, value)
119 }
120
121 branch ((short) value > (short) zargs[1]);
122
123}/* z_inc_chk */
124
125/*
126 * z_load, store the value of a variable.
127 *
128 * zargs[0] = variable to store
129 *
130 */
131
132void z_load (void)
133{
134 zword value;
135
136 if (zargs[0] == 0)
137 value = *sp;
138 else if (zargs[0] < 16)
139 value = *(fp - zargs[0]);
140 else {
141 zword addr = h_globals + 2 * (zargs[0] - 16);
142 LOW_WORD (addr, value)
143 }
144
145 store (value);
146
147}/* z_load */
148
149/*
150 * z_pop, pop a value off the game stack and discard it.
151 *
152 * no zargs used
153 *
154 */
155
156void z_pop (void)
157{
158
159 sp++;
160
161}/* z_pop */
162
163/*
164 * z_pop_stack, pop n values off the game or user stack and discard them.
165 *
166 * zargs[0] = number of values to discard
167 * zargs[1] = address of user stack (optional)
168 *
169 */
170
171void z_pop_stack (void)
172{
173
174 if (zargc == 2) { /* it's a user stack */
175
176 zword size;
177 zword addr = zargs[1];
178
179 LOW_WORD (addr, size)
180
181 size += zargs[0];
182 storew (addr, size);
183
184 } else sp += zargs[0]; /* it's the game stack */
185
186}/* z_pop_stack */
187
188/*
189 * z_pull, pop a value off...
190 *
191 * a) ...the game or a user stack and store it (V6)
192 *
193 * zargs[0] = address of user stack (optional)
194 *
195 * b) ...the game stack and write it to a variable (other than V6)
196 *
197 * zargs[0] = variable to write value to
198 *
199 */
200
201void z_pull (void)
202{
203 zword value;
204
205 if (h_version != V6) { /* not a V6 game, pop stack and write */
206
207 value = *sp++;
208
209 if (zargs[0] == 0)
210 *sp = value;
211 else if (zargs[0] < 16)
212 *(fp - zargs[0]) = value;
213 else {
214 zword addr = h_globals + 2 * (zargs[0] - 16);
215 SET_WORD (addr, value)
216 }
217
218 } else { /* it's V6, but is there a user stack? */
219
220 if (zargc == 1) { /* it's a user stack */
221
222 zword size;
223 zword addr = zargs[0];
224
225 LOW_WORD (addr, size)
226
227 size++;
228 storew (addr, size);
229
230 addr += 2 * size;
231 LOW_WORD (addr, value)
232
233 } else value = *sp++; /* it's the game stack */
234
235 store (value);
236
237 }
238
239}/* z_pull */
240
241/*
242 * z_push, push a value onto the game stack.
243 *
244 * zargs[0] = value to push onto the stack
245 *
246 */
247
248void z_push (void)
249{
250
251 *--sp = zargs[0];
252
253}/* z_push */
254
255/*
256 * z_push_stack, push a value onto a user stack then branch if successful.
257 *
258 * zargs[0] = value to push onto the stack
259 * zargs[1] = address of user stack
260 *
261 */
262
263void z_push_stack (void)
264{
265 zword size;
266 zword addr = zargs[1];
267
268 LOW_WORD (addr, size)
269
270 if (size != 0) {
271
272 storew ((zword) (addr + 2 * size), zargs[0]);
273
274 size--;
275 storew (addr, size);
276
277 }
278
279 branch (size);
280
281}/* z_push_stack */
282
283/*
284 * z_store, write a value to a variable.
285 *
286 * zargs[0] = variable to be written to
287 * zargs[1] = value to write
288 *
289 */
290
291void z_store (void)
292{
293 zword value = zargs[1];
294
295 if (zargs[0] == 0)
296 *sp = value;
297 else if (zargs[0] < 16)
298 *(fp - zargs[0]) = value;
299 else {
300 zword addr = h_globals + 2 * (zargs[0] - 16);
301 SET_WORD (addr, value)
302 }
303
304}/* z_store */
diff --git a/apps/plugins/viewers.config b/apps/plugins/viewers.config
index 205bafb12a..985115672d 100644
--- a/apps/plugins/viewers.config
+++ b/apps/plugins/viewers.config
@@ -61,3 +61,11 @@ link,viewers/shortcuts_view,-
61lua,viewers/lua,- 61lua,viewers/lua,-
62ipod,viewers/crypt_firmware,- 62ipod,viewers/crypt_firmware,-
63ipodx,viewers/crypt_firmware,- 63ipodx,viewers/crypt_firmware,-
64z1,viewers/frotz,-
65z2,viewers/frotz,-
66z3,viewers/frotz,-
67z4,viewers/frotz,-
68z5,viewers/frotz,-
69z6,viewers/frotz,-
70z7,viewers/frotz,-
71z8,viewers/frotz,-
diff --git a/manual/plugins/frotz.tex b/manual/plugins/frotz.tex
new file mode 100644
index 0000000000..c1d59e4905
--- /dev/null
+++ b/manual/plugins/frotz.tex
@@ -0,0 +1,67 @@
1% $Id$ %
2\subsection{Frotz}
3Frotz is a Z-Machine interpreter for playing Infocom's interactive fiction
4games, and newer games using the same format. To start a game open a
5\fname{.z1 - .z8} file in the \setting{File Browser}. Most modern games are
6in the \fname{.z5} or \fname{.z8} format but the older formats used by
7Infocom are supported.
8
9Z-Machine games are text based and most depend heavily on typed commands.
10The virtual keyboard is used for text entry, both for typing entire lines
11and for typing single characters when the game requires single character
12input.
13
14Sounds, pictures, colour and Unicode are not currently supported, but
15the interpreter informs the game of this and almost all games will
16adapt so that they are still playable. This port of Frotz attempts to be
17compliant with the Z-Machine Specification version 1.0.
18
19Some places where you can find Z-Machine games, and information about
20interactive fiction:
21\begin{itemize}
22\item The Interactive Fiction Archive, where many free modern works
23can be downloaded:
24\url{http://www.ifarchive.org/}
25\item The specific folder on the if-archive containing Z-Machine games:
26\url{http://www.ifarchive.org/indexes/if-archiveXgamesXzcode.html}
27\item The Infocom homepage, with information about how to get the
28classic commercial Infocom games:
29\url{http://www.csd.uwo.ca/Infocom/}
30\item The Frotz homepage (for the original Unix port):
31\url{http://frotz.sourceforge.net/}
32\item A Beginner's Guide to Playing Interactive Fiction:
33\url{http://www.microheaven.com/IFGuide/}
34\end{itemize}
35
36\begin{table}
37 \begin{btnmap}{}{}
38 \opt{RECORDER_PAD,IRIVER_H100_PAD,IRIVER_H300_PAD}{\ButtonOn}
39 \opt{IPOD_4G_PAD,IPOD_3G_PAD,IRIVER_H10_PAD,GIGABEAT_S_PAD}{\ButtonPlay}
40 \opt{ONDIO_PAD}{\ButtonMenu}
41 \opt{IAUDIO_X5_PAD,MROBE100_PAD}{\ButtonPower}
42 \opt{SANSA_E200_PAD,SANSA_C200_PAD}{\ButtonUp}
43 \opt{GIGABEAT_PAD}{\ButtonA}
44 \opt{HAVEREMOTEKEYMAP}{&
45 \opt{IRIVER_RC_H100_PAD}{\ButtonRCOn}
46 }
47 & Display keyboard to enter text\\
48 \opt{IRIVER_H100_PAD,IRIVER_H300_PAD,IAUDIO_X5_PAD,IPOD_4G_PAD,IPOD_3G_PAD%
49 ,SANSA_E200_PAD,SANSA_C200_PAD,GIGABEAT_PAD,GIGABEAT_S_PAD,MROBE100_PAD}{\ButtonSelect}
50 \opt{RECORDER_PAD}{\ButtonPlay}
51 \opt{ONDIO_PAD}{\ButtonUp}
52 \opt{IRIVER_H10_PAD}{\ButtonRew}
53 \opt{COWON_D2_PAD}{\ButtonMenu{}, \TouchCenter{} or \TouchBottomMiddle}
54 \opt{HAVEREMOTEKEYMAP}{& }
55 & Press enter\\
56 \opt{RECORDER_PAD,ONDIO_PAD,IRIVER_H100_PAD,IRIVER_H300_PAD}{\ButtonOff}
57 \opt{IPOD_4G_PAD,IPOD_3G_PAD}{\ButtonMenu}
58 \opt{IAUDIO_X5_PAD,IRIVER_H10_PAD,SANSA_E200_PAD,SANSA_C200_PAD,GIGABEAT_PAD%
59 ,MROBE100_PAD}{\ButtonPower}
60 \opt{GIGABEAT_S_PAD}{\ButtonBack}
61 \opt{COWON_D2_PAD}{\ButtonPower{} or \TouchBottomRight}
62 \opt{HAVEREMOTEKEYMAP}{&
63 \opt{IRIVER_RC_H100_PAD}{\ButtonRCStop}
64 }
65 & Open Frotz menu (not available at MORE prompts)\\
66 \end{btnmap}
67\end{table}
diff --git a/manual/plugins/main.tex b/manual/plugins/main.tex
index a7dc3d7bf0..ff332b70c5 100644
--- a/manual/plugins/main.tex
+++ b/manual/plugins/main.tex
@@ -135,6 +135,7 @@ option from the \setting{Context Menu} (see \reference{ref:Contextmenu}).}
135 {}{} 135 {}{}
136 Shortcuts & \fname{.link} & \\ 136 Shortcuts & \fname{.link} & \\
137 Chip-8 Emulator & \fname{.ch8} & \\ 137 Chip-8 Emulator & \fname{.ch8} & \\
138 Frotz & \fname{.z1 - .z8} & \\
138 JPEG Viewer & \fname{.jpg, .jpeg} & \\ 139 JPEG Viewer & \fname{.jpg, .jpeg} & \\
139 Lua scripting language& \fname{.lua} & \\ 140 Lua scripting language& \fname{.lua} & \\
140 Midiplay & \fname{.mid, .midi} & \\ 141 Midiplay & \fname{.mid, .midi} & \\
@@ -160,6 +161,8 @@ option from the \setting{Context Menu} (see \reference{ref:Contextmenu}).}
160 161
161\opt{lcd_bitmap}{\input{plugins/chip8emulator.tex}} 162\opt{lcd_bitmap}{\input{plugins/chip8emulator.tex}}
162 163
164\opt{lcd_bitmap}{\input{plugins/frotz.tex}}
165
163\opt{lcd_bitmap}{\input{plugins/jpegviewer.tex}} 166\opt{lcd_bitmap}{\input{plugins/jpegviewer.tex}}
164 167
165\opt{large_plugin_buffer}{\input{plugins/lua.tex}} 168\opt{large_plugin_buffer}{\input{plugins/lua.tex}}