mirror of
https://github.com/holub/mame
synced 2025-05-20 04:39:11 +03:00
917 lines
25 KiB
C
917 lines
25 KiB
C
/***************************************************************************
|
|
|
|
state.c
|
|
|
|
Save state management functions.
|
|
|
|
Copyright 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 "deprecat.h"
|
|
#include <zlib.h>
|
|
|
|
|
|
|
|
/***************************************************************************
|
|
DEBUGGING
|
|
***************************************************************************/
|
|
|
|
#define VERBOSE 0
|
|
|
|
#define LOG(x) do { if (VERBOSE) logerror x; } while (0)
|
|
|
|
|
|
|
|
/***************************************************************************
|
|
CONSTANTS
|
|
***************************************************************************/
|
|
|
|
#define SAVE_VERSION 1
|
|
|
|
#define TAG_STACK_SIZE 4
|
|
|
|
/* Available flags */
|
|
enum
|
|
{
|
|
SS_MSB_FIRST = 0x02
|
|
};
|
|
|
|
|
|
|
|
/***************************************************************************
|
|
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 */
|
|
union
|
|
{
|
|
state_presave_func presave; /* presave callback */
|
|
state_postload_func postload; /* postload callback */
|
|
} func; /* function pointers */
|
|
void * param; /* function parameter */
|
|
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 (*const 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);
|
|
if (Machine->gamedrv->flags & GAME_SUPPORTS_SAVE)
|
|
fatalerror("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, bitmap_t *val)
|
|
{
|
|
state_save_register_memory(module, instance, name, val->base, val->bpp / 8, val->rowpixels * val->height);
|
|
}
|
|
|
|
|
|
|
|
/***************************************************************************
|
|
CALLBACK FUNCTION REGISTRATION
|
|
***************************************************************************/
|
|
|
|
/*-------------------------------------------------
|
|
state_save_register_presave -
|
|
register a pre-save function callback
|
|
-------------------------------------------------*/
|
|
|
|
void state_save_register_presave(running_machine *machine, state_presave_func func, 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 = &ss_prefunc_reg; *cur; cur = &(*cur)->next)
|
|
if ((*cur)->func.presave == func && (*cur)->param == 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)->func.presave = func;
|
|
(*cur)->param = param;
|
|
(*cur)->tag = ss_current_tag;
|
|
restrack_register_object(OBJTYPE_STATEREG, *cur, 1, __FILE__, __LINE__);
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------
|
|
state_save_register_postload -
|
|
register a post-load function callback
|
|
-------------------------------------------------*/
|
|
|
|
void state_save_register_postload(running_machine *machine, state_postload_func func, 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 = &ss_postfunc_reg; *cur; cur = &(*cur)->next)
|
|
if ((*cur)->func.postload == func && (*cur)->param == 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)->func.postload = func;
|
|
(*cur)->param = param;
|
|
(*cur)->tag = ss_current_tag;
|
|
restrack_register_object(OBJTYPE_STATEREG, *cur, 2, __FILE__, __LINE__);
|
|
}
|
|
|
|
|
|
|
|
/***************************************************************************
|
|
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;
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------
|
|
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;
|
|
|
|
LOG(("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();
|
|
LOG((" 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;
|
|
ss_func *func;
|
|
int count = 0;
|
|
|
|
LOG(("Saving tag %d\n", ss_current_tag));
|
|
|
|
/* call the pre-save functions */
|
|
LOG((" calling pre-save functions\n"));
|
|
|
|
/* iterate over the list of functions */
|
|
for (func = ss_prefunc_reg; func; func = func->next)
|
|
if (func->tag == ss_current_tag)
|
|
{
|
|
count++;
|
|
(*func->func.presave)(Machine, func->param);
|
|
}
|
|
LOG((" %d functions called\n", count));
|
|
|
|
/* then copy in all the data */
|
|
LOG((" 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);
|
|
LOG((" %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;
|
|
|
|
LOG(("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)
|
|
{
|
|
LOG(("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;
|
|
ss_func *func;
|
|
int need_convert;
|
|
int count = 0;
|
|
|
|
/* 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
|
|
|
|
LOG(("Loading tag %d\n", ss_current_tag));
|
|
LOG((" 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);
|
|
LOG((" %s: %x..%x\n", astring_c(entry->name), entry->offset, entry->offset + entry->typesize * entry->typecount - 1));
|
|
}
|
|
|
|
/* call the post-load functions */
|
|
LOG((" calling post-load functions\n"));
|
|
for (func = ss_postfunc_reg; func; func = func->next)
|
|
if (func->tag == ss_current_tag)
|
|
{
|
|
count++;
|
|
(*func->func.postload)(Machine, func->param);
|
|
}
|
|
LOG((" %d functions called\n", count));
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------
|
|
state_save_load_finish - complete the process
|
|
of loading the state
|
|
-------------------------------------------------*/
|
|
|
|
void state_save_load_finish(void)
|
|
{
|
|
LOG(("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_combine_module_and_tag - creates
|
|
a name from a given module and tag that can
|
|
be used as the first agrument to the
|
|
state_save_register_item family of functions
|
|
-------------------------------------------------*/
|
|
|
|
void state_save_combine_module_and_tag(char *dest, const char *module, const char *tag)
|
|
{
|
|
astring *module_lower;
|
|
astring *tag_lower;
|
|
astring *combined;
|
|
|
|
/* validate arguments */
|
|
assert(dest != NULL);
|
|
assert(module != NULL);
|
|
assert(tag != NULL);
|
|
assert(strlen(module) > 0);
|
|
assert(strlen(tag) > 0);
|
|
|
|
/* allocate objects */
|
|
module_lower = astring_alloc();
|
|
tag_lower = astring_alloc();
|
|
combined = astring_alloc();
|
|
|
|
/* convert both arguments to lower case for case insenstive comparisson */
|
|
astring_tolower(astring_cpyc(module_lower, module));
|
|
astring_tolower(astring_cpyc(tag_lower, tag));
|
|
|
|
/* if the tag contains the module name, just use the tag as the combined name */
|
|
if (astring_find(tag_lower, 0, module_lower) >= 0)
|
|
astring_cpyc(combined, tag);
|
|
|
|
/* otherwise combine the module and the tag */
|
|
else
|
|
astring_assemble_3(combined, module, ".", tag);
|
|
|
|
/* copy the result to the destination array */
|
|
strcpy(dest, astring_c(combined));
|
|
|
|
/* free the objects */
|
|
astring_free(module_lower);
|
|
astring_free(tag_lower);
|
|
astring_free(combined);
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------
|
|
state_save_dump_registry - dump the registry
|
|
to the logfile
|
|
-------------------------------------------------*/
|
|
|
|
void state_save_dump_registry(void)
|
|
{
|
|
ss_entry *entry;
|
|
|
|
for (entry = ss_registry; entry; entry=entry->next)
|
|
LOG(("%s: %d x %d\n", astring_c(entry->name), entry->typesize, entry->typecount));
|
|
}
|
|
|