This uses BSON as savestate format, to allow external tools to parse it (so that we can add proper test of the states). The BSON is not 100% correct according to spec (no ordered keys) but can be parsed by most libraries. This fixes also a bug in the savestate palette color recalculation that was wrongly overwritting the original palette (which could cause some problems on some games). Also fixes some potential issues by serializing some more stuff and cleans up unused stuff. Testing shows that states look good and there's only minor differences in audio ticks, related to buffer sizes (since buffer flushes are de-synced from video frames due to different frequency).
153 lines
3.5 KiB
C
153 lines
3.5 KiB
C
|
|
#include "common.h"
|
|
|
|
const u8 *state_mem_read_ptr;
|
|
u8 *state_mem_write_ptr;
|
|
|
|
const u8* bson_find_key(const u8 *srcp, const char *key)
|
|
{
|
|
unsigned keyl = strlen(key) + 1;
|
|
unsigned doclen = bson_read_u32(srcp);
|
|
const u8* p = &srcp[4];
|
|
while (*p != 0 && (p - srcp) < doclen) {
|
|
u8 tp = *p;
|
|
unsigned tlen = strlen((char*)&p[1]) + 1;
|
|
if (keyl == tlen && !memcmp(key, &p[1], tlen))
|
|
return &p[tlen + 1];
|
|
p += 1 + tlen;
|
|
if (tp == 3 || tp == 4)
|
|
p += bson_read_u32(p);
|
|
else if (tp == 5)
|
|
p += bson_read_u32(p) + 1 + 4;
|
|
else if (tp == 0x10)
|
|
p += 4;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
bool bson_read_int32(const u8 *srcp, const char *key, u32* value)
|
|
{
|
|
const u8* p = srcp ? bson_find_key(srcp, key) : NULL;
|
|
if (!p)
|
|
return false;
|
|
*value = bson_read_u32(p);
|
|
return true;
|
|
}
|
|
|
|
bool bson_read_int32_array(const u8 *srcp, const char *key, u32* value, unsigned cnt)
|
|
{
|
|
const u8* p = srcp ? bson_find_key(srcp, key) : NULL;
|
|
if (p) {
|
|
unsigned arrsz = bson_read_u32(p);
|
|
p += 4;
|
|
if (arrsz < 5)
|
|
return false;
|
|
arrsz = (arrsz - 5) >> 3;
|
|
while (arrsz--) {
|
|
p += 4; // type and name
|
|
*value++ = bson_read_u32(p);
|
|
p += 4; // value
|
|
}
|
|
return true;
|
|
}
|
|
*value = bson_read_u32(p);
|
|
return false;
|
|
}
|
|
|
|
bool bson_read_bytes(const u8 *srcp, const char *key, void* buffer, unsigned cnt)
|
|
{
|
|
const u8* p = srcp ? bson_find_key(srcp, key) : NULL;
|
|
if (p) {
|
|
unsigned bufsz = bson_read_u32(p);
|
|
if (bufsz != cnt)
|
|
return false;
|
|
|
|
// Skip byte array type and size
|
|
memcpy(buffer, &p[5], cnt);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool gba_load_state(const void* src)
|
|
{
|
|
u32 i, tmp;
|
|
u8* srcptr = (u8*)src;
|
|
u32 docsize = bson_read_u32(srcptr);
|
|
if (docsize != GBA_STATE_MEM_SIZE)
|
|
return false;
|
|
|
|
if (!bson_read_int32(srcptr, "info-magic", &tmp) || tmp != GBA_STATE_MAGIC)
|
|
return false;
|
|
if (!bson_read_int32(srcptr, "info-version", &tmp) || tmp != GBA_STATE_VERSION)
|
|
return false;
|
|
|
|
if (!(cpu_read_savestate(srcptr) &&
|
|
input_read_savestate(srcptr) &&
|
|
main_read_savestate(srcptr) &&
|
|
memory_read_savestate(srcptr) &&
|
|
sound_read_savestate(srcptr)))
|
|
{
|
|
// TODO: reset state instead! Should revert instead??
|
|
return false;
|
|
}
|
|
|
|
// Generate converted palette (since it is not saved)
|
|
for(i = 0; i < 512; i++)
|
|
{
|
|
palette_ram_converted[i] = convert_palette(palette_ram[i]);
|
|
}
|
|
|
|
video_reload_counters();
|
|
|
|
// Reset most of the frame state and dynarec state
|
|
#ifdef HAVE_DYNAREC
|
|
if (dynarec_enable)
|
|
init_caches();
|
|
#endif
|
|
|
|
instruction_count = 0;
|
|
reg[CHANGED_PC_STATUS] = 1;
|
|
reg[COMPLETED_FRAME] = 0;
|
|
reg[OAM_UPDATED] = 1;
|
|
gbc_sound_update = 1;
|
|
|
|
return true;
|
|
}
|
|
|
|
void gba_save_state(void* dst)
|
|
{
|
|
u8 *stptr = (u8*)dst;
|
|
u8 *wrptr = (u8*)dst;
|
|
|
|
// Initial bson size
|
|
bson_write_u32(wrptr, 0);
|
|
|
|
// Add some info fields
|
|
bson_write_int32(wrptr, "info-magic", GBA_STATE_MAGIC);
|
|
bson_write_int32(wrptr, "info-version", GBA_STATE_VERSION);
|
|
|
|
wrptr += cpu_write_savestate(wrptr);
|
|
wrptr += input_write_savestate(wrptr);
|
|
wrptr += main_write_savestate(wrptr);
|
|
wrptr += memory_write_savestate(wrptr);
|
|
wrptr += sound_write_savestate(wrptr);
|
|
|
|
// The padding space is pushed into a padding field for easy parsing
|
|
{
|
|
unsigned padsize = GBA_STATE_MEM_SIZE - (wrptr - stptr);
|
|
padsize -= 1 + 9 + 4 + 1 + 1;
|
|
*wrptr++ = 0x05; // Byte array
|
|
bson_write_cstring(wrptr, "zpadding");
|
|
bson_write_u32(wrptr, padsize);
|
|
*wrptr++ = 0;
|
|
wrptr += padsize;
|
|
}
|
|
|
|
*wrptr++ = 0;
|
|
|
|
// Update the doc size
|
|
bson_write_u32(stptr, wrptr - stptr);
|
|
}
|
|
|
|
|