home *** CD-ROM | disk | FTP | other *** search
/ Team Palmtops 7 / Palmtops_numero07.iso / Epoc / Palmtime / files / FrotzCE2_src.ZIP / FrotzCE / FASTMEM.C < prev    next >
Encoding:
C/C++ Source or Header  |  1998-08-23  |  16.4 KB  |  872 lines

  1. /*
  2.  * fastmem.c
  3.  *
  4.  * Memory related functions (fast version without virtual memory)
  5.  *
  6.  */
  7.  
  8. #ifndef _WIN32_WCE
  9. #include <stdio.h>
  10. #else
  11. #include "FrotzCEIO.h"
  12. #endif
  13.  
  14. #include <string.h>
  15.  
  16. #ifndef _WIN32_WCE
  17. #include "frotz.h"
  18. #else
  19. #include "Frotz\Frotz.h"
  20. #endif
  21.  
  22. #ifdef __MSDOS__
  23.  
  24. #include <alloc.h>
  25.  
  26. #define malloc(size)    farmalloc (size)
  27. #define realloc(size,p)    farrealloc (size,p)
  28. #define free(size)    farfree (size)
  29. #define memcpy(d,s,n)    _fmemcpy (d,s,n)
  30.  
  31. #else
  32.  
  33. #ifdef _WIN32_WCE
  34.  
  35. #include <windows.h>
  36.  
  37. #define malloc( size )        LocalAlloc( LPTR, (UINT) size )
  38. #define realloc( size, p )    LocalReAlloc( (HLOCAL) p, (UINT) size, 0 )
  39. #define free( p )            LocalFree( (HLOCAL) p )
  40.  
  41. #ifndef SEEK_SET
  42. #define SEEK_SET 0
  43. #define SEEK_CUR 1
  44. #define SEEK_END 2
  45. #endif
  46.  
  47. #define far
  48.  
  49. #endif
  50.  
  51. #endif
  52.  
  53. extern void (*op0_opcodes[]) (void);
  54. extern void (*op1_opcodes[]) (void);
  55. extern void (*op2_opcodes[]) (void);
  56. extern void (*var_opcodes[]) (void);
  57.  
  58. char save_name[MAX_FILE_NAME + 1] = DEFAULT_SAVE_NAME;
  59. char auxilary_name[MAX_FILE_NAME + 1] = DEFAULT_AUXILARY_NAME;
  60.  
  61. zbyte far *zmp = NULL;
  62. zbyte far *pcp = NULL;
  63.  
  64. FILE *story_fp = NULL;
  65.  
  66. zbyte far *undo[MAX_UNDO_SLOTS];
  67.  
  68. int undo_slots = 0;
  69. int undo_count = 0;
  70. int undo_valid = 0;
  71.  
  72. /*
  73.  * restart_header
  74.  *
  75.  * Set all header fields which hold information about the interpreter.
  76.  *
  77.  */
  78.  
  79. void restart_header (void)
  80. {
  81.     zword screen_x_size;
  82.     zword screen_y_size;
  83.     zbyte font_x_size;
  84.     zbyte font_y_size;
  85.  
  86.     int i;
  87.  
  88.     SET_BYTE (H_CONFIG, h_config)
  89.     SET_WORD (H_FLAGS, h_flags)
  90.  
  91.     if (h_version >= V4) {
  92.     SET_BYTE (H_INTERPRETER_NUMBER, h_interpreter_number)
  93.     SET_BYTE (H_INTERPRETER_VERSION, h_interpreter_version)
  94.     SET_BYTE (H_SCREEN_ROWS, h_screen_rows)
  95.     SET_BYTE (H_SCREEN_COLS, h_screen_cols)
  96.     }
  97.  
  98.     /* It's less trouble to use font size 1x1 for V5 games, especially
  99.        because of a bug in the unreleased German version of "Zork 1" */
  100.  
  101.     if (h_version != V6) {
  102.     screen_x_size = ZWORD (h_screen_cols);
  103.     screen_y_size = ZWORD (h_screen_rows);
  104.     font_x_size = 1;
  105.     font_y_size = 1;
  106.     } else {
  107.     screen_x_size = h_screen_width;
  108.     screen_y_size = h_screen_height;
  109.     font_x_size = h_font_width;
  110.     font_y_size = h_font_height;
  111.     }
  112.  
  113.     if (h_version >= V5) {
  114.     SET_WORD (H_SCREEN_WIDTH, screen_x_size)
  115.     SET_WORD (H_SCREEN_HEIGHT, screen_y_size)
  116.     SET_BYTE (H_FONT_HEIGHT, font_y_size)
  117.     SET_BYTE (H_FONT_WIDTH, font_x_size)
  118.     SET_BYTE (H_DEFAULT_BACKGROUND, h_default_background)
  119.     SET_BYTE (H_DEFAULT_FOREGROUND, h_default_foreground)
  120.     }
  121.  
  122.     if (h_version == V6)
  123.     for (i = 0; i < 8; i++)
  124.         storeb (ZWORD (H_USER_NAME + i), h_user_name[i]);
  125.  
  126.     SET_BYTE (H_STANDARD_HIGH, h_standard_high)
  127.     SET_BYTE (H_STANDARD_LOW, h_standard_low)
  128.  
  129. }/* restart_header */
  130.  
  131. /*
  132.  * init_memory
  133.  *
  134.  * Allocate memory and load the story file.
  135.  *
  136.  */
  137.  
  138. void init_memory (void)
  139. {
  140.     long size;
  141.     zword addr;
  142.     unsigned n;
  143.     int i, j;
  144.  
  145. #ifndef AMIGA
  146.  
  147.     /* Amiga interface has its own game detection routines */
  148.  
  149.     static struct {
  150.     zword release;
  151.     zbyte serial[6];
  152.     int id;
  153.     } records[] = {
  154.     { 21, "871214", SHERLOCK },
  155.     { 26, "880127", SHERLOCK },
  156.     { 47, "870915", BEYOND_ZORK },
  157.     { 49, "870917", BEYOND_ZORK },
  158.     { 51, "870923", BEYOND_ZORK },
  159.     { 57, "871221", BEYOND_ZORK }
  160.     };
  161.  
  162. #endif
  163.  
  164.     /* Open story file */
  165.  
  166.     if ((story_fp = fopen (story_name, "rb")) == NULL)
  167.     os_fatal ("Cannot open story file");
  168.  
  169.     /* Allocate memory for story header */
  170.  
  171.     if ((zmp = (zbyte far *) malloc (64)) == NULL)
  172.     os_fatal ("Out of memory");
  173.  
  174.     /* Load header into memory */
  175.  
  176.     if (fread (zmp, 1, 64, story_fp) != 64)
  177.     os_fatal ("Story file read error");
  178.  
  179.     /* Copy header fields to global variables */
  180.  
  181.     LOW_BYTE (H_VERSION, h_version)
  182.  
  183.     if (h_version < V1 || h_version > V8)
  184.     os_fatal ("Unknown Z-code version");
  185.  
  186.     LOW_BYTE (H_CONFIG, h_config)
  187.  
  188.     if (h_version == V3 && (h_config & CONFIG_BYTE_SWAPPED))
  189.     os_fatal ("Byte swapped story file");
  190.  
  191.     LOW_WORD (H_RELEASE, h_release)
  192.     LOW_WORD (H_RESIDENT_SIZE, h_resident_size)
  193.     LOW_WORD (H_START_PC, h_start_pc)
  194.     LOW_WORD (H_DICTIONARY, h_dictionary)
  195.     LOW_WORD (H_OBJECTS, h_objects)
  196.     LOW_WORD (H_GLOBALS, h_globals)
  197.     LOW_WORD (H_DYNAMIC_SIZE, h_dynamic_size)
  198.     LOW_WORD (H_FLAGS, h_flags)
  199.  
  200.     for (i = 0, addr = H_SERIAL; i < 6; i++, addr++)
  201.     LOW_BYTE (addr, h_serial[i])
  202.  
  203. #ifndef AMIGA
  204.  
  205.     /* Auto-detect buggy story files that need special fixes */
  206.  
  207.     for (i = 0; i < 6; i++) {
  208.  
  209.     if (h_release == records[i].release) {
  210.  
  211.         for (j = 0; j < 6; j++)
  212.         if (h_serial[j] != records[i].serial[j])
  213.             goto no_match;
  214.  
  215.         story_id = records[i].id;
  216.  
  217.     }
  218.  
  219.     }
  220.  
  221. no_match:
  222.  
  223. #endif
  224.  
  225.     LOW_WORD (H_ABBREVIATIONS, h_abbreviations)
  226.     LOW_WORD (H_FILE_SIZE, h_file_size)
  227.  
  228.     /* Calculate story file size in bytes */
  229.  
  230.     if (h_file_size) {
  231.  
  232.     story_size = (long) 2 * h_file_size;
  233.  
  234.     if (h_version >= V4)
  235.         story_size *= 2;
  236.     if (h_version >= V6)
  237.         story_size *= 2;
  238.  
  239.     } else {        /* some old games lack the file size entry */
  240.  
  241.     fseek (story_fp, 0, SEEK_END);
  242.     story_size = ftell (story_fp);
  243.     fseek (story_fp, 64, SEEK_SET);
  244.  
  245.     }
  246.  
  247.     LOW_WORD (H_CHECKSUM, h_checksum)
  248.     LOW_WORD (H_ALPHABET, h_alphabet)
  249.     LOW_WORD (H_FUNCTIONS_OFFSET, h_functions_offset)
  250.     LOW_WORD (H_STRINGS_OFFSET, h_strings_offset)
  251.     LOW_WORD (H_TERMINATING_KEYS, h_terminating_keys)
  252.     LOW_WORD (H_MOUSE_TABLE, h_mouse_table)
  253.  
  254.     /* Adjust opcode tables for pre-V5 games */
  255.  
  256.     if (h_version <= V4) 
  257.     {
  258.         op0_opcodes[0x09] = z_pop;
  259.         op1_opcodes[0x0f] = z_not;
  260.     }
  261.      else
  262.      {
  263.          op0_opcodes[0x09] = z_catch;
  264.          op1_opcodes[0x0f] = z_call_n;
  265.      }
  266.     /* Allocate memory for story data */
  267.  
  268.     free( zmp );
  269.     zmp = NULL;
  270.  
  271.     if ((zmp = (zbyte far *) malloc ( story_size)) == NULL)
  272.         os_fatal ("Out of memory");
  273.  
  274.     fseek( story_fp, 0, SEEK_SET );
  275.     
  276.     if (fread (zmp, 1, 64, story_fp) != 64)
  277.     os_fatal ("Story file read error");
  278.  
  279.     /* Load story file in chunks of 32KB */
  280.  
  281.     n = 0x8000;
  282.  
  283.     for (size = 64; size < story_size; size += n) {
  284.  
  285.     if (story_size - size < 0x8000)
  286.         n = (unsigned) (story_size - size);
  287.  
  288.     SET_PC (size)
  289.  
  290.     if (fread (pcp, 1, n, story_fp) != n)
  291.         os_fatal ("Story file read error");
  292.  
  293.     }
  294.  
  295. }/* init_memory */
  296.  
  297. /*
  298.  * init_undo
  299.  *
  300.  * Allocate memory for multiple undo. It is important not to occupy
  301.  * all the memory available, since the IO interface may need memory
  302.  * during the game, e.g. for loading sounds or pictures.
  303.  *
  304.  */
  305.  
  306. void init_undo (void)
  307. {
  308.     void far *reserved;
  309.  
  310.     undo_slots = 0;
  311.     undo_count = 0;
  312.     undo_valid = 0;
  313.  
  314.     if (reserve_mem)
  315.     if ((reserved = malloc (reserve_mem)) == NULL)
  316.         return;
  317.  
  318.     while (undo_slots < MAX_UNDO_SLOTS) {
  319.  
  320.     void far *mem = malloc ((long) sizeof (stack) + h_dynamic_size);
  321.  
  322.     if (mem == NULL)
  323.         break;
  324.  
  325.     undo[undo_slots++] = mem;
  326.  
  327.     }
  328.  
  329.     if (reserve_mem)
  330.     free (reserved);
  331.  
  332. }/* init_undo */
  333.  
  334. /*
  335.  * reset_memory
  336.  *
  337.  * Close the story file and deallocate memory.
  338.  *
  339.  */
  340.  
  341. void reset_memory (void)
  342. {
  343. int nSlot;
  344.  
  345.     fclose (story_fp);
  346.  
  347.     for (nSlot = 0; nSlot < MAX_UNDO_SLOTS; nSlot++)
  348.         free (undo[nSlot]);
  349.  
  350.     undo_slots = 0;
  351.     undo_count = 0;
  352.     undo_valid = 0;
  353.  
  354.     free (zmp);
  355.  
  356. }/* reset_memory */
  357.  
  358. /*
  359.  * storeb
  360.  *
  361.  * Write a byte value to the dynamic Z-machine memory.
  362.  *
  363.  */
  364.  
  365. void storeb (zword addr, zbyte value)
  366. {
  367.  
  368.     if (addr >= h_dynamic_size)
  369.     runtime_error ("Store out of dynamic memory");
  370.  
  371.     if (addr == H_FLAGS + 1) {    /* flags register is modified */
  372.  
  373.     h_flags &= ~(SCRIPTING_FLAG | FIXED_FONT_FLAG);
  374.     h_flags |= value & (SCRIPTING_FLAG | FIXED_FONT_FLAG);
  375.  
  376.     if (value & SCRIPTING_FLAG) {
  377.         if (!ostream_script)
  378.         script_open ();
  379.     } else {
  380.         if (ostream_script)
  381.         script_close ();
  382.     }
  383.  
  384.     refresh_text_style ();
  385.  
  386.     }
  387.  
  388.     SET_BYTE (addr, value)
  389.  
  390. }/* storeb */
  391.  
  392. /*
  393.  * storew
  394.  *
  395.  * Write a word value to the dynamic Z-machine memory.
  396.  *
  397.  */
  398.  
  399. void storew (zword addr, zword value)
  400. {
  401.  
  402.     storeb (ZWORD (addr + 0), (zbyte) hi (value));
  403.     storeb (ZWORD (addr + 1), (zbyte) lo (value));
  404.  
  405. }/* storew */
  406.  
  407. /*
  408.  * z_restart, re-load dynamic area, clear the stack and set the PC.
  409.  *
  410.  *     no zargs used
  411.  *
  412.  */
  413.  
  414. void z_restart (void)
  415. {
  416.     static first_restart = 1;
  417.  
  418.     restart_screen ();
  419.  
  420.     seed_random (0);
  421.  
  422.     if (!first_restart) {
  423.  
  424.     fseek (story_fp, 0, SEEK_SET);
  425.  
  426.     if (fread (zmp, 1, h_dynamic_size, story_fp) != h_dynamic_size)
  427.         os_fatal ("Story file read error");
  428.  
  429.     } else first_restart = 0;
  430.  
  431.     restart_header ();
  432.  
  433.     sp = fp = stack + STACK_SIZE;
  434.  
  435.     if (h_version != V6) {
  436.  
  437.     long pc = (long) h_start_pc;
  438.     SET_PC (pc)
  439.  
  440.     } else call (h_start_pc, 0, NULL, 0);
  441.  
  442. }/* z_restart */
  443.  
  444. /*
  445.  * get_default_name
  446.  *
  447.  * Read a default file name from the memory of the Z-machine and
  448.  * copy it to a string.
  449.  *
  450.  */
  451.  
  452. static void get_default_name (char *default_name, zword addr)
  453. {
  454.  
  455.     if (addr) {
  456.  
  457.     zbyte len;
  458.     int i;
  459.  
  460.     LOW_BYTE (addr, len)
  461.     addr++;
  462.  
  463.     for (i = 0; i < len; i++) {
  464.  
  465.         zbyte c;
  466.  
  467.         LOW_BYTE (addr, c)
  468.         addr++;
  469.  
  470.         default_name[i] = c;
  471.  
  472.     }
  473.  
  474.     default_name[i] = 0;
  475.  
  476.     if (strchr (default_name, '.') == NULL)
  477.         strcpy (default_name + i, ".AUX");
  478.  
  479.     } else strcpy (default_name, auxilary_name);
  480.  
  481. }/* get_default_name */
  482.  
  483. /*
  484.  * z_restore, restore [a part of] a Z-machine state from disk
  485.  *
  486.  *    zargs[0] = address of area to restore (optional)
  487.  *    zargs[1] = number of bytes to restore
  488.  *    zargs[2] = address of suggested file name
  489.  *
  490.  */
  491.  
  492. void z_restore (void)
  493. {
  494.     char new_name[MAX_FILE_NAME + 1];
  495.     char default_name[MAX_FILE_NAME + 1];
  496.     FILE *gfp;
  497.  
  498.     int success = 0;
  499.  
  500.     if (zargc != 0) {
  501.  
  502.     /* Get the file name */
  503.  
  504.     get_default_name (default_name, ZWORD ((zargc >= 3) ? zargs[2] : 0));
  505.  
  506.     if (os_read_file_name (new_name, default_name, FILE_LOAD_AUX) == 0)
  507.         goto finished;
  508.  
  509.     strcpy (auxilary_name, default_name);
  510.  
  511.     /* Open auxilary file */
  512.  
  513.     if ((gfp = fopen (new_name, "rb")) == NULL)
  514.         goto finished;
  515.  
  516.     /* Load auxilary file */
  517.  
  518.     success = fread (zmp + zargs[1], 1, zargs[2], gfp);
  519.  
  520.     /* Close auxilary file */
  521.  
  522.     fclose (gfp);
  523.  
  524.     } else {
  525.  
  526.     long pc;
  527.     zword release;
  528.     zword addr;
  529.     int i;
  530.  
  531.     /* Get the file name */
  532.  
  533.     if (os_read_file_name (new_name, save_name, FILE_RESTORE) == 0)
  534.         goto finished;
  535.  
  536.     strcpy (save_name, new_name);
  537.  
  538.     /* Open game file */
  539.  
  540.     if ((gfp = fopen (new_name, "rb")) == NULL)
  541.         goto finished;
  542.  
  543.     /* Load game file */
  544.  
  545.     release = (unsigned) fgetc (gfp) << 8;
  546.     release |= fgetc (gfp);
  547.  
  548.     (void) fgetc (gfp);
  549.     (void) fgetc (gfp);
  550.  
  551.     /* Check the release number */
  552.  
  553.     if (release == h_release) {
  554.  
  555.         pc = (long) fgetc (gfp) << 16;
  556.         pc |= (unsigned) fgetc (gfp) << 8;
  557.         pc |= fgetc (gfp);
  558.  
  559.         SET_PC (pc)
  560.  
  561.         sp = stack + (fgetc (gfp) << 8);
  562.         sp += fgetc (gfp);
  563.         fp = stack + (fgetc (gfp) << 8);
  564.         fp += fgetc (gfp);
  565.  
  566.         for (i = (int) (sp - stack); i < STACK_SIZE; i++) {
  567.         stack[i] = (unsigned) fgetc (gfp) << 8;
  568.         stack[i] |= fgetc (gfp);
  569.         }
  570.  
  571.         fseek (story_fp, 0, SEEK_SET);
  572.  
  573.         for (addr = 0; addr < h_dynamic_size; addr++) {
  574.         int skip = fgetc (gfp);
  575.         for (i = 0; i < skip; i++)
  576.             zmp[addr++] = ZBYTE (fgetc (story_fp));
  577.         zmp[addr] = ZBYTE (fgetc (gfp));
  578.         (void) fgetc (story_fp);
  579.         }
  580.  
  581.         /* Check for errors */
  582.  
  583.         if (ferror (gfp) || ferror (story_fp))
  584.         os_fatal ("Error reading save file");
  585.  
  586.         /* Reset upper window (V3 only) */
  587.  
  588.         if (h_version == V3)
  589.         split_window (0);
  590.  
  591.         /* Initialise story header */
  592.  
  593.         restart_header ();
  594.  
  595.         /* Success */
  596.  
  597.         success = 2;
  598.  
  599.     } else print_string ("Invalid save file\n");
  600.  
  601.     /* Close game file */
  602.  
  603.     fclose (gfp);
  604.  
  605.     }
  606.  
  607. finished:
  608.  
  609.     if (h_version <= V3)
  610.     branch (success);
  611.     else
  612.     store (ZWORD (success));
  613.  
  614. }/* z_restore */
  615.  
  616. /*
  617.  * restore_undo
  618.  *
  619.  * This function does the dirty work for z_restore_undo.
  620.  *
  621.  */
  622.  
  623. int restore_undo (void)
  624. {
  625.  
  626.     if (undo_slots == 0)    /* undo feature unavailable */
  627.  
  628.     return -1;
  629.  
  630.     else if (undo_valid == 0)    /* no saved game state */
  631.  
  632.     return 0;
  633.  
  634.     else {            /* undo possible */
  635.  
  636.     long pc;
  637.  
  638.     if (undo_count == 0)
  639.         undo_count = undo_slots;
  640.  
  641.     memcpy (stack, undo[undo_count - 1], sizeof (stack));
  642.     memcpy (zmp, undo[undo_count - 1] + sizeof (stack), h_dynamic_size);
  643.  
  644.     pc = ((long) stack[0] << 16) | stack[1];
  645.     sp = stack + stack[2];
  646.     fp = stack + stack[3];
  647.  
  648.     SET_PC (pc)
  649.  
  650.     restart_header ();
  651.  
  652.     undo_count--;
  653.     undo_valid--;
  654.  
  655.     return 2;
  656.  
  657.     }
  658.  
  659. }/* restore_undo */
  660.  
  661. /*
  662.  * z_restore_undo, restore a Z-machine state from memory.
  663.  *
  664.  *    no zargs used
  665.  *
  666.  */
  667.  
  668. void z_restore_undo (void)
  669. {
  670.  
  671.     store (ZWORD (restore_undo ()));
  672.  
  673. }/* restore_undo */
  674.  
  675. /*
  676.  * z_save, save [a part of] the Z-machine state to disk.
  677.  *
  678.  *    zargs[0] = address of memory area to save (optional)
  679.  *    zargs[1] = number of bytes to save
  680.  *    zargs[2] = address of suggested file name
  681.  *
  682.  */
  683.  
  684. void z_save (void)
  685. {
  686.     char new_name[MAX_FILE_NAME + 1];
  687.     char default_name[MAX_FILE_NAME + 1];
  688.     FILE *gfp;
  689.  
  690.     int success = 0;
  691.  
  692.     if (zargc != 0) {
  693.  
  694.     /* Get the file name */
  695.  
  696.     get_default_name (default_name, ZWORD ((zargc >= 3) ? zargs[2] : 0));
  697.  
  698.     if (os_read_file_name (new_name, default_name, FILE_SAVE_AUX) == 0)
  699.         goto finished;
  700.  
  701.     strcpy (auxilary_name, default_name);
  702.  
  703.     /* Open auxilary file */
  704.  
  705.     if ((gfp = fopen (new_name, "wb")) == NULL)
  706.         goto finished;
  707.  
  708.     /* Write auxilary file */
  709.  
  710.     success = fwrite (zmp + zargs[0], zargs[1], 1, gfp);
  711.  
  712.     /* Close auxilary file */
  713.  
  714.     fclose (gfp);
  715.  
  716.     } else {
  717.  
  718.     long pc;
  719.     zword addr;
  720.     int nsp, nfp;
  721.     int skip;
  722.     int i;
  723.  
  724.     /* Get the file name */
  725.  
  726.     if (os_read_file_name (new_name, save_name, FILE_SAVE) == 0)
  727.         goto finished;
  728.  
  729.     strcpy (save_name, new_name);
  730.  
  731.     /* Open game file */
  732.  
  733.     if ((gfp = fopen (new_name, "wb")) == NULL)
  734.         goto finished;
  735.  
  736.     /* Write game file */
  737.  
  738.     fputc ((int) hi (h_release), gfp);
  739.     fputc ((int) lo (h_release), gfp);
  740.     fputc ((int) hi (h_checksum), gfp);
  741.     fputc ((int) lo (h_checksum), gfp);
  742.  
  743.     GET_PC (pc)
  744.  
  745.     fputc ((int) (pc >> 16) & 0xff, gfp);
  746.     fputc ((int) (pc >> 8) & 0xff, gfp);
  747.     fputc ((int) (pc) & 0xff, gfp);
  748.  
  749.     nsp = (int) (sp - stack);
  750.     nfp = (int) (fp - stack);
  751.  
  752.     fputc ((int) hi (nsp), gfp);
  753.     fputc ((int) lo (nsp), gfp);
  754.     fputc ((int) hi (nfp), gfp);
  755.     fputc ((int) lo (nfp), gfp);
  756.  
  757.     for (i = nsp; i < STACK_SIZE; i++) {
  758.         fputc ((int) hi (stack[i]), gfp);
  759.         fputc ((int) lo (stack[i]), gfp);
  760.     }
  761.  
  762.     fseek (story_fp, 0, SEEK_SET);
  763.  
  764.     for (addr = 0, skip = 0; addr < h_dynamic_size; addr++)
  765.         if (zmp[addr] != fgetc (story_fp) || skip == 255 || addr + 1 == h_dynamic_size) {
  766.         fputc (skip, gfp);
  767.         fputc (zmp[addr], gfp);
  768.         skip = 0;
  769.         } else skip++;
  770.  
  771.     /* Close game file and check for errors */
  772.  
  773.     if (fclose (gfp) == EOF || ferror (story_fp)) {
  774.         print_string ("Error writing save file\n");
  775.         goto finished;
  776.     }
  777.  
  778.     /* Success */
  779.  
  780.     success = 1;
  781.  
  782.     }
  783.  
  784. finished:
  785.  
  786.     if (h_version <= V3)
  787.     branch (success);
  788.     else
  789.     store (ZWORD (success));
  790.  
  791. }/* z_save */
  792.  
  793. /*
  794.  * save_undo
  795.  *
  796.  * This function does the dirty work for z_save_undo.
  797.  *
  798.  */
  799.  
  800. int save_undo (void)
  801. {
  802.     long pc;
  803.  
  804.     if (undo_slots == 0)    /* undo feature unavailable */
  805.  
  806.     return -1;
  807.  
  808.     else {            /* save undo possible */
  809.  
  810.     if (undo_count == undo_slots)
  811.         undo_count = 0;
  812.  
  813.     GET_PC (pc)
  814.  
  815.     stack[0] = ZWORD (pc >> 16);
  816.     stack[1] = ZWORD (pc & 0xffff);
  817.     stack[2] = ZWORD (sp - stack);
  818.     stack[3] = ZWORD (fp - stack);
  819.  
  820.     memcpy (undo[undo_count], stack, sizeof (stack));
  821.     memcpy (undo[undo_count] + sizeof (stack), zmp, h_dynamic_size);
  822.  
  823.     if (++undo_count == undo_slots)
  824.         undo_count = 0;
  825.     if (++undo_valid > undo_slots)
  826.         undo_valid = undo_slots;
  827.  
  828.     return 1;
  829.  
  830.     }
  831.  
  832. }/* save_undo */
  833.  
  834. /*
  835.  * z_save_undo, save the current Z-machine state for a future undo.
  836.  *
  837.  *    no zargs used
  838.  *
  839.  */
  840.  
  841. void z_save_undo (void)
  842. {
  843.  
  844.     store (ZWORD (save_undo ()));
  845.  
  846. }/* z_save_undo */
  847.  
  848. /*
  849.  * z_verify, verify the story file integrity.
  850.  *
  851.  *    no zargs used
  852.  *
  853.  */
  854.  
  855. void z_verify (void)
  856. {
  857.     zword checksum = 0;
  858.     long i;
  859.  
  860.     /* Sum all bytes in story file except header bytes */
  861.  
  862.     fseek (story_fp, 64, SEEK_SET);
  863.  
  864.     for (i = 64; i < story_size; i++)
  865.     checksum += fgetc (story_fp);
  866.  
  867.     /* Branch if the checksums are equal */
  868.  
  869.     branch (checksum == h_checksum);
  870.  
  871. }/* z_verify */
  872.