mirror of
https://github.com/libretro/scummvm.git
synced 2024-12-30 05:34:00 +00:00
1042 lines
21 KiB
C++
1042 lines
21 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 2
|
|
* 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, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
*
|
|
*/
|
|
|
|
#include "common/util.h"
|
|
#include "common/endian.h"
|
|
#include "common/memstream.h"
|
|
#include "common/system.h"
|
|
#include "common/savefile.h"
|
|
|
|
#include "gob/gob.h"
|
|
#include "gob/save/savefile.h"
|
|
#include "gob/video.h"
|
|
#include "gob/inter.h"
|
|
#include "gob/variables.h"
|
|
|
|
namespace Gob {
|
|
|
|
static inline bool flushStream(Common::WriteStream &stream) {
|
|
// Flush and check for errors
|
|
|
|
if (!stream.flush())
|
|
return false;
|
|
if (stream.err())
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
SaveHeader::SaveHeader(uint32 type, uint32 version, uint32 size) {
|
|
_type = type;
|
|
_version = version;
|
|
_size = size;
|
|
}
|
|
|
|
bool SaveHeader::read(Common::ReadStream &stream) {
|
|
// Read the header and verify the global IDs
|
|
if (stream.readUint32BE() != kID1)
|
|
return false;
|
|
if (stream.readUint32BE() != kID2)
|
|
return false;
|
|
|
|
_type = stream.readUint32BE();
|
|
_version = stream.readUint32LE();
|
|
_size = stream.readUint32LE();
|
|
|
|
return !stream.err();
|
|
}
|
|
|
|
bool SaveHeader::verify(Common::ReadStream &stream) const {
|
|
// Compare the header with the stream's content
|
|
|
|
if (stream.readUint32BE() != kID1)
|
|
return false;
|
|
if (stream.readUint32BE() != kID2)
|
|
return false;
|
|
if (stream.readUint32BE() != _type)
|
|
return false;
|
|
if (stream.readUint32LE() != _version)
|
|
return false;
|
|
if (stream.readUint32LE() != _size)
|
|
return false;
|
|
|
|
return !stream.err();
|
|
}
|
|
|
|
bool SaveHeader::operator==(const SaveHeader &header) const {
|
|
return (_type == header._type) &&
|
|
(_version == header._version) &&
|
|
(_size == header._size);
|
|
}
|
|
|
|
bool SaveHeader::operator!=(const SaveHeader &header) const {
|
|
return !(*this == header);
|
|
}
|
|
|
|
bool SaveHeader::verifyReadSize(Common::ReadStream &stream) {
|
|
// Compare the header with the stream's content, expect for the size
|
|
|
|
if (stream.readUint32BE() != kID1)
|
|
return false;
|
|
if (stream.readUint32BE() != kID2)
|
|
return false;
|
|
if (stream.readUint32BE() != _type)
|
|
return false;
|
|
if (stream.readUint32LE() != _version)
|
|
return false;
|
|
|
|
// Read the size out of the stream instead
|
|
_size = stream.readUint32LE();
|
|
|
|
return !stream.err();
|
|
}
|
|
|
|
bool SaveHeader::write(Common::WriteStream &stream) const {
|
|
stream.writeUint32BE(kID1);
|
|
stream.writeUint32BE(kID2);
|
|
stream.writeUint32BE(_type);
|
|
stream.writeUint32LE(_version);
|
|
stream.writeUint32LE(_size);
|
|
|
|
return flushStream(stream);
|
|
}
|
|
|
|
uint32 SaveHeader::getType() const {
|
|
return _type;
|
|
}
|
|
|
|
uint32 SaveHeader::getVersion() const {
|
|
return _version;
|
|
}
|
|
|
|
uint32 SaveHeader::getSize() const {
|
|
return _size;
|
|
}
|
|
|
|
void SaveHeader::setType(uint32 type) {
|
|
_type = type;
|
|
}
|
|
|
|
void SaveHeader::setVersion(uint32 version) {
|
|
_version = version;
|
|
}
|
|
|
|
void SaveHeader::setSize(uint32 size) {
|
|
_size = size;
|
|
}
|
|
|
|
SavePart::SavePart() {
|
|
}
|
|
|
|
SavePart::~SavePart() {
|
|
}
|
|
|
|
uint32 SavePart::getSize() const {
|
|
// A part's size is the content's size plus the header's size
|
|
return _header.getSize() + SaveHeader::kSize;
|
|
}
|
|
|
|
SavePartMem::SavePartMem(uint32 size) : SavePart(), _size(size) {
|
|
_header.setType(kID);
|
|
_header.setVersion(kVersion);
|
|
_header.setSize(size);
|
|
|
|
_data = new byte[size];
|
|
}
|
|
|
|
SavePartMem::~SavePartMem() {
|
|
delete[] _data;
|
|
}
|
|
|
|
bool SavePartMem::read(Common::ReadStream &stream) {
|
|
if (!_header.verify(stream))
|
|
return false;
|
|
|
|
if (stream.read(_data, _size) != _size)
|
|
return false;
|
|
|
|
return !stream.err();
|
|
}
|
|
|
|
bool SavePartMem::write(Common::WriteStream &stream) const {
|
|
if (!_header.write(stream))
|
|
return false;
|
|
|
|
if (stream.write(_data, _size) != _size)
|
|
return false;
|
|
|
|
return flushStream(stream);
|
|
}
|
|
|
|
bool SavePartMem::readFrom(const byte *data, uint32 offset, uint32 size) {
|
|
if ((offset + size) > _size)
|
|
return false;
|
|
|
|
memcpy(_data + offset, data, size);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool SavePartMem::writeInto(byte *data, uint32 offset, uint32 size) const {
|
|
if ((offset + size) > _size)
|
|
return false;
|
|
|
|
memcpy(data, _data + offset, size);
|
|
|
|
return true;
|
|
}
|
|
|
|
SavePartVars::SavePartVars(GobEngine *vm, uint32 size) : SavePart(), _size(size), _vm(vm) {
|
|
_header.setType(kID);
|
|
_header.setVersion(kVersion);
|
|
_header.setSize(size);
|
|
|
|
_data = new byte[size];
|
|
}
|
|
|
|
SavePartVars::~SavePartVars() {
|
|
delete[] _data;
|
|
}
|
|
|
|
bool SavePartVars::read(Common::ReadStream &stream) {
|
|
if (!_header.verify(stream))
|
|
return false;
|
|
|
|
if (stream.read(_data, _size) != _size)
|
|
return false;
|
|
|
|
return !stream.err();
|
|
}
|
|
|
|
bool SavePartVars::write(Common::WriteStream &stream) const {
|
|
if (!_header.write(stream))
|
|
return false;
|
|
|
|
if (stream.write(_data, _size) != _size)
|
|
return false;
|
|
|
|
return flushStream(stream);
|
|
}
|
|
|
|
bool SavePartVars::readFrom(uint32 var, uint32 offset, uint32 size) {
|
|
if (!_vm->_inter->_variables)
|
|
return false;
|
|
|
|
if ((offset + size) > _size)
|
|
return false;
|
|
|
|
// Get raw variables
|
|
return _vm->_inter->_variables->copyTo(var, _data + offset, size);
|
|
}
|
|
|
|
bool SavePartVars::readFromRaw(const byte *data, uint32 size) {
|
|
if (size != _size)
|
|
return false;
|
|
|
|
memcpy(_data, data, size);
|
|
return true;
|
|
}
|
|
|
|
bool SavePartVars::writeInto(uint32 var, uint32 offset, uint32 size) const {
|
|
if (!_vm->_inter->_variables)
|
|
return false;
|
|
|
|
if ((offset + size) > _size)
|
|
return false;
|
|
|
|
// Write raw variables
|
|
if (!_vm->_inter->_variables->copyFrom(var, _data + offset, size))
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
SavePartSprite::SavePartSprite(uint32 width, uint32 height, bool trueColor) {
|
|
assert((width > 0) && (height > 0));
|
|
|
|
_width = width;
|
|
_height = height;
|
|
|
|
_oldFormat = false;
|
|
_trueColor = trueColor;
|
|
|
|
_header.setType(kID);
|
|
_header.setVersion(kVersion);
|
|
|
|
_spriteSize = _width * _height;
|
|
if (_trueColor)
|
|
_spriteSize *= 3;
|
|
|
|
// width + height + color + sprite + palette
|
|
_header.setSize(4 + 4 + 1 + _spriteSize + 768);
|
|
|
|
_dataSprite = new byte[_spriteSize];
|
|
_dataPalette = new byte[768];
|
|
|
|
memset(_dataSprite, 0, _spriteSize);
|
|
memset(_dataPalette, 0, 768);
|
|
}
|
|
|
|
SavePartSprite::~SavePartSprite() {
|
|
delete[] _dataSprite;
|
|
delete[] _dataPalette;
|
|
}
|
|
|
|
bool SavePartSprite::read(Common::ReadStream &stream) {
|
|
SaveHeader header;
|
|
header.read(stream);
|
|
|
|
if (_header != header) {
|
|
if (!_trueColor) {
|
|
// Header validation failed, trying again with the old version
|
|
|
|
_header.setVersion(1);
|
|
_header.setSize(_header.getSize() - 1);
|
|
|
|
if (_header != header)
|
|
// Nope, isn't it either
|
|
return false;
|
|
|
|
_oldFormat = true;
|
|
|
|
_header.setVersion(kVersion);
|
|
_header.setSize(_header.getSize() + 1);
|
|
|
|
} else
|
|
return false;
|
|
}
|
|
|
|
// The sprite's dimensions have to fit
|
|
if (stream.readUint32LE() != _width)
|
|
return false;
|
|
if (stream.readUint32LE() != _height)
|
|
return false;
|
|
|
|
// If it's in the current format, the true color flag has to be the same too
|
|
if (!_oldFormat)
|
|
if ((stream.readByte() != 0) != _trueColor)
|
|
return false;
|
|
|
|
// Sprite data
|
|
if (stream.read(_dataSprite, _spriteSize) != _spriteSize)
|
|
return false;
|
|
|
|
// Palette data
|
|
if (stream.read(_dataPalette, 768) != 768)
|
|
return false;
|
|
|
|
return !stream.err();
|
|
}
|
|
|
|
bool SavePartSprite::write(Common::WriteStream &stream) const {
|
|
if (!_header.write(stream))
|
|
return false;
|
|
|
|
// The sprite's dimensions
|
|
stream.writeUint32LE(_width);
|
|
stream.writeUint32LE(_height);
|
|
stream.writeByte(_trueColor);
|
|
|
|
// Sprite data
|
|
if (stream.write(_dataSprite, _spriteSize) != _spriteSize)
|
|
return false;
|
|
|
|
// Palette data
|
|
if (stream.write(_dataPalette, 768) != 768)
|
|
return false;
|
|
|
|
return flushStream(stream);
|
|
}
|
|
|
|
bool SavePartSprite::readPalette(const byte *palette) {
|
|
memcpy(_dataPalette, palette, 768);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool SavePartSprite::readSprite(const Surface &sprite) {
|
|
// The sprite's dimensions have to fit
|
|
if (((uint32)sprite.getWidth()) != _width)
|
|
return false;
|
|
if (((uint32)sprite.getHeight()) != _height)
|
|
return false;
|
|
|
|
if (_trueColor) {
|
|
if (sprite.getBPP() <= 1)
|
|
return false;
|
|
|
|
Graphics::PixelFormat pixelFormat = g_system->getScreenFormat();
|
|
|
|
byte *data = _dataSprite;
|
|
ConstPixel pixel = sprite.get();
|
|
for (uint32 i = 0; i < (_width * _height); i++, ++pixel, data += 3)
|
|
pixelFormat.colorToRGB(pixel.get(), data[0], data[1], data[2]);
|
|
|
|
} else {
|
|
if (sprite.getBPP() != 1)
|
|
return false;
|
|
|
|
memcpy(_dataSprite, sprite.getData(), _width * _height);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool SavePartSprite::readSpriteRaw(const byte *data, uint32 size) {
|
|
if (size != _spriteSize)
|
|
return false;
|
|
|
|
memcpy(_dataSprite, data, size);
|
|
return true;
|
|
}
|
|
|
|
bool SavePartSprite::writePalette(byte *palette) const {
|
|
memcpy(palette, _dataPalette, 768);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool SavePartSprite::writeSprite(Surface &sprite) const {
|
|
// The sprite's dimensions have to fit
|
|
if (((uint32)sprite.getWidth()) != _width)
|
|
return false;
|
|
if (((uint32)sprite.getHeight()) != _height)
|
|
return false;
|
|
|
|
if (_trueColor) {
|
|
if (sprite.getBPP() <= 1)
|
|
return false;
|
|
|
|
Graphics::PixelFormat pixelFormat = g_system->getScreenFormat();
|
|
|
|
const byte *data = _dataSprite;
|
|
Pixel pixel = sprite.get();
|
|
for (uint32 i = 0; i < (_width * _height); i++, ++pixel, data += 3)
|
|
pixel.set(pixelFormat.RGBToColor(data[0], data[1], data[2]));
|
|
|
|
} else {
|
|
if (sprite.getBPP() != 1)
|
|
return false;
|
|
|
|
memcpy(sprite.getData(), _dataSprite, _spriteSize);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
SavePartInfo::SavePartInfo(uint32 descMaxLength, uint32 gameID,
|
|
uint32 gameVersion, byte endian, uint32 varCount) {
|
|
|
|
_header.setType(kID);
|
|
_header.setVersion(kVersion);
|
|
// descMaxLength + gameID + gameVersion + endian + varCount
|
|
_header.setSize(descMaxLength + 4 + 4 + 4 + 1 + 4);
|
|
|
|
_descMaxLength = descMaxLength;
|
|
_gameID = gameID;
|
|
_gameVersion = gameVersion;
|
|
_endian = endian;
|
|
_varCount = varCount;
|
|
|
|
_desc = new char[_descMaxLength + 1];
|
|
memset(_desc, 0, _descMaxLength + 1);
|
|
}
|
|
|
|
SavePartInfo::~SavePartInfo() {
|
|
delete[] _desc;
|
|
}
|
|
|
|
const char *SavePartInfo::getDesc() const {
|
|
return _desc;
|
|
}
|
|
|
|
uint32 SavePartInfo::getDescMaxLength() const {
|
|
return _descMaxLength;
|
|
}
|
|
|
|
void SavePartInfo::setVarCount(uint32 varCount) {
|
|
_varCount = varCount;
|
|
}
|
|
|
|
void SavePartInfo::setDesc(const char *desc) {
|
|
if (!desc) {
|
|
memset(_desc, 0, _descMaxLength + 1);
|
|
return;
|
|
}
|
|
|
|
uint32 n = MIN<uint32>(strlen(desc), _descMaxLength);
|
|
|
|
// Copy the description and fill with 0
|
|
memcpy(_desc, desc, n);
|
|
memset(_desc + n, 0, _descMaxLength + 1 - n);
|
|
}
|
|
|
|
void SavePartInfo::setDesc(const byte *desc, uint32 size) {
|
|
if (!desc || !size) {
|
|
memset(_desc, 0, _descMaxLength + 1);
|
|
return;
|
|
}
|
|
|
|
uint32 n = MIN<uint32>(size, _descMaxLength);
|
|
memcpy(_desc, desc, n);
|
|
memset(_desc + n, 0, _descMaxLength + 1 - n);
|
|
}
|
|
|
|
bool SavePartInfo::read(Common::ReadStream &stream) {
|
|
if (!_header.verify(stream))
|
|
return false;
|
|
|
|
if (stream.readUint32LE() != _gameID)
|
|
return false;
|
|
if (stream.readUint32LE() != _gameVersion)
|
|
return false;
|
|
if (stream.readByte() != _endian)
|
|
return false;
|
|
if (stream.readUint32LE() != _varCount)
|
|
return false;
|
|
if (stream.readUint32LE() != _descMaxLength)
|
|
return false;
|
|
|
|
if (stream.read(_desc, _descMaxLength) != _descMaxLength)
|
|
return false;
|
|
|
|
_desc[_descMaxLength] = 0;
|
|
|
|
return !stream.err();
|
|
}
|
|
|
|
bool SavePartInfo::write(Common::WriteStream &stream) const {
|
|
if (!_header.write(stream))
|
|
return false;
|
|
|
|
stream.writeUint32LE(_gameID);
|
|
stream.writeUint32LE(_gameVersion);
|
|
stream.writeByte(_endian);
|
|
stream.writeUint32LE(_varCount);
|
|
stream.writeUint32LE(_descMaxLength);
|
|
|
|
if (stream.write(_desc, _descMaxLength) != _descMaxLength)
|
|
return false;
|
|
|
|
return flushStream(stream);
|
|
}
|
|
|
|
SaveContainer::Part::Part(uint32 s) {
|
|
size = s;
|
|
data = new byte[size];
|
|
}
|
|
|
|
SaveContainer::Part::~Part() {
|
|
delete[] data;
|
|
}
|
|
|
|
Common::WriteStream *SaveContainer::Part::createWriteStream() {
|
|
return new Common::MemoryWriteStream(data, size);
|
|
}
|
|
|
|
Common::ReadStream *SaveContainer::Part::createReadStream() const {
|
|
return new Common::MemoryReadStream(data, size);
|
|
}
|
|
|
|
SaveContainer::SaveContainer(uint32 partCount, uint32 slot) {
|
|
assert(partCount > 0);
|
|
|
|
_slot = slot;
|
|
_partCount = partCount;
|
|
|
|
_parts.resize(partCount);
|
|
for (PartIterator it = _parts.begin(); it != _parts.end(); ++it)
|
|
*it = 0;
|
|
|
|
_header.setType(kID);
|
|
_header.setVersion(kVersion);
|
|
_header.setSize(4); // uint32 # of parts
|
|
}
|
|
|
|
SaveContainer::~SaveContainer() {
|
|
clear();
|
|
}
|
|
|
|
uint32 SaveContainer::getSlot() const {
|
|
return _slot;
|
|
}
|
|
|
|
uint32 SaveContainer::getSize() const {
|
|
return _header.getSize() + SaveHeader::kSize;
|
|
}
|
|
|
|
bool SaveContainer::hasAllParts() const {
|
|
for (PartConstIterator it = _parts.begin(); it != _parts.end(); ++it)
|
|
if (!*it)
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
uint32 SaveContainer::calcSize() const {
|
|
uint32 size = 4; // uint32 # of parts
|
|
|
|
for (PartConstIterator it = _parts.begin(); it != _parts.end(); ++it)
|
|
if (*it)
|
|
// uint32 part size
|
|
size += (*it)->size + 4;
|
|
|
|
return size;
|
|
}
|
|
|
|
void SaveContainer::clear() {
|
|
for (PartIterator it = _parts.begin(); it != _parts.end(); ++it) {
|
|
Part *&p = *it;
|
|
|
|
delete p;
|
|
p = 0;
|
|
}
|
|
}
|
|
|
|
bool SaveContainer::writePart(uint32 partN, const SavePart *part) {
|
|
// Sanity checks
|
|
if (!part)
|
|
return false;
|
|
if (partN >= _partCount)
|
|
return false;
|
|
|
|
Part *&p = _parts[partN];
|
|
|
|
delete p;
|
|
// Create the part
|
|
p = new Part(part->getSize());
|
|
|
|
Common::WriteStream *pStream = p->createWriteStream();
|
|
|
|
// Write
|
|
if (!part->write(*pStream)) {
|
|
delete p;
|
|
p = 0;
|
|
|
|
delete pStream;
|
|
return false;
|
|
}
|
|
|
|
delete pStream;
|
|
|
|
// Update size
|
|
_header.setSize(calcSize());
|
|
|
|
return true;
|
|
}
|
|
|
|
bool SaveContainer::readPart(uint32 partN, SavePart *part) const {
|
|
// Sanity checks
|
|
if (!part)
|
|
return false;
|
|
if (partN >= _partCount)
|
|
return false;
|
|
|
|
const Part * const &p = _parts[partN];
|
|
|
|
// Check if the part actually exists
|
|
if (!p)
|
|
return false;
|
|
|
|
Common::ReadStream *pStream = p->createReadStream();
|
|
|
|
// Read
|
|
if (!part->read(*pStream)) {
|
|
delete pStream;
|
|
return false;
|
|
}
|
|
|
|
delete pStream;
|
|
return true;
|
|
}
|
|
|
|
bool SaveContainer::readPartHeader(uint32 partN, SaveHeader *header) const {
|
|
// Sanity checks
|
|
if (!header)
|
|
return false;
|
|
if (partN >= _partCount)
|
|
return false;
|
|
|
|
const Part * const &p = _parts[partN];
|
|
|
|
// Check if the part actually exists
|
|
if (!p)
|
|
return false;
|
|
|
|
Common::ReadStream *pStream = p->createReadStream();
|
|
|
|
// Read
|
|
if (!header->read(*pStream)) {
|
|
delete pStream;
|
|
return false;
|
|
}
|
|
|
|
delete pStream;
|
|
return true;
|
|
}
|
|
|
|
bool SaveContainer::read(Common::ReadStream &stream) {
|
|
// Verify the header and get the container's size
|
|
if (!_header.verifyReadSize(stream))
|
|
return false;
|
|
|
|
// The part count has to be correct
|
|
if (stream.readUint32LE() != _partCount)
|
|
return false;
|
|
|
|
// Iterate over all parts
|
|
for (PartIterator it = _parts.begin(); it != _parts.end(); ++it) {
|
|
// Read the size
|
|
uint32 size = stream.readUint32LE();
|
|
|
|
if (stream.err()) {
|
|
clear();
|
|
return false;
|
|
}
|
|
|
|
Part *&p = *it;
|
|
|
|
delete p;
|
|
// Create a suitable part
|
|
p = new Part(size);
|
|
}
|
|
|
|
// Update size
|
|
_header.setSize(calcSize());
|
|
|
|
// Iterate over all parts
|
|
for (PartIterator it = _parts.begin(); it != _parts.end(); ++it) {
|
|
Part *&p = *it;
|
|
|
|
// Read the part
|
|
if (stream.read(p->data, p->size) != p->size) {
|
|
clear();
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return !stream.err();
|
|
}
|
|
|
|
bool SaveContainer::write(Common::WriteStream &stream) const {
|
|
// Write the header
|
|
if (!_header.write(stream))
|
|
return false;
|
|
|
|
// Write the part count
|
|
stream.writeUint32LE(_partCount);
|
|
|
|
// Iterate over all parts
|
|
for (PartConstIterator it = _parts.begin(); it != _parts.end(); ++it) {
|
|
// Part doesn't actually exist => error
|
|
if (!*it)
|
|
return false;
|
|
|
|
// Write the part's size
|
|
stream.writeUint32LE((*it)->size);
|
|
}
|
|
|
|
if (!flushStream(stream))
|
|
return false;
|
|
|
|
// Iterate over all parts
|
|
for (PartConstIterator it = _parts.begin(); it != _parts.end(); ++it) {
|
|
Part * const &p = *it;
|
|
|
|
// Write the part
|
|
if (stream.write(p->data, p->size) != p->size)
|
|
return false;
|
|
}
|
|
|
|
return flushStream(stream);
|
|
}
|
|
|
|
Common::Array<SaveContainer::PartInfo> *SaveContainer::getPartsInfo(Common::SeekableReadStream &stream) {
|
|
Common::Array<PartInfo> *parts = 0;
|
|
|
|
// Remember the stream's position to seek back to
|
|
uint32 startPos = stream.pos();
|
|
|
|
SaveHeader header;
|
|
|
|
header.setType(kID);
|
|
header.setVersion(kVersion);
|
|
|
|
// Verify the header
|
|
if (!header.verifyReadSize(stream)) {
|
|
// Seek back
|
|
stream.seek(startPos);
|
|
return 0;
|
|
}
|
|
|
|
// Read the part count
|
|
uint32 partCount = stream.readUint32LE();
|
|
|
|
// Create a part information array
|
|
parts = new Common::Array<PartInfo>;
|
|
parts->resize(partCount);
|
|
|
|
// Read all part sizes
|
|
for (uint32 i = 0; i < partCount; i++)
|
|
(*parts)[i].size = stream.readUint32LE();
|
|
|
|
// Iterate over all parts
|
|
for (uint32 i = 0; i < partCount; i++) {
|
|
// The part's offset (from the starting point of the stream)
|
|
(*parts)[i].offset = stream.pos() - startPos;
|
|
|
|
SaveHeader partHeader;
|
|
|
|
// Read the header
|
|
if (!partHeader.read(stream)) {
|
|
// Seek back
|
|
stream.seek(startPos);
|
|
delete parts;
|
|
return 0;
|
|
}
|
|
|
|
// Fill in the ID
|
|
(*parts)[i].id = partHeader.getType();
|
|
|
|
// Skip the part's content
|
|
stream.skip(partHeader.getSize());
|
|
}
|
|
|
|
if (stream.err()) {
|
|
delete parts;
|
|
parts = 0;
|
|
}
|
|
|
|
// Seek back
|
|
stream.seek(startPos);
|
|
|
|
return parts;
|
|
}
|
|
|
|
bool SaveContainer::isSave(Common::SeekableReadStream &stream) {
|
|
// Remember the stream's position to seek back to
|
|
uint32 startPos = stream.pos();
|
|
|
|
SaveHeader header;
|
|
|
|
header.setType(kID);
|
|
header.setVersion(kVersion);
|
|
|
|
bool result = header.verifyReadSize(stream);
|
|
|
|
// Seek back
|
|
stream.seek(startPos);
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
SaveReader::SaveReader(uint32 partCount, uint32 slot, const Common::String &fileName) :
|
|
SaveContainer(partCount, slot), _fileName(fileName) {
|
|
|
|
_stream = 0;
|
|
|
|
_loaded = false;
|
|
}
|
|
|
|
SaveReader::SaveReader(uint32 partCount, uint32 slot, Common::SeekableReadStream &stream) :
|
|
SaveContainer(partCount, slot) {
|
|
|
|
_stream = &stream;
|
|
|
|
_loaded = false;
|
|
}
|
|
|
|
SaveReader::~SaveReader() {
|
|
}
|
|
|
|
// Open the save and read it
|
|
bool SaveReader::load() {
|
|
|
|
Common::InSaveFile *in = 0;
|
|
Common::SeekableReadStream *stream;
|
|
|
|
if (!_fileName.empty()) {
|
|
in = openSave();
|
|
|
|
if (!in)
|
|
return false;
|
|
|
|
stream = in;
|
|
} else if (_stream)
|
|
stream = _stream;
|
|
else
|
|
return false;
|
|
|
|
if (!SaveContainer::read(*stream)) {
|
|
delete in;
|
|
return false;
|
|
}
|
|
|
|
delete in;
|
|
_loaded = true;
|
|
return true;
|
|
}
|
|
|
|
bool SaveReader::readPartHeader(uint32 partN, SaveHeader *header) const {
|
|
// The save has to be loaded
|
|
if (!_loaded)
|
|
return false;
|
|
|
|
return SaveContainer::readPartHeader(partN, header);
|
|
}
|
|
|
|
bool SaveReader::readPart(uint32 partN, SavePart *part) const {
|
|
// The save has to be loaded
|
|
if (!_loaded)
|
|
return false;
|
|
|
|
if (!SaveContainer::readPart(partN, part))
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
Common::InSaveFile *SaveReader::openSave(const Common::String &fileName) {
|
|
if (fileName.empty())
|
|
return 0;
|
|
|
|
Common::SaveFileManager *saveMan = g_system->getSavefileManager();
|
|
return saveMan->openForLoading(fileName);
|
|
}
|
|
|
|
Common::InSaveFile *SaveReader::openSave() {
|
|
return openSave(_fileName);
|
|
}
|
|
|
|
bool SaveReader::getInfo(Common::SeekableReadStream &stream, SavePartInfo &info) {
|
|
// Remeber the stream's starting position to seek back to
|
|
uint32 startPos = stream.pos();
|
|
|
|
// Get parts' basic information
|
|
Common::Array<SaveContainer::PartInfo> *partsInfo = getPartsInfo(stream);
|
|
|
|
// No parts => fail
|
|
if (!partsInfo) {
|
|
stream.seek(startPos);
|
|
return false;
|
|
}
|
|
|
|
bool result = false;
|
|
// Iterate over all parts
|
|
for (Common::Array<SaveContainer::PartInfo>::iterator it = partsInfo->begin();
|
|
it != partsInfo->end(); ++it) {
|
|
|
|
// Check for the info part
|
|
if (it->id == SavePartInfo::kID) {
|
|
if (!stream.seek(it->offset))
|
|
break;
|
|
|
|
// Read it
|
|
result = info.read(stream);
|
|
break;
|
|
}
|
|
}
|
|
|
|
stream.seek(startPos);
|
|
|
|
delete partsInfo;
|
|
return result;
|
|
}
|
|
|
|
bool SaveReader::getInfo(const Common::String &fileName, SavePartInfo &info) {
|
|
Common::InSaveFile *in = openSave(fileName);
|
|
|
|
if (!in)
|
|
return false;
|
|
|
|
bool result = getInfo(*in, info);
|
|
|
|
delete in;
|
|
|
|
return result;
|
|
}
|
|
|
|
SaveWriter::SaveWriter(uint32 partCount, uint32 slot) :
|
|
SaveContainer(partCount, slot) {
|
|
}
|
|
|
|
SaveWriter::SaveWriter(uint32 partCount, uint32 slot, const Common::String &fileName) :
|
|
SaveContainer(partCount, slot), _fileName(fileName) {
|
|
}
|
|
|
|
SaveWriter::~SaveWriter() {
|
|
}
|
|
|
|
bool SaveWriter::writePart(uint32 partN, const SavePart *part) {
|
|
// Write the part
|
|
if (!SaveContainer::writePart(partN, part))
|
|
return false;
|
|
|
|
// If all parts have been written, save and clear
|
|
if (hasAllParts() && canSave()) {
|
|
if (save()) {
|
|
clear();
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool SaveWriter::save(Common::WriteStream &stream) {
|
|
return SaveContainer::write(stream);
|
|
}
|
|
|
|
bool SaveWriter::save() {
|
|
Common::OutSaveFile *out = openSave();
|
|
|
|
if (!out)
|
|
return false;
|
|
|
|
bool success = save(*out);
|
|
|
|
delete out;
|
|
|
|
return success;
|
|
}
|
|
|
|
bool SaveWriter::canSave() const {
|
|
return (!_fileName.empty());
|
|
}
|
|
|
|
Common::OutSaveFile *SaveWriter::openSave(const Common::String &fileName) {
|
|
if (fileName.empty())
|
|
return 0;
|
|
|
|
Common::SaveFileManager *saveMan = g_system->getSavefileManager();
|
|
return saveMan->openForSaving(fileName);
|
|
}
|
|
|
|
Common::OutSaveFile *SaveWriter::openSave() {
|
|
return openSave(_fileName);
|
|
}
|
|
|
|
} // End of namespace Gob
|