Update the discussion of themeing in the manual, and put a note in the wps tags appen...
[kugel-rb.git] / apps / plugins / frotz / fastmem.c
blobce424af1f2ed77827369b2038e71bc7e92c796a3
1 /* fastmem.c - Memory related functions (fast version without virtual memory)
2 * Copyright (c) 1995-1997 Stefan Jokisch
4 * Changes for Rockbox copyright 2009 Torne Wuff
6 * This file is part of Frotz.
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.
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.
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
24 * New undo mechanism added by Jim Dunleavy <jim.dunleavy@erha.ie>
27 #include "frotz.h"
29 #define far
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);
39 extern zword save_quetzal (int, int);
40 extern zword restore_quetzal (int, int);
42 extern void erase_window (zword);
44 extern void (*op0_opcodes[]) (void);
45 extern void (*op1_opcodes[]) (void);
46 extern void (*op2_opcodes[]) (void);
47 extern void (*var_opcodes[]) (void);
49 char save_name[MAX_PATH];
50 char auxilary_name[MAX_PATH];
52 zbyte far *zmp = NULL;
53 zbyte far *pcp = NULL;
55 int story_fp = -1;
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.
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 */
76 static undo_t *first_undo = NULL, *last_undo = NULL, *curr_undo = NULL;
77 static zbyte *prev_zmp, *undo_diff;
79 static int undo_count = 0;
81 static void *arena_start = NULL, *arena_end = NULL, *arena_next = NULL;
84 * get_header_extension
86 * Read a value from the header extension (former mouse table).
90 zword get_header_extension (int entry)
92 zword addr;
93 zword val;
95 if (h_extension_table == 0 || entry > hx_table_size)
96 return 0;
98 addr = h_extension_table + 2 * entry;
99 LOW_WORD (addr, val)
101 return val;
103 }/* get_header_extension */
106 * set_header_extension
108 * Set an entry in the header extension (former mouse table).
112 void set_header_extension (int entry, zword val)
114 zword addr;
116 if (h_extension_table == 0 || entry > hx_table_size)
117 return;
119 addr = h_extension_table + 2 * entry;
120 SET_WORD (addr, val)
122 }/* set_header_extension */
125 * restart_header
127 * Set all header fields which hold information about the interpreter.
131 void restart_header (void)
133 zword screen_x_size;
134 zword screen_y_size;
135 zbyte font_x_size;
136 zbyte font_y_size;
138 int i;
140 SET_BYTE (H_CONFIG, h_config)
141 SET_WORD (H_FLAGS, h_flags)
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)
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" */
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;
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)
174 if (h_version == V6)
175 for (i = 0; i < 8; i++)
176 storeb ((zword) (H_USER_NAME + i), h_user_name[i]);
178 SET_BYTE (H_STANDARD_HIGH, h_standard_high)
179 SET_BYTE (H_STANDARD_LOW, h_standard_low)
181 }/* restart_header */
184 * init_memory
186 * Allocate memory and load the story file.
190 void init_memory (void)
192 long size;
193 zword addr;
194 unsigned n;
195 int i, j;
196 zbyte *audiobuf;
197 size_t buf_size;
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, "------" }
231 /* Open story file */
233 if ((story_fp = rb->open(story_name, O_RDONLY)) < 0)
234 os_fatal ("Cannot open story file");
236 /* Allocate memory for story header */
238 zmp = rb->plugin_get_buffer(&buf_size);
240 /* Load header into memory */
242 if (fread (zmp, 1, 64, story_fp) != 64)
243 os_fatal ("Story file read error");
245 /* Copy header fields to global variables */
247 LOW_BYTE (H_VERSION, h_version)
249 if (h_version < V1 || h_version > V8)
250 os_fatal ("Unknown Z-code version");
252 LOW_BYTE (H_CONFIG, h_config)
254 if (h_version == V3 && (h_config & CONFIG_BYTE_SWAPPED))
255 os_fatal ("Byte swapped story file");
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)
266 for (i = 0, addr = H_SERIAL; i < 6; i++, addr++)
267 LOW_BYTE (addr, h_serial[i])
269 /* Auto-detect buggy story files that need special fixes */
271 story_id = UNKNOWN;
273 for (i = 0; records[i].story_id != UNKNOWN; i++) {
275 if (h_release == records[i].release) {
277 for (j = 0; j < 6; j++)
278 if (h_serial[j] != records[i].serial[j])
279 goto no_match;
281 story_id = records[i].story_id;
285 no_match: ; /* null statement */
289 LOW_WORD (H_ABBREVIATIONS, h_abbreviations)
290 LOW_WORD (H_FILE_SIZE, h_file_size)
292 /* Calculate story file size in bytes */
294 if (h_file_size != 0) {
296 story_size = (long) 2 * h_file_size;
298 if (h_version >= V4)
299 story_size *= 2;
300 if (h_version >= V6)
301 story_size *= 2;
303 } else { /* some old games lack the file size entry */
305 fseek (story_fp, 0, SEEK_END);
306 story_size = ftell (story_fp);
307 fseek (story_fp, 64, SEEK_SET);
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)
318 /* Zork Zero Macintosh doesn't have the graphics flag set */
320 if (story_id == ZORK_ZERO && h_release == 296)
321 h_flags |= GRAPHICS_FLAG;
323 /* Adjust opcode tables */
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;
333 /* Allocate memory for story data */
335 if ((size_t)story_size > buf_size)
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;
344 /* Assign left over memory as the arena for undo alloc */
345 arena_start = arena_next = (void*)((intptr_t)(zmp + story_size + 3) & ~3);
346 arena_end = zmp + buf_size;
348 /* Load story file in chunks of 32KB */
350 n = 0x8000;
352 for (size = 64; size < story_size; size += n) {
354 if (story_size - size < 0x8000)
355 n = (unsigned) (story_size - size);
357 SET_PC (size)
359 if (fread (pcp, 1, n, story_fp) != (signed)n)
360 os_fatal ("Story file read error");
364 /* Read header extension table */
366 hx_table_size = get_header_extension (HX_TABLE_SIZE);
367 hx_unicode_table = get_header_extension (HX_UNICODE_TABLE);
369 }/* init_memory */
372 * init_undo
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.
380 void init_undo (void)
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*)((intptr_t)(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;
394 }/* init_undo */
397 * free_undo
399 * Free count undo blocks from the beginning of the undo list.
403 static void free_undo (int count)
405 undo_t *p;
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--;
416 if (first_undo)
417 first_undo->prev = NULL;
418 else
419 last_undo = NULL;
420 }/* free_undo */
423 * reset_memory
425 * Close the story file and deallocate memory.
429 void reset_memory (void)
431 if (story_fp != -1)
432 fclose (story_fp);
433 story_fp = -1;
435 free_undo (undo_count);
436 undo_count = 0;
438 arena_start = NULL;
439 arena_end = NULL;
440 arena_next = NULL;
441 zmp = NULL;
442 }/* reset_memory */
445 * storeb
447 * Write a byte value to the dynamic Z-machine memory.
451 void storeb (zword addr, zbyte value)
454 if (addr >= h_dynamic_size)
455 runtime_error (ERR_STORE_RANGE);
457 if (addr == H_FLAGS + 1) { /* flags register is modified */
459 h_flags &= ~(SCRIPTING_FLAG | FIXED_FONT_FLAG);
460 h_flags |= value & (SCRIPTING_FLAG | FIXED_FONT_FLAG);
462 if (value & SCRIPTING_FLAG) {
463 if (!ostream_script)
464 script_open ();
465 } else {
466 if (ostream_script)
467 script_close ();
470 refresh_text_style ();
474 SET_BYTE (addr, value)
476 }/* storeb */
479 * storew
481 * Write a word value to the dynamic Z-machine memory.
485 void storew (zword addr, zword value)
488 storeb ((zword) (addr + 0), hi (value));
489 storeb ((zword) (addr + 1), lo (value));
491 }/* storew */
494 * z_restart, re-load dynamic area, clear the stack and set the PC.
496 * no zargs used
500 void z_restart (void)
502 static bool first_restart = TRUE;
504 flush_buffer ();
506 os_restart_game (RESTART_BEGIN);
508 seed_random (0);
510 if (!first_restart) {
512 fseek (story_fp, 0, SEEK_SET);
514 if (fread (zmp, 1, h_dynamic_size, story_fp) != h_dynamic_size)
515 os_fatal ("Story file read error");
517 } else first_restart = FALSE;
519 restart_header ();
520 restart_screen ();
522 sp = fp = stack + STACK_SIZE;
523 frame_count = 0;
525 if (h_version != V6) {
527 long pc = (long) h_start_pc;
528 SET_PC (pc)
530 } else call (h_start_pc, 0, NULL, 0);
532 os_restart_game (RESTART_END);
534 }/* z_restart */
537 * get_default_name
539 * Read a default file name from the memory of the Z-machine and
540 * copy it to a string.
544 static void get_default_name (char *default_name, zword addr)
547 if (addr != 0) {
549 zbyte len;
550 int i;
552 LOW_BYTE (addr, len)
553 addr++;
555 for (i = 0; i < len; i++) {
557 zbyte c;
559 LOW_BYTE (addr, c)
560 addr++;
562 if (c >= 'A' && c <= 'Z')
563 c += 'a' - 'A';
565 default_name[i] = c;
569 default_name[i] = 0;
571 if (strchr (default_name, '.') == NULL)
572 strcpy (default_name + i, ".AUX");
574 } else strcpy (default_name, auxilary_name);
576 }/* get_default_name */
579 * z_restore, restore [a part of] a Z-machine state from disk
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
587 void z_restore (void)
589 char new_name[MAX_PATH];
590 char default_name[MAX_PATH];
591 int gfp;
593 zword success = 0;
595 if (zargc != 0) {
597 /* Get the file name */
599 get_default_name (default_name, (zargc >= 3) ? zargs[2] : 0);
601 if (os_read_file_name (new_name, default_name, FILE_LOAD_AUX) == 0)
602 goto finished;
604 strcpy (auxilary_name, default_name);
606 /* Open auxilary file */
608 if ((gfp = rb->open (new_name, O_RDONLY)) < 0)
609 goto finished;
611 /* Load auxilary file */
613 success = fread (zmp + zargs[0], 1, zargs[1], gfp);
615 /* Close auxilary file */
617 fclose (gfp);
619 } else {
621 /* Get the file name */
623 if (os_read_file_name (new_name, save_name, FILE_RESTORE) == 0)
624 goto finished;
626 strcpy (save_name, new_name);
628 /* Open game file */
630 if ((gfp = rb->open (new_name, O_RDONLY)) < 0)
631 goto finished;
633 success = restore_quetzal (gfp, story_fp);
635 /* Close game file */
637 fclose (gfp);
639 if ((short) success >= 0) {
641 if ((short) success > 0) {
642 zbyte old_screen_rows;
643 zbyte old_screen_cols;
645 /* In V3, reset the upper window. */
646 if (h_version == V3)
647 split_window (0);
649 LOW_BYTE (H_SCREEN_ROWS, old_screen_rows);
650 LOW_BYTE (H_SCREEN_COLS, old_screen_cols);
652 /* Reload cached header fields. */
653 restart_header ();
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.
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);
665 } else
666 os_fatal ("Error reading save file");
669 finished:
671 if (h_version <= V3)
672 branch (success);
673 else
674 store (success);
676 }/* z_restore */
679 * mem_diff
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.
689 static long mem_diff (zbyte *a, zbyte *b, zword mem_size, zbyte *diff)
691 unsigned size = mem_size;
692 zbyte *p = diff;
693 unsigned j;
694 zbyte c = 0;
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;
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;
717 *p++ = c;
718 *(b - 1) ^= c;
720 return p - diff;
721 }/* mem_diff */
724 * mem_undiff
726 * Applies a quetzal-like diff to dest
730 static void mem_undiff (zbyte *diff, long diff_length, zbyte *dest)
732 zbyte c;
734 while (diff_length) {
735 c = *diff++;
736 diff_length--;
737 if (c == 0) {
738 unsigned runlen;
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);
752 dest += runlen + 1;
753 } else {
754 *dest++ ^= c;
757 }/* mem_undiff */
760 * restore_undo
762 * This function does the dirty work for z_restore_undo.
766 int restore_undo (void)
769 if (f_setup.undo_slots == 0) /* undo feature unavailable */
771 return -1;
773 if (curr_undo == NULL) /* no saved game state */
775 return 0;
777 /* undo possible */
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));
788 curr_undo = curr_undo->prev;
790 restart_header ();
792 return 2;
794 }/* restore_undo */
797 * z_restore_undo, restore a Z-machine state from memory.
799 * no zargs used
803 void z_restore_undo (void)
806 store ((zword) restore_undo ());
808 }/* z_restore_undo */
811 * z_save, save [a part of] the Z-machine state to disk.
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
819 void z_save (void)
821 char new_name[MAX_PATH];
822 char default_name[MAX_PATH];
823 int gfp;
825 zword success = 0;
827 if (zargc != 0) {
829 /* Get the file name */
831 get_default_name (default_name, (zargc >= 3) ? zargs[2] : 0);
833 if (os_read_file_name (new_name, default_name, FILE_SAVE_AUX) == 0)
834 goto finished;
836 strcpy (auxilary_name, default_name);
838 /* Open auxilary file */
840 if ((gfp = rb->open (new_name, O_WRONLY|O_CREAT|O_TRUNC, 0666)) < 0)
841 goto finished;
843 /* Write auxilary file */
845 success = fwrite (zmp + zargs[0], zargs[1], 1, gfp);
847 /* Close auxilary file */
849 fclose (gfp);
851 } else {
853 /* Get the file name */
855 if (os_read_file_name (new_name, save_name, FILE_SAVE) == 0)
856 goto finished;
858 strcpy (save_name, new_name);
860 /* Open game file */
862 if ((gfp = rb->open (new_name, O_WRONLY|O_CREAT|O_TRUNC, 0666)) < 0)
863 goto finished;
865 success = save_quetzal (gfp, story_fp);
867 /* Close game file and check for errors */
869 if (fclose (gfp) != 0 || ferror (story_fp)) {
870 print_string ("Error writing save file\n");
871 goto finished;
874 /* Success */
876 success = 1;
880 finished:
882 if (h_version <= V3)
883 branch (success);
884 else
885 store (success);
887 }/* z_save */
890 * save_undo
892 * This function does the dirty work for z_save_undo.
896 int save_undo (void)
898 long diff_size;
899 zword stack_size;
900 int size;
901 undo_t *p;
903 if (f_setup.undo_slots == 0) /* undo feature unavailable */
904 return -1;
906 /* save undo possible */
908 while (last_undo != curr_undo) {
909 p = last_undo;
910 last_undo = last_undo->prev;
911 arena_next = p;
912 undo_count--;
914 if (last_undo)
915 last_undo->next = NULL;
916 else
917 first_undo = NULL;
919 if (undo_count == f_setup.undo_slots)
920 free_undo (1);
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*)((intptr_t)(arena_next + size + 3) & ~3);
932 } else {
933 /* Need to wrap */
934 arena_next = arena_start;
935 p = NULL;
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*)((intptr_t)(arena_next + size + 3) & ~3);
943 } else {
944 /* Not enough room, just need to free some */
945 p = NULL;
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));
962 if (!first_undo) {
963 p->prev = NULL;
964 first_undo = p;
965 } else {
966 last_undo->next = p;
967 p->prev = last_undo;
969 p->next = NULL;
970 curr_undo = last_undo = p;
971 undo_count++;
972 return 1;
974 }/* save_undo */
977 * z_save_undo, save the current Z-machine state for a future undo.
979 * no zargs used
983 void z_save_undo (void)
986 store ((zword) save_undo ());
988 }/* z_save_undo */
991 * z_verify, check the story file integrity.
993 * no zargs used
997 void z_verify (void)
999 zword checksum = 0;
1000 long i;
1002 /* Sum all bytes in story file except header bytes */
1004 fseek (story_fp, 64, SEEK_SET);
1006 for (i = 64; i < story_size; i++)
1007 checksum += fgetc (story_fp);
1009 /* Branch if the checksums are equal */
1011 branch (checksum == h_checksum);
1013 }/* z_verify */