1351 lines
26 KiB
C
1351 lines
26 KiB
C
|
|
/* $Id: fileio.c,v 1.3 2000/07/05 15:20:34 jholder Exp $
|
|
* --------------------------------------------------------------------
|
|
* see doc/License.txt for License Information
|
|
* --------------------------------------------------------------------
|
|
*
|
|
* File name: $Id: fileio.c,v 1.3 2000/07/05 15:20:34 jholder Exp $
|
|
*
|
|
* Description:
|
|
*
|
|
* Modification history:
|
|
* $Log: fileio.c,v $
|
|
* Revision 1.3 2000/07/05 15:20:34 jholder
|
|
* Updated code to remove warnings.
|
|
*
|
|
* Revision 1.2 2000/05/25 22:28:56 jholder
|
|
* changes routine names to reflect zmachine opcode names per spec 1.0
|
|
*
|
|
* Revision 1.1.1.1 2000/05/10 14:21:34 jholder
|
|
*
|
|
* imported
|
|
*
|
|
*
|
|
* --------------------------------------------------------------------
|
|
*/
|
|
|
|
/*
|
|
* fileio.c
|
|
*
|
|
* File manipulation routines. Should be generic.
|
|
*
|
|
*/
|
|
|
|
#include "ztypes.h"
|
|
//#include "jzexe.h" /* mol 951115 */
|
|
|
|
/* Static data */
|
|
|
|
extern int GLOBALVER;
|
|
|
|
#ifdef USE_ZLIB
|
|
static gzFile *gfp = NULL; /* Zcode file pointer */
|
|
#else
|
|
static FILE *gfp = NULL; /* Zcode file pointer */
|
|
#endif
|
|
|
|
static FILE *sfp = NULL; /* Script file pointer */
|
|
static FILE *rfp = NULL; /* Record file pointer */
|
|
|
|
#if defined BUFFER_FILES
|
|
#ifndef USE_ZLIB
|
|
static char gfpbuffer[BUFSIZ];
|
|
#endif
|
|
#endif
|
|
//static char sfpbuffer[BUFSIZ];
|
|
//static char rfpbuffer[BUFSIZ];
|
|
|
|
char save_name[Z_FILENAME_MAX + Z_PATHNAME_MAX + 1] = "story.sav";
|
|
char script_name[Z_FILENAME_MAX + Z_PATHNAME_MAX + 1] = "story.scr";
|
|
char record_name[Z_FILENAME_MAX + Z_PATHNAME_MAX + 1] = "story.rec";
|
|
char auxilary_name[Z_FILENAME_MAX + Z_PATHNAME_MAX + 1] = "story.aux";
|
|
|
|
static int undo_valid = FALSE;
|
|
static zword_t undo_stack[STACK_SIZE];
|
|
|
|
static int script_file_valid = FALSE;
|
|
|
|
static long story_offset = 0; /* mol 951114 */
|
|
//char *magic = ( char * ) MAGIC_STRING; /* mol */
|
|
|
|
static int save_restore( const char *, int );
|
|
|
|
/*
|
|
* set_names
|
|
*
|
|
* Set up the story names intelligently.
|
|
* John Holder, 28 Sept 1995
|
|
*/
|
|
void set_names( const char *storyname )
|
|
{
|
|
char *per_pos = 0;
|
|
|
|
strcpy( save_name, storyname );
|
|
strcpy( script_name, storyname );
|
|
strcpy( record_name, storyname );
|
|
strcpy( auxilary_name, storyname );
|
|
|
|
/* experimental setting of save_name, added by John Holder 26 July 1995 */
|
|
per_pos = strrchr( storyname, '.' ); /* find last '.' in storyname. */
|
|
if ( per_pos ) /* The story file looks like "Odius.dat" or "odieus.z3" */
|
|
{
|
|
per_pos = strrchr( save_name, '.' );
|
|
*( per_pos ) = '\0';
|
|
strcat( save_name, ".sav" );
|
|
|
|
per_pos = strrchr( script_name, '.' );
|
|
*( per_pos ) = '\0';
|
|
strcat( script_name, ".scr" );
|
|
|
|
per_pos = strrchr( record_name, '.' );
|
|
*( per_pos ) = '\0';
|
|
strcat( record_name, ".rec" );
|
|
|
|
per_pos = strrchr( auxilary_name, '.' );
|
|
*( per_pos ) = '\0';
|
|
strcat( auxilary_name, ".aux" );
|
|
}
|
|
else /* The story file looks like: "OdieusQuest" */
|
|
{
|
|
strcat( save_name, ".sav" );
|
|
strcat( script_name, ".src" );
|
|
strcat( record_name, ".rec" );
|
|
strcat( auxilary_name, ".aux" );
|
|
}
|
|
} /* set_names */
|
|
|
|
/*
|
|
* open_story
|
|
*
|
|
* Open game file for read.
|
|
*
|
|
*/
|
|
|
|
void open_story( const char *storyname )
|
|
{
|
|
char *path = NULL;
|
|
char *p = NULL;
|
|
char tmp[Z_FILENAME_MAX + Z_PATHNAME_MAX + 1];
|
|
|
|
// if ( !STANDALONE_FLAG )
|
|
// {
|
|
story_offset = 0;
|
|
// }
|
|
// else
|
|
// {
|
|
/* standalone game; offset to story start is saved in low-endian */
|
|
/* format after magic string */
|
|
// story_offset =
|
|
// magic[MAGIC_END + 1] + magic[MAGIC_END + 2] * 256L + magic[MAGIC_END + 3] * 65536L;
|
|
// }
|
|
|
|
strcpy( tmp, storyname );
|
|
if ( ( gfp = jz_open( tmp, "rb" ) ) != NULL )
|
|
{
|
|
#if defined BUFFER_FILES
|
|
#ifndef USE_ZLIB
|
|
setbuf( gfp, gfpbuffer );
|
|
#endif
|
|
#endif
|
|
set_names( storyname );
|
|
return;
|
|
#if defined MSDOS || defined OS2
|
|
}
|
|
else
|
|
{
|
|
sprintf( tmp, "%s.exe", storyname );
|
|
if ( ( gfp = jz_open( tmp, "rb" ) ) != NULL )
|
|
{
|
|
#if defined BUFFER_FILES
|
|
#ifndef USE_ZLIB
|
|
setbuf( gfp, gfpbuffer );
|
|
#endif
|
|
#endif
|
|
set_names( storyname );
|
|
return;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
if ( !STANDALONE_FLAG && ( path = getenv( "INFOCOM_PATH" ) ) == NULL )
|
|
{
|
|
fprintf( stderr, "%s ", tmp );
|
|
fatal( "open_story(): Zcode file not found" );
|
|
}
|
|
else if ( STANDALONE_FLAG && ( path = getenv( "PATH" ) ) == NULL )
|
|
{
|
|
fprintf( stderr, "%s ", tmp );
|
|
fatal( "open_story(): Zcode file not found" );
|
|
}
|
|
*/
|
|
|
|
/* dos path will be like: */
|
|
/* SET INFOCOM_PATH = C:\INFOCOM\LTOI1;C:\INFOCOM\LTOI2;C:\INFOCOM\INFORM */
|
|
#if defined MSDOS || defined OS2
|
|
p = strtok( path, ";" );
|
|
#else
|
|
/* UNIX path will be like: */
|
|
/* setenv INFOCOM_PATH /usr/local/lib/ltoi1:/usr/local/lib/ltoi2 */
|
|
p = strtok( path, ":" );
|
|
#endif
|
|
|
|
while ( p )
|
|
{
|
|
sprintf( tmp, "%s/%s", p, storyname );
|
|
if ( ( gfp = jz_open( tmp, "rb" ) ) != NULL )
|
|
{
|
|
#if defined BUFFER_FILES
|
|
#ifndef USE_ZLIB
|
|
setbuf( gfp, gfpbuffer );
|
|
#endif
|
|
#endif
|
|
set_names( storyname );
|
|
return;
|
|
#if defined MSDOS || defined OS2
|
|
}
|
|
else
|
|
{
|
|
sprintf( tmp, "%s/%s.exe", p, storyname );
|
|
if ( ( gfp = jz_open( tmp, "rb" ) ) != NULL )
|
|
{
|
|
#if defined BUFFER_FILES
|
|
#ifndef USE_ZLIB
|
|
setbuf( gfp, gfpbuffer );
|
|
#endif
|
|
#endif
|
|
set_names( storyname );
|
|
return;
|
|
}
|
|
#endif
|
|
}
|
|
#if defined MSDOS || defined OS2
|
|
p = strtok( NULL, ";" );
|
|
#else
|
|
p = strtok( NULL, ":" );
|
|
#endif
|
|
}
|
|
|
|
fprintf( stderr, "%s ", tmp );
|
|
fatal( "open_story(): Zcode file not found" );
|
|
} /* open_story */
|
|
|
|
|
|
/*
|
|
* close_story
|
|
*
|
|
* Close game file if open.
|
|
*
|
|
*/
|
|
|
|
void close_story( void )
|
|
{
|
|
|
|
if ( gfp != NULL )
|
|
{
|
|
jz_close( gfp );
|
|
}
|
|
|
|
} /* close_story */
|
|
|
|
/*
|
|
* get_story_size
|
|
*
|
|
* Calculate the size of the game file. Only used for very old games that do not
|
|
* have the game file size in the header.
|
|
*
|
|
*/
|
|
|
|
unsigned int get_story_size( void )
|
|
{
|
|
unsigned long file_length;
|
|
|
|
/* Read whole file to calculate file size */
|
|
jz_rewind( gfp );
|
|
for ( file_length = 0; jz_getc( gfp ) != EOF; file_length++ )
|
|
;
|
|
jz_rewind( gfp );
|
|
|
|
/* Calculate length of file in game allocation units */
|
|
file_length =
|
|
( file_length + ( unsigned long ) ( story_scaler - 1 ) ) / ( unsigned long ) story_scaler;
|
|
|
|
return ( ( unsigned int ) file_length );
|
|
|
|
} /* get_story_size */
|
|
|
|
/*
|
|
* read_page
|
|
*
|
|
* Read one game file page.
|
|
*
|
|
*/
|
|
|
|
void read_page( int page, void *buffer )
|
|
{
|
|
unsigned long file_size;
|
|
unsigned int pages, offset;
|
|
|
|
/* Seek to start of page */
|
|
jz_seek( gfp, story_offset + ( long ) page * PAGE_SIZE, SEEK_SET );
|
|
|
|
/* Read the page */
|
|
|
|
#ifdef USE_ZLIB
|
|
if ( gzread( gfp, buffer, PAGE_SIZE ) == -1 )
|
|
#else
|
|
if ( fread( buffer, PAGE_SIZE, 1, gfp ) != 1 )
|
|
#endif
|
|
{
|
|
/* Read failed. Are we in the last page? */
|
|
file_size = ( unsigned long )(h_file_size *story_scaler);
|
|
|
|
pages = ( unsigned int ) ( ( unsigned long ) file_size / PAGE_SIZE );
|
|
offset = ( unsigned int ) ( ( unsigned long ) file_size & PAGE_MASK );
|
|
|
|
if ( ( unsigned int ) page == pages )
|
|
{
|
|
/* Read partial page if this is the last page in the game file */
|
|
jz_seek( gfp, story_offset + ( long ) page * PAGE_SIZE, SEEK_SET );
|
|
#ifdef USE_ZLIB
|
|
if ( gzread( gfp, buffer, offset ) == -1 )
|
|
#else
|
|
if ( fread( buffer, offset, 1, gfp ) != 1 )
|
|
#endif
|
|
{
|
|
fatal( "read_page(): Zcode file read error" );
|
|
}
|
|
}
|
|
}
|
|
|
|
} /* read_page */
|
|
|
|
/*
|
|
* z_verify
|
|
*
|
|
* Verify game ($verify verb). Add all bytes in game file except for bytes in
|
|
* the game file header.
|
|
*
|
|
*/
|
|
|
|
void z_verify( void )
|
|
{
|
|
unsigned long file_size;
|
|
unsigned int pages, offset;
|
|
unsigned int start, end, i, j;
|
|
zword_t checksum = 0;
|
|
zbyte_t buffer[PAGE_SIZE] = { 0 };
|
|
char szBuffer[6] = { 0 };
|
|
|
|
/* Print version banner */
|
|
|
|
z_new_line( );
|
|
write_string( "Running on " );
|
|
write_string( JZIPVER );
|
|
write_string( " (" );
|
|
switch ( JTERP )
|
|
{
|
|
case INTERP_GENERIC:
|
|
write_string( "Generic" );
|
|
break;
|
|
case INTERP_AMIGA:
|
|
write_string( "Amiga" );
|
|
break;
|
|
case INTERP_ATARI_ST:
|
|
write_string( "Atari ST" );
|
|
break;
|
|
case INTERP_MSDOS:
|
|
write_string( "DOS" );
|
|
break;
|
|
case INTERP_UNIX:
|
|
write_string( "UNIX" );
|
|
break;
|
|
case INTERP_VMS:
|
|
write_string( "VMS" );
|
|
break;
|
|
}
|
|
write_string( "). Reporting Spec " );
|
|
sprintf( szBuffer, "%d.%d", get_byte( H_STANDARD_HIGH ), get_byte( H_STANDARD_LOW ) );
|
|
write_string( szBuffer );
|
|
write_string( " Compliance." );
|
|
z_new_line( );
|
|
|
|
write_string( "Compile options: " );
|
|
#ifdef USE_QUETZAL
|
|
write_string( "USE_QUETZAL " );
|
|
#endif
|
|
#ifdef STRICTZ
|
|
write_string( "STRICTZ " );
|
|
#endif
|
|
#ifdef USE_ZLIB
|
|
write_string( "USE_ZLIB " );
|
|
#endif
|
|
#ifdef LOUSY_RANDOM
|
|
write_string( "LOUSY_RANDOM " );
|
|
#endif
|
|
#ifdef HARD_COLORS
|
|
write_string( "HARD_COLORS " );
|
|
#endif
|
|
z_new_line( );
|
|
|
|
write_string( "Release " );
|
|
write_string( JZIPRELDATE );
|
|
write_string( "." );
|
|
z_new_line( );
|
|
|
|
write_string( "Playing a Version " );
|
|
z_print_num( (zword_t) GLOBALVER );
|
|
write_string( " Story." );
|
|
z_new_line( );
|
|
|
|
z_new_line( );
|
|
|
|
/* Calculate game file dimensions */
|
|
|
|
file_size = ( unsigned long )(h_file_size *story_scaler);
|
|
|
|
pages = ( unsigned int ) ( ( unsigned long ) file_size / PAGE_SIZE );
|
|
offset = ( unsigned int ) file_size & PAGE_MASK;
|
|
|
|
/* Sum all bytes in game file, except header bytes */
|
|
|
|
for ( i = 0; i <= pages; i++ )
|
|
{
|
|
read_page( (int)i, buffer );
|
|
start = ( i == 0 ) ? 64 : 0;
|
|
end = ( i == pages ) ? offset : PAGE_SIZE;
|
|
for ( j = start; j < end; j++ )
|
|
{
|
|
checksum += buffer[j];
|
|
}
|
|
}
|
|
|
|
/* Make a conditional jump based on whether the checksum is equal */
|
|
|
|
conditional_jump( checksum == h_checksum );
|
|
|
|
} /* z_verify */
|
|
|
|
|
|
/*
|
|
* get_default_name
|
|
*
|
|
* Read a default file name from the memory of the Z-machine and
|
|
* copy it to an array.
|
|
*
|
|
*/
|
|
|
|
static void get_default_name( char *default_name, zword_t addr )
|
|
{
|
|
zbyte_t len;
|
|
zbyte_t c;
|
|
unsigned int i;
|
|
|
|
if ( addr != 0 )
|
|
{
|
|
len = get_byte( addr );
|
|
|
|
for ( i = 0; i < len; i++ )
|
|
{
|
|
addr++;
|
|
c = get_byte( addr );
|
|
default_name[i] = (char)c;
|
|
}
|
|
default_name[i] = 0;
|
|
|
|
if ( strchr( default_name, '.' ) == 0 )
|
|
{
|
|
strcpy( default_name + i, ".aux" );
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
strcpy( default_name, auxilary_name );
|
|
}
|
|
|
|
} /* get_default_name */
|
|
|
|
|
|
/*
|
|
* z_save
|
|
*
|
|
* Saves data to disk. Returns:
|
|
* 0 = save failed
|
|
* 1 = save succeeded
|
|
*
|
|
*/
|
|
|
|
int z_save( int argc, zword_t table, zword_t bytes, zword_t name )
|
|
{
|
|
char new_name[Z_FILENAME_MAX + Z_PATHNAME_MAX + 1];
|
|
char default_name[Z_FILENAME_MAX + Z_PATHNAME_MAX + 1];
|
|
FILE *afp;
|
|
|
|
#if defined BUFFER_FILES
|
|
char afpbuffer[BUFSIZ];
|
|
#endif
|
|
int status = 0;
|
|
|
|
if ( argc == 3 )
|
|
{
|
|
get_default_name( default_name, name );
|
|
if ( get_file_name( new_name, default_name, GAME_SAVE_AUX ) != 0 )
|
|
{
|
|
goto finished;
|
|
}
|
|
|
|
if ( ( afp = fopen( new_name, "wb" ) ) == NULL )
|
|
{
|
|
goto finished;
|
|
}
|
|
|
|
#if defined BUFFER_FILES
|
|
setbuf( afp, afpbuffer );
|
|
#endif
|
|
|
|
status = (int)fwrite( datap + table, bytes, 1, afp );
|
|
|
|
fclose( afp );
|
|
|
|
if ( status != 0 )
|
|
{
|
|
strcpy( auxilary_name, default_name );
|
|
}
|
|
|
|
status = !status;
|
|
}
|
|
else
|
|
{
|
|
/* Get the file name */
|
|
status = 1;
|
|
|
|
if ( get_file_name( new_name, save_name, GAME_SAVE ) == 0 )
|
|
{
|
|
/* Do a save operation */
|
|
if ( save_restore( new_name, GAME_SAVE ) == 0 )
|
|
{
|
|
/* Cleanup file */
|
|
file_cleanup( new_name, GAME_SAVE );
|
|
|
|
/* Save the new name as the default file name */
|
|
strcpy( save_name, new_name );
|
|
|
|
/* Indicate success */
|
|
status = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
finished:
|
|
|
|
/* Return result of save to Z-code */
|
|
|
|
if ( h_type < V4 )
|
|
{
|
|
conditional_jump( status == 0 );
|
|
}
|
|
else
|
|
{
|
|
store_operand( (zword_t)(( status == 0 ) ? 1 : 0) );
|
|
}
|
|
|
|
return ( status );
|
|
} /* z_save */
|
|
|
|
|
|
/*
|
|
* z_restore
|
|
*
|
|
* Restore game state from disk. Returns:
|
|
* 0 = restore failed
|
|
* 2 = restore succeeded
|
|
*/
|
|
|
|
int z_restore( int argc, zword_t table, zword_t bytes, zword_t name )
|
|
{
|
|
char new_name[Z_FILENAME_MAX + Z_PATHNAME_MAX + 1];
|
|
char default_name[Z_FILENAME_MAX + Z_PATHNAME_MAX + 1];
|
|
FILE *afp;
|
|
|
|
#if defined BUFFER_FILES
|
|
char afpbuffer[BUFSIZ];
|
|
#endif
|
|
int status;
|
|
|
|
status = 0;
|
|
|
|
if ( argc == 3 )
|
|
{
|
|
get_default_name( default_name, name );
|
|
if ( get_file_name( new_name, default_name, GAME_LOAD_AUX ) == 0 )
|
|
{
|
|
goto finished;
|
|
}
|
|
|
|
if ( ( afp = fopen( new_name, "rb" ) ) == NULL )
|
|
{
|
|
goto finished;
|
|
}
|
|
|
|
#if defined BUFFER_FILES
|
|
setbuf( afp, afpbuffer );
|
|
#endif
|
|
|
|
status = (int)fread( datap + table, bytes, 1, afp );
|
|
|
|
fclose( afp );
|
|
|
|
if ( status != 0 )
|
|
{
|
|
strcpy( auxilary_name, default_name );
|
|
}
|
|
|
|
status = !status;
|
|
}
|
|
else
|
|
{
|
|
/* Get the file name */
|
|
status = 1;
|
|
if ( get_file_name( new_name, save_name, GAME_RESTORE ) == 0 )
|
|
{
|
|
/* Do the restore operation */
|
|
if ( save_restore( new_name, GAME_RESTORE ) == 0 )
|
|
{
|
|
/* Reset the status region (this is just for Seastalker) */
|
|
if ( h_type < V4 )
|
|
{
|
|
z_split_window( 0 );
|
|
blank_status_line( );
|
|
}
|
|
|
|
/* Cleanup file */
|
|
file_cleanup( new_name, GAME_SAVE );
|
|
|
|
/* Save the new name as the default file name */
|
|
strcpy( save_name, new_name );
|
|
|
|
/* Indicate success */
|
|
status = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
finished:
|
|
/* Return result of save to Z-code */
|
|
|
|
if ( h_type < V4 )
|
|
{
|
|
conditional_jump( status == 0 );
|
|
}
|
|
else
|
|
{
|
|
store_operand( (zword_t)(( status == 0 ) ? 2 : 0) );
|
|
}
|
|
|
|
return ( status );
|
|
} /* z_restore */
|
|
|
|
/*
|
|
* z_save_undo
|
|
*
|
|
* Save the current Z machine state in memory for a future undo. Returns:
|
|
* -1 = feature unavailable
|
|
* 0 = save failed
|
|
* 1 = save succeeded
|
|
*
|
|
*/
|
|
|
|
void z_save_undo( void )
|
|
{
|
|
/* Check if undo is available first */
|
|
if ( undo_datap != NULL )
|
|
{
|
|
/* Save the undo data and return success */
|
|
save_restore( NULL, UNDO_SAVE );
|
|
undo_valid = TRUE;
|
|
store_operand( 1 );
|
|
}
|
|
else
|
|
{
|
|
/* If no memory for data area then say undo is not available */
|
|
store_operand( ( zword_t ) - 1 );
|
|
}
|
|
|
|
} /* z_save_undo */
|
|
|
|
/*
|
|
* z_restore_undo
|
|
*
|
|
* Restore the current Z machine state from memory. Returns:
|
|
* -1 = feature unavailable
|
|
* 0 = restore failed
|
|
* 2 = restore succeeded
|
|
*
|
|
*/
|
|
|
|
void z_restore_undo( void )
|
|
{
|
|
/* Check if undo is available first */
|
|
if ( undo_datap != NULL )
|
|
{
|
|
/* If no undo save done then return an error */
|
|
if ( undo_valid == TRUE )
|
|
{
|
|
/* Restore the undo data and return success */
|
|
save_restore( NULL, UNDO_RESTORE );
|
|
store_operand( 2 );
|
|
}
|
|
else
|
|
{
|
|
store_operand( 0 );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* If no memory for data area then say undo is not available */
|
|
store_operand( ( zword_t ) - 1 );
|
|
}
|
|
|
|
} /* z_restore_undo */
|
|
|
|
|
|
/*
|
|
* swap_bytes
|
|
*
|
|
* Swap the low and high bytes in every word of the specified array.
|
|
* The length is specified in BYTES!
|
|
*
|
|
* This routine added by Mark Phillips(msp@bnr.co.uk), Thanks Mark!
|
|
*/
|
|
#if !defined(USE_QUETZAL)
|
|
void swap_bytes( zword_t * ptr, int len )
|
|
{
|
|
unsigned char *pbyte;
|
|
unsigned char tmp;
|
|
|
|
len /= 2; /* convert len into number of 2 byte words */
|
|
|
|
pbyte = ( unsigned char * ) ptr;
|
|
|
|
while ( len )
|
|
{
|
|
tmp = pbyte[0];
|
|
pbyte[0] = pbyte[1];
|
|
pbyte[1] = tmp;
|
|
pbyte += 2;
|
|
len--;
|
|
}
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* save_restore
|
|
*
|
|
* Common save and restore code. Just save or restore the game stack and the
|
|
* writeable data area.
|
|
*
|
|
*/
|
|
|
|
static int save_restore( const char *file_name, int flag )
|
|
{
|
|
FILE *tfp = NULL;
|
|
|
|
#if defined BUFFER_FILES
|
|
char tfpbuffer[BUFSIZ];
|
|
#endif
|
|
int scripting_flag = 0, status = 0;
|
|
|
|
#if !defined(USE_QUETZAL)
|
|
zword_t zw;
|
|
int little_endian = 0;
|
|
|
|
/* Find out if we are big-endian */
|
|
zw = 0x0001;
|
|
if ( *( zbyte_t * ) & zw )
|
|
{ /* We are little-endian, like an Intel 80x86 chip. */
|
|
little_endian = 1;
|
|
}
|
|
#endif
|
|
|
|
/* Open the save file and disable scripting */
|
|
|
|
if ( flag == GAME_SAVE || flag == GAME_RESTORE )
|
|
{
|
|
if ( ( tfp = fopen( file_name, ( flag == GAME_SAVE ) ? "wb" : "rb" ) ) == NULL )
|
|
{
|
|
output_line( "Cannot open SAVE file" );
|
|
return ( 1 );
|
|
}
|
|
#if defined BUFFER_FILES
|
|
setbuf( tfp, tfpbuffer );
|
|
#endif
|
|
scripting_flag = get_word( H_FLAGS ) & SCRIPTING_FLAG;
|
|
set_word( H_FLAGS, get_word( H_FLAGS ) & ( ~SCRIPTING_FLAG ) );
|
|
}
|
|
|
|
#if defined(USE_QUETZAL)
|
|
if ( flag == GAME_SAVE )
|
|
{
|
|
status = !save_quetzal( tfp, gfp );
|
|
}
|
|
else if ( flag == GAME_RESTORE )
|
|
{
|
|
status = !restore_quetzal( tfp, gfp );
|
|
}
|
|
else
|
|
{
|
|
#endif /* defined(USE_QUETZAL) */
|
|
/* Push PC, FP, version and store SP in special location */
|
|
|
|
stack[--sp] = ( zword_t ) ( pc / PAGE_SIZE );
|
|
stack[--sp] = ( zword_t ) ( pc % PAGE_SIZE );
|
|
stack[--sp] = fp;
|
|
stack[--sp] = h_version;
|
|
stack[0] = sp;
|
|
|
|
/* Save or restore stack */
|
|
|
|
#if !defined(USE_QUETZAL)
|
|
if ( flag == GAME_SAVE )
|
|
{
|
|
if ( little_endian )
|
|
swap_bytes( stack, sizeof ( stack ) );
|
|
if ( status == 0 && fwrite( stack, sizeof ( stack ), 1, tfp ) != 1 )
|
|
status = 1;
|
|
if ( little_endian )
|
|
swap_bytes( stack, sizeof ( stack ) );
|
|
}
|
|
else if ( flag == GAME_RESTORE )
|
|
{
|
|
if ( little_endian )
|
|
swap_bytes( stack, sizeof ( stack ) );
|
|
if ( status == 0 && fread( stack, sizeof ( stack ), 1, tfp ) != 1 )
|
|
status = 1;
|
|
if ( little_endian )
|
|
swap_bytes( stack, sizeof ( stack ) );
|
|
}
|
|
else
|
|
#endif /* !defined(USE_QUETZAL) */
|
|
{
|
|
if ( flag == UNDO_SAVE )
|
|
{
|
|
memmove( undo_stack, stack, sizeof ( stack ) );
|
|
}
|
|
else /* if (flag == UNDO_RESTORE) */
|
|
{
|
|
memmove( stack, undo_stack, sizeof ( stack ) );
|
|
}
|
|
}
|
|
|
|
/* Restore SP, check version, restore FP and PC */
|
|
|
|
sp = stack[0];
|
|
|
|
if ( stack[sp++] != h_version )
|
|
{
|
|
fatal( "save_restore(): Wrong game or version" );
|
|
}
|
|
|
|
fp = stack[sp++];
|
|
pc = stack[sp++];
|
|
pc += ( unsigned long ) stack[sp++] * PAGE_SIZE;
|
|
|
|
/* Save or restore writeable game data area */
|
|
|
|
#if !defined(USE_QUETZAL)
|
|
if ( flag == GAME_SAVE )
|
|
{
|
|
if ( status == 0 && fwrite( datap, h_restart_size, 1, tfp ) != 1 )
|
|
status = 1;
|
|
}
|
|
else if ( flag == GAME_RESTORE )
|
|
{
|
|
if ( status == 0 && fread( datap, h_restart_size, 1, tfp ) != 1 )
|
|
status = 1;
|
|
}
|
|
else
|
|
#endif /* !defined(USE_QUETZAL) */
|
|
{
|
|
if ( flag == UNDO_SAVE )
|
|
{
|
|
memmove( undo_datap, datap, h_restart_size );
|
|
}
|
|
else /* if (flag == UNDO_RESTORE) */
|
|
{
|
|
memmove( datap, undo_datap, h_restart_size );
|
|
}
|
|
}
|
|
|
|
#if defined(USE_QUETZAL)
|
|
}
|
|
#endif /* defined(USE_QUETZAL) */
|
|
|
|
|
|
/* Close the save file and restore scripting */
|
|
|
|
if ( flag == GAME_SAVE )
|
|
{
|
|
fclose( tfp );
|
|
if ( scripting_flag )
|
|
{
|
|
set_word( H_FLAGS, get_word( H_FLAGS ) | SCRIPTING_FLAG );
|
|
}
|
|
}
|
|
else if ( flag == GAME_RESTORE )
|
|
{
|
|
fclose( tfp );
|
|
restart_screen( );
|
|
restart_interp( scripting_flag );
|
|
}
|
|
|
|
/* Handle read or write errors */
|
|
|
|
if ( status )
|
|
{
|
|
if ( flag == GAME_SAVE )
|
|
{
|
|
output_line( "Write to SAVE file failed" );
|
|
remove( file_name );
|
|
}
|
|
else
|
|
{
|
|
fatal( "save_restore(): Read from SAVE file failed" );
|
|
}
|
|
}
|
|
|
|
return ( status );
|
|
|
|
} /* save_restore */
|
|
|
|
/*
|
|
* open_script
|
|
*
|
|
* Open the scripting file.
|
|
*
|
|
*/
|
|
|
|
void open_script( void )
|
|
{
|
|
char new_script_name[Z_FILENAME_MAX + Z_PATHNAME_MAX + 1];
|
|
|
|
/* Open scripting file if closed */
|
|
if ( scripting == OFF )
|
|
{
|
|
if ( script_file_valid == TRUE )
|
|
{
|
|
sfp = fopen( script_name, "a" );
|
|
|
|
/* Turn on scripting if open succeeded */
|
|
if ( sfp != NULL )
|
|
{
|
|
#if defined BUFFER_FILES
|
|
setbuf( sfp, sfpbuffer );
|
|
#endif
|
|
scripting = ON;
|
|
}
|
|
else
|
|
{
|
|
output_line( "Script file open failed" );
|
|
}
|
|
}
|
|
else
|
|
{ /* Get scripting file name and record it */
|
|
if ( get_file_name( new_script_name, script_name, GAME_SCRIPT ) == 0 )
|
|
{
|
|
/* Open scripting file */
|
|
sfp = fopen( new_script_name, "w" );
|
|
|
|
/* Turn on scripting if open succeeded */
|
|
if ( sfp != NULL )
|
|
{
|
|
#if defined BUFFER_FILES
|
|
setbuf( sfp, sfpbuffer );
|
|
#endif
|
|
script_file_valid = TRUE;
|
|
|
|
/* Make file name the default name */
|
|
strcpy( script_name, new_script_name );
|
|
|
|
/* Turn on scripting */
|
|
scripting = ON;
|
|
}
|
|
else
|
|
{
|
|
output_line( "Script file create failed" );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Set the scripting flag in the game file flags */
|
|
if ( scripting == ON )
|
|
{
|
|
set_word( H_FLAGS, get_word( H_FLAGS ) | SCRIPTING_FLAG );
|
|
}
|
|
else
|
|
{
|
|
set_word( H_FLAGS, get_word( H_FLAGS ) & ( ~SCRIPTING_FLAG ) );
|
|
}
|
|
|
|
} /* open_script */
|
|
|
|
/*
|
|
* flush_script
|
|
* Flush the scripting file.
|
|
*
|
|
*/
|
|
void flush_script( void )
|
|
{
|
|
/* Flush scripting file if open */
|
|
if ( scripting == ON )
|
|
{
|
|
fflush( sfp );
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* close_script
|
|
*
|
|
* Close the scripting file.
|
|
*
|
|
*/
|
|
void close_script( void )
|
|
{
|
|
/* Close scripting file if open */
|
|
if ( scripting == ON )
|
|
{
|
|
fclose( sfp );
|
|
#if 0
|
|
/* Cleanup */
|
|
file_cleanup( script_name, GAME_SCRIPT );
|
|
#endif
|
|
/* Turn off scripting */
|
|
scripting = OFF;
|
|
}
|
|
|
|
/* Set the scripting flag in the game file flags */
|
|
if ( scripting == OFF )
|
|
{
|
|
set_word( H_FLAGS, get_word( H_FLAGS ) & ( ~SCRIPTING_FLAG ) );
|
|
}
|
|
else
|
|
{
|
|
set_word( H_FLAGS, get_word( H_FLAGS ) | SCRIPTING_FLAG );
|
|
}
|
|
|
|
} /* close_script */
|
|
|
|
/*
|
|
* script_char
|
|
*
|
|
* Write one character to scripting file.
|
|
*
|
|
* Check the state of the scripting flag first. Older games only set the
|
|
* scripting flag in the game flags instead of calling the set_print_modes
|
|
* function. This is because they expect a physically attached printer that
|
|
* doesn't need opening like a file.
|
|
*/
|
|
|
|
void script_char( int c )
|
|
{
|
|
|
|
/* Check the state of the scripting flag in the game flags. If it is on
|
|
* then check to see if the scripting file is open as well */
|
|
|
|
if ( ( get_word( H_FLAGS ) & SCRIPTING_FLAG ) != 0 && scripting == OFF )
|
|
{
|
|
open_script( );
|
|
}
|
|
|
|
/* Check the state of the scripting flag in the game flags. If it is off
|
|
* then check to see if the scripting file is closed as well */
|
|
|
|
if ( ( get_word( H_FLAGS ) & SCRIPTING_FLAG ) == 0 && scripting == ON )
|
|
{
|
|
close_script( );
|
|
}
|
|
|
|
/* If scripting file is open, we are in the text window and the character is
|
|
* printable then write the character */
|
|
|
|
if ( scripting == ON && scripting_disable == OFF && ( c == '\n' || ( isprint( c ) ) ) )
|
|
{
|
|
putc( c, sfp );
|
|
}
|
|
|
|
} /* script_char */
|
|
|
|
/*
|
|
* script_string
|
|
*
|
|
* Write a string to the scripting file.
|
|
*
|
|
*/
|
|
|
|
void script_string( const char *s )
|
|
{
|
|
/* Write string */
|
|
while ( *s )
|
|
{
|
|
script_char( *s++ );
|
|
}
|
|
|
|
} /* script_string */
|
|
|
|
/*
|
|
* script_line
|
|
*
|
|
* Write a string followed by a new line to the scripting file.
|
|
*
|
|
*/
|
|
|
|
void script_line( const char *s )
|
|
{
|
|
|
|
/* Write string */
|
|
script_string( s );
|
|
|
|
/* Write new line */
|
|
script_new_line( );
|
|
|
|
} /* script_line */
|
|
|
|
/*
|
|
* script_new_line
|
|
*
|
|
* Write a new line to the scripting file.
|
|
*
|
|
*/
|
|
|
|
void script_new_line( void )
|
|
{
|
|
|
|
script_char( '\n' );
|
|
|
|
} /* script_new_line */
|
|
|
|
/*
|
|
* open_record
|
|
*
|
|
* Turn on recording of all input to an output file.
|
|
*
|
|
*/
|
|
|
|
void open_record( void )
|
|
{
|
|
char new_record_name[Z_FILENAME_MAX + Z_PATHNAME_MAX + 1];
|
|
|
|
/* If recording or playback is already on then complain */
|
|
|
|
if ( recording == ON || replaying == ON )
|
|
{
|
|
output_line( "Recording or playback are already active." );
|
|
}
|
|
else
|
|
{ /* Get recording file name */
|
|
if ( get_file_name( new_record_name, record_name, GAME_RECORD ) == 0 )
|
|
{
|
|
/* Open recording file */
|
|
rfp = fopen( new_record_name, "w" );
|
|
|
|
/* Turn on recording if open succeeded */
|
|
if ( rfp != NULL )
|
|
{
|
|
#if defined BUFFER_FILES
|
|
setbuf( rfp, rfpbuffer );
|
|
#endif
|
|
/* Make file name the default name */
|
|
strcpy( record_name, new_record_name );
|
|
|
|
/* Set recording on */
|
|
recording = ON;
|
|
}
|
|
else
|
|
{
|
|
output_line( "Record file create failed" );
|
|
}
|
|
}
|
|
}
|
|
|
|
} /* open_record */
|
|
|
|
/*
|
|
* record_line
|
|
*
|
|
* Write a string followed by a new line to the recording file.
|
|
*
|
|
*/
|
|
|
|
void record_line( const char *s )
|
|
{
|
|
if ( recording == ON && replaying == OFF )
|
|
{
|
|
/* Write string */
|
|
fprintf( rfp, "%s\n", s );
|
|
}
|
|
|
|
} /* record_line */
|
|
|
|
/*
|
|
* record_key
|
|
*
|
|
* Write a key followed by a new line to the recording file.
|
|
*
|
|
*/
|
|
|
|
void record_key( int c )
|
|
{
|
|
if ( recording == ON && replaying == OFF )
|
|
{
|
|
/* Write the key */
|
|
fprintf( rfp, "<%0o>\n", c );
|
|
}
|
|
|
|
} /* record_key */
|
|
|
|
/*
|
|
* close_record
|
|
*
|
|
* Turn off recording of all input to an output file.
|
|
*
|
|
*/
|
|
|
|
void close_record( void )
|
|
{
|
|
/* Close recording file */
|
|
if ( rfp != NULL )
|
|
{
|
|
fclose( rfp );
|
|
rfp = NULL;
|
|
|
|
/* Cleanup */
|
|
|
|
if ( recording == ON )
|
|
{
|
|
file_cleanup( record_name, GAME_RECORD );
|
|
}
|
|
else /* (replaying == ON) */
|
|
{
|
|
file_cleanup( record_name, GAME_PLAYBACK );
|
|
}
|
|
}
|
|
|
|
/* Set recording and replaying off */
|
|
|
|
recording = OFF;
|
|
replaying = OFF;
|
|
|
|
} /* close_record */
|
|
|
|
/*
|
|
* z_input_stream
|
|
*
|
|
* Take input from command file instead of keyboard.
|
|
*
|
|
*/
|
|
|
|
void z_input_stream( int arg )
|
|
{
|
|
char new_record_name[Z_FILENAME_MAX + Z_PATHNAME_MAX + 1];
|
|
|
|
UNUSEDVAR( arg );
|
|
|
|
/* If recording or replaying is already on then complain */
|
|
|
|
if ( recording == ON || replaying == ON )
|
|
{
|
|
output_line( "Recording or replaying is already active." );
|
|
}
|
|
else
|
|
{ /* Get recording file name */
|
|
|
|
if ( get_file_name( new_record_name, record_name, GAME_PLAYBACK ) == 0 )
|
|
{
|
|
/* Open recording file */
|
|
rfp = fopen( new_record_name, "r" );
|
|
|
|
/* Turn on recording if open succeeded */
|
|
if ( rfp != NULL )
|
|
{
|
|
#if defined BUFFER_FILES
|
|
setbuf( rfp, rfpbuffer );
|
|
#endif
|
|
/* Make file name the default name */
|
|
strcpy( record_name, new_record_name );
|
|
|
|
/* Set replaying on */
|
|
replaying = ON;
|
|
}
|
|
else
|
|
{
|
|
output_line( "Record file open failed" );
|
|
}
|
|
}
|
|
}
|
|
|
|
} /* z_input_stream */
|
|
|
|
/*
|
|
* playback_line
|
|
*
|
|
* Get a line of input from the command file.
|
|
*
|
|
*/
|
|
|
|
int playback_line( int buflen, char *buffer, int *read_size )
|
|
{
|
|
char *cp;
|
|
|
|
if ( recording == ON || replaying == OFF )
|
|
{
|
|
return ( -1 );
|
|
}
|
|
|
|
if ( fgets( buffer, buflen, rfp ) == NULL )
|
|
{
|
|
close_record( );
|
|
return ( -1 );
|
|
}
|
|
else
|
|
{
|
|
cp = strrchr( buffer, '\n' );
|
|
if ( cp != NULL )
|
|
{
|
|
*cp = '\0';
|
|
}
|
|
*read_size = (int)strlen( buffer );
|
|
output_line( buffer );
|
|
}
|
|
|
|
return ( '\n' );
|
|
|
|
} /* playback_line */
|
|
|
|
/*
|
|
* playback_key
|
|
*
|
|
* Get a key from the command file.
|
|
*
|
|
*/
|
|
|
|
int playback_key( void )
|
|
{
|
|
int c;
|
|
|
|
if ( recording == ON || replaying == OFF )
|
|
{
|
|
return ( -1 );
|
|
}
|
|
|
|
if ( fscanf( rfp, "<%o>\n", &c ) == EOF )
|
|
{
|
|
close_record( );
|
|
c = -1;
|
|
}
|
|
|
|
return ( c );
|
|
|
|
} /* playback_key */
|