scummvm/engines/lastexpress/game/savegame.h

327 lines
8.7 KiB
C++

/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#ifndef LASTEXPRESS_SAVELOAD_H
#define LASTEXPRESS_SAVELOAD_H
/*
Savegame format
---------------
header: 32 bytes
uint32 {4} - signature: 0x12001200
uint32 {4} - chapter - needs to be [0; 5]
uint32 {4} - time - needs to be >= 32 [1061100; timeMax]
uint32 {4} - ?? needs to be >= 32
uint32 {4} - ?? needs to be = 1
uint32 {4} - Brightness (needs to be [0-6])
uint32 {4} - Volume (needs to be [0-7])
uint32 {4} - ?? needs to be = 9
Game data Format
-----------------
uint32 {4} - entity
uint32 {4} - current time
uint32 {4} - time delta (how much a tick is in "real" time)
uint32 {4} - time ticks
uint32 {4} - scene Index max: 2500
byte {1} - use backup scene
uint32 {4} - backup Scene Index 1 max: 2500
uint32 {4} - backup Scene Index 2 max: 2500
uint32 {4} - selected inventory item max: 32
uint32 {4*100*10} - positions by car(BlockedView)
uint32 {4*16} - compartments (BlockedX)
uint32 {4*16} - compartments? (SoftBlockedX)
uint32 {4*128} - game progress
byte {512} - game events
byte {7*32} - inventory
byte {5*128} - objects
byte {1262*40} - entities (characters and train entities)
uint32 {4} - sound queue state
uint32 {4} - ??
uint32 {4} - number of sound entries
byte {count*68} - sound entries
byte {16*128} - save point data
uint32 {4} - number of save points (max: 128)
byte {count*16} - save points
... more unknown stuff
*/
#include "lastexpress/shared.h"
#include "common/savefile.h"
#include "common/serializer.h"
#include "common/memstream.h"
namespace LastExpress {
// Savegame signatures
#define SAVEGAME_SIGNATURE 0x12001200 // 301994496
#define SAVEGAME_ENTRY_SIGNATURE 0xE660E660 // 3865110112
#define WRAP_SYNC_FUNCTION(instance, className, method) \
new Common::Functor1Mem<Common::Serializer &, void, className>(instance, &className::method)
class LastExpressEngine;
class SavegameStream : public Common::MemoryWriteStreamDynamic, public Common::SeekableReadStream {
public:
SavegameStream() : MemoryWriteStreamDynamic(DisposeAfterUse::YES), _eos(false) {
_enableCompression = false;
_bufferOffset = -1;
_valueCount = 0;
_previousValue = 0;
_repeatCount = 0;
_offset = 0;
_status = kStatusReady;
memset(_buffer, 0, 256);
}
int32 pos() const { return MemoryWriteStreamDynamic::pos(); }
int32 size() const { return MemoryWriteStreamDynamic::size(); }
bool seek(int32 offset, int whence = SEEK_SET) { return MemoryWriteStreamDynamic::seek(offset, whence); }
bool eos() const { return _eos; }
uint32 read(void *dataPtr, uint32 dataSize);
uint32 write(const void *dataPtr, uint32 dataSize);
uint32 process();
private:
enum CompressedStreamStatus {
kStatusReady,
kStatusReading,
kStatusWriting
};
uint32 readUncompressed(void *dataPtr, uint32 dataSize);
// Compressed data
uint32 writeCompressed(const void *dataPtr, uint32 dataSize);
uint32 readCompressed(void *dataPtr, uint32 dataSize);
void writeBuffer(uint8 value, bool onlyValue = true);
uint8 readBuffer();
private:
bool _eos;
// Compression handling
bool _enableCompression;
int16 _bufferOffset;
byte _valueCount;
byte _previousValue;
int16 _repeatCount;
uint32 _offset;
CompressedStreamStatus _status;
byte _buffer[256];
};
class SaveLoad {
public:
SaveLoad(LastExpressEngine *engine);
~SaveLoad();
// Init
void create(GameId id);
void clear(bool clearStream = false);
uint32 init(GameId id, bool resetHeaders);
// Save & Load
void loadLastGame();
void loadGame(uint32 index);
void saveGame(SavegameType type, EntityIndex entity, uint32 value);
void loadVolumeBrightness();
void saveVolumeBrightness();
// Getting information
static bool isSavegamePresent(GameId id);
static bool isSavegameValid(GameId id);
bool isGameFinished(uint32 menuIndex, uint32 savegameIndex);
// Accessors
uint32 getTime(uint32 index) { return getEntry(index)->time; }
ChapterIndex getChapter(uint32 index) { return getEntry(index)->chapter; }
uint32 getValue(uint32 index) { return getEntry(index)->value; }
uint32 getLastSavegameTicks() const { return _gameTicksLastSavegame; }
private:
LastExpressEngine *_engine;
struct SavegameMainHeader : Common::Serializable {
uint32 signature;
uint32 count;
uint32 offset;
uint32 offsetEntry;
uint32 keepIndex;
int32 brightness;
int32 volume;
uint32 field_1C;
SavegameMainHeader() {
signature = SAVEGAME_SIGNATURE;
count = 0;
offset = 32;
offsetEntry = 32;
keepIndex = 0;
brightness = 3;
volume = 7;
field_1C = 9;
}
void saveLoadWithSerializer(Common::Serializer &s) {
s.syncAsUint32LE(signature);
s.syncAsUint32LE(count);
s.syncAsUint32LE(offset);
s.syncAsUint32LE(offsetEntry);
s.syncAsUint32LE(keepIndex);
s.syncAsUint32LE(brightness);
s.syncAsUint32LE(volume);
s.syncAsUint32LE(field_1C);
}
bool isValid() {
if (signature != SAVEGAME_SIGNATURE)
return false;
/* Check not needed as it can never be < 0
if (header.chapter < 0)
return false;*/
if (offset < 32)
return false;
if (offsetEntry < 32)
return false;
if (keepIndex != 1 && keepIndex != 0)
return false;
if (brightness < 0 || brightness > 6)
return false;
if (volume < 0 || volume > 7)
return false;
if (field_1C != 9)
return false;
return true;
}
};
struct SavegameEntryHeader : Common::Serializable {
uint32 signature;
SavegameType type;
uint32 time;
int offset;
ChapterIndex chapter;
uint32 value;
int field_18;
int field_1C;
SavegameEntryHeader() {
signature = SAVEGAME_ENTRY_SIGNATURE;
type = kSavegameTypeIndex;
time = kTimeNone;
offset = 0;
chapter = kChapterAll;
value = 0;
field_18 = 0;
field_1C = 0;
}
void saveLoadWithSerializer(Common::Serializer &s) {
s.syncAsUint32LE(signature);
s.syncAsUint32LE(type);
s.syncAsUint32LE(time);
s.syncAsUint32LE(offset);
s.syncAsUint32LE(chapter);
s.syncAsUint32LE(value);
s.syncAsUint32LE(field_18);
s.syncAsUint32LE(field_1C);
}
bool isValid() {
if (signature != SAVEGAME_ENTRY_SIGNATURE)
return false;
if (type < kSavegameTypeTime || type > kSavegameTypeTickInterval)
return false;
if (time < kTimeStartGame || time > kTimeCityConstantinople)
return false;
if (offset <= 0 || offset & 15)
return false;
/* No check for < 0, as it cannot happen normaly */
if (chapter == 0)
return false;
return true;
}
};
SavegameStream *_savegame;
Common::Array<SavegameEntryHeader *> _gameHeaders;
uint32 _gameTicksLastSavegame;
// Headers
static bool loadMainHeader(Common::InSaveFile *stream, SavegameMainHeader *header);
// Entries
void writeEntry(SavegameType type, EntityIndex entity, uint32 val);
void readEntry(SavegameType *type, EntityIndex *entity, uint32 *val, bool keepIndex);
uint32 writeValue(Common::Serializer &ser, const char *name, Common::Functor1<Common::Serializer &, void> *function, uint size);
uint32 readValue(Common::Serializer &ser, const char *name, Common::Functor1<Common::Serializer &, void> *function, uint size = 0);
SavegameEntryHeader *getEntry(uint32 index);
// Opening save files
static Common::String getFilename(GameId id);
static Common::InSaveFile *openForLoading(GameId id);
static Common::OutSaveFile *openForSaving(GameId id);
// Savegame stream
void initStream();
void loadStream(GameId id);
void flushStream(GameId id);
// Misc
EntityIndex _entity;
void syncEntity(Common::Serializer &ser);
};
} // End of namespace LastExpress
#endif // LASTEXPRESS_SAVELOAD_H