/*************************************************************************** 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/include/sci_memory.h" #include "sci/include/gfx_operations.h" #include "sci/include/sfx_engine.h" #include "sci/include/engine.h" #include #include "sci/engine/heap.h" #ifdef _MSC_VER #include #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; }