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; +} |