summaryrefslogtreecommitdiff
path: root/serialize.c
diff options
context:
space:
mode:
authorMichael Pavone <pavone@retrodev.com>2017-08-06 00:06:36 -0700
committerMichael Pavone <pavone@retrodev.com>2017-08-06 00:06:36 -0700
commit581601741c3b94bc66a03eece1774618312b260a (patch)
treeb554582c4e697996207ceb7bf207177f6ade235d /serialize.c
parent3b9d676e5bc08488d54b2479201ede060f727b68 (diff)
WIP - New savestate format
Diffstat (limited to 'serialize.c')
-rw-r--r--serialize.c276
1 files changed, 276 insertions, 0 deletions
diff --git a/serialize.c b/serialize.c
new file mode 100644
index 0000000..08ee7e8
--- /dev/null
+++ b/serialize.c
@@ -0,0 +1,276 @@
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include "serialize.h"
+#include "util.h"
+
+#ifndef SERIALIZE_DEFAULT_SIZE
+#define SERIALIZE_DEFAULT_SIZE (256*1024) //default to enough for a Genesis save state
+#endif
+
+
+void init_serialize(serialize_buffer *buf)
+{
+ buf->storage = SERIALIZE_DEFAULT_SIZE;
+ buf->size = 0;
+ buf->current_section_start = 0;
+ buf->data = malloc(SERIALIZE_DEFAULT_SIZE);
+}
+
+static void reserve(serialize_buffer *buf, size_t amount)
+{
+ if (amount > (buf->storage - buf->size)) {
+ buf->storage *= 2;
+ buf = realloc(buf, buf->storage + sizeof(*buf));
+ }
+}
+
+void save_int32(serialize_buffer *buf, uint32_t val)
+{
+ reserve(buf, sizeof(val));
+ buf->data[buf->size++] = val >> 24;
+ buf->data[buf->size++] = val >> 16;
+ buf->data[buf->size++] = val >> 8;
+ buf->data[buf->size++] = val;
+}
+
+void save_int16(serialize_buffer *buf, uint16_t val)
+{
+ reserve(buf, sizeof(val));
+ buf->data[buf->size++] = val >> 8;
+ buf->data[buf->size++] = val;
+}
+
+void save_int8(serialize_buffer *buf, uint8_t val)
+{
+ reserve(buf, sizeof(val));
+ buf->data[buf->size++] = val;
+}
+
+void save_string(serialize_buffer *buf, char *val)
+{
+ size_t len = strlen(val);
+ save_buffer8(buf, val, len);
+}
+
+void save_buffer8(serialize_buffer *buf, void *val, size_t len)
+{
+ reserve(buf, len);
+ memcpy(&buf->data[buf->size], val, len);
+ buf->size += len;
+}
+
+void save_buffer16(serialize_buffer *buf, uint16_t *val, size_t len)
+{
+ reserve(buf, len * sizeof(*val));
+ for(; len != 0; len--, val++) {
+ buf->data[buf->size++] = *val >> 8;
+ buf->data[buf->size++] = *val;
+ }
+}
+
+void save_buffer32(serialize_buffer *buf, uint32_t *val, size_t len)
+{
+ reserve(buf, len * sizeof(*val));
+ for(; len != 0; len--, val++) {
+ buf->data[buf->size++] = *val >> 24;
+ buf->data[buf->size++] = *val >> 16;
+ buf->data[buf->size++] = *val >> 8;
+ buf->data[buf->size++] = *val;
+ }
+}
+
+void start_section(serialize_buffer *buf, uint16_t section_id)
+{
+ save_int16(buf, section_id);
+ //reserve some space for size once we end this section
+ reserve(buf, sizeof(uint32_t));
+ buf->size += sizeof(uint32_t);
+ //save start point for use in end_device
+ buf->current_section_start = buf->size;
+}
+
+void end_section(serialize_buffer *buf)
+{
+ size_t section_size = buf->size - buf->current_section_start;
+ if (section_size > 0xFFFFFFFFU) {
+ fatal_error("Sections larger than 4GB are not supported");
+ }
+ uint32_t size = section_size;
+ uint8_t *field = buf->data + buf->current_section_start - sizeof(uint32_t);
+ *(field++) = size >> 24;
+ *(field++) = size >> 16;
+ *(field++) = size >> 8;
+ *(field++) = size;
+ buf->current_section_start = 0;
+}
+
+void register_section_handler(deserialize_buffer *buf, section_handler handler, uint16_t section_id)
+{
+ if (section_id > buf->max_handler) {
+ uint16_t old_max = buf->max_handler;
+ if (buf->max_handler < 0x8000) {
+ buf->max_handler *= 2;
+ } else {
+ buf->max_handler = 0xFFFF;
+ }
+ buf->handlers = realloc(buf->handlers, (buf->max_handler+1) * sizeof(handler));
+ memset(buf->handlers + old_max + 1, 0, (buf->max_handler - old_max) * sizeof(handler));
+ }
+ if (!buf->handlers) {
+ buf->handlers = calloc(buf->max_handler + 1, sizeof(handler));
+ }
+ buf->handlers[section_id] = handler;
+}
+
+void init_deserialize(deserialize_buffer *buf, uint8_t *data, size_t size)
+{
+ buf->size = size;
+ buf->cur_pos = 0;
+ buf->data = data;
+ buf->handlers = NULL;
+ buf->max_handler = 8;
+}
+
+uint32_t load_int32(deserialize_buffer *buf)
+{
+ uint32_t val;
+ if ((buf->size - buf->cur_pos) < sizeof(val)) {
+ fatal_error("Failed to load required int32 field");
+ }
+ val = buf->data[buf->cur_pos++] << 24;
+ val |= buf->data[buf->cur_pos++] << 16;
+ val |= buf->data[buf->cur_pos++] << 8;
+ val |= buf->data[buf->cur_pos++];
+ return val;
+}
+
+uint16_t load_int16(deserialize_buffer *buf)
+{
+ uint16_t val;
+ if ((buf->size - buf->cur_pos) < sizeof(val)) {
+ fatal_error("Failed to load required int16 field");
+ }
+ val = buf->data[buf->cur_pos++] << 8;
+ val |= buf->data[buf->cur_pos++];
+ return val;
+}
+
+uint8_t load_int8(deserialize_buffer *buf)
+{
+ uint8_t val;
+ if ((buf->size - buf->cur_pos) < sizeof(val)) {
+ fatal_error("Failed to load required int8 field");
+ }
+ val = buf->data[buf->cur_pos++];
+ return val;
+}
+
+void load_buffer8(deserialize_buffer *buf, void *dst, size_t len)
+{
+ if ((buf->size - buf->cur_pos) < len) {
+ fatal_error("Failed to load required buffer of size %d", len);
+ }
+ memcpy(dst, buf->data + buf->cur_pos, len);
+ buf->cur_pos += len;
+}
+
+void load_buffer16(deserialize_buffer *buf, uint16_t *dst, size_t len)
+{
+ if ((buf->size - buf->cur_pos) < len * sizeof(uint16_t)) {
+ fatal_error("Failed to load required buffer of size %d\n", len);
+ }
+ for(; len != 0; len--, dst++) {
+ uint16_t value = buf->data[buf->cur_pos++] << 8;
+ value |= buf->data[buf->cur_pos++];
+ *dst = value;
+ }
+}
+void load_buffer32(deserialize_buffer *buf, uint32_t *dst, size_t len)
+{
+ if ((buf->size - buf->cur_pos) < len * sizeof(uint32_t)) {
+ fatal_error("Failed to load required buffer of size %d\n", len);
+ }
+ for(; len != 0; len--, dst++) {
+ uint32_t value = buf->data[buf->cur_pos++] << 24;
+ value |= buf->data[buf->cur_pos++] << 16;
+ value |= buf->data[buf->cur_pos++] << 8;
+ value |= buf->data[buf->cur_pos++];
+ *dst = value;
+ }
+}
+
+void load_section(deserialize_buffer *buf)
+{
+ if (!buf->handlers) {
+ fatal_error("load_section called on a deserialize_buffer with no handlers registered\n");
+ }
+ uint16_t section_id = load_int16(buf);
+ uint32_t size = load_int32(buf);
+ if (size > (buf->size - buf->cur_pos)) {
+ fatal_error("Section is bigger than remaining space in file");
+ }
+ if (section_id > buf->max_handler || !buf->handlers[section_id].fun) {
+ warning("No handler for section ID %d, save state may be from a newer version\n", section_id);
+ buf->cur_pos += size;
+ return;
+ }
+ deserialize_buffer section;
+ init_deserialize(&section, buf->data + buf->cur_pos, size);
+ buf->handlers[section_id].fun(&section, buf->handlers[section_id].data);
+ buf->cur_pos += size;
+}
+
+static const char sz_ident[] = "BLSTSZ\x01\x07";
+
+uint8_t save_to_file(serialize_buffer *buf, char *path)
+{
+ FILE *f = fopen(path, "wb");
+ if (!f) {
+ return 0;
+ }
+ if (fwrite(sz_ident, 1, sizeof(sz_ident)-1, f) != sizeof(sz_ident)-1) {
+ fclose(f);
+ return 0;
+ }
+ if (fwrite(buf->data, 1, buf->size, f) != buf->size) {
+ fclose(f);
+ return 0;
+ }
+ fclose(f);
+ return 1;
+}
+
+uint8_t load_from_file(deserialize_buffer *buf, char *path)
+{
+ FILE *f = fopen(path, "rb");
+ if (!f) {
+ return 0;
+ }
+ char ident[sizeof(sz_ident)-1];
+ long size = file_size(f);
+ if (size < sizeof(ident)) {
+ fclose(f);
+ return 0;
+ }
+ if (fread(ident, 1, sizeof(ident), f) != sizeof(ident)) {
+ fclose(f);
+ return 0;
+ }
+ fclose(f);
+ if (memcmp(ident, sz_ident, sizeof(ident))) {
+ return 0;
+ }
+ buf->size = size - sizeof(ident);
+ buf->cur_pos = 0;
+ buf->data = malloc(buf->size);
+ buf->handlers = NULL;
+ buf->max_handler = 8;
+ if (fread(buf->data, 1, buf->size, f) != buf->size) {
+ free(buf->data);
+ buf->data = NULL;
+ buf->size = 0;
+ return 0;
+ }
+ return 1;
+}