/* 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 . * */ #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 children = resource->listChildren(); 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 children = resource->listChildren(); 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