2017-02-25 00:38:14 +01:00
|
|
|
/*
|
2016-11-30 22:25:43 +01:00
|
|
|
* UAE - The Un*x Amiga Emulator
|
|
|
|
*
|
|
|
|
* Save/restore emulator state
|
|
|
|
*
|
|
|
|
* (c) 1999-2001 Toni Wilen
|
|
|
|
*
|
|
|
|
* see below for ASF-structure
|
|
|
|
*/
|
|
|
|
|
2017-02-25 00:38:14 +01:00
|
|
|
/* Features:
|
|
|
|
*
|
|
|
|
* - full CPU state (68000/68010/68020/68030/68040/68060)
|
|
|
|
* - FPU (68881/68882/68040/68060)
|
|
|
|
* - full CIA-A and CIA-B state (with all internal registers)
|
|
|
|
* - saves all custom registers and audio internal state.
|
|
|
|
* - Chip, Bogo, Fast, Z3 and Picasso96 RAM supported
|
|
|
|
* - disk drive type, imagefile, track and motor state
|
|
|
|
* - Kickstart ROM version, address and size is saved. This data is not used during restore yet.
|
|
|
|
* - Action Replay state is saved
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* Notes:
|
|
|
|
*
|
|
|
|
* - blitter state is not saved, blitter is forced to finish immediately if it
|
|
|
|
* was active
|
|
|
|
* - disk DMA state is completely saved
|
|
|
|
* - does not ask for statefile name and description. Currently uses DF0's disk
|
|
|
|
* image name (".adf" is replaced with ".asf")
|
|
|
|
* - only Amiga state is restored, harddisk support, autoconfig, expansion boards etc..
|
|
|
|
* are not saved/restored (and probably never will).
|
|
|
|
* - use this for saving games that can't be saved to disk
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* Usage :
|
|
|
|
*
|
|
|
|
* save:
|
|
|
|
*
|
|
|
|
* set savestate_state = STATE_DOSAVE, savestate_filename = "..."
|
|
|
|
*
|
|
|
|
* restore:
|
|
|
|
*
|
|
|
|
* set savestate_state = STATE_DORESTORE, savestate_filename = "..."
|
|
|
|
*
|
|
|
|
*/
|
2015-05-13 18:47:23 +00:00
|
|
|
|
|
|
|
#include "sysconfig.h"
|
|
|
|
#include "sysdeps.h"
|
|
|
|
|
|
|
|
#include "options.h"
|
|
|
|
#include "zfile.h"
|
|
|
|
#include "autoconf.h"
|
2016-04-24 09:45:29 +00:00
|
|
|
#include "custom.h"
|
2015-05-13 18:47:23 +00:00
|
|
|
#include "savestate.h"
|
2016-04-24 09:45:29 +00:00
|
|
|
#include "uae.h"
|
2015-09-09 21:49:41 +02:00
|
|
|
#include "gui.h"
|
2015-05-17 07:52:43 +00:00
|
|
|
#include "audio.h"
|
2015-09-09 21:49:41 +02:00
|
|
|
#include "filesys.h"
|
2015-05-13 18:47:23 +00:00
|
|
|
|
|
|
|
int savestate_state = 0;
|
|
|
|
|
2016-04-24 09:45:29 +00:00
|
|
|
static bool new_blitter = false;
|
|
|
|
|
2017-02-25 00:38:14 +01:00
|
|
|
struct zfile* savestate_file;
|
2015-11-16 22:32:10 +01:00
|
|
|
static int savestate_docompress, savestate_specialdump, savestate_nodialogs;
|
2015-05-13 18:47:23 +00:00
|
|
|
|
2016-04-24 09:45:29 +00:00
|
|
|
TCHAR savestate_fname[MAX_DPATH];
|
2015-05-13 18:47:23 +00:00
|
|
|
|
2017-02-25 00:38:14 +01:00
|
|
|
static void state_incompatible_warn()
|
2015-09-09 21:49:41 +02:00
|
|
|
{
|
2017-02-25 00:38:14 +01:00
|
|
|
static int warned;
|
|
|
|
int dowarn = 0;
|
|
|
|
int i;
|
2015-09-09 21:49:41 +02:00
|
|
|
|
2016-04-24 09:45:29 +00:00
|
|
|
#ifdef BSDSOCKET
|
2016-11-30 22:25:43 +01:00
|
|
|
if (currprefs.socket_emu)
|
|
|
|
dowarn = 1;
|
2015-09-09 21:49:41 +02:00
|
|
|
#endif
|
2016-08-27 20:39:53 +02:00
|
|
|
#ifdef SCSIEMU
|
2016-11-30 22:25:43 +01:00
|
|
|
if (currprefs.scsi)
|
|
|
|
dowarn = 1;
|
2016-08-27 20:39:53 +02:00
|
|
|
#endif
|
|
|
|
#ifdef CATWEASEL
|
2016-11-30 22:25:43 +01:00
|
|
|
if (currprefs.catweasel)
|
|
|
|
dowarn = 1;
|
2016-08-27 20:39:53 +02:00
|
|
|
#endif
|
2015-09-09 21:49:41 +02:00
|
|
|
#ifdef FILESYS
|
2017-02-25 00:38:14 +01:00
|
|
|
for (i = 0; i < currprefs.mountitems; i++)
|
|
|
|
{
|
|
|
|
struct mountedinfo mi;
|
|
|
|
int type = get_filesys_unitconfig(&currprefs, i, &mi);
|
|
|
|
if (mi.ismounted && type != FILESYS_VIRTUAL && type != FILESYS_HARDFILE && type != FILESYS_HARDFILE_RDB)
|
|
|
|
dowarn = 1;
|
|
|
|
}
|
2015-09-09 21:49:41 +02:00
|
|
|
#endif
|
2017-02-25 00:38:14 +01:00
|
|
|
if (!warned && dowarn)
|
|
|
|
{
|
|
|
|
warned = 1;
|
|
|
|
notify_user(NUMSG_STATEHD);
|
|
|
|
}
|
2015-09-09 21:49:41 +02:00
|
|
|
}
|
|
|
|
|
2015-05-13 18:47:23 +00:00
|
|
|
/* functions for reading/writing bytes, shorts and longs in big-endian
|
|
|
|
* format independent of host machine's endianess */
|
|
|
|
|
2017-02-25 00:38:14 +01:00
|
|
|
void save_u32_func(uae_u8** dstp, uae_u32 v)
|
2015-05-13 18:47:23 +00:00
|
|
|
{
|
2017-02-25 00:38:14 +01:00
|
|
|
uae_u8* dst = *dstp;
|
|
|
|
*dst++ = uae_u8(v >> 24);
|
|
|
|
*dst++ = uae_u8(v >> 16);
|
|
|
|
*dst++ = uae_u8(v >> 8);
|
|
|
|
*dst++ = uae_u8(v >> 0);
|
|
|
|
*dstp = dst;
|
2015-05-13 18:47:23 +00:00
|
|
|
}
|
2017-02-25 00:38:14 +01:00
|
|
|
|
|
|
|
void save_u64_func(uae_u8** dstp, uae_u64 v)
|
2015-05-13 18:47:23 +00:00
|
|
|
{
|
2017-02-25 00:38:14 +01:00
|
|
|
save_u32_func(dstp, uae_u32(v >> 32));
|
|
|
|
save_u32_func(dstp, uae_u32(v));
|
2015-05-13 18:47:23 +00:00
|
|
|
}
|
2017-02-25 00:38:14 +01:00
|
|
|
|
|
|
|
void save_u16_func(uae_u8** dstp, uae_u16 v)
|
2015-05-13 18:47:23 +00:00
|
|
|
{
|
2017-02-25 00:38:14 +01:00
|
|
|
uae_u8* dst = *dstp;
|
|
|
|
*dst++ = uae_u8(v >> 8);
|
|
|
|
*dst++ = uae_u8(v >> 0);
|
|
|
|
*dstp = dst;
|
2015-05-13 18:47:23 +00:00
|
|
|
}
|
2017-02-25 00:38:14 +01:00
|
|
|
|
|
|
|
void save_u8_func(uae_u8** dstp, uae_u8 v)
|
2015-05-13 18:47:23 +00:00
|
|
|
{
|
2017-02-25 00:38:14 +01:00
|
|
|
uae_u8* dst = *dstp;
|
|
|
|
*dst++ = v;
|
|
|
|
*dstp = dst;
|
2016-04-24 09:45:29 +00:00
|
|
|
}
|
2017-02-25 00:38:14 +01:00
|
|
|
|
|
|
|
void save_string_func(uae_u8** dstp, const TCHAR* from)
|
2016-04-24 09:45:29 +00:00
|
|
|
{
|
2017-02-25 00:38:14 +01:00
|
|
|
uae_u8* dst = *dstp;
|
2016-11-30 22:25:43 +01:00
|
|
|
char *s, *s2;
|
2017-02-25 00:38:14 +01:00
|
|
|
s2 = s = uutf8(from);
|
2016-11-30 22:25:43 +01:00
|
|
|
while (s && *s)
|
|
|
|
*dst++ = *s++;
|
2017-02-25 00:38:14 +01:00
|
|
|
*dst++ = 0;
|
|
|
|
*dstp = dst;
|
2016-11-30 22:25:43 +01:00
|
|
|
xfree (s2);
|
2015-05-13 18:47:23 +00:00
|
|
|
}
|
2017-02-25 00:38:14 +01:00
|
|
|
|
|
|
|
void save_path_func(uae_u8** dstp, const TCHAR* from, int type)
|
2015-05-13 18:47:23 +00:00
|
|
|
{
|
2017-02-25 00:38:14 +01:00
|
|
|
save_string_func(dstp, from);
|
2015-05-13 18:47:23 +00:00
|
|
|
}
|
|
|
|
|
2017-02-25 00:38:14 +01:00
|
|
|
uae_u32 restore_u32_func(uae_u8** dstp)
|
2015-05-13 18:47:23 +00:00
|
|
|
{
|
2017-02-25 00:38:14 +01:00
|
|
|
uae_u32 v;
|
|
|
|
uae_u8* dst = *dstp;
|
|
|
|
v = (dst[0] << 24) | (dst[1] << 16) | (dst[2] << 8) | (dst[3]);
|
|
|
|
*dstp = dst + 4;
|
|
|
|
return v;
|
2015-05-13 18:47:23 +00:00
|
|
|
}
|
2017-02-25 00:38:14 +01:00
|
|
|
|
|
|
|
uae_u64 restore_u64_func(uae_u8** dstp)
|
2015-05-13 18:47:23 +00:00
|
|
|
{
|
2017-02-25 00:38:14 +01:00
|
|
|
uae_u64 v;
|
|
|
|
|
|
|
|
v = restore_u32_func(dstp);
|
|
|
|
v <<= 32;
|
|
|
|
v |= restore_u32_func(dstp);
|
|
|
|
return v;
|
2015-05-13 18:47:23 +00:00
|
|
|
}
|
2017-02-25 00:38:14 +01:00
|
|
|
|
|
|
|
uae_u16 restore_u16_func(uae_u8** dstp)
|
2015-05-13 18:47:23 +00:00
|
|
|
{
|
2017-02-25 00:38:14 +01:00
|
|
|
uae_u16 v;
|
|
|
|
uae_u8* dst = *dstp;
|
|
|
|
v = (dst[0] << 8) | (dst[1]);
|
|
|
|
*dstp = dst + 2;
|
|
|
|
return v;
|
2015-05-13 18:47:23 +00:00
|
|
|
}
|
2017-02-25 00:38:14 +01:00
|
|
|
|
|
|
|
uae_u8 restore_u8_func(uae_u8** dstp)
|
2015-05-13 18:47:23 +00:00
|
|
|
{
|
2017-02-25 00:38:14 +01:00
|
|
|
uae_u8 v;
|
|
|
|
uae_u8* dst = *dstp;
|
|
|
|
v = dst[0];
|
|
|
|
*dstp = dst + 1;
|
|
|
|
return v;
|
2015-05-13 18:47:23 +00:00
|
|
|
}
|
2017-02-25 00:38:14 +01:00
|
|
|
|
|
|
|
TCHAR* restore_string_func(uae_u8** dstp)
|
2015-05-13 18:47:23 +00:00
|
|
|
{
|
2017-02-25 00:38:14 +01:00
|
|
|
int len;
|
|
|
|
uae_u8 v;
|
|
|
|
uae_u8* dst = *dstp;
|
|
|
|
char *top, *to;
|
|
|
|
TCHAR* s;
|
|
|
|
len = strlen(reinterpret_cast<char *>(dst)) + 1;
|
|
|
|
top = to = xmalloc (char, len);
|
|
|
|
do
|
|
|
|
{
|
|
|
|
v = *dst++;
|
|
|
|
*top++ = v;
|
|
|
|
}
|
|
|
|
while (v);
|
|
|
|
*dstp = dst;
|
|
|
|
s = utf8u(to);
|
2016-11-30 22:25:43 +01:00
|
|
|
xfree (to);
|
|
|
|
return s;
|
2016-04-24 09:45:29 +00:00
|
|
|
}
|
2017-02-25 00:38:14 +01:00
|
|
|
|
|
|
|
TCHAR* restore_path_func(uae_u8** dstp, int type)
|
2016-04-24 09:45:29 +00:00
|
|
|
{
|
2017-02-25 00:38:14 +01:00
|
|
|
TCHAR* newpath;
|
|
|
|
TCHAR* s;
|
|
|
|
TCHAR tmp[MAX_DPATH], tmp2[MAX_DPATH];
|
2016-11-30 22:25:43 +01:00
|
|
|
|
|
|
|
s = restore_string_func(dstp);
|
|
|
|
if (s[0] == 0)
|
|
|
|
return s;
|
2017-02-25 00:38:14 +01:00
|
|
|
if (zfile_exists(s))
|
2016-11-30 22:25:43 +01:00
|
|
|
return s;
|
|
|
|
if (type == SAVESTATE_PATH_HD)
|
|
|
|
return s;
|
2017-02-25 00:38:14 +01:00
|
|
|
getfilepart(tmp, sizeof tmp / sizeof (TCHAR), s);
|
|
|
|
if (zfile_exists(tmp))
|
|
|
|
{
|
2016-11-30 22:25:43 +01:00
|
|
|
xfree (s);
|
2017-02-25 00:38:14 +01:00
|
|
|
return my_strdup(tmp);
|
2016-11-30 22:25:43 +01:00
|
|
|
}
|
|
|
|
|
2017-02-25 00:38:14 +01:00
|
|
|
newpath = nullptr;
|
2016-11-30 22:25:43 +01:00
|
|
|
if (type == SAVESTATE_PATH_FLOPPY)
|
|
|
|
newpath = currprefs.path_floppy;
|
|
|
|
else if (type == SAVESTATE_PATH_VDIR || type == SAVESTATE_PATH_HDF)
|
|
|
|
newpath = currprefs.path_hardfile;
|
|
|
|
else if (type == SAVESTATE_PATH_CD)
|
|
|
|
newpath = currprefs.path_cd;
|
2017-02-25 00:38:14 +01:00
|
|
|
if (newpath != nullptr && newpath[0] != 0)
|
|
|
|
{
|
2016-11-30 22:25:43 +01:00
|
|
|
_tcscpy (tmp2, newpath);
|
2017-02-25 00:38:14 +01:00
|
|
|
fixtrailing(tmp2);
|
2016-11-30 22:25:43 +01:00
|
|
|
_tcscat (tmp2, tmp);
|
2017-02-25 00:38:14 +01:00
|
|
|
if (zfile_exists(tmp2))
|
|
|
|
{
|
2016-11-30 22:25:43 +01:00
|
|
|
xfree (s);
|
2017-02-25 00:38:14 +01:00
|
|
|
return my_strdup(tmp2);
|
2016-11-30 22:25:43 +01:00
|
|
|
}
|
2017-02-25 00:38:14 +01:00
|
|
|
}
|
|
|
|
getpathpart(tmp2, sizeof tmp2 / sizeof (TCHAR), savestate_fname);
|
2016-11-30 22:25:43 +01:00
|
|
|
_tcscat (tmp2, tmp);
|
2017-02-25 00:38:14 +01:00
|
|
|
if (zfile_exists(tmp2))
|
|
|
|
{
|
2016-11-30 22:25:43 +01:00
|
|
|
xfree (s);
|
2017-02-25 00:38:14 +01:00
|
|
|
return my_strdup(tmp2);
|
2016-11-30 22:25:43 +01:00
|
|
|
}
|
|
|
|
return s;
|
2015-05-13 18:47:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* read and write IFF-style hunks */
|
|
|
|
|
2017-02-25 00:38:14 +01:00
|
|
|
static void save_chunk(struct zfile* f, uae_u8* chunk, size_t len, const char* name, int compress)
|
2015-05-13 18:47:23 +00:00
|
|
|
{
|
2017-02-25 00:38:14 +01:00
|
|
|
uae_u8 tmp[8], *dst;
|
|
|
|
uae_u8 zero[4] = {0, 0, 0, 0};
|
|
|
|
uae_u32 flags;
|
|
|
|
size_t pos;
|
|
|
|
size_t chunklen, len2;
|
|
|
|
char* s;
|
|
|
|
|
|
|
|
if (!chunk)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (compress < 0)
|
|
|
|
{
|
|
|
|
zfile_fwrite(chunk, 1, len, f);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
/* chunk name */
|
|
|
|
s = ua(name);
|
|
|
|
zfile_fwrite(s, 1, 4, f);
|
2016-11-30 22:25:43 +01:00
|
|
|
xfree (s);
|
2017-02-25 00:38:14 +01:00
|
|
|
pos = zfile_ftell(f);
|
|
|
|
/* chunk size */
|
|
|
|
dst = &tmp[0];
|
|
|
|
chunklen = len + 4 + 4 + 4;
|
|
|
|
save_u32 (chunklen);
|
|
|
|
zfile_fwrite(&tmp[0], 1, 4, f);
|
|
|
|
/* chunk flags */
|
|
|
|
flags = 0;
|
|
|
|
dst = &tmp[0];
|
|
|
|
save_u32 (flags | compress);
|
|
|
|
zfile_fwrite(&tmp[0], 1, 4, f);
|
|
|
|
/* chunk data */
|
|
|
|
if (compress)
|
|
|
|
{
|
|
|
|
int tmplen = len;
|
|
|
|
size_t opos;
|
|
|
|
dst = &tmp[0];
|
|
|
|
save_u32 (len);
|
|
|
|
opos = zfile_ftell(f);
|
|
|
|
zfile_fwrite(&tmp[0], 1, 4, f);
|
|
|
|
len = zfile_zcompress(f, chunk, len);
|
|
|
|
if (len > 0)
|
|
|
|
{
|
|
|
|
zfile_fseek(f, pos, SEEK_SET);
|
|
|
|
dst = &tmp[0];
|
|
|
|
save_u32 (len + 4 + 4 + 4 + 4);
|
|
|
|
zfile_fwrite(&tmp[0], 1, 4, f);
|
|
|
|
zfile_fseek(f, 0, SEEK_END);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
len = tmplen;
|
|
|
|
compress = 0;
|
|
|
|
zfile_fseek(f, opos, SEEK_SET);
|
|
|
|
dst = &tmp[0];
|
|
|
|
save_u32 (flags);
|
|
|
|
zfile_fwrite(&tmp[0], 1, 4, f);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!compress)
|
|
|
|
zfile_fwrite(chunk, 1, len, f);
|
|
|
|
/* alignment */
|
|
|
|
len2 = 4 - (len & 3);
|
|
|
|
if (len2)
|
|
|
|
zfile_fwrite(zero, 1, len2, f);
|
|
|
|
|
|
|
|
write_log (_T("Chunk '%s' chunk size %d (%d)\n"), name, chunklen, len);
|
2015-05-13 18:47:23 +00:00
|
|
|
}
|
|
|
|
|
2017-02-25 00:38:14 +01:00
|
|
|
static uae_u8* restore_chunk(struct zfile* f, TCHAR* name, size_t* len, size_t* totallen, size_t* filepos)
|
2015-05-13 18:47:23 +00:00
|
|
|
{
|
2017-02-25 00:38:14 +01:00
|
|
|
uae_u8 tmp[6], dummy[4], *mem, *src;
|
|
|
|
uae_u32 flags;
|
|
|
|
int len2;
|
2016-11-30 22:25:43 +01:00
|
|
|
|
2017-02-25 00:38:14 +01:00
|
|
|
*totallen = 0;
|
|
|
|
/* chunk name */
|
|
|
|
zfile_fread(tmp, 1, 4, f);
|
2016-11-30 22:25:43 +01:00
|
|
|
tmp[4] = 0;
|
2017-02-25 00:38:14 +01:00
|
|
|
au_copy(name, 5, reinterpret_cast<char*>(tmp));
|
|
|
|
/* chunk size */
|
|
|
|
zfile_fread(tmp, 1, 4, f);
|
|
|
|
src = tmp;
|
|
|
|
len2 = restore_u32 () - 4 - 4 - 4;
|
|
|
|
if (len2 < 0)
|
|
|
|
len2 = 0;
|
|
|
|
*len = len2;
|
|
|
|
if (len2 == 0)
|
|
|
|
{
|
|
|
|
*filepos = zfile_ftell(f);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* chunk flags */
|
|
|
|
zfile_fread(tmp, 1, 4, f);
|
|
|
|
src = tmp;
|
|
|
|
flags = restore_u32 ();
|
|
|
|
*totallen = *len;
|
|
|
|
if (flags & 1)
|
|
|
|
{
|
|
|
|
zfile_fread(tmp, 1, 4, f);
|
|
|
|
src = tmp;
|
|
|
|
*totallen = restore_u32();
|
|
|
|
*filepos = zfile_ftell(f) - 4 - 4 - 4;
|
|
|
|
len2 -= 4;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
*filepos = zfile_ftell(f) - 4 - 4;
|
|
|
|
}
|
|
|
|
/* chunk data. RAM contents will be loaded during the reset phase,
|
|
|
|
no need to malloc multiple megabytes here. */
|
|
|
|
if (_tcscmp (name, _T("CRAM")) != 0
|
|
|
|
&& _tcscmp (name, _T("BRAM")) != 0
|
|
|
|
&& _tcscmp (name, _T("FRAM")) != 0
|
|
|
|
&& _tcscmp (name, _T("ZRAM")) != 0
|
2016-11-30 22:25:43 +01:00
|
|
|
&& _tcscmp (name, _T("ZCRM")) != 0
|
2017-02-25 00:38:14 +01:00
|
|
|
&& _tcscmp (name, _T("PRAM")) != 0
|
|
|
|
&& _tcscmp (name, _T("A3K1")) != 0
|
2016-11-30 22:25:43 +01:00
|
|
|
&& _tcscmp (name, _T("A3K2")) != 0
|
|
|
|
&& _tcscmp (name, _T("BORO")) != 0
|
|
|
|
)
|
2017-02-25 00:38:14 +01:00
|
|
|
{
|
|
|
|
/* extra bytes at the end needed to handle old statefiles that now have new fields */
|
|
|
|
mem = xcalloc (uae_u8, *totallen + 100);
|
|
|
|
if (!mem)
|
|
|
|
return nullptr;
|
|
|
|
if (flags & 1)
|
|
|
|
{
|
|
|
|
zfile_zuncompress(mem, *totallen, f, len2);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
zfile_fread(mem, 1, len2, f);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
mem = nullptr;
|
|
|
|
zfile_fseek(f, len2, SEEK_CUR);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* alignment */
|
|
|
|
len2 = 4 - (len2 & 3);
|
|
|
|
if (len2)
|
|
|
|
zfile_fread(dummy, 1, len2, f);
|
|
|
|
return mem;
|
2015-05-13 18:47:23 +00:00
|
|
|
}
|
|
|
|
|
2017-02-25 00:38:14 +01:00
|
|
|
void restore_ram(size_t filepos, uae_u8* memory)
|
2015-05-13 18:47:23 +00:00
|
|
|
{
|
2017-02-25 00:38:14 +01:00
|
|
|
uae_u8 tmp[8];
|
|
|
|
uae_u8* src = tmp;
|
|
|
|
int size, fullsize;
|
|
|
|
uae_u32 flags;
|
|
|
|
|
|
|
|
if (filepos == 0 || memory == nullptr)
|
|
|
|
return;
|
|
|
|
zfile_fseek(savestate_file, filepos, SEEK_SET);
|
|
|
|
zfile_fread(tmp, 1, sizeof tmp, savestate_file);
|
|
|
|
size = restore_u32();
|
|
|
|
flags = restore_u32();
|
|
|
|
size -= 4 + 4 + 4;
|
|
|
|
if (flags & 1)
|
|
|
|
{
|
|
|
|
zfile_fread(tmp, 1, 4, savestate_file);
|
|
|
|
src = tmp;
|
|
|
|
fullsize = restore_u32();
|
|
|
|
size -= 4;
|
|
|
|
zfile_zuncompress(memory, fullsize, savestate_file, size);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
zfile_fread(memory, 1, size, savestate_file);
|
|
|
|
}
|
2015-09-09 21:49:41 +02:00
|
|
|
}
|
|
|
|
|
2017-02-25 00:38:14 +01:00
|
|
|
static void restore_header(uae_u8* src)
|
2015-05-13 18:47:23 +00:00
|
|
|
{
|
2017-02-25 00:38:14 +01:00
|
|
|
TCHAR *emuname, *emuversion, *description;
|
2016-11-30 22:25:43 +01:00
|
|
|
|
2017-02-25 00:38:14 +01:00
|
|
|
restore_u32();
|
|
|
|
emuname = restore_string ();
|
|
|
|
emuversion = restore_string ();
|
|
|
|
description = restore_string ();
|
2016-11-30 22:25:43 +01:00
|
|
|
write_log (_T("Saved with: '%s %s', description: '%s'\n"),
|
2017-02-25 00:38:14 +01:00
|
|
|
emuname,emuversion,description);
|
|
|
|
xfree (description);
|
|
|
|
xfree (emuversion);
|
|
|
|
xfree (emuname);
|
2015-05-13 18:47:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* restore all subsystems */
|
|
|
|
|
2017-02-25 00:38:14 +01:00
|
|
|
void restore_state(const TCHAR* filename)
|
2015-05-13 18:47:23 +00:00
|
|
|
{
|
2017-02-25 00:38:14 +01:00
|
|
|
struct zfile* f;
|
|
|
|
uae_u8 *chunk, *end;
|
|
|
|
TCHAR name[5];
|
|
|
|
size_t len, totallen;
|
|
|
|
size_t filepos, filesize;
|
|
|
|
int z3num;
|
|
|
|
|
|
|
|
chunk = 0;
|
|
|
|
f = zfile_fopen(filename, _T("rb"), ZFD_NORMAL);
|
|
|
|
if (!f)
|
|
|
|
goto error;
|
|
|
|
zfile_fseek(f, 0, SEEK_END);
|
|
|
|
filesize = zfile_ftell(f);
|
|
|
|
zfile_fseek(f, 0, SEEK_SET);
|
|
|
|
savestate_state = STATE_RESTORE;
|
|
|
|
|
|
|
|
chunk = restore_chunk(f, name, &len, &totallen, &filepos);
|
|
|
|
if (!chunk || _tcsncmp (name, _T("ASF "), 4))
|
|
|
|
{
|
2016-11-30 22:25:43 +01:00
|
|
|
write_log (_T("%s is not an AmigaStateFile\n"), filename);
|
2017-02-25 00:38:14 +01:00
|
|
|
goto error;
|
|
|
|
}
|
2016-11-30 22:25:43 +01:00
|
|
|
write_log (_T("STATERESTORE: '%s'\n"), filename);
|
2017-02-25 00:38:14 +01:00
|
|
|
savestate_file = f;
|
|
|
|
restore_header(chunk);
|
|
|
|
xfree (chunk);
|
|
|
|
restore_cia_start();
|
|
|
|
changed_prefs.bogomem_size = 0;
|
|
|
|
changed_prefs.chipmem_size = 0;
|
|
|
|
changed_prefs.fastmem_size = 0;
|
|
|
|
changed_prefs.z3fastmem_size = 0;
|
|
|
|
z3num = 0;
|
|
|
|
for (;;)
|
|
|
|
{
|
|
|
|
name[0] = 0;
|
|
|
|
chunk = end = restore_chunk(f, name, &len, &totallen, &filepos);
|
|
|
|
write_log (_T("Chunk '%s' size %d (%d)\n"), name, len, totallen);
|
|
|
|
if (!_tcscmp (name, _T("END ")))
|
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (!_tcscmp (name, _T("CRAM")))
|
|
|
|
{
|
|
|
|
restore_cram(totallen, filepos);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
else if (!_tcscmp (name, _T("BRAM")))
|
|
|
|
{
|
|
|
|
restore_bram(totallen, filepos);
|
|
|
|
continue;
|
2015-05-13 18:47:23 +00:00
|
|
|
#ifdef AUTOCONFIG
|
2017-02-25 00:38:14 +01:00
|
|
|
}
|
|
|
|
else if (!_tcscmp (name, _T("FRAM")))
|
|
|
|
{
|
|
|
|
restore_fram(totallen, filepos);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
else if (!_tcscmp (name, _T("ZRAM")))
|
|
|
|
{
|
|
|
|
restore_zram(totallen, filepos, z3num++);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
else if (!_tcscmp (name, _T("BORO")))
|
|
|
|
{
|
|
|
|
restore_bootrom(totallen, filepos);
|
|
|
|
continue;
|
2015-05-13 18:47:23 +00:00
|
|
|
#endif
|
|
|
|
#ifdef PICASSO96
|
2017-02-25 00:38:14 +01:00
|
|
|
}
|
|
|
|
else if (!_tcscmp (name, _T("PRAM")))
|
|
|
|
{
|
|
|
|
restore_pram(totallen, filepos);
|
|
|
|
continue;
|
2015-05-13 18:47:23 +00:00
|
|
|
#endif
|
2017-02-25 00:38:14 +01:00
|
|
|
}
|
|
|
|
else if (!_tcscmp (name, _T("CPU ")))
|
|
|
|
{
|
|
|
|
end = restore_cpu(chunk);
|
|
|
|
}
|
|
|
|
else if (!_tcscmp (name, _T("CPUX")))
|
|
|
|
end = restore_cpu_extra(chunk);
|
2015-05-13 18:47:23 +00:00
|
|
|
#ifdef FPUEMU
|
2017-02-25 00:38:14 +01:00
|
|
|
else if (!_tcscmp (name, _T("FPU ")))
|
|
|
|
end = restore_fpu(chunk);
|
2015-05-13 18:47:23 +00:00
|
|
|
#endif
|
2017-02-25 00:38:14 +01:00
|
|
|
else if (!_tcscmp (name, _T("AGAC")))
|
|
|
|
end = restore_custom_agacolors(chunk);
|
2016-11-30 22:25:43 +01:00
|
|
|
else if (!_tcscmp (name, _T("SPR0")))
|
2017-02-25 00:38:14 +01:00
|
|
|
end = restore_custom_sprite(0, chunk);
|
2016-11-30 22:25:43 +01:00
|
|
|
else if (!_tcscmp (name, _T("SPR1")))
|
2017-02-25 00:38:14 +01:00
|
|
|
end = restore_custom_sprite(1, chunk);
|
2016-11-30 22:25:43 +01:00
|
|
|
else if (!_tcscmp (name, _T("SPR2")))
|
2017-02-25 00:38:14 +01:00
|
|
|
end = restore_custom_sprite(2, chunk);
|
2016-11-30 22:25:43 +01:00
|
|
|
else if (!_tcscmp (name, _T("SPR3")))
|
2017-02-25 00:38:14 +01:00
|
|
|
end = restore_custom_sprite(3, chunk);
|
2016-11-30 22:25:43 +01:00
|
|
|
else if (!_tcscmp (name, _T("SPR4")))
|
2017-02-25 00:38:14 +01:00
|
|
|
end = restore_custom_sprite(4, chunk);
|
2016-11-30 22:25:43 +01:00
|
|
|
else if (!_tcscmp (name, _T("SPR5")))
|
2017-02-25 00:38:14 +01:00
|
|
|
end = restore_custom_sprite(5, chunk);
|
2016-11-30 22:25:43 +01:00
|
|
|
else if (!_tcscmp (name, _T("SPR6")))
|
2017-02-25 00:38:14 +01:00
|
|
|
end = restore_custom_sprite(6, chunk);
|
2016-11-30 22:25:43 +01:00
|
|
|
else if (!_tcscmp (name, _T("SPR7")))
|
2017-02-25 00:38:14 +01:00
|
|
|
end = restore_custom_sprite(7, chunk);
|
2016-11-30 22:25:43 +01:00
|
|
|
else if (!_tcscmp (name, _T("CIAA")))
|
2017-02-25 00:38:14 +01:00
|
|
|
end = restore_cia(0, chunk);
|
2016-11-30 22:25:43 +01:00
|
|
|
else if (!_tcscmp (name, _T("CIAB")))
|
2017-02-25 00:38:14 +01:00
|
|
|
end = restore_cia(1, chunk);
|
2016-11-30 22:25:43 +01:00
|
|
|
else if (!_tcscmp (name, _T("CHIP")))
|
2017-02-25 00:38:14 +01:00
|
|
|
end = restore_custom(chunk);
|
2016-11-30 22:25:43 +01:00
|
|
|
else if (!_tcscmp (name, _T("CINP")))
|
2017-02-25 00:38:14 +01:00
|
|
|
end = restore_input(chunk);
|
2016-11-30 22:25:43 +01:00
|
|
|
else if (!_tcscmp (name, _T("CHPX")))
|
2017-02-25 00:38:14 +01:00
|
|
|
end = restore_custom_extra(chunk);
|
|
|
|
else if (!_tcscmp (name, _T("AUD0")))
|
|
|
|
end = restore_audio(0, chunk);
|
2016-11-30 22:25:43 +01:00
|
|
|
else if (!_tcscmp (name, _T("AUD1")))
|
2017-02-25 00:38:14 +01:00
|
|
|
end = restore_audio(1, chunk);
|
2016-11-30 22:25:43 +01:00
|
|
|
else if (!_tcscmp (name, _T("AUD2")))
|
2017-02-25 00:38:14 +01:00
|
|
|
end = restore_audio(2, chunk);
|
2016-11-30 22:25:43 +01:00
|
|
|
else if (!_tcscmp (name, _T("AUD3")))
|
2017-02-25 00:38:14 +01:00
|
|
|
end = restore_audio(3, chunk);
|
2016-11-30 22:25:43 +01:00
|
|
|
else if (!_tcscmp (name, _T("BLIT")))
|
2017-02-25 00:38:14 +01:00
|
|
|
end = restore_blitter(chunk);
|
2016-11-30 22:25:43 +01:00
|
|
|
else if (!_tcscmp (name, _T("BLTX")))
|
2017-02-25 00:38:14 +01:00
|
|
|
end = restore_blitter_new(chunk);
|
2016-11-30 22:25:43 +01:00
|
|
|
else if (!_tcscmp (name, _T("DISK")))
|
2017-02-25 00:38:14 +01:00
|
|
|
end = restore_floppy(chunk);
|
2016-11-30 22:25:43 +01:00
|
|
|
else if (!_tcscmp (name, _T("DSK0")))
|
2017-02-25 00:38:14 +01:00
|
|
|
end = restore_disk(0, chunk);
|
2016-11-30 22:25:43 +01:00
|
|
|
else if (!_tcscmp (name, _T("DSK1")))
|
2017-02-25 00:38:14 +01:00
|
|
|
end = restore_disk(1, chunk);
|
2016-11-30 22:25:43 +01:00
|
|
|
else if (!_tcscmp (name, _T("DSK2")))
|
2017-02-25 00:38:14 +01:00
|
|
|
end = restore_disk(2, chunk);
|
2016-11-30 22:25:43 +01:00
|
|
|
else if (!_tcscmp (name, _T("DSK3")))
|
2017-02-25 00:38:14 +01:00
|
|
|
end = restore_disk(3, chunk);
|
2016-11-30 22:25:43 +01:00
|
|
|
else if (!_tcscmp (name, _T("DSD0")))
|
2017-02-25 00:38:14 +01:00
|
|
|
end = restore_disk2(0, chunk);
|
2016-11-30 22:25:43 +01:00
|
|
|
else if (!_tcscmp (name, _T("DSD1")))
|
2017-02-25 00:38:14 +01:00
|
|
|
end = restore_disk2(1, chunk);
|
2016-11-30 22:25:43 +01:00
|
|
|
else if (!_tcscmp (name, _T("DSD2")))
|
2017-02-25 00:38:14 +01:00
|
|
|
end = restore_disk2(2, chunk);
|
2016-11-30 22:25:43 +01:00
|
|
|
else if (!_tcscmp (name, _T("DSD3")))
|
2017-02-25 00:38:14 +01:00
|
|
|
end = restore_disk2(3, chunk);
|
2016-11-30 22:25:43 +01:00
|
|
|
else if (!_tcscmp (name, _T("KEYB")))
|
2017-02-25 00:38:14 +01:00
|
|
|
end = restore_keyboard(chunk);
|
2015-05-13 18:47:23 +00:00
|
|
|
#ifdef AUTOCONFIG
|
2016-11-30 22:25:43 +01:00
|
|
|
else if (!_tcscmp (name, _T("EXPA")))
|
2017-02-25 00:38:14 +01:00
|
|
|
end = restore_expansion(chunk);
|
2015-05-13 18:47:23 +00:00
|
|
|
#endif
|
2016-11-30 22:25:43 +01:00
|
|
|
else if (!_tcscmp (name, _T("ROM ")))
|
2017-02-25 00:38:14 +01:00
|
|
|
end = restore_rom(chunk);
|
2015-05-17 07:52:43 +00:00
|
|
|
#ifdef PICASSO96
|
2016-11-30 22:25:43 +01:00
|
|
|
else if (!_tcscmp (name, _T("P96 ")))
|
2017-02-25 00:38:14 +01:00
|
|
|
end = restore_p96(chunk);
|
2015-05-17 07:52:43 +00:00
|
|
|
#endif
|
2015-05-13 18:47:23 +00:00
|
|
|
#ifdef FILESYS
|
2016-11-30 22:25:43 +01:00
|
|
|
else if (!_tcscmp (name, _T("FSYS")))
|
2017-02-25 00:38:14 +01:00
|
|
|
end = restore_filesys(chunk);
|
2016-11-30 22:25:43 +01:00
|
|
|
else if (!_tcscmp (name, _T("FSYC")))
|
2017-02-25 00:38:14 +01:00
|
|
|
end = restore_filesys_common(chunk);
|
2015-09-09 21:49:41 +02:00
|
|
|
#endif
|
2016-07-10 13:48:11 +02:00
|
|
|
#ifdef CD32
|
2016-11-30 22:25:43 +01:00
|
|
|
else if (!_tcscmp (name, _T("CD32")))
|
2017-02-25 00:38:14 +01:00
|
|
|
end = restore_akiko(chunk);
|
2016-07-10 13:48:11 +02:00
|
|
|
#endif
|
2016-11-30 22:25:43 +01:00
|
|
|
|
|
|
|
else if (!_tcsncmp (name, _T("CDU"), 3))
|
2017-02-25 00:38:14 +01:00
|
|
|
end = restore_cd(name[3] - '0', chunk);
|
|
|
|
else
|
|
|
|
{
|
|
|
|
end = chunk + len;
|
2016-11-30 22:25:43 +01:00
|
|
|
write_log (_T("unknown chunk '%s' size %d bytes\n"), name, len);
|
2017-02-25 00:38:14 +01:00
|
|
|
}
|
|
|
|
if (end == nullptr)
|
|
|
|
write_log (_T("Chunk '%s', size %d bytes was not accepted!\n"),
|
|
|
|
name, len);
|
|
|
|
else if (totallen != end - chunk)
|
|
|
|
write_log (_T("Chunk '%s' total size %d bytes but read %d bytes!\n"),
|
|
|
|
name, totallen, end - chunk);
|
|
|
|
xfree (chunk);
|
|
|
|
}
|
|
|
|
return;
|
2016-04-24 09:45:29 +00:00
|
|
|
|
|
|
|
error:
|
2017-02-25 00:38:14 +01:00
|
|
|
savestate_state = 0;
|
|
|
|
savestate_file = nullptr;
|
|
|
|
if (chunk)
|
|
|
|
xfree (chunk);
|
|
|
|
if (f)
|
|
|
|
zfile_fclose(f);
|
2015-05-13 18:47:23 +00:00
|
|
|
}
|
|
|
|
|
2017-02-25 00:38:14 +01:00
|
|
|
void savestate_restore_finish()
|
2015-05-13 18:47:23 +00:00
|
|
|
{
|
2017-02-25 00:38:14 +01:00
|
|
|
if (!isrestore())
|
|
|
|
return;
|
|
|
|
zfile_fclose(savestate_file);
|
|
|
|
savestate_file = 0;
|
|
|
|
restore_cpu_finish();
|
|
|
|
restore_audio_finish();
|
|
|
|
restore_disk_finish();
|
|
|
|
restore_akiko_finish();
|
2016-04-24 09:45:29 +00:00
|
|
|
#ifdef PICASSO96
|
2017-02-25 00:38:14 +01:00
|
|
|
restore_p96_finish();
|
2016-04-24 09:45:29 +00:00
|
|
|
#endif
|
2017-02-25 00:38:14 +01:00
|
|
|
restore_cia_finish();
|
2016-11-30 22:25:43 +01:00
|
|
|
savestate_state = 0;
|
2017-02-25 00:38:14 +01:00
|
|
|
init_hz_full();
|
|
|
|
audio_activate();
|
2015-05-13 18:47:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* 1=compressed,2=not compressed,3=ram dump,4=audio dump */
|
2017-02-25 00:38:14 +01:00
|
|
|
void savestate_initsave(const TCHAR* filename, int mode, int nodialogs, bool save)
|
2015-05-13 18:47:23 +00:00
|
|
|
{
|
2017-02-25 00:38:14 +01:00
|
|
|
if (filename == nullptr)
|
|
|
|
{
|
|
|
|
savestate_fname[0] = 0;
|
|
|
|
savestate_docompress = 0;
|
|
|
|
savestate_specialdump = 0;
|
|
|
|
savestate_nodialogs = 0;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
_tcscpy (savestate_fname, filename);
|
|
|
|
savestate_docompress = (mode == 1) ? 1 : 0;
|
|
|
|
savestate_specialdump = (mode == 3) ? 1 : (mode == 4) ? 2 : 0;
|
|
|
|
savestate_nodialogs = nodialogs;
|
2016-11-30 22:25:43 +01:00
|
|
|
new_blitter = false;
|
2015-05-13 18:47:23 +00:00
|
|
|
}
|
|
|
|
|
2017-02-25 00:38:14 +01:00
|
|
|
static void save_rams(struct zfile* f, int comp)
|
2015-05-13 18:47:23 +00:00
|
|
|
{
|
2017-02-25 00:38:14 +01:00
|
|
|
uae_u8* dst;
|
|
|
|
int len;
|
2015-05-13 18:47:23 +00:00
|
|
|
|
2017-02-25 00:38:14 +01:00
|
|
|
dst = save_cram(&len);
|
|
|
|
save_chunk(f, dst, len, _T("CRAM"), comp);
|
|
|
|
dst = save_bram(&len);
|
|
|
|
save_chunk(f, dst, len, _T("BRAM"), comp);
|
2015-05-13 18:47:23 +00:00
|
|
|
#ifdef AUTOCONFIG
|
2017-02-25 00:38:14 +01:00
|
|
|
dst = save_fram(&len);
|
|
|
|
save_chunk(f, dst, len, _T("FRAM"), comp);
|
|
|
|
dst = save_zram(&len, 0);
|
|
|
|
save_chunk(f, dst, len, _T("ZRAM"), comp);
|
|
|
|
dst = save_bootrom(&len);
|
|
|
|
save_chunk(f, dst, len, _T("BORO"), comp);
|
2015-05-13 18:47:23 +00:00
|
|
|
#endif
|
|
|
|
#ifdef PICASSO96
|
2017-02-25 00:38:14 +01:00
|
|
|
dst = save_pram(&len);
|
|
|
|
save_chunk(f, dst, len, _T("PRAM"), comp);
|
2015-05-13 18:47:23 +00:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Save all subsystems */
|
|
|
|
|
2017-02-25 00:38:14 +01:00
|
|
|
static int save_state_internal(struct zfile* f, const TCHAR* description, int comp, bool savepath)
|
2015-05-13 18:47:23 +00:00
|
|
|
{
|
2017-02-25 00:38:14 +01:00
|
|
|
uae_u8 endhunk[] = {'E', 'N', 'D', ' ', 0, 0, 0, 8};
|
|
|
|
uae_u8 header[1000];
|
|
|
|
TCHAR tmp[100];
|
|
|
|
uae_u8* dst;
|
|
|
|
TCHAR name[5];
|
2016-11-30 22:25:43 +01:00
|
|
|
int i, len;
|
|
|
|
|
|
|
|
write_log (_T("STATESAVE (%s):\n"), f ? zfile_getname (f) : _T("<internal>"));
|
2017-02-25 00:38:14 +01:00
|
|
|
dst = header;
|
|
|
|
save_u32 (0);
|
|
|
|
save_string(_T("UAE"));
|
|
|
|
_stprintf(tmp, _T("%d.%d.%d"), UAEMAJOR, UAEMINOR, UAESUBREV);
|
|
|
|
save_string (tmp);
|
|
|
|
save_string (description);
|
|
|
|
save_chunk(f, header, dst - header, _T("ASF "), 0);
|
|
|
|
|
|
|
|
dst = save_cpu(&len, 0);
|
|
|
|
save_chunk(f, dst, len, _T("CPU "), 0);
|
|
|
|
xfree (dst);
|
|
|
|
|
|
|
|
dst = save_cpu_extra(&len, 0);
|
|
|
|
save_chunk(f, dst, len, _T("CPUX"), 0);
|
|
|
|
xfree (dst);
|
2015-05-13 18:47:23 +00:00
|
|
|
|
|
|
|
#ifdef FPUEMU
|
2017-02-25 00:38:14 +01:00
|
|
|
dst = save_fpu(&len, 0);
|
|
|
|
save_chunk(f, dst, len, _T("FPU "), 0);
|
|
|
|
xfree (dst);
|
2015-05-13 18:47:23 +00:00
|
|
|
#endif
|
|
|
|
|
2017-02-25 00:38:14 +01:00
|
|
|
_tcscpy(name, _T("DSKx"));
|
|
|
|
for (i = 0; i < 4; i++)
|
|
|
|
{
|
|
|
|
dst = save_disk(i, &len, 0, savepath);
|
|
|
|
if (dst)
|
|
|
|
{
|
|
|
|
name[3] = i + '0';
|
|
|
|
save_chunk(f, dst, len, name, 0);
|
|
|
|
xfree (dst);
|
|
|
|
}
|
|
|
|
}
|
2016-11-30 22:25:43 +01:00
|
|
|
_tcscpy(name, _T("DSDx"));
|
2017-02-25 00:38:14 +01:00
|
|
|
for (i = 0; i < 4; i++)
|
|
|
|
{
|
|
|
|
dst = save_disk2(i, &len, 0);
|
|
|
|
if (dst)
|
|
|
|
{
|
2016-11-30 22:25:43 +01:00
|
|
|
name[3] = i + '0';
|
2017-02-25 00:38:14 +01:00
|
|
|
save_chunk(f, dst, len, name, comp);
|
2016-11-30 22:25:43 +01:00
|
|
|
xfree (dst);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-02-25 00:38:14 +01:00
|
|
|
dst = save_floppy(&len, 0);
|
|
|
|
save_chunk(f, dst, len, _T("DISK"), 0);
|
|
|
|
xfree (dst);
|
2016-11-30 22:25:43 +01:00
|
|
|
|
2017-02-25 00:38:14 +01:00
|
|
|
dst = save_custom(&len, 0, 0);
|
|
|
|
save_chunk(f, dst, len, _T("CHIP"), 0);
|
|
|
|
xfree (dst);
|
2016-11-30 22:25:43 +01:00
|
|
|
|
2017-02-25 00:38:14 +01:00
|
|
|
dst = save_custom_extra(&len, 0);
|
|
|
|
save_chunk(f, dst, len, _T("CHPX"), 0);
|
2016-11-30 22:25:43 +01:00
|
|
|
xfree (dst);
|
|
|
|
|
2017-02-25 00:38:14 +01:00
|
|
|
dst = save_blitter_new(&len, 0);
|
|
|
|
save_chunk(f, dst, len, _T("BLTX"), 0);
|
2016-11-30 22:25:43 +01:00
|
|
|
xfree (dst);
|
2017-02-25 00:38:14 +01:00
|
|
|
if (new_blitter == false)
|
|
|
|
{
|
|
|
|
dst = save_blitter(&len, 0);
|
|
|
|
save_chunk(f, dst, len, _T("BLIT"), 0);
|
|
|
|
xfree (dst);
|
|
|
|
}
|
2016-11-30 22:25:43 +01:00
|
|
|
|
2017-02-25 00:38:14 +01:00
|
|
|
dst = save_input(&len, 0);
|
|
|
|
save_chunk(f, dst, len, _T("CINP"), 0);
|
|
|
|
xfree (dst);
|
|
|
|
|
|
|
|
dst = save_custom_agacolors(&len, 0);
|
|
|
|
save_chunk(f, dst, len, _T("AGAC"), 0);
|
|
|
|
xfree (dst);
|
2016-11-30 22:25:43 +01:00
|
|
|
|
|
|
|
_tcscpy (name, _T("SPRx"));
|
2017-02-25 00:38:14 +01:00
|
|
|
for (i = 0; i < 8; i++)
|
|
|
|
{
|
|
|
|
dst = save_custom_sprite(i, &len, 0);
|
|
|
|
name[3] = i + '0';
|
|
|
|
save_chunk(f, dst, len, name, 0);
|
|
|
|
xfree (dst);
|
|
|
|
}
|
2016-11-30 22:25:43 +01:00
|
|
|
|
|
|
|
_tcscpy (name, _T("AUDx"));
|
2017-02-25 00:38:14 +01:00
|
|
|
for (i = 0; i < 4; i++)
|
|
|
|
{
|
|
|
|
dst = save_audio(i, &len, 0);
|
|
|
|
name[3] = i + '0';
|
|
|
|
save_chunk(f, dst, len, name, 0);
|
|
|
|
xfree (dst);
|
|
|
|
}
|
2016-11-30 22:25:43 +01:00
|
|
|
|
2017-02-25 00:38:14 +01:00
|
|
|
dst = save_cia(0, &len, 0);
|
|
|
|
save_chunk(f, dst, len, _T("CIAA"), 0);
|
|
|
|
xfree (dst);
|
2016-11-30 22:25:43 +01:00
|
|
|
|
2017-02-25 00:38:14 +01:00
|
|
|
dst = save_cia(1, &len, 0);
|
|
|
|
save_chunk(f, dst, len, _T("CIAB"), 0);
|
|
|
|
xfree (dst);
|
2016-11-30 22:25:43 +01:00
|
|
|
|
2017-02-25 00:38:14 +01:00
|
|
|
dst = save_keyboard(&len, nullptr);
|
|
|
|
save_chunk(f, dst, len, _T("KEYB"), 0);
|
|
|
|
xfree (dst);
|
2015-05-13 18:47:23 +00:00
|
|
|
|
|
|
|
#ifdef AUTOCONFIG
|
2017-02-25 00:38:14 +01:00
|
|
|
dst = save_expansion(&len, 0);
|
|
|
|
save_chunk(f, dst, len, _T("EXPA"), 0);
|
|
|
|
xfree (dst);
|
2015-05-13 18:47:23 +00:00
|
|
|
#endif
|
2016-04-24 09:45:29 +00:00
|
|
|
#ifdef PICASSO96
|
2017-02-25 00:38:14 +01:00
|
|
|
dst = save_p96(&len, 0);
|
|
|
|
save_chunk(f, dst, len, _T("P96 "), 0);
|
2016-04-24 09:45:29 +00:00
|
|
|
#endif
|
2017-02-25 00:38:14 +01:00
|
|
|
save_rams(f, comp);
|
|
|
|
|
|
|
|
dst = save_rom(1, &len, 0);
|
|
|
|
do
|
|
|
|
{
|
|
|
|
if (!dst)
|
|
|
|
break;
|
|
|
|
save_chunk(f, dst, len, _T("ROM "), 0);
|
|
|
|
xfree (dst);
|
|
|
|
}
|
|
|
|
while ((dst = save_rom(0, &len, 0)));
|
2015-05-13 18:47:23 +00:00
|
|
|
|
2016-07-10 13:48:11 +02:00
|
|
|
#ifdef CD32
|
2017-02-25 00:38:14 +01:00
|
|
|
dst = save_akiko(&len, nullptr);
|
|
|
|
save_chunk(f, dst, len, _T("CD32"), 0);
|
2016-11-30 22:25:43 +01:00
|
|
|
xfree (dst);
|
2016-07-10 13:48:11 +02:00
|
|
|
#endif
|
2016-08-27 20:39:53 +02:00
|
|
|
|
2015-05-13 18:47:23 +00:00
|
|
|
#ifdef FILESYS
|
2017-02-25 00:38:14 +01:00
|
|
|
dst = save_filesys_common(&len);
|
|
|
|
if (dst)
|
|
|
|
{
|
|
|
|
save_chunk(f, dst, len, _T("FSYC"), 0);
|
|
|
|
for (i = 0; i < nr_units(); i++)
|
|
|
|
{
|
|
|
|
dst = save_filesys(i, &len);
|
|
|
|
if (dst)
|
|
|
|
{
|
|
|
|
save_chunk(f, dst, len, _T("FSYS"), 0);
|
|
|
|
xfree (dst);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2015-09-09 21:49:41 +02:00
|
|
|
#endif
|
|
|
|
|
2017-02-25 00:38:14 +01:00
|
|
|
for (i = 0; i < MAX_TOTAL_SCSI_DEVICES; i++)
|
|
|
|
{
|
|
|
|
dst = save_cd(i, &len);
|
|
|
|
if (dst)
|
|
|
|
{
|
|
|
|
_stprintf(name, _T("CDU%d"), i);
|
|
|
|
save_chunk(f, dst, len, name, 0);
|
2016-11-30 22:25:43 +01:00
|
|
|
}
|
|
|
|
}
|
2016-07-10 13:48:11 +02:00
|
|
|
|
2017-02-25 00:38:14 +01:00
|
|
|
zfile_fwrite(endhunk, 1, 8, f);
|
2016-04-24 09:45:29 +00:00
|
|
|
|
2017-02-25 00:38:14 +01:00
|
|
|
return 1;
|
2016-04-24 09:45:29 +00:00
|
|
|
}
|
|
|
|
|
2017-02-25 00:38:14 +01:00
|
|
|
int save_state(const TCHAR* filename, const TCHAR* description)
|
2016-04-24 09:45:29 +00:00
|
|
|
{
|
2017-02-25 00:38:14 +01:00
|
|
|
struct zfile* f;
|
|
|
|
int comp = savestate_docompress;
|
|
|
|
|
|
|
|
if (!savestate_specialdump && !savestate_nodialogs)
|
|
|
|
{
|
|
|
|
state_incompatible_warn();
|
|
|
|
if (!save_filesys_cando())
|
|
|
|
{
|
|
|
|
gui_message(_T("Filesystem active. Try again later."));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
2016-11-30 22:25:43 +01:00
|
|
|
new_blitter = false;
|
2017-02-25 00:38:14 +01:00
|
|
|
savestate_nodialogs = 0;
|
|
|
|
custom_prepare_savestate();
|
|
|
|
f = zfile_fopen(filename, _T("w+b"), 0);
|
|
|
|
if (!f)
|
|
|
|
return 0;
|
|
|
|
int v = save_state_internal(f, description, comp, true);
|
2016-11-30 22:25:43 +01:00
|
|
|
if (v)
|
2017-02-25 00:38:14 +01:00
|
|
|
write_log (_T("Save of '%s' complete\n"), filename);
|
|
|
|
zfile_fclose(f);
|
|
|
|
savestate_state = 0;
|
2016-11-30 22:25:43 +01:00
|
|
|
return v;
|
2015-05-13 18:47:23 +00:00
|
|
|
}
|
|
|
|
|
2017-02-25 00:38:14 +01:00
|
|
|
bool savestate_check()
|
2015-05-13 18:47:23 +00:00
|
|
|
{
|
2017-02-25 00:38:14 +01:00
|
|
|
if (savestate_state == STATE_DORESTORE)
|
|
|
|
{
|
2016-11-30 22:25:43 +01:00
|
|
|
savestate_state = STATE_RESTORE;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
2015-05-13 18:47:23 +00:00
|
|
|
}
|
|
|
|
|
2016-04-24 09:45:29 +00:00
|
|
|
|
2015-05-13 18:47:23 +00:00
|
|
|
/*
|
|
|
|
|
|
|
|
My (Toni Wilen <twilen@arabuusimiehet.com>)
|
|
|
|
proposal for Amiga-emulators' state-save format
|
|
|
|
|
|
|
|
Feel free to comment...
|
|
|
|
|
|
|
|
This is very similar to IFF-fileformat
|
|
|
|
Every hunk must end to 4 byte boundary,
|
|
|
|
fill with zero bytes if needed
|
|
|
|
|
|
|
|
version 0.8
|
|
|
|
|
|
|
|
HUNK HEADER (beginning of every hunk)
|
|
|
|
|
|
|
|
hunk name (4 ascii-characters)
|
|
|
|
hunk size (including header)
|
2016-11-30 22:25:43 +01:00
|
|
|
hunk flags
|
2015-05-13 18:47:23 +00:00
|
|
|
|
|
|
|
bit 0 = chunk contents are compressed with zlib (maybe RAM chunks only?)
|
|
|
|
|
|
|
|
HEADER
|
|
|
|
|
|
|
|
"ASF " (AmigaStateFile)
|
2016-11-30 22:25:43 +01:00
|
|
|
|
2015-05-13 18:47:23 +00:00
|
|
|
statefile version
|
|
|
|
emulator name ("uae", "fellow" etc..)
|
|
|
|
emulator version string (example: "0.8.15")
|
|
|
|
free user writable comment string
|
|
|
|
|
|
|
|
CPU
|
|
|
|
|
2015-09-09 21:49:41 +02:00
|
|
|
"CPU "
|
2015-05-13 18:47:23 +00:00
|
|
|
|
2015-09-09 21:49:41 +02:00
|
|
|
CPU model 4 (68000,68010,68020,68030,68040,68060)
|
|
|
|
CPU typeflags bit 0=EC-model or not, bit 31 = clock rate included
|
|
|
|
D0-D7 8*4=32
|
|
|
|
A0-A6 7*4=32
|
|
|
|
PC 4
|
2015-05-13 18:47:23 +00:00
|
|
|
unused 4
|
|
|
|
68000 prefetch (IRC) 2
|
|
|
|
68000 prefetch (IR) 2
|
2015-09-09 21:49:41 +02:00
|
|
|
USP 4
|
|
|
|
ISP 4
|
|
|
|
SR/CCR 2
|
|
|
|
flags 4 (bit 0=CPU was HALTed)
|
2015-05-13 18:47:23 +00:00
|
|
|
|
2015-09-09 21:49:41 +02:00
|
|
|
CPU specific registers
|
2015-05-13 18:47:23 +00:00
|
|
|
|
2015-09-09 21:49:41 +02:00
|
|
|
68000: SR/CCR is last saved register
|
|
|
|
68010: save also DFC,SFC and VBR
|
|
|
|
68020: all 68010 registers and CAAR,CACR and MSP
|
|
|
|
etc..
|
2015-05-13 18:47:23 +00:00
|
|
|
|
2015-09-09 21:49:41 +02:00
|
|
|
68010+:
|
|
|
|
|
|
|
|
DFC 4
|
|
|
|
SFC 4
|
|
|
|
VBR 4
|
|
|
|
|
|
|
|
68020+:
|
|
|
|
|
|
|
|
CAAR 4
|
|
|
|
CACR 4
|
|
|
|
MSP 4
|
|
|
|
|
|
|
|
68030+:
|
|
|
|
|
|
|
|
AC0 4
|
|
|
|
AC1 4
|
|
|
|
ACUSR 2
|
|
|
|
TT0 4
|
|
|
|
TT1 4
|
|
|
|
|
|
|
|
68040+:
|
|
|
|
|
|
|
|
ITT0 4
|
|
|
|
ITT1 4
|
|
|
|
DTT0 4
|
|
|
|
DTT1 4
|
|
|
|
TCR 4
|
|
|
|
URP 4
|
|
|
|
SRP 4
|
|
|
|
|
|
|
|
68060:
|
|
|
|
|
|
|
|
BUSCR 4
|
|
|
|
PCR 4
|
|
|
|
|
|
|
|
All:
|
|
|
|
|
|
|
|
Clock in KHz 4 (only if bit 31 in flags)
|
|
|
|
4 (spare, only if bit 31 in flags)
|
2015-05-13 18:47:23 +00:00
|
|
|
|
|
|
|
|
|
|
|
FPU (only if used)
|
|
|
|
|
|
|
|
"FPU "
|
|
|
|
|
2015-09-09 21:49:41 +02:00
|
|
|
FPU model 4 (68881/68882/68040/68060)
|
|
|
|
FPU typeflags 4 (bit 31 = clock rate included)
|
|
|
|
FP0-FP7 4+4+2 (80 bits)
|
|
|
|
FPCR 4
|
|
|
|
FPSR 4
|
|
|
|
FPIAR 4
|
2015-05-13 18:47:23 +00:00
|
|
|
|
2015-09-09 21:49:41 +02:00
|
|
|
Clock in KHz 4 (only if bit 31 in flags)
|
|
|
|
4 (spare, only if bit 31 in flags)
|
2015-05-13 18:47:23 +00:00
|
|
|
|
2015-09-09 21:49:41 +02:00
|
|
|
MMU (when and if MMU is supported in future..)
|
2015-05-13 18:47:23 +00:00
|
|
|
|
2016-04-24 09:45:29 +00:00
|
|
|
"MMU "
|
2015-05-13 18:47:23 +00:00
|
|
|
|
2016-04-24 09:45:29 +00:00
|
|
|
MMU model 4 (68040)
|
|
|
|
flags 4 (none defined yet)
|
2015-05-13 18:47:23 +00:00
|
|
|
|
|
|
|
CUSTOM CHIPS
|
|
|
|
|
2015-09-09 21:49:41 +02:00
|
|
|
"CHIP"
|
2015-05-13 18:47:23 +00:00
|
|
|
|
2015-09-09 21:49:41 +02:00
|
|
|
chipset flags 4 OCS=0,ECSAGNUS=1,ECSDENISE=2,AGA=4
|
|
|
|
ECSAGNUS and ECSDENISE can be combined
|
2015-05-13 18:47:23 +00:00
|
|
|
|
2015-09-09 21:49:41 +02:00
|
|
|
DFF000-DFF1FF 352 (0x120 - 0x17f and 0x0a0 - 0xdf excluded)
|
2015-05-13 18:47:23 +00:00
|
|
|
|
2015-09-09 21:49:41 +02:00
|
|
|
sprite registers (0x120 - 0x17f) saved with SPRx chunks
|
|
|
|
audio registers (0x0a0 - 0xdf) saved with AUDx chunks
|
2015-05-13 18:47:23 +00:00
|
|
|
|
|
|
|
AGA COLORS
|
|
|
|
|
2015-09-09 21:49:41 +02:00
|
|
|
"AGAC"
|
2015-05-13 18:47:23 +00:00
|
|
|
|
2015-09-09 21:49:41 +02:00
|
|
|
AGA color 8 banks * 32 registers *
|
|
|
|
registers LONG (XRGB) = 1024
|
2015-05-13 18:47:23 +00:00
|
|
|
|
|
|
|
SPRITE
|
|
|
|
|
2015-09-09 21:49:41 +02:00
|
|
|
"SPR0" - "SPR7"
|
2015-05-13 18:47:23 +00:00
|
|
|
|
|
|
|
|
2015-09-09 21:49:41 +02:00
|
|
|
SPRxPT 4
|
|
|
|
SPRxPOS 2
|
|
|
|
SPRxCTL 2
|
|
|
|
SPRxDATA 2
|
|
|
|
SPRxDATB 2
|
|
|
|
AGA sprite DATA/DATB 3 * 2 * 2
|
|
|
|
sprite "armed" status 1
|
2015-05-13 18:47:23 +00:00
|
|
|
|
2015-09-09 21:49:41 +02:00
|
|
|
sprites maybe armed in non-DMA mode
|
|
|
|
use bit 0 only, other bits are reserved
|
2015-05-13 18:47:23 +00:00
|
|
|
|
|
|
|
|
|
|
|
AUDIO
|
2015-09-09 21:49:41 +02:00
|
|
|
"AUD0" "AUD1" "AUD2" "AUD3"
|
2015-05-13 18:47:23 +00:00
|
|
|
|
2015-09-09 21:49:41 +02:00
|
|
|
audio state 1
|
|
|
|
machine mode
|
|
|
|
AUDxVOL 1
|
2015-05-13 18:47:23 +00:00
|
|
|
irq? 1
|
|
|
|
data_written? 1
|
2015-09-09 21:49:41 +02:00
|
|
|
internal AUDxLEN 2
|
|
|
|
AUDxLEN 2
|
2015-05-13 18:47:23 +00:00
|
|
|
internal AUDxPER 2
|
|
|
|
AUDxPER 2
|
2015-09-09 21:49:41 +02:00
|
|
|
internal AUDxLC 4
|
2015-05-13 18:47:23 +00:00
|
|
|
AUDxLC 4
|
|
|
|
evtime? 4
|
|
|
|
|
|
|
|
BLITTER
|
|
|
|
|
2015-09-09 21:49:41 +02:00
|
|
|
"BLIT"
|
2015-05-13 18:47:23 +00:00
|
|
|
|
2015-09-09 21:49:41 +02:00
|
|
|
internal blitter state
|
2015-05-13 18:47:23 +00:00
|
|
|
|
2015-09-09 21:49:41 +02:00
|
|
|
flags 4
|
|
|
|
bit 0=blitter active
|
|
|
|
bit 1=fill carry bit
|
|
|
|
internal ahold 4
|
|
|
|
internal bhold 4
|
|
|
|
internal hsize 2
|
|
|
|
internal vsize 2
|
2015-05-13 18:47:23 +00:00
|
|
|
|
|
|
|
CIA
|
|
|
|
|
2015-09-09 21:49:41 +02:00
|
|
|
"CIAA" and "CIAB"
|
2015-05-13 18:47:23 +00:00
|
|
|
|
2015-09-09 21:49:41 +02:00
|
|
|
BFE001-BFEF01 16*1 (CIAA)
|
|
|
|
BFD000-BFDF00 16*1 (CIAB)
|
2015-05-13 18:47:23 +00:00
|
|
|
|
2015-09-09 21:49:41 +02:00
|
|
|
internal registers
|
2015-05-13 18:47:23 +00:00
|
|
|
|
2015-09-09 21:49:41 +02:00
|
|
|
IRQ mask (ICR) 1 BYTE
|
|
|
|
timer latches 2 timers * 2 BYTES (LO/HI)
|
|
|
|
latched tod 3 BYTES (LO/MED/HI)
|
|
|
|
alarm 3 BYTES (LO/MED/HI)
|
|
|
|
flags 1 BYTE
|
|
|
|
bit 0=tod latched (read)
|
|
|
|
bit 1=tod stopped (write)
|
2015-05-13 18:47:23 +00:00
|
|
|
div10 counter 1 BYTE
|
|
|
|
|
|
|
|
FLOPPY DRIVES
|
|
|
|
|
2015-09-09 21:49:41 +02:00
|
|
|
"DSK0" "DSK1" "DSK2" "DSK3"
|
2015-05-13 18:47:23 +00:00
|
|
|
|
2015-09-09 21:49:41 +02:00
|
|
|
drive state
|
2015-05-13 18:47:23 +00:00
|
|
|
|
2015-09-09 21:49:41 +02:00
|
|
|
drive ID-word 4
|
|
|
|
state 1 (bit 0: motor on, bit 1: drive disabled, bit 2: current id bit)
|
|
|
|
rw-head track 1
|
|
|
|
dskready 1
|
|
|
|
id-mode 1 (ID mode bit number 0-31)
|
|
|
|
floppy information
|
2015-05-13 18:47:23 +00:00
|
|
|
|
2015-09-09 21:49:41 +02:00
|
|
|
bits from 4
|
|
|
|
beginning of track
|
|
|
|
CRC of disk-image 4 (used during restore to check if image
|
|
|
|
is correct)
|
|
|
|
disk-image null-terminated
|
|
|
|
file name
|
2015-05-13 18:47:23 +00:00
|
|
|
|
2015-09-09 21:49:41 +02:00
|
|
|
INTERNAL FLOPPY CONTROLLER STATUS
|
2015-05-13 18:47:23 +00:00
|
|
|
|
2015-09-09 21:49:41 +02:00
|
|
|
"DISK"
|
2015-05-13 18:47:23 +00:00
|
|
|
|
2015-09-09 21:49:41 +02:00
|
|
|
current DMA word 2
|
|
|
|
DMA word bit offset 1
|
|
|
|
WORDSYNC found 1 (no=0,yes=1)
|
|
|
|
hpos of next bit 1
|
|
|
|
DSKLENGTH status 0=off,1=written once,2=written twice
|
|
|
|
unused 2
|
2015-05-13 18:47:23 +00:00
|
|
|
|
2015-09-09 21:49:41 +02:00
|
|
|
RAM SPACE
|
2015-05-13 18:47:23 +00:00
|
|
|
|
2015-09-09 21:49:41 +02:00
|
|
|
"xRAM" (CRAM = chip, BRAM = bogo, FRAM = fast, ZRAM = Z3, P96 = RTG RAM, A3K1/A3K2 = MB RAM)
|
2015-05-13 18:47:23 +00:00
|
|
|
|
2015-09-09 21:49:41 +02:00
|
|
|
start address 4 ("bank"=chip/slow/fast etc..)
|
|
|
|
of RAM "bank"
|
|
|
|
RAM "bank" size 4
|
|
|
|
RAM flags 4 (bit 0 = zlib compressed)
|
|
|
|
RAM "bank" contents
|
2015-05-13 18:47:23 +00:00
|
|
|
|
|
|
|
ROM SPACE
|
|
|
|
|
2015-09-09 21:49:41 +02:00
|
|
|
"ROM "
|
|
|
|
|
|
|
|
ROM start 4
|
|
|
|
address
|
|
|
|
size of ROM 4
|
|
|
|
ROM type 4 KICK=0
|
|
|
|
ROM flags 4
|
|
|
|
ROM version 2
|
|
|
|
ROM revision 2
|
|
|
|
ROM CRC 4 see below
|
|
|
|
ROM-image ID-string null terminated, see below
|
|
|
|
path to rom image
|
|
|
|
ROM contents (Not mandatory, use hunk size to check if
|
|
|
|
this hunk contains ROM data or not)
|
2015-05-13 18:47:23 +00:00
|
|
|
|
2015-09-09 21:49:41 +02:00
|
|
|
Kickstart ROM:
|
|
|
|
ID-string is "Kickstart x.x"
|
|
|
|
ROM version: version in high word and revision in low word
|
|
|
|
Kickstart ROM version and revision can be found from ROM start
|
|
|
|
+ 12 (version) and +14 (revision)
|
2015-05-13 18:47:23 +00:00
|
|
|
|
2015-09-09 21:49:41 +02:00
|
|
|
ROM version and CRC is only meant for emulator to automatically
|
|
|
|
find correct image from its ROM-directory during state restore.
|
2015-05-13 18:47:23 +00:00
|
|
|
|
2015-09-09 21:49:41 +02:00
|
|
|
Usually saving ROM contents is not good idea.
|
2015-05-13 18:47:23 +00:00
|
|
|
|
|
|
|
ACTION REPLAY
|
|
|
|
|
|
|
|
"ACTR"
|
|
|
|
|
|
|
|
Model (1,2,3) 4
|
|
|
|
path to rom image
|
|
|
|
RAM space (depends on model)
|
|
|
|
ROM CRC 4
|
|
|
|
|
2016-04-24 09:45:29 +00:00
|
|
|
"CDx "
|
|
|
|
|
|
|
|
Flags 4 (bit 0 = scsi, bit 1 = ide, bit 2 = image)
|
|
|
|
Path (for example image file or drive letter)
|
|
|
|
|
2015-05-13 18:47:23 +00:00
|
|
|
END
|
2015-09-09 21:49:41 +02:00
|
|
|
hunk "END " ends, remember hunk size 8!
|
2015-05-13 18:47:23 +00:00
|
|
|
|
|
|
|
|
|
|
|
EMULATOR SPECIFIC HUNKS
|
|
|
|
|
|
|
|
Read only if "emulator name" in header is same as used emulator.
|
|
|
|
Maybe useful for configuration?
|
|
|
|
|
|
|
|
misc:
|
|
|
|
|
|
|
|
- save only at position 0,0 before triggering VBLANK interrupt
|
|
|
|
- all data must be saved in bigendian format
|
|
|
|
- should we strip all paths from image file names?
|
|
|
|
|
|
|
|
*/
|