diff options
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 | |||
28 | fireworks,demos | 28 | fireworks,demos |
29 | firmware_flash,apps | 29 | firmware_flash,apps |
30 | flipit,games | 30 | flipit,games |
31 | frotz,viewers | ||
31 | goban,games | 32 | goban,games |
32 | greyscale,demos | 33 | greyscale,demos |
33 | helloworld,demos | 34 | helloworld,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 | |||
25 | sudoku | 25 | sudoku |
26 | reversi | 26 | reversi |
27 | goban | 27 | goban |
28 | /* setjmp/longjmp are not implemented on sh */ | ||
29 | #if (CONFIG_CPU != SH7034) | ||
30 | frotz | ||
31 | #endif | ||
28 | #ifdef HAVE_LCD_COLOR | 32 | #ifdef HAVE_LCD_COLOR |
29 | png | 33 | png |
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 @@ | |||
1 | frotz.c | ||
2 | buffer.c | ||
3 | err.c | ||
4 | fastmem.c | ||
5 | files.c | ||
6 | hotkey.c | ||
7 | input.c | ||
8 | main.c | ||
9 | math.c | ||
10 | object.c | ||
11 | process.c | ||
12 | quetzal.c | ||
13 | random.c | ||
14 | redirect.c | ||
15 | screen.c | ||
16 | sound.c | ||
17 | stream.c | ||
18 | table.c | ||
19 | text.c | ||
20 | variable.c | ||
21 | dumb_init.c | ||
22 | dumb_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 @@ | |||
1 | frotz is quite portable and is divided into 'common' and os-specific files. | ||
2 | The common files are included here with minimal modifications. For the | ||
3 | os-specific files I have started with dumbfrotz, the port intended for a plain | ||
4 | C stdio system - it has its own screen buffering which is needed for rockbox. | ||
5 | |||
6 | Things that work | ||
7 | ---------------- | ||
8 | |||
9 | Games, mostly! If the game is too large to fit in the plugin buffer it will | ||
10 | stop playback and steal the audio buffer. | ||
11 | |||
12 | Saving and restoring (/path/to/story.sav, no filename selection). | ||
13 | |||
14 | Transcripts, command records and replays (likewise). | ||
15 | |||
16 | Undo, up to the limit of available memory (the rest of the plugin buffer if | ||
17 | the game fit there, or the rest of the audio buffer if not). | ||
18 | |||
19 | Timed input, though it resets the timer when you enter the menu and only | ||
20 | counts until you enter the keyboard. | ||
21 | |||
22 | Input line preloading, though the actual displayed line after editing looks | ||
23 | wrong. | ||
24 | |||
25 | Things that don't work because I've not implemented it | ||
26 | ------------------------------------------------------ | ||
27 | |||
28 | Reading buttons that don't appear on the rockbox keyboard. | ||
29 | |||
30 | Audible beeps (just a splash for now). | ||
31 | |||
32 | Setting the random seed. | ||
33 | |||
34 | Things which don't work in the original frotz anyway | ||
35 | ---------------------------------------------------- | ||
36 | |||
37 | Mouse and menus. | ||
38 | |||
39 | Pictures. | ||
40 | |||
41 | Non-beep sound samples. | ||
42 | |||
43 | Unicode. | ||
44 | |||
45 | Colours. | ||
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 | |||
23 | extern void stream_char (zchar); | ||
24 | extern void stream_word (const zchar *); | ||
25 | extern void stream_new_line (void); | ||
26 | |||
27 | static zchar buffer[TEXT_BUFFER_SIZE]; | ||
28 | static int bufpos = 0; | ||
29 | |||
30 | static 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 | |||
39 | void 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 | |||
80 | void 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 | |||
131 | void 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 | |||
146 | void 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 */ | ||
14 | extern f_setup_t f_setup; | ||
15 | |||
16 | /* dumb-output.c */ | ||
17 | void dumb_init_output(void); | ||
18 | void dumb_show_screen(bool show_cursor); | ||
19 | void 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 | |||
13 | static int user_screen_width = LCD_WIDTH / SYSFONT_WIDTH; | ||
14 | static int user_screen_height = LCD_HEIGHT / SYSFONT_HEIGHT; | ||
15 | static int user_interpreter_number = -1; | ||
16 | static int user_random_seed = -1; | ||
17 | static int user_tandy_bit = 0; | ||
18 | |||
19 | |||
20 | void 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 | |||
52 | int 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 | |||
60 | void os_restart_game (int stage) { (void)stage; } | ||
61 | |||
62 | void os_fatal (const char *s) | ||
63 | { | ||
64 | rb->splash(HZ*10, s); | ||
65 | exit(1); | ||
66 | } | ||
67 | |||
68 | void 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. */ | ||
19 | typedef unsigned short cell; | ||
20 | static cell screen_data[(LCD_WIDTH/SYSFONT_WIDTH) * (LCD_HEIGHT/SYSFONT_HEIGHT)]; | ||
21 | |||
22 | static cell make_cell(int style, char c) {return (style << 8) | (0xff & c);} | ||
23 | static char cell_char(cell c) {return c & 0xff;} | ||
24 | static int cell_style(cell c) {return c >> 8;} | ||
25 | |||
26 | static int current_style = 0; | ||
27 | static int cursor_row = 0, cursor_col = 0; | ||
28 | |||
29 | static cell *dumb_row(int r) {return screen_data + r * h_screen_cols;} | ||
30 | |||
31 | int os_char_width (zchar z) | ||
32 | { | ||
33 | (void)z; | ||
34 | return 1; | ||
35 | } | ||
36 | |||
37 | int 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 | |||
51 | void 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 */ | ||
59 | static void dumb_set_cell(int row, int col, cell c) | ||
60 | { | ||
61 | dumb_row(row)[col] = c; | ||
62 | } | ||
63 | |||
64 | void os_set_text_style(int x) | ||
65 | { | ||
66 | current_style = x & REVERSE_STYLE; | ||
67 | } | ||
68 | |||
69 | static 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. */ | ||
86 | static 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 | |||
99 | void 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? */ | ||
115 | void 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 | |||
129 | void 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 | |||
140 | void 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 | |||
159 | int 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 | |||
167 | void os_set_colour (int x, int y) { (void)x; (void)y; } | ||
168 | void os_set_font (int x) { (void)x; } | ||
169 | |||
170 | /* Unconditionally show whole screen. */ | ||
171 | void 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. */ | ||
182 | void dumb_show_screen(bool show_cursor) | ||
183 | { | ||
184 | (void)show_cursor; | ||
185 | dumb_dump_screen(); | ||
186 | } | ||
187 | |||
188 | void 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 | |||
204 | void 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 | |||
214 | void os_prepare_sample (int a) { (void)a; } | ||
215 | void os_finish_with_sample (int a) { (void)a; } | ||
216 | void os_start_sample (int a, int b, int c, zword d) | ||
217 | { | ||
218 | (void)a; (void)b; (void)c; (void)d; | ||
219 | } | ||
220 | void os_stop_sample (int a) { (void)a; } | ||
221 | |||
222 | void 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 | |||
241 | bool os_picture_data(int num, int *height, int *width) | ||
242 | { | ||
243 | (void)num; | ||
244 | *height = 0; | ||
245 | *width = 0; | ||
246 | return FALSE; | ||
247 | } | ||
248 | |||
249 | void os_draw_picture (int num, int row, int col) | ||
250 | { | ||
251 | (void)num; | ||
252 | (void)row; | ||
253 | (void)col; | ||
254 | } | ||
255 | |||
256 | int 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 | |||
30 | static int error_count[ERR_NUM_ERRORS]; | ||
31 | |||
32 | static 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 | |||
67 | static void print_long (unsigned long value, int base); | ||
68 | |||
69 | /* | ||
70 | * init_err | ||
71 | * | ||
72 | * Initialise error reporting. | ||
73 | * | ||
74 | */ | ||
75 | |||
76 | void 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 | |||
96 | void 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 | |||
143 | static 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 | |||
31 | extern void seed_random (int); | ||
32 | extern void restart_screen (void); | ||
33 | extern void refresh_text_style (void); | ||
34 | extern void call (zword, int, zword *, int); | ||
35 | extern void split_window (zword); | ||
36 | extern void script_open (void); | ||
37 | extern void script_close (void); | ||
38 | |||
39 | extern zword save_quetzal (int, int); | ||
40 | extern zword restore_quetzal (int, int); | ||
41 | |||
42 | extern void erase_window (zword); | ||
43 | |||
44 | extern void (*op0_opcodes[]) (void); | ||
45 | extern void (*op1_opcodes[]) (void); | ||
46 | extern void (*op2_opcodes[]) (void); | ||
47 | extern void (*var_opcodes[]) (void); | ||
48 | |||
49 | char save_name[MAX_PATH]; | ||
50 | char auxilary_name[MAX_PATH]; | ||
51 | |||
52 | zbyte far *zmp = NULL; | ||
53 | zbyte far *pcp = NULL; | ||
54 | |||
55 | int 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 | |||
64 | typedef struct undo_struct undo_t; | ||
65 | struct 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 | |||
76 | static undo_t *first_undo = NULL, *last_undo = NULL, *curr_undo = NULL; | ||
77 | static zbyte *prev_zmp, *undo_diff; | ||
78 | |||
79 | static int undo_count = 0; | ||
80 | |||
81 | static 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 | |||
90 | zword 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 | |||
112 | void 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 | |||
131 | void 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 | |||
190 | void 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 | |||
380 | void 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 | |||
403 | static 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 | |||
429 | void 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 | |||
451 | void 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 | |||
485 | void 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 | |||
500 | void 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 | |||
544 | static 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 | |||
587 | void 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 | |||
669 | finished: | ||
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 | |||
689 | static 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 | |||
730 | static 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 | |||
766 | int 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 | |||
803 | void 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 | |||
819 | void 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 | |||
880 | finished: | ||
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 | |||
896 | int 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 | |||
983 | void 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 | |||
997 | void 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 | |||
25 | extern void set_more_prompts (bool); | ||
26 | |||
27 | extern bool is_terminator (zchar); | ||
28 | |||
29 | extern bool read_yes_or_no (const char *); | ||
30 | |||
31 | char script_name[MAX_PATH]; | ||
32 | char command_name[MAX_PATH]; | ||
33 | |||
34 | #ifdef __MSDOS__ | ||
35 | extern char latin1_to_ibm[]; | ||
36 | #endif | ||
37 | |||
38 | static int script_width = 0; | ||
39 | |||
40 | int sfp = -1; | ||
41 | int rfp = -1; | ||
42 | int 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 | |||
58 | void 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 | |||
90 | done: | ||
91 | |||
92 | SET_WORD (H_FLAGS, h_flags) | ||
93 | |||
94 | }/* script_open */ | ||
95 | |||
96 | /* | ||
97 | * script_close | ||
98 | * | ||
99 | * Stop transscription. | ||
100 | * | ||
101 | */ | ||
102 | |||
103 | void 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 | |||
121 | void 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 | |||
138 | void 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 | |||
165 | void 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 | |||
209 | void 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 | |||
235 | void 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 | |||
254 | void 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 | |||
271 | void 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 | |||
285 | void 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 | |||
309 | void 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 | |||
324 | static 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 | |||
350 | static 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 | |||
372 | void 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 | |||
389 | void 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 | |||
410 | void 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 | |||
437 | void 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 | |||
454 | static 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 | |||
480 | static 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 | |||
517 | zchar 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 | |||
539 | zchar 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 | |||
26 | PLUGIN_HEADER | ||
27 | |||
28 | extern int frotz_main(void); | ||
29 | extern bool hot_key_quit(void); | ||
30 | |||
31 | f_setup_t f_setup; | ||
32 | extern char save_name[], auxilary_name[], script_name[], command_name[]; | ||
33 | extern int story_fp, sfp, rfp, pfp; | ||
34 | |||
35 | static struct viewport vp[NB_SCREENS]; | ||
36 | |||
37 | static void atexit_cleanup(void); | ||
38 | |||
39 | enum 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 | |||
76 | void 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 | |||
88 | MENUITEM_STRINGLIST(ingame_menu, "Frotz", NULL, "Resume", "Undo", "Restart", | ||
89 | "Toggle input recording", "Play back input", | ||
90 | "Debug options", "Exit"); | ||
91 | |||
92 | zchar 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 | |||
115 | const struct button_mapping* plugin_contexts[]={generic_actions}; | ||
116 | |||
117 | void 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 | |||
139 | zchar 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 | |||
185 | zchar 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 | |||
211 | zchar 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 | |||
253 | bool 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 | |||
268 | zchar os_read_mouse(void) | ||
269 | { | ||
270 | return 0; | ||
271 | } | ||
272 | |||
273 | int 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 | |||
280 | void os_beep(int volume) | ||
281 | { | ||
282 | rb->splashf(HZ/2, "[%s-PITCHED BEEP]", (volume==1) ? "HIGH" : "LOW"); | ||
283 | } | ||
284 | |||
285 | static unsigned char unget_buf; | ||
286 | static int unget_file; | ||
287 | |||
288 | int ungetc(int c, int f) | ||
289 | { | ||
290 | unget_file = f; | ||
291 | unget_buf = c; | ||
292 | return c; | ||
293 | } | ||
294 | |||
295 | int 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 | |||
308 | int 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 | |||
18 | typedef unsigned char zbyte; | ||
19 | typedef unsigned short zword; | ||
20 | |||
21 | enum story { | ||
22 | BEYOND_ZORK, | ||
23 | SHERLOCK, | ||
24 | ZORK_ZERO, | ||
25 | SHOGUN, | ||
26 | ARTHUR, | ||
27 | JOURNEY, | ||
28 | LURKING_HORROR, | ||
29 | UNKNOWN | ||
30 | }; | ||
31 | |||
32 | typedef 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 | |||
231 | extern zbyte *pcp; | ||
232 | extern 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 | |||
250 | extern zbyte *pcp; | ||
251 | extern 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 | |||
268 | extern zbyte h_version; | ||
269 | extern zbyte h_config; | ||
270 | extern zword h_release; | ||
271 | extern zword h_resident_size; | ||
272 | extern zword h_start_pc; | ||
273 | extern zword h_dictionary; | ||
274 | extern zword h_objects; | ||
275 | extern zword h_globals; | ||
276 | extern zword h_dynamic_size; | ||
277 | extern zword h_flags; | ||
278 | extern zbyte h_serial[6]; | ||
279 | extern zword h_abbreviations; | ||
280 | extern zword h_file_size; | ||
281 | extern zword h_checksum; | ||
282 | extern zbyte h_interpreter_number; | ||
283 | extern zbyte h_interpreter_version; | ||
284 | extern zbyte h_screen_rows; | ||
285 | extern zbyte h_screen_cols; | ||
286 | extern zword h_screen_width; | ||
287 | extern zword h_screen_height; | ||
288 | extern zbyte h_font_height; | ||
289 | extern zbyte h_font_width; | ||
290 | extern zword h_functions_offset; | ||
291 | extern zword h_strings_offset; | ||
292 | extern zbyte h_default_background; | ||
293 | extern zbyte h_default_foreground; | ||
294 | extern zword h_terminating_keys; | ||
295 | extern zword h_line_width; | ||
296 | extern zbyte h_standard_high; | ||
297 | extern zbyte h_standard_low; | ||
298 | extern zword h_alphabet; | ||
299 | extern zword h_extension_table; | ||
300 | extern zbyte h_user_name[8]; | ||
301 | |||
302 | extern zword hx_table_size; | ||
303 | extern zword hx_mouse_x; | ||
304 | extern zword hx_mouse_y; | ||
305 | extern zword hx_unicode_table; | ||
306 | |||
307 | /*** Various data ***/ | ||
308 | |||
309 | extern const char *story_name; | ||
310 | |||
311 | extern enum story story_id; | ||
312 | extern long story_size; | ||
313 | |||
314 | extern zword stack[STACK_SIZE]; | ||
315 | extern zword *sp; | ||
316 | extern zword *fp; | ||
317 | extern zword frame_count; | ||
318 | |||
319 | extern zword zargs[8]; | ||
320 | extern int zargc; | ||
321 | |||
322 | extern bool ostream_screen; | ||
323 | extern bool ostream_script; | ||
324 | extern bool ostream_memory; | ||
325 | extern bool ostream_record; | ||
326 | extern bool istream_replay; | ||
327 | extern bool message; | ||
328 | |||
329 | extern int cwin; | ||
330 | extern int mwin; | ||
331 | |||
332 | extern int mouse_x; | ||
333 | extern int mouse_y; | ||
334 | |||
335 | extern bool enable_wrapping; | ||
336 | extern bool enable_scripting; | ||
337 | extern bool enable_scrolling; | ||
338 | extern bool enable_buffering; | ||
339 | |||
340 | |||
341 | extern char *option_zcode_path; /* dg */ | ||
342 | |||
343 | |||
344 | /*** Blorb stuff ***/ | ||
345 | /* | ||
346 | bb_err_t blorb_err; | ||
347 | bb_map_t *blorb_map; | ||
348 | */ | ||
349 | |||
350 | /*** Z-machine opcodes ***/ | ||
351 | |||
352 | void z_add (void); | ||
353 | void z_and (void); | ||
354 | void z_art_shift (void); | ||
355 | void z_buffer_mode (void); | ||
356 | void z_call_n (void); | ||
357 | void z_call_s (void); | ||
358 | void z_catch (void); | ||
359 | void z_check_arg_count (void); | ||
360 | void z_check_unicode (void); | ||
361 | void z_clear_attr (void); | ||
362 | void z_copy_table (void); | ||
363 | void z_dec (void); | ||
364 | void z_dec_chk (void); | ||
365 | void z_div (void); | ||
366 | void z_draw_picture (void); | ||
367 | void z_encode_text (void); | ||
368 | void z_erase_line (void); | ||
369 | void z_erase_picture (void); | ||
370 | void z_erase_window (void); | ||
371 | void z_get_child (void); | ||
372 | void z_get_cursor (void); | ||
373 | void z_get_next_prop (void); | ||
374 | void z_get_parent (void); | ||
375 | void z_get_prop (void); | ||
376 | void z_get_prop_addr (void); | ||
377 | void z_get_prop_len (void); | ||
378 | void z_get_sibling (void); | ||
379 | void z_get_wind_prop (void); | ||
380 | void z_inc (void); | ||
381 | void z_inc_chk (void); | ||
382 | void z_input_stream (void); | ||
383 | void z_insert_obj (void); | ||
384 | void z_je (void); | ||
385 | void z_jg (void); | ||
386 | void z_jin (void); | ||
387 | void z_jl (void); | ||
388 | void z_jump (void); | ||
389 | void z_jz (void); | ||
390 | void z_load (void); | ||
391 | void z_loadb (void); | ||
392 | void z_loadw (void); | ||
393 | void z_log_shift (void); | ||
394 | void z_make_menu (void); | ||
395 | void z_mod (void); | ||
396 | void z_mouse_window (void); | ||
397 | void z_move_window (void); | ||
398 | void z_mul (void); | ||
399 | void z_new_line (void); | ||
400 | void z_nop (void); | ||
401 | void z_not (void); | ||
402 | void z_or (void); | ||
403 | void z_output_stream (void); | ||
404 | void z_picture_data (void); | ||
405 | void z_picture_table (void); | ||
406 | void z_piracy (void); | ||
407 | void z_pop (void); | ||
408 | void z_pop_stack (void); | ||
409 | void z_print (void); | ||
410 | void z_print_addr (void); | ||
411 | void z_print_char (void); | ||
412 | void z_print_form (void); | ||
413 | void z_print_num (void); | ||
414 | void z_print_obj (void); | ||
415 | void z_print_paddr (void); | ||
416 | void z_print_ret (void); | ||
417 | void z_print_table (void); | ||
418 | void z_print_unicode (void); | ||
419 | void z_pull (void); | ||
420 | void z_push (void); | ||
421 | void z_push_stack (void); | ||
422 | void z_put_prop (void); | ||
423 | void z_put_wind_prop (void); | ||
424 | void z_quit (void); | ||
425 | void z_random (void); | ||
426 | void z_read (void); | ||
427 | void z_read_char (void); | ||
428 | void z_read_mouse (void); | ||
429 | void z_remove_obj (void); | ||
430 | void z_restart (void); | ||
431 | void z_restore (void); | ||
432 | void z_restore_undo (void); | ||
433 | void z_ret (void); | ||
434 | void z_ret_popped (void); | ||
435 | void z_rfalse (void); | ||
436 | void z_rtrue (void); | ||
437 | void z_save (void); | ||
438 | void z_save_undo (void); | ||
439 | void z_scan_table (void); | ||
440 | void z_scroll_window (void); | ||
441 | void z_set_attr (void); | ||
442 | void z_set_font (void); | ||
443 | void z_set_colour (void); | ||
444 | void z_set_cursor (void); | ||
445 | void z_set_margins (void); | ||
446 | void z_set_window (void); | ||
447 | void z_set_text_style (void); | ||
448 | void z_show_status (void); | ||
449 | void z_sound_effect (void); | ||
450 | void z_split_window (void); | ||
451 | void z_store (void); | ||
452 | void z_storeb (void); | ||
453 | void z_storew (void); | ||
454 | void z_sub (void); | ||
455 | void z_test (void); | ||
456 | void z_test_attr (void); | ||
457 | void z_throw (void); | ||
458 | void z_tokenise (void); | ||
459 | void z_verify (void); | ||
460 | void z_window_size (void); | ||
461 | void z_window_style (void); | ||
462 | |||
463 | /* Definitions for error handling functions and error codes. */ | ||
464 | |||
465 | /* extern int err_report_mode; */ | ||
466 | |||
467 | void init_err (void); | ||
468 | void 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 | |||
528 | zchar translate_from_zscii (zbyte); | ||
529 | zbyte translate_to_zscii (zchar); | ||
530 | |||
531 | void flush_buffer (void); | ||
532 | void new_line (void); | ||
533 | void print_char (zchar); | ||
534 | void print_num (zword); | ||
535 | void print_object (zword); | ||
536 | void print_string (const char *); | ||
537 | |||
538 | void stream_mssg_on (void); | ||
539 | void stream_mssg_off (void); | ||
540 | |||
541 | void ret (zword); | ||
542 | void store (zword); | ||
543 | void branch (bool); | ||
544 | |||
545 | void storeb (zword, zbyte); | ||
546 | void storew (zword, zword); | ||
547 | |||
548 | /*** Interface functions ***/ | ||
549 | |||
550 | void os_beep (int); | ||
551 | int os_char_width (zchar); | ||
552 | void os_display_char (zchar); | ||
553 | void os_display_string (const zchar *); | ||
554 | void os_draw_picture (int, int, int); | ||
555 | void os_erase_area (int, int, int, int); | ||
556 | void os_fatal (const char *) __attribute__((noreturn)); | ||
557 | void os_finish_with_sample (int); | ||
558 | int os_font_data (int, int *, int *); | ||
559 | void os_init_screen (void); | ||
560 | void os_more_prompt (void); | ||
561 | int os_peek_colour (void); | ||
562 | bool os_picture_data (int, int *, int *); | ||
563 | void os_prepare_sample (int); | ||
564 | void os_process_arguments (int, char *[]); | ||
565 | int os_random_seed (void); | ||
566 | int os_read_file_name (char *, const char *, int); | ||
567 | zchar os_read_key (int, bool); | ||
568 | zchar os_read_line (int, zchar *, int, int, int); | ||
569 | zchar os_read_mouse (void); | ||
570 | void os_reset_screen (void); | ||
571 | void os_restart_game (int); | ||
572 | void os_scroll_area (int, int, int, int, int); | ||
573 | void os_set_colour (int, int); | ||
574 | void os_set_cursor (int, int); | ||
575 | void os_set_font (int); | ||
576 | void os_set_text_style (int); | ||
577 | void os_start_sample (int, int, int, zword); | ||
578 | void os_stop_sample (int); | ||
579 | int os_string_width (const zchar *); | ||
580 | void os_init_setup (void); | ||
581 | int 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 | |||
10 | FROTZSRCDIR := $(APPSDIR)/plugins/frotz | ||
11 | FROTZBUILDDIR := $(BUILDDIR)/apps/plugins/frotz | ||
12 | |||
13 | ROCKS += $(FROTZBUILDDIR)/frotz.rock | ||
14 | |||
15 | FROTZ_SRC := $(call preprocess, $(FROTZSRCDIR)/SOURCES) | ||
16 | FROTZ_OBJ := $(call c2obj, $(FROTZ_SRC)) | ||
17 | |||
18 | # add source files to OTHER_SRC to get automatic dependencies | ||
19 | OTHER_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 | */ | ||
40 | extern int ungetc(int c, int f); | ||
41 | extern int fgetc(int f); | ||
42 | extern 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 | */ | ||
48 | extern 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 | |||
26 | extern int restore_undo (void); | ||
27 | |||
28 | extern int read_number (void); | ||
29 | |||
30 | extern bool read_yes_or_no (const char *); | ||
31 | |||
32 | extern void replay_open (void); | ||
33 | extern void replay_close (void); | ||
34 | extern void record_open (void); | ||
35 | extern void record_close (void); | ||
36 | |||
37 | extern void seed_random (int); | ||
38 | |||
39 | /* | ||
40 | * hot_key_debugging | ||
41 | * | ||
42 | * ...allows user to toggle cheating options on/off. | ||
43 | * | ||
44 | */ | ||
45 | |||
46 | static 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 | |||
66 | static 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 | |||
85 | static 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 | |||
110 | static 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 | |||
127 | static 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 | |||
157 | static 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 | |||
176 | bool 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 | |||
195 | bool 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 | |||
25 | extern int save_undo (void); | ||
26 | |||
27 | extern zchar stream_read_key (zword, zword, bool); | ||
28 | extern zchar stream_read_input (int, zchar *, zword, zword, bool, bool); | ||
29 | |||
30 | extern 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 | |||
39 | bool 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 | |||
77 | void 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 | |||
88 | extern 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 | |||
97 | void 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 | |||
118 | int 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 | |||
144 | void 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 | |||
254 | void 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 | |||
286 | void 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 | |||
37 | extern void interpret (void); | ||
38 | extern void init_memory (void); | ||
39 | extern void init_undo (void); | ||
40 | extern void reset_memory (void); | ||
41 | extern void init_buffer (void); | ||
42 | extern void init_sound (void); | ||
43 | extern void init_process (void); | ||
44 | extern void script_close (void); | ||
45 | extern void record_close (void); | ||
46 | extern void replay_close (void); | ||
47 | |||
48 | /* Story file name, id number and size */ | ||
49 | |||
50 | const char *story_name = 0; | ||
51 | |||
52 | enum story story_id = UNKNOWN; | ||
53 | long story_size = 0; | ||
54 | |||
55 | /* Story file header data */ | ||
56 | |||
57 | zbyte h_version = 0; | ||
58 | zbyte h_config = 0; | ||
59 | zword h_release = 0; | ||
60 | zword h_resident_size = 0; | ||
61 | zword h_start_pc = 0; | ||
62 | zword h_dictionary = 0; | ||
63 | zword h_objects = 0; | ||
64 | zword h_globals = 0; | ||
65 | zword h_dynamic_size = 0; | ||
66 | zword h_flags = 0; | ||
67 | zbyte h_serial[6] = { 0, 0, 0, 0, 0, 0 }; | ||
68 | zword h_abbreviations = 0; | ||
69 | zword h_file_size = 0; | ||
70 | zword h_checksum = 0; | ||
71 | zbyte h_interpreter_number = 0; | ||
72 | zbyte h_interpreter_version = 0; | ||
73 | zbyte h_screen_rows = 0; | ||
74 | zbyte h_screen_cols = 0; | ||
75 | zword h_screen_width = 0; | ||
76 | zword h_screen_height = 0; | ||
77 | zbyte h_font_height = 1; | ||
78 | zbyte h_font_width = 1; | ||
79 | zword h_functions_offset = 0; | ||
80 | zword h_strings_offset = 0; | ||
81 | zbyte h_default_background = 0; | ||
82 | zbyte h_default_foreground = 0; | ||
83 | zword h_terminating_keys = 0; | ||
84 | zword h_line_width = 0; | ||
85 | zbyte h_standard_high = 1; | ||
86 | zbyte h_standard_low = 0; | ||
87 | zword h_alphabet = 0; | ||
88 | zword h_extension_table = 0; | ||
89 | zbyte h_user_name[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; | ||
90 | |||
91 | zword hx_table_size = 0; | ||
92 | zword hx_mouse_x = 0; | ||
93 | zword hx_mouse_y = 0; | ||
94 | zword hx_unicode_table = 0; | ||
95 | |||
96 | /* Stack data */ | ||
97 | |||
98 | zword stack[STACK_SIZE]; | ||
99 | zword *sp = 0; | ||
100 | zword *fp = 0; | ||
101 | zword frame_count = 0; | ||
102 | |||
103 | /* IO streams */ | ||
104 | |||
105 | bool ostream_screen = TRUE; | ||
106 | bool ostream_script = FALSE; | ||
107 | bool ostream_memory = FALSE; | ||
108 | bool ostream_record = FALSE; | ||
109 | bool istream_replay = FALSE; | ||
110 | bool message = FALSE; | ||
111 | |||
112 | /* Current window and mouse data */ | ||
113 | |||
114 | int cwin = 0; | ||
115 | int mwin = 0; | ||
116 | |||
117 | int mouse_y = 0; | ||
118 | int mouse_x = 0; | ||
119 | |||
120 | /* Window attributes */ | ||
121 | |||
122 | bool enable_wrapping = FALSE; | ||
123 | bool enable_scripting = FALSE; | ||
124 | bool enable_scrolling = FALSE; | ||
125 | bool enable_buffering = FALSE; | ||
126 | |||
127 | /* User options */ | ||
128 | |||
129 | /* | ||
130 | int option_attribute_assignment = 0; | ||
131 | int option_attribute_testing = 0; | ||
132 | int option_context_lines = 0; | ||
133 | int option_object_locating = 0; | ||
134 | int option_object_movement = 0; | ||
135 | int option_left_margin = 0; | ||
136 | int option_right_margin = 0; | ||
137 | int option_ignore_errors = 0; | ||
138 | int option_piracy = 0; | ||
139 | int option_undo_slots = MAX_UNDO_SLOTS; | ||
140 | int option_expand_abbreviations = 0; | ||
141 | int option_script_cols = 80; | ||
142 | int option_save_quetzal = 1; | ||
143 | */ | ||
144 | |||
145 | int option_sound = 1; | ||
146 | char *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 | |||
156 | void 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 | |||
170 | int 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 | |||
31 | void 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 | |||
46 | void 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 | |||
61 | void 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 | |||
79 | void 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 | |||
99 | void 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 | |||
117 | void 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 | |||
132 | void 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 | |||
146 | void 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 | |||
161 | void 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 | |||
179 | void 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 | |||
197 | void 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 | |||
211 | void 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 | |||
226 | void 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 | |||
241 | void 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 | |||
256 | void 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 | |||
44 | static 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 | |||
74 | zword 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 | |||
102 | static 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 | |||
128 | static 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 | |||
165 | static 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 | |||
268 | void 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 | |||
316 | void 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 | |||
376 | void 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 | |||
438 | void 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 | |||
488 | void 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 | |||
547 | void 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 | |||
614 | void 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 | |||
666 | void 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 | |||
703 | void 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 | |||
756 | void 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 | |||
832 | void 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 | |||
886 | void 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 | |||
912 | void 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 | |||
966 | void 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 | |||
28 | zword zargs[8]; | ||
29 | int zargc; | ||
30 | |||
31 | static int finished = 0; | ||
32 | |||
33 | static void __extended__ (void); | ||
34 | static void __illegal__ (void); | ||
35 | |||
36 | void (*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 | |||
55 | void (*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 | |||
74 | void (*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 | |||
141 | void (*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 | |||
181 | void 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 | |||
194 | static 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 | |||
234 | static 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 | |||
258 | void 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 | |||
326 | void 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 | |||
402 | void 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 | |||
449 | void 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 | |||
495 | void 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 | |||
525 | int 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 | |||
567 | static 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 | |||
589 | static 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 | |||
603 | void 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 | |||
618 | void 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 | |||
649 | void 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 | |||
667 | void 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 | |||
684 | void 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 | |||
701 | void 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 | |||
723 | void 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 | |||
737 | void 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 | |||
751 | void 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 | |||
765 | void 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 | |||
779 | void 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 | |||
793 | void 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 | |||
30 | typedef 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 | |||
37 | static 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. */ | ||
81 | static 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. */ | ||
93 | static 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 | |||
112 | zword 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 | |||
398 | zword 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 | |||
23 | static long A = 1; | ||
24 | |||
25 | static int interval = 0; | ||
26 | static int counter = 0; | ||
27 | |||
28 | /* | ||
29 | * seed_random | ||
30 | * | ||
31 | * Set the seed value for the random number generator. | ||
32 | * | ||
33 | */ | ||
34 | |||
35 | void 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 | |||
58 | void 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 | |||
25 | extern zword get_max_width (zword); | ||
26 | |||
27 | static int depth = -1; | ||
28 | |||
29 | static 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 | |||
43 | void 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 | |||
73 | void 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 | |||
104 | void 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 | |||
148 | void 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 | |||
23 | extern void set_header_extension (int, zword); | ||
24 | |||
25 | extern int direct_call (zword); | ||
26 | |||
27 | static 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 | |||
42 | static int font_height = 1; | ||
43 | static int font_width = 1; | ||
44 | |||
45 | static bool input_redraw = FALSE; | ||
46 | static bool more_prompts = TRUE; | ||
47 | static bool discarding = FALSE; | ||
48 | static bool cursor = TRUE; | ||
49 | |||
50 | static int input_window = 0; | ||
51 | |||
52 | static 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 | |||
80 | static 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 | |||
102 | static 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 | |||
122 | static 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 | |||
138 | static 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 | |||
160 | void 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 | |||
177 | static 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 | |||
192 | zword 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 | |||
214 | static 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 | |||
230 | void 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 | |||
305 | void 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 | |||
336 | void 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 | |||
390 | void 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 | |||
413 | void 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 | |||
442 | zchar 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 | |||
486 | zchar 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 | |||
509 | static 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 | |||
538 | void 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 | |||
568 | static 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 | |||
604 | void 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 | |||
634 | void 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 | |||
682 | static 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 | |||
708 | void 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 | |||
732 | void 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 | |||
798 | bool 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 | |||
844 | void 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 | |||
867 | void 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 | |||
887 | void 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 | |||
921 | void 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 | |||
988 | void 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 | |||
1018 | void 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 | |||
1048 | void 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 | |||
1067 | void 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 | |||
1094 | void 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 | |||
1113 | void 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 | |||
1129 | void 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 | |||
1151 | void 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 | |||
1193 | void 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 | |||
1213 | void 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 | |||
1271 | void 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 | |||
1291 | void 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 | |||
1326 | void 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 | |||
1388 | void 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 | |||
1424 | void 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 | |||
1491 | void 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 | |||
1520 | void 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 | |||
1541 | void 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 | |||
1555 | static 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 | |||
1577 | void 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 | |||
1678 | void 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 | |||
1694 | void 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 | |||
1719 | void 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 | |||
6 | typedef 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 | |||
25 | extern f_setup_t f_setup; | ||
26 | |||
27 | |||
28 | typedef 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 | |||
32 | extern int direct_call (zword); | ||
33 | |||
34 | static zword routine = 0; | ||
35 | |||
36 | static int next_sample = 0; | ||
37 | static int next_volume = 0; | ||
38 | |||
39 | static bool locked = FALSE; | ||
40 | static bool playing = FALSE; | ||
41 | |||
42 | /* | ||
43 | * init_sound | ||
44 | * | ||
45 | * Initialize sound variables. | ||
46 | * | ||
47 | */ | ||
48 | |||
49 | void 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 | |||
63 | static 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 | |||
92 | static 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 | |||
112 | void 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 | |||
145 | void 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 | |||
23 | extern bool handle_hot_key (zchar); | ||
24 | |||
25 | extern bool validate_click (void); | ||
26 | |||
27 | extern void replay_open (void); | ||
28 | extern void replay_close (void); | ||
29 | extern void memory_open (zword, zword, bool); | ||
30 | extern void memory_close (void); | ||
31 | extern void record_open (void); | ||
32 | extern void record_close (void); | ||
33 | extern void script_open (void); | ||
34 | extern void script_close (void); | ||
35 | |||
36 | extern void memory_word (const zchar *); | ||
37 | extern void memory_new_line (void); | ||
38 | extern void record_write_key (zchar); | ||
39 | extern void record_write_input (const zchar *, zchar); | ||
40 | extern void script_char (zchar); | ||
41 | extern void script_word (const zchar *); | ||
42 | extern void script_new_line (void); | ||
43 | extern void script_write_input (const zchar *, zchar); | ||
44 | extern void script_erase_input (const zchar *); | ||
45 | extern void script_mssg_on (void); | ||
46 | extern void script_mssg_off (void); | ||
47 | extern void screen_char (zchar); | ||
48 | extern void screen_word (const zchar *); | ||
49 | extern void screen_new_line (void); | ||
50 | extern void screen_write_input (const zchar *, zchar); | ||
51 | extern void screen_erase_input (const zchar *); | ||
52 | extern void screen_mssg_on (void); | ||
53 | extern void screen_mssg_off (void); | ||
54 | |||
55 | extern zchar replay_read_key (void); | ||
56 | extern zchar replay_read_input (zchar *); | ||
57 | extern zchar console_read_key (zword); | ||
58 | extern zchar console_read_input (int, zchar *, zword, bool); | ||
59 | |||
60 | extern int direct_call (zword); | ||
61 | |||
62 | /* | ||
63 | * stream_mssg_on | ||
64 | * | ||
65 | * Start printing a "debugging" message. | ||
66 | * | ||
67 | */ | ||
68 | |||
69 | void 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 | |||
90 | void 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 | |||
113 | void 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 | |||
148 | void 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 | |||
165 | void 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 | |||
190 | void 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 | |||
215 | void 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 | |||
234 | zchar 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 | |||
243 | continue_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 | |||
297 | zchar 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 | |||
315 | continue_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 | |||
35 | void 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 | |||
73 | void 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 | |||
92 | void 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 | |||
116 | void 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 | |||
156 | finished: | ||
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 | |||
172 | void 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 | |||
188 | void 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 | |||
23 | enum string_type { | ||
24 | LOW_STRING, ABBREVIATION, HIGH_STRING, EMBEDDED_STRING, VOCABULARY | ||
25 | }; | ||
26 | |||
27 | extern zword object_name (zword); | ||
28 | |||
29 | static zchar decoded[10]; | ||
30 | static 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 | */ | ||
37 | static 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 | |||
56 | zchar 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 | |||
108 | zbyte 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 | |||
168 | static 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 | |||
200 | static 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 | |||
234 | static 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 | |||
316 | void 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 | |||
344 | void 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 | |||
380 | static 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 | |||
530 | void 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 | |||
544 | void 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 | |||
558 | void 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 | |||
572 | void 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 | |||
586 | void 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 | |||
628 | void 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 | |||
654 | void 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 | |||
668 | void 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 | |||
696 | void 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 | |||
710 | void 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 | |||
724 | void 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 | |||
740 | void 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 | |||
760 | void 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 | |||
781 | static 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 | |||
875 | static 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 | |||
913 | void 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 | |||
1017 | void 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 | |||
1053 | int 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 | |||
30 | void 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 | |||
55 | void 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 | |||
81 | void 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 | |||
106 | void 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 | |||
132 | void 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 | |||
156 | void 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 | |||
171 | void 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 | |||
201 | void 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 | |||
248 | void 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 | |||
263 | void 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 | |||
291 | void 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,- | |||
61 | lua,viewers/lua,- | 61 | lua,viewers/lua,- |
62 | ipod,viewers/crypt_firmware,- | 62 | ipod,viewers/crypt_firmware,- |
63 | ipodx,viewers/crypt_firmware,- | 63 | ipodx,viewers/crypt_firmware,- |
64 | z1,viewers/frotz,- | ||
65 | z2,viewers/frotz,- | ||
66 | z3,viewers/frotz,- | ||
67 | z4,viewers/frotz,- | ||
68 | z5,viewers/frotz,- | ||
69 | z6,viewers/frotz,- | ||
70 | z7,viewers/frotz,- | ||
71 | z8,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} | ||
3 | Frotz is a Z-Machine interpreter for playing Infocom's interactive fiction | ||
4 | games, 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 | ||
6 | in the \fname{.z5} or \fname{.z8} format but the older formats used by | ||
7 | Infocom are supported. | ||
8 | |||
9 | Z-Machine games are text based and most depend heavily on typed commands. | ||
10 | The virtual keyboard is used for text entry, both for typing entire lines | ||
11 | and for typing single characters when the game requires single character | ||
12 | input. | ||
13 | |||
14 | Sounds, pictures, colour and Unicode are not currently supported, but | ||
15 | the interpreter informs the game of this and almost all games will | ||
16 | adapt so that they are still playable. This port of Frotz attempts to be | ||
17 | compliant with the Z-Machine Specification version 1.0. | ||
18 | |||
19 | Some places where you can find Z-Machine games, and information about | ||
20 | interactive fiction: | ||
21 | \begin{itemize} | ||
22 | \item The Interactive Fiction Archive, where many free modern works | ||
23 | can 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 | ||
28 | classic 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}} |