|
@@ -52,6 +52,7 @@
|
|
|
|
|
|
#include "table/strings.h"
|
|
|
|
|
|
#include "saveload_buffer.h"
|
|
|
#include "saveload_internal.h"
|
|
|
#include "saveload_filter.h"
|
|
|
|
|
@@ -59,6 +60,8 @@
|
|
|
|
|
|
extern const SaveLoadVersion SAVEGAME_VERSION = (SaveLoadVersion)(SL_MAX_VERSION - 1); ///< Current savegame version of OpenTTD.
|
|
|
|
|
|
const SaveLoadVersion SAVEGAME_VERSION_EXT = (SaveLoadVersion)(0x8000); ///< Savegame extension indicator mask
|
|
|
|
|
|
SavegameType _savegame_type; ///< type of savegame we are loading
|
|
|
FileToSaveLoad _file_to_saveload; ///< File to save or load in the openttd loop.
|
|
|
|
|
@@ -68,6 +71,11 @@ byte _sl_minor_version; ///< the m
|
|
|
std::string _savegame_format; ///< how to compress savegames
|
|
|
bool _do_autosave; ///< are we doing an autosave at the moment?
|
|
|
|
|
|
extern bool _sl_is_ext_version;
|
|
|
extern bool _sl_might_be_legacy_patchpack_save;
|
|
|
extern SaveLoadVersion _sl_is_faked_ext;
|
|
|
extern std::string _sl_xv_version_label;
|
|
|
|
|
|
/** What are we currently doing? */
|
|
|
enum SaveLoadAction {
|
|
|
SLA_LOAD, ///< loading
|
|
@@ -83,113 +91,6 @@ enum NeedLength {
|
|
|
NL_CALCLENGTH = 2, ///< need to calculate the length
|
|
|
};
|
|
|
|
|
|
/** Save in chunks of 128 KiB. */
|
|
|
static const size_t MEMORY_CHUNK_SIZE = 128 * 1024;
|
|
|
|
|
|
/** A buffer for reading (and buffering) savegame data. */
|
|
|
struct ReadBuffer {
|
|
|
byte buf[MEMORY_CHUNK_SIZE]; ///< Buffer we're going to read from.
|
|
|
byte *bufp; ///< Location we're at reading the buffer.
|
|
|
byte *bufe; ///< End of the buffer we can read from.
|
|
|
LoadFilter *reader; ///< The filter used to actually read.
|
|
|
size_t read; ///< The amount of read bytes so far from the filter.
|
|
|
|
|
|
/**
|
|
|
* Initialise our variables.
|
|
|
* @param reader The filter to actually read data.
|
|
|
*/
|
|
|
ReadBuffer(LoadFilter *reader) : bufp(nullptr), bufe(nullptr), reader(reader), read(0)
|
|
|
{
|
|
|
}
|
|
|
|
|
|
inline byte ReadByte()
|
|
|
{
|
|
|
if (this->bufp == this->bufe) {
|
|
|
size_t len = this->reader->Read(this->buf, lengthof(this->buf));
|
|
|
if (len == 0) SlErrorCorrupt("Unexpected end of chunk");
|
|
|
|
|
|
this->read += len;
|
|
|
this->bufp = this->buf;
|
|
|
this->bufe = this->buf + len;
|
|
|
}
|
|
|
|
|
|
return *this->bufp++;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* Get the size of the memory dump made so far.
|
|
|
* @return The size.
|
|
|
*/
|
|
|
size_t GetSize() const
|
|
|
{
|
|
|
return this->read - (this->bufe - this->bufp);
|
|
|
}
|
|
|
};
|
|
|
|
|
|
|
|
|
/** Container for dumping the savegame (quickly) to memory. */
|
|
|
struct MemoryDumper {
|
|
|
std::vector<byte *> blocks; ///< Buffer with blocks of allocated memory.
|
|
|
byte *buf; ///< Buffer we're going to write to.
|
|
|
byte *bufe; ///< End of the buffer we write to.
|
|
|
|
|
|
/** Initialise our variables. */
|
|
|
MemoryDumper() : buf(nullptr), bufe(nullptr)
|
|
|
{
|
|
|
}
|
|
|
|
|
|
~MemoryDumper()
|
|
|
{
|
|
|
for (auto p : this->blocks) {
|
|
|
free(p);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* Write a single byte into the dumper.
|
|
|
* @param b The byte to write.
|
|
|
*/
|
|
|
inline void WriteByte(byte b)
|
|
|
{
|
|
|
/* Are we at the end of this chunk? */
|
|
|
if (this->buf == this->bufe) {
|
|
|
this->buf = CallocT<byte>(MEMORY_CHUNK_SIZE);
|
|
|
this->blocks.push_back(this->buf);
|
|
|
this->bufe = this->buf + MEMORY_CHUNK_SIZE;
|
|
|
}
|
|
|
|
|
|
*this->buf++ = b;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* Flush this dumper into a writer.
|
|
|
* @param writer The filter we want to use.
|
|
|
*/
|
|
|
void Flush(SaveFilter *writer)
|
|
|
{
|
|
|
uint i = 0;
|
|
|
size_t t = this->GetSize();
|
|
|
|
|
|
while (t > 0) {
|
|
|
size_t to_write = std::min(MEMORY_CHUNK_SIZE, t);
|
|
|
|
|
|
writer->Write(this->blocks[i++], to_write);
|
|
|
t -= to_write;
|
|
|
}
|
|
|
|
|
|
writer->Finish();
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* Get the size of the memory dump made so far.
|
|
|
* @return The size.
|
|
|
*/
|
|
|
size_t GetSize() const
|
|
|
{
|
|
|
return this->blocks.size() * MEMORY_CHUNK_SIZE - (this->bufe - this->buf);
|
|
|
}
|
|
|
};
|
|
|
|
|
|
/** The saveload struct, containing reader-writer functions, buffer, version, etc. */
|
|
|
struct SaveLoadParams {
|
|
|
SaveLoadAction action; ///< are we doing a save or a load atm.
|
|
@@ -216,9 +117,127 @@ struct SaveLoadParams {
|
|
|
|
|
|
static SaveLoadParams _sl; ///< Parameters used for/at saveload.
|
|
|
|
|
|
void ReadBuffer::SkipBytesSlowPath(size_t bytes)
|
|
|
{
|
|
|
bytes -= (this->bufe - this->bufp);
|
|
|
while (true) {
|
|
|
size_t len = this->reader->Read(this->buf, lengthof(this->buf));
|
|
|
if (len == 0) SlErrorCorrupt("Unexpected end of chunk");
|
|
|
this->read += len;
|
|
|
if (len >= bytes) {
|
|
|
this->bufp = this->buf + bytes;
|
|
|
this->bufe = this->buf + len;
|
|
|
return;
|
|
|
}
|
|
|
else {
|
|
|
bytes -= len;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
void ReadBuffer::AcquireBytes()
|
|
|
{
|
|
|
size_t remainder = this->bufe - this->bufp;
|
|
|
if (remainder) {
|
|
|
memmove(this->buf, this->bufp, remainder);
|
|
|
}
|
|
|
size_t len = this->reader->Read(this->buf + remainder, lengthof(this->buf) - remainder);
|
|
|
if (len == 0) SlErrorCorrupt("Unexpected end of chunk");
|
|
|
|
|
|
this->read += len;
|
|
|
this->bufp = this->buf;
|
|
|
this->bufe = this->buf + remainder + len;
|
|
|
}
|
|
|
|
|
|
void MemoryDumper::FinaliseBlock()
|
|
|
{
|
|
|
assert(this->saved_buf == nullptr);
|
|
|
if (!this->blocks.empty()) {
|
|
|
size_t s = MEMORY_CHUNK_SIZE - (this->bufe - this->buf);
|
|
|
this->blocks.back().size = s;
|
|
|
this->completed_block_bytes += s;
|
|
|
}
|
|
|
this->buf = this->bufe = nullptr;
|
|
|
}
|
|
|
|
|
|
void MemoryDumper::AllocateBuffer()
|
|
|
{
|
|
|
if (this->saved_buf) {
|
|
|
const size_t offset = this->buf - this->autolen_buf;
|
|
|
const size_t size = (this->autolen_buf_end - this->autolen_buf) * 2;
|
|
|
this->autolen_buf = ReallocT<byte>(this->autolen_buf, size);
|
|
|
this->autolen_buf_end = this->autolen_buf + size;
|
|
|
this->buf = this->autolen_buf + offset;
|
|
|
this->bufe = this->autolen_buf_end;
|
|
|
return;
|
|
|
}
|
|
|
this->FinaliseBlock();
|
|
|
this->buf = MallocT<byte>(MEMORY_CHUNK_SIZE);
|
|
|
this->blocks.emplace_back(this->buf);
|
|
|
this->bufe = this->buf + MEMORY_CHUNK_SIZE;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* Flush this dumper into a writer.
|
|
|
* @param writer The filter we want to use.
|
|
|
*/
|
|
|
void MemoryDumper::Flush(SaveFilter* writer)
|
|
|
{
|
|
|
this->FinaliseBlock();
|
|
|
|
|
|
size_t block_count = this->blocks.size();
|
|
|
for (size_t i = 0; i < block_count; i++) {
|
|
|
writer->Write(this->blocks[i].data, this->blocks[i].size);
|
|
|
}
|
|
|
|
|
|
writer->Finish();
|
|
|
}
|
|
|
|
|
|
void MemoryDumper::StartAutoLength()
|
|
|
{
|
|
|
assert(this->saved_buf == nullptr);
|
|
|
|
|
|
this->saved_buf = this->buf;
|
|
|
this->saved_bufe = this->bufe;
|
|
|
this->buf = this->autolen_buf;
|
|
|
this->bufe = this->autolen_buf_end;
|
|
|
}
|
|
|
|
|
|
std::pair<byte*, size_t> MemoryDumper::StopAutoLength()
|
|
|
{
|
|
|
assert(this->saved_buf != nullptr);
|
|
|
auto res = std::make_pair(this->autolen_buf, this->buf - this->autolen_buf);
|
|
|
|
|
|
this->buf = this->saved_buf;
|
|
|
this->bufe = this->saved_bufe;
|
|
|
this->saved_buf = this->saved_bufe = nullptr;
|
|
|
return res;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* Get the size of the memory dump made so far.
|
|
|
* @return The size.
|
|
|
*/
|
|
|
size_t MemoryDumper::GetSize() const
|
|
|
{
|
|
|
assert(this->saved_buf == nullptr);
|
|
|
return this->completed_block_bytes + (this->bufe ? (MEMORY_CHUNK_SIZE - (this->bufe - this->buf)) : 0);
|
|
|
}
|
|
|
|
|
|
ReadBuffer* ReadBuffer::GetCurrent()
|
|
|
{
|
|
|
return _sl.reader;
|
|
|
}
|
|
|
|
|
|
MemoryDumper* MemoryDumper::GetCurrent()
|
|
|
{
|
|
|
return _sl.dumper;
|
|
|
}
|
|
|
|
|
|
static const std::vector<ChunkHandlerRef> &ChunkHandlers()
|
|
|
{
|
|
|
/* These define the chunks */
|
|
|
extern const ChunkHandlerTable _version_ext_chunk_handlers;
|
|
|
extern const ChunkHandlerTable _gamelog_chunk_handlers;
|
|
|
extern const ChunkHandlerTable _map_chunk_handlers;
|
|
|
extern const ChunkHandlerTable _misc_chunk_handlers;
|
|
@@ -255,6 +274,7 @@ static const std::vector<ChunkHandlerRef
|
|
|
|
|
|
/** List of all chunks in a savegame. */
|
|
|
static const ChunkHandlerTable _chunk_handler_tables[] = {
|
|
|
_version_ext_chunk_handlers,
|
|
|
_gamelog_chunk_handlers,
|
|
|
_map_chunk_handlers,
|
|
|
_misc_chunk_handlers,
|
|
@@ -312,6 +332,7 @@ static void SlNullPointers()
|
|
|
* during NULLing; especially those that try to get
|
|
|
* pointers from other pools. */
|
|
|
_sl_version = SAVEGAME_VERSION;
|
|
|
SlXvSetCurrentState();
|
|
|
|
|
|
for (const ChunkHandler &ch : ChunkHandlers()) {
|
|
|
Debug(sl, 3, "Nulling pointers for {:c}{:c}{:c}{:c}", ch.id >> 24, ch.id >> 16, ch.id >> 8, ch.id);
|
|
@@ -435,41 +456,60 @@ void SlWriteByte(byte b)
|
|
|
_sl.dumper->WriteByte(b);
|
|
|
}
|
|
|
|
|
|
static inline int SlReadUint16()
|
|
|
int SlReadUint16()
|
|
|
{
|
|
|
int x = SlReadByte() << 8;
|
|
|
return x | SlReadByte();
|
|
|
_sl.reader->CheckBytes(2);
|
|
|
return _sl.reader->RawReadUint16();
|
|
|
}
|
|
|
|
|
|
static inline uint32 SlReadUint32()
|
|
|
uint32 SlReadUint32()
|
|
|
{
|
|
|
uint32 x = SlReadUint16() << 16;
|
|
|
return x | SlReadUint16();
|
|
|
_sl.reader->CheckBytes(4);
|
|
|
return _sl.reader->RawReadUint32();
|
|
|
}
|
|
|
|
|
|
static inline uint64 SlReadUint64()
|
|
|
uint64 SlReadUint64()
|
|
|
{
|
|
|
uint32 x = SlReadUint32();
|
|
|
uint32 y = SlReadUint32();
|
|
|
return (uint64)x << 32 | y;
|
|
|
_sl.reader->CheckBytes(8);
|
|
|
return _sl.reader->RawReadUint64();
|
|
|
}
|
|
|
|
|
|
void SlWriteUint16(uint16 v)
|
|
|
{
|
|
|
_sl.dumper->CheckBytes(2);
|
|
|
_sl.dumper->RawWriteUint16(v);
|
|
|
}
|
|
|
|
|
|
static inline void SlWriteUint16(uint16 v)
|
|
|
void SlWriteUint32(uint32 v)
|
|
|
{
|
|
|
SlWriteByte(GB(v, 8, 8));
|
|
|
SlWriteByte(GB(v, 0, 8));
|
|
|
_sl.dumper->CheckBytes(4);
|
|
|
_sl.dumper->RawWriteUint32(v);
|
|
|
}
|
|
|
|
|
|
static inline void SlWriteUint32(uint32 v)
|
|
|
void SlWriteUint64(uint64 x)
|
|
|
{
|
|
|
SlWriteUint16(GB(v, 16, 16));
|
|
|
SlWriteUint16(GB(v, 0, 16));
|
|
|
_sl.dumper->CheckBytes(8);
|
|
|
_sl.dumper->RawWriteUint64(x);
|
|
|
}
|
|
|
|
|
|
static inline void SlWriteUint64(uint64 x)
|
|
|
/**
|
|
|
* Returns number of bytes read so far
|
|
|
* May only be called during a load/load check action
|
|
|
*/
|
|
|
size_t SlGetBytesRead()
|
|
|
{
|
|
|
SlWriteUint32((uint32)(x >> 32));
|
|
|
SlWriteUint32((uint32)x);
|
|
|
assert(_sl.action == SLA_LOAD || _sl.action == SLA_LOAD_CHECK);
|
|
|
return _sl.reader->GetSize();
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* Returns number of bytes written so far
|
|
|
* May only be called during a save action
|
|
|
*/
|
|
|
size_t SlGetBytesWritten()
|
|
|
{
|
|
|
assert(_sl.action == SLA_SAVE);
|
|
|
return _sl.dumper->GetSize();
|
|
|
}
|
|
|
|
|
|
/**
|
|
@@ -1060,6 +1100,9 @@ static void SlStdString(void *ptr, VarTy
|
|
|
case SLA_LOAD_CHECK:
|
|
|
case SLA_LOAD: {
|
|
|
size_t len = SlReadArrayLength();
|
|
|
if (len > 65535) {
|
|
|
SlErrorCorrupt("String too long");
|
|
|
}
|
|
|
if (GetVarMemType(conv) == SLE_VAR_NULL) {
|
|
|
SlSkipBytes(len);
|
|
|
return;
|
|
@@ -1554,7 +1597,7 @@ static void SlVector(void *vector, VarTy
|
|
|
/** Are we going to save this object or not? */
|
|
|
static inline bool SlIsObjectValidInSavegame(const SaveLoad &sld)
|
|
|
{
|
|
|
return (_sl_version >= sld.version_from && _sl_version < sld.version_to);
|
|
|
return sld.ext_feature_test.IsFeaturePresent(_sl_version, sld.version_from, sld.version_to);
|
|
|
}
|
|
|
|
|
|
/**
|
|
@@ -2048,7 +2091,7 @@ std::vector<SaveLoad> SlCompatTableHeade
|
|
|
/* In old savegames there can be data we no longer care for. We
|
|
|
* skip this by simply reading the amount of bytes indicated and
|
|
|
* send those to /dev/null. */
|
|
|
saveloads.push_back({"", SL_NULL, SLE_FILE_U8 | SLE_VAR_NULL, slc.length, slc.version_from, slc.version_to, 0, nullptr, 0, nullptr});
|
|
|
saveloads.push_back({"", SL_NULL, SLE_FILE_U8 | SLE_VAR_NULL, slc.length, slc.version_from, slc.version_to, 0, nullptr, 0, nullptr, slc.ext_feature_test});
|
|
|
} else {
|
|
|
auto sld_it = key_lookup.find(slc.name);
|
|
|
/* If this branch triggers, it means that an entry in the
|
|
@@ -2061,6 +2104,7 @@ std::vector<SaveLoad> SlCompatTableHeade
|
|
|
SlErrorCorrupt("Internal error with savegame compatibility");
|
|
|
}
|
|
|
for (auto &sld : sld_it->second) {
|
|
|
if (!slc.ext_feature_test.IsFeaturePresent(_sl_version, SL_MIN_VERSION, SL_MAX_VERSION)) continue;
|
|
|
saveloads.push_back(*sld);
|
|
|
}
|
|
|
}
|
|
@@ -2186,7 +2230,7 @@ static void SlLoadChunk(const ChunkHandl
|
|
|
* If the chunkhandler is nullptr, the chunk is skipped.
|
|
|
* @param ch The chunkhandler that will be used for the operation
|
|
|
*/
|
|
|
static void SlLoadCheckChunk(const ChunkHandler &ch)
|
|
|
static void SlLoadCheckChunk(const ChunkHandler* ch)
|
|
|
{
|
|
|
byte m = SlReadByte();
|
|
|
size_t len;
|
|
@@ -2206,11 +2250,25 @@ static void SlLoadCheckChunk(const Chunk
|
|
|
case CH_TABLE:
|
|
|
case CH_ARRAY:
|
|
|
_sl.array_index = 0;
|
|
|
ch.LoadCheck();
|
|
|
if (ch)
|
|
|
{
|
|
|
ch->LoadCheck();
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
SlSkipArray();
|
|
|
}
|
|
|
break;
|
|
|
case CH_SPARSE_TABLE:
|
|
|
case CH_SPARSE_ARRAY:
|
|
|
ch.LoadCheck();
|
|
|
if (ch)
|
|
|
{
|
|
|
ch->LoadCheck();
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
SlSkipArray();
|
|
|
}
|
|
|
break;
|
|
|
case CH_RIFF:
|
|
|
/* Read length */
|
|
@@ -2218,7 +2276,13 @@ static void SlLoadCheckChunk(const Chunk
|
|
|
len += SlReadUint16();
|
|
|
_sl.obj_len = len;
|
|
|
endoffs = _sl.reader->GetSize() + len;
|
|
|
ch.LoadCheck(len);
|
|
|
if (ch) {
|
|
|
ch->LoadCheck(len);
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
SlSkipBytes(len);
|
|
|
}
|
|
|
if (_sl.reader->GetSize() != endoffs) SlErrorCorrupt("Invalid chunk size");
|
|
|
break;
|
|
|
default:
|
|
@@ -2230,6 +2294,25 @@ static void SlLoadCheckChunk(const Chunk
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* Load a chunk of data for checking savegames.
|
|
|
* @param ch The chunkhandler that will be used for the operation
|
|
|
*/
|
|
|
static void SlLoadCheckChunk(const ChunkHandler &ch)
|
|
|
{
|
|
|
SlLoadCheckChunk(&ch);
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* Skip unwanted chunk
|
|
|
* @param id Chunk ID
|
|
|
*/
|
|
|
static void SlLoadSkipChunk(uint32 id)
|
|
|
{
|
|
|
Debug(sl, 1, "Discarding chunk {:c}{:c}{:c}{:c}", id >> 24, id >> 16, id >> 8, id);
|
|
|
SlLoadCheckChunk(nullptr);
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* Save a chunk of data (eg. vehicles, stations, etc.). Each chunk is
|
|
|
* prefixed by an ID identifying it, followed by data, and terminator where appropriate
|
|
|
* @param ch The chunkhandler that will be used for the operation
|
|
@@ -2302,8 +2385,17 @@ static void SlLoadChunks()
|
|
|
Debug(sl, 2, "Loading chunk {:c}{:c}{:c}{:c}", id >> 24, id >> 16, id >> 8, id);
|
|
|
|
|
|
ch = SlFindChunkHandler(id);
|
|
|
if (ch == nullptr) SlErrorCorrupt("Unknown chunk type");
|
|
|
SlLoadChunk(*ch);
|
|
|
if (ch == nullptr) {
|
|
|
if (SlXvIsChunkDiscardable(id)) {
|
|
|
SlLoadSkipChunk(id);
|
|
|
}
|
|
|
else {
|
|
|
SlErrorCorrupt("Unknown chunk type");
|
|
|
}
|
|
|
}
|
|
|
else {
|
|
|
SlLoadChunk(*ch);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -2317,8 +2409,17 @@ static void SlLoadCheckChunks()
|
|
|
Debug(sl, 2, "Loading chunk {:c}{:c}{:c}{:c}", id >> 24, id >> 16, id >> 8, id);
|
|
|
|
|
|
ch = SlFindChunkHandler(id);
|
|
|
if (ch == nullptr) SlErrorCorrupt("Unknown chunk type");
|
|
|
SlLoadCheckChunk(*ch);
|
|
|
if (ch == nullptr) {
|
|
|
if (SlXvIsChunkDiscardable(id)) {
|
|
|
SlLoadSkipChunk(id);
|
|
|
}
|
|
|
else {
|
|
|
SlErrorCorruptFmt("Unknown chunk type %c%c%c%c", id >> 24, id >> 16, id >> 8, id);
|
|
|
}
|
|
|
}
|
|
|
else {
|
|
|
SlLoadCheckChunk(*ch);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -2976,7 +3077,7 @@ static SaveOrLoadResult SaveFileToDisk(b
|
|
|
const SaveLoadFormat *fmt = GetSavegameFormat(_savegame_format, &compression);
|
|
|
|
|
|
/* We have written our stuff to memory, now write it to file! */
|
|
|
uint32 hdr[2] = { fmt->tag, TO_BE32(SAVEGAME_VERSION << 16) };
|
|
|
uint32 hdr[2] = { fmt->tag, TO_BE32((uint32)(SAVEGAME_VERSION | SAVEGAME_VERSION_EXT) << 16) };
|
|
|
_sl.sf->Write((byte*)hdr, sizeof(hdr));
|
|
|
|
|
|
_sl.sf = fmt->init_write(_sl.sf, compression);
|
|
@@ -3035,6 +3136,7 @@ static SaveOrLoadResult DoSave(SaveFilte
|
|
|
_sl.sf = writer;
|
|
|
|
|
|
_sl_version = SAVEGAME_VERSION;
|
|
|
SlXvSetCurrentState();
|
|
|
|
|
|
SaveViewportBeforeSaveGame();
|
|
|
SlSaveChunks();
|
|
@@ -3087,6 +3189,8 @@ static SaveOrLoadResult DoLoad(LoadFilte
|
|
|
_load_check_data.checkable = true;
|
|
|
}
|
|
|
|
|
|
SlXvResetState();
|
|
|
|
|
|
uint32 hdr[2];
|
|
|
if (_sl.lf->Read((byte*)hdr, sizeof(hdr)) != sizeof(hdr)) SlError(STR_GAME_SAVELOAD_ERROR_FILE_NOT_READABLE);
|
|
|
|
|
@@ -3099,6 +3203,7 @@ static SaveOrLoadResult DoLoad(LoadFilte
|
|
|
_sl.lf->Reset();
|
|
|
_sl_version = SL_MIN_VERSION;
|
|
|
_sl_minor_version = 0;
|
|
|
SlXvResetState();
|
|
|
|
|
|
/* Try to find the LZO savegame format; it uses 'OTTD' as tag. */
|
|
|
fmt = _saveload_formats;
|
|
@@ -3121,11 +3226,18 @@ static SaveOrLoadResult DoLoad(LoadFilte
|
|
|
* Therefore it is loaded, but never saved (or, it saves a 0 in any scenario). */
|
|
|
_sl_minor_version = (TO_BE32(hdr[1]) >> 8) & 0xFF;
|
|
|
|
|
|
Debug(sl, 1, "Loading savegame version {}", _sl_version);
|
|
|
if (_sl_version & SAVEGAME_VERSION_EXT) {
|
|
|
_sl_version = (SaveLoadVersion)(_sl_version & ~SAVEGAME_VERSION_EXT);
|
|
|
_sl_is_ext_version = true;
|
|
|
} else if (_settings_client.gui.load_legacy_patchpack_savedata ) {
|
|
|
SlXvCheckSpecialSavegameVersionsA();
|
|
|
}
|
|
|
|
|
|
Debug(sl, 1, "Loading savegame version {}{}", _sl_version, _sl_is_ext_version ? " (extended)" : "");
|
|
|
|
|
|
/* Is the version higher than the current? */
|
|
|
if (_sl_version > SAVEGAME_VERSION) SlError(STR_GAME_SAVELOAD_ERROR_TOO_NEW_SAVEGAME);
|
|
|
if (_sl_version >= SLV_START_PATCHPACKS && _sl_version <= SLV_END_PATCHPACKS) SlError(STR_GAME_SAVELOAD_ERROR_PATCHPACK);
|
|
|
if (_sl_version >= SLV_START_PATCHPACKS && _sl_version < SLV_END_PATCHPACKS && !_sl_might_be_legacy_patchpack_save) SlError(STR_GAME_SAVELOAD_ERROR_PATCHPACK);
|
|
|
break;
|
|
|
}
|
|
|
|
|
@@ -3182,6 +3294,8 @@ static SaveOrLoadResult DoLoad(LoadFilte
|
|
|
if (load_check) {
|
|
|
/* Load chunks into _load_check_data.
|
|
|
* No pools are loaded. References are not possible, and thus do not need resolving. */
|
|
|
_load_check_data.save_version = _sl_is_faked_ext != SL_MIN_VERSION ? _sl_is_faked_ext : _sl_version;
|
|
|
_load_check_data.save_ext_type = _sl_is_ext_version ? PSXT_EXTENDED : PSXT_NONE;
|
|
|
SlLoadCheckChunks();
|
|
|
} else {
|
|
|
/* Load chunks and resolve references */
|
|
@@ -3207,6 +3321,7 @@ static SaveOrLoadResult DoLoad(LoadFilte
|
|
|
}
|
|
|
|
|
|
GamelogStopAction();
|
|
|
SlXvSetCurrentState();
|
|
|
}
|
|
|
|
|
|
return SL_OK;
|
|
@@ -3263,12 +3378,14 @@ SaveOrLoadResult SaveOrLoad(const std::s
|
|
|
if (!LoadOldSaveGame(filename)) return SL_REINIT;
|
|
|
_sl_version = SL_MIN_VERSION;
|
|
|
_sl_minor_version = 0;
|
|
|
SlXvResetState();
|
|
|
GamelogStartAction(GLAT_LOAD);
|
|
|
if (!AfterLoadGame()) {
|
|
|
GamelogStopAction();
|
|
|
return SL_REINIT;
|
|
|
}
|
|
|
GamelogStopAction();
|
|
|
SlXvSetCurrentState();
|
|
|
return SL_OK;
|
|
|
}
|
|
|
|