Initial commit

This commit is contained in:
2026-02-02 04:50:13 +01:00
commit 5b11698731
22592 changed files with 7677434 additions and 0 deletions

View File

@@ -0,0 +1,443 @@
/* 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 <http://www.gnu.org/licenses/>.
*
*
* This file is dual-licensed.
* In addition to the GPLv3 license mentioned above, this code is also
* licensed under LGPL 2.1. See LICENSES/COPYING.LGPL file for the
* full text of the license.
*
*/
#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 = nullptr;
_stream = nullptr;
}
SaveConverter::~SaveConverter() {
delete _stream;
delete[] _data;
}
void SaveConverter::clear() {
delete[] _data;
delete _stream;
_data = nullptr;
_stream = nullptr;
}
void SaveConverter::setFileName(const Common::String &fileName) {
clear();
_fileName = fileName;
}
Common::InSaveFile *SaveConverter::openSave() const {
if (_fileName.empty())
return nullptr;
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 nullptr;
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 nullptr;
char *desc = getDescription(stream);
if (!desc)
return nullptr;
// If it has sizes, skip them
if (hasSizes)
if (!stream.skip(descLength)) {
delete[] desc;
return nullptr;
}
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 nullptr;
}
/* 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 nullptr;
}
// Swap bytes
if (!swapDataEndian(data, sizes, count)) {
delete[] data;
delete[] sizes;
return nullptr;
}
delete[] sizes;
} else {
// Little endian => just skip the sizes part
if (!stream.skip(count)) {
delete[] data;
return nullptr;
}
}
return data;
}
SavePartVars *SaveConverter::readVars(Common::SeekableReadStream &stream,
uint32 count, bool endian) const {
byte *data = readData(stream, count, endian);
if (!data)
return nullptr;
SavePartVars *vars = new SavePartVars(_vm, count);
// Read variables into part
if (!vars->readFromRaw(data, 0, count)) {
delete[] data;
delete vars;
return nullptr;
}
delete[] data;
return vars;
}
SavePartMem *SaveConverter::readMem(Common::SeekableReadStream &stream,
uint32 count, bool endian) const {
byte *data = readData(stream, count, endian);
if (!data)
return nullptr;
SavePartMem *mem = new SavePartMem(count);
// Read mem into part
if (!mem->readFrom(data, 0, count)) {
delete[] data;
delete mem;
return nullptr;
}
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 nullptr;
byte *data = new byte[spriteSize];
// Read variable data
if (stream.read(data, spriteSize) != spriteSize) {
delete[] data;
return nullptr;
}
SavePartSprite *sprite = new SavePartSprite(width, height);
if (!sprite->readSpriteRaw(data, spriteSize)) {
delete[] data;
delete sprite;
return nullptr;
}
delete[] data;
if (palette)
if (!sprite->readPalette(pal)) {
delete sprite;
return nullptr;
}
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);
}
int64 SaveConverter::pos() const {
if (!_data || !_stream)
return -1;
return _stream->pos();
}
int64 SaveConverter::size() const {
if (!_data || !_stream)
return -1;
return _stream->size();
}
bool SaveConverter::seek(int64 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 = nullptr;
}
return 0;
}
char *SaveConverter_Notes::getDescription(Common::SeekableReadStream &save) const {
return nullptr;
}
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(nullptr, save);
// We don't need the save anymore
delete save;
// Write all parts
if (!writer.writePart(0, vars))
return loadFail(nullptr, nullptr);
// We don't need this anymore
delete vars;
// Create the final read stream
if (!createStream(writer))
return loadFail(nullptr, nullptr);
return true;
}
} // End of namespace Gob

View File

@@ -0,0 +1,189 @@
/* 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 <http://www.gnu.org/licenses/>.
*
*
* This file is dual-licensed.
* In addition to the GPLv3 license mentioned above, this code is also
* licensed under LGPL 2.1. See LICENSES/COPYING.LGPL file for the
* full text of the license.
*
*/
#ifndef GOB_SAVE_SAVECONVERTER_H
#define GOB_SAVE_SAVECONVERTER_H
#include "common/stream.h"
namespace Gob {
class GobEngine;
class SavePartInfo;
class SavePartVars;
class SavePartMem;
class SavePartSprite;
class SaveWriter;
/** A wrapping stream class for old saves. */
class SaveConverter : public Common::SeekableReadStream {
public:
SaveConverter(GobEngine *vm, const Common::String &fileName);
~SaveConverter() override;
/** Clear the converter. */
virtual void clear();
/** Set the filename on which to operate. */
virtual void setFileName(const Common::String &fileName);
/** Is it actually an old save? */
virtual int isOldSave(Common::InSaveFile **save = 0) const = 0;
/** Directly return the description without processing the whole save. */
virtual char *getDescription(Common::SeekableReadStream &save) const = 0;
/** Load the whole save. */
virtual bool load() = 0;
/** Set the name and return the description. */
char *getDescription(const Common::String &fileName);
/** Get the current fileName's description. */
char *getDescription() const;
// Stream
bool err() const override;
void clearErr() override;
// ReadStream
bool eos() const override;
uint32 read(void *dataPtr, uint32 dataSize) override;
// SeekableReadStream
int64 pos() const override;
int64 size() const override;
bool seek(int64 offset, int whence = SEEK_SET) override;
protected:
GobEngine *_vm;
Common::String _fileName;
byte *_data;
Common::SeekableReadStream *_stream;
Common::InSaveFile *openSave() const;
/** Write a warning to stdout to notify the user what's going on. */
virtual void displayWarning() const;
virtual uint32 getActualSize(Common::InSaveFile **save = 0) const;
SavePartInfo *readInfo(Common::SeekableReadStream &stream,
uint32 descLength, bool hasSizes = true) const;
SavePartVars *readVars(Common::SeekableReadStream &stream,
uint32 count, bool endian) const;
SavePartMem *readMem(Common::SeekableReadStream &stream,
uint32 count, bool endian) const;
SavePartSprite *readSprite(Common::SeekableReadStream &stream,
uint32 width, uint32 height, bool palette) const;
bool createStream(SaveWriter &writer);
/** Swap the endianness of the complete data area. */
static bool swapDataEndian(byte *data, const byte *sizes, uint32 count);
private:
byte *readData(Common::SeekableReadStream &stream,
uint32 count, bool endian) const;
};
/** A wrapper for old notes saves. */
class SaveConverter_Notes : public SaveConverter {
public:
SaveConverter_Notes(GobEngine *vm, uint32 notesSize, const Common::String &fileName = "");
~SaveConverter_Notes() override;
int isOldSave(Common::InSaveFile **save = 0) const override;
char *getDescription(Common::SeekableReadStream &save) const override;
bool load() override;
private:
uint32 _size;
bool loadFail(SavePartVars *vars, Common::InSaveFile *save);
};
/** A wrapper for old v2-style saves (Gobliins 2, Ween: The Prophecy and Bargon Attack). */
class SaveConverter_v2 : public SaveConverter {
public:
SaveConverter_v2(GobEngine *vm, const Common::String &fileName = "");
~SaveConverter_v2() override;
int isOldSave(Common::InSaveFile **save = 0) const override;
char *getDescription(Common::SeekableReadStream &save) const override;
bool load() override;
private:
static const uint32 kSlotCount = 15;
static const uint32 kSlotNameLength = 40;
bool loadFail(SavePartInfo *info, SavePartVars *vars,
Common::InSaveFile *save);
};
/** A wrapper for old v3-style saves (Goblins 3 and Lost in Time). */
class SaveConverter_v3 : public SaveConverter {
public:
SaveConverter_v3(GobEngine *vm, const Common::String &fileName = "");
~SaveConverter_v3() override;
int isOldSave(Common::InSaveFile **save = 0) const override;
char *getDescription(Common::SeekableReadStream &save) const override;
bool load() override;
private:
static const uint32 kSlotCount = 30;
static const uint32 kSlotNameLength = 40;
bool loadFail(SavePartInfo *info, SavePartVars *vars,
SavePartSprite *sprite, Common::InSaveFile *save);
void getScreenShotProps(int type,
bool &used, uint32 &width, uint32 &height);
};
/** A wrapper for old v4-style saves (Woodruff). */
class SaveConverter_v4 : public SaveConverter {
public:
SaveConverter_v4(GobEngine *vm, const Common::String &fileName = "");
~SaveConverter_v4() override;
int isOldSave(Common::InSaveFile **save = 0) const override;
char *getDescription(Common::SeekableReadStream &save) const override;
bool load() override;
private:
static const uint32 kSlotCount = 60;
static const uint32 kSlotNameLength = 40;
bool loadFail(SavePartInfo *info, SavePartVars *vars,
SavePartMem *props, Common::InSaveFile *save);
};
} // End of namespace Gob
#endif // GOB_SAVE_SAVECONVERTER_H

View File

@@ -0,0 +1,137 @@
/* 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 <http://www.gnu.org/licenses/>.
*
*
* This file is dual-licensed.
* In addition to the GPLv3 license mentioned above, this code is also
* licensed under LGPL 2.1. See LICENSES/COPYING.LGPL file for the
* full text of the license.
*
*/
#include "common/endian.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_v2::SaveConverter_v2(GobEngine *vm, const Common::String &fileName) :
SaveConverter(vm, fileName) {
}
SaveConverter_v2::~SaveConverter_v2() {
}
int SaveConverter_v2::isOldSave(Common::InSaveFile **save) const {
uint32 varSize = SaveHandler::getVarSize(_vm);
if (varSize == 0)
return 0;
uint32 saveSize = getActualSize(save);
if (saveSize == 0)
return 0;
// The size of the old save always follows that rule
if (saveSize == (varSize * 2 + kSlotNameLength * 2))
return 1;
// Not an old save, clean up
if (save) {
delete *save;
*save = nullptr;
}
return 0;
}
char *SaveConverter_v2::getDescription(Common::SeekableReadStream &save) const {
char *desc = new char[kSlotNameLength];
// Read the description
if (save.read(desc, kSlotNameLength) != kSlotNameLength) {
delete[] desc;
return nullptr;
}
return desc;
}
bool SaveConverter_v2::loadFail(SavePartInfo *info, SavePartVars *vars,
Common::InSaveFile *save) {
delete info;
delete vars;
delete save;
clear();
return false;
}
// Loads the old save by constructing a new save containing the old save's data
bool SaveConverter_v2::load() {
clear();
uint32 varSize = SaveHandler::getVarSize(_vm);
if (varSize == 0)
return false;
Common::InSaveFile *save;
// Test if it's an old savd
if (!isOldSave(&save) || !save)
return false;
displayWarning();
SaveWriter writer(2, 0);
SavePartInfo *info = readInfo(*save, kSlotNameLength);
if (!info)
return loadFail(nullptr, nullptr, save);
SavePartVars *vars = readVars(*save, varSize, true);
if (!vars)
return loadFail(info, nullptr, save);
// We don't need the save anymore
delete save;
// Write all parts
if (!writer.writePart(0, info))
return loadFail(info, vars, nullptr);
if (!writer.writePart(1, vars))
return loadFail(info, vars, nullptr);
// We don't need those anymore
delete info;
delete vars;
// Create the final read stream
if (!createStream(writer))
return loadFail(nullptr, nullptr, nullptr);
return true;
}
} // End of namespace Gob

View File

@@ -0,0 +1,190 @@
/* 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 <http://www.gnu.org/licenses/>.
*
*
* This file is dual-licensed.
* In addition to the GPLv3 license mentioned above, this code is also
* licensed under LGPL 2.1. See LICENSES/COPYING.LGPL file for the
* full text of the license.
*
*/
#include "common/endian.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_v3::SaveConverter_v3(GobEngine *vm, const Common::String &fileName) :
SaveConverter(vm, fileName) {
}
SaveConverter_v3::~SaveConverter_v3() {
}
int SaveConverter_v3::isOldSave(Common::InSaveFile **save) const {
uint32 varSize = SaveHandler::getVarSize(_vm);
if (varSize == 0)
return 0;
uint32 saveSize = getActualSize(save);
if (saveSize == 0)
return 0;
// The size of the old save always follows one of these rules
if (saveSize == (varSize * 2 + kSlotNameLength + 1000))
return 1; // No screenshot
if (saveSize == (varSize * 2 + kSlotNameLength + 1000 + 19968))
return 2; // Big screenshot, Goblins 3
if (saveSize == (varSize * 2 + kSlotNameLength + 1000 + 4768))
return 3; // Small screenshot, Lost in Time
// Not an old save, clean up
if (save) {
delete *save;
*save = nullptr;
}
return 0;
}
char *SaveConverter_v3::getDescription(Common::SeekableReadStream &save) const {
// The description starts at 1000
if (!save.seek(1000))
return nullptr;
char *desc = new char[kSlotNameLength];
// Read the description
if (save.read(desc, kSlotNameLength) != kSlotNameLength) {
delete[] desc;
return nullptr;
}
return desc;
}
bool SaveConverter_v3::loadFail(SavePartInfo *info, SavePartVars *vars,
SavePartSprite *sprite, Common::InSaveFile *save) {
delete info;
delete vars;
delete sprite;
delete save;
clear();
return false;
}
void SaveConverter_v3::getScreenShotProps(int type,
bool &used, uint32 &width, uint32 &height) {
switch (type) {
case 2:
used = true;
width = 120;
height = 160;
break;
case 3:
used = true;
width = 80;
height = 50;
break;
default:
used = false;
width = 0;
height = 0;
break;
}
}
// Loads the old save by constructing a new save containing the old save's data
bool SaveConverter_v3::load() {
clear();
uint32 varSize = SaveHandler::getVarSize(_vm);
if (varSize == 0)
return false;
Common::InSaveFile *save;
int type = isOldSave(&save);
// Test if it's an old savd
if ((type == 0) || !save)
return false;
displayWarning();
bool screenShot;
uint32 screenShotWidth;
uint32 screenShotHeight;
getScreenShotProps(type, screenShot, screenShotWidth, screenShotHeight);
SaveWriter writer(screenShot ? 3 : 2, 0);
SavePartInfo *info = readInfo(*save, kSlotNameLength, false);
if (!info)
return loadFail(nullptr, nullptr, nullptr, save);
SavePartVars *vars = readVars(*save, varSize, true);
if (!vars)
return loadFail(info, nullptr, nullptr, save);
if (screenShot) {
SavePartSprite *sprite = readSprite(*save, screenShotWidth, screenShotHeight, true);
if (!sprite)
return loadFail(info, vars, nullptr, save);
if (!writer.writePart(2, sprite))
return loadFail(info, vars, sprite, save);
delete sprite;
}
// We don't need the save anymore
delete save;
// Write all parts
if (!writer.writePart(0, info))
return loadFail(info, vars, nullptr, nullptr);
if (!writer.writePart(1, vars))
return loadFail(info, vars, nullptr, nullptr);
// We don't need those anymore
delete info;
delete vars;
// Create the final read stream
if (!createStream(writer))
return loadFail(nullptr, nullptr, nullptr, nullptr);
return true;
}
} // End of namespace Gob

View File

@@ -0,0 +1,149 @@
/* 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 <http://www.gnu.org/licenses/>.
*
*
* This file is dual-licensed.
* In addition to the GPLv3 license mentioned above, this code is also
* licensed under LGPL 2.1. See LICENSES/COPYING.LGPL file for the
* full text of the license.
*
*/
#include "common/endian.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_v4::SaveConverter_v4(GobEngine *vm, const Common::String &fileName) :
SaveConverter(vm, fileName) {
}
SaveConverter_v4::~SaveConverter_v4() {
}
int SaveConverter_v4::isOldSave(Common::InSaveFile **save) const {
uint32 varSize = SaveHandler::getVarSize(_vm);
if (varSize == 0)
return 0;
uint32 saveSize = getActualSize(save);
if (saveSize == 0)
return 0;
// The size of the old save always follows that rule
if (saveSize == (varSize * 2 + kSlotNameLength + 1000 + 512000))
return 1;
// Not an old save, clean up
if (save) {
delete *save;
*save = nullptr;
}
return 0;
}
char *SaveConverter_v4::getDescription(Common::SeekableReadStream &save) const {
// The description starts at 1000
if (!save.seek(1000))
return nullptr;
char *desc = new char[kSlotNameLength];
// Read the description
if (save.read(desc, kSlotNameLength) != kSlotNameLength) {
delete[] desc;
return nullptr;
}
return desc;
}
bool SaveConverter_v4::loadFail(SavePartInfo *info, SavePartVars *vars,
SavePartMem *props, Common::InSaveFile *save) {
delete info;
delete vars;
delete props;
delete save;
clear();
return false;
}
// Loads the old save by constructing a new save containing the old save's data
bool SaveConverter_v4::load() {
clear();
uint32 varSize = SaveHandler::getVarSize(_vm);
if (varSize == 0)
return false;
Common::InSaveFile *save;
// Test if it's an old savd
if (!isOldSave(&save) || !save)
return false;
displayWarning();
SaveWriter writer(3, 0);
SavePartInfo *info = readInfo(*save, kSlotNameLength, false);
if (!info)
return loadFail(nullptr, nullptr, nullptr, save);
SavePartVars *vars = readVars(*save, varSize, true);
if (!vars)
return loadFail(info, nullptr, nullptr, save);
SavePartMem *props = readMem(*save, 256000, true);
if (!props)
return loadFail(info, vars, nullptr, save);
// We don't need the save anymore
delete save;
// Write all parts
if (!writer.writePart(0, info))
return loadFail(info, vars, props, nullptr);
if (!writer.writePart(1, vars))
return loadFail(info, vars, props, nullptr);
if (!writer.writePart(2, props))
return loadFail(info, vars, props, nullptr);
// We don't need those anymore
delete info;
delete vars;
delete props;
// Create the final read stream
if (!createStream(writer))
return loadFail(nullptr, nullptr, nullptr, nullptr);
return true;
}
} // End of namespace Gob

File diff suppressed because it is too large Load Diff

365
engines/gob/save/savefile.h Normal file
View File

@@ -0,0 +1,365 @@
/* 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 <http://www.gnu.org/licenses/>.
*
*
* This file is dual-licensed.
* In addition to the GPLv3 license mentioned above, this code is also
* licensed under LGPL 2.1. See LICENSES/COPYING.LGPL file for the
* full text of the license.
*
*/
#ifndef GOB_SAVE_SAVEFILE_H
#define GOB_SAVE_SAVEFILE_H
#include "common/endian.h"
#include "common/array.h"
#include "common/savefile.h"
namespace Gob {
class GobEngine;
class Surface;
/**
* A class wrapping a save part header.
*
* A save part header consists of 4 fields:
* ID : The 8 character ID \0SCVMGOB
* Type : The 4 character ID for this part's type
* Version : This part's version. Each type has its own version counter
* Size : The size of the contents, i.e. excluding this header
*/
class SaveHeader {
public:
/** The size of the header. */
static const int kSize = 20;
static const uint32 kID1 = MKTAG(0,'S','C','V');
static const uint32 kID2 = MKTAG('M','G','O','B');
SaveHeader(uint32 type = 0, uint32 version = 0, uint32 size = 0);
bool operator==(const SaveHeader &header) const;
bool operator!=(const SaveHeader &header) const;
/** Read the header out of a stream into this class. */
bool read(Common::ReadStream &stream);
/** Read the header out of a stream and checks it against this class's contents. */
bool verify(Common::ReadStream &stream) const;
/** Read the header out of a stream and checks it against this class's contents,
* but read the size field instead.
*/
bool verifyReadSize(Common::ReadStream &stream);
/** Write this class's contents into a stream. */
bool write(Common::WriteStream &stream) const;
uint32 getType() const;
uint32 getVersion() const;
uint32 getSize() const;
void setType(uint32 type);
void setVersion(uint32 version);
void setSize(uint32 size);
private:
/** An ID specifying the part's type. */
uint32 _type;
/** The part's version. */
uint32 _version;
/** The size of the contents. */
uint32 _size;
};
/** An abstract class for a part in a save file. */
class SavePart {
public:
SavePart();
virtual ~SavePart();
/** Return the total size of the part. */
virtual uint32 getSize() const;
/** Read the part (with header) out of the stream. */
virtual bool read(Common::ReadStream &stream) = 0;
/** Write the part (with header) into the stream. */
virtual bool write(Common::WriteStream &stream) const = 0;
protected:
SaveHeader _header;
};
/** A save part consisting of plain memory. */
class SavePartMem : public SavePart {
public:
static const uint32 kVersion = 1;
static const uint32 kID = MKTAG('P','M','E','M');
SavePartMem(uint32 size);
~SavePartMem() override;
bool read(Common::ReadStream &stream) override;
bool write(Common::WriteStream &stream) const override;
/** Read size bytes of data into the part at the specified offset. */
bool readFrom(const byte *data, uint32 offset, uint32 size);
/** Write size bytes of the part at the specified offset int data. */
bool writeInto(byte *data, uint32 offset, uint32 size) const;
private:
uint32 _size;
byte *_data;
};
/** A save part holding script variables. */
class SavePartVars : public SavePart {
public:
static const uint32 kVersion = 1;
static const uint32 kID = MKTAG('V','A','R','S');
SavePartVars(GobEngine *vm, uint32 size);
~SavePartVars() override;
bool read(Common::ReadStream &stream) override;
bool write(Common::WriteStream &stream) const override;
/** Read size bytes of variables starting at var into the part at the specified offset. */
bool readFrom(uint32 var, uint32 offset, uint32 size);
/** Write size bytes of the part at the specified offset into the variable starting at var. */
bool writeInto(uint32 var, uint32 offset, uint32 size) const;
/** Read size bytes of raw data into the part. */
bool readFromRaw(const byte *data, uint32 offset, uint32 size);
/** Write size bytes of the part at the specified offset into a raw buffer. */
bool writeIntoRaw(byte *data, uint32 offset, uint32 size) const;
const byte* data() const { return _data; }
private:
GobEngine *_vm;
uint32 _size;
byte *_data;
};
/** A save part holding a sprite. */
class SavePartSprite : public SavePart {
public:
static const uint32 kVersion = 2;
static const uint32 kID = MKTAG('S','P','R','T');
SavePartSprite(uint32 width, uint32 height, bool trueColor = false);
~SavePartSprite() override;
bool read(Common::ReadStream &stream) override;
bool write(Common::WriteStream &stream) const override;
/** Read a palette into the part. */
bool readPalette(const byte *palette);
/** Read a sprite into the part. */
bool readSprite(const Surface &sprite);
/** Read size bytes of raw data into the sprite. */
bool readSpriteRaw(const byte *data, uint32 size);
/** Write a palette out of the part. */
bool writePalette(byte *palette) const;
/** Write a sprite out of the part. */
bool writeSprite(Surface &sprite) const;
private:
uint32 _width;
uint32 _height;
uint32 _spriteSize;
bool _oldFormat;
bool _trueColor;
byte *_dataSprite;
byte *_dataPalette;
};
/** A save part containing informations about the save's game. */
class SavePartInfo : public SavePart {
public:
static const uint32 kVersion = 1;
static const uint32 kID = MKTAG('I','N','F','O');
/**
* The constructor.
* @param descMaxLength The maximal number of bytes that fit into the description.
* @param gameID An ID for the game (Gob1, Gob2, Gob3, ...).
* @param gameVersion An ID for game specific versioning
* @param endian Endianness of the platform the game originally ran on.
* @param varCount The number of script variables.
*/
SavePartInfo(uint32 descMaxLength, uint32 gameID,
uint32 gameVersion, byte endian, uint32 varCount);
~SavePartInfo() override;
/** Return the save's description. */
const char *getDesc() const;
/** Return the description's maximal length. */
uint32 getDescMaxLength() const;
/** Set the variable count. */
void setVarCount(uint32 varCount);
/** Set the save's description. */
void setDesc(const char *desc = 0);
/** Set the save's description. */
void setDesc(const byte *desc, uint32 size);
bool read(Common::ReadStream &stream) override;
bool write(Common::WriteStream &stream) const override;
private:
char *_desc;
uint32 _descMaxLength;
uint32 _gameID;
uint32 _gameVersion;
uint32 _varCount;
byte _endian;
};
/** A container for several save parts. */
class SaveContainer {
public:
static const uint32 kVersion = 1;
static const uint32 kID = MKTAG('C','O','N','T');
/**
* The constructor.
* @param partCount The number parts this container shall hold.
* @param slot The save slot this save's for.
*/
SaveContainer(uint32 partCount, uint32 slot);
~SaveContainer();
uint32 getSlot() const;
uint32 getSize() const;
/** All parts filled? */
bool hasAllParts() const;
/** Empty all parts. */
void clear();
/** Write a SavePart into the container's part. */
bool writePart(uint32 partN, const SavePart *part);
/** Read the container's part's content into a SavePart. */
bool readPart(uint32 partN, SavePart *part) const;
/** Read only the container's part's header. */
bool readPartHeader(uint32 partN, SaveHeader *header) const;
/** Checks if the stream is a valid save container. */
static bool isSave(Common::SeekableReadStream &stream);
protected:
/** A part. */
struct Part {
uint32 size;
byte *data;
Part(uint32 s);
~Part();
Common::WriteStream *createWriteStream();
Common::ReadStream *createReadStream() const;
};
/** Basic information about a part. */
struct PartInfo {
uint32 id;
uint32 offset;
uint32 size;
};
typedef Common::Array<Part *>::iterator PartIterator;
typedef Common::Array<Part *>::const_iterator PartConstIterator;
uint32 _partCount;
uint32 _slot;
SaveHeader _header;
Common::Array<Part *> _parts;
uint32 calcSize() const;
bool read(Common::ReadStream &stream);
bool write(Common::WriteStream &stream) const;
/** Get an array containing basic information about all parts in the container in the stream. */
static Common::Array<PartInfo> *getPartsInfo(Common::SeekableReadStream &stream);
};
/** Reads a save. */
class SaveReader : public SaveContainer {
public:
SaveReader(uint32 partCount, uint32 slot, const Common::String &fileName);
SaveReader(uint32 partCount, uint32 slot, Common::SeekableReadStream &stream);
~SaveReader();
bool load();
bool readPart(uint32 partN, SavePart *part) const;
bool readPartHeader(uint32 partN, SaveHeader *header) const;
/** Find and read the save's info part. */
static bool getInfo(Common::SeekableReadStream &stream, SavePartInfo &info);
/** Find and read the save's info part. */
static bool getInfo(const Common::String &fileName, SavePartInfo &info);
protected:
Common::String _fileName;
Common::SeekableReadStream *_stream;
bool _loaded;
static Common::InSaveFile *openSave(const Common::String &fileName);
Common::InSaveFile *openSave();
};
/** Writes a save. */
class SaveWriter: public SaveContainer {
public:
SaveWriter(uint32 partCount, uint32 slot);
SaveWriter(uint32 partCount, uint32 slot, const Common::String &fileName);
~SaveWriter();
bool writePart(uint32 partN, const SavePart *part);
bool save(Common::WriteStream &stream);
bool deleteFile();
protected:
bool save();
Common::String _fileName;
/** Is everything ready for saving? */
bool canSave() const;
static Common::OutSaveFile *openSave(const Common::String &fileName);
Common::OutSaveFile *openSave();
};
} // End of namespace Gob
#endif // GOB_SAVE_SAVEFILE_H

View File

@@ -0,0 +1,599 @@
/* 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 <http://www.gnu.org/licenses/>.
*
*
* This file is dual-licensed.
* In addition to the GPLv3 license mentioned above, this code is also
* licensed under LGPL 2.1. See LICENSES/COPYING.LGPL file for the
* full text of the license.
*
*/
#include "common/endian.h"
#include "gob/gob.h"
#include "gob/save/savehandler.h"
#include "gob/save/savefile.h"
#include "gob/save/saveconverter.h"
#include "gob/global.h"
#include "gob/video.h"
#include "gob/draw.h"
#include "gob/variables.h"
#include "gob/inter.h"
namespace Gob {
SlotFile::SlotFile(GobEngine *vm, uint32 slotCount, const Common::String &base) : _vm(vm) {
_base = base;
_slotCount = slotCount;
}
SlotFile::~SlotFile() {
}
uint32 SlotFileIndexed::getSlotMax() const {
Common::SaveFileManager *saveMan = g_system->getSavefileManager();
Common::InSaveFile *in;
// Find the last filled save slot and base the save file size calculate on that
for (int i = (_slotCount - 1); i >= 0; i--) {
Common::String slotFile = build(i);
if (slotFile.empty())
continue;
in = saveMan->openForLoading(slotFile);
if (in) {
delete in;
return i + 1;
}
}
return 0;
}
int32 SlotFileIndexed::tallyUpFiles(uint32 slotSize, uint32 indexSize) const {
uint32 maxSlot = getSlotMax();
if (maxSlot == 0)
return -1;
return ((maxSlot * slotSize) + indexSize);
}
void SlotFileIndexed::buildIndex(byte *buffer, SavePartInfo &info,
SaveConverter *converter, bool setLongest) const {
uint32 descLength = info.getDescMaxLength();
uint32 longest = 0;
byte *bufferStart = buffer;
// Iterate over all files
for (uint32 i = 0; i < _slotCount; i++, buffer += descLength) {
Common::String slotFile = build(i);
if (!slotFile.empty()) {
char *desc = nullptr;
if (converter && (desc = converter->getDescription(slotFile)))
// Old style save
memcpy(buffer, desc, descLength);
else if (SaveReader::getInfo(slotFile, info))
// New style save
memcpy(buffer, info.getDesc(), descLength);
else
// No known format, fill with 0
memset(buffer, 0, descLength);
delete[] desc;
longest = MAX<uint32>(longest, strlen((const char *) buffer));
} else
// No valid slot, fill with 0
memset(buffer, 0, descLength);
}
if (setLongest) {
uint32 slot0Len;
for (slot0Len = strlen((const char *) bufferStart); slot0Len < longest; slot0Len++)
bufferStart[slot0Len] = ' ';
bufferStart[slot0Len] = '\0';
}
}
bool SlotFileIndexed::exists(int slot) const {
Common::InSaveFile *in = openRead(slot);
bool result = (in != nullptr);
delete in;
return result;
}
Common::InSaveFile *SlotFileIndexed::openRead(int slot) const {
Common::String name = build(slot);
if (name.empty())
return nullptr;
Common::SaveFileManager *saveMan = g_system->getSavefileManager();
Common::InSaveFile *result = saveMan->openForLoading(name);
return result;
}
Common::OutSaveFile *SlotFileIndexed::openWrite(int slot) const {
Common::String name = build(slot);
if (name.empty())
return nullptr;
Common::SaveFileManager *saveMan = g_system->getSavefileManager();
Common::OutSaveFile *result = saveMan->openForSaving(name);
return result;
}
SlotFileIndexed::SlotFileIndexed(GobEngine *vm, uint32 slotCount,
const Common::String &base, const Common::String &extStub) : SlotFile(vm, slotCount, base) {
_ext = extStub;
}
SlotFileIndexed::~SlotFileIndexed() {
}
Common::String SlotFileIndexed::build(int slot) const {
if ((slot < 0) || (((uint32) slot) >= _slotCount))
return Common::String();
Common::String buf = Common::String::format("%02d", slot);
return _base + "." + _ext + buf;
}
SlotFileStatic::SlotFileStatic(GobEngine *vm, const Common::String &base,
const Common::String &ext) : SlotFile(vm, 1, base) {
_ext = "." + ext;
}
SlotFileStatic::~SlotFileStatic() {
}
int SlotFileStatic::getSlot(int32 offset) const {
return -1;
}
int SlotFileStatic::getSlotRemainder(int32 offset) const {
return -1;
}
Common::String SlotFileStatic::build() const {
return _base + _ext;
}
bool SlotFileStatic::exists() const {
Common::InSaveFile *in = openRead();
bool result = (in != nullptr);
delete in;
return result;
}
Common::InSaveFile *SlotFileStatic::openRead() const {
Common::String name = build();
if (name.empty())
return nullptr;
Common::SaveFileManager *saveMan = g_system->getSavefileManager();
Common::InSaveFile *result = saveMan->openForLoading(name);
return result;
}
Common::OutSaveFile *SlotFileStatic::openWrite() const {
Common::String name = build();
if (name.empty())
return nullptr;
Common::SaveFileManager *saveMan = g_system->getSavefileManager();
Common::OutSaveFile *result = saveMan->openForSaving(name);
return result;
}
SaveHandler::SaveHandler(GobEngine *vm) : _vm(vm) {
}
SaveHandler::~SaveHandler() {
}
uint32 SaveHandler::getVarSize(GobEngine *vm) {
// Sanity checks
if (!vm || !vm->_inter || !vm->_inter->_variables)
return 0;
return vm->_inter->_variables->getSize();
}
bool SaveHandler::deleteFile() {
return true;
}
bool SaveHandler::loadToRaw(byte *ptr, int32 size, int32 offset) {
warning("SaveHandler::loadToRaw() not implemented");
return false;
}
bool SaveHandler::saveFromRaw(const byte *ptr, int32 size, int32 offset) {
warning("SaveHandler::saveFromRaw() not implemented");
return false;
}
TempSpriteHandler::TempSpriteHandler(GobEngine *vm) : SaveHandler(vm) {
_sprite = nullptr;
}
TempSpriteHandler::~TempSpriteHandler() {
delete _sprite;
}
int32 TempSpriteHandler::getSize() {
if (!_sprite)
return -1;
return _sprite->getSize();
}
bool TempSpriteHandler::load(int16 dataVar, int32 size, int32 offset) {
if (isDummy(size))
return true;
// Sprite available?
if (!_sprite)
return false;
// Sprite requested?
if (!isSprite(size))
return false;
// Index sane?
int index = getIndex(size);
if ((index < 0) || (index >= Draw::kSpriteCount))
return false;
SurfacePtr sprite = _vm->_draw->_spritesArray[index];
// Target sprite exists?
if (!sprite)
return false;
// Load the sprite
if (!_sprite->writeSprite(*sprite))
return false;
// Handle palette
if (usesPalette(size)) {
if (!_sprite->writePalette((byte *)_vm->_global->_pPaletteDesc->vgaPal))
return false;
_vm->_video->setFullPalette(_vm->_global->_pPaletteDesc);
}
if (index == 21) {
// We wrote into the backbuffer, blit
_vm->_draw->forceBlit();
_vm->_video->retrace();
} else if (index == 20)
// We wrote into the frontbuffer, retrace
_vm->_video->retrace();
return true;
}
bool TempSpriteHandler::save(int16 dataVar, int32 size, int32 offset) {
if (isDummy(size))
return true;
SurfacePtr sprite = createSprite(dataVar, size, offset);
if (!sprite)
return false;
// Save the sprite
if (!_sprite->readSprite(*sprite))
return false;
// Handle palette
if (usesPalette(size))
if (!_sprite->readPalette((const byte *)_vm->_global->_pPaletteDesc->vgaPal))
return false;
return true;
}
bool TempSpriteHandler::create(uint32 width, uint32 height, bool trueColor) {
delete _sprite;
_sprite = nullptr;
// Create a new temporary sprite
_sprite = new SavePartSprite(width, height, trueColor);
return true;
}
bool TempSpriteHandler::loadToRaw(byte *ptr, int32 size, int32 offset) {
// Sprite available?
if (!_sprite)
return false;
Surface destSprite(1, size, 1);
// Load the sprite
if (!_sprite->writeSprite(destSprite))
return false;
// Copy the sprite to the buffer
memcpy(ptr, destSprite.getData(), size);
return true;
}
bool TempSpriteHandler::saveFromRaw(const byte *ptr, int32 size, int32 offset) {
create(1, size, false);
if (!_sprite->readSpriteRaw(ptr, size))
return false;
// Assume no palette used
return true;
}
bool TempSpriteHandler::createFromSprite(int16 dataVar, int32 size, int32 offset) {
return createSprite(dataVar, size, offset) != nullptr;
}
SurfacePtr TempSpriteHandler::createSprite(int16 dataVar, int32 size, int32 offset) {
SurfacePtr sprt;
// Sprite requested?
if (!isSprite(size))
return sprt;
// Index sane?
int index = getIndex(size);
if ((index < 0) || (index >= Draw::kSpriteCount))
return sprt;
// Sprite exists?
if (!(sprt = _vm->_draw->_spritesArray[index]))
return sprt;
if (!create(sprt->getWidth(), sprt->getHeight(), sprt->getBPP() > 1))
sprt.reset();
return sprt;
}
// A size of 0 means no proper sprite should be saved/loaded,
// but no error should be thrown either.
bool TempSpriteHandler::isDummy(int32 size) {
return (size == 0);
}
// A negative size is the flag for using a sprite
bool TempSpriteHandler::isSprite(int32 size) {
return (size < 0);
}
// Contruct the index
int TempSpriteHandler::getIndex(int32 size) {
// Palette flag
if (size < -3000)
size += 3000;
if (size < -1000)
size += 1000;
return (-size - 1);
}
// A size smaller than -1000 indicates palette usage
bool TempSpriteHandler::usesPalette(int32 size) {
return (size < -1000);
}
NotesHandler::File::File(GobEngine *vm, const Common::String &base) :
SlotFileStatic(vm, base, "blo") {
}
NotesHandler::File::~File() {
}
NotesHandler::NotesHandler(uint32 notesSize, GobEngine *vm, const Common::String &target) :
SaveHandler(vm) {
_notesSize = notesSize;
_file = new File(vm, target);
_notes = new SavePartVars(vm, _notesSize);
}
NotesHandler::~NotesHandler() {
delete _file;
delete _notes;
}
int32 NotesHandler::getSize() {
Common::String fileName = _file->build();
if (fileName.empty())
return -1;
Common::InSaveFile *saveFile;
SaveConverter_Notes converter(_vm, _notesSize, fileName);
if (converter.isOldSave(&saveFile)) {
// Old save, get the size olden-style
int32 size = saveFile->size();
delete saveFile;
return size;
}
SaveReader reader(1, 0, fileName);
SaveHeader header;
if (!reader.load())
return -1;
if (!reader.readPartHeader(0, &header))
return -1;
// Return the part's size
return header.getSize();
}
bool NotesHandler::load(int16 dataVar, int32 size, int32 offset) {
if ((dataVar < 0) || (size < 0) || (offset < 0))
return false;
Common::String fileName = _file->build();
if (fileName.empty())
return false;
SaveReader *reader;
SaveConverter_Notes converter(_vm, _notesSize, fileName);
if (converter.isOldSave()) {
// Old save, plug the converter in
if (!converter.load())
return false;
reader = new SaveReader(1, 0, converter);
} else
// New save, load directly
reader = new SaveReader(1, 0, fileName);
SavePartVars vars(_vm, _notesSize);
if (!reader->load()) {
delete reader;
return false;
}
if (!reader->readPart(0, &vars)) {
delete reader;
return false;
}
if (!vars.writeInto(dataVar, offset, size)) {
delete reader;
return false;
}
delete reader;
return true;
}
bool NotesHandler::save(int16 dataVar, int32 size, int32 offset) {
if ((dataVar < 0) || (size < 0) || (offset < 0))
return false;
Common::String fileName = _file->build();
if (fileName.empty())
return false;
SaveWriter writer(1, 0, fileName);
SavePartVars vars(_vm, _notesSize);
if (!vars.readFrom(dataVar, offset, size))
return false;
return writer.writePart(0, &vars);
}
FakeFileHandler::FakeFileHandler(GobEngine *vm) : SaveHandler(vm) {
}
FakeFileHandler::~FakeFileHandler() {
}
int32 FakeFileHandler::getSize() {
if (_data.empty())
return -1;
return _data.size();
}
bool FakeFileHandler::load(int16 dataVar, int32 size, int32 offset) {
if (size <= 0)
return false;
if ((uint32)(offset + size) > _data.size())
return false;
_vm->_inter->_variables->copyFrom((uint16) dataVar, &_data[0] + offset, size);
return true;
}
bool FakeFileHandler::save(int16 dataVar, int32 size, int32 offset) {
if (size <= 0)
return false;
if ((uint32)(offset + size) > _data.size())
_data.resize(offset + size);
_vm->_inter->_variables->copyTo((uint16) dataVar, &_data[0] + offset, size);
return true;
}
bool FakeFileHandler::loadToRaw(byte *ptr, int32 size, int32 offset) {
if (size <= 0)
return false;
if ((uint32)(offset + size) > _data.size())
return false;
memcpy(ptr, &_data[0] + offset, size);
return true;
}
bool FakeFileHandler::saveFromRaw(const byte *ptr, int32 size, int32 offset) {
if (size <= 0)
return false;
if ((uint32)(offset + size) > _data.size())
_data.resize(offset + size);
memcpy(&_data[0] + offset, ptr, size);
return true;
}
bool FakeFileHandler::deleteFile() {
_data.clear();
return true;
}
} // End of namespace Gob

View File

@@ -0,0 +1,213 @@
/* 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 <http://www.gnu.org/licenses/>.
*
*
* This file is dual-licensed.
* In addition to the GPLv3 license mentioned above, this code is also
* licensed under LGPL 2.1. See LICENSES/COPYING.LGPL file for the
* full text of the license.
*
*/
#ifndef GOB_SAVE_SAVEHANDLER_H
#define GOB_SAVE_SAVEHANDLER_H
#include "common/savefile.h"
#include "common/array.h"
#include "engines/gob/video.h" // for SurfacePtr
namespace Gob {
class GobEngine;
class SavePartInfo;
class SavePartVars;
class SavePartSprite;
class SaveConverter;
/** Slot file related class. */
class SlotFile {
public:
/** The constructor.
*
* @param slotCount Number of slots.
* @param base The file's base string.
*/
SlotFile(GobEngine *vm, uint32 slotCount, const Common::String &base);
virtual ~SlotFile();
/** Calculates which slot to use. */
virtual int getSlot(int32 offset) const = 0;
/** Calculates the slot remainder, for error checking. */
virtual int getSlotRemainder(int32 offset) const = 0;
protected:
GobEngine *_vm;
Common::String _base;
uint32 _slotCount;
};
/** An indexed slot file ("foobar.s00", "foobar.s01", ...). */
class SlotFileIndexed : public SlotFile {
public:
SlotFileIndexed(GobEngine *vm, uint32 slotCount, const Common::String &base,
const Common::String &extStub);
~SlotFileIndexed() override;
/** Build the save file name. */
Common::String build(int slot) const;
/** Returns the highest filled slot number. */
virtual uint32 getSlotMax() const;
/** Returns the size of all existing slots + the index. */
virtual int32 tallyUpFiles(uint32 slotSize, uint32 indexSize) const;
/** Creates an index in buffer. */
virtual void buildIndex(byte *buffer, SavePartInfo &info,
SaveConverter *converter = 0, bool setLongest = false) const;
virtual bool exists(int slot) const;
virtual Common::InSaveFile *openRead(int slot) const;
virtual Common::OutSaveFile *openWrite(int slot) const;
protected:
Common::String _ext;
};
/** A static slot file ("foo.bar"). */
class SlotFileStatic : public SlotFile {
public:
SlotFileStatic(GobEngine *vm, const Common::String &base, const Common::String &ext);
~SlotFileStatic() override;
int getSlot(int32 offset) const override;
int getSlotRemainder(int32 offset) const override;
/** Build the save file name. */
Common::String build() const;
virtual bool exists() const;
virtual Common::InSaveFile *openRead() const;
virtual Common::OutSaveFile *openWrite() const;
protected:
Common::String _ext;
};
/** A handler for a specific save file. */
class SaveHandler {
public:
SaveHandler(GobEngine *vm);
virtual ~SaveHandler();
/** Returns the file's (virtual) size. */
virtual int32 getSize() = 0;
/** Loads (parts of) the file. */
virtual bool load(int16 dataVar, int32 size, int32 offset) = 0;
/** Saves (parts of) the file. */
virtual bool save(int16 dataVar, int32 size, int32 offset) = 0;
virtual bool loadToRaw(byte *ptr, int32 size, int32 offset);
virtual bool saveFromRaw(const byte *ptr, int32 size, int32 offset);
/** Deletes the file. */
virtual bool deleteFile();
static uint32 getVarSize(GobEngine *vm);
protected:
GobEngine *_vm;
};
/** A handler for temporary sprites. */
class TempSpriteHandler : public SaveHandler {
public:
TempSpriteHandler(GobEngine *vm);
~TempSpriteHandler() override;
int32 getSize() override;
bool load(int16 dataVar, int32 size, int32 offset) override;
bool save(int16 dataVar, int32 size, int32 offset) override;
bool loadToRaw(byte *ptr, int32 size, int32 offset) override;
bool saveFromRaw(const byte *ptr, int32 size, int32 offset) override;
bool create(uint32 width, uint32 height, bool trueColor);
bool createFromSprite(int16 dataVar, int32 size, int32 offset);
protected:
SavePartSprite *_sprite;
/** Determine whether it's a dummy sprite save/load. */
static bool isDummy(int32 size);
/** Determine whether using a sprite was requested. */
static bool isSprite(int32 size);
/** Determine which sprite is meant. */
static int getIndex(int32 size);
/** Determine whether the palette should be used too. */
static bool usesPalette(int32 size);
SurfacePtr createSprite(int16 dataVar, int32 size, int32 offset);
};
/** A handler for notes. */
class NotesHandler : public SaveHandler {
public:
NotesHandler(uint32 notesSize, GobEngine *vm, const Common::String &target);
~NotesHandler() override;
int32 getSize() override;
bool load(int16 dataVar, int32 size, int32 offset) override;
bool save(int16 dataVar, int32 size, int32 offset) override;
private:
class File : public SlotFileStatic {
public:
File(GobEngine *vm, const Common::String &base);
~File() override;
};
uint32 _notesSize;
File *_file;
SavePartVars *_notes;
};
/** A handler that behaves like a file but keeps the contents in memory. */
class FakeFileHandler : public SaveHandler {
public:
FakeFileHandler(GobEngine *vm);
~FakeFileHandler() override;
int32 getSize() override;
bool load(int16 dataVar, int32 size, int32 offset) override;
bool save(int16 dataVar, int32 size, int32 offset) override;
bool loadToRaw(byte *ptr, int32 size, int32 offset) override;
bool saveFromRaw(const byte *ptr, int32 size, int32 offset) override;
bool deleteFile() override;
private:
Common::Array<byte> _data;
};
} // End of namespace Gob
#endif // GOB_SAVE_SAVEHANDLER_H

View File

@@ -0,0 +1,272 @@
/* 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 <http://www.gnu.org/licenses/>.
*
*
* This file is dual-licensed.
* In addition to the GPLv3 license mentioned above, this code is also
* licensed under LGPL 2.1. See LICENSES/COPYING.LGPL file for the
* full text of the license.
*
*/
#include "gob/gob.h"
#include "gob/save/saveload.h"
#include "gob/global.h"
#include "gob/video.h"
#include "gob/draw.h"
namespace Gob {
SaveLoad::SaveLoad(GobEngine *vm) : _vm(vm) {
}
SaveLoad::~SaveLoad() {
}
const char *SaveLoad::stripPath(const char *fileName, char separator) {
const char *backSlash;
if ((backSlash = strrchr(fileName, separator)))
return backSlash + 1;
return fileName;
}
Common::String SaveLoad::replacePathSeparators(const char *path, char newSeparator) {
Common::String result = path;
for (char &c : result) {
if (c != newSeparator && (c == '\\' || c == '/' || c == ':'))
c = newSeparator;
}
return result;
}
Common::List<Common::Path> SaveLoad::getFilesMatchingPattern(const Common::Path &pattern) const {
warning("SaveLoad::getFilesMatchingPattern not implemented");
return Common::List<Common::Path>();
}
int32 SaveLoad::getSize(const char *fileName) {
debugC(3, kDebugSaveLoad, "Requested size of save file \"%s\"", fileName);
SaveHandler *handler = getHandler(fileName);
if (!handler) {
warning("No save handler for \"%s\"", fileName);
return -1;
}
int32 size = handler->getSize();
debugC(4, kDebugSaveLoad, "Size is %d", size);
return size;
}
bool SaveLoad::load(const char *fileName, int16 dataVar, int32 size, int32 offset) {
debugC(3, kDebugSaveLoad, "Requested loading of save file \"%s\" - %d, %d, %d",
fileName, dataVar, size, offset);
SaveHandler *handler = getHandler(fileName);
if (!handler) {
warning("No save handler for \"%s\" (%d, %d, %d)", fileName, dataVar, size, offset);
return false;
}
if (!handler->load(dataVar, size, offset)) {
const char *desc = getDescription(fileName);
if (!desc)
desc = "Unknown";
warning("Could not load %s (\"%s\" (%d, %d, %d))",
desc, fileName, dataVar, size, offset);
return false;
}
debugC(3, kDebugSaveLoad, "Successfully loaded game");
return true;
}
bool SaveLoad::loadToRaw(const char *fileName, byte *ptr, int32 size, int32 offset) {
debugC(3, kDebugSaveLoad, "Requested loading of save file \"%s\" - raw %p, %d, %d",
fileName, (void *)ptr, size, offset);
SaveHandler *handler = getHandler(fileName);
if (!handler) {
warning("No save handler for \"%s\" (raw %p, %d, %d)", fileName, (void*) ptr, size, offset);
return false;
}
if (!handler->loadToRaw(ptr, size, offset)) {
const char *desc = getDescription(fileName);
if (!desc)
desc = "Unknown";
warning("Could not load %s (\"%s\" (raw %p, %d, %d))",
desc, fileName, (void*) ptr, size, offset);
return false;
}
debugC(3, kDebugSaveLoad, "Successfully loaded game");
return true;
}
bool SaveLoad::save(const char *fileName, int16 dataVar, int32 size, int32 offset) {
debugC(3, kDebugSaveLoad, "Requested saving of save file \"%s\" - %d, %d, %d",
fileName, dataVar, size, offset);
SaveHandler *handler = getHandler(fileName);
if (!handler) {
warning("No save handler for \"%s\" (%d, %d, %d)", fileName, dataVar, size, offset);
return false;
}
if (!handler->save(dataVar, size, offset)) {
const char *desc = getDescription(fileName);
if (!desc)
desc = "Unknown";
warning("Could not save %s (\"%s\" (%d, %d, %d))",
desc, fileName, dataVar, size, offset);
return false;
}
debugC(3, kDebugSaveLoad, "Successfully saved game");
return true;
}
bool SaveLoad::saveFromRaw(const char *fileName, byte *ptr, int32 size, int32 offset) {
debugC(3, kDebugSaveLoad, "Requested saving of save file \"%s\" - raw %p, %d, %d",
fileName, (void*) ptr, size, offset);
SaveHandler *handler = getHandler(fileName);
if (!handler) {
warning("No save handler for \"%s\" (raw %p, %d, %d)", fileName, (void*) ptr, size, offset);
return false;
}
if (!handler->saveFromRaw(ptr, size, offset)) {
const char *desc = getDescription(fileName);
if (!desc)
desc = "Unknown";
warning("Could not save %s (\"%s\" (raw %p, %d, %d))",
desc, fileName, (void*) ptr, size, offset);
return false;
}
debugC(3, kDebugSaveLoad, "Successfully saved game");
return true;
}
bool SaveLoad::copySaveGame(const char *fileNameSrc, const char *fileNameDest) {
SaveHandler *handlerSrc = getHandler(fileNameSrc);
if (!handlerSrc) {
warning("copySaveGame: no save handler for source \"%s\" ", fileNameSrc);
return false;
}
SaveHandler *handlerDest = getHandler(fileNameDest);
if (!handlerDest) {
warning("copySaveGame: no save handler for destination \"%s\" ", fileNameDest);
return false;
}
int32 size = handlerSrc->getSize();
if (size == -1) {
warning("copySaveGame: source file \"%s\" does not exists", fileNameSrc);
return false;
}
byte *buffer = new byte[size];
if (!handlerSrc->loadToRaw(buffer, size, 0)) {
const char *desc = getDescription(fileNameSrc);
if (!desc)
desc = "Unknown";
warning("Could not load %s (\"%s\") for copying to %s", desc, fileNameSrc, fileNameDest);
delete[] buffer;
return false;
}
if (!handlerDest->saveFromRaw(buffer, size, 0)) {
const char *desc = getDescription(fileNameDest);
if (!desc)
desc = "Unknown";
warning("Could not save %s (\"%s\") when copying from %s", desc, fileNameDest, fileNameSrc);
delete[] buffer;
return false;
}
debugC(3, kDebugSaveLoad, "Successfully copied saved game");
delete[] buffer;
return true;
}
bool SaveLoad::deleteFile(const char *fileName) {
debugC(3, kDebugSaveLoad, "Requested deletion save file \"%s\"", fileName);
SaveHandler *handler = getHandler(fileName);
if (!handler) {
warning("No save handler for \"%s\"", fileName);
return false;
}
if (!handler->deleteFile()) {
const char *desc = getDescription(fileName);
if (!desc)
desc = "Unknown";
warning("Could not delete %s (\"%s\")", desc, fileName);
return false;
}
debugC(3, kDebugSaveLoad, "Successfully deleted file");
return true;
}
SaveLoad::SaveMode SaveLoad::getSaveMode(const char *fileName) const {
return kSaveModeNone;
}
SaveHandler *SaveLoad::getHandler(const char *fileName) const {
return nullptr;
}
const char *SaveLoad::getDescription(const char *fileName) const {
return nullptr;
}
} // End of namespace Gob

1131
engines/gob/save/saveload.h Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,401 @@
/* 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 <http://www.gnu.org/licenses/>.
*
*
* This file is dual-licensed.
* In addition to the GPLv3 license mentioned above, this code is also
* licensed under LGPL 2.1. See LICENSES/COPYING.LGPL file for the
* full text of the license.
*
*/
#include "gob/save/saveload.h"
#include "gob/save/saveconverter.h"
#include "gob/inter.h"
#include "gob/variables.h"
namespace Gob {
SaveLoad_Adibou1::SaveFile SaveLoad_Adibou1::_saveFiles[] = {
{ "bou.inf", kSaveModeSave, nullptr, "adibou1"},
{ "dessin.inf", kSaveModeSave, nullptr, "paint game drawing"},
{ "const.inf", kSaveModeSave, nullptr, "construction game"},
{ "menu.inf", kSaveModeSave, nullptr, "temporary sprite"},
{ "dessin1.inf", kSaveModeSave, nullptr, "paint game drawing"},
{ "dessin2.inf", kSaveModeSave, nullptr, "paint game drawing"},
{ "dessin3.inf", kSaveModeSave, nullptr, "paint game drawing"},
{ "dessin4.inf", kSaveModeSave, nullptr, "paint game drawing"},
{ "dessin5.inf", kSaveModeSave, nullptr, "paint game drawing"},
{ "dessin6.inf", kSaveModeSave, nullptr, "paint game drawing"},
{ "dessin7.inf", kSaveModeSave, nullptr, "paint game drawing"},
{ "dessin8.inf", kSaveModeSave, nullptr, "paint game drawing"},
{ "a:\\bou.inf", kSaveModeIgnore, nullptr, "copy of the savegame to be sent "
"to Coktel Vision on a floppy"
"when completing the game"},
};
SaveLoad_Adibou1::SaveLoad_Adibou1(GobEngine *vm, const char *targetName) :
SaveLoad(vm) {
uint32 index = 0;
_saveFiles[index++].handler = _bouHandler = new GameFileHandler(vm, targetName, "bouinf");
_saveFiles[index++].handler = _drawingHandler = new SpriteHandler(vm, targetName, "drawing");
_saveFiles[index++].handler = _constructionHandler = new GameFileHandler(vm, targetName, "construction");
_saveFiles[index++].handler = _menuHandler = new TempSpriteHandler(vm);
for (int i = 0; i < kAdibou1NbrOfDrawings; i++) {
_saveFiles[index++].handler = _drawingWithThumbnailHandler[i] = new DrawingWithThumbnailHandler(vm,
targetName,
Common::String::format("drawing%02d", i + 1));
}
}
SaveLoad_Adibou1::~SaveLoad_Adibou1() {
delete _bouHandler;
delete _drawingHandler;
delete _constructionHandler;
delete _menuHandler;
for (int i = 0; i < kAdibou1NbrOfDrawings; i++)
delete _drawingWithThumbnailHandler[i];
}
SaveLoad_Adibou1::SpriteHandler::File::File(GobEngine *vm, const Common::String &base, const Common::String &ext) :
SlotFileStatic(vm, base, ext) {
}
SaveLoad_Adibou1::SpriteHandler::File::~File() {
}
SaveLoad_Adibou1::SpriteHandler::SpriteHandler(GobEngine *vm, const Common::String &target, const Common::String &ext)
: TempSpriteHandler(vm), _file(vm, target, ext) {
}
SaveLoad_Adibou1::SpriteHandler::~SpriteHandler() {
}
int32 SaveLoad_Adibou1::SpriteHandler::getSize() {
Common::String fileName = _file.build();
if (fileName.empty())
return -1;
SaveReader reader(1, 0, fileName);
SaveHeader header;
if (!reader.load())
return -1;
if (!reader.readPartHeader(0, &header))
return -1;
// Return the part's size
return header.getSize();
}
bool SaveLoad_Adibou1::SpriteHandler::load(int16 dataVar, int32 size, int32 offset) {
if (!TempSpriteHandler::createFromSprite(dataVar, size, offset))
return false;
Common::String fileName = _file.build();
if (fileName.empty())
return false;
SaveReader reader(1, 0, fileName);
if (!reader.load())
return false;
if (!reader.readPart(0, _sprite))
return false;
return TempSpriteHandler::load(dataVar, size, offset);
}
bool SaveLoad_Adibou1::SpriteHandler::save(int16 dataVar, int32 size, int32 offset) {
if (!TempSpriteHandler::save(dataVar, size, offset))
return false;
Common::String fileName = _file.build();
if (fileName.empty())
return false;
SaveWriter writer(1, 0, fileName);
return writer.writePart(0, _sprite);
}
SaveLoad_Adibou1::DrawingWithThumbnailHandler::File::File(GobEngine *vm, const Common::String &base, const Common::String &ext) :
SlotFileStatic(vm, base, ext) {
}
SaveLoad_Adibou1::DrawingWithThumbnailHandler::File::~File() {
}
SaveLoad_Adibou1::DrawingWithThumbnailHandler::DrawingWithThumbnailHandler(GobEngine *vm,
const Common::String &target,
const Common::String &ext)
: TempSpriteHandler(vm), _file(vm, target, ext) {
Common::String fileName = _file.build();
_reader = new SaveReader(2, 0, fileName); // Two parts: the thumbnail + the actual drawing
_writer = new SaveWriter(2, 0, fileName);
}
SaveLoad_Adibou1::DrawingWithThumbnailHandler::~DrawingWithThumbnailHandler() {
delete _reader;
delete _writer;
}
int32 SaveLoad_Adibou1::DrawingWithThumbnailHandler::getSize() {
if (!_reader)
return -1;
if (!_reader->load())
return -1;
SaveHeader header1, header2;
if (!_reader->readPartHeader(0, &header1))
return -1;
if (!_reader->readPartHeader(1, &header2))
return -1;
return header1.getSize() + header2.getSize();
}
bool SaveLoad_Adibou1::DrawingWithThumbnailHandler::load(int16 dataVar, int32 size, int32 offset) {
if (!TempSpriteHandler::createFromSprite(dataVar, size, offset))
return false;
Common::String fileName = _file.build();
if (fileName.empty())
return false;
if (!_reader->load())
return false;
uint32 part = (offset == 0) ? 0 : 1;
if (!_reader->readPart(part, _sprite))
return false;
return TempSpriteHandler::load(dataVar, size, offset);
}
bool SaveLoad_Adibou1::DrawingWithThumbnailHandler::save(int16 dataVar, int32 size, int32 offset) {
if (!TempSpriteHandler::save(dataVar, size, offset))
return false;
Common::String fileName = _file.build();
if (fileName.empty())
return false;
uint32 part = (offset == 0) ? 0 : 1;
return _writer->writePart(part, _sprite);
}
SaveLoad_Adibou1::GameFileHandler::File::File(GobEngine *vm, const Common::String &base, const Common::String &ext) :
SlotFileStatic(vm, base, ext) {
}
SaveLoad_Adibou1::GameFileHandler::File::~File() {
}
SaveLoad_Adibou1::GameFileHandler::GameFileHandler(GobEngine *vm, const Common::String &target, const Common::String &ext) :
SaveHandler(vm), _file(vm, target, ext) {
}
SaveLoad_Adibou1::GameFileHandler::~GameFileHandler() {
}
int32 SaveLoad_Adibou1::GameFileHandler::getSize() {
Common::String fileName = _file.build();
if (fileName.empty())
return -1;
SaveReader reader(1, 0, fileName);
SaveHeader header;
if (!reader.load())
return -1;
if (!reader.readPartHeader(0, &header))
return -1;
// Return the part's size
return header.getSize();
}
bool SaveLoad_Adibou1::GameFileHandler::load(int16 dataVar, int32 size, int32 offset) {
Common::String fileName = _file.build();
if (fileName.empty())
return false;
if (size < 0) {
// Hack in original game, using a bitmap memory as a temporary buffer to make
// a copy the savegame. We do not need this copy, so we can just ignore it.
debugC(1, kDebugSaveLoad, "Ignoring bou.inf save with negative size");
return true;
}
if (size == 0) {
uint32 varSize = SaveHandler::getVarSize(_vm);
// Indicator to load all variables
dataVar = 0;
size = (int32) varSize;
}
int32 fileSize = getSize();
if (fileSize < 0)
return false;
SaveReader reader(1, 0, fileName);
SavePartVars vars(_vm, fileSize);
if (!reader.load()) {
return false;
}
if (!reader.readPart(0, &vars)) {
return false;
}
if (!vars.writeInto((uint16) dataVar, offset, size)) {
return false;
}
return true;
}
bool SaveLoad_Adibou1::GameFileHandler::save(const byte *ptrRaw, int16 dataVar, int32 size, int32 offset) {
Common::String fileName = _file.build();
if (fileName.empty())
return false;
if (size == 0) {
// Indicator to save all variables
dataVar = 0;
uint32 varSize = SaveHandler::getVarSize(_vm);
size = (int32) varSize;
}
int32 fileSize = getSize();
int32 newFileSize = size;
if (fileSize > 0) {
newFileSize = MAX<int32>(fileSize, size + offset);
}
SavePartVars vars(_vm, newFileSize);
if (fileSize > 0
&&
(offset > 0 || size < fileSize)) {
// Load data from file, as some of it will not be overwritten
SaveReader reader(1, 0, fileName);
if (!reader.load()) {
return false;
}
if (fileSize == newFileSize) {
// We can use the same SavePartVars object
if (!reader.readPart(0, &vars)) {
return false;
}
} else {
// We need to use a temporary SavePartVars object to load data
SavePartVars vars_from_file(_vm, fileSize);
if (!reader.readPart(0, &vars_from_file)) {;
return false;
}
// Copy data from temporary SavePartVars object to the real one
vars.readFromRaw(vars_from_file.data(), 0, fileSize);
}
}
SaveWriter writer(1, 0, fileName);
if (ptrRaw) {
// Write data from raw pointer
vars.readFromRaw(ptrRaw, offset, size);
} else {
// Write data from variables
if (!vars.readFrom((uint16) dataVar, offset, size))
return false;
}
return writer.writePart(0, &vars);
}
bool SaveLoad_Adibou1::GameFileHandler::save(int16 dataVar, int32 size, int32 offset) {
return save(nullptr, dataVar, size, offset);
}
bool SaveLoad_Adibou1::GameFileHandler::deleteFile() {
Common::String fileName = _file.build();
if (fileName.empty())
return false;
SaveWriter writer(1, 0, fileName);
return writer.deleteFile();
}
const SaveLoad_Adibou1::SaveFile *SaveLoad_Adibou1::getSaveFile(const char *fileName) const {
for (int i = 0; i < ARRAYSIZE(_saveFiles); i++)
if (!scumm_stricmp(fileName, _saveFiles[i].sourceName))
return &_saveFiles[i];
return 0;
}
SaveLoad_Adibou1::SaveFile *SaveLoad_Adibou1::getSaveFile(const char *fileName) {
fileName = stripPath(fileName);
for (int i = 0; i < ARRAYSIZE(_saveFiles); i++)
if (!scumm_stricmp(fileName, _saveFiles[i].sourceName))
return &_saveFiles[i];
return 0;
}
SaveHandler *SaveLoad_Adibou1::getHandler(const char *fileName) const {
const SaveFile *saveFile = getSaveFile(fileName);
if (saveFile)
return saveFile->handler;
return 0;
}
const char *SaveLoad_Adibou1::getDescription(const char *fileName) const {
const SaveFile *saveFile = getSaveFile(fileName);
if (saveFile)
return saveFile->description;
return 0;
}
SaveLoad::SaveMode SaveLoad_Adibou1::getSaveMode(const char *fileName) const {
const SaveFile *saveFile = getSaveFile(fileName);
if (saveFile)
return saveFile->mode;
return kSaveModeNone;
}
} // End of namespace Gob

View File

@@ -0,0 +1,324 @@
/* 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 <http://www.gnu.org/licenses/>.
*
*
* This file is dual-licensed.
* In addition to the GPLv3 license mentioned above, this code is also
* licensed under LGPL 2.1. See LICENSES/COPYING.LGPL file for the
* full text of the license.
*
*/
#include "gob/save/saveload.h"
#include "gob/save/saveconverter.h"
#include "gob/inter.h"
#include "gob/variables.h"
namespace Gob {
SaveLoad_Fascination::SaveFile SaveLoad_Fascination::_saveFiles[] = {
{ "cat.cat", kSaveModeSave, -1, nullptr, "savegame catalog"},
{ "save0.inf", kSaveModeSave, 0, nullptr, "savegame"},
{ "save1.inf", kSaveModeSave, 1, nullptr, "savegame"},
{ "save2.inf", kSaveModeSave, 2, nullptr, "savegame"},
{ "save3.inf", kSaveModeSave, 3, nullptr, "savegame"},
{ "save4.inf", kSaveModeSave, 4, nullptr, "savegame"},
{ "save5.inf", kSaveModeSave, 5, nullptr, "savegame"},
{ "save6.inf", kSaveModeSave, 6, nullptr, "savegame"},
{ "save7.inf", kSaveModeSave, 7, nullptr, "savegame"},
{ "save8.inf", kSaveModeSave, 8, nullptr, "savegame"},
{ "save9.inf", kSaveModeSave, 9, nullptr, "savegame"},
{ "save10.inf", kSaveModeSave, 10, nullptr, "savegame"},
{ "save11.inf", kSaveModeSave, 11, nullptr, "savegame"},
{ "save12.inf", kSaveModeSave, 12, nullptr, "savegame"},
{ "save13.inf", kSaveModeSave, 13, nullptr, "savegame"},
{ "save14.inf", kSaveModeSave, 14, nullptr, "savegame"},
};
SaveLoad_Fascination::GameHandler::File::File(GobEngine *vm, const char *base) :
SlotFileIndexed(vm, SaveLoad_Fascination::kSlotCount, base, "s") {
}
SaveLoad_Fascination::GameHandler::File::~File() {
}
int SaveLoad_Fascination::GameHandler::File::getSlot(int32 offset) const {
return ((offset - kIndexSize) / 320);
}
int SaveLoad_Fascination::GameHandler::File::getSlotRemainder(int32 offset) const {
return ((offset - kIndexSize) % 320);
}
SaveLoad_Fascination::GameHandler::GameHandler(GobEngine *vm, const char *target,
int slot, byte *index, bool *hasIndex) : SaveHandler(vm) {
_index = index;
_hasIndex = hasIndex;
_slot = slot;
_slotFile = new File(vm, target);
}
SaveLoad_Fascination::GameHandler::~GameHandler() {
delete _slotFile;
}
int32 SaveLoad_Fascination::GameHandler::getSize() {
uint32 varSize = SaveHandler::getVarSize(_vm);
if (varSize == 0)
return -1;
return _slotFile->tallyUpFiles(varSize, kIndexSize);
}
bool SaveLoad_Fascination::GameHandler::load(int16 dataVar, int32 size, int32 offset) {
uint32 varSize = SaveHandler::getVarSize(_vm);
if (varSize == 0)
return false;
if (size == 0) {
// Indicator to load all variables
dataVar = 0;
size = varSize;
}
if ((offset == 0) && (_slot == -1)) {
// Save index
if (((uint32) size) != kIndexSize) {
warning("Requested index has wrong size (%d)", size);
return false;
}
// Create/Fake the index
buildIndex(_vm->_inter->_variables->getAddressOff8(dataVar));
} else {
// Load slot
uint32 slot = _slot;
int slotRem = 0;
if (_slot == -1) {
slot = _slotFile->getSlot(offset);
slotRem = _slotFile->getSlotRemainder(offset);
}
debugC(2, kDebugSaveLoad, "Loading from slot %d", slot);
if ((slot >= kSlotCount) || (slotRem != 0) || (size != 320)) {
warning("Invalid loading procedure (%d, %d, %d, %d, %d)",
dataVar, size, offset, slot, slotRem);
return false;
}
Common::String slotFile = _slotFile->build(slot);
SaveReader *reader = nullptr;
// New save, load directly
reader = new SaveReader(2, slot, slotFile);
SavePartInfo info(kSlotNameLength, (uint32) _vm->getGameType(), 0,
_vm->getEndianness(), varSize);
SavePartVars vars(_vm, size);
if (!reader->load()) {
delete reader;
return false;
}
if (!reader->readPart(0, &info)) {
delete reader;
return false;
}
if (!reader->readPart(1, &vars)) {
delete reader;
return false;
}
// Get all variables
if (!vars.writeInto(dataVar, 0, size)) {
delete reader;
return false;
}
delete reader;
}
return true;
}
bool SaveLoad_Fascination::GameHandler::save(int16 dataVar, int32 size, int32 offset) {
if ((_slot == -1) && (offset == 0) && (size == 5400))
// Initialize empty file
return true;
uint32 varSize = SaveHandler::getVarSize(_vm);
if (varSize == 0)
return false;
if (size == 0) {
// Indicator to save all variables
dataVar = 0;
size = varSize;
}
if ((offset == 0) && (_slot == -1)) {
// Save index
if (((uint32) size) != kIndexSize) {
warning("Requested index has wrong size (%d)", size);
return false;
}
// Just copy the index into our buffer
_vm->_inter->_variables->copyTo(dataVar, _index, kIndexSize);
*_hasIndex = true;
} else {
// Save slot
uint32 slot = _slot;
int slotRem = 0;
if (_slot == -1) {
slot = _slotFile->getSlot(offset);
slotRem = _slotFile->getSlotRemainder(offset);
}
debugC(2, kDebugSaveLoad, "Saving to slot %d", slot);
if ((slot >= kSlotCount) || (slotRem != 0) || (size != 320)) {
warning("Invalid saving procedure (%d, %d, %d, %d, %d)",
dataVar, size, offset, slot, slotRem);
return false;
}
// An index is needed for the save slot description
if (!*_hasIndex) {
warning("No index written yet");
return false;
}
*_hasIndex = false;
Common::String slotFile = _slotFile->build(slot);
SaveWriter writer(2, slot, slotFile);
SavePartInfo info(kSlotNameLength, (uint32) _vm->getGameType(), 0,
_vm->getEndianness(), varSize);
SavePartVars vars(_vm, size);
// Write the description
info.setDesc(_index + (slot * kSlotNameLength), kSlotNameLength);
// Write all variables
if (!vars.readFrom(dataVar, 0, size))
return false;
if (!writer.writePart(0, &info))
return false;
if (!writer.writePart(1, &vars))
return false;
}
return true;
}
void SaveLoad_Fascination::GameHandler::buildIndex(byte *buffer) const {
uint32 varSize = SaveHandler::getVarSize(_vm);
if (varSize == 0)
return;
SavePartInfo info(kSlotNameLength, (uint32) _vm->getGameType(),
0, _vm->getEndianness(), varSize);
_slotFile->buildIndex(buffer, info, nullptr);
}
SaveLoad_Fascination::SaveLoad_Fascination(GobEngine *vm, const char *targetName) :
SaveLoad(vm) {
memset(_index, 0, kIndexSize);
_hasIndex = false;
for (int i = 0; i < 16; i++)
_saveFiles[i].handler = new GameHandler(vm, targetName, _saveFiles[i].slot, _index, &_hasIndex);
}
SaveLoad_Fascination::~SaveLoad_Fascination() {
for (int i = 0; i < 16; i++)
delete _saveFiles[i].handler;
}
const SaveLoad_Fascination::SaveFile *SaveLoad_Fascination::getSaveFile(const char *fileName) const {
fileName = stripPath(fileName);
for (int i = 0; i < ARRAYSIZE(_saveFiles); i++)
if (!scumm_stricmp(fileName, _saveFiles[i].sourceName))
return &_saveFiles[i];
return nullptr;
}
SaveLoad_Fascination::SaveFile *SaveLoad_Fascination::getSaveFile(const char *fileName) {
fileName = stripPath(fileName);
for (int i = 0; i < ARRAYSIZE(_saveFiles); i++)
if (!scumm_stricmp(fileName, _saveFiles[i].sourceName))
return &_saveFiles[i];
return nullptr;
}
SaveHandler *SaveLoad_Fascination::getHandler(const char *fileName) const {
const SaveFile *saveFile = getSaveFile(fileName);
if (saveFile)
return saveFile->handler;
return nullptr;
}
const char *SaveLoad_Fascination::getDescription(const char *fileName) const {
const SaveFile *saveFile = getSaveFile(fileName);
if (saveFile)
return saveFile->description;
return nullptr;
}
SaveLoad::SaveMode SaveLoad_Fascination::getSaveMode(const char *fileName) const {
const SaveFile *saveFile = getSaveFile(fileName);
if (saveFile)
return saveFile->mode;
return kSaveModeNone;
}
} // End of namespace Gob

View File

@@ -0,0 +1,220 @@
/* 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 <http://www.gnu.org/licenses/>.
*
*
* This file is dual-licensed.
* In addition to the GPLv3 license mentioned above, this code is also
* licensed under LGPL 2.1. See LICENSES/COPYING.LGPL file for the
* full text of the license.
*
*/
#include "gob/save/saveload.h"
#include "gob/save/saveconverter.h"
#include "gob/inter.h"
#include "gob/variables.h"
namespace Gob {
SaveLoad_Geisha::SaveFile SaveLoad_Geisha::_saveFiles[] = {
{"save.inf", kSaveModeSave, nullptr, "savegame"},
};
SaveLoad_Geisha::GameHandler::File::File(GobEngine *vm, const Common::String &base) :
SlotFileIndexed(vm, SaveLoad_Geisha::kSlotCount, base, "s") {
}
SaveLoad_Geisha::GameHandler::File::~File() {
}
int SaveLoad_Geisha::GameHandler::File::getSlot(int32 offset) const {
return 0;
}
int SaveLoad_Geisha::GameHandler::File::getSlotRemainder(int32 offset) const {
return 0;
}
SaveLoad_Geisha::GameHandler::GameHandler(GobEngine *vm, const Common::String &target) :
SaveHandler(vm), _file(vm, target) {
}
SaveLoad_Geisha::GameHandler::~GameHandler() {
}
int32 SaveLoad_Geisha::GameHandler::getSize() {
if (_file.getSlotMax() == 0)
return -1;
return SaveLoad_Geisha::kSaveFileSize;
}
bool SaveLoad_Geisha::GameHandler::load(int16 dataVar, int32 size, int32 offset) {
if ((size != 0) || (offset != 0)) {
warning("Invalid loading procedure: %d, %d, %d", dataVar, size, offset);
return false;
}
memset(_vm->_inter->_variables->getAddressOff8(dataVar), 0, SaveLoad_Geisha::kSaveFileSize);
for (uint32 slot = 0; slot < SaveLoad_Geisha::kSlotCount;
slot++, dataVar += SaveLoad_Geisha::kSlotSize) {
if (!_file.exists(slot))
continue;
Common::String slotFile = _file.build(slot);
if (slotFile.empty())
return false;
SaveReader reader(2, slot, slotFile);
if (!reader.load()) {
warning("Save slot %d contains corrupted save", slot);
continue;
}
SavePartInfo info(20, (uint32) _vm->getGameType(), 0,
_vm->getEndianness(), _vm->_inter->_variables->getSize());
SavePartVars vars(_vm, SaveLoad_Geisha::kSlotSize);
if (!reader.readPart(0, &info) || !reader.readPart(1, &vars)) {
warning("Save slot %d contains corrupted save", slot);
continue;
}
if (!vars.writeInto(dataVar, 0, SaveLoad_Geisha::kSlotSize)) {
warning("Save slot %d contains corrupted save", slot);
continue;
}
}
return true;
}
bool SaveLoad_Geisha::GameHandler::save(int16 dataVar, int32 size, int32 offset) {
if (((uint32)size != SaveLoad_Geisha::kSaveFileSize) || (offset != 0)) {
warning("Invalid saving procedure: %d, %d, %d", dataVar, size, offset);
return false;
}
for (uint32 slot = 0; slot < SaveLoad_Geisha::kSlotCount;
slot++, dataVar += SaveLoad_Geisha::kSlotSize) {
const byte *slotData = _vm->_inter->_variables->getAddressOff8(dataVar);
// Check of the slot's data is empty
bool empty = true;
for (uint32 j = 0; j < SaveLoad_Geisha::kSlotSize; j++) {
if (slotData[j] != 0) {
empty = false;
break;
}
}
// Don't save empty slots
if (empty)
continue;
Common::String slotFile = _file.build(slot);
if (slotFile.empty())
return false;
SaveWriter writer(2, slot, slotFile);
SavePartInfo info(20, (uint32) _vm->getGameType(), 0,
_vm->getEndianness(), _vm->_inter->_variables->getSize());
SavePartVars vars(_vm, SaveLoad_Geisha::kSlotSize);
info.setDesc(Common::String::format("Geisha, slot %d", slot).c_str());
if (!vars.readFrom(dataVar, 0, SaveLoad_Geisha::kSlotSize))
return false;
if (!writer.writePart(0, &info))
return false;
if (!writer.writePart(1, &vars))
return false;
}
return true;
}
SaveLoad_Geisha::SaveLoad_Geisha(GobEngine *vm, const char *targetName) :
SaveLoad(vm) {
_saveFiles[0].handler = new GameHandler(vm, targetName);
}
SaveLoad_Geisha::~SaveLoad_Geisha() {
for (int i = 0; i < ARRAYSIZE(_saveFiles); i++)
delete _saveFiles[i].handler;
}
const SaveLoad_Geisha::SaveFile *SaveLoad_Geisha::getSaveFile(const char *fileName) const {
fileName = stripPath(fileName);
for (int i = 0; i < ARRAYSIZE(_saveFiles); i++)
if (!scumm_stricmp(fileName, _saveFiles[i].sourceName))
return &_saveFiles[i];
return nullptr;
}
SaveLoad_Geisha::SaveFile *SaveLoad_Geisha::getSaveFile(const char *fileName) {
fileName = stripPath(fileName);
for (int i = 0; i < ARRAYSIZE(_saveFiles); i++)
if (!scumm_stricmp(fileName, _saveFiles[i].sourceName))
return &_saveFiles[i];
return nullptr;
}
SaveHandler *SaveLoad_Geisha::getHandler(const char *fileName) const {
const SaveFile *saveFile = getSaveFile(fileName);
if (saveFile)
return saveFile->handler;
return nullptr;
}
const char *SaveLoad_Geisha::getDescription(const char *fileName) const {
const SaveFile *saveFile = getSaveFile(fileName);
if (saveFile)
return saveFile->description;
return nullptr;
}
SaveLoad::SaveMode SaveLoad_Geisha::getSaveMode(const char *fileName) const {
const SaveFile *saveFile = getSaveFile(fileName);
if (saveFile)
return saveFile->mode;
return kSaveModeNone;
}
} // End of namespace Gob

View File

@@ -0,0 +1,454 @@
/* 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 <http://www.gnu.org/licenses/>.
*
*
* This file is dual-licensed.
* In addition to the GPLv3 license mentioned above, this code is also
* licensed under LGPL 2.1. See LICENSES/COPYING.LGPL file for the
* full text of the license.
*
*/
#include "gob/save/saveload.h"
#include "gob/save/saveconverter.h"
#include "gob/global.h"
#include "gob/inter.h"
namespace Gob {
SaveLoad_Inca2::SaveFile SaveLoad_Inca2::_saveFiles[] = {
{"speak.inf", kSaveModeExists, nullptr, nullptr}, // Exists = speech enabled
{"voice.inf", kSaveModeSave , nullptr, nullptr}, // Contains the language of the voices
{"intro.$$$", kSaveModeSave , nullptr, "temporary sprite"},
{ "cat.inf", kSaveModeSave , nullptr, "savegame"},
{ "ima.inf", kSaveModeSave , nullptr, "screenshot"},
};
SaveLoad_Inca2::GameHandler::File::File(GobEngine *vm, const char *base) :
SlotFileIndexed(vm, SaveLoad_Inca2::kSlotCount, base, "s") {
}
SaveLoad_Inca2::GameHandler::File::File(const File &file) :
SlotFileIndexed(file._vm, file._slotCount, file._base, file._ext) {
}
SaveLoad_Inca2::GameHandler::File::~File() {
}
int SaveLoad_Inca2::GameHandler::File::getSlot(int32 offset) const {
uint32 varSize = SaveHandler::getVarSize(_vm);
if (varSize == 0)
return -1;
return offset - kPropsSize;
}
int SaveLoad_Inca2::GameHandler::File::getSlotRemainder(int32 offset) const {
return 0;
}
SaveLoad_Inca2::GameHandler::GameHandler(GobEngine *vm, const char *target) :
SaveHandler(vm) {
_slotFile = new File(vm, target);
memset(_props, 0x00, kPropsSize);
memset(_props, 0x20, 10);
_props[43] = 0x01;
_props[79] = 0x03;
buildIndex();
_writer = nullptr;
_reader = nullptr;
}
SaveLoad_Inca2::GameHandler::~GameHandler() {
delete _slotFile;
delete _reader;
delete _writer;
}
int32 SaveLoad_Inca2::GameHandler::getSize() {
return _slotFile->tallyUpFiles(1, kPropsSize);
}
bool SaveLoad_Inca2::GameHandler::load(int16 dataVar, int32 size, int32 offset) {
if (((uint32) offset) < kPropsSize) {
// Global properties, like joker usage
debugC(3, kDebugSaveLoad, "Loading global properties");
if (((uint32) (offset + size)) > kPropsSize) {
warning("Wrong global properties list size (%d, %d)", size, offset);
return false;
}
if (((uint32) (offset + size)) >= kPropsSize)
buildIndex();
_vm->_inter->_variables->copyFrom(dataVar, _props + offset, size);
} else {
int32 slot = offset - kPropsSize;
if ((size != 1) || (slot < 0) || ((uint32)slot >= kSlotCount)) {
warning("Invalid loading procedure (%d, %d, %d)", dataVar, size, offset);
return false;
}
WRITE_VARO_UINT8(dataVar, 0);
if (!createReader(slot))
return true;
SavePartInfo info(0, (uint32) _vm->getGameType(), 0, _vm->getEndianness(), 1);
SavePartVars vars(_vm, 1);
if (!_reader->readPart(0, &info))
return true;
if (!_reader->readPart(1, &vars))
return true;
// Read the save point number
if (!vars.writeInto(dataVar, 0, 1)) {
WRITE_VARO_UINT8(dataVar, 0);
return true;
}
}
return true;
}
bool SaveLoad_Inca2::GameHandler::save(int16 dataVar, int32 size, int32 offset) {
if (((uint32) offset) < kPropsSize) {
// Global properties, like joker usage
debugC(3, kDebugSaveLoad, "Saving global properties");
if (((uint32) (offset + size)) > kPropsSize) {
warning("Wrong global properties list size (%d, %d)", size, offset);
return false;
}
_vm->_inter->_variables->copyTo(dataVar, _props + offset, size);
} else {
// Save point flag
int32 slot = offset - kPropsSize;
if ((size != 1) || (slot < 0) || ((uint32)slot >= kSlotCount)) {
warning("Invalid saving procedure (%d, %d, %d)", dataVar, size, offset);
return false;
}
if (!createWriter(slot))
return false;
SavePartInfo info(0, (uint32) _vm->getGameType(), 0, _vm->getEndianness(), 1);
SavePartVars vars(_vm, 1);
// Write the save point number
if (!vars.readFrom(dataVar, 0, 1))
return false;
if (!_writer->writePart(0, &info))
return false;
if (!_writer->writePart(1, &vars))
return false;
}
return true;
}
bool SaveLoad_Inca2::GameHandler::saveScreenshot(int slot,
const SavePartSprite *screenshot) {
if (!createWriter(slot))
return false;
return _writer->writePart(2, screenshot);
}
bool SaveLoad_Inca2::GameHandler::loadScreenshot(int slot,
SavePartSprite *screenshot) {
if (!createReader(slot))
return false;
return _reader->readPart(2, screenshot);
}
void SaveLoad_Inca2::GameHandler::buildIndex() {
_props[499] = _slotFile->getSlotMax();
}
bool SaveLoad_Inca2::GameHandler::createReader(int slot) {
// If slot < 0, just check if a reader exists
if (slot < 0)
return (_reader != nullptr);
if (!_reader || (_reader->getSlot() != ((uint32) slot))) {
Common::String slotFile = _slotFile->build(slot);
if (slotFile.empty())
return false;
delete _reader;
_reader = new SaveReader(3, slot, slotFile);
if (!_reader->load()) {
delete _reader;
_reader = nullptr;
return false;
}
}
return true;
}
bool SaveLoad_Inca2::GameHandler::createWriter(int slot) {
// If slot < 0, just check if a writer exists
if (slot < 0)
return (_writer != nullptr);
if (!_writer || (_writer->getSlot() != ((uint32) slot))) {
Common::String slotFile = _slotFile->build(slot);
if (slotFile.empty())
return false;
delete _writer;
_writer = new SaveWriter(3, slot, slotFile);
}
return true;
}
SaveLoad_Inca2::ScreenshotHandler::File::File(const SaveLoad_Inca2::GameHandler::File &file) : SaveLoad_Inca2::GameHandler::File(file) {
}
SaveLoad_Inca2::ScreenshotHandler::File::~File() {
}
int SaveLoad_Inca2::ScreenshotHandler::File::getSlot(int32 offset) const {
return (offset - 80) / 15168;
}
int SaveLoad_Inca2::ScreenshotHandler::File::getSlotRemainder(int32 offset) const {
return (offset - 80) % 15168;
}
void SaveLoad_Inca2::ScreenshotHandler::File::buildScreenshotIndex(byte *buffer) const {
Common::SaveFileManager *saveMan = g_system->getSavefileManager();
Common::InSaveFile *in;
for (uint32 i = 0; i < 40; i++, buffer++) {
Common::String slotFile = build(i);
if (!slotFile.empty() && ((in = saveMan->openForLoading(slotFile)))) {
delete in;
*buffer = 1;
} else
*buffer = 0;
}
}
SaveLoad_Inca2::ScreenshotHandler::ScreenshotHandler(GobEngine *vm,
GameHandler *gameHandler) : TempSpriteHandler(vm) {
assert(gameHandler);
_gameHandler = gameHandler;
_file = new File(*_gameHandler->_slotFile);
memset(_index, 0, 80);
}
SaveLoad_Inca2::ScreenshotHandler::~ScreenshotHandler() {
delete _file;
}
int32 SaveLoad_Inca2::ScreenshotHandler::getSize() {
return _file->tallyUpFiles(15168, 80);
}
bool SaveLoad_Inca2::ScreenshotHandler::load(int16 dataVar, int32 size, int32 offset) {
if (offset < 80) {
// Screenshot index list
if ((size + offset) > 80) {
warning("Wrong screenshot index offset (%d, %d)", size, offset);
return false;
}
// Create/Fake the index
_file->buildScreenshotIndex(_index + 40);
_vm->_inter->_variables->copyFrom(dataVar, _index + offset, size);
} else {
// Screenshot
uint32 slot = _file->getSlot(offset);
int slotRem = _file->getSlotRemainder(offset);
if ((slot >= kSlotCount) || (slotRem != 0)) {
warning("Invalid screenshot loading procedure (%d, %d, %d, %d, %d)",
dataVar, size, offset, slot, slotRem);
return false;
}
if (!TempSpriteHandler::createFromSprite(dataVar, size, offset))
return false;
if (!_gameHandler->loadScreenshot(slot, _sprite))
return false;
if (!TempSpriteHandler::load(dataVar, size, offset))
return false;
}
return true;
}
bool SaveLoad_Inca2::ScreenshotHandler::save(int16 dataVar, int32 size, int32 offset) {
if (offset < 80) {
// Index, we'll ignore that
} else {
// Screenshot
uint32 slot = _file->getSlot(offset);
int slotRem = _file->getSlotRemainder(offset);
if ((slot >= kSlotCount) || (slotRem != 0)) {
warning("Invalid screenshot saving procedure (%d, %d, %d, %d, %d)",
dataVar, size, offset, slot, slotRem);
return false;
}
if (!TempSpriteHandler::save(dataVar, size, offset))
return false;
return _gameHandler->saveScreenshot(slot, _sprite);
}
return true;
}
SaveLoad_Inca2::VoiceHandler::VoiceHandler(GobEngine *vm) : SaveHandler(vm) {
}
SaveLoad_Inca2::VoiceHandler::~VoiceHandler() {
}
int32 SaveLoad_Inca2::VoiceHandler::getSize() {
return 1;
}
bool SaveLoad_Inca2::VoiceHandler::load(int16 dataVar, int32 size, int32 offset) {
if ((size != 1) || (offset != 0)) {
warning("Invalid voice language loading?!? (%d, %d, %d)", dataVar, size, offset);
return false;
}
WRITE_VARO_UINT8(dataVar, _vm->_global->_language);
return true;
}
bool SaveLoad_Inca2::VoiceHandler::save(int16 dataVar, int32 size, int32 offset) {
return false;
}
SaveLoad_Inca2::SaveLoad_Inca2(GobEngine *vm, const char *targetName) : SaveLoad(vm) {
_voiceHandler = new VoiceHandler(vm);
_tempSpriteHandler = new TempSpriteHandler(vm);
_gameHandler = new GameHandler(vm, targetName);
_screenshotHandler = new ScreenshotHandler(vm, _gameHandler);
_saveFiles[1].handler = _voiceHandler;
_saveFiles[2].handler = _tempSpriteHandler;
_saveFiles[3].handler = _gameHandler;
_saveFiles[4].handler = _screenshotHandler;
}
SaveLoad_Inca2::~SaveLoad_Inca2() {
delete _voiceHandler;
}
const SaveLoad_Inca2::SaveFile *SaveLoad_Inca2::getSaveFile(const char *fileName) const {
fileName = stripPath(fileName);
for (int i = 0; i < ARRAYSIZE(_saveFiles); i++)
if (!scumm_stricmp(fileName, _saveFiles[i].sourceName))
return &_saveFiles[i];
return nullptr;
}
SaveLoad_Inca2::SaveFile *SaveLoad_Inca2::getSaveFile(const char *fileName) {
fileName = stripPath(fileName);
for (int i = 0; i < ARRAYSIZE(_saveFiles); i++)
if (!scumm_stricmp(fileName, _saveFiles[i].sourceName))
return &_saveFiles[i];
return nullptr;
}
SaveHandler *SaveLoad_Inca2::getHandler(const char *fileName) const {
const SaveFile *saveFile = getSaveFile(fileName);
if (saveFile)
return saveFile->handler;
return nullptr;
}
const char *SaveLoad_Inca2::getDescription(const char *fileName) const {
const SaveFile *saveFile = getSaveFile(fileName);
if (saveFile)
return saveFile->description;
return nullptr;
}
SaveLoad::SaveMode SaveLoad_Inca2::getSaveMode(const char *fileName) const {
const SaveFile *saveFile = getSaveFile(fileName);
if (saveFile)
return saveFile->mode;
return kSaveModeNone;
}
} // End of namespace Gob

View File

@@ -0,0 +1,364 @@
/* 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 <http://www.gnu.org/licenses/>.
*
*
* This file is dual-licensed.
* In addition to the GPLv3 license mentioned above, this code is also
* licensed under LGPL 2.1. See LICENSES/COPYING.LGPL file for the
* full text of the license.
*
*/
#include "gob/save/saveload.h"
#include "gob/inter.h"
#include "gob/variables.h"
namespace Gob {
SaveLoad_Playtoons::SaveFile SaveLoad_Playtoons::_saveFiles[] = {
{ "did.inf", kSaveModeSave, nullptr, nullptr}, // Purpose ignored at the moment, intensively used to save things.
{ "dan.itk", kSaveModeNone, nullptr, nullptr}, // Playtoons CK detection file
{ "cat.inf", kSaveModeNone, nullptr, nullptr},
{ "titre.009", kSaveModeIgnore, nullptr, nullptr}, // Playtoons theoritical title files that are checked for nothing
{ "titre.010", kSaveModeIgnore, nullptr, nullptr},
{ "titre.011", kSaveModeIgnore, nullptr, nullptr},
{ "titre.012", kSaveModeIgnore, nullptr, nullptr},
{ "titre.013", kSaveModeIgnore, nullptr, nullptr},
{ "titre.014", kSaveModeIgnore, nullptr, nullptr},
{ "titre.015", kSaveModeIgnore, nullptr, nullptr},
{ "titre.016", kSaveModeIgnore, nullptr, nullptr},
{ "titre.017", kSaveModeIgnore, nullptr, nullptr},
{ "titre.018", kSaveModeIgnore, nullptr, nullptr},
{ "titre.019", kSaveModeIgnore, nullptr, nullptr},
{ "titre.020", kSaveModeIgnore, nullptr, nullptr},
{ "titre.021", kSaveModeIgnore, nullptr, nullptr},
{ "titre.022", kSaveModeIgnore, nullptr, nullptr},
{ "titre.023", kSaveModeIgnore, nullptr, nullptr},
{ "titre.024", kSaveModeIgnore, nullptr, nullptr},
{ "titre.025", kSaveModeIgnore, nullptr, nullptr},
{ "titre.026", kSaveModeIgnore, nullptr, nullptr},
{ "titre.027", kSaveModeIgnore, nullptr, nullptr},
{ "titre.028", kSaveModeIgnore, nullptr, nullptr},
{ "titre.029", kSaveModeIgnore, nullptr, nullptr},
{ "titre.030", kSaveModeIgnore, nullptr, nullptr},
{ "titre.031", kSaveModeIgnore, nullptr, nullptr},
{ "titre.032", kSaveModeIgnore, nullptr, nullptr},
{ "titre.033", kSaveModeIgnore, nullptr, nullptr},
{ "titre.034", kSaveModeIgnore, nullptr, nullptr},
{ "titre.035", kSaveModeIgnore, nullptr, nullptr},
{ "titre.036", kSaveModeIgnore, nullptr, nullptr},
{ "titre.037", kSaveModeIgnore, nullptr, nullptr},
{ "titre.038", kSaveModeIgnore, nullptr, nullptr},
{ "titre.039", kSaveModeIgnore, nullptr, nullptr},
};
SaveLoad_Playtoons::GameHandler::File::File(GobEngine *vm, const char *base) :
SlotFileIndexed(vm, SaveLoad_Playtoons::kSlotCount, base, "s") {
}
SaveLoad_Playtoons::GameHandler::File::~File() {
}
int SaveLoad_Playtoons::GameHandler::File::getSlot(int32 offset) const {
uint32 varSize = SaveHandler::getVarSize(_vm);
if (varSize == 0)
return -1;
return ((offset - (kPropsSize + kIndexSize)) / varSize);
}
int SaveLoad_Playtoons::GameHandler::File::getSlotRemainder(int32 offset) const {
uint32 varSize = SaveHandler::getVarSize(_vm);
if (varSize == 0)
return -1;
return ((offset - (kPropsSize + kIndexSize)) % varSize);
}
SaveLoad_Playtoons::GameHandler::GameHandler(GobEngine *vm, const char *target) : SaveHandler(vm) {
memset(_props, 0, kPropsSize);
memset(_index, 0, kIndexSize);
_tempSpriteHandler = new TempSpriteHandler(vm);
_slotFile = new File(vm, target);
}
SaveLoad_Playtoons::GameHandler::~GameHandler() {
delete _slotFile;
delete _tempSpriteHandler;
}
int32 SaveLoad_Playtoons::GameHandler::getSize() {
uint32 varSize = SaveHandler::getVarSize(_vm);
if (varSize == 0)
return -1;
return _slotFile->tallyUpFiles(varSize, kPropsSize + kIndexSize);
}
bool SaveLoad_Playtoons::GameHandler::load(int16 dataVar, int32 size, int32 offset) {
uint32 varSize;
if (size < 0) {
// Load a temporary sprite
debugC(2, kDebugSaveLoad, "Loading temporary sprite %d at pos %d", size, offset);
_tempSpriteHandler->load(dataVar, size, offset);
return true;
}
varSize = SaveHandler::getVarSize(_vm);
if (varSize == 0)
return false;
if (size == 0) {
// Indicator to load all variables
dataVar = 0;
size = varSize;
}
if (((uint32) offset) < kPropsSize) {
// Properties
if (((uint32) (offset + size)) > kPropsSize) {
warning("Wrong index size (%d, %d)", size, offset);
return false;
}
_vm->_inter->_variables->copyFrom(dataVar, _props + offset, size);
} else if (((uint32) offset) < kPropsSize + kIndexSize) {
// Save index
if (((uint32) size) != kIndexSize) {
warning("Wrong index size (%d, %d)", size, offset);
return false;
}
buildIndex(_vm->_inter->_variables->getAddressOff8(dataVar));
} else {
// Save slot, whole variable block
uint32 slot = _slotFile->getSlot(offset);
int slotRem = _slotFile->getSlotRemainder(offset);
debugC(2, kDebugSaveLoad, "Loading from slot %d", slot);
if ((slot >= kSlotCount) || (slotRem != 0) ||
(dataVar != 0) || (((uint32) size) != varSize)) {
warning("Invalid loading procedure (%d, %d, %d, %d, %d)",
dataVar, size, offset, slot, slotRem);
return false;
}
Common::String slotFile = _slotFile->build(slot);
SaveReader *reader = nullptr;
// New save, load directly
reader = new SaveReader(2, slot, slotFile);
SavePartInfo info(kSlotNameLength, (uint32) _vm->getGameType(), 0,
_vm->getEndianness(), varSize);
SavePartVars vars(_vm, varSize);
if (!reader->load()) {
delete reader;
return false;
}
if (!reader->readPart(0, &info)) {
delete reader;
return false;
}
if (!reader->readPart(1, &vars)) {
delete reader;
return false;
}
// Get all variables
if (!vars.writeInto(0, 0, varSize)) {
delete reader;
return false;
}
delete reader;
}
return true;
}
bool SaveLoad_Playtoons::GameHandler::save(int16 dataVar, int32 size, int32 offset) {
uint32 varSize;
if (size < 0) {
// Save a temporary sprite
debugC(2, kDebugSaveLoad, "Saving temporary sprite %d at pos %d", size, offset);
_tempSpriteHandler->save(dataVar, size, offset);
return true;
}
varSize = SaveHandler::getVarSize(_vm);
if (varSize == 0)
return false;
if (size == 0) {
// Indicator to save all variables
dataVar = 0;
size = varSize;
}
if (((uint32) offset) < kPropsSize) {
// Properties
if (((uint32) (offset + size)) > kPropsSize) {
warning("Wrong index size (%d, %d)", size, offset);
return false;
}
_vm->_inter->_variables->copyTo(dataVar, _props + offset, size);
} else if (((uint32) offset) < kPropsSize + kIndexSize) {
// Save index
if (((uint32) size) != kIndexSize) {
warning("Wrong index size (%d, %d)", size, offset);
return false;
}
// Just copy the index into our buffer
_vm->_inter->_variables->copyTo(dataVar, _index, kIndexSize);
} else {
// Save slot, whole variable block
uint32 slot = _slotFile->getSlot(offset);
int slotRem = _slotFile->getSlotRemainder(offset);
debugC(2, kDebugSaveLoad, "Saving to slot %d", slot);
if ((slot >= kSlotCount) || (slotRem != 0) ||
(dataVar != 0) || (((uint32) size) != varSize)) {
warning("Invalid saving procedure (%d, %d, %d, %d, %d)",
dataVar, size, offset, slot, slotRem);
return false;
}
Common::String slotFile = _slotFile->build(slot);
SaveWriter writer(2, slot, slotFile);
SavePartInfo info(kSlotNameLength, (uint32) _vm->getGameType(), 0,
_vm->getEndianness(), varSize);
SavePartVars vars(_vm, varSize);
// Write the description
info.setDesc(_index + (slot * kSlotNameLength), kSlotNameLength);
// Write all variables
if (!vars.readFrom(0, 0, varSize))
return false;
if (!writer.writePart(0, &info))
return false;
if (!writer.writePart(1, &vars))
return false;
}
return true;
}
void SaveLoad_Playtoons::GameHandler::buildIndex(byte *buffer) const {
uint32 varSize = SaveHandler::getVarSize(_vm);
if (varSize == 0)
return;
SavePartInfo info(kSlotNameLength, (uint32) _vm->getGameType(),
0, _vm->getEndianness(), varSize);
_slotFile->buildIndex(buffer, info, nullptr);
}
SaveLoad_Playtoons::SaveLoad_Playtoons(GobEngine *vm, const char *targetName) :
SaveLoad(vm) {
_gameHandler = new GameHandler(vm, targetName);
_saveFiles[0].handler = _gameHandler;
}
SaveLoad_Playtoons::~SaveLoad_Playtoons() {
delete _gameHandler;
}
const SaveLoad_Playtoons::SaveFile *SaveLoad_Playtoons::getSaveFile(const char *fileName) const {
fileName = stripPath(fileName);
for (int i = 0; i < ARRAYSIZE(_saveFiles); i++)
if (!scumm_stricmp(fileName, _saveFiles[i].sourceName))
return &_saveFiles[i];
return nullptr;
}
SaveLoad_Playtoons::SaveFile *SaveLoad_Playtoons::getSaveFile(const char *fileName) {
fileName = stripPath(fileName);
for (int i = 0; i < ARRAYSIZE(_saveFiles); i++)
if (!scumm_stricmp(fileName, _saveFiles[i].sourceName))
return &_saveFiles[i];
return nullptr;
}
SaveHandler *SaveLoad_Playtoons::getHandler(const char *fileName) const {
const SaveFile *saveFile = getSaveFile(fileName);
if (saveFile)
return saveFile->handler;
return nullptr;
}
const char *SaveLoad_Playtoons::getDescription(const char *fileName) const {
const SaveFile *saveFile = getSaveFile(fileName);
if (saveFile)
return saveFile->description;
return nullptr;
}
SaveLoad::SaveMode SaveLoad_Playtoons::getSaveMode(const char *fileName) const {
const SaveFile *saveFile = getSaveFile(fileName);
if (saveFile)
return saveFile->mode;
return kSaveModeNone;
}
} // End of namespace Gob

View File

@@ -0,0 +1,325 @@
/* 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 <http://www.gnu.org/licenses/>.
*
*
* This file is dual-licensed.
* In addition to the GPLv3 license mentioned above, this code is also
* licensed under LGPL 2.1. See LICENSES/COPYING.LGPL file for the
* full text of the license.
*
*/
#include "gob/save/saveload.h"
#include "gob/save/saveconverter.h"
#include "gob/inter.h"
#include "gob/variables.h"
namespace Gob {
SaveLoad_v2::SaveFile SaveLoad_v2::_saveFiles[] = {
{ "cat.inf", kSaveModeSave, nullptr, "savegame"},
{ "cat.cat", kSaveModeSave, nullptr, "savegame"}, // Alternative file
{ "save.inf", kSaveModeSave, nullptr, "temporary sprite"},
{ "bloc.inf", kSaveModeSave, nullptr, "notes"},
};
SaveLoad_v2::GameHandler::File::File(GobEngine *vm, const char *base) :
SlotFileIndexed(vm, SaveLoad_v2::kSlotCount, base, "s") {
}
SaveLoad_v2::GameHandler::File::~File() {
}
int SaveLoad_v2::GameHandler::File::getSlot(int32 offset) const {
uint32 varSize = SaveHandler::getVarSize(_vm);
if (varSize == 0)
return -1;
return ((offset - kIndexSize) / varSize);
}
int SaveLoad_v2::GameHandler::File::getSlotRemainder(int32 offset) const {
uint32 varSize = SaveHandler::getVarSize(_vm);
if (varSize == 0)
return -1;
return ((offset - kIndexSize) % varSize);
}
SaveLoad_v2::GameHandler::GameHandler(GobEngine *vm, const char *target) : SaveHandler(vm) {
memset(_index, 0, kIndexSize);
_hasIndex = false;
_slotFile = new File(vm, target);
}
SaveLoad_v2::GameHandler::~GameHandler() {
delete _slotFile;
}
int32 SaveLoad_v2::GameHandler::getSize() {
uint32 varSize = SaveHandler::getVarSize(_vm);
if (varSize == 0)
return -1;
return _slotFile->tallyUpFiles(varSize, kIndexSize);
}
bool SaveLoad_v2::GameHandler::load(int16 dataVar, int32 size, int32 offset) {
uint32 varSize = SaveHandler::getVarSize(_vm);
if (varSize == 0)
return false;
if (size == 0) {
// Indicator to load all variables
dataVar = 0;
size = varSize;
}
if (offset == 0) {
// Save index
if (((uint32) size) != kIndexSize) {
warning("Requested index has wrong size (%d)", size);
return false;
}
// Create/Fake the index
buildIndex(_vm->_inter->_variables->getAddressOff8(dataVar));
} else {
// Save slot, whole variable block
uint32 slot = _slotFile->getSlot(offset);
int slotRem = _slotFile->getSlotRemainder(offset);
debugC(2, kDebugSaveLoad, "Loading from slot %d", slot);
if ((slot >= kSlotCount) || (slotRem != 0) ||
(dataVar != 0) || (((uint32) size) != varSize)) {
warning("Invalid loading procedure (%d, %d, %d, %d, %d)",
dataVar, size, offset, slot, slotRem);
return false;
}
Common::String slotFile = _slotFile->build(slot);
SaveReader *reader = nullptr;
SaveConverter_v2 converter(_vm, slotFile);
if (converter.isOldSave()) {
// Old save, plug the converter in
if (!converter.load())
return false;
reader = new SaveReader(2, slot, converter);
} else
// New save, load directly
reader = new SaveReader(2, slot, slotFile);
SavePartInfo info(kSlotNameLength, (uint32) _vm->getGameType(), 0,
_vm->getEndianness(), varSize);
SavePartVars vars(_vm, varSize);
if (!reader->load()) {
delete reader;
return false;
}
if (!reader->readPart(0, &info)) {
delete reader;
return false;
}
if (!reader->readPart(1, &vars)) {
delete reader;
return false;
}
// Get all variables
if (!vars.writeInto(0, 0, varSize)) {
delete reader;
return false;
}
delete reader;
}
return true;
}
bool SaveLoad_v2::GameHandler::save(int16 dataVar, int32 size, int32 offset) {
uint32 varSize = SaveHandler::getVarSize(_vm);
if (varSize == 0)
return false;
if (size == 0) {
// Indicator to save all variables
dataVar = 0;
size = varSize;
}
if (offset == 0) {
// Save index
if (((uint32) size) != kIndexSize) {
warning("Requested index has wrong size (%d)", size);
return false;
}
// Just copy the index into our buffer
_vm->_inter->_variables->copyTo(dataVar, _index, kIndexSize);
_hasIndex = true;
} else {
// Save slot, whole variable block
uint32 slot = _slotFile->getSlot(offset);
int slotRem = _slotFile->getSlotRemainder(offset);
debugC(2, kDebugSaveLoad, "Saving to slot %d", slot);
if ((slot >= kSlotCount) || (slotRem != 0) ||
(dataVar != 0) || (((uint32) size) != varSize)) {
warning("Invalid saving procedure (%d, %d, %d, %d, %d)",
dataVar, size, offset, slot, slotRem);
return false;
}
// An index is needed for the save slot description
if (!_hasIndex) {
warning("No index written yet");
return false;
}
_hasIndex = false;
Common::String slotFile = _slotFile->build(slot);
SaveWriter writer(2, slot, slotFile);
SavePartInfo info(kSlotNameLength, (uint32) _vm->getGameType(), 0,
_vm->getEndianness(), varSize);
SavePartVars vars(_vm, varSize);
// Write the description
info.setDesc(_index + (slot * kSlotNameLength), kSlotNameLength);
// Write all variables
if (!vars.readFrom(0, 0, varSize))
return false;
if (!writer.writePart(0, &info))
return false;
if (!writer.writePart(1, &vars))
return false;
}
return true;
}
void SaveLoad_v2::GameHandler::buildIndex(byte *buffer) const {
uint32 varSize = SaveHandler::getVarSize(_vm);
if (varSize == 0)
return;
SavePartInfo info(kSlotNameLength, (uint32) _vm->getGameType(),
0, _vm->getEndianness(), varSize);
SaveConverter_v2 converter(_vm);
_slotFile->buildIndex(buffer, info, &converter);
}
SaveLoad_v2::SaveLoad_v2(GobEngine *vm, const char *targetName) :
SaveLoad(vm) {
_gameHandler = new GameHandler(vm, targetName);
_notesHandler = new NotesHandler(600, vm, targetName);
_tempSpriteHandler = new TempSpriteHandler(vm);
_saveFiles[0].handler = _gameHandler;
_saveFiles[1].handler = _gameHandler;
_saveFiles[2].handler = _tempSpriteHandler;
_saveFiles[3].handler = _notesHandler;
}
SaveLoad_v2::~SaveLoad_v2() {
delete _gameHandler;
delete _notesHandler;
delete _tempSpriteHandler;
}
const SaveLoad_v2::SaveFile *SaveLoad_v2::getSaveFile(const char *fileName) const {
fileName = stripPath(fileName);
for (int i = 0; i < ARRAYSIZE(_saveFiles); i++)
if (!scumm_stricmp(fileName, _saveFiles[i].sourceName))
return &_saveFiles[i];
return nullptr;
}
SaveLoad_v2::SaveFile *SaveLoad_v2::getSaveFile(const char *fileName) {
fileName = stripPath(fileName);
for (int i = 0; i < ARRAYSIZE(_saveFiles); i++)
if (!scumm_stricmp(fileName, _saveFiles[i].sourceName))
return &_saveFiles[i];
return nullptr;
}
SaveHandler *SaveLoad_v2::getHandler(const char *fileName) const {
const SaveFile *saveFile = getSaveFile(fileName);
if (saveFile)
return saveFile->handler;
return nullptr;
}
const char *SaveLoad_v2::getDescription(const char *fileName) const {
const SaveFile *saveFile = getSaveFile(fileName);
if (saveFile)
return saveFile->description;
return nullptr;
}
SaveLoad::SaveMode SaveLoad_v2::getSaveMode(const char *fileName) const {
const SaveFile *saveFile = getSaveFile(fileName);
if (saveFile)
return saveFile->mode;
return kSaveModeNone;
}
} // End of namespace Gob

View File

@@ -0,0 +1,564 @@
/* 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 <http://www.gnu.org/licenses/>.
*
*
* This file is dual-licensed.
* In addition to the GPLv3 license mentioned above, this code is also
* licensed under LGPL 2.1. See LICENSES/COPYING.LGPL file for the
* full text of the license.
*
*/
#include "gob/save/saveload.h"
#include "gob/save/saveconverter.h"
#include "gob/inter.h"
#include "gob/variables.h"
namespace Gob {
SaveLoad_v3::SaveFile SaveLoad_v3::_saveFiles[] = {
{ "cat.inf", kSaveModeSave , nullptr, "savegame"},
{ "ima.inf", kSaveModeSave , nullptr, "screenshot"},
{ "intro.$$$", kSaveModeSave , nullptr, "temporary sprite"},
{ "bloc.inf", kSaveModeSave , nullptr, "notes"},
{ "prot", kSaveModeIgnore, nullptr, nullptr},
{ "config", kSaveModeIgnore, nullptr, nullptr},
};
SaveLoad_v3::GameHandler::File::File(GobEngine *vm, const char *base) :
SlotFileIndexed(vm, SaveLoad_v3::kSlotCount, base, "s") {
}
SaveLoad_v3::GameHandler::File::File(const File &file) :
SlotFileIndexed(file._vm, file._slotCount, file._base, file._ext) {
}
SaveLoad_v3::GameHandler::File::~File() {
}
int SaveLoad_v3::GameHandler::File::getSlot(int32 offset) const {
uint32 varSize = SaveHandler::getVarSize(_vm);
if (varSize == 0)
return -1;
return ((offset - (kPropsSize + kIndexSize)) / varSize);
}
int SaveLoad_v3::GameHandler::File::getSlotRemainder(int32 offset) const {
uint32 varSize = SaveHandler::getVarSize(_vm);
if (varSize == 0)
return -1;
return ((offset - (kPropsSize + kIndexSize)) % varSize);
}
SaveLoad_v3::GameHandler::GameHandler(GobEngine *vm, const char *target,
bool usesScreenshots) : SaveHandler(vm) {
_slotFile = new File(vm, target);
_usesScreenshots = usesScreenshots;
_firstSize = true;
memset(_props, 0, kPropsSize);
memset(_index, 0, kIndexSize);
_hasIndex = false;
_writer = nullptr;
_reader = nullptr;
}
SaveLoad_v3::GameHandler::~GameHandler() {
delete _slotFile;
delete _reader;
delete _writer;
}
int32 SaveLoad_v3::GameHandler::getSize() {
// Fake an empty save file for the very first query, to get clear properties
if (_firstSize) {
_firstSize = false;
return -1;
}
uint32 varSize = SaveHandler::getVarSize(_vm);
if (varSize == 0)
return -1;
return _slotFile->tallyUpFiles(varSize, kPropsSize + kIndexSize);
}
bool SaveLoad_v3::GameHandler::load(int16 dataVar, int32 size, int32 offset) {
uint32 varSize = SaveHandler::getVarSize(_vm);
if (varSize == 0)
return false;
if (size == 0) {
// Indicator to load all variables
dataVar = 0;
size = varSize;
}
if (((uint32) offset) < kPropsSize) {
// Global properties, like joker usage
debugC(3, kDebugSaveLoad, "Loading global properties");
if (((uint32) (offset + size)) > kPropsSize) {
warning("Wrong global properties list size (%d, %d)", size, offset);
return false;
}
_vm->_inter->_variables->copyFrom(dataVar, _props + offset, size);
} else if (((uint32) offset) == kPropsSize) {
// Save index
if (((uint32) size) != kIndexSize) {
warning("Requested index has wrong size (%d)", size);
return false;
}
// Create/Fake the index
buildIndex(_vm->_inter->_variables->getAddressOff8(dataVar));
} else {
// Save slot, whole variable block
uint32 slot = _slotFile->getSlot(offset);
int slotRem = _slotFile->getSlotRemainder(offset);
debugC(2, kDebugSaveLoad, "Loading from slot %d", slot);
if ((slot >= kSlotCount) || (slotRem != 0) ||
(dataVar != 0) || (((uint32) size) != varSize)) {
warning("Invalid saving procedure (%d, %d, %d, %d, %d)",
dataVar, size, offset, slot, slotRem);
return false;
}
_hasIndex = false;
if (!createReader(slot))
return false;
SavePartInfo info(kSlotNameLength, (uint32) _vm->getGameType(), 0,
_vm->getEndianness(), varSize);
SavePartVars vars(_vm, varSize);
if (!_reader->readPart(0, &info))
return false;
if (!_reader->readPart(1, &vars))
return false;
// Get all variables
if (!vars.writeInto(0, 0, varSize))
return false;
}
return true;
}
bool SaveLoad_v3::GameHandler::save(int16 dataVar, int32 size, int32 offset) {
uint32 varSize = SaveHandler::getVarSize(_vm);
if (varSize == 0)
return false;
if (size == 0) {
// Indicator to save all variables
dataVar = 0;
size = varSize;
}
if (((uint32) offset) < kPropsSize) {
// Global properties, like joker usage
debugC(3, kDebugSaveLoad, "Saving global properties");
if (((uint32) (offset + size)) > kPropsSize) {
warning("Wrong global properties list size (%d, %d)", size, offset);
return false;
}
_vm->_inter->_variables->copyTo(dataVar, _props + offset, size);
} else if (((uint32) offset) == kPropsSize) {
// Save index
if (((uint32) size) != kIndexSize) {
warning("Requested index has wrong size (%d)", size);
return false;
}
// Just copy the index into our buffer
_vm->_inter->_variables->copyTo(dataVar, _index, kIndexSize);
_hasIndex = true;
} else {
// Save slot, whole variable block
uint32 slot = _slotFile->getSlot(offset);
int slotRem = _slotFile->getSlotRemainder(offset);
debugC(2, kDebugSaveLoad, "Saving to slot %d", slot);
if ((slot >= kSlotCount) || (slotRem != 0) ||
(dataVar != 0) || (((uint32) size) != varSize)) {
warning("Invalid saving procedure (%d, %d, %d, %d, %d)",
dataVar, size, offset, slot, slotRem);
return false;
}
// An index is needed for the save slot description
if (!_hasIndex) {
warning("No index written yet");
return false;
}
_hasIndex = false;
if (!createWriter(slot))
return false;
SavePartInfo info(kSlotNameLength, (uint32) _vm->getGameType(), 0,
_vm->getEndianness(), varSize);
SavePartVars vars(_vm, varSize);
// Write the description
info.setDesc(_index + (slot * kSlotNameLength), kSlotNameLength);
// Write all variables
if (!vars.readFrom(0, 0, varSize))
return false;
if (!_writer->writePart(0, &info))
return false;
if (!_writer->writePart(1, &vars))
return false;
}
return true;
}
bool SaveLoad_v3::GameHandler::saveScreenshot(int slot,
const SavePartSprite *screenshot) {
if (!createWriter(slot))
return false;
return _writer->writePart(2, screenshot);
}
bool SaveLoad_v3::GameHandler::loadScreenshot(int slot,
SavePartSprite *screenshot) {
if (!createReader(slot))
return false;
return _reader->readPart(2, screenshot);
}
void SaveLoad_v3::GameHandler::buildIndex(byte *buffer) const {
uint32 varSize = SaveHandler::getVarSize(_vm);
if (varSize == 0)
return;
SavePartInfo info(40, (uint32) _vm->getGameType(), 0, _vm->getEndianness(), varSize);
SaveConverter_v3 converter(_vm);
_slotFile->buildIndex(buffer, info, &converter);
}
bool SaveLoad_v3::GameHandler::createReader(int slot) {
// If slot < 0, just check if a reader exists
if (slot < 0)
return (_reader != nullptr);
if (!_reader || (_reader->getSlot() != ((uint32) slot))) {
Common::String slotFile = _slotFile->build(slot);
if (slotFile.empty())
return false;
delete _reader;
SaveConverter_v3 converter(_vm, slotFile);
if (converter.isOldSave()) {
// Old save, plug the converter in
if (!converter.load()) {
return false;
}
_reader = new SaveReader(_usesScreenshots ? 3 : 2, slot, converter);
} else
_reader = new SaveReader(_usesScreenshots ? 3 : 2, slot, slotFile);
if (!_reader->load()) {
delete _reader;
_reader = nullptr;
return false;
}
}
return true;
}
bool SaveLoad_v3::GameHandler::createWriter(int slot) {
// If slot < 0, just check if a writer exists
if (slot < 0)
return (_writer != nullptr);
if (!_writer || (_writer->getSlot() != ((uint32) slot))) {
Common::String slotFile = _slotFile->build(slot);
if (slotFile.empty())
return false;
delete _writer;
_writer = new SaveWriter(_usesScreenshots ? 3 : 2, slot, slotFile);
}
return true;
}
SaveLoad_v3::ScreenshotHandler::File::File(const SaveLoad_v3::GameHandler::File &file,
uint32 shotSize, uint32 shotIndexSize) : SaveLoad_v3::GameHandler::File(file) {
_shotSize = shotSize;
_shotIndexSize = shotIndexSize;
}
SaveLoad_v3::ScreenshotHandler::File::~File() {
}
int SaveLoad_v3::ScreenshotHandler::File::getSlot(int32 offset) const {
return ((offset - _shotIndexSize) / _shotSize);
}
int SaveLoad_v3::ScreenshotHandler::File::getSlotRemainder(int32 offset) const {
return ((offset - _shotIndexSize) % _shotSize);
}
void SaveLoad_v3::ScreenshotHandler::File::buildScreenshotIndex(byte *buffer) const {
Common::SaveFileManager *saveMan = g_system->getSavefileManager();
Common::InSaveFile *in;
for (uint32 i = 0; i < _slotCount; i++, buffer++) {
Common::String slotFile = build(i);
if (!slotFile.empty() && ((in = saveMan->openForLoading(slotFile)))) {
delete in;
*buffer = 1;
} else
*buffer = 0;
}
}
SaveLoad_v3::ScreenshotHandler::ScreenshotHandler(GobEngine *vm,
GameHandler *gameHandler, ScreenshotType sShotType) : TempSpriteHandler(vm) {
assert(gameHandler);
_gameHandler = gameHandler;
_sShotType = sShotType;
_shotSize = (_sShotType == kScreenshotTypeLost) ? 4768 : 19968;
_shotIndexSize = (_sShotType == kScreenshotTypeLost) ? 50 : 80;
_file = new File(*_gameHandler->_slotFile, _shotSize, _shotIndexSize);
memset(_index, 0, 80);
}
SaveLoad_v3::ScreenshotHandler::~ScreenshotHandler() {
delete _file;
}
int32 SaveLoad_v3::ScreenshotHandler::getSize() {
return _file->tallyUpFiles(_shotSize, _shotIndexSize);
}
bool SaveLoad_v3::ScreenshotHandler::load(int16 dataVar, int32 size, int32 offset) {
if (offset < _shotIndexSize) {
// Screenshot index list
if ((size + offset) > _shotIndexSize) {
warning("Wrong screenshot index offset (%d, %d)", size, offset);
return false;
}
if (_sShotType == kScreenshotTypeGob3) {
// Create/Fake the index
_file->buildScreenshotIndex(_index + 40);
// The last 10 bytes are 0
memset(_index + 70, 0, 10);
} else if (_sShotType == kScreenshotTypeLost) {
// Create/Fake the index
_file->buildScreenshotIndex(_index);
// The last byte is 0
_index[30] = 0;
}
_vm->_inter->_variables->copyFrom(dataVar, _index + offset, size);
} else {
// Screenshot
uint32 slot = _file->getSlot(offset);
int slotRem = _file->getSlotRemainder(offset);
if ((slot >= kSlotCount) || (slotRem != 0))
return false;
if (!TempSpriteHandler::createFromSprite(dataVar, size, offset))
return false;
if (!_gameHandler->loadScreenshot(slot, _sprite))
return false;
if (!TempSpriteHandler::load(dataVar, size, offset))
return false;
}
return true;
}
bool SaveLoad_v3::ScreenshotHandler::save(int16 dataVar, int32 size, int32 offset) {
if (offset < _shotIndexSize) {
// Screenshot index list
if ((size + offset) > _shotIndexSize) {
warning("Wrong screenshot index offset (%d, %d)", size, offset);
return false;
}
_vm->_inter->_variables->copyTo(dataVar, _index + offset, size);
} else {
// Screenshot
if (!TempSpriteHandler::save(dataVar, size, offset))
return false;
uint32 slot = _file->getSlot(offset);
int slotRem = _file->getSlotRemainder(offset);
if ((slot >= kSlotCount) || (slotRem != 0))
return false;
return _gameHandler->saveScreenshot(slot, _sprite);
}
return true;
}
SaveLoad_v3::SaveLoad_v3(GobEngine *vm, const char *targetName, ScreenshotType sShotType) :
SaveLoad(vm) {
_sShotType = sShotType;
// The Amiga version doesn't use screenshots
if (_vm->getPlatform() == Common::kPlatformAmiga) {
_gameHandler = new GameHandler(vm, targetName, false);
_screenshotHandler = nullptr;
} else {
_gameHandler = new GameHandler(vm, targetName, true);
_screenshotHandler = new ScreenshotHandler(vm, _gameHandler, sShotType);
}
_tempSpriteHandler = new TempSpriteHandler(vm);
_notesHandler = new NotesHandler(2560, vm, targetName);
_saveFiles[0].handler = _gameHandler;
_saveFiles[1].handler = _screenshotHandler;
_saveFiles[2].handler = _tempSpriteHandler;
_saveFiles[3].handler = _notesHandler;
}
SaveLoad_v3::~SaveLoad_v3() {
delete _screenshotHandler;
delete _gameHandler;
delete _notesHandler;
delete _tempSpriteHandler;
}
const SaveLoad_v3::SaveFile *SaveLoad_v3::getSaveFile(const char *fileName) const {
fileName = stripPath(fileName);
for (int i = 0; i < ARRAYSIZE(_saveFiles); i++)
if (!scumm_stricmp(fileName, _saveFiles[i].sourceName))
return &_saveFiles[i];
return nullptr;
}
SaveLoad_v3::SaveFile *SaveLoad_v3::getSaveFile(const char *fileName) {
fileName = stripPath(fileName);
for (int i = 0; i < ARRAYSIZE(_saveFiles); i++)
if (!scumm_stricmp(fileName, _saveFiles[i].sourceName))
return &_saveFiles[i];
return nullptr;
}
SaveHandler *SaveLoad_v3::getHandler(const char *fileName) const {
const SaveFile *saveFile = getSaveFile(fileName);
if (saveFile)
return saveFile->handler;
return nullptr;
}
const char *SaveLoad_v3::getDescription(const char *fileName) const {
const SaveFile *saveFile = getSaveFile(fileName);
if (saveFile)
return saveFile->description;
return nullptr;
}
SaveLoad::SaveMode SaveLoad_v3::getSaveMode(const char *fileName) const {
const SaveFile *saveFile = getSaveFile(fileName);
if (saveFile)
return saveFile->mode;
return kSaveModeNone;
}
} // End of namespace Gob

View File

@@ -0,0 +1,566 @@
/* 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 <http://www.gnu.org/licenses/>.
*
*
* This file is dual-licensed.
* In addition to the GPLv3 license mentioned above, this code is also
* licensed under LGPL 2.1. See LICENSES/COPYING.LGPL file for the
* full text of the license.
*
*/
#include "gob/save/saveload.h"
#include "gob/save/saveconverter.h"
#include "gob/inter.h"
#include "gob/variables.h"
namespace Gob {
SaveLoad_v4::SaveFile SaveLoad_v4::_saveFiles[] = {
{ "cat.inf", kSaveModeSave, nullptr, "savegame"},
{ "save.tmp", kSaveModeSave, nullptr, "current screen properties"},
{ "save0.tmp", kSaveModeSave, nullptr, "savegame screen properties"}, // Slot 0
{ "save1.tmp", kSaveModeSave, nullptr, "savegame screen properties"}, // Slot 1
{ "save2.tmp", kSaveModeSave, nullptr, "savegame screen properties"}, // Slot 2
{ "save3.tmp", kSaveModeSave, nullptr, "savegame screen properties"}, // Slot 3
{ "save4.tmp", kSaveModeSave, nullptr, "savegame screen properties"}, // Slot 4
{ "save5.tmp", kSaveModeSave, nullptr, "savegame screen properties"}, // Slot 5
{ "save6.tmp", kSaveModeSave, nullptr, "savegame screen properties"}, // Slot 6
{ "save7.tmp", kSaveModeSave, nullptr, "savegame screen properties"}, // Slot 7
{ "save8.tmp", kSaveModeSave, nullptr, "savegame screen properties"}, // Slot 8
{ "save9.tmp", kSaveModeSave, nullptr, "savegame screen properties"}, // Slot 9
};
SaveLoad_v4::GameHandler::File::File(GobEngine *vm, const char *base) :
SlotFileIndexed(vm, SaveLoad_v4::kSlotCount, base, "s") {
}
SaveLoad_v4::GameHandler::File::File(const File &file) :
SlotFileIndexed(file._vm, file._slotCount, file._base, file._ext) {
}
SaveLoad_v4::GameHandler::File::~File() {
}
int SaveLoad_v4::GameHandler::File::getSlot(int32 offset) const {
uint32 varSize = SaveHandler::getVarSize(_vm);
if (varSize == 0)
return -1;
return ((offset - (kPropsSize + kIndexSize)) / varSize);
}
int SaveLoad_v4::GameHandler::File::getSlotRemainder(int32 offset) const {
uint32 varSize = SaveHandler::getVarSize(_vm);
if (varSize == 0)
return -1;
return ((offset - (kPropsSize + kIndexSize)) % varSize);
}
SaveLoad_v4::GameHandler::GameHandler(GobEngine *vm, const char *target) : SaveHandler(vm) {
_firstSize = true;
memset(_props, 0, kPropsSize);
memset(_index, 0, kIndexSize);
_hasIndex = false;
_slotFile = new File(vm, target);
_lastSlot = -1;
_writer = nullptr;
_reader = nullptr;
}
SaveLoad_v4::GameHandler::~GameHandler() {
delete _slotFile;
delete _reader;
delete _writer;
}
int SaveLoad_v4::GameHandler::getLastSlot() const {
return _lastSlot;
}
int32 SaveLoad_v4::GameHandler::getSize() {
// Fake an empty save file for the very first query, to get clear properties
if (_firstSize) {
_firstSize = false;
return -1;
}
uint32 varSize = SaveHandler::getVarSize(_vm);
if (varSize == 0)
return -1;
return _slotFile->tallyUpFiles(varSize, kPropsSize + kIndexSize);
}
bool SaveLoad_v4::GameHandler::load(int16 dataVar, int32 size, int32 offset) {
uint32 varSize = SaveHandler::getVarSize(_vm);
if (varSize == 0)
return false;
if (size == 0) {
// Indicator to load all variables
dataVar = 0;
size = varSize;
}
if (((uint32) offset) < kPropsSize) {
// Global properties
debugC(3, kDebugSaveLoad, "Loading global properties");
if (((uint32) (offset + size)) > kPropsSize) {
warning("Wrong global properties list size (%d, %d)", size, offset);
return false;
}
_vm->_inter->_variables->copyFrom(dataVar, _props + offset, size);
} else if (((uint32) offset) == kPropsSize) {
// Save index
if (((uint32) size) != kIndexSize) {
warning("Requested index has wrong size (%d)", size);
return false;
}
// Create/Fake the index
buildIndex(_vm->_inter->_variables->getAddressOff8(dataVar));
} else {
// Save slot, whole variable block
uint32 slot = _slotFile->getSlot(offset);
int slotRem = _slotFile->getSlotRemainder(offset);
debugC(2, kDebugSaveLoad, "Loading from slot %d", slot);
if ((slot >= kSlotCount) || (slotRem != 0) ||
(dataVar != 0) || (((uint32) size) != varSize)) {
warning("Invalid saving procedure (%d, %d, %d, %d, %d)",
dataVar, size, offset, slot, slotRem);
return false;
}
_hasIndex = false;
if (!createReader(slot))
return false;
SavePartInfo info(kSlotNameLength, (uint32) _vm->getGameType(), 0,
_vm->getEndianness(), varSize);
SavePartVars vars(_vm, varSize);
if (!_reader->readPart(0, &info))
return false;
if (!_reader->readPart(1, &vars))
return false;
// Get all variables
if (!vars.writeInto(0, 0, varSize))
return false;
_lastSlot = slot;
}
return true;
}
bool SaveLoad_v4::GameHandler::save(int16 dataVar, int32 size, int32 offset) {
uint32 varSize = SaveHandler::getVarSize(_vm);
if (varSize == 0)
return false;
if (size == 0) {
// Indicator to load all variables
dataVar = 0;
size = varSize;
}
if (((uint32) offset) < kPropsSize) {
// Global properties
debugC(3, kDebugSaveLoad, "Saving global properties");
if (((uint32) (offset + size)) > kPropsSize) {
warning("Wrong global properties list size (%d, %d)", size, offset);
return false;
}
_vm->_inter->_variables->copyTo(dataVar, _props + offset, size);
} else if (((uint32) offset) == kPropsSize) {
// Save index
if (((uint32) size) != kIndexSize) {
warning("Requested index has wrong size (%d)", size);
return false;
}
// Just copy the index into our buffer
_vm->_inter->_variables->copyTo(dataVar, _index, kIndexSize);
_hasIndex = true;
} else {
// Save slot, whole variable block
uint32 slot = _slotFile->getSlot(offset);
int slotRem = _slotFile->getSlotRemainder(offset);
debugC(2, kDebugSaveLoad, "Saving to slot %d", slot);
if ((slot >= kSlotCount) || (slotRem != 0) ||
(dataVar != 0) || (((uint32) size) != varSize)) {
warning("Invalid saving procedure (%d, %d, %d, %d, %d)",
dataVar, size, offset, slot, slotRem);
return false;
}
// An index is needed for the save slot description
if (!_hasIndex) {
warning("No index written yet");
return false;
}
_hasIndex = false;
if (!createWriter(slot))
return false;
SavePartInfo info(kSlotNameLength, (uint32) _vm->getGameType(), 0,
_vm->getEndianness(), varSize);
SavePartVars vars(_vm, varSize);
// Write the description
info.setDesc(_index + (slot * kSlotNameLength), kSlotNameLength);
// Write all variables
if (!vars.readFrom(0, 0, varSize))
return false;
if (!_writer->writePart(0, &info))
return false;
if (!_writer->writePart(1, &vars))
return false;
_lastSlot = slot;
}
return true;
}
bool SaveLoad_v4::GameHandler::saveScreenProps(int slot, const byte *props) {
if (!createWriter(slot))
return false;
SavePartMem mem(256000);
if (!mem.readFrom(props, 0, 256000))
return false;
return _writer->writePart(2, &mem);
}
bool SaveLoad_v4::GameHandler::loadScreenProps(int slot, byte *props) {
if (!createReader(slot))
return false;
SavePartMem mem(256000);
if (!_reader->readPart(2, &mem))
return false;
if (!mem.writeInto(props, 0, 256000))
return false;
return true;
}
void SaveLoad_v4::GameHandler::buildIndex(byte *buffer) const {
uint32 varSize = SaveHandler::getVarSize(_vm);
if (varSize == 0)
return;
SavePartInfo info(kSlotNameLength, (uint32) _vm->getGameType(),
0, _vm->getEndianness(), varSize);
SaveConverter_v4 converter(_vm);
_slotFile->buildIndex(buffer, info, &converter);
// 400 bytes index + 800 bytes 0
memset(buffer + 400, 0, 800);
}
bool SaveLoad_v4::GameHandler::createReader(int slot) {
// If slot < 0, just check if a reader exists
if (slot < 0)
return (_reader != nullptr);
if (!_reader || (_reader->getSlot() != ((uint32) slot))) {
Common::String slotFile = _slotFile->build(slot);
if (slotFile.empty())
return false;
delete _reader;
SaveConverter_v4 converter(_vm, slotFile);
if (converter.isOldSave()) {
// Old save, plug the converter in
if (!converter.load()) {
return false;
}
_reader = new SaveReader(3, slot, converter);
} else
_reader = new SaveReader(3, slot, slotFile);
if (!_reader->load()) {
delete _reader;
_reader = nullptr;
return false;
}
}
return true;
}
bool SaveLoad_v4::GameHandler::createWriter(int slot) {
// If slot < 0, just check if a writer exists
if (slot < 0)
return (_writer != nullptr);
if (!_writer || (_writer->getSlot() != ((uint32) slot))) {
Common::String slotFile = _slotFile->build(slot);
if (slotFile.empty())
return false;
delete _writer;
_writer = new SaveWriter(3, slot, slotFile);
}
return true;
}
SaveLoad_v4::CurScreenPropsHandler::CurScreenPropsHandler(GobEngine *vm) :
SaveHandler(vm) {
_props = new byte[256000]();
}
SaveLoad_v4::CurScreenPropsHandler::~CurScreenPropsHandler() {
delete[] _props;
}
int32 SaveLoad_v4::CurScreenPropsHandler::getSize() {
return 256000;
}
bool SaveLoad_v4::CurScreenPropsHandler::load(int16 dataVar,
int32 size, int32 offset) {
// Using a sprite as a buffer
if (size <= 0)
return true;
if ((offset < 0) || (size + offset) > 256000) {
warning("Invalid size (%d) or offset (%d)", size, offset);
return false;
}
debugC(3, kDebugSaveLoad, "Loading screen properties (%d, %d, %d)",
dataVar, size, offset);
_vm->_inter->_variables->copyFrom(dataVar, _props + offset, size);
return true;
}
bool SaveLoad_v4::CurScreenPropsHandler::save(int16 dataVar,
int32 size, int32 offset) {
// Using a sprite as a buffer
if (size <= 0)
return true;
if ((offset < 0) || (size + offset) > 256000) {
warning("Invalid size (%d) or offset (%d)", size, offset);
return false;
}
debugC(3, kDebugSaveLoad, "Saving screen properties (%d, %d, %d)",
dataVar, size, offset);
_vm->_inter->_variables->copyTo(dataVar, _props + offset, size);
return true;
}
SaveLoad_v4::ScreenPropsHandler::File::File(const SaveLoad_v4::GameHandler::File &file,
uint32 slot) : SaveLoad_v4::GameHandler::File(file) {
_slot = slot;
}
SaveLoad_v4::ScreenPropsHandler::File::~File() {
}
int SaveLoad_v4::ScreenPropsHandler::File::getSlot(int32 offset) const {
return _slot;
}
int SaveLoad_v4::ScreenPropsHandler::File::getSlotRemainder(int32 offset) const {
return 0;
}
SaveLoad_v4::ScreenPropsHandler::ScreenPropsHandler(GobEngine *vm, uint32 slot,
CurScreenPropsHandler *curProps, GameHandler *gameHandler) : SaveHandler(vm) {
_slot = slot;
_curProps = curProps;
_gameHandler = gameHandler;
_file = new File(*_gameHandler->_slotFile, _slot);
}
SaveLoad_v4::ScreenPropsHandler::~ScreenPropsHandler() {
delete _file;
}
int32 SaveLoad_v4::ScreenPropsHandler::getSize() {
if (_file->exists(_slot))
return 256000;
return 0;
}
bool SaveLoad_v4::ScreenPropsHandler::load(int16 dataVar, int32 size, int32 offset) {
if (size != -5) {
warning("Invalid saving procedure (%d, %d, %d)", dataVar, size, offset);
return false;
}
int slot = _gameHandler->getLastSlot();
if (slot == -1)
slot = _file->getSlot(offset);
return _gameHandler->loadScreenProps(slot, _curProps->_props);
}
bool SaveLoad_v4::ScreenPropsHandler::save(int16 dataVar, int32 size, int32 offset) {
if (size != -5) {
warning("Invalid saving procedure (%d, %d, %d)", dataVar, size, offset);
return false;
}
int slot = _gameHandler->getLastSlot();
if (slot == -1)
slot = _file->getSlot(offset);
return _gameHandler->saveScreenProps(slot, _curProps->_props);
}
SaveLoad_v4::SaveLoad_v4(GobEngine *vm, const char *targetName) :
SaveLoad(vm) {
_gameHandler = new GameHandler(vm, targetName);
_curProps = new CurScreenPropsHandler(vm);
for (int i = 0; i < 10; i++)
_props[i] = new ScreenPropsHandler(vm, i, _curProps, _gameHandler);
_saveFiles[0].handler = _gameHandler;
_saveFiles[1].handler = _curProps;
for (int i = 0; i < 10; i++)
_saveFiles[i + 2].handler = _props[i];
}
SaveLoad_v4::~SaveLoad_v4() {
delete _gameHandler;
delete _curProps;
for (int i = 0; i < 10; i++)
delete _props[i];
}
const SaveLoad_v4::SaveFile *SaveLoad_v4::getSaveFile(const char *fileName) const {
fileName = stripPath(fileName);
for (int i = 0; i < ARRAYSIZE(_saveFiles); i++)
if (!scumm_stricmp(fileName, _saveFiles[i].sourceName))
return &_saveFiles[i];
return nullptr;
}
SaveLoad_v4::SaveFile *SaveLoad_v4::getSaveFile(const char *fileName) {
fileName = stripPath(fileName);
for (int i = 0; i < ARRAYSIZE(_saveFiles); i++)
if (!scumm_stricmp(fileName, _saveFiles[i].sourceName))
return &_saveFiles[i];
return nullptr;
}
SaveHandler *SaveLoad_v4::getHandler(const char *fileName) const {
const SaveFile *saveFile = getSaveFile(fileName);
if (saveFile)
return saveFile->handler;
return nullptr;
}
const char *SaveLoad_v4::getDescription(const char *fileName) const {
const SaveFile *saveFile = getSaveFile(fileName);
if (saveFile)
return saveFile->description;
return nullptr;
}
SaveLoad::SaveMode SaveLoad_v4::getSaveMode(const char *fileName) const {
const SaveFile *saveFile = getSaveFile(fileName);
if (saveFile)
return saveFile->mode;
return kSaveModeNone;
}
} // End of namespace Gob

View File

@@ -0,0 +1,942 @@
/* 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 <http://www.gnu.org/licenses/>.
*
*
* This file is dual-licensed.
* In addition to the GPLv3 license mentioned above, this code is also
* licensed under LGPL 2.1. See LICENSES/COPYING.LGPL file for the
* full text of the license.
*
*/
#include "gob/save/saveload.h"
#include "gob/save/saveconverter.h"
#include "gob/inter.h"
#include "gob/variables.h"
namespace Gob {
SaveLoad_v6::SaveFile SaveLoad_v6::_saveFiles[] = {
{ "cat.inf", kSaveModeSave, nullptr, "savegame" }, // Save file
{ "cata1.inf", kSaveModeSave, nullptr, "autosave" }, // Autosave file
{ "mdo.def", kSaveModeExists, nullptr, nullptr },
{ "no_cd.txt", kSaveModeExists, nullptr, nullptr },
{ "vide.inf", kSaveModeIgnore, nullptr, nullptr },
{"fenetre.txt", kSaveModeIgnore, nullptr, nullptr },
{ "music.txt", kSaveModeIgnore, nullptr, nullptr },
{ "cata2.inf", kSaveModeSave, nullptr, "temp save" },
{ "cata3.inf", kSaveModeSave, nullptr, "temp save" },
{ "cata2.000", kSaveModeSave, nullptr, "extra save" }, // Slot 00
{ "cata2.001", kSaveModeSave, nullptr, "extra save" }, // Slot 01
{ "cata2.002", kSaveModeSave, nullptr, "extra save" }, // Slot 02
{ "cata2.003", kSaveModeSave, nullptr, "extra save" }, // Slot 03
{ "cata2.004", kSaveModeSave, nullptr, "extra save" }, // Slot 04
{ "cata2.005", kSaveModeSave, nullptr, "extra save" }, // Slot 05
{ "cata2.006", kSaveModeSave, nullptr, "extra save" }, // Slot 06
{ "cata2.007", kSaveModeSave, nullptr, "extra save" }, // Slot 07
{ "cata2.008", kSaveModeSave, nullptr, "extra save" }, // Slot 08
{ "cata2.009", kSaveModeSave, nullptr, "extra save" }, // Slot 09
{ "cata2.010", kSaveModeSave, nullptr, "extra save" }, // Slot 10
{ "cata2.011", kSaveModeSave, nullptr, "extra save" }, // Slot 11
{ "cata2.012", kSaveModeSave, nullptr, "extra save" }, // Slot 12
{ "cata2.013", kSaveModeSave, nullptr, "extra save" }, // Slot 13
{ "cata2.014", kSaveModeSave, nullptr, "extra save" }, // Slot 14
{ "cata2.015", kSaveModeSave, nullptr, "extra save" }, // Slot 15
{ "cata2.016", kSaveModeSave, nullptr, "extra save" }, // Slot 16
{ "cata2.017", kSaveModeSave, nullptr, "extra save" }, // Slot 17
{ "cata2.018", kSaveModeSave, nullptr, "extra save" }, // Slot 18
{ "cata2.019", kSaveModeSave, nullptr, "extra save" }, // Slot 19
{ "cata2.020", kSaveModeSave, nullptr, "extra save" }, // Slot 20
{ "cata2.021", kSaveModeSave, nullptr, "extra save" }, // Slot 21
{ "cata2.022", kSaveModeSave, nullptr, "extra save" }, // Slot 22
{ "cata2.023", kSaveModeSave, nullptr, "extra save" }, // Slot 23
{ "cata2.024", kSaveModeSave, nullptr, "extra save" }, // Slot 24
{ "cata2.025", kSaveModeSave, nullptr, "extra save" }, // Slot 25
{ "cata2.026", kSaveModeSave, nullptr, "extra save" }, // Slot 26
{ "cata2.027", kSaveModeSave, nullptr, "extra save" }, // Slot 27
{ "cata2.028", kSaveModeSave, nullptr, "extra save" }, // Slot 28
{ "cata2.029", kSaveModeSave, nullptr, "extra save" }, // Slot 29
{ "cata2.030", kSaveModeSave, nullptr, "extra save" }, // Slot 30
{ "cata2.031", kSaveModeSave, nullptr, "extra save" }, // Slot 31
{ "cata2.032", kSaveModeSave, nullptr, "extra save" }, // Slot 32
{ "cata2.033", kSaveModeSave, nullptr, "extra save" }, // Slot 33
{ "cata2.034", kSaveModeSave, nullptr, "extra save" }, // Slot 34
{ "cata2.035", kSaveModeSave, nullptr, "extra save" }, // Slot 35
{ "cata2.036", kSaveModeSave, nullptr, "extra save" }, // Slot 36
{ "cata2.037", kSaveModeSave, nullptr, "extra save" }, // Slot 37
{ "cata2.038", kSaveModeSave, nullptr, "extra save" }, // Slot 38
{ "cata2.039", kSaveModeSave, nullptr, "extra save" }, // Slot 39
{ "cata2.040", kSaveModeSave, nullptr, "extra save" }, // Slot 40
{ "cata2.041", kSaveModeSave, nullptr, "extra save" }, // Slot 41
{ "cata2.042", kSaveModeSave, nullptr, "extra save" }, // Slot 42
{ "cata2.043", kSaveModeSave, nullptr, "extra save" }, // Slot 43
{ "cata2.044", kSaveModeSave, nullptr, "extra save" }, // Slot 44
{ "cata2.045", kSaveModeSave, nullptr, "extra save" }, // Slot 45
{ "cata2.046", kSaveModeSave, nullptr, "extra save" }, // Slot 46
{ "cata2.047", kSaveModeSave, nullptr, "extra save" }, // Slot 47
{ "cata2.048", kSaveModeSave, nullptr, "extra save" }, // Slot 48
{ "cata2.049", kSaveModeSave, nullptr, "extra save" }, // Slot 49
{ "cata2.050", kSaveModeSave, nullptr, "extra save" }, // Slot 50
{ "cata2.051", kSaveModeSave, nullptr, "extra save" }, // Slot 51
{ "cata2.052", kSaveModeSave, nullptr, "extra save" }, // Slot 52
{ "cata2.053", kSaveModeSave, nullptr, "extra save" }, // Slot 53
{ "cata2.054", kSaveModeSave, nullptr, "extra save" }, // Slot 54
{ "cata2.055", kSaveModeSave, nullptr, "extra save" }, // Slot 55
{ "cata2.056", kSaveModeSave, nullptr, "extra save" }, // Slot 56
{ "cata2.057", kSaveModeSave, nullptr, "extra save" }, // Slot 57
{ "cata2.058", kSaveModeSave, nullptr, "extra save" }, // Slot 58
{ "cata2.059", kSaveModeSave, nullptr, "extra save" }, // Slot 59
{ "cata3.000", kSaveModeSave, nullptr, "extra save" }, // Slot 00
{ "cata3.001", kSaveModeSave, nullptr, "extra save" }, // Slot 01
{ "cata3.002", kSaveModeSave, nullptr, "extra save" }, // Slot 02
{ "cata3.003", kSaveModeSave, nullptr, "extra save" }, // Slot 03
{ "cata3.004", kSaveModeSave, nullptr, "extra save" }, // Slot 04
{ "cata3.005", kSaveModeSave, nullptr, "extra save" }, // Slot 05
{ "cata3.006", kSaveModeSave, nullptr, "extra save" }, // Slot 06
{ "cata3.007", kSaveModeSave, nullptr, "extra save" }, // Slot 07
{ "cata3.008", kSaveModeSave, nullptr, "extra save" }, // Slot 08
{ "cata3.009", kSaveModeSave, nullptr, "extra save" }, // Slot 09
{ "cata3.010", kSaveModeSave, nullptr, "extra save" }, // Slot 10
{ "cata3.011", kSaveModeSave, nullptr, "extra save" }, // Slot 11
{ "cata3.012", kSaveModeSave, nullptr, "extra save" }, // Slot 12
{ "cata3.013", kSaveModeSave, nullptr, "extra save" }, // Slot 13
{ "cata3.014", kSaveModeSave, nullptr, "extra save" }, // Slot 14
{ "cata3.015", kSaveModeSave, nullptr, "extra save" }, // Slot 15
{ "cata3.016", kSaveModeSave, nullptr, "extra save" }, // Slot 16
{ "cata3.017", kSaveModeSave, nullptr, "extra save" }, // Slot 17
{ "cata3.018", kSaveModeSave, nullptr, "extra save" }, // Slot 18
{ "cata3.019", kSaveModeSave, nullptr, "extra save" }, // Slot 19
{ "cata3.020", kSaveModeSave, nullptr, "extra save" }, // Slot 20
{ "cata3.021", kSaveModeSave, nullptr, "extra save" }, // Slot 21
{ "cata3.022", kSaveModeSave, nullptr, "extra save" }, // Slot 22
{ "cata3.023", kSaveModeSave, nullptr, "extra save" }, // Slot 23
{ "cata3.024", kSaveModeSave, nullptr, "extra save" }, // Slot 24
{ "cata3.025", kSaveModeSave, nullptr, "extra save" }, // Slot 25
{ "cata3.026", kSaveModeSave, nullptr, "extra save" }, // Slot 26
{ "cata3.027", kSaveModeSave, nullptr, "extra save" }, // Slot 27
{ "cata3.028", kSaveModeSave, nullptr, "extra save" }, // Slot 28
{ "cata3.029", kSaveModeSave, nullptr, "extra save" }, // Slot 29
{ "cata3.030", kSaveModeSave, nullptr, "extra save" }, // Slot 30
{ "cata3.031", kSaveModeSave, nullptr, "extra save" }, // Slot 31
{ "cata3.032", kSaveModeSave, nullptr, "extra save" }, // Slot 32
{ "cata3.033", kSaveModeSave, nullptr, "extra save" }, // Slot 33
{ "cata3.034", kSaveModeSave, nullptr, "extra save" }, // Slot 34
{ "cata3.035", kSaveModeSave, nullptr, "extra save" }, // Slot 35
{ "cata3.036", kSaveModeSave, nullptr, "extra save" }, // Slot 36
{ "cata3.037", kSaveModeSave, nullptr, "extra save" }, // Slot 37
{ "cata3.038", kSaveModeSave, nullptr, "extra save" }, // Slot 38
{ "cata3.039", kSaveModeSave, nullptr, "extra save" }, // Slot 39
{ "cata3.040", kSaveModeSave, nullptr, "extra save" }, // Slot 40
{ "cata3.041", kSaveModeSave, nullptr, "extra save" }, // Slot 41
{ "cata3.042", kSaveModeSave, nullptr, "extra save" }, // Slot 42
{ "cata3.043", kSaveModeSave, nullptr, "extra save" }, // Slot 43
{ "cata3.044", kSaveModeSave, nullptr, "extra save" }, // Slot 44
{ "cata3.045", kSaveModeSave, nullptr, "extra save" }, // Slot 45
{ "cata3.046", kSaveModeSave, nullptr, "extra save" }, // Slot 46
{ "cata3.047", kSaveModeSave, nullptr, "extra save" }, // Slot 47
{ "cata3.048", kSaveModeSave, nullptr, "extra save" }, // Slot 48
{ "cata3.049", kSaveModeSave, nullptr, "extra save" }, // Slot 49
{ "cata3.050", kSaveModeSave, nullptr, "extra save" }, // Slot 50
{ "cata3.051", kSaveModeSave, nullptr, "extra save" }, // Slot 51
{ "cata3.052", kSaveModeSave, nullptr, "extra save" }, // Slot 52
{ "cata3.053", kSaveModeSave, nullptr, "extra save" }, // Slot 53
{ "cata3.054", kSaveModeSave, nullptr, "extra save" }, // Slot 54
{ "cata3.055", kSaveModeSave, nullptr, "extra save" }, // Slot 55
{ "cata3.056", kSaveModeSave, nullptr, "extra save" }, // Slot 56
{ "cata3.057", kSaveModeSave, nullptr, "extra save" }, // Slot 57
{ "cata3.058", kSaveModeSave, nullptr, "extra save" }, // Slot 58
{ "cata3.059", kSaveModeSave, nullptr, "extra save" }, // Slot 59
{ "intro.0xx", kSaveModeSave, nullptr, "temp sprite"}, // Autosave sprite
{ "intro.000", kSaveModeSave, nullptr, "temp sprite"}, // Slot 00
{ "intro.001", kSaveModeSave, nullptr, "temp sprite"}, // Slot 01
{ "intro.002", kSaveModeSave, nullptr, "temp sprite"}, // Slot 02
{ "intro.003", kSaveModeSave, nullptr, "temp sprite"}, // Slot 03
{ "intro.004", kSaveModeSave, nullptr, "temp sprite"}, // Slot 04
{ "intro.005", kSaveModeSave, nullptr, "temp sprite"}, // Slot 05
{ "intro.006", kSaveModeSave, nullptr, "temp sprite"}, // Slot 06
{ "intro.007", kSaveModeSave, nullptr, "temp sprite"}, // Slot 07
{ "intro.008", kSaveModeSave, nullptr, "temp sprite"}, // Slot 08
{ "intro.009", kSaveModeSave, nullptr, "temp sprite"}, // Slot 09
{ "intro.010", kSaveModeSave, nullptr, "temp sprite"}, // Slot 10
{ "intro.011", kSaveModeSave, nullptr, "temp sprite"}, // Slot 11
{ "intro.012", kSaveModeSave, nullptr, "temp sprite"}, // Slot 12
{ "intro.013", kSaveModeSave, nullptr, "temp sprite"}, // Slot 13
{ "intro.014", kSaveModeSave, nullptr, "temp sprite"}, // Slot 14
{ "intro.015", kSaveModeSave, nullptr, "temp sprite"}, // Slot 15
{ "intro.016", kSaveModeSave, nullptr, "temp sprite"}, // Slot 16
{ "intro.017", kSaveModeSave, nullptr, "temp sprite"}, // Slot 17
{ "intro.018", kSaveModeSave, nullptr, "temp sprite"}, // Slot 18
{ "intro.019", kSaveModeSave, nullptr, "temp sprite"}, // Slot 19
{ "intro.020", kSaveModeSave, nullptr, "temp sprite"}, // Slot 20
{ "intro.021", kSaveModeSave, nullptr, "temp sprite"}, // Slot 21
{ "intro.022", kSaveModeSave, nullptr, "temp sprite"}, // Slot 22
{ "intro.023", kSaveModeSave, nullptr, "temp sprite"}, // Slot 23
{ "intro.024", kSaveModeSave, nullptr, "temp sprite"}, // Slot 24
{ "intro.025", kSaveModeSave, nullptr, "temp sprite"}, // Slot 25
{ "intro.026", kSaveModeSave, nullptr, "temp sprite"}, // Slot 26
{ "intro.027", kSaveModeSave, nullptr, "temp sprite"}, // Slot 27
{ "intro.028", kSaveModeSave, nullptr, "temp sprite"}, // Slot 28
{ "intro.029", kSaveModeSave, nullptr, "temp sprite"}, // Slot 29
{ "intro.030", kSaveModeSave, nullptr, "temp sprite"}, // Slot 30
{ "intro.031", kSaveModeSave, nullptr, "temp sprite"}, // Slot 31
{ "intro.032", kSaveModeSave, nullptr, "temp sprite"}, // Slot 32
{ "intro.033", kSaveModeSave, nullptr, "temp sprite"}, // Slot 33
{ "intro.034", kSaveModeSave, nullptr, "temp sprite"}, // Slot 34
{ "intro.035", kSaveModeSave, nullptr, "temp sprite"}, // Slot 35
{ "intro.036", kSaveModeSave, nullptr, "temp sprite"}, // Slot 36
{ "intro.037", kSaveModeSave, nullptr, "temp sprite"}, // Slot 37
{ "intro.038", kSaveModeSave, nullptr, "temp sprite"}, // Slot 38
{ "intro.039", kSaveModeSave, nullptr, "temp sprite"}, // Slot 39
{ "intro.040", kSaveModeSave, nullptr, "temp sprite"}, // Slot 40
{ "intro.041", kSaveModeSave, nullptr, "temp sprite"}, // Slot 41
{ "intro.042", kSaveModeSave, nullptr, "temp sprite"}, // Slot 42
{ "intro.043", kSaveModeSave, nullptr, "temp sprite"}, // Slot 43
{ "intro.044", kSaveModeSave, nullptr, "temp sprite"}, // Slot 44
{ "intro.045", kSaveModeSave, nullptr, "temp sprite"}, // Slot 45
{ "intro.046", kSaveModeSave, nullptr, "temp sprite"}, // Slot 46
{ "intro.047", kSaveModeSave, nullptr, "temp sprite"}, // Slot 47
{ "intro.048", kSaveModeSave, nullptr, "temp sprite"}, // Slot 48
{ "intro.049", kSaveModeSave, nullptr, "temp sprite"}, // Slot 49
{ "intro.050", kSaveModeSave, nullptr, "temp sprite"}, // Slot 50
{ "intro.051", kSaveModeSave, nullptr, "temp sprite"}, // Slot 51
{ "intro.052", kSaveModeSave, nullptr, "temp sprite"}, // Slot 52
{ "intro.053", kSaveModeSave, nullptr, "temp sprite"}, // Slot 53
{ "intro.054", kSaveModeSave, nullptr, "temp sprite"}, // Slot 54
{ "intro.055", kSaveModeSave, nullptr, "temp sprite"}, // Slot 55
{ "intro.056", kSaveModeSave, nullptr, "temp sprite"}, // Slot 56
{ "intro.057", kSaveModeSave, nullptr, "temp sprite"}, // Slot 57
{ "intro.058", kSaveModeSave, nullptr, "temp sprite"}, // Slot 58
{ "intro.059", kSaveModeSave, nullptr, "temp sprite"}, // Slot 59
};
SaveLoad_v6::SpriteHandler::SpriteHandler(GobEngine *vm) : TempSpriteHandler(vm) {
}
SaveLoad_v6::SpriteHandler::~SpriteHandler() {
}
bool SaveLoad_v6::SpriteHandler::set(SaveReader *reader, uint32 part) {
if (!TempSpriteHandler::create(624, 272, true))
return false;
return reader->readPart(part, _sprite);
}
bool SaveLoad_v6::SpriteHandler::get(SaveWriter *writer, uint32 part) {
if (getSize() < 0)
if (!TempSpriteHandler::create(624, 272, true))
return false;
return writer->writePart(part, _sprite);
}
SaveLoad_v6::GameHandler::File::File(GobEngine *vm, const char *base) :
SlotFileIndexed(vm, SaveLoad_v6::kSlotCount, base, "s") {
}
SaveLoad_v6::GameHandler::File::~File() {
}
int SaveLoad_v6::GameHandler::File::getSlot(int32 offset) const {
uint32 varSize = SaveHandler::getVarSize(_vm);
if (varSize == 0)
return -1;
return ((offset - (kPropsSize + kIndexSize)) / varSize);
}
int SaveLoad_v6::GameHandler::File::getSlotRemainder(int32 offset) const {
uint32 varSize = SaveHandler::getVarSize(_vm);
if (varSize == 0)
return -1;
return ((offset - (kPropsSize + kIndexSize)) % varSize);
}
SaveLoad_v6::GameHandler::GameHandler(GobEngine *vm, const char *target,
SpriteHandler &spriteHandler) : SaveHandler(vm), _spriteHandler(&spriteHandler),
_reader(nullptr), _writer(nullptr), _hasExtra(false) {
memset(_props, 0, kPropsSize);
memset(_index, 0, kIndexSize);
_slotFile = new File(vm, target);
}
SaveLoad_v6::GameHandler::~GameHandler() {
delete _slotFile;
delete _reader;
delete _writer;
}
int32 SaveLoad_v6::GameHandler::getSize() {
uint32 varSize = SaveHandler::getVarSize(_vm);
if (varSize == 0)
return -1;
return _slotFile->tallyUpFiles(varSize, kPropsSize + kIndexSize);
}
bool SaveLoad_v6::GameHandler::load(int16 dataVar, int32 size, int32 offset) {
uint32 varSize = SaveHandler::getVarSize(_vm);
if (varSize == 0)
return false;
if (size == 0) {
// Indicator to load all variables
dataVar = 0;
size = varSize;
}
if (((uint32) offset) < kPropsSize) {
// Properties
refreshProps();
if (((uint32) (offset + size)) > kPropsSize) {
warning("Wrong index size (%d, %d)", size, offset);
return false;
}
_vm->_inter->_variables->copyFrom(dataVar, _props + offset, size);
} else if (((uint32) offset) < kPropsSize + kIndexSize) {
// Save index
if (((uint32) size) != kIndexSize) {
warning("Wrong index size (%d, %d)", size, offset);
return false;
}
buildIndex(_vm->_inter->_variables->getAddressOff8(dataVar));
} else {
// Save slot, whole variable block
uint32 slot = _slotFile->getSlot(offset);
int slotRem = _slotFile->getSlotRemainder(offset);
debugC(2, kDebugSaveLoad, "Loading from slot %d", slot);
if ((slot >= kSlotCount) || (slotRem != 0) ||
(dataVar != 0) || (((uint32) size) != varSize)) {
warning("Invalid loading procedure (%d, %d, %d, %d, %d)",
dataVar, size, offset, slot, slotRem);
return false;
}
if (!createReader(slot))
return false;
SavePartInfo info(kSlotNameLength, (uint32) _vm->getGameType(), 0,
_vm->getEndianness(), varSize);
SavePartVars vars(_vm, varSize);
if (!_reader->load())
return false;
if (!_reader->readPart(0, &info))
return false;
if (!_reader->readPart(1, &vars))
return false;
// Get all variables
if (!vars.writeInto(0, 0, varSize))
return false;
if (!_spriteHandler->set(_reader, 4))
return false;
}
return true;
}
bool SaveLoad_v6::GameHandler::save(int16 dataVar, int32 size, int32 offset) {
uint32 varSize = SaveHandler::getVarSize(_vm);
if (varSize == 0)
return false;
if (size == 0) {
// Indicator to save all variables
dataVar = 0;
size = varSize;
}
if (((uint32) offset) < kPropsSize) {
// Properties
if (((uint32) (offset + size)) > kPropsSize) {
warning("Wrong index size (%d, %d)", size, offset);
return false;
}
_vm->_inter->_variables->copyTo(dataVar, _props + offset, size);
refreshProps();
// If that screen doesn't save any extra temp saves, write a dummy
if (_writer && (size == 40) && (offset == 0)) {
if (!_hasExtra) {
SavePartMem mem(1);
SavePartVars vars(_vm, varSize);
uint8 extraSaveNumber = 0;
if (!mem.readFrom(&extraSaveNumber, 0, 1))
return false;
if (!vars.readFrom(0, 0, varSize))
return false;
if (!_writer->writePart(2, &mem))
return false;
if (!_writer->writePart(3, &vars))
return false;
}
}
} else if (((uint32) offset) < kPropsSize + kIndexSize) {
// Save index
if (((uint32) size) != kIndexSize) {
warning("Wrong index size (%d, %d)", size, offset);
return false;
}
// Just copy the index into our buffer
_vm->_inter->_variables->copyTo(dataVar, _index, kIndexSize);
} else {
// Save slot, whole variable block
_hasExtra = false;
uint32 slot = _slotFile->getSlot(offset);
int slotRem = _slotFile->getSlotRemainder(offset);
debugC(2, kDebugSaveLoad, "Saving to slot %d", slot);
if ((slot >= kSlotCount) || (slotRem != 0) ||
(dataVar != 0) || (((uint32) size) != varSize)) {
warning("Invalid saving procedure (%d, %d, %d, %d, %d)",
dataVar, size, offset, slot, slotRem);
return false;
}
if (!createWriter(slot))
return false;
SavePartInfo info(kSlotNameLength, (uint32) _vm->getGameType(), 0,
_vm->getEndianness(), varSize);
SavePartVars vars(_vm, varSize);
// Write the description
info.setDesc(_index + (slot * kSlotNameLength), kSlotNameLength);
// Write all variables
if (!vars.readFrom(0, 0, varSize))
return false;
if (!_writer->writePart(0, &info))
return false;
if (!_writer->writePart(1, &vars))
return false;
if (!_spriteHandler->get(_writer, 4))
return false;
}
return true;
}
uint8 SaveLoad_v6::GameHandler::getExtraID(int slot) {
if (!_reader || (_reader->getSlot() != (uint32)slot))
return 0;
SavePartMem mem(1);
if (!_reader->readPart(2, &mem))
return 0;
uint8 extraSaveNumber;
if (!mem.writeInto(&extraSaveNumber, 0, 1))
return 0;
return extraSaveNumber;
}
bool SaveLoad_v6::GameHandler::loadExtra(int slot, uint8 id,
int16 dataVar, int32 size, int32 offset) {
if (!_reader || (_reader->getSlot() != (uint32)slot))
return false;
SavePartMem mem(1);
if (!_reader->readPart(2, &mem))
return false;
uint8 extraSaveNumber;
if (!mem.writeInto(&extraSaveNumber, 0, 1))
return false;
if (extraSaveNumber != id)
return false;
uint32 varSize = SaveHandler::getVarSize(_vm);
SavePartVars vars(_vm, varSize);
if (!_reader->readPart(3, &vars))
return false;
if (!vars.writeInto(0, 0, varSize))
return false;
return true;
}
bool SaveLoad_v6::GameHandler::saveExtra(int slot, uint8 id,
int16 dataVar, int32 size, int32 offset) {
if (!_writer || (_writer->getSlot() != (uint32)slot))
return false;
uint32 varSize = SaveHandler::getVarSize(_vm);
SavePartMem mem(1);
SavePartVars vars(_vm, varSize);
if (!mem.readFrom(&id, 0, 1))
return false;
if (!vars.readFrom(0, 0, varSize))
return false;
if (!_writer->writePart(2, &mem))
return false;
if (!_writer->writePart(3, &vars))
return false;
_hasExtra = true;
return true;
}
void SaveLoad_v6::GameHandler::buildIndex(byte *buffer) const {
uint32 varSize = SaveHandler::getVarSize(_vm);
if (varSize == 0)
return;
SavePartInfo info(kSlotNameLength, (uint32) _vm->getGameType(),
0, _vm->getEndianness(), varSize);
_slotFile->buildIndex(buffer, info, nullptr, true);
}
void SaveLoad_v6::GameHandler::refreshProps() {
uint32 maxSlot = _slotFile->getSlotMax();
memset(_props + 40, 0xFF, 40); // Joker
_props[159] = 0x03; // # of joker unused
WRITE_LE_UINT32(_props + 160, maxSlot); // # of saves
}
bool SaveLoad_v6::GameHandler::createReader(int slot) {
// If slot < 0, just check if a reader exists
if (slot < 0)
return (_reader != nullptr);
if (!_reader || (_reader->getSlot() != ((uint32) slot))) {
Common::String slotFile = _slotFile->build(slot);
if (slotFile.empty())
return false;
delete _reader;
_reader = new SaveReader(5, slot, slotFile);
if (!_reader->load()) {
delete _reader;
_reader = nullptr;
return false;
}
}
return true;
}
bool SaveLoad_v6::GameHandler::createWriter(int slot) {
// If slot < 0, just check if a writer exists
if (slot < 0)
return (_writer != nullptr);
if (!_writer || (_writer->getSlot() != ((uint32) slot))) {
Common::String slotFile = _slotFile->build(slot);
if (slotFile.empty())
return false;
delete _writer;
_writer = new SaveWriter(5, slot, slotFile);
}
return true;
}
SaveLoad_v6::AutoHandler::File::File(GobEngine *vm, const Common::String &base) :
SlotFileStatic(vm, base, "aut") {
}
SaveLoad_v6::AutoHandler::File::~File() {
}
SaveLoad_v6::AutoHandler::AutoHandler(GobEngine *vm, const Common::String &target) :
SaveHandler(vm), _file(vm, target) {
}
SaveLoad_v6::AutoHandler::~AutoHandler() {
}
int32 SaveLoad_v6::AutoHandler::getSize() {
Common::String fileName = _file.build();
if (fileName.empty())
return -1;
SaveReader reader(1, 0, fileName);
SaveHeader header;
if (!reader.load())
return -1;
if (!reader.readPartHeader(0, &header))
return -1;
// Return the part's size
return header.getSize() + 2900;
}
bool SaveLoad_v6::AutoHandler::load(int16 dataVar, int32 size, int32 offset) {
uint32 varSize = SaveHandler::getVarSize(_vm);
if (varSize == 0)
return false;
if ((size != 0) || (offset != 2900)) {
warning("Invalid autoloading procedure (%d, %d, %d)", dataVar, size, offset);
return false;
}
Common::String fileName = _file.build();
if (fileName.empty())
return false;
SaveReader reader(1, 0, fileName);
SaveHeader header;
SavePartVars vars(_vm, varSize);
if (!reader.load())
return false;
if (!reader.readPartHeader(0, &header))
return false;
if (header.getSize() != varSize) {
warning("Autosave mismatch (%d, %d)", header.getSize(), varSize);
return false;
}
if (!reader.readPart(0, &vars))
return false;
if (!vars.writeInto(0, 0, varSize))
return false;
return true;
}
bool SaveLoad_v6::AutoHandler::save(int16 dataVar, int32 size, int32 offset) {
uint32 varSize = SaveHandler::getVarSize(_vm);
if (varSize == 0)
return false;
if ((size != 0) || (offset != 2900)) {
warning("Invalid autosaving procedure (%d, %d, %d)", dataVar, size, offset);
return false;
}
Common::String fileName = _file.build();
if (fileName.empty())
return false;
SaveWriter writer(1, 0, fileName);
SavePartVars vars(_vm, varSize);
if (!vars.readFrom(0, 0, varSize))
return false;
return writer.writePart(0, &vars);
}
SaveLoad_v6::AutoSpriteHandler::File::File(GobEngine *vm, const Common::String &base) :
SlotFileStatic(vm, base, "asp") {
}
SaveLoad_v6::AutoSpriteHandler::File::~File() {
}
SaveLoad_v6::AutoSpriteHandler::AutoSpriteHandler(GobEngine *vm,
const Common::String &target) : TempSpriteHandler(vm), _file(vm, target) {
}
SaveLoad_v6::AutoSpriteHandler::~AutoSpriteHandler() {
}
int32 SaveLoad_v6::AutoSpriteHandler::getSize() {
Common::InSaveFile *file = _file.openRead();
if (!file)
return -1;
delete file;
return 1;
}
bool SaveLoad_v6::AutoSpriteHandler::load(int16 dataVar, int32 size, int32 offset) {
if (offset != 0) {
warning("Invalid autosprite saving procedure (%d, %d, %d)", dataVar, size, offset);
return false;
}
if (!TempSpriteHandler::create(624, 272, true))
return false;
Common::String fileName = _file.build();
if (fileName.empty())
return false;
SaveReader reader(1, 0, fileName);
if (!reader.load())
return false;
if (!reader.readPart(0, _sprite))
return false;
return TempSpriteHandler::load(dataVar, size, offset);
}
bool SaveLoad_v6::AutoSpriteHandler::save(int16 dataVar, int32 size, int32 offset) {
if (!TempSpriteHandler::save(dataVar, size, offset))
return false;
if (offset != 0) {
warning("Invalid autosprite saving procedure (%d, %d, %d)", dataVar, size, offset);
return false;
}
Common::String fileName = _file.build();
if (fileName.empty())
return false;
SaveWriter writer(1, 0, fileName);
return writer.writePart(0, _sprite);
}
SaveLoad_v6::TempHandler::TempHandler(GobEngine *vm) : SaveHandler(vm),
_empty(true), _size(0), _data(nullptr) {
}
SaveLoad_v6::TempHandler::~TempHandler() {
delete[] _data;
}
int32 SaveLoad_v6::TempHandler::getSize() {
if (_empty)
return -1;
return _size + 2900;
}
bool SaveLoad_v6::TempHandler::load(int16 dataVar, int32 size, int32 offset) {
if (_empty || (_size == 0) || !_data)
return false;
if ((size != 0) || (offset != 2900)) {
warning("Invalid temp loading procedure (%d, %d, %d)", dataVar, size, offset);
return false;
}
_vm->_inter->_variables->copyFrom(0, _data, _size);
return true;
}
bool SaveLoad_v6::TempHandler::save(int16 dataVar, int32 size, int32 offset) {
if ((size != 0) || (offset != 2900)) {
warning("Invalid temp saving procedure (%d, %d, %d)", dataVar, size, offset);
return false;
}
delete[] _data;
_size = SaveHandler::getVarSize(_vm);
_data = new byte[_size];
_vm->_inter->_variables->copyTo(0, _data, _size);
_empty = false;
return true;
}
bool SaveLoad_v6::TempHandler::deleteFile() {
delete[] _data;
_empty = true;
_size = 0;
_data = nullptr;
return true;
}
SaveLoad_v6::ExtraHandler::ExtraHandler(GobEngine *vm, GameHandler &game,
uint8 id, int slot) : SaveHandler(vm), _game(&game), _id(id), _slot(slot) {
}
SaveLoad_v6::ExtraHandler::~ExtraHandler() {
}
int32 SaveLoad_v6::ExtraHandler::getSize() {
if (_game->getExtraID(_slot) != _id)
return -1;
return SaveHandler::getVarSize(_vm) + 2900;
}
bool SaveLoad_v6::ExtraHandler::load(int16 dataVar, int32 size, int32 offset) {
return _game->loadExtra(_slot, _id, dataVar, size, offset);
}
bool SaveLoad_v6::ExtraHandler::save(int16 dataVar, int32 size, int32 offset) {
return _game->saveExtra(_slot, _id, dataVar, size, offset);
}
SaveLoad_v6::SaveLoad_v6(GobEngine *vm, const char *targetName) :
SaveLoad(vm) {
_spriteHandler = new SpriteHandler(vm);
_gameHandler = new GameHandler(vm, targetName, *_spriteHandler);
_autoHandler = new AutoHandler(vm, targetName);
_autoSpriteHandler = new AutoSpriteHandler(vm, targetName);
_tmpHandler[0] = new TempHandler(vm);
_tmpHandler[1] = new TempHandler(vm);
_saveFiles[0].handler = _gameHandler;
_saveFiles[1].handler = _autoHandler;
_saveFiles[7].handler = _tmpHandler[0];
_saveFiles[8].handler = _tmpHandler[1];
for (int i = 0; i < 60; i++)
_saveFiles[ 9 + i].handler =
_extraHandler[ i] = new ExtraHandler(_vm, *_gameHandler, 2, i);
for (int i = 0; i < 60; i++)
_saveFiles[69 + i].handler =
_extraHandler[60 + i] = new ExtraHandler(_vm, *_gameHandler, 3, i);
_saveFiles[129].handler = _autoSpriteHandler;
for (int i = 0; i < 60; i++)
_saveFiles[130 + i].handler = _spriteHandler;
}
SaveLoad_v6::~SaveLoad_v6() {
for (int i = 0; i < 120; i++)
delete _extraHandler[i];
delete _tmpHandler[0];
delete _tmpHandler[1];
delete _autoSpriteHandler;
delete _autoHandler;
delete _gameHandler;
delete _spriteHandler;
}
const SaveLoad_v6::SaveFile *SaveLoad_v6::getSaveFile(const char *fileName) const {
fileName = stripPath(fileName);
for (int i = 0; i < ARRAYSIZE(_saveFiles); i++)
if (!scumm_stricmp(fileName, _saveFiles[i].sourceName))
return &_saveFiles[i];
return nullptr;
}
SaveLoad_v6::SaveFile *SaveLoad_v6::getSaveFile(const char *fileName) {
fileName = stripPath(fileName);
for (int i = 0; i < ARRAYSIZE(_saveFiles); i++)
if (!scumm_stricmp(fileName, _saveFiles[i].sourceName))
return &_saveFiles[i];
return nullptr;
}
SaveHandler *SaveLoad_v6::getHandler(const char *fileName) const {
const SaveFile *saveFile = getSaveFile(fileName);
if (saveFile)
return saveFile->handler;
return nullptr;
}
const char *SaveLoad_v6::getDescription(const char *fileName) const {
const SaveFile *saveFile = getSaveFile(fileName);
if (saveFile)
return saveFile->description;
return nullptr;
}
SaveLoad::SaveMode SaveLoad_v6::getSaveMode(const char *fileName) const {
const SaveFile *saveFile = getSaveFile(fileName);
if (saveFile)
return saveFile->mode;
return kSaveModeNone;
}
} // End of namespace Gob

File diff suppressed because it is too large Load Diff