view serialize.c @ 1971:80920c21bb52

Add an event log soft flush and call it twice per frame in between hard flushes to netplay latency when there are insufficient hardware updates to flush packets in the middle of a frame
author Michael Pavone <pavone@retrodev.com>
date Fri, 08 May 2020 11:40:30 -0700
parents c3c62dbf1ceb
children
line wrap: on
line source

#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)) {
		if (amount < buf->storage) {
			buf->storage *= 2;
		} else {
			//doublign isn't enough, increase by the precise amount needed
			buf->storage += amount - (buf->storage - buf->size);
		}
		buf->data = realloc(buf->data, 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;
	}
	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) {
		fclose(f);
		free(buf->data);
		buf->data = NULL;
		buf->size = 0;
		return 0;
	}
	fclose(f);
	return 1;
}