scummvm/engines/gob/save/savefile.cpp
2011-05-12 01:16:22 +02:00

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