/* fastmem.c - Memory related functions (fast version without virtual memory)
 *	Copyright (c) 1995-1997 Stefan Jokisch
 *
 * This file is part of Frotz.
 *
 * Frotz is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * Frotz is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
 */

/*
 * New undo mechanism added by Jim Dunleavy <jim.dunleavy@erha.ie>
 */

#include <PA9.h>

#include <stdio.h>
#include <string.h>
#include "frotz.h"

#include "main.h"
#include "draw.h"

#ifdef MSDOS_16BIT

#include <alloc.h>

#define malloc(size)	farmalloc (size)
#define realloc(size,p)	farrealloc (size,p)
#define free(size)		farfree (size)
#define memcpy(d,s,n)	_fmemcpy (d,s,n)

#else

#include <stdlib.h>

#ifndef SEEK_SET
#define SEEK_SET 0
#define SEEK_CUR 1
#define SEEK_END 2
#endif

#define far

#endif


// -------------------------------------------------------------------------------------------------
// -------------------------------------------------------------------------------------------------
// DS version specifics
// -------------------------------------------------------------------------------------------------
// -------------------------------------------------------------------------------------------------

//extern void 		PRINT_STRING		( int screen, char* _text, int x, int y, int letterCol, int shadowsCol, int _widthmax );
//extern int 		FILL_SCREEN			( int screen, int colour );
//#define WRITE_CHAR( _c_ ) 			PA_Save8bit( offset, (unsigned char)(_c_) ); offset ++
//#define FGETC_SRAM() 					((int)PA_Load8bit(offset))

#define WRITE_CHAR( _data_, _offset_, _c_ ) 	_data_[offset] = _c_; offset ++
#define READ_CHAR( _data_, _offset_ ) 		 	_data_[offset]

// -------------------------------------------------------------------------------------------------
// end of DS version specifics
// -------------------------------------------------------------------------------------------------

extern void seed_random (int);
extern void restart_screen (void);
extern void refresh_text_style (void);
extern void call (zword, int, zword *, int);
extern void split_window (zword);
extern void script_open (void);
extern void script_close (void);

extern ZFILE *os_path_open (const char *, const char *);

extern zword save_quetzal (ZFILE *, ZFILE *);
extern zword restore_quetzal (ZFILE *, ZFILE *);

extern void erase_window (zword);

extern void (*op0_opcodes[]) (void);
extern void (*op1_opcodes[]) (void);
extern void (*op2_opcodes[]) (void);
extern void (*var_opcodes[]) (void);

char save_name[MAX_FILE_NAME + 1] = DEFAULT_SAVE_NAME;
char auxilary_name[MAX_FILE_NAME + 1] = DEFAULT_AUXILARY_NAME;

zbyte far *zmp = NULL;
zbyte far *pcp = NULL;

#ifdef VMEM

zchar *pagebuf;
zchar **pages;

#define VMEM_SIZE 512
#define VMEM_PAGES 256

#endif

static ZFILE *story_fp = NULL;

/*
 * Data for the undo mechanism.
 * This undo mechanism is based on the scheme used in Evin Robertson's
 * Nitfol interpreter.
 * Undo blocks are stored as differences between states.
 */

typedef struct undo_struct undo_t;
struct undo_struct {
    undo_t *next;
    undo_t *prev;
    long pc;
    long diff_size;
    zword frame_count;
    zword stack_size;
    zword frame_offset;
    /* undo diff and stack data follow */
};

static undo_t *first_undo = NULL, *last_undo = NULL, *curr_undo = NULL;
static zbyte *undo_mem = NULL, *prev_zmp, *undo_diff;

static int undo_count = 0;

/*
 * get_header_extension
 *
 * Read a value from the header extension (former mouse table).
 *
 */

zword get_header_extension (int entry)
{
    zword addr;
    zword val;

    if (h_extension_table == 0 || entry > hx_table_size)
	return 0;

    addr = h_extension_table + 2 * entry;
    LOW_WORD (addr, val)

    return val;

}/* get_header_extension */

/*
 * set_header_extension
 *
 * Set an entry in the header extension (former mouse table).
 *
 */

void set_header_extension (int entry, zword val)
{
    zword addr;

    if (h_extension_table == 0 || entry > hx_table_size)
	return;

    addr = h_extension_table + 2 * entry;
    SET_WORD (addr, val)

}/* set_header_extension */

/*
 * restart_header
 *
 * Set all header fields which hold information about the interpreter.
 *
 */

void restart_header (void)
{
    zword screen_x_size;
    zword screen_y_size;
    zbyte font_x_size;
    zbyte font_y_size;

    int i;

    SET_BYTE (H_CONFIG, h_config)
    SET_WORD (H_FLAGS, h_flags)

    if (h_version >= V4) {
	SET_BYTE (H_INTERPRETER_NUMBER, h_interpreter_number)
	SET_BYTE (H_INTERPRETER_VERSION, h_interpreter_version)
	SET_BYTE (H_SCREEN_ROWS, h_screen_rows)
	SET_BYTE (H_SCREEN_COLS, h_screen_cols)
    }

    /* It's less trouble to use font size 1x1 for V5 games, especially
       because of a bug in the unreleased German version of "Zork 1" */

    if (h_version != V6) {
	screen_x_size = (zword) h_screen_cols;
	screen_y_size = (zword) h_screen_rows;
	font_x_size = 1;
	font_y_size = 1;
    } else {
	screen_x_size = h_screen_width;
	screen_y_size = h_screen_height;
	font_x_size = h_font_width;
	font_y_size = h_font_height;
    }

    if (h_version >= V5) {
	SET_WORD (H_SCREEN_WIDTH, screen_x_size)
	SET_WORD (H_SCREEN_HEIGHT, screen_y_size)
	SET_BYTE (H_FONT_HEIGHT, font_y_size)
	SET_BYTE (H_FONT_WIDTH, font_x_size)
	SET_BYTE (H_DEFAULT_BACKGROUND, h_default_background)
	SET_BYTE (H_DEFAULT_FOREGROUND, h_default_foreground)
    }

    if (h_version == V6)
	for (i = 0; i < 8; i++)
	    storeb ((zword) (H_USER_NAME + i), h_user_name[i]);

    SET_BYTE (H_STANDARD_HIGH, h_standard_high)
    SET_BYTE (H_STANDARD_LOW, h_standard_low)

}/* restart_header */

/*
 * init_memory
 *
 * Allocate memory and load the story file.
 *
 */

void init_memory (void)
{
    long size;
    zword addr;
    unsigned n;
    int i, j;

    static struct {
	enum story story_id;
	zword release;
	zbyte serial[6];
    } records[] = {
	{       SHERLOCK,  21, "871214" },
	{       SHERLOCK,  26, "880127" },
	{    BEYOND_ZORK,  47, "870915" },
	{    BEYOND_ZORK,  49, "870917" },
	{    BEYOND_ZORK,  51, "870923" },
	{    BEYOND_ZORK,  57, "871221" },
	{      ZORK_ZERO, 296, "881019" },
	{      ZORK_ZERO, 366, "890323" },
	{      ZORK_ZERO, 383, "890602" },
	{      ZORK_ZERO, 393, "890714" },
	{         SHOGUN, 292, "890314" },
	{         SHOGUN, 295, "890321" },
	{         SHOGUN, 311, "890510" },
	{         SHOGUN, 322, "890706" },
	{         ARTHUR,  54, "890606" },
	{         ARTHUR,  63, "890622" },
	{         ARTHUR,  74, "890714" },
	{        JOURNEY,  26, "890316" },
	{        JOURNEY,  30, "890322" },
	{        JOURNEY,  77, "890616" },
	{        JOURNEY,  83, "890706" },
	{ LURKING_HORROR, 203, "870506" },
	{ LURKING_HORROR, 219, "870912" },
	{ LURKING_HORROR, 221, "870918" },
	{        UNKNOWN,   0, "------" }
    };

	StartTrapMainChars();

    /* Open story file */

    //if ((story_fp = os_path_open(story_name, "rb")) == NULL)
	if ((story_fp = os_path_open(gGameName, "rb")) == NULL)
		os_fatal ("Cannot open story file");

    /* Allocate memory for story header */

    if ((zmp = (zbyte far *) malloc (64)) == NULL)
	os_fatal ("Out of memory");

#ifdef VMEM
	/* Allocate the page lookup tables */
	pages = malloc(VMEM_SIZE*4 * sizeof(int*));
	pages[0] = zmp;
#endif

    /* Load header into memory */

    if (ZFREAD (zmp, 1, 64, story_fp) != 64)
	os_fatal ("Story file read error");

    /* Copy header fields to global variables */

    LOW_BYTE (H_VERSION, h_version)

    if (h_version < V1 || h_version > V8)
	os_fatal ("Unknown Z-code version");

    LOW_BYTE (H_CONFIG, h_config)

    if (h_version == V3 && (h_config & CONFIG_BYTE_SWAPPED))
	os_fatal ("Byte swapped story file");

    LOW_WORD (H_RELEASE, h_release)
    LOW_WORD (H_RESIDENT_SIZE, h_resident_size)
    LOW_WORD (H_START_PC, h_start_pc)
    LOW_WORD (H_DICTIONARY, h_dictionary)
    LOW_WORD (H_OBJECTS, h_objects)
    LOW_WORD (H_GLOBALS, h_globals)
    LOW_WORD (H_DYNAMIC_SIZE, h_dynamic_size)
    LOW_WORD (H_FLAGS, h_flags)

    for (i = 0, addr = H_SERIAL; i < 6; i++, addr++)
	LOW_BYTE (addr, h_serial[i])

    /* Auto-detect buggy story files that need special fixes */

    for (i = 0; records[i].story_id != UNKNOWN; i++) {

	if (h_release == records[i].release) {

	    for (j = 0; j < 6; j++)
		if (h_serial[j] != records[i].serial[j])
		    goto no_match;

	    story_id = records[i].story_id;

	}

    no_match: ; /* null statement */

    }

    LOW_WORD (H_ABBREVIATIONS, h_abbreviations)
    LOW_WORD (H_FILE_SIZE, h_file_size)

    /* Calculate story file size in bytes */

    if (h_file_size != 0) {

	story_size = (long) 2 * h_file_size;

	if (h_version >= V4)
	    story_size *= 2;
	if (h_version >= V6)
	    story_size *= 2;

    } else {		/* some old games lack the file size entry */

	ZFSEEK (story_fp, 0, SEEK_END);
	story_size = ZFTELL (story_fp);
	ZFSEEK (story_fp, 64, SEEK_SET);

    }

	/*
	DBG_PRINT( "H_VERSION - %d -", h_version )	
	DBG_PRINT( "H_CONFIG - %d -", h_config )	
    DBG_PRINT ("H_RELEASE - %d -", h_release)
    DBG_PRINT ("H_RESIDENT_SIZE - %d -", h_resident_size)
    DBG_PRINT ("H_START_PC - %d -", h_start_pc)
    DBG_PRINT ("H_DICTIONARY - %d -", h_dictionary)
    DBG_PRINT ("H_OBJECTS - %d -", h_objects)
    DBG_PRINT ("H_GLOBALS - %d -", h_globals)
    DBG_PRINT ("H_DYNAMIC_SIZE - %d -", (int)h_dynamic_size)
    DBG_PRINT ("H_FLAGS - %d -", (int)h_flags)
	DBG_PRINT ("STORY_SIZE - %d -", (int)story_size)
    DBG_PRINT ("STORY_FP - %d -", (int)story_fp)
	*/

    LOW_WORD (H_CHECKSUM, h_checksum)
    LOW_WORD (H_ALPHABET, h_alphabet)
    LOW_WORD (H_FUNCTIONS_OFFSET, h_functions_offset)
    LOW_WORD (H_STRINGS_OFFSET, h_strings_offset)
    LOW_WORD (H_TERMINATING_KEYS, h_terminating_keys)
    LOW_WORD (H_EXTENSION_TABLE, h_extension_table)

    /* Zork Zero Macintosh doesn't have the graphics flag set */

    if (story_id == ZORK_ZERO && h_release == 296)
	h_flags |= GRAPHICS_FLAG;

    /* Adjust opcode tables */

    if (h_version <= V4) {
	op0_opcodes[0x09] = z_pop;
	op1_opcodes[0x0f] = z_not;
    } else {
	op0_opcodes[0x09] = z_catch;
	op1_opcodes[0x0f] = z_call_n;
    }


#ifdef VMEM

	free(zmp);
	zmp = story_rom_ptr;

	for(i=0; i<VMEM_SIZE*4;i ++)
		pages[i] = &zmp[i*256];
	pagebuf = malloc(VMEM_PAGES * 256);

#else

    /* Allocate memory for story data */

    if ((zmp = (zbyte far *) realloc (zmp, story_size)) == NULL)
	os_fatal ("Out of memory");

    /* Load story file in chunks of 32KB */

    n = 0x8000;

    for (size = 64; size < story_size; size += n) {

	if (story_size - size < 0x8000)
	    n = (unsigned) (story_size - size);

	SET_PC (size)

	if (ZFREAD (pcp, 1, n, story_fp) != n)
	    os_fatal ("Story file read error");

    }

#endif


    /* Read header extension table */

    hx_table_size = get_header_extension (HX_TABLE_SIZE);
    hx_unicode_table = get_header_extension (HX_UNICODE_TABLE);

/*int k;
GET_PC(k);
sprintf( tttt,"INIT OK - zmp=%d, pcp=%d - GET_PC=%d", zmp,pcp,k );
DBG_PRINT(tttt,0);
*/
//while( TRUE ) { if ( Stylus.Newpress ) break; PA_WaitForVBL(); } PA_WaitForVBL();

}/* init_memory */

/*
 * init_undo
 *
 * Allocate memory for multiple undo. It is important not to occupy
 * all the memory available, since the IO interface may need memory
 * during the game, e.g. for loading sounds or pictures.
 *
 */

void init_undo (void)
{
    void far *reserved;

    reserved = NULL;	/* makes compilers shut up */

    if (reserve_mem != 0) {
	if ((reserved = malloc (reserve_mem)) == NULL)
	    return;
    }

    /* Allocate h_dynamic_size bytes for previous dynamic zmp state
       + 1.5 h_dynamic_size for Quetzal diff + 2. */
    undo_mem = malloc ((h_dynamic_size * 5) / 2 + 2);
    if (undo_mem != NULL) {
	prev_zmp = undo_mem;
	undo_diff = undo_mem + h_dynamic_size;
	memcpy (prev_zmp, zmp, h_dynamic_size);
    } else
	option_undo_slots = 0;

    if (reserve_mem != 0)
	free (reserved);

}/* init_undo */

/*
 * free_undo
 *
 * Free count undo blocks from the beginning of the undo list.
 *
 */

static void free_undo (int count)
{
    undo_t *p;

    if (count > undo_count)
	count = undo_count;
    while (count--) {
	p = first_undo;
	if (curr_undo == first_undo)
	    curr_undo = curr_undo->next;
	first_undo = first_undo->next;
	free (p);
	undo_count--;
    }
    if (first_undo)
	first_undo->prev = NULL;
    else
	last_undo = NULL;
}/* free_undo */

/*
 * reset_memory
 *
 * Close the story file and deallocate memory.
 *
 */

void reset_memory (void)
{

    ZFCLOSE (story_fp);

    if (undo_mem) {
	free_undo (undo_count);
	free (undo_mem);
    }
    free (zmp);

}/* reset_memory */

/*
 * storeb
 *
 * Write a byte value to the dynamic Z-machine memory.
 *
 */

void storeb (zword addr, zbyte value)
{

    if (addr >= h_dynamic_size)
	runtime_error (ERR_STORE_RANGE);

    if (addr == H_FLAGS + 1) {	/* flags register is modified */

	h_flags &= ~(SCRIPTING_FLAG | FIXED_FONT_FLAG);
	h_flags |= value & (SCRIPTING_FLAG | FIXED_FONT_FLAG);

	if (value & SCRIPTING_FLAG) {
	    if (!ostream_script)
		script_open ();
	} else {
	    if (ostream_script)
		script_close ();
	}

	refresh_text_style ();

    }

    SET_BYTE (addr, value)

}/* storeb */

/*
 * storew
 *
 * Write a word value to the dynamic Z-machine memory.
 *
 */

void storew (zword addr, zword value)
{

    storeb ((zword) (addr + 0), hi (value));
    storeb ((zword) (addr + 1), lo (value));

}/* storew */

/*
 * z_restart, re-load dynamic area, clear the stack and set the PC.
 *
 * 	no zargs used
 *
 */

void z_restart (void)
{
    static zbool first_restart = TRUE;

    flush_buffer ();

    os_restart_game (RESTART_BEGIN);

    seed_random (0);

    if (!first_restart) {

	ZFSEEK (story_fp, 0, SEEK_SET);

	if (ZFREAD (zmp, 1, h_dynamic_size, story_fp) != h_dynamic_size)
	    os_fatal ("Story file read error");

    } else first_restart = FALSE;

    restart_header ();
    restart_screen ();

    sp = zfp = stack + STACK_SIZE;
    frame_count = 0;

    if (h_version != V6) {

	long pc = (long) h_start_pc;
	SET_PC (pc)

    } else call (h_start_pc, 0, NULL, 0);

    os_restart_game (RESTART_END);

}/* z_restart */

/*
 * get_default_name
 *
 * Read a default file name from the memory of the Z-machine and
 * copy it to a string.
 *
 */
/*
static void get_default_name (char *default_name, zword addr)
{

    if (addr != 0) {

	zbyte len;
	int i;

	LOW_BYTE (addr, len)
	addr++;

	for (i = 0; i < len; i++) {

	    zbyte c;

	    LOW_BYTE (addr, c)
	    addr++;

	    if (c >= 'A' && c <= 'Z')
		c += 'a' - 'A';

	    default_name[i] = c;

	}

	default_name[i] = 0;

	if (strchr (default_name, '.') == NULL)
	    strcpy (default_name + i, ".AUX");

    } else strcpy (default_name, auxilary_name);

}	// get_default_name 
*/



/* --------------------------------------------------------------------------------------------------------
   --------------------------------------------------------------------------------------------------------
   --------------------------------------------------------------------------------------------------------
 *
 * z_save_DS : save the Z-machine state to SRAM
 *
 --------------------------------------------------------------------------------------------------------
 --------------------------------------------------------------------------------------------------------
 -------------------------------------------------------------------------------------------------------- */
 

int z_save_DS ( unsigned char* _data, int _start_offset )
{
	long	pc;
	zword	addr;
	zword	nsp, nfp;
	int		skip;
	int		i;
	int		offset = _start_offset;

DBG_PRINT( "SAVE SRAM offset - %d", offset );

	WRITE_CHAR ( _data, offset, (int) hi (h_release)  ); 

DBG_PRINT( "SAVE SRAM - %d", (int) hi (h_release) );

	WRITE_CHAR ( _data, offset, (int) lo (h_release)  );
DBG_PRINT( "SAVE SRAM - %d", (int) lo (h_release) );

	WRITE_CHAR ( _data, offset, (int) hi (h_checksum) );
DBG_PRINT( "SAVE SRAM - %d", (int) hi (h_checksum) );

	WRITE_CHAR ( _data, offset, (int) lo (h_checksum) );
DBG_PRINT( "SAVE SRAM - %d", (int) lo (h_checksum) );

    GET_PC ( pc );
DBG_PRINT( "SAVE SRAM PC - %d", (int) pc );

    WRITE_CHAR ( _data, offset, (int) (pc >> 16) & 0xff 	);
    WRITE_CHAR ( _data, offset, (int) (pc >> 8) & 0xff 	);
    WRITE_CHAR ( _data, offset, (int) (pc) & 0xff 		);

    nsp = (int) (sp - stack);
DBG_PRINT( "SAVE SRAM NSP - %d", (int) nsp );

    WRITE_CHAR ( _data, offset, (int) hi (nsp)			);
    WRITE_CHAR ( _data, offset, (int) lo (nsp)			);

    nfp = (int) (zfp - stack);
DBG_PRINT( "SAVE SRAM NFP - %d", (int) nfp );

    WRITE_CHAR ( _data, offset, (int) hi (nfp)			);
    WRITE_CHAR ( _data, offset, (int) lo (nfp)			);

	for (i = nsp; i < STACK_SIZE; i++) 
	{
		WRITE_CHAR ( _data, offset, (int) hi (stack[i]) );
		WRITE_CHAR ( _data, offset, (int) lo (stack[i]) );
	}

DBG_PRINT( "SAVE SRAM OFFSET - %d", (int)(offset - _start_offset) );

	ZFSEEK ( story_fp, 0, SEEK_SET );

#ifdef VMEM

zozozo

		for (addr = 0, skip = 0; addr < h_dynamic_size; addr++)
		{
			zchar bt;
			LOW_BYTE(addr, bt);
			if (bt != ZFGETC (story_fp) || skip == 255 || addr + 1 == h_dynamic_size)
			{
				WRITE_CHAR (skip );
				WRITE_CHAR (bt );
				skip = 0;
			} else skip++;
		}

#else
DBG_PRINT( "SAVE SRAM DYNSIZE - %d", (int) h_dynamic_size );

		for (addr = 0, skip = 0; addr < h_dynamic_size; addr++)
		{
			if (zmp[addr] != ZFGETC (story_fp) || skip == 255 || addr + 1 == h_dynamic_size) 
			{
				WRITE_CHAR ( _data, offset,  skip	);
				WRITE_CHAR ( _data, offset,  zmp[addr] );
				skip = 0;
			} 
			else 
			{
				skip++;
			}
		}
#endif

DBG_PRINT( "SAVE SRAM OFFSET - %d", (int)(offset - _start_offset) );

DBG_PRINT( "WRITTEN DATA - %d", offset - _start_offset );


	return offset - _start_offset;
}


/* --------------------------------------------------------------------------------------------------------
   --------------------------------------------------------------------------------------------------------
   --------------------------------------------------------------------------------------------------------
 * z_restore_DS : restore a Z-machine state from SRAM
 *		result < 0		error
		result >= 0 	success
 --------------------------------------------------------------------------------------------------------
 --------------------------------------------------------------------------------------------------------
 ---------------------------------------------------------------------------------------------------------- */

extern int z_restore_DS( unsigned char* _data, int _start_offset )
{
	long	pc;
	zword	addr;
	int 	i;
	int		nsp, nfp;
	int 	success = 0;
	zword	release	= 0;
	int		offset 	= _start_offset;

DBG_PRINT( "RESTORE OFFSET - %d", offset );

	release = (unsigned) READ_CHAR(_data, offset) << 8; 		offset ++;
	release |= READ_CHAR(_data, offset);						offset ++;

DBG_PRINT( "RESTORE RELEASE - %d", release );
DBG_PRINT( "RESTORE H_RELEASE - %d", h_release );

	(void) READ_CHAR(_data, offset);							offset ++;	// checksum : ignored
	(void) READ_CHAR(_data, offset);							offset ++;	// checksum : ignored

	    /* Check the release number */

	if (release == h_release) 
	{
		pc = (long) READ_CHAR(_data, offset) << 16;			offset ++;
		pc |= (unsigned) READ_CHAR(_data, offset) << 8;		offset ++;
		pc |= READ_CHAR(_data, offset);						offset ++;

DBG_PRINT( "RESTORE PC - %d", (int) pc );

		SET_PC (pc)
		
		nsp = (READ_CHAR(_data, offset) << 8);					offset ++;
		nsp += READ_CHAR(_data, offset);						offset ++;
		sp = stack + nsp;

DBG_PRINT( "RESTORE SP - %d", (int) nsp );

		nfp = (READ_CHAR(_data, offset) << 8);					offset ++;
		nfp += READ_CHAR(_data, offset);						offset ++;
		zfp = stack + nfp;
DBG_PRINT( "RESTORE ZFP - %d", (int) nfp );

		for (i = (int) (sp - stack); i < STACK_SIZE; i++) 
		{
		    stack[i] = (unsigned) READ_CHAR(_data, offset) << 8;	offset ++;
		    stack[i] |= READ_CHAR(_data, offset);					offset ++;
		}
DBG_PRINT( "RESTORE lastoffset : %d", (int)(offset-_start_offset) );

		ZFSEEK (story_fp, 0, SEEK_SET);

		for (addr = 0; addr < h_dynamic_size; addr++) 
		{
			int skip = READ_CHAR(_data, offset);			offset ++;
			for (i = 0; i < skip; i++)
			{
				zmp[addr] = ZFGETC (story_fp);
				addr ++;
			}
			zmp[addr] = READ_CHAR(_data, offset);			offset ++;
			ZFGETC (story_fp);
		}

DBG_PRINT( "RESTORE lastoffset : %d", (int)(offset - _start_offset) );
//DBG_PRINT( "RESTORE zozo : %d", zozo );
		/* Check for errors */
DBG_PRINT( "RESTORE addr : %d", addr );
DBG_PRINT( "RESTORE h_dynamic_size : %d", h_dynamic_size );
		//if (ferror (gfp) || ferror (story_fp) || addr != h_dynamic_size)
		if ( addr != h_dynamic_size)
		{
DBG_PRINT( "RESTORE Success : %d", -1 );
		    success = -1;
		}
		else
		{
		    /* Success */
DBG_PRINT( "RESTORE Success : %d", 2 );
		    success = 2;
		}
	} 
	else 
	{
		print_string ("Invalid save file\n");
	}

	return success;
}

/* --------------------------------------------------------------------------------------------------------
 * z_restore, restore [a part of] a Z-machine state from disk
 *
 *	zargs[0] = address of area to restore (optional)
 *	zargs[1] = number of bytes to restore
 *	zargs[2] = address of suggested file name
 *
 ---------------------------------------------------------------------------------------------------------- */

void z_restore (void)
{
	zword success = 0;
	
	if (zargc != 0) 
	{
		// not implemented
	}
	else
	{
		if ( option_save_quetzal )
		{
			// not implemented
		}
		else
		{
			success = Restore();
		}
	}
#if 0
    char new_name[MAX_FILE_NAME + 1];
    char default_name[MAX_FILE_NAME + 1];
    ZFILE *gfp;

    zword success = 0;

    if (zargc != 0) {

	/* Get the file name */

	get_default_name (default_name, (zargc >= 3) ? zargs[2] : 0);

	if (os_read_file_name (new_name, default_name, FILE_LOAD_AUX) == 0)
	    goto finished;

	strcpy (auxilary_name, default_name);

	/* Open auxilary file */

	if ((gfp = fopen (new_name, "rb")) == NULL)
	    goto finished;

	/* Load auxilary file */

	success = ZFREAD (zmp + zargs[0], 1, zargs[1], gfp);

	/* Close auxilary file */

	fclose (gfp);

    } else {

	long pc;
	zword release;
	zword addr;
	int i;

	/* Get the file name */

	if (os_read_file_name (new_name, save_name, FILE_RESTORE) == 0)
	    goto finished;

	strcpy (save_name, new_name);

	/* Open game file */

	if ((gfp = fopen (new_name, "rb")) == NULL)
	    goto finished;

	if (option_save_quetzal) {
	    success = restore_quetzal (gfp, story_fp);

	} else {
	    /* Load game file */

	    release = (unsigned) ZFGETC (gfp) << 8;
	    release |= ZFGETC (gfp);

	    (void) ZFGETC (gfp);
	    (void) ZFGETC (gfp);

	    /* Check the release number */

	    if (release == h_release) 
		{

			pc = (long) ZFGETC (gfp) << 16;
			pc |= (unsigned) ZFGETC (gfp) << 8;
			pc |= ZFGETC (gfp);
	
			SET_PC (pc)
	
			sp = stack + (ZFGETC (gfp) << 8);
			sp += ZFGETC (gfp);
			zfp = stack + (ZFGETC (gfp) << 8);
			zfp += ZFGETC (gfp);
	
			for (i = (int) (sp - stack); i < STACK_SIZE; i++) 
			{
				stack[i] = (unsigned) ZFGETC (gfp) << 8;
				stack[i] |= ZFGETC (gfp);
			}
	
			ZFSEEK (story_fp, 0, SEEK_SET);
	
			for (addr = 0; addr < h_dynamic_size; addr++) 
			{
				int skip = ZFGETC (gfp);
	#ifdef VMEM
				for (i = 0; i < skip; i++) {
					int bt, c;
					LOW_BYTE(addr, bt);
					c = ZFGETC (story_fp);
					if(c != bt)
						SET_BYTE(addr, c);
					addr++;
				}
				SET_BYTE(addr, ZFGETC (gfp));
	#else
				for (i = 0; i < skip; i++)
					zmp[addr++] = ZFGETC (story_fp);
				zmp[addr] = ZFGETC (gfp);
	#endif
				(void) ZFGETC (story_fp);
			}

		/* Check for errors */

			if (ferror (gfp) || ferror (story_fp) || addr != h_dynamic_size)
				success = -1;
			else
	
				/* Success */
	
				success = 2;

	    } else print_string ("Invalid save file\n");
	}
#endif

	if ((short) success >= 0) {

	    /* Close game file */

	    // DS version - no file : fclose (gfp);

	    if ((short) success > 0) 
		{
			zbyte old_screen_rows;
			zbyte old_screen_cols;
	
			/* In V3, reset the upper window. */
			if (h_version == V3)
				split_window (0);
	
			LOW_BYTE (H_SCREEN_ROWS, old_screen_rows);
			LOW_BYTE (H_SCREEN_COLS, old_screen_cols);
	
			/* Reload cached header fields. */
			restart_header ();
	
			/*
			 * Since QUETZAL files may be saved on many different machines,
			 * the screen sizes may vary a lot. Erasing the status window
			 * seems to cover up most of the resulting badness.
			 */
			if (h_version > V3 && h_version != V6
				&& (h_screen_rows != old_screen_rows
				|| h_screen_cols != old_screen_cols))
				erase_window (1);
	    }
	} 
	else
	{
	    os_fatal ("Error reading save file");
    }

//finished:

    if (h_version <= V3)
		branch (success);
    else
		store (success);

}/* z_restore */

/*
 * mem_diff
 *
 * Set diff to a Quetzal-like difference between a and b,
 * copying a to b as we go.  It is assumed that diff points to a
 * buffer which is large enough to hold the diff.
 * mem_size is the number of bytes to compare.
 * Returns the number of bytes copied to diff.
 *
 */

static long mem_diff (zbyte *a, zbyte *b, zword mem_size, zbyte *diff)
{
    unsigned size = mem_size;
    zbyte *p = diff;
    unsigned j;
    zbyte c;

    for (;;) {
	for (j = 0; size > 0 && (c = *a++ ^ *b++) == 0; j++)
	    size--;
	if (size == 0) break;
	size--;
	if (j > 0x8000) {
	    *p++ = 0;
	    *p++ = 0xff;
	    *p++ = 0xff;
	    j -= 0x8000;
	}
	if (j > 0) {
	    *p++ = 0;
	    j--;
	    if (j <= 0x7f) {
		*p++ = j;
	    } else {
		*p++ = (j & 0x7f) | 0x80;
		*p++ = (j & 0x7f80) >> 7;
	    }
	}
	*p++ = c;
	*(b - 1) ^= c;
    }
    return p - diff;
}/* mem_diff */

/*
 * mem_undiff
 *
 * Applies a quetzal-like diff to dest
 *
 */

static void mem_undiff (zbyte *diff, long diff_length, zbyte *dest)
{
    zbyte c;

    while (diff_length) {
	c = *diff++;
	diff_length--;
	if (c == 0) {
	    unsigned runlen;

	    if (!diff_length)
		return;  /* Incomplete run */
	    runlen = *diff++;
	    diff_length--;
	    if (runlen & 0x80) {
		if (!diff_length)
		    return; /* Incomplete extended run */
		c = *diff++;
		diff_length--;
		runlen = (runlen & 0x7f) | (((unsigned) c) << 7);
	    }

	    dest += runlen + 1;
	} else {
	    *dest++ ^= c;
	}
    }
}/* mem_undiff */

/*
 * restore_undo
 *
 * This function does the dirty work for z_restore_undo.
 *
 */

int restore_undo (void)
{

    if (option_undo_slots == 0)	/* undo feature unavailable */

	return -1;

    if (curr_undo == NULL)	/* no saved game state */

	return 0;

    /* undo possible */

    memcpy (zmp, prev_zmp, h_dynamic_size);
    SET_PC (curr_undo->pc)
    sp = stack + STACK_SIZE - curr_undo->stack_size;
    zfp = stack + curr_undo->frame_offset;
    frame_count = curr_undo->frame_count;
    mem_undiff ((zbyte *) (curr_undo + 1), curr_undo->diff_size, prev_zmp);
    memcpy (sp, (zbyte *)(curr_undo + 1) + curr_undo->diff_size,
	    curr_undo->stack_size * sizeof (*sp));

    curr_undo = curr_undo->prev;

    restart_header ();

    return 2;

}/* restore_undo */

/*
 * z_restore_undo, restore a Z-machine state from memory.
 *
 *	no zargs used
 *
 */

void z_restore_undo (void)
{

    store ((zword) restore_undo ());

}/* z_restore_undo */


/* --------------------------------------------------------------------------------------------------------
 * z_save, save [a part of] the Z-machine state to disk.
 *
 *	zargs[0] = address of memory area to save (optional)
 *	zargs[1] = number of bytes to save
 *	zargs[2] = address of suggested file name
 *
 -------------------------------------------------------------------------------------------------------- */

void z_save (void)
{
    zword success = 0;

DBG_PRINT( "SAVE _START %d", 0 );


	if (zargc != 0) 
	{
DBG_MSG( "SAVE _START %d", 1 );

	}
	else
	{
		if ( option_save_quetzal )
		{
DBG_MSG( "SAVE _QUETZAL %d", 1 );

		}
		else
		{
DBG_MSG( "SAVE_RESTORE %d bytes", zargs[1] );

			Save(  zargs[1] );
		}
	}


#if 0
    char new_name[MAX_FILE_NAME + 1];
    char default_name[MAX_FILE_NAME + 1];
    ZFILE *gfp;


    if (zargc != 0) {

	/* Get the file name */

	get_default_name (default_name, (zargc >= 3) ? zargs[2] : 0);

	if (os_read_file_name (new_name, default_name, FILE_SAVE_AUX) == 0)
	    goto finished;

	strcpy (auxilary_name, default_name);

	/* Open auxilary file */

	if ((gfp = fopen (new_name, "wb")) == NULL)
	    goto finished;

	/* Write auxilary file */

	success = fwrite (zmp + zargs[0], zargs[1], 1, gfp);

	/* Close auxilary file */

	fclose (gfp);

    } else {

	long pc;
	zword addr;
	zword nsp, nfp;
	int skip;
	int i;

	/* Get the file name */

	if (os_read_file_name (new_name, save_name, FILE_SAVE) == 0)
	    goto finished;

	strcpy (save_name, new_name);

	/* Open game file */

	if ((gfp = fopen (new_name, "wb")) == NULL)
	    goto finished;

	if (option_save_quetzal) {
	    success = save_quetzal (gfp, story_fp);
	} else {
	    /* Write game file */

	    fputc ((int) hi (h_release), gfp);
	    fputc ((int) lo (h_release), gfp);
	    fputc ((int) hi (h_checksum), gfp);
	    fputc ((int) lo (h_checksum), gfp);

	    GET_PC (pc)

	    fputc ((int) (pc >> 16) & 0xff, gfp);
	    fputc ((int) (pc >> 8) & 0xff, gfp);
	    fputc ((int) (pc) & 0xff, gfp);

	    nsp = (int) (sp - stack);
	    nfp = (int) (zfp - stack);

	    fputc ((int) hi (nsp), gfp);
	    fputc ((int) lo (nsp), gfp);
	    fputc ((int) hi (nfp), gfp);
	    fputc ((int) lo (nfp), gfp);

	    for (i = nsp; i < STACK_SIZE; i++) {
		fputc ((int) hi (stack[i]), gfp);
		fputc ((int) lo (stack[i]), gfp);
	    }

	    ZFSEEK (story_fp, 0, SEEK_SET);

#ifdef VMEM
		for (addr = 0, skip = 0; addr < h_dynamic_size; addr++)
		{
			zchar bt;
			LOW_BYTE(addr, bt);
			if (bt != ZFGETC (story_fp) || skip == 255 || addr + 1 == h_dynamic_size) {
				fputc (skip, gfp);
				fputc (bt, gfp);
				skip = 0;
			} else skip++;
		}

#else
		for (addr = 0, skip = 0; addr < h_dynamic_size; addr++)
			if (zmp[addr] != ZFGETC (story_fp) || skip == 255 || addr + 1 == h_dynamic_size) {
				fputc (skip, gfp);
				fputc (zmp[addr], gfp);
				skip = 0;
			} else skip++;
#endif
	}

	/* Close game file and check for errors */

	if (fclose (gfp) == EOF || ferror (story_fp)) {
	    print_string ("Error writing save file\n");
	    goto finished;
	}

	/* Success */

finished:

    }

#endif
	success = 1;

    if (h_version <= V3)
	branch (success);
    else
	store (success);
}/* z_save */

/*
 * save_undo
 *
 * This function does the dirty work for z_save_undo.
 *
 */

int save_undo (void)
{
    long diff_size;
    zword stack_size;
    undo_t *p;

    if (option_undo_slots == 0)		/* undo feature unavailable */

	return -1;

    /* save undo possible */

    while (last_undo != curr_undo) {
	p = last_undo;
	last_undo = last_undo->prev;
	free (p);
	undo_count--;
    }
    if (last_undo)
	last_undo->next = NULL;
    else
	first_undo = NULL;

    if (undo_count == option_undo_slots)
	free_undo (1);

    diff_size = mem_diff (zmp, prev_zmp, h_dynamic_size, undo_diff);
    stack_size = stack + STACK_SIZE - sp;
    do {
	p = malloc (sizeof (undo_t) + diff_size + stack_size * sizeof (*sp));
	if (p == NULL)
	    free_undo (1);
    } while (!p && undo_count);
    if (p == NULL)
	return -1;
    GET_PC (p->pc)
    p->frame_count = frame_count;
    p->diff_size = diff_size;
    p->stack_size = stack_size;
    p->frame_offset = zfp - stack;
    memcpy (p + 1, undo_diff, diff_size);
    memcpy ((zbyte *)(p + 1) + diff_size, sp, stack_size * sizeof (*sp));

    if (!first_undo) {
	p->prev = NULL;
	first_undo = p;
    } else {
	last_undo->next = p;
	p->prev = last_undo;
    }
    p->next = NULL;
    curr_undo = last_undo = p;
    undo_count++;
    return 1;

}/* save_undo */

/*
 * z_save_undo, save the current Z-machine state for a future undo.
 *
 *	no zargs used
 *
 */

void z_save_undo (void)
{

    store ((zword) save_undo ());

}/* z_save_undo */

/*
 * z_verify, check the story file integrity.
 *
 *	no zargs used
 *
 */

void z_verify (void)
{
    zword checksum = 0;
    long i;

    /* Sum all bytes in story file except header bytes */

    ZFSEEK (story_fp, 64, SEEK_SET);

    for (i = 64; i < story_size; i++)
	checksum += ZFGETC (story_fp);

    /* Branch if the checksums are equal */

    branch (checksum == h_checksum);

}/* z_verify */
