scummvm/engines/gob/save/saveconverter.cpp
2013-04-15 18:25:01 +02:00

437 lines
8.9 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/endian.h"
#include "common/memstream.h"
#include "common/savefile.h"
#include "gob/gob.h"
#include "gob/save/saveconverter.h"
#include "gob/save/savefile.h"
#include "gob/save/savehandler.h"
namespace Gob {
SaveConverter::SaveConverter(GobEngine *vm, const Common::String &fileName)
: _vm(vm), _fileName(fileName) {
_data = 0;
_stream = 0;
}
SaveConverter::~SaveConverter() {
delete _stream;
delete[] _data;
}
void SaveConverter::clear() {
delete[] _data;
delete _stream;
_data = 0;
_stream = 0;
}
void SaveConverter::setFileName(const Common::String &fileName) {
clear();
_fileName = fileName;
}
Common::InSaveFile *SaveConverter::openSave() const {
if (_fileName.empty())
return 0;
Common::SaveFileManager *saveMan = g_system->getSavefileManager();
return saveMan->openForLoading(_fileName);
}
void SaveConverter::displayWarning() const {
warning("Old save format detected, trying to convert. If this does not work, your "
"save is broken and can't be used anymore. Sorry for the inconvenience");
}
char *SaveConverter::getDescription(const Common::String &fileName) {
setFileName(fileName);
return getDescription();
}
char *SaveConverter::getDescription() const {
Common::InSaveFile *save;
// Test if it's an old savd
if (!isOldSave(&save) || !save)
return 0;
char *desc = getDescription(*save);
delete save;
return desc;
}
uint32 SaveConverter::getActualSize(Common::InSaveFile **save) const {
Common::InSaveFile *saveFile = openSave();
if (!saveFile)
return false;
// Is it a valid new save?
if (SaveContainer::isSave(*saveFile)) {
delete saveFile;
return false;
}
int32 saveSize = saveFile->size();
if (saveSize <= 0) {
delete saveFile;
return 0;
}
if (save)
*save = saveFile;
else
delete saveFile;
return saveSize;
}
bool SaveConverter::swapDataEndian(byte *data, const byte *sizes, uint32 count) {
if (!data || !sizes || (count == 0))
return false;
while (count-- > 0) {
if (*sizes == 3) // 32bit value (3 additional bytes)
WRITE_UINT32(data, SWAP_BYTES_32(READ_UINT32(data)));
else if (*sizes == 1) // 16bit value (1 additional byte)
WRITE_UINT16(data, SWAP_BYTES_16(READ_UINT16(data)));
else if (*sizes != 0) // else, it has to be an 8bit value
return false;
count -= *sizes;
data += *sizes + 1;
sizes += *sizes + 1;
}
return true;
}
SavePartInfo *SaveConverter::readInfo(Common::SeekableReadStream &stream,
uint32 descLength, bool hasSizes) const {
uint32 varSize = SaveHandler::getVarSize(_vm);
if (varSize == 0)
return 0;
char *desc = getDescription(stream);
if (!desc)
return 0;
// If it has sizes, skip them
if (hasSizes)
if (!stream.skip(descLength)) {
delete[] desc;
return 0;
}
SavePartInfo *info = new SavePartInfo(descLength, (uint32) _vm->getGameType(),
0, _vm->getEndianness(), varSize);
info->setDesc(desc);
delete[] desc;
return info;
}
byte *SaveConverter::readData(Common::SeekableReadStream &stream,
uint32 count, bool endian) const {
byte *data = new byte[count];
// Read variable data
if (stream.read(data, count) != count) {
delete[] data;
return 0;
}
/* Check the endianness. The old save data was always written
* as little endian, so we might need to swap the bytes. */
if (endian && (_vm->getEndianness() == kEndiannessBE)) {
// Big endian => swapping needed
// Read variable sizes
byte *sizes = new byte[count];
if (stream.read(sizes, count) != count) {
delete[] data;
delete[] sizes;
return 0;
}
// Swap bytes
if (!swapDataEndian(data, sizes, count)) {
delete[] data;
delete[] sizes;
return 0;
}
delete[] sizes;
} else {
// Little endian => just skip the sizes part
if (!stream.skip(count)) {
delete[] data;
return 0;
}
}
return data;
}
SavePartVars *SaveConverter::readVars(Common::SeekableReadStream &stream,
uint32 count, bool endian) const {
byte *data = readData(stream, count, endian);
if (!data)
return 0;
SavePartVars *vars = new SavePartVars(_vm, count);
// Read variables into part
if (!vars->readFromRaw(data, count)) {
delete[] data;
delete vars;
return 0;
}
delete[] data;
return vars;
}
SavePartMem *SaveConverter::readMem(Common::SeekableReadStream &stream,
uint32 count, bool endian) const {
byte *data = readData(stream, count, endian);
if (!data)
return 0;
SavePartMem *mem = new SavePartMem(count);
// Read mem into part
if (!mem->readFrom(data, 0, count)) {
delete[] data;
delete mem;
return 0;
}
delete[] data;
return mem;
}
SavePartSprite *SaveConverter::readSprite(Common::SeekableReadStream &stream,
uint32 width, uint32 height, bool palette) const {
assert((width > 0) && (height > 0));
uint32 spriteSize = width * height;
byte pal[768];
if (palette)
if (stream.read(pal, 768) != 768)
return 0;
byte *data = new byte[spriteSize];
// Read variable data
if (stream.read(data, spriteSize) != spriteSize) {
delete[] data;
return 0;
}
SavePartSprite *sprite = new SavePartSprite(width, height);
if (!sprite->readSpriteRaw(data, spriteSize)) {
delete[] data;
delete sprite;
return 0;
}
delete[] data;
if (palette)
if (!sprite->readPalette(pal))
return 0;
return sprite;
}
bool SaveConverter::createStream(SaveWriter &writer) {
// Allocate memory for the internal new save data
uint32 contSize = writer.getSize();
_data = new byte[contSize];
// Save the newly created new save data
Common::MemoryWriteStream writeStream(_data, contSize);
if (!writer.save(writeStream))
return false;
// Create a reading stream upon that new save data
_stream = new Common::MemoryReadStream(_data, contSize);
return true;
}
/* Stream functions. If the new save data stream is available, redirect the stream
* operations to that stream. Normal stream error behavior if not. */
bool SaveConverter::err() const {
if (!_data || !_stream)
return true;
return _stream->err();
}
void SaveConverter::clearErr() {
if (!_data || !_stream)
return;
_stream->clearErr();
}
bool SaveConverter::eos() const {
if (!_data || !_stream)
return true;
return _stream->eos();
}
uint32 SaveConverter::read(void *dataPtr, uint32 dataSize) {
if (!_data || !_stream)
return 0;
return _stream->read(dataPtr, dataSize);
}
int32 SaveConverter::pos() const {
if (!_data || !_stream)
return -1;
return _stream->pos();
}
int32 SaveConverter::size() const {
if (!_data || !_stream)
return -1;
return _stream->size();
}
bool SaveConverter::seek(int32 offset, int whence) {
if (!_data || !_stream)
return false;
return _stream->seek(offset, whence);
}
SaveConverter_Notes::SaveConverter_Notes(GobEngine *vm, uint32 notesSize,
const Common::String &fileName) : SaveConverter(vm, fileName) {
_size = notesSize;
}
SaveConverter_Notes::~SaveConverter_Notes() {
}
int SaveConverter_Notes::isOldSave(Common::InSaveFile **save) const {
if (_size == 0)
return 0;
uint32 saveSize = getActualSize(save);
if (saveSize == 0)
return 0;
// The size of the old save always follows that rule
if (saveSize == (_size * 2))
return 1;
// Not an old save, clean up
if (save) {
delete *save;
*save = 0;
}
return 0;
}
char *SaveConverter_Notes::getDescription(Common::SeekableReadStream &save) const {
return 0;
}
bool SaveConverter_Notes::loadFail(SavePartVars *vars, Common::InSaveFile *save) {
delete vars;
delete save;
clear();
return false;
}
// Loads the old save by constructing a new save containing the old save's data
bool SaveConverter_Notes::load() {
if (_size == 0)
return false;
Common::InSaveFile *save;
// Test if it's an old savd
if (!isOldSave(&save) || !save)
return false;
displayWarning();
SaveWriter writer(1, 0);
SavePartVars *vars = readVars(*save, _size, false);
if (!vars)
return loadFail(0, save);
// We don't need the save anymore
delete save;
// Write all parts
if (!writer.writePart(0, vars))
return loadFail(0, 0);
// We don't need this anymore
delete vars;
// Create the final read stream
if (!createStream(writer))
return loadFail(0, 0);
return true;
}
} // End of namespace Gob