284 lines
8.6 KiB
C++
284 lines
8.6 KiB
C++
/* ScummVM - Graphic Adventure Engine
|
|
*
|
|
* ScummVM is the legal property of its developers, whose names
|
|
* are too numerous to list here. Please refer to the COPYRIGHT
|
|
* file distributed with this source distribution.
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*
|
|
*/
|
|
|
|
#include "engines/stark/services/stateprovider.h"
|
|
|
|
#include "common/memstream.h"
|
|
|
|
#include "engines/stark/resources/level.h"
|
|
#include "engines/stark/resources/location.h"
|
|
#include "engines/stark/resources/object.h"
|
|
|
|
namespace Stark {
|
|
|
|
StateReadStream::StateReadStream(Common::SeekableReadStream *parentStream, DisposeAfterUse::Flag disposeParentStream) :
|
|
SeekableSubReadStream(parentStream, 0, parentStream->size(), disposeParentStream) {
|
|
}
|
|
|
|
StateReadStream::~StateReadStream() {
|
|
}
|
|
|
|
Common::String StateReadStream::readString() {
|
|
// Read the string length
|
|
uint32 length = readUint32LE();
|
|
|
|
// Read the string
|
|
char *data = new char[length];
|
|
read(data, length);
|
|
Common::String string(data, length);
|
|
delete[] data;
|
|
|
|
return string;
|
|
}
|
|
|
|
StateProvider::ResourceTreeState::ResourceTreeState(uint32 size, byte *data, uint32 version) :
|
|
_size(size),
|
|
_data(data),
|
|
_version(version) {
|
|
}
|
|
|
|
StateProvider::ResourceTreeState::~ResourceTreeState() {
|
|
free(_data);
|
|
}
|
|
|
|
StateProvider::~StateProvider() {
|
|
clear();
|
|
}
|
|
|
|
void StateProvider::clear() {
|
|
for (ResourceTreeStateMap::iterator it = _stateStore.begin(); it != _stateStore.end(); it++) {
|
|
delete it->_value;
|
|
}
|
|
_stateStore.clear();
|
|
}
|
|
|
|
void StateProvider::restoreLevelState(Resources::Level *level) {
|
|
Common::String storeKey = level->getName();
|
|
|
|
restoreResourceTreeState(storeKey, level, false);
|
|
}
|
|
|
|
void StateProvider::restoreCurrentLevelState(Resources::Level *level) {
|
|
restoreResourceTreeState("Current", level, true);
|
|
}
|
|
|
|
void StateProvider::restoreLocationState(Resources::Level *level, Resources::Location *location) {
|
|
Common::String storeKey = level->getName() + location->getName();
|
|
|
|
restoreResourceTreeState(storeKey, location, false);
|
|
}
|
|
|
|
void StateProvider::restoreCurrentLocationState(Resources::Level *level, Resources::Location *location) {
|
|
restoreResourceTreeState("CurrentCurrent", location, true);
|
|
}
|
|
|
|
void StateProvider::restoreGlobalState(Resources::Level *level) {
|
|
restoreResourceTreeState("CurrentGlobal", level, true);
|
|
}
|
|
|
|
void StateProvider::restoreResourceTreeState(const Common::String &storeKey, Resources::Object *root, bool current) {
|
|
if (_stateStore.contains(storeKey)) {
|
|
ResourceTreeState *state = _stateStore[storeKey];
|
|
|
|
Common::MemoryReadStream stream(state->getData(), state->getSize(), DisposeAfterUse::NO);
|
|
readResourceTree(root, &stream, current, state->getVersion());
|
|
}
|
|
}
|
|
|
|
void StateProvider::readResourceTree(Resources::Object *resource, Common::SeekableReadStream *stream, bool current, uint32 version) {
|
|
// Read the resource to the source stream
|
|
/* byte type = */ stream->readByte();
|
|
/* byte subType = */ stream->readByte();
|
|
uint32 size = stream->readUint32LE();
|
|
|
|
if (size > 0) {
|
|
Common::SeekableReadStream *resourceStream = stream->readStream(size);
|
|
ResourceSerializer serializer(resourceStream, nullptr, version);
|
|
|
|
// Deserialize the resource state from stream
|
|
if (current) {
|
|
resource->saveLoadCurrent(&serializer);
|
|
} else {
|
|
resource->saveLoad(&serializer);
|
|
}
|
|
|
|
delete resourceStream;
|
|
}
|
|
|
|
// Deserialize the resource children
|
|
Common::Array<Resources::Object *> children = resource->listChildren<Resources::Object>();
|
|
for (uint i = 0; i < children.size(); i++) {
|
|
readResourceTree(children[i], stream, current, version);
|
|
}
|
|
}
|
|
|
|
void StateProvider::saveLevelState(Resources::Level *level) {
|
|
Common::String storeKey = level->getName();
|
|
|
|
saveResourceTreeState(storeKey, level, false);
|
|
}
|
|
|
|
void StateProvider::saveCurrentLevelState(Resources::Level *level) {
|
|
saveResourceTreeState("Current", level, true);
|
|
}
|
|
|
|
void StateProvider::saveLocationState(Resources::Level *level, Resources::Location *location) {
|
|
Common::String storeKey = level->getName() + location->getName();
|
|
|
|
saveResourceTreeState(storeKey, location, false);
|
|
}
|
|
|
|
void StateProvider::saveCurrentLocationState(Resources::Level *level, Resources::Location *location) {
|
|
saveResourceTreeState("CurrentCurrent", location, true);
|
|
}
|
|
|
|
void StateProvider::saveGlobalState(Resources::Level *level) {
|
|
saveResourceTreeState("CurrentGlobal", level, true);
|
|
}
|
|
|
|
void StateProvider::saveResourceTreeState(const Common::String &storeKey, Resources::Object *root, bool current) {
|
|
// Delete any previous data
|
|
if (_stateStore.contains(storeKey)) {
|
|
delete _stateStore[storeKey];
|
|
_stateStore.erase(storeKey);
|
|
}
|
|
|
|
// Write the tree state to memory
|
|
Common::MemoryWriteStreamDynamic stream(DisposeAfterUse::NO);
|
|
writeResourceTree(root, &stream, current);
|
|
|
|
// Add the state to the store
|
|
_stateStore[storeKey] = new ResourceTreeState(stream.size(), stream.getData(), kSaveVersion);
|
|
}
|
|
|
|
void StateProvider::writeResourceTree(Resources::Object *resource, Common::WriteStream *stream, bool current) {
|
|
// Explicit scope to control the lifespan of the memory stream
|
|
{
|
|
Common::MemoryWriteStreamDynamic resourceStream(DisposeAfterUse::YES);
|
|
ResourceSerializer serializer(nullptr, &resourceStream, kSaveVersion);
|
|
|
|
// Serialize the resource to a memory stream
|
|
if (current) {
|
|
resource->saveLoadCurrent(&serializer);
|
|
} else {
|
|
resource->saveLoad(&serializer);
|
|
}
|
|
|
|
// Write the resource to the target stream
|
|
stream->writeByte(resource->getType().get());
|
|
stream->writeByte(resource->getSubType());
|
|
stream->writeUint32LE(resourceStream.size());
|
|
stream->write(resourceStream.getData(), resourceStream.size());
|
|
}
|
|
|
|
// Serialize the resource children
|
|
Common::Array<Resources::Object *> children = resource->listChildren<Resources::Object>();
|
|
for (uint i = 0; i < children.size(); i++) {
|
|
writeResourceTree(children[i], stream, current);
|
|
}
|
|
}
|
|
|
|
void StateProvider::readStateFromStream(StateReadStream *stream, uint saveVersion) {
|
|
clear();
|
|
|
|
uint32 treeCount = stream->readUint32LE();
|
|
for (uint i = 0; i < treeCount; i++) {
|
|
// Read the store key
|
|
Common::String key = stream->readString();
|
|
|
|
// Each resource tree state needs to have its own version because
|
|
// some may never be made active again and serialized in the latest version.
|
|
// In that case they stay in a previous version.
|
|
uint treeVersion = 6;
|
|
if (saveVersion > 6) {
|
|
treeVersion = stream->readUint32LE();
|
|
}
|
|
|
|
// Read the data size
|
|
uint32 dataSize = stream->readUint32LE();
|
|
|
|
// Read the data
|
|
byte *data = (byte *) malloc(dataSize);
|
|
stream->read(data, dataSize);
|
|
|
|
_stateStore[key] = new ResourceTreeState(dataSize, data, treeVersion);
|
|
}
|
|
}
|
|
|
|
void StateProvider::writeStateToStream(Common::WriteStream *stream) {
|
|
stream->writeUint32LE(_stateStore.size());
|
|
|
|
for (ResourceTreeStateMap::iterator it = _stateStore.begin(); it != _stateStore.end(); it++) {
|
|
stream->writeUint32LE(it->_key.size());
|
|
stream->writeString(it->_key);
|
|
stream->writeUint32LE(it->_value->getVersion());
|
|
stream->writeUint32LE(it->_value->getSize());
|
|
stream->write(it->_value->getData(), it->_value->getSize());
|
|
}
|
|
}
|
|
|
|
ResourceSerializer::ResourceSerializer(Common::SeekableReadStream *in, Common::WriteStream *out, uint32 version) :
|
|
Common::Serializer(in, out) {
|
|
_version = version;
|
|
}
|
|
|
|
void ResourceSerializer::syncAsFloat(float &value) {
|
|
if (isLoading()) {
|
|
value = _loadStream->readFloatLE();
|
|
} else {
|
|
_saveStream->writeFloatLE(value);
|
|
}
|
|
}
|
|
|
|
void ResourceSerializer::syncAsVector3d(Math::Vector3d &value) {
|
|
syncAsFloat(value.x());
|
|
syncAsFloat(value.y());
|
|
syncAsFloat(value.z());
|
|
}
|
|
|
|
void ResourceSerializer::syncAsResourceReference(ResourceReference &reference) {
|
|
if (isLoading()) {
|
|
reference.loadFromStream(_loadStream);
|
|
} else {
|
|
reference.saveToStream(_saveStream);
|
|
}
|
|
}
|
|
|
|
void ResourceSerializer::syncAsString32(Common::String &string) {
|
|
if (isLoading()) {
|
|
string.clear();
|
|
|
|
uint32 length = _loadStream->readUint32LE();
|
|
for (uint i = 0; i < length; i++) {
|
|
char c = _loadStream->readByte();
|
|
string += c;
|
|
}
|
|
|
|
_bytesSynced += 4 + length;
|
|
} else {
|
|
_saveStream->writeUint32LE(string.size());
|
|
_saveStream->writeString(string);
|
|
_bytesSynced += 4 + string.size();
|
|
}
|
|
}
|
|
|
|
} // End of namespace Stark
|