summaryrefslogtreecommitdiffstatshomepage
path: root/src/emu/state.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/emu/state.c')
-rw-r--r--src/emu/state.c966
1 files changed, 966 insertions, 0 deletions
diff --git a/src/emu/state.c b/src/emu/state.c
new file mode 100644
index 00000000000..9b4159f82a5
--- /dev/null
+++ b/src/emu/state.c
@@ -0,0 +1,966 @@
+/***************************************************************************
+
+ state.c
+
+ Save state management functions.
+
+ Copyright (c) 1996-2007, Nicola Salmoria and the MAME Team.
+ Visit http://mamedev.org for licensing and usage restrictions.
+
+****************************************************************************
+
+ Save state file format:
+
+ 0.. 7 'MAMESAVE"
+ 8 Format version (this is format 1)
+ 9 Flags
+ a..13 Game name padded with \0
+ 14..17 Signature
+ 18..end Save game data
+
+***************************************************************************/
+
+#include "driver.h"
+#include "astring.h"
+#include <zlib.h>
+
+
+
+/***************************************************************************
+ DEBUGGING
+***************************************************************************/
+
+#define VERBOSE
+
+#ifdef VERBOSE
+#define TRACE(x) do {x;} while (0)
+#else
+#define TRACE(x)
+#endif
+
+
+
+/***************************************************************************
+ CONSTANTS
+***************************************************************************/
+
+#define SAVE_VERSION 1
+
+#define TAG_STACK_SIZE 4
+
+/* Available flags */
+enum
+{
+ SS_MSB_FIRST = 0x02
+};
+
+enum
+{
+ FUNC_NOPARAM,
+ FUNC_INTPARAM,
+ FUNC_PTRPARAM
+};
+
+
+
+/***************************************************************************
+ TYPE DEFINITIONS
+***************************************************************************/
+
+typedef struct _ss_entry ss_entry;
+struct _ss_entry
+{
+ ss_entry * next; /* pointer to next entry */
+ void * data; /* pointer to the memory to save/restore */
+ astring * name; /* full name */
+ UINT8 typesize; /* size of the raw data type */
+ UINT32 typecount; /* number of items */
+ int tag; /* saving tag */
+ UINT32 offset; /* offset within the final structure */
+};
+
+
+typedef struct _ss_func ss_func;
+struct _ss_func
+{
+ ss_func * next; /* pointer to next entry */
+ int type; /* type of callback */
+ union
+ {
+ void (*voidf)(void);
+ void (*intf)(int param);
+ void (*ptrf)(void *param);
+ } func; /* function pointers */
+ union
+ {
+ int intp;
+ void *ptrp;
+ } param; /* parameters */
+ int tag; /* saving tag */
+};
+
+
+
+/***************************************************************************
+ GLOBAL VARIABLES
+***************************************************************************/
+
+static int ss_illegal_regs;
+static ss_entry *ss_registry;
+static ss_func *ss_prefunc_reg;
+static ss_func *ss_postfunc_reg;
+
+static int ss_tag_stack[TAG_STACK_SIZE];
+static int ss_tag_stack_index;
+static int ss_current_tag;
+static UINT8 ss_registration_allowed;
+
+static UINT8 *ss_dump_array;
+static mame_file *ss_dump_file;
+static UINT32 ss_dump_size;
+
+#ifdef MESS
+static const char ss_magic_num[8] = { 'M', 'E', 'S', 'S', 'S', 'A', 'V', 'E' };
+#else
+static const char ss_magic_num[8] = { 'M', 'A', 'M', 'E', 'S', 'A', 'V', 'E' };
+#endif
+
+
+
+/***************************************************************************
+ FUNCTION PROTOTYPES
+***************************************************************************/
+
+static void ss_c2(UINT8 *, UINT32);
+static void ss_c4(UINT8 *, UINT32);
+static void ss_c8(UINT8 *, UINT32);
+
+static void (*ss_conv[])(UINT8 *, UINT32) = { 0, 0, ss_c2, 0, ss_c4, 0, 0, 0, ss_c8 };
+
+
+
+/***************************************************************************
+ INITIALIZATION
+***************************************************************************/
+
+/*-------------------------------------------------
+ state_init - initialize the system and reset
+ all registrations
+-------------------------------------------------*/
+
+void state_init(running_machine *machine)
+{
+ ss_illegal_regs = 0;
+ ss_current_tag = 0;
+ ss_tag_stack_index = 0;
+ ss_registration_allowed = FALSE;
+}
+
+
+
+/***************************************************************************
+ TAGGING
+***************************************************************************/
+
+/*-------------------------------------------------
+ state_save_push_tag - push the current tag
+ onto the stack and set a new tag
+-------------------------------------------------*/
+
+void state_save_push_tag(int tag)
+{
+ if (ss_tag_stack_index == TAG_STACK_SIZE - 1)
+ fatalerror("state_save tag stack overflow");
+ ss_tag_stack[ss_tag_stack_index++] = ss_current_tag;
+ ss_current_tag = tag;
+}
+
+
+/*-------------------------------------------------
+ state_save_pop_tag - pop the tag from the top
+ of the stack
+-------------------------------------------------*/
+
+void state_save_pop_tag(void)
+{
+ if (ss_tag_stack_index == 0)
+ fatalerror("state_save tag stack underflow");
+ ss_current_tag = ss_tag_stack[--ss_tag_stack_index];
+}
+
+
+/*-------------------------------------------------
+ state_save_get_reg_count - count the number of
+ registered entries on the current tag
+-------------------------------------------------*/
+
+int state_save_get_reg_count(void)
+{
+ ss_entry *entry;
+ int count = 0;
+
+ /* iterate over entries with matching tags */
+ for (entry = ss_registry; entry; entry = entry->next)
+ if (entry->tag == ss_current_tag)
+ count++;
+
+ return count;
+}
+
+
+
+/***************************************************************************
+ REGISTRATION HANDLING
+***************************************************************************/
+
+/*-------------------------------------------------
+ state_save_allow_registration - allow/disallow
+ registrations to happen
+-------------------------------------------------*/
+
+void state_save_allow_registration(int allowed)
+{
+ /* allow/deny registration */
+ ss_registration_allowed = allowed;
+}
+
+
+/*-------------------------------------------------
+ state_save_registration_allowed - query
+ whether or not registrations are allowed
+-------------------------------------------------*/
+
+int state_save_registration_allowed(void)
+{
+ return ss_registration_allowed;
+}
+
+
+/*-------------------------------------------------
+ state_save_register_memory - register an
+ array of data in memory
+-------------------------------------------------*/
+
+void state_save_register_memory(const char *module, UINT32 instance, const char *name, void *val, UINT32 valsize, UINT32 valcount)
+{
+ ss_entry **entry, *next;
+ astring *totalname;
+
+ assert(valsize == 1 || valsize == 2 || valsize == 4 || valsize == 8);
+
+ /* check for invalid timing */
+ if (!ss_registration_allowed)
+ {
+ logerror("Attempt to register save state entry after state registration is closed! module %s name %s\n",module,name);
+ ss_illegal_regs++;
+ return;
+ }
+
+ /* create the full name */
+ totalname = astring_alloc();
+ astring_printf(totalname, "%X/%s/%X/%s", ss_current_tag, module, instance, name);
+
+ /* look for duplicates and an entry to insert in front of */
+ for (entry = &ss_registry; *entry; entry = &(*entry)->next)
+ {
+ /* stop if the next guy's string is greater than ours */
+ int cmpval = astring_cmp((*entry)->name, totalname);
+ if (cmpval > 0)
+ break;
+
+ /* error if we are equal */
+ if ((*entry)->tag == ss_current_tag && cmpval == 0)
+ fatalerror("Duplicate save state registration entry (%d, %s)", ss_current_tag, astring_c(totalname));
+ }
+
+ /* didn't find one; allocate a new one */
+ next = *entry;
+ *entry = malloc_or_die(sizeof(**entry));
+ memset(*entry, 0, sizeof(**entry));
+
+ /* fill in the rest */
+ (*entry)->next = next;
+ (*entry)->data = val;
+ (*entry)->name = totalname;
+ (*entry)->typesize = valsize;
+ (*entry)->typecount = valcount;
+ (*entry)->tag = ss_current_tag;
+ restrack_register_object(OBJTYPE_STATEREG, *entry, 0, __FILE__, __LINE__);
+}
+
+
+/*-------------------------------------------------
+ state_save_register_bitmap - register a
+ bitmap to be saved
+-------------------------------------------------*/
+
+void state_save_register_bitmap(const char *module, UINT32 instance, const char *name, mame_bitmap *val)
+{
+ state_save_register_memory(module, instance, name, val->base, val->bpp / 8, val->rowpixels * val->height);
+}
+
+
+
+/***************************************************************************
+ CALLBACK FUNCTION REGISTRATION
+***************************************************************************/
+
+/*-------------------------------------------------
+ register_func_void - register a function
+ callback that takes no parameters
+-------------------------------------------------*/
+
+static void register_func_void(ss_func **root, void (*func)(void))
+{
+ ss_func **cur;
+
+ /* check for invalid timing */
+ if (!ss_registration_allowed)
+ {
+ logerror("Attempt to register callback function after state registration is closed!");
+ ss_illegal_regs++;
+ return;
+ }
+
+ /* scan for duplicates and push through to the end */
+ for (cur = root; *cur; cur = &(*cur)->next)
+ if ((*cur)->func.voidf == func && (*cur)->tag == ss_current_tag)
+ fatalerror("Duplicate save state function (%d, 0x%p)", ss_current_tag, func);
+
+ /* allocate a new entry */
+ *cur = malloc_or_die(sizeof(ss_func));
+
+ /* fill it in */
+ (*cur)->next = NULL;
+ (*cur)->type = FUNC_NOPARAM;
+ (*cur)->func.voidf = func;
+ (*cur)->tag = ss_current_tag;
+ restrack_register_object(OBJTYPE_STATEREG, *cur, (root == &ss_prefunc_reg) ? 1 : 2, __FILE__, __LINE__);
+}
+
+void state_save_register_func_presave(void (*func)(void))
+{
+ register_func_void(&ss_prefunc_reg, func);
+}
+
+void state_save_register_func_postload(void (*func)(void))
+{
+ register_func_void(&ss_postfunc_reg, func);
+}
+
+
+/*-------------------------------------------------
+ register_func_int - register a function
+ callback that takes an integer parameter
+-------------------------------------------------*/
+
+static void register_func_int(ss_func **root, void (*func)(int), int param)
+{
+ ss_func **cur;
+
+ /* check for invalid timing */
+ if (!ss_registration_allowed)
+ fatalerror("Attempt to register callback function after state registration is closed!");
+
+ /* scan for duplicates and push through to the end */
+ for (cur = root; *cur; cur = &(*cur)->next)
+ if ((*cur)->func.intf == func && (*cur)->param.intp == param && (*cur)->tag == ss_current_tag)
+ fatalerror("Duplicate save state function (%d, %d, %p)", ss_current_tag, param, func);
+
+ /* allocate a new entry */
+ *cur = malloc_or_die(sizeof(ss_func));
+
+ /* fill it in */
+ (*cur)->next = NULL;
+ (*cur)->type = FUNC_INTPARAM;
+ (*cur)->func.intf = func;
+ (*cur)->param.intp = param;
+ (*cur)->tag = ss_current_tag;
+ restrack_register_object(OBJTYPE_STATEREG, *cur, (root == &ss_prefunc_reg) ? 1 : 2, __FILE__, __LINE__);
+}
+
+void state_save_register_func_presave_int(void (*func)(int), int param)
+{
+ register_func_int(&ss_prefunc_reg, func, param);
+}
+
+void state_save_register_func_postload_int(void (*func)(int), int param)
+{
+ register_func_int(&ss_postfunc_reg, func, param);
+}
+
+
+/*-------------------------------------------------
+ register_func_ptr - register a function
+ callback that takes a void * parameter
+-------------------------------------------------*/
+
+static void register_func_ptr(ss_func **root, void (*func)(void *), void *param)
+{
+ ss_func **cur;
+
+ /* check for invalid timing */
+ if (!ss_registration_allowed)
+ fatalerror("Attempt to register callback function after state registration is closed!");
+
+ /* scan for duplicates and push through to the end */
+ for (cur = root; *cur; cur = &(*cur)->next)
+ if ((*cur)->func.ptrf == func && (*cur)->param.ptrp == param && (*cur)->tag == ss_current_tag)
+ fatalerror("Duplicate save state function (%d, %p, %p)", ss_current_tag, param, func);
+
+ /* allocate a new entry */
+ *cur = malloc_or_die(sizeof(ss_func));
+
+ /* fill it in */
+ (*cur)->next = NULL;
+ (*cur)->type = FUNC_PTRPARAM;
+ (*cur)->func.ptrf = func;
+ (*cur)->param.ptrp = param;
+ (*cur)->tag = ss_current_tag;
+ restrack_register_object(OBJTYPE_STATEREG, *cur, (root == &ss_prefunc_reg) ? 1 : 2, __FILE__, __LINE__);
+}
+
+void state_save_register_func_presave_ptr(void (*func)(void *), void * param)
+{
+ register_func_ptr(&ss_prefunc_reg, func, param);
+}
+
+void state_save_register_func_postload_ptr(void (*func)(void *), void * param)
+{
+ register_func_ptr(&ss_postfunc_reg, func, param);
+}
+
+
+
+/***************************************************************************
+ REGISTRATION FREEING
+***************************************************************************/
+
+/*-------------------------------------------------
+ func_free - free registered functions attached
+ to the current resource tag
+-------------------------------------------------*/
+
+static void func_free(ss_func **root, void *ptr)
+{
+ ss_func **func;
+
+ /* iterate over the function list */
+ for (func = root; *func; )
+ {
+ /* if this entry matches, free it */
+ if (*func == ptr)
+ {
+ ss_func *func_to_free = *func;
+
+ /* de-link us from the list and free our memory */
+ *func = (*func)->next;
+ free(func_to_free);
+ break;
+ }
+
+ /* otherwise, advance */
+ else
+ func = &(*func)->next;
+ }
+}
+
+
+/*-------------------------------------------------
+ state_save_free - free all registrations that
+ have been tagged with the current resource
+ tag
+-------------------------------------------------*/
+
+void state_destructor(void *ptr, size_t size)
+{
+ /* size of 0 means an entry */
+ if (size == 0)
+ {
+ ss_entry **entry;
+
+ /* iterate over entries */
+ for (entry = &ss_registry; *entry; )
+ {
+ /* if this entry matches, free it */
+ if (*entry == ptr)
+ {
+ ss_entry *entry_to_free = *entry;
+
+ /* de-link us from the list and free our memory */
+ *entry = (*entry)->next;
+ astring_free(entry_to_free->name);
+ free(entry_to_free);
+ break;
+ }
+
+ /* if not a match, move on */
+ else
+ entry = &(*entry)->next;
+ }
+ }
+
+ /* size of 1 means a pre function */
+ else if (size == 1)
+ func_free(&ss_prefunc_reg, ptr);
+
+ /* size of 2 means a post function */
+ else if (size == 2)
+ func_free(&ss_postfunc_reg, ptr);
+
+ /* if we're clear of all registrations, reset the invalid counter */
+ if (ss_registry == NULL && ss_prefunc_reg == NULL && ss_postfunc_reg == NULL)
+ ss_illegal_regs = 0;
+}
+
+
+
+/***************************************************************************
+ ENDIAN CONVERSION
+***************************************************************************/
+
+/*-------------------------------------------------
+ ss_c2 - byte swap an array of 16-bit data
+-------------------------------------------------*/
+
+static void ss_c2(UINT8 *data, UINT32 size)
+{
+ UINT16 *convert = (UINT16 *)data;
+ unsigned i;
+
+ for (i = 0; i < size; i++)
+ convert[i] = FLIPENDIAN_INT16(convert[i]);
+}
+
+
+/*-------------------------------------------------
+ ss_c4 - byte swap an array of 32-bit data
+-------------------------------------------------*/
+
+static void ss_c4(UINT8 *data, UINT32 size)
+{
+ UINT32 *convert = (UINT32 *)data;
+ unsigned i;
+
+ for (i = 0; i < size; i++)
+ convert[i] = FLIPENDIAN_INT32(convert[i]);
+}
+
+
+/*-------------------------------------------------
+ ss_c8 - byte swap an array of 64-bit data
+-------------------------------------------------*/
+
+static void ss_c8(UINT8 *data, UINT32 size)
+{
+ UINT64 *convert = (UINT64 *)data;
+ unsigned i;
+
+ for (i = 0; i < size; i++)
+ convert[i] = FLIPENDIAN_INT64(convert[i]);
+}
+
+
+
+/***************************************************************************
+ PROCESSING HELPERS
+***************************************************************************/
+
+/*-------------------------------------------------
+ compute_size_and_offsets - compute the total
+ size and offsets of each individual item
+-------------------------------------------------*/
+
+static int compute_size_and_offsets(void)
+{
+ ss_entry *entry;
+ int total_size;
+
+ /* start with the header size */
+ total_size = 0x18;
+
+ /* iterate over entries */
+ for (entry = ss_registry; entry; entry = entry->next)
+ {
+ /* note the offset and accumulate a total size */
+ entry->offset = total_size;
+ total_size += entry->typesize * entry->typecount;
+ }
+
+ /* return the total size */
+ return total_size;
+}
+
+
+/*-------------------------------------------------
+ call_hook_functions - loop through all the
+ hook functions and call them
+-------------------------------------------------*/
+
+static int call_hook_functions(ss_func *funclist)
+{
+ ss_func *func;
+ int count = 0;
+
+ /* iterate over the list of functions */
+ for (func = funclist; func; func = func->next)
+ if (func->tag == ss_current_tag)
+ {
+ count++;
+
+ /* call with the appropriate parameters */
+ switch (func->type)
+ {
+ case FUNC_NOPARAM: (func->func.voidf)(); break;
+ case FUNC_INTPARAM: (func->func.intf)(func->param.intp); break;
+ case FUNC_PTRPARAM: (func->func.ptrf)(func->param.ptrp); break;
+ }
+ }
+
+ return count;
+}
+
+
+/*-------------------------------------------------
+ get_signature - compute the signature, which
+ is a CRC over the structure of the data
+-------------------------------------------------*/
+
+static UINT32 get_signature(void)
+{
+ ss_entry *entry;
+ UINT32 crc = 0;
+
+ /* iterate over entries */
+ for (entry = ss_registry; entry; entry = entry->next)
+ {
+ UINT32 temp[2];
+
+ /* add the entry name to the CRC */
+ crc = crc32(crc, (UINT8 *)astring_c(entry->name), astring_len(entry->name));
+
+ /* add the type and size to the CRC */
+ temp[0] = LITTLE_ENDIANIZE_INT32(entry->typecount);
+ temp[1] = LITTLE_ENDIANIZE_INT32(entry->typesize);
+ crc = crc32(crc, (UINT8 *)&temp[0], 8);
+ }
+
+ return crc;
+}
+
+
+
+/***************************************************************************
+ STATE FILE VALIDATION
+***************************************************************************/
+
+/*-------------------------------------------------
+ validate_header - validate the data in the
+ header
+-------------------------------------------------*/
+
+static int validate_header(const UINT8 *header, const char *gamename, UINT32 signature,
+ void (CLIB_DECL *errormsg)(const char *fmt, ...), const char *error_prefix)
+{
+ /* check magic number */
+ if (memcmp(header, ss_magic_num, 8))
+ {
+ if (errormsg)
+ errormsg("%sThis is not a " APPNAME " save file", error_prefix);
+ return -1;
+ }
+
+ /* check save state version */
+ if (header[8] != SAVE_VERSION)
+ {
+ if (errormsg)
+ errormsg("%sWrong version in save file (%d, 1 expected)", error_prefix, header[8]);
+ return -1;
+ }
+
+ /* check gamename, if we were asked to */
+ if (gamename && strcmp(gamename, (const char *)&header[10]))
+ {
+ if (errormsg)
+ errormsg("%s'%s' is not a valid savestate file for game '%s'.", error_prefix, gamename);
+ return -1;
+ }
+
+ /* check signature, if we were asked to */
+ if (signature)
+ {
+ UINT32 rawsig = *(UINT32 *)&header[0x14];
+ UINT32 filesig = LITTLE_ENDIANIZE_INT32(rawsig);
+ if (signature != filesig)
+ {
+ if (errormsg)
+ errormsg("%sIncompatible save file (signature %08x, expected %08x)", error_prefix, filesig, signature);
+ return -1;
+ }
+ }
+ return 0;
+}
+
+
+/*-------------------------------------------------
+ state_save_check_file - check if a file is
+ a valid save state
+-------------------------------------------------*/
+
+int state_save_check_file(mame_file *file, const char *gamename, int validate_signature, void (CLIB_DECL *errormsg)(const char *fmt, ...))
+{
+ UINT32 signature = 0;
+ UINT8 header[0x18];
+
+ /* if we want to validate the signature, compute it */
+ if (validate_signature)
+ signature = get_signature();
+
+ /* seek to the beginning and read the header */
+ mame_fseek(file, 0, SEEK_SET);
+ if (mame_fread(file, header, sizeof(header)) != sizeof(header))
+ {
+ if (errormsg)
+ errormsg("Could not read " APPNAME " save file header");
+ return -1;
+ }
+
+ /* let the generic header check work out the rest */
+ return validate_header(header, gamename, signature, errormsg, "");
+}
+
+
+
+/***************************************************************************
+ SAVE STATE PROCESSING
+***************************************************************************/
+
+/*-------------------------------------------------
+ state_save_save_begin - begin the process of
+ saving
+-------------------------------------------------*/
+
+int state_save_save_begin(mame_file *file)
+{
+ /* if we have illegal registrations, return an error */
+ if (ss_illegal_regs > 0)
+ return 1;
+
+ TRACE(logerror("Beginning save\n"));
+ ss_dump_file = file;
+
+ /* compute the total dump size and the offsets of each element */
+ ss_dump_size = compute_size_and_offsets();
+ TRACE(logerror(" total size %u\n", ss_dump_size));
+
+ /* allocate memory for the array */
+ ss_dump_array = malloc_or_die(ss_dump_size);
+ return 0;
+}
+
+
+/*-------------------------------------------------
+ state_save_save_continue - save within the
+ current tag
+-------------------------------------------------*/
+
+void state_save_save_continue(void)
+{
+ ss_entry *entry;
+ int count;
+
+ TRACE(logerror("Saving tag %d\n", ss_current_tag));
+
+ /* call the pre-save functions */
+ TRACE(logerror(" calling pre-save functions\n"));
+ count = call_hook_functions(ss_prefunc_reg);
+ TRACE(logerror(" %d functions called\n", count));
+
+ /* then copy in all the data */
+ TRACE(logerror(" copying data\n"));
+
+ /* iterate over entries with matching tags */
+ for (entry = ss_registry; entry; entry = entry->next)
+ if (entry->tag == ss_current_tag)
+ {
+ memcpy(ss_dump_array + entry->offset, entry->data, entry->typesize * entry->typecount);
+ TRACE(logerror(" %s: %x..%x\n", astring_c(entry->name), entry->offset, entry->offset + entry->typesize * entry->typecount - 1));
+ }
+}
+
+
+/*-------------------------------------------------
+ state_save_save_finish - finish saving the
+ file by writing the header and closing
+-------------------------------------------------*/
+
+void state_save_save_finish(void)
+{
+ UINT32 signature;
+ UINT8 flags = 0;
+
+ TRACE(logerror("Finishing save\n"));
+
+ /* compute the flags */
+#ifndef LSB_FIRST
+ flags |= SS_MSB_FIRST;
+#endif
+
+ /* build up the header */
+ memcpy(ss_dump_array, ss_magic_num, 8);
+ ss_dump_array[8] = SAVE_VERSION;
+ ss_dump_array[9] = flags;
+ memset(ss_dump_array+0xa, 0, 10);
+ strcpy((char *)ss_dump_array+0xa, Machine->gamedrv->name);
+
+ /* copy in the signature */
+ signature = get_signature();
+ *(UINT32 *)&ss_dump_array[0x14] = LITTLE_ENDIANIZE_INT32(signature);
+
+ /* write the file */
+ mame_fwrite(ss_dump_file, ss_dump_array, ss_dump_size);
+
+ /* free memory and reset the global states */
+ free(ss_dump_array);
+ ss_dump_array = NULL;
+ ss_dump_size = 0;
+ ss_dump_file = NULL;
+}
+
+
+
+/***************************************************************************
+ LOAD STATE PROCESSING
+***************************************************************************/
+
+/*-------------------------------------------------
+ state_save_load_begin - begin the process
+ of loading the state
+-------------------------------------------------*/
+
+int state_save_load_begin(mame_file *file)
+{
+ TRACE(logerror("Beginning load\n"));
+
+ /* read the file into memory */
+ ss_dump_size = mame_fsize(file);
+ ss_dump_array = malloc_or_die(ss_dump_size);
+ ss_dump_file = file;
+ mame_fread(ss_dump_file, ss_dump_array, ss_dump_size);
+
+ /* verify the header and report an error if it doesn't match */
+ if (validate_header(ss_dump_array, NULL, get_signature(), popmessage, "Error: "))
+ {
+ free(ss_dump_array);
+ ss_dump_array = NULL;
+ ss_dump_size = 0;
+ ss_dump_file = NULL;
+ return 1;
+ }
+
+ /* compute the total size and offset of all the entries */
+ compute_size_and_offsets();
+ return 0;
+}
+
+
+/*-------------------------------------------------
+ state_save_load_continue - load all state in
+ the current tag
+-------------------------------------------------*/
+
+void state_save_load_continue(void)
+{
+ ss_entry *entry;
+ int need_convert;
+ int count;
+
+ /* first determine whether or not we need to convert the endianness of the data */
+#ifdef LSB_FIRST
+ need_convert = (ss_dump_array[9] & SS_MSB_FIRST) != 0;
+#else
+ need_convert = (ss_dump_array[9] & SS_MSB_FIRST) == 0;
+#endif
+
+ TRACE(logerror("Loading tag %d\n", ss_current_tag));
+ TRACE(logerror(" copying data\n"));
+
+ /* iterate over entries with matching tags */
+ for (entry = ss_registry; entry; entry = entry->next)
+ if (entry->tag == ss_current_tag)
+ {
+ memcpy(entry->data, ss_dump_array + entry->offset, entry->typesize * entry->typecount);
+ if (need_convert && ss_conv[entry->typesize])
+ (*ss_conv[entry->typesize])(entry->data, entry->typecount);
+ TRACE(logerror(" %s: %x..%x\n", astring_c(entry->name), entry->offset, entry->offset + entry->typesize * entry->typecount - 1));
+ }
+
+ /* call the post-load functions */
+ TRACE(logerror(" calling post-load functions\n"));
+ count = call_hook_functions(ss_postfunc_reg);
+ TRACE(logerror(" %d functions called\n", count));
+}
+
+
+/*-------------------------------------------------
+ state_save_load_finish - complete the process
+ of loading the state
+-------------------------------------------------*/
+
+void state_save_load_finish(void)
+{
+ TRACE(logerror("Finishing load\n"));
+
+ /* free memory and reset the global states */
+ free(ss_dump_array);
+ ss_dump_array = NULL;
+ ss_dump_size = 0;
+ ss_dump_file = NULL;
+}
+
+
+
+/***************************************************************************
+ DEBUGGING
+***************************************************************************/
+
+/*-------------------------------------------------
+ state_save_get_indexed_item - return an item
+ with the given index
+-------------------------------------------------*/
+
+const char *state_save_get_indexed_item(int index, void **base, UINT32 *valsize, UINT32 *valcount)
+{
+ ss_entry *ss;
+
+ for (ss = ss_registry; ss != NULL; ss = ss->next)
+ if (index-- == 0)
+ {
+ if (base != NULL)
+ *base = ss->data;
+ if (valsize != NULL)
+ *valsize = ss->typesize;
+ if (valcount != NULL)
+ *valcount = ss->typecount;
+ return astring_c(ss->name);
+ }
+
+ return NULL;
+}
+
+
+/*-------------------------------------------------
+ state_save_dump_registry - dump the registry
+ to the logfile
+-------------------------------------------------*/
+
+void state_save_dump_registry(void)
+{
+#ifdef VERBOSE
+ ss_entry *entry;
+
+ for (entry = ss_registry; entry; entry=entry->next)
+ logerror("%s: %d x %d\n", astring_c(entry->name), entry->typesize, entry->typecount);
+#endif
+}
+