Mercurial > repos > blastem
diff serialize.c @ 1427:4e5797b3935a
WIP - New savestate format
author | Michael Pavone <pavone@retrodev.com> |
---|---|
date | Sun, 06 Aug 2017 00:06:36 -0700 |
parents | |
children | 2540c05520f2 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/serialize.c Sun Aug 06 00:06:36 2017 -0700 @@ -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(§ion, buf->data + buf->cur_pos, size); + buf->handlers[section_id].fun(§ion, 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; +}