Save States: Make states a bit more robust to changes and compress their contents (breaks save state compatibility)

This commit is contained in:
Sour 2019-10-23 18:19:49 -04:00
parent c8f6822ed7
commit afa55938ec
6 changed files with 85 additions and 153 deletions

View file

@ -6,47 +6,26 @@
void RewindData::GetStateData(stringstream &stateData)
{
unsigned long length = OriginalSaveStateSize;
uint8_t* buffer = new uint8_t[length];
uncompress(buffer, &length, SaveStateData.data(), (unsigned long)SaveStateData.size());
stateData.write((char*)buffer, length);
delete[] buffer;
stateData.write((char*)SaveStateData.data(), SaveStateData.size());
}
void RewindData::LoadState(shared_ptr<Console> &console)
{
if(SaveStateData.size() > 0 && OriginalSaveStateSize > 0) {
unsigned long length = OriginalSaveStateSize;
uint8_t* buffer = new uint8_t[length];
uncompress(buffer, &length, SaveStateData.data(), (unsigned long)SaveStateData.size());
if(SaveStateData.size() > 0) {
stringstream stream;
stream.write((char*)buffer, length);
stream.write((char*)SaveStateData.data(), SaveStateData.size());
stream.seekg(0, ios::beg);
console->Deserialize(stream, SaveStateManager::FileFormatVersion);
delete[] buffer;
}
}
void RewindData::CompressState(string stateData, vector<uint8_t>& compressedState)
{
unsigned long compressedSize = compressBound((unsigned long)stateData.size());
uint8_t* compressedData = new uint8_t[compressedSize];
compress2(compressedData, &compressedSize, (unsigned char*)stateData.c_str(), (unsigned long)stateData.size(), MZ_BEST_SPEED);
compressedState = vector<uint8_t>(compressedData, compressedData + compressedSize);
delete[] compressedData;
}
void RewindData::SaveState(shared_ptr<Console> &console)
{
std::stringstream state;
console->Serialize(state);
string stateData = state.str();
vector<uint8_t> compressedState;
CompressState(stateData, compressedState);
SaveStateData = compressedState;
OriginalSaveStateSize = (uint32_t)stateData.size();
string data = state.str();
SaveStateData = vector<uint8_t>(data.c_str(), data.c_str()+data.size());
FrameCount = 0;
}

View file

@ -9,9 +9,6 @@ class RewindData
{
private:
vector<uint8_t> SaveStateData;
uint32_t OriginalSaveStateSize = 0;
void CompressState(string stateData, vector<uint8_t> &compressedState);
public:
std::deque<ControlDeviceState> InputLogs[BaseControlDevice::PortCount];

View file

@ -133,7 +133,7 @@ bool SaveStateManager::LoadState(istream &stream, bool hashCheckRequired)
}
stream.read((char*)&fileFormatVersion, sizeof(fileFormatVersion));
if(fileFormatVersion <= 4) {
if(fileFormatVersion <= 5) {
MessageManager::DisplayMessage("SaveStates", "SaveStateIncompatibleVersion");
return false;
} else {

View file

@ -14,7 +14,7 @@ private:
string GetStateFilepath(int stateIndex);
public:
static constexpr uint32_t FileFormatVersion = 5;
static constexpr uint32_t FileFormatVersion = 6;
SaveStateManager(shared_ptr<Console> console);

View file

@ -2,14 +2,14 @@
#include <algorithm>
#include "Serializer.h"
#include "ISerializable.h"
#include "miniz.h"
Serializer::Serializer(uint32_t version)
{
_version = version;
_streamSize = 0x50000;
_stream = new uint8_t[_streamSize];
_position = 0;
_block.Data = vector<uint8_t>(0x50000);
_block.Position = 0;
_saving = true;
}
@ -17,56 +17,36 @@ Serializer::Serializer(istream &file, uint32_t version)
{
_version = version;
_position = 0;
_block.Position = 0;
_saving = false;
file.read((char*)&_streamSize, sizeof(_streamSize));
_stream = new uint8_t[_streamSize];
file.read((char*)_stream, _streamSize);
}
uint32_t decompressedSize;
file.read((char*)&decompressedSize, sizeof(decompressedSize));
Serializer::~Serializer()
{
delete[] _stream;
uint32_t compressedSize;
file.read((char*)&compressedSize, sizeof(compressedSize));
vector<uint8_t> compressedData(compressedSize, 0);
file.read((char*)compressedData.data(), compressedSize);
_block.Data = vector<uint8_t>(decompressedSize, 0);
unsigned long decompSize;
uncompress(_block.Data.data(), &decompSize, compressedData.data(), (unsigned long)compressedData.size());
}
void Serializer::EnsureCapacity(uint32_t typeSize)
{
//Make sure the current block/stream is large enough to fit the next write
uint32_t oldSize;
uint32_t sizeRequired;
uint8_t *oldBuffer;
if(_inBlock) {
oldBuffer = _blockBuffer;
oldSize = _blockSize;
sizeRequired = _blockPosition + typeSize;
} else {
oldBuffer = _stream;
oldSize = _streamSize;
sizeRequired = _position + typeSize;
uint32_t oldSize = (uint32_t)_block.Data.size();
uint32_t sizeRequired = _block.Position + typeSize;
uint32_t newSize = oldSize;
while(newSize < sizeRequired) {
newSize *= 2;
}
uint8_t *newBuffer = nullptr;
uint32_t newSize = oldSize * 2;
if(oldSize < sizeRequired) {
while(newSize < sizeRequired) {
newSize *= 2;
}
newBuffer = new uint8_t[newSize];
memcpy(newBuffer, oldBuffer, oldSize);
delete[] oldBuffer;
}
if(newBuffer) {
if(_inBlock) {
_blockBuffer = newBuffer;
_blockSize = newSize;
} else {
_stream = newBuffer;
_streamSize = newSize;
}
}
_block.Data.resize(newSize);
}
void Serializer::RecursiveStream()
@ -75,41 +55,47 @@ void Serializer::RecursiveStream()
void Serializer::StreamStartBlock()
{
if(_inBlock) {
throw std::runtime_error("Cannot start a new block before ending the last block");
}
BlockData block;
block.Position = 0;
if(!_saving) {
InternalStream(_blockSize);
_blockSize = std::min(_blockSize, (uint32_t)0xFFFFF);
_blockBuffer = new uint8_t[_blockSize];
ArrayInfo<uint8_t> arrayInfo = { _blockBuffer, _blockSize };
InternalStream(arrayInfo);
VectorInfo<uint8_t> vectorInfo = { &block.Data };
InternalStream(vectorInfo);
} else {
_blockSize = 0x100;
_blockBuffer = new uint8_t[_blockSize];
block.Data = vector<uint8_t>(0x100);
}
_blockPosition = 0;
_inBlock = true;
_blocks.push_back(_block);
_block = block;
}
void Serializer::StreamEndBlock()
{
_inBlock = false;
if(_saving) {
InternalStream(_blockPosition);
ArrayInfo<uint8_t> arrayInfo = { _blockBuffer, _blockPosition };
InternalStream(arrayInfo);
if(_blocks.empty()) {
throw std::runtime_error("Invalid call to end block");
}
delete[] _blockBuffer;
_blockBuffer = nullptr;
BlockData block = _block;
_block = _blocks.back();
_blocks.pop_back();
if(_saving) {
ArrayInfo<uint8_t> arrayInfo { block.Data.data(), block.Position };
InternalStream(arrayInfo);
}
}
void Serializer::Save(ostream& file)
void Serializer::Save(ostream& file, int compressionLevel)
{
file.write((char*)&_position, sizeof(_position));
file.write((char*)_stream, _position);
unsigned long compressedSize = compressBound((unsigned long)_block.Position);
uint8_t* compressedData = new uint8_t[compressedSize];
compress2(compressedData, &compressedSize, (unsigned char*)_block.Data.data(), (unsigned long)_block.Position, compressionLevel);
file.write((char*)&_block.Position, sizeof(_block.Position));
file.write((char*)&compressedSize, sizeof(compressedSize));
file.write((char*)compressedData, compressedSize);
delete[] compressedData;
}
void Serializer::WriteEmptyBlock(ostream* file)
@ -127,14 +113,13 @@ void Serializer::SkipBlock(istream* file)
void Serializer::Stream(ISerializable &obj)
{
//StreamStartBlock();
StreamStartBlock();
obj.Serialize(*this);
//StreamEndBlock();
StreamEndBlock();
}
void Serializer::Stream(ISerializable *obj)
{
//StreamStartBlock();
StreamStartBlock();
obj->Serialize(*this);
//StreamEndBlock();
StreamEndBlock();
}

View file

@ -25,32 +25,27 @@ struct ValueInfo
T DefaultValue;
};
template<typename T>
struct EmptyInfo
struct BlockData
{
T Empty;
vector<uint8_t> Data;
uint32_t Position;
};
class Serializer
{
private:
uint8_t* _stream = nullptr;
uint32_t _position = 0;
uint32_t _streamSize = 0;
vector<BlockData> _blocks;
BlockData _block;
uint32_t _version = 0;
bool _inBlock = false;
uint8_t* _blockBuffer = nullptr;
uint32_t _blockSize = 0;
uint32_t _blockPosition = 0;
bool _saving = false;
private:
void EnsureCapacity(uint32_t typeSize);
template<typename T> void StreamElement(T &value, T defaultValue = T());
template<typename T> void InternalStream(EmptyInfo<T> &info);
template<typename T> void InternalStream(ArrayInfo<T> &info);
template<typename T> void InternalStream(VectorInfo<T> &info);
template<typename T> void InternalStream(ValueInfo<T> &info);
@ -65,7 +60,6 @@ private:
public:
Serializer(uint32_t version);
Serializer(istream &file, uint32_t version);
~Serializer();
uint32_t GetVersion() { return _version; }
bool IsSaving() { return _saving; }
@ -74,7 +68,7 @@ public:
template<typename T> void StreamArray(T *array, uint32_t size);
template<typename T> void StreamVector(vector<T> &list);
void Save(ostream &file);
void Save(ostream &file, int compressionLevel = 1);
void Stream(ISerializable &obj);
void Stream(ISerializable *obj);
@ -91,63 +85,40 @@ void Serializer::StreamElement(T &value, T defaultValue)
int typeSize = sizeof(T);
EnsureCapacity(typeSize);
for(int i = 0; i < typeSize; i++) {
if(_inBlock) {
_blockBuffer[_blockPosition++] = bytes[i];
} else {
_stream[_position++] = bytes[i];
}
_block.Data[_block.Position++] = bytes[i];
}
} else {
if(_inBlock) {
if(_blockPosition + sizeof(T) <= _blockSize) {
memcpy(&value, _blockBuffer + _blockPosition, sizeof(T));
_blockPosition += sizeof(T);
} else {
value = defaultValue;
_blockPosition = _blockSize;
}
if(_block.Position + sizeof(T) <= _block.Data.size()) {
memcpy(&value, _block.Data.data() + _block.Position, sizeof(T));
_block.Position += sizeof(T);
} else {
if(_position + sizeof(T) <= _streamSize) {
memcpy(&value, _stream + _position, sizeof(T));
_position += sizeof(T);
} else {
value = defaultValue;
_position = _streamSize;
}
value = defaultValue;
_block.Position = (uint32_t)_block.Data.size();
}
}
}
template<typename T>
void Serializer::InternalStream(EmptyInfo<T> &info)
{
if(_inBlock) {
_blockPosition += sizeof(T);
} else {
_position += sizeof(T);
}
}
template<typename T>
void Serializer::InternalStream(ArrayInfo<T> &info)
{
T* pointer = info.Array;
uint32_t count = info.ElementCount;
StreamElement<uint32_t>(count);
if(!_saving) {
//Reset array to 0 before loading from file
memset(info.Array, 0, sizeof(T) * info.ElementCount);
memset(info.Array, 0, info.ElementCount * sizeof(T));
}
//Load the number of elements requested, or the maximum possible (based on what is present in the save state)
for(uint32_t i = 0; i < info.ElementCount && i < count; i++) {
StreamElement<T>(*pointer);
pointer++;
EnsureCapacity(info.ElementCount * sizeof(T));
if(_saving) {
memcpy(_block.Data.data() + _block.Position, info.Array, info.ElementCount * sizeof(T));
} else {
memcpy(info.Array, _block.Data.data() + _block.Position, info.ElementCount * sizeof(T));
}
_block.Position += info.ElementCount * sizeof(T);
}
template<typename T>