diff options
| author | Filippos Karapetis | 2009-02-18 14:28:32 +0000 | 
|---|---|---|
| committer | Filippos Karapetis | 2009-02-18 14:28:32 +0000 | 
| commit | 4c406e69eed2afa6cdb7216a163acc698435e172 (patch) | |
| tree | ba53f3ccfd8eaef167f0ddc69749c267b3aee2f2 | |
| parent | 9658836f5d332b9e63605df9cde27be16ab11c4a (diff) | |
| download | scummvm-rg350-4c406e69eed2afa6cdb7216a163acc698435e172.tar.gz scummvm-rg350-4c406e69eed2afa6cdb7216a163acc698435e172.tar.bz2 scummvm-rg350-4c406e69eed2afa6cdb7216a163acc698435e172.zip | |
Added missing file savegame.cfsml (hopefully, compilation should REALLY be fixed now, sorry again...)
svn-id: r38481
| -rw-r--r-- | engines/sci/engine/savegame.cfsml | 1226 | 
1 files changed, 1226 insertions, 0 deletions
| diff --git a/engines/sci/engine/savegame.cfsml b/engines/sci/engine/savegame.cfsml new file mode 100644 index 0000000000..57abbfb862 --- /dev/null +++ b/engines/sci/engine/savegame.cfsml @@ -0,0 +1,1226 @@ +/*************************************************************************** + savegame.cfsml Copyright (C) 1999 Christoph Reichenbach, TU Darmstadt + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + +    Christoph Reichenbach (CJR) [jameson@linuxgames.com] + +***************************************************************************/ +/* Savegame handling for state_t structs. Makes heavy use of cfsml magic. */ +/* DON'T EDIT savegame.c ! Only modify savegame.cfsml, if something needs +** to be changed. Refer to freesci/docs/misc/cfsml.spec if you don't understand +** savegame.cfsml. If this doesn't solve your problem, contact the maintainer. +*/ + +#include <sci_memory.h> +#include <gfx_operations.h> +#include <sfx_engine.h> +#include <engine.h> +#include <assert.h> +#include "heap.h" + +#ifdef _MSC_VER +#include <direct.h> +#endif + +#ifdef _WIN32 +#pragma warning( disable : 4101 ) +#endif + +#define HUNK_TYPE_GFX_SNAPSHOT_STRING "g\n" + +/* Missing: +** - SFXdriver +** - File input/output state (this is likely not to happen) +*/ + +static state_t *_global_save_state; +/* Needed for some graphical stuff. */ +#define FILE_VERSION _global_save_state->savegame_version + + +void +write_reg_t(FILE *fh, reg_t *foo) +{ +	fprintf(fh, PREG, PRINT_REG(*foo)); +} + +int +read_reg_t(FILE *fh, reg_t *foo, char *lastval, int *line, int *hiteof) +{ +	int segment, offset; + +	if (sscanf(lastval, PREG, &segment, &offset)<2) +	{ +		sciprintf("Error parsing reg_t on line %d\n", *line); +		return 1; +	} + +	*foo = make_reg(segment, offset); +	return 0; +} + +void +write_sci_version(FILE *fh, sci_version_t *foo) +{ +	fprintf(fh, "%d.%03d.%03d", SCI_VERSION_MAJOR(*foo), SCI_VERSION_MINOR(*foo), +		SCI_VERSION_PATCHLEVEL(*foo)); +} + +int +read_sci_version(FILE *fh, sci_version_t *foo, char *lastval, int *line, int *hiteof) +{ +	return version_parse(lastval, foo); +} + +void +write_PTN(FILE *fh, parse_tree_node_t *foo) +{ +	if (foo->type == PARSE_TREE_NODE_LEAF) +		fprintf(fh, "L%d", foo->content.value); +	else +		fprintf(fh, "B(%d,%d)", foo->content.branches[0], foo->content.branches[1]); +} + +int +read_PTN(FILE *fh, parse_tree_node_t *foo, char *lastval, int *line, int *hiteof) +{ +	if (lastval[0] == 'L') { +		char *c = lastval + 1; +		char *strend; + +		while (*c && isspace(*c)) +			++c; + +		if (!*c) +			return 1; + +		foo->content.value = strtol(c, &strend, 0); + +		return (strend == c); /* Error if nothing could be read */ + +		return 0; +	} else if (lastval[0] == 'B') { +		char *c = lastval + 1; +		char *strend; + +		while (*c && isspace(*c)) ++c; +		if (*c++ != '(') return 1; +		while (*c && isspace(*c)) ++c; + +		foo->content.branches[0] = strtol(c, &strend, 0); +		if (strend == c) +			return 1; +		c = strend; + +		while (*c && isspace(*c)) ++c; +		if (*c++ != ',') +			return 1; + +		while (*c && isspace(*c)) ++c; + +		foo->content.branches[1] = strtol(c, &strend, 0); +		if (strend == c) +			return 1; +		c = strend; + +		while (*c && isspace(*c)) ++c; +		if (*c++ != ')') return 1; + +		return 0; +	} else return 1; /* failure to parse anything */ +} + + +void +write_menubar_tp(FILE *fh, menubar_t **foo); +int +read_menubar_tp(FILE *fh, menubar_t **foo, char *lastval, int *line, int *hiteof); + +void +write_mem_obj_tp(FILE *fh, mem_obj_t **foo); +int +read_mem_obj_tp(FILE *fh, mem_obj_t **foo, char *lastval, int *line, int *hiteof); + +void +write_int_hash_map_tp(FILE *fh, int_hash_map_t **foo); +int +read_int_hash_map_tp(FILE *fh, int_hash_map_t **foo, char *lastval, int *line, int *hiteof); + +void +write_songlib_t(FILE *fh, songlib_t *foo); +int +read_songlib_t(FILE *fh, songlib_t *foo, char *lastval, int *line, int *hiteof); + +void +write_int_hash_map_node_tp(FILE *fh, int_hash_map_node_t **foo); +int +read_int_hash_map_node_tp(FILE *fh, int_hash_map_node_t **foo, char *lastval, int *line, int *hiteof); + +int +read_song_tp(FILE *fh, song_t **foo, char *lastval, int *line, int *hiteof); + +typedef mem_obj_t *mem_obj_ptr; + +%CFSML + +TYPE byte "byte" LIKE int; +TYPE long "long" LIKE int; +TYPE gint16 "gint16" LIKE int; +TYPE seg_id_t "seg_id_t" LIKE int; +TYPE sci_version_t "sci_version_t" USING write_sci_version read_sci_version; +TYPE menubar_tp "menubar_t *" USING write_menubar_tp read_menubar_tp; +TYPE mem_obj_t "mem_obj_t" USING write_mem_obj_t read_mem_obj_t; +TYPE mem_obj_ptr "mem_obj_t *" USING write_mem_obj_tp read_mem_obj_tp; +TYPE reg_t "reg_t" USING write_reg_t read_reg_t; +TYPE size_t "size_t" LIKE int; +TYPE int_hash_map_tp "int_hash_map_t *" USING write_int_hash_map_tp read_int_hash_map_tp; +TYPE int_hash_map_node_tp "int_hash_map_node_t *" USING write_int_hash_map_node_tp read_int_hash_map_node_tp; +TYPE songlib_t "songlib_t" USING write_songlib_t read_songlib_t; +TYPE song_tp "song_t *" USING write_song_tp read_song_tp; +TYPE song_iterator_t "song_iterator_t" USING write_song_iterator_t read_song_iterator_t; +TYPE song_handle_t "song_handle_t" LIKE int; + +RECORD song_t "song_t" { +	song_handle_t handle; +	int resource_num; +	int priority; +	int status; +	int restore_behavior; +	int restore_time; +	int loops; +	int hold; +} +	 +RECORD int_hash_map_t "int_hash_map_t" { +	int base_value; +	int_hash_map_node_tp nodes[STATIC DCS_INT_HASH_MAX+1]; +} + +RECORD menu_item_t "menu_item_t" { +	int type; +	string keytext; +	int keytext_size; + +	int flags; +	byte said[STATIC MENU_SAID_SPEC_SIZE]; +	reg_t said_pos; +	string text; +	reg_t text_pos; +	int modifiers; +	int key; +	int enabled; +	int tag; +} + +RECORD menu_t "menu_t" { +	string title; +	int title_width; +	int width; + +	menu_item_t items[DYNAMIC items_nr]; +} + +RECORD menubar_t "menubar_t" { +	menu_t menus[DYNAMIC menus_nr]; +} + +RECORD synonym_t "synonym_t" { +	int replaceant; +	int replacement; +} + + +RECORD seg_manager_t "seg_manager_t" { +	int_hash_map_tp id_seg_map; +	mem_obj_ptr heap[DYNAMIC heap_size]; +	int heap_size; +	int reserved_id; +	int exports_wide; +	int sci1_1; +	int gc_mark_bits; +	size_t mem_allocated; +	seg_id_t clones_seg_id; +	seg_id_t lists_seg_id; +	seg_id_t nodes_seg_id; +} + +RECORD class_t "class_t" { +	int script; +	reg_t reg; +} + +RECORD sfx_state_t "sfx_state_t" { +	songlib_t songlib; +} + +RECORD state_t "state_t" { +	int savegame_version; + +	string game_version; +	sci_version_t version; +	menubar_tp menubar; +	int status_bar_foreground; +	int status_bar_background; +	seg_manager_t seg_manager; +	int classtable_size; +	class_t classtable[DYNAMIC classtable_size]; +	sfx_state_t sound; +} + +RECORD local_variables_t "local_variables_t" { +	int script_id; +	int nr; +	reg_t locals[DYNAMIC nr]; +} + +RECORD object_t "object_t" { +	int flags; +	reg_t pos; +	int variables_nr; +	int variable_names_nr; +	int methods_nr; +	reg_t variables[DYNAMIC variables_nr]; +} + +RECORD clone_t "clone_t" { +	int flags; +	reg_t pos; +	int variables_nr; +	int variable_names_nr; +	int methods_nr; +	reg_t variables[DYNAMIC variables_nr]; +} + +RECORD list_t "list_t" { +	reg_t first; +	reg_t last; +} + +RECORD node_t "node_t" { +	reg_t pred; +	reg_t succ; +	reg_t key; +	reg_t value; +} + +RECORD clone_entry_t "clone_entry_t" { +	int next_free; +	clone_t entry; +} + +RECORD clone_table_t "clone_table_t" { +	int entries_nr; +	int first_free; +	int entries_used; +	int max_entry; +	clone_entry_t table[DYNAMIC entries_nr]; +} + +RECORD list_entry_t "list_entry_t" { +	int next_free; +	list_t entry; +} + +RECORD list_table_t "list_table_t" { +	int entries_nr; +	int first_free; +	int entries_used; +	int max_entry; +	list_entry_t table[DYNAMIC entries_nr]; +} + +RECORD node_entry_t "node_entry_t" { +	int next_free; +	node_t entry; +} + +RECORD node_table_t "node_table_t" { +	int entries_nr; +	int first_free; +	int entries_used; +	int max_entry; +	node_entry_t table[DYNAMIC entries_nr]; +} + +RECORD script_t "script_t" { +	int nr; + +	size_t buf_size; +	size_t script_size; +	size_t heap_size; + +	int_hash_map_tp obj_indices; +	int exports_nr; +	int synonyms_nr; +	int lockers; +	int objects_allocated; +	int objects_nr; +	object_t objects[DYNAMIC objects_allocated]; + +	int locals_offset; +	int locals_segment; + +	int marked_as_deleted; +} + +RECORD sys_string_t "sys_string_t" { +	string name; +	int max_size; +	string value; +} + +RECORD sys_strings_t "sys_strings_t" { +	sys_string_t strings[STATIC SYS_STRINGS_MAX]; +} + +RECORD dynmem_t "dynmem_t" { +	int size; +	string description; +	byte buf[DYNAMIC size]; +} + +%END CFSML + +void  +write_songlib_t(FILE *fh, songlib_t *songlib) +{ +  song_t *seeker = *(songlib->lib); +  int songcount = song_lib_count(*songlib); + +  fprintf(fh, "{\n"); +  fprintf(fh, "songcount = %d\n", songcount); +  fprintf(fh, "list = \n"); +  fprintf(fh, "[\n"); +  while (seeker) +    { +      seeker->restore_time = seeker->it->get_timepos(seeker->it); +      %CFSMLWRITE song_t seeker INTO fh; +      seeker = seeker->next; +    } +  fprintf(fh, "]\n"); +  fprintf(fh, "}\n"); +} + +int +read_songlib_t(FILE *fh, songlib_t *songlib, char *lastval, int *line, int *hiteof) +{ +  int songcount; +  int i; +  song_t *newsong; +  int oldstatus; + +  fscanf(fh, "{\n"); +  fscanf(fh, "songcount = %d\n", &songcount); +  fscanf(fh, "list = \n"); +  fscanf(fh, "[\n"); +  *line += 4; +  song_lib_init(songlib); +  for (i = 0; i < songcount; i++) +    { +      %CFSMLREAD song_tp &newsong FROM fh ERRVAR *hiteof FIRSTTOKEN lastval LINECOUNTER *line; +      song_lib_add(*songlib, newsong); +    }   +  fscanf(fh, "]\n"); +  fscanf(fh, "}\n");; +  *line += 2; +  return 0; +} + +struct { +	int type; +	char *name; +} mem_obj_string_names[] = { +	{MEM_OBJ_INVALID, "INVALID"}, +	{MEM_OBJ_SCRIPT, "SCRIPT"}, +	{MEM_OBJ_CLONES, "CLONES"}, +	{MEM_OBJ_LOCALS, "LOCALS"}, +	{MEM_OBJ_STACK, "STACK"}, +	{MEM_OBJ_SYS_STRINGS,"SYS_STRINGS"}, +	{MEM_OBJ_LISTS,"LISTS"}, +	{MEM_OBJ_NODES,"NODES"}, +	{MEM_OBJ_HUNK,"HUNK"}, +	{MEM_OBJ_DYNMEM,"DYNMEM"}}; + +int +mem_obj_string_to_enum(char *str) +{ +	int i; + +	for (i = 0; i <= MEM_OBJ_MAX; i++) +	{		if (!strcasecmp(mem_obj_string_names[i].name, str)) +			return i; +	} + +	return -1; +} + +static int bucket_length; + +void +write_int_hash_map_tp(FILE *fh, int_hash_map_t **foo) +{ +	%CFSMLWRITE int_hash_map_t *foo INTO fh; +} + +void +write_song_tp(FILE *fh, song_t **foo) +{ +	%CFSMLWRITE song_t *foo INTO fh; +} + +song_iterator_t * +build_iterator(state_t *s, int song_nr, int type, songit_id_t id); + +int +read_song_tp(FILE *fh, song_t **foo, char *lastval, int *line, int *hiteof) +{ +  char *token; +  int assignment; +  *foo = (song_t*) malloc(sizeof(song_t)); +  token = _cfsml_get_identifier(fh, line, hiteof, &assignment); +  %CFSMLREAD song_t (*foo) FROM fh ERRVAR *hiteof FIRSTTOKEN token LINECOUNTER *line; +  (*foo)->delay = 0; +  (*foo)->it = NULL; +  (*foo)->next_playing = (*foo)->next_stopping = (*foo)->next = NULL; +  return 0; +} +int +read_int_hash_map_tp(FILE *fh, int_hash_map_t **foo, char *lastval, int *line, int *hiteof) +{ +	*foo = (int_hash_map_t*)malloc(sizeof(int_hash_map_t)); +	%CFSMLREAD int_hash_map_t (*foo) FROM fh ERRVAR *hiteof FIRSTTOKEN lastval LINECOUNTER *line; +	(*foo)->holes = NULL; +	return 0; +} + +void +write_int_hash_map_node_tp(FILE *fh, int_hash_map_node_t **foo) +{ +	if (!(*foo)) +	{ +		fputs("\\null", fh); +	} else +	{ +		fprintf(fh,"[\n%d=>%d\n", (*foo)->name, (*foo)->value); +		if ((*foo)->next) +		{ +			%CFSMLWRITE int_hash_map_node_tp &((*foo)->next) INTO fh; +		} else fputc('L', fh); +		fputs("]", fh); +	} +} + +int +read_int_hash_map_node_tp(FILE *fh, int_hash_map_node_t **foo, char *lastval, int *line, int *hiteof) +{ +	static char buffer[80]; + +	if (lastval[0] == '\\') { +		*foo = NULL; /* No hash map node */ +	} else { +		*foo = (int_hash_map_node_t*)malloc(sizeof(int_hash_map_node_t)); +		if (lastval[0] != '[') +		{ +			sciprintf("Expected opening bracket in hash_map_node_t on line %d\n", *line); +			return 1; +		} +		 +		do { +			(*line)++; +			fgets(buffer, 80, fh); +			if (buffer[0] == 'L') +			{ +				(*foo)->next = NULL; +				buffer[0] = buffer[1]; +			} /* HACK: deliberately no else clause here */ +			if (buffer[0] == ']')  +			{ +				break; +			} +			else if (buffer[0] == '[') +			{ +				if (read_int_hash_map_node_tp(fh, &((*foo)->next), buffer, line, hiteof)) +					return 1; +			} +			else if (sscanf(buffer, "%d=>%d", &((*foo)->name), &((*foo)->value))<2) +			{ +				sciprintf("Error parsing hash_map_node_t on line %d\n", *line); +				return 1; +			} +		} while (1); +	} + +	return 0; +} + +void +write_menubar_tp(FILE *fh, menubar_t **foo) +{ +	if (*foo) { + +		%CFSMLWRITE menubar_t (*foo) INTO fh; + +	} else { /* Nothing to write */ +		fputs("\\null\\", fh); +	} +} + + +int +read_menubar_tp(FILE *fh, menubar_t **foo, char *lastval, int *line, int *hiteof) +{ + +	if (lastval[0] == '\\') { +		*foo = NULL; /* No menu bar */ +	} else { + +		*foo = (menubar_t *) sci_malloc(sizeof(menubar_t)); +		%CFSMLREAD menubar_t (*foo) FROM fh ERRVAR *hiteof FIRSTTOKEN lastval LINECOUNTER *line; + +	} +	return *hiteof; +} + +void +write_mem_obj_t(FILE *fh, mem_obj_t *foo) +{ +	fprintf(fh, "%s\n", mem_obj_string_names[foo->type].name);	 +	%CFSMLWRITE int &foo->segmgr_id INTO fh; +	switch (foo->type) +	{ +	case MEM_OBJ_SCRIPT: +		%CFSMLWRITE script_t &foo->data.script INTO fh; +	break; +	case MEM_OBJ_CLONES: +		%CFSMLWRITE clone_table_t &foo->data.clones INTO fh; +	break; +	case MEM_OBJ_LOCALS: +		%CFSMLWRITE local_variables_t &foo->data.locals INTO fh; +	break; +	case MEM_OBJ_SYS_STRINGS: +		%CFSMLWRITE sys_strings_t &foo->data.sys_strings INTO fh; +	break; +	case MEM_OBJ_STACK: +		%CFSMLWRITE int &foo->data.stack.nr INTO fh; +	break; +	case MEM_OBJ_HUNK: +		break; +	case MEM_OBJ_LISTS:	 +		%CFSMLWRITE list_table_t &foo->data.lists INTO fh; +	break; +	case MEM_OBJ_NODES:	 +		%CFSMLWRITE node_table_t &foo->data.nodes INTO fh; +	break; +	case MEM_OBJ_DYNMEM: +		%CFSMLWRITE dynmem_t  &foo->data.dynmem INTO fh; +	break; +	} +} + +int +read_mem_obj_t(FILE *fh, mem_obj_t *foo, char *lastval, int *line, int *hiteof) +{ +	char buffer[80]; +	foo->type = mem_obj_string_to_enum(lastval); +	if (foo->type < 0) +	{ +		sciprintf("Unknown mem_obj_t type %s on line %d\n", lastval, *line); +		return 1; +	} + +	%CFSMLREAD int &foo->segmgr_id FROM fh ERRVAR *hiteof LINECOUNTER *line; +	switch (foo->type) +	{ +	case MEM_OBJ_SCRIPT: +		%CFSMLREAD script_t &foo->data.script FROM fh ERRVAR *hiteof LINECOUNTER *line; +	break; +	case MEM_OBJ_CLONES: +		%CFSMLREAD clone_table_t &foo->data.clones FROM fh ERRVAR *hiteof LINECOUNTER *line; +	break; +	case MEM_OBJ_LOCALS: +		%CFSMLREAD local_variables_t &foo->data.locals FROM fh ERRVAR *hiteof LINECOUNTER *line; +	break; +	case MEM_OBJ_SYS_STRINGS: +		%CFSMLREAD sys_strings_t &foo->data.sys_strings FROM fh ERRVAR *hiteof LINECOUNTER *line; +	break; +	case MEM_OBJ_LISTS: +		%CFSMLREAD list_table_t &foo->data.lists FROM fh ERRVAR *hiteof LINECOUNTER *line; +	break; +	case MEM_OBJ_NODES: +		%CFSMLREAD node_table_t &foo->data.nodes FROM fh ERRVAR *hiteof LINECOUNTER *line; +	break; +	case MEM_OBJ_STACK: +		%CFSMLREAD int &foo->data.stack.nr FROM fh ERRVAR *hiteof LINECOUNTER *line; +	foo->data.stack.entries = (reg_t *)sci_calloc(foo->data.stack.nr, sizeof(reg_t)); +	break; +	case MEM_OBJ_HUNK: +		init_hunk_table(&foo->data.hunks); +		break; +	case MEM_OBJ_DYNMEM: +		%CFSMLREAD dynmem_t  &foo->data.dynmem FROM fh ERRVAR *hiteof LINECOUNTER *line; +	break; +	} + +	return *hiteof; +} + +void +write_mem_obj_tp(FILE *fh, mem_obj_t **foo) +{ +	if (*foo) { + +		%CFSMLWRITE mem_obj_t (*foo) INTO fh; + +	} else { /* Nothing to write */ +		fputs("\\null\\", fh); +	} +} + +int +read_mem_obj_tp(FILE *fh, mem_obj_t **foo, char *lastval, int *line, int *hiteof) +{ + +	if (lastval[0] == '\\') { +		*foo = NULL; /* No menu bar */ +	} else { +		*foo = (mem_obj_t *) sci_malloc(sizeof(mem_obj_t)); +		%CFSMLREAD mem_obj_t (*foo) FROM fh ERRVAR *hiteof FIRSTTOKEN lastval LINECOUNTER *line; +		return *hiteof; +	} +	return 0; +} + + + +/* This function is called to undo some strange stuff done in preparation +** to writing a gamestate to disk +*/ +void +_gamestate_unfrob(state_t *s) +{ +} + + +int +gamestate_save(state_t *s, char *dirname) +{ +	FILE *fh; +	sci_dir_t dir; +	char *filename; +	int fd; + +	_global_save_state = s; +	s->savegame_version = FREESCI_CURRENT_SAVEGAME_VERSION; +	s->dyn_views_list_serial = (s->dyn_views)? s->dyn_views->serial : -2; +	s->drop_views_list_serial = (s->drop_views)? s->drop_views->serial : -2; +	s->port_serial = (s->port)? s->port->serial : -2; + +	if (s->execution_stack_base) { +		sciprintf("Cannot save from below kernel function\n"); +		return 1; +	} + +	scimkdir (dirname, 0700); + +	if (chdir (dirname)) { +		sciprintf("Could not enter directory '%s'\n", dirname); +		return 1; +	} + +	sci_init_dir(&dir); +	filename = sci_find_first(&dir, "*"); +	while (filename) { +		if (strcmp(filename, "..") && strcmp(filename, ".")) +			unlink(filename); /* Delete all files in directory */ +		filename = sci_find_next(&dir); +	} +	sci_finish_find(&dir); + +/* +	if (s->sound_server) { +		if ((s->sound_server->save)(s, dirname)) { +			sciprintf("Saving failed for the sound subsystem\n"); +			chdir (".."); +			return 1; +		} +	} +*/ +	fh = fopen("state", "w" FO_TEXT); + +	/* Calculate the time spent with this game */ +	s->game_time = time(NULL) - s->game_start_time.tv_sec; + +SCI_MEMTEST; +	%CFSMLWRITE state_t s INTO fh; +SCI_MEMTEST; + +	fclose(fh); + +	_gamestate_unfrob(s); + + +	chdir (".."); +	return 0; +} + +static seg_id_t +find_unique_seg_by_type(seg_manager_t *self, int type) +{ +	int i; + +	for (i = 0; i < self->heap_size; i++) +		if (self->heap[i] && +		    self->heap[i]->type == type) +			return i; +	return -1; +} + +static byte * +find_unique_script_block(state_t *s, byte *buf, int type) +{ +	int magic_pos_adder = s->version >= SCI_VERSION_FTU_NEW_SCRIPT_HEADER ? 0 : 2; + +	buf += magic_pos_adder; +	do { +		int seeker_type = getUInt16(buf); +		int seeker_size; + +		if (seeker_type == 0) break; +		if (seeker_type == type) return buf; + +		seeker_size = getUInt16(buf + 2); +		buf += seeker_size; +	} while(1); + +	return NULL; +} + +static +void reconstruct_stack(state_t *retval) +{ +	seg_id_t stack_seg = find_unique_seg_by_type(&retval->seg_manager, MEM_OBJ_STACK); +	dstack_t *stack = &(retval->seg_manager.heap[stack_seg]->data.stack); + +	retval->stack_segment = stack_seg; +	retval->stack_base = stack->entries; +	retval->stack_top = retval->stack_base + VM_STACK_SIZE; +} + +static +int clone_entry_used(clone_table_t *table, int n) +{ +	int backup; +	int seeker = table->first_free; +	clone_entry_t *entries = table->table; + +	if (seeker == HEAPENTRY_INVALID) return 1; + +	do { +		if (seeker == n) return 0; +		backup = seeker; +		seeker = entries[seeker].next_free; +	} while (entries[backup].next_free != HEAPENTRY_INVALID); + +	return 1; +} + +static +void load_script(state_t *s, seg_id_t seg) +{ +	resource_t *script, *heap; +	script_t *scr = &(s->seg_manager.heap[seg]->data.script); + +	scr->buf = (byte *) malloc(scr->buf_size); + +	script = scir_find_resource(s->resmgr, sci_script, scr->nr, 0); +	if (s->version >= SCI_VERSION(1,001,000)) +		heap = scir_find_resource(s->resmgr, sci_heap, scr->nr, 0); + +	switch (s->seg_manager.sci1_1) +	{ +	case 0 : +		sm_mcpy_in_out( &s->seg_manager, 0, script->data, script->size, seg, SEG_ID); +		break; +	case 1 : +		sm_mcpy_in_out( &s->seg_manager, 0, script->data, script->size, seg, SEG_ID); +		sm_mcpy_in_out( &s->seg_manager, scr->script_size, heap->data, heap->size, seg, SEG_ID); +		break; +	} +} + +static +void reconstruct_scripts(state_t *s, seg_manager_t *self) +{ +	int i; +	mem_obj_t *mobj; +	object_t **objects; +	int *objects_nr; +	for (i = 0; i < self->heap_size; i++) +		if (self->heap[i]) +		{ +			mobj = self->heap[i]; +			switch (mobj->type)  +			{ +			case MEM_OBJ_SCRIPT: +			{ +				int j; +				script_t *scr = &mobj->data.script; + +				load_script(s, i); +				scr->locals_block = scr->locals_segment == 0 ? NULL : +					&s->seg_manager.heap[scr->locals_segment]->data.locals; +				scr->export_table = (guint16 *) find_unique_script_block(s, scr->buf, sci_obj_exports); +				scr->synonyms = find_unique_script_block(s, scr->buf, sci_obj_synonyms); +				scr->code = NULL; +				scr->code_blocks_nr = 0; +				scr->code_blocks_allocated = 0; + +				if (!self->sci1_1) +					scr->export_table += 3; +				 +				for (j = 0; j < scr->objects_nr; j++) +				{ +					byte *data = scr->buf + scr->objects[j].pos.offset; +					scr->objects[j].base = scr->buf; +					scr->objects[j].base_obj = data; +				} + +			} +			} +		} + +	for (i = 0; i < self->heap_size; i++) +		if (self->heap[i]) +		{ +			mobj = self->heap[i]; +			switch (mobj->type)  +			{ +			case MEM_OBJ_SCRIPT: +			{ +				int j; +				script_t *scr = &mobj->data.script; + +				for (j = 0; j < scr->objects_nr; j++) +				{ +					byte *data = scr->buf + scr->objects[j].pos.offset; + +					if (self->sci1_1) +					{ +						guint16 *funct_area = (guint16 *) (scr->buf + getUInt16( data + 6 )); +						guint16 *prop_area = (guint16 *) (scr->buf + getUInt16( data + 4 )); + +						scr->objects[j].base_method = funct_area; +						scr->objects[j].base_vars = prop_area; +					} else +					{ +						int funct_area = getUInt16( data + SCRIPT_FUNCTAREAPTR_OFFSET ); +						object_t *base_obj; + +						base_obj = obj_get(s, scr->objects[j].variables[SCRIPT_SPECIES_SELECTOR]); + +						if (!base_obj) +						{ +							sciprintf("Object without a base class: Script %d, index %d (reg address "PREG"\n", +								  scr->nr, j, PRINT_REG(scr->objects[j].variables[SCRIPT_SPECIES_SELECTOR])); +							continue; +						} +						scr->objects[j].variable_names_nr = base_obj->variables_nr; +						scr->objects[j].base_obj = base_obj->base_obj; + +						scr->objects[j].base_method = (guint16 *) (data + funct_area); +						scr->objects[j].base_vars = (guint16 *) (data + scr->objects[j].variable_names_nr * 2 + SCRIPT_SELECTOR_OFFSET); +					} +				} +			} +			} +		} +} + +void +reconstruct_clones(state_t *s, seg_manager_t *self) +{ +	int i; +	mem_obj_t *mobj; + +	for (i = 0; i < self->heap_size; i++) +		if (self->heap[i]) +		{ +			mobj = self->heap[i]; +			switch (mobj->type)  +			{ +			case MEM_OBJ_CLONES: +			{ +				int j; +				clone_entry_t *seeker = mobj->data.clones.table; +				 +				sciprintf("Free list: "); +				for (j = mobj->data.clones.first_free; +				     j != HEAPENTRY_INVALID; +				     j = mobj->data.clones.table[j].next_free) +				{ +					sciprintf("%d ", j); +				} +				sciprintf("\n"); + +				sciprintf("Entries w/zero vars: "); +				for (j = 0; j < mobj->data.clones.max_entry; j++) +				{ +					if (mobj->data.clones.table[j].entry.variables == NULL) +						sciprintf("%d ", j); +				} +				sciprintf("\n"); + +				for (j = 0; j < mobj->data.clones.max_entry; j++) +				{ + 					object_t *base_obj; + +					if (!clone_entry_used(&mobj->data.clones, j)) { +						seeker++; +						continue; +					} +					base_obj = obj_get(s, seeker->entry.variables[SCRIPT_SPECIES_SELECTOR]); +					if (!base_obj) +					{ +						sciprintf("Clone entry without a base class: %d\n", j); +						seeker->entry.base = seeker->entry.base_obj = NULL; +						seeker->entry.base_vars = seeker->entry.base_method = NULL; +						continue; +					} +					seeker->entry.base = base_obj->base; +					seeker->entry.base_obj = base_obj->base_obj; +					seeker->entry.base_vars = base_obj->base_vars; +					seeker->entry.base_method = base_obj->base_method; + +					seeker++; +				} + +				break; +			} +			} +		} +} + +int +_reset_graphics_input(state_t *s); + +song_iterator_t * +new_fast_forward_iterator(song_iterator_t *it, int delta); + +static +void reconstruct_sounds(state_t *s) +{ +  song_t *seeker; +  int it_type = s->resmgr->sci_version >= SCI_VERSION_01 ? +    SCI_SONG_ITERATOR_TYPE_SCI1 +    : SCI_SONG_ITERATOR_TYPE_SCI0; + +  if (s->sound.songlib.lib) +    seeker = *(s->sound.songlib.lib); +  else +    { +      song_lib_init(&s->sound.songlib); +      seeker = NULL; +    } +  while (seeker) +    { +      song_iterator_t *base, *ff; +      int oldstatus; +      song_iterator_message_t msg; + +      base = ff = build_iterator(s, seeker->resource_num, it_type, seeker->handle); +      if (seeker->restore_behavior == RESTORE_BEHAVIOR_CONTINUE) +	  ff = (song_iterator_t *) new_fast_forward_iterator(base, seeker->restore_time); +      ff->init(ff); + +      msg = songit_make_message(seeker->handle, SIMSG_SET_LOOPS(seeker->loops)); +      songit_handle_message(&ff, msg); +      msg = songit_make_message(seeker->handle, SIMSG_SET_HOLD(seeker->hold)); +      songit_handle_message(&ff, msg); + + +      oldstatus = seeker->status; +      seeker->status = SOUND_STATUS_STOPPED; +      seeker->it = ff; +      sfx_song_set_status(&s->sound, seeker->handle, oldstatus); +      seeker = seeker->next; +    } + +} + +state_t * +gamestate_restore(state_t *s, char *dirname) +{ +	FILE *fh; +	int fd; +	int i; +	int read_eof = 0; +	state_t *retval; +	songlib_t temp; + +	if (chdir (dirname)) { +		sciprintf("Game state '%s' does not exist\n", dirname); +		return NULL; +	} + +/* +	if (s->sound_server) { +		if ((s->sound_server->restore)(s, dirname)) { +			sciprintf("Restoring failed for the sound subsystem\n"); +			return NULL; +		} +	} +*/ + +	retval = (state_t *) sci_malloc(sizeof(state_t)); + +	memset(retval, 0, sizeof(state_t)); + +	retval->savegame_version = -1; +	_global_save_state = retval; +	retval->gfx_state = s->gfx_state; + +	fh = fopen("state", "r" FO_TEXT); +	if (!fh) { +		free(retval); +		return NULL; +	} + +	 /* Backwards compatibility settings */ +	retval->dyn_views = NULL; +	retval->drop_views = NULL; +	retval->port = NULL; +	retval->save_dir_copy_buf = NULL; + +	retval->sound_mute = s->sound_mute; +	retval->sound_volume = s->sound_volume; + +	%CFSMLREAD-ATOMIC state_t retval FROM fh ERRVAR read_eof; + +	fclose(fh); + +	if ((retval->savegame_version < FREESCI_MINIMUM_SAVEGAME_VERSION) ||  +	    (retval->savegame_version > FREESCI_CURRENT_SAVEGAME_VERSION)) { + +		if (retval->savegame_version < FREESCI_MINIMUM_SAVEGAME_VERSION) +			sciprintf("Old savegame version detected- can't load\n"); +		else +			sciprintf("Savegame version is %d- maximum supported is %0d\n", retval->savegame_version, FREESCI_CURRENT_SAVEGAME_VERSION); + +		chdir(".."); +		free(retval); +		return NULL; +	} + +	sfx_exit(&s->sound); +	_gamestate_unfrob(retval); + +	/* Set exec stack base to zero */ +	retval->execution_stack_base = 0; +	retval->execution_stack_pos = 0; + +	/* Now copy all current state information */ +	/* Graphics and input state: */ +	retval->animation_delay = s->animation_delay; +	retval->animation_granularity = s->animation_granularity; +	retval->gfx_state = s->gfx_state; + +	retval->resmgr = s->resmgr; + +	temp = retval->sound.songlib; +	sfx_init(&retval->sound, retval->resmgr, s->sfx_init_flags); +	retval->sfx_init_flags = s->sfx_init_flags; +	song_lib_free(retval->sound.songlib); +	retval->sound.songlib = temp; + +	_reset_graphics_input(retval); +	reconstruct_stack(retval); +	reconstruct_scripts(retval, &retval->seg_manager); +	reconstruct_clones(retval, &retval->seg_manager); +	retval->game_obj = s->game_obj; +	retval->script_000 = &retval->seg_manager.heap[script_get_segment(s, 0, SCRIPT_GET_DONT_LOAD)]->data.script; +	retval->gc_countdown = GC_INTERVAL - 1; +	retval->save_dir_copy = make_reg(s->sys_strings_segment, SYS_STRING_SAVEDIR); +	retval->save_dir_edit_offset = 0; +	retval->sys_strings_segment = find_unique_seg_by_type(&retval->seg_manager, MEM_OBJ_SYS_STRINGS); +	retval->sys_strings = &(((mem_obj_t *)(GET_SEGMENT(retval->seg_manager, retval->sys_strings_segment, MEM_OBJ_SYS_STRINGS)))->data.sys_strings); +	sys_strings_restore(retval->sys_strings, +			    s->sys_strings); + +	/* Time state: */ +	sci_get_current_time(&(retval->last_wait_time)); +	retval->game_start_time.tv_sec = time(NULL) - retval->game_time; +	retval->game_start_time.tv_usec = 0; + +	/* File IO state: */ +	retval->file_handles_nr = 2; +	retval->file_handles = (FILE **)sci_calloc(2, sizeof(FILE *)); + +	/* static parser information: */ +	retval->parser_rules = s->parser_rules; +	retval->parser_words_nr = s->parser_words_nr; +	retval->parser_words = s->parser_words; +	retval->parser_suffices_nr = s->parser_suffices_nr; +	retval->parser_suffices = s->parser_suffices; +	retval->parser_branches_nr = s->parser_branches_nr; +	retval->parser_branches = s->parser_branches; + +	/* static VM/Kernel information: */ +	retval->selector_names_nr = s->selector_names_nr; +	retval->selector_names = s->selector_names; +	retval->kernel_names_nr = s->kernel_names_nr; +	retval->kernel_names = s->kernel_names; +	retval->kfunct_table = s->kfunct_table; +	retval->kfunct_nr = s->kfunct_nr; +	retval->opcodes = s->opcodes; + +	memcpy(&(retval->selector_map), &(s->selector_map), sizeof(selector_map_t)); + +	retval->max_version = retval->version; +	retval->min_version = retval->version; +	retval->parser_base = make_reg(s->sys_strings_segment, SYS_STRING_PARSER_BASE); + +	/* Copy breakpoint information from current game instance */ +	retval->have_bp = s->have_bp; +	retval->bp_list = s->bp_list; + +	retval->debug_mode = s->debug_mode; + +	retval->resource_dir = s->resource_dir; +	retval->work_dir = s->work_dir; +	retval->kernel_opt_flags = 0; +	retval->have_mouse_flag = s->have_mouse_flag; + +	retval->successor = NULL; +	retval->pic_priority_table = (int*)gfxop_get_pic_metainfo(retval->gfx_state); +	retval->game_name = sci_strdup(obj_get_name(retval, retval->game_obj)); + +	retval->sound.it = NULL; +	retval->sound.flags = s->sound.flags; +	retval->sound.song = NULL; +	retval->sound.suspended = s->sound.suspended; +	retval->sound.debug = s->sound.debug; +	reconstruct_sounds(retval); + +	chdir (".."); + +	return retval; +} | 
