Sven Hesse 9e338a170f GOB: Add support for saving true color sprites
Not exactly the "nice", since up-depthing the sprite data to 24bit
happens after it has been adapted to the current system's color
format, so we're going to gradually lose precision when the save
gets passed around different systems. Still, this is the least
headachy solution, I guess...

svn-id: r55383
2011-01-21 19:15:10 +00:00

1045 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.
*
* $URL$
* $Id$
*
*/
#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() != _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