diff --git a/projects/openttd_vs100.vcxproj b/projects/openttd_vs100.vcxproj
index cf29b3377a..d66ac1bbd7 100644
--- a/projects/openttd_vs100.vcxproj
+++ b/projects/openttd_vs100.vcxproj
@@ -932,6 +932,7 @@
+
diff --git a/projects/openttd_vs100.vcxproj.filters b/projects/openttd_vs100.vcxproj.filters
index 5fe2ac6f1b..3b82a1b2ac 100644
--- a/projects/openttd_vs100.vcxproj.filters
+++ b/projects/openttd_vs100.vcxproj.filters
@@ -1956,6 +1956,9 @@
Save/Load handlers
+
+ Save/Load handlers
+
Save/Load handlers
diff --git a/projects/openttd_vs140.vcxproj b/projects/openttd_vs140.vcxproj
index a6085d0b5c..65b379f56d 100644
--- a/projects/openttd_vs140.vcxproj
+++ b/projects/openttd_vs140.vcxproj
@@ -953,6 +953,7 @@
+
diff --git a/projects/openttd_vs140.vcxproj.filters b/projects/openttd_vs140.vcxproj.filters
index 5fe2ac6f1b..3b82a1b2ac 100644
--- a/projects/openttd_vs140.vcxproj.filters
+++ b/projects/openttd_vs140.vcxproj.filters
@@ -1956,6 +1956,9 @@
Save/Load handlers
+
+ Save/Load handlers
+
Save/Load handlers
diff --git a/projects/openttd_vs141.vcxproj b/projects/openttd_vs141.vcxproj
index 791e8042ce..3912edd19b 100644
--- a/projects/openttd_vs141.vcxproj
+++ b/projects/openttd_vs141.vcxproj
@@ -953,6 +953,7 @@
+
diff --git a/projects/openttd_vs141.vcxproj.filters b/projects/openttd_vs141.vcxproj.filters
index 5fe2ac6f1b..3b82a1b2ac 100644
--- a/projects/openttd_vs141.vcxproj.filters
+++ b/projects/openttd_vs141.vcxproj.filters
@@ -1956,6 +1956,9 @@
Save/Load handlers
+
+ Save/Load handlers
+
Save/Load handlers
diff --git a/projects/openttd_vs80.vcproj b/projects/openttd_vs80.vcproj
index ac79ce3581..8e23a786cd 100644
--- a/projects/openttd_vs80.vcproj
+++ b/projects/openttd_vs80.vcproj
@@ -2934,6 +2934,10 @@
RelativePath=".\..\src\saveload\saveload_internal.h"
>
+
+
diff --git a/projects/openttd_vs90.vcproj b/projects/openttd_vs90.vcproj
index 99d3bb5ab5..0683f8c604 100644
--- a/projects/openttd_vs90.vcproj
+++ b/projects/openttd_vs90.vcproj
@@ -2931,6 +2931,10 @@
RelativePath=".\..\src\saveload\saveload_internal.h"
>
+
+
diff --git a/source.list b/source.list
index 5acfab1ffe..55427f7c66 100644
--- a/source.list
+++ b/source.list
@@ -670,6 +670,7 @@ saveload/saveload.cpp
saveload/saveload.h
saveload/saveload_filter.h
saveload/saveload_internal.h
+saveload/saveload_buffer.h
saveload/signs_sl.cpp
saveload/station_sl.cpp
saveload/storage_sl.cpp
diff --git a/src/core/bitmath_func.hpp b/src/core/bitmath_func.hpp
index e6cf00bae8..33f16e137a 100644
--- a/src/core/bitmath_func.hpp
+++ b/src/core/bitmath_func.hpp
@@ -393,13 +393,32 @@ static inline T ROR(const T x, const uint8 n)
* (since it will use hardware swapping if available).
* Even though they should return uint16 and uint32, we get
* warnings if we don't cast those (why?) */
+ #define BSWAP64(x) ((uint32)CFSwapInt64(x))
#define BSWAP32(x) ((uint32)CFSwapInt32(x))
#define BSWAP16(x) ((uint16)CFSwapInt16(x))
#elif defined(_MSC_VER)
/* MSVC has intrinsics for swapping, resulting in faster code */
+ #define BSWAP64(x) (_byteswap_uint64(x))
#define BSWAP32(x) (_byteswap_ulong(x))
#define BSWAP16(x) (_byteswap_ushort(x))
#else
+ /**
+ * Perform a 64 bits endianness bitswap on x.
+ * @param x the variable to bitswap
+ * @return the bitswapped value.
+ */
+ static inline uint64 BSWAP64(uint64 x)
+ {
+#if !defined(__ICC) && (defined(__GNUC__) || defined(__clang__))
+ /* GCC >= 4.3 provides a builtin, resulting in faster code */
+ return (uint64)__builtin_bswap64((uint64)x);
+#else
+ return ((x >> 56) & 0xFFULL) | ((x >> 40) & 0xFF00ULL) | ((x >> 24) & 0xFF0000ULL) | ((x >> 8) & 0xFF000000ULL) |
+ ((x << 8) & 0xFF00000000ULL) | ((x << 24) & 0xFF0000000000ULL) | ((x << 40) & 0xFF000000000000ULL) | ((x << 56) & 0xFF000000000000ULL);
+ ;
+#endif /* __GNUC__ || __clang__ */
+ }
+
/**
* Perform a 32 bits endianness bitswap on x.
* @param x the variable to bitswap
diff --git a/src/core/endian_func.hpp b/src/core/endian_func.hpp
index ab5b181500..48060bd1ad 100644
--- a/src/core/endian_func.hpp
+++ b/src/core/endian_func.hpp
@@ -19,25 +19,33 @@
#if TTD_ENDIAN == TTD_BIG_ENDIAN
#define FROM_BE16(x) (x)
#define FROM_BE32(x) (x)
+ #define FROM_BE64(x) (x)
#define TO_BE16(x) (x)
#define TO_BE32(x) (x)
#define TO_BE32X(x) (x)
+ #define TO_BE64(x) (x)
#define FROM_LE16(x) BSWAP16(x)
#define FROM_LE32(x) BSWAP32(x)
+ #define FROM_LE64(x) BSWAP64(x)
#define TO_LE16(x) BSWAP16(x)
#define TO_LE32(x) BSWAP32(x)
#define TO_LE32X(x) BSWAP32(x)
+ #define TO_LE64(x) BSWAP64(x)
#else
#define FROM_BE16(x) BSWAP16(x)
#define FROM_BE32(x) BSWAP32(x)
+ #define FROM_BE64(x) BSWAP64(x)
#define TO_BE16(x) BSWAP16(x)
#define TO_BE32(x) BSWAP32(x)
#define TO_BE32X(x) BSWAP32(x)
+ #define TO_BE64(x) BSWAP64(x)
#define FROM_LE16(x) (x)
#define FROM_LE32(x) (x)
+ #define FROM_LE64(x) (x)
#define TO_LE16(x) (x)
#define TO_LE32(x) (x)
#define TO_LE32X(x) (x)
+ #define TO_LE64(x) (x)
#endif /* TTD_ENDIAN == TTD_BIG_ENDIAN */
static inline uint16 ReadLE16Aligned(const void *x)
diff --git a/src/saveload/saveload.cpp b/src/saveload/saveload.cpp
index af11b5147b..56797d7875 100644
--- a/src/saveload/saveload.cpp
+++ b/src/saveload/saveload.cpp
@@ -52,6 +52,7 @@
#include "saveload_internal.h"
#include "saveload_filter.h"
+#include "saveload_buffer.h"
#include "extended_ver_sl.h"
#include "../safeguards.h"
@@ -301,108 +302,113 @@ enum SaveLoadAction {
enum NeedLength {
NL_NONE = 0, ///< not working in NeedLength mode
NL_WANTLENGTH = 1, ///< writing length and data
- 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(NULL), bufe(NULL), 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;
+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;
}
+ }
+}
- return *this->bufp++;
+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(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 = CallocT(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();
+
+ uint block_count = this->blocks.size();
+ for (uint i = 0; i < block_count; i++) {
+ writer->Write(this->blocks[i].data, this->blocks[i].size);
}
- /**
- * Get the size of the memory dump made so far.
- * @return The size.
- */
- size_t GetSize() const
- {
- return this->read - (this->bufe - this->bufp);
- }
-};
+ writer->Finish();
+}
+void MemoryDumper::StartAutoLength()
+{
+ assert(this->saved_buf == nullptr);
-/** Container for dumping the savegame (quickly) to memory. */
-struct MemoryDumper {
- AutoFreeSmallVector 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.
+ this->saved_buf = this->buf;
+ this->saved_bufe = this->bufe;
+ this->buf = this->autolen_buf;
+ this->bufe = this->autolen_buf_end;
+}
- /** Initialise our variables. */
- MemoryDumper() : buf(NULL), bufe(NULL)
- {
- }
+std::pair MemoryDumper::StopAutoLength()
+{
+ assert(this->saved_buf != nullptr);
+ auto res = std::make_pair(this->autolen_buf, this->buf - this->autolen_buf);
- /**
- * 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(MEMORY_CHUNK_SIZE);
- *this->blocks.Append() = this->buf;
- this->bufe = this->buf + MEMORY_CHUNK_SIZE;
- }
+ this->buf = this->saved_buf;
+ this->bufe = this->saved_bufe;
+ this->saved_buf = this->saved_bufe = nullptr;
+ return res;
+}
- *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 = 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.Length() * MEMORY_CHUNK_SIZE - (this->bufe - this->buf);
- }
-};
+/**
+ * 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);
+}
/** The saveload struct, containing reader-writer functions, buffer, version, etc. */
struct SaveLoadParams {
@@ -429,6 +435,16 @@ struct SaveLoadParams {
static SaveLoadParams _sl; ///< Parameters used for/at saveload.
+ReadBuffer *ReadBuffer::GetCurrent()
+{
+ return _sl.reader;
+}
+
+MemoryDumper *MemoryDumper::GetCurrent()
+{
+ return _sl.dumper;
+}
+
/* these define the chunks */
extern const ChunkHandler _version_ext_chunk_handlers[];
extern const ChunkHandler _gamelog_chunk_handlers[];
@@ -665,6 +681,34 @@ byte SlReadByte()
return _sl.reader->ReadByte();
}
+/**
+ * Read in bytes from the file/data structure but don't do
+ * anything with them, discarding them in effect
+ * @param length The amount of bytes that is being treated this way
+ */
+void SlSkipBytes(size_t length)
+{
+ return _sl.reader->SkipBytes(length);
+}
+
+int SlReadUint16()
+{
+ _sl.reader->CheckBytes(2);
+ return _sl.reader->RawReadUint16();
+}
+
+uint32 SlReadUint32()
+{
+ _sl.reader->CheckBytes(4);
+ return _sl.reader->RawReadUint32();
+}
+
+uint64 SlReadUint64()
+{
+ _sl.reader->CheckBytes(8);
+ return _sl.reader->RawReadUint64();
+}
+
/**
* Wrapper for writing a byte to the dumper.
* @param b The byte to write.
@@ -674,6 +718,24 @@ void SlWriteByte(byte b)
_sl.dumper->WriteByte(b);
}
+void SlWriteUint16(uint16 v)
+{
+ _sl.dumper->CheckBytes(2);
+ _sl.dumper->RawWriteUint16(v);
+}
+
+void SlWriteUint32(uint32 v)
+{
+ _sl.dumper->CheckBytes(4);
+ _sl.dumper->RawWriteUint32(v);
+}
+
+void SlWriteUint64(uint64 v)
+{
+ _sl.dumper->CheckBytes(8);
+ _sl.dumper->RawWriteUint64(v);
+}
+
/**
* Returns number of bytes read so far
* May only be called during a load/load check action
@@ -942,10 +1004,6 @@ void SlSetLength(size_t length)
}
break;
- case NL_CALCLENGTH:
- _sl.obj_len += (int)length;
- break;
-
default: NOT_REACHED();
}
}
@@ -963,10 +1021,10 @@ static void SlCopyBytes(void *ptr, size_t length)
switch (_sl.action) {
case SLA_LOAD_CHECK:
case SLA_LOAD:
- for (; length != 0; length--) *p++ = SlReadByte();
+ _sl.reader->CopyBytes(p, length);
break;
case SLA_SAVE:
- for (; length != 0; length--) SlWriteByte(*p++);
+ _sl.dumper->CopyBytes(p, length);
break;
default: NOT_REACHED();
}
@@ -1279,8 +1337,6 @@ void SlArray(void *array, size_t length, VarType conv)
/* Automatically calculate the length? */
if (_sl.need_length != NL_NONE) {
SlSetLength(SlCalcArrayLen(length, conv));
- /* Determine length only? */
- if (_sl.need_length == NL_CALCLENGTH) return;
}
/* NOTICE - handle some buggy stuff, in really old versions everything was saved
@@ -1480,8 +1536,6 @@ static void SlList(void *list, SLRefType conv)
/* Automatically calculate the length? */
if (_sl.need_length != NL_NONE) {
SlSetLength(SlCalcListLen(list));
- /* Determine length only? */
- if (_sl.need_length == NL_CALCLENGTH) return;
}
PtrList *l = (PtrList *)list;
@@ -1538,8 +1592,6 @@ static void SlVarList(void *list, VarType conv)
/* Automatically calculate the length? */
if (_sl.need_length != NL_NONE) {
SlSetLength(SlCalcVarListLen(list, size_len));
- /* Determine length only? */
- if (_sl.need_length == NL_CALCLENGTH) return;
}
PtrList *l = (PtrList *)list;
@@ -1813,7 +1865,6 @@ void SlObject(void *object, const SaveLoad *sld)
/* Automatically calculate the length? */
if (_sl.need_length != NL_NONE) {
SlSetLength(SlCalcObjLength(object, sld));
- if (_sl.need_length == NL_CALCLENGTH) return;
}
for (; sld->cmd != SL_END; sld++) {
@@ -1838,25 +1889,17 @@ void SlGlobList(const SaveLoadGlobVarList *sldg)
*/
void SlAutolength(AutolengthProc *proc, void *arg)
{
- size_t offs;
-
assert(_sl.action == SLA_SAVE);
+ assert(_sl.need_length == NL_WANTLENGTH);
- /* Tell it to calculate the length */
- _sl.need_length = NL_CALCLENGTH;
- _sl.obj_len = 0;
+ _sl.need_length = NL_NONE;
+ _sl.dumper->StartAutoLength();
proc(arg);
-
+ auto result = _sl.dumper->StopAutoLength();
/* Setup length */
_sl.need_length = NL_WANTLENGTH;
- SlSetLength(_sl.obj_len);
-
- offs = _sl.dumper->GetSize() + _sl.obj_len;
-
- /* And write the stuff */
- proc(arg);
-
- if (offs != _sl.dumper->GetSize()) SlErrorCorrupt("Invalid chunk size");
+ SlSetLength(result.second);
+ _sl.dumper->CopyBytes(result.first, result.second);
}
/*
diff --git a/src/saveload/saveload.h b/src/saveload/saveload.h
index 08a19b0690..e306aab8ce 100644
--- a/src/saveload/saveload.h
+++ b/src/saveload/saveload.h
@@ -694,52 +694,15 @@ size_t SlCalcObjLength(const void *object, const SaveLoad *sld);
byte SlReadByte();
void SlWriteByte(byte b);
-static inline int SlReadUint16()
-{
- int x = SlReadByte() << 8;
- return x | SlReadByte();
-}
+int SlReadUint16();
+uint32 SlReadUint32();
+uint64 SlReadUint64();
-static inline uint32 SlReadUint32()
-{
- uint32 x = SlReadUint16() << 16;
- return x | SlReadUint16();
-}
+void SlWriteUint16(uint16 v);
+void SlWriteUint32(uint32 v);
+void SlWriteUint64(uint64 v);
-static inline uint64 SlReadUint64()
-{
- uint32 x = SlReadUint32();
- uint32 y = SlReadUint32();
- return (uint64)x << 32 | y;
-}
-
-static inline void SlWriteUint16(uint16 v)
-{
- SlWriteByte(GB(v, 8, 8));
- SlWriteByte(GB(v, 0, 8));
-}
-
-static inline void SlWriteUint32(uint32 v)
-{
- SlWriteUint16(GB(v, 16, 16));
- SlWriteUint16(GB(v, 0, 16));
-}
-
-static inline void SlWriteUint64(uint64 x)
-{
- SlWriteUint32((uint32)(x >> 32));
- SlWriteUint32((uint32)x);
-}
-
-/**
- * Read in bytes from the file/data structure but don't do
- * anything with them, discarding them in effect
- * @param length The amount of bytes that is being treated this way
- */
-static inline void SlSkipBytes(size_t length)
-{
- for (; length != 0; length--) SlReadByte();
-}
+void SlSkipBytes(size_t length);
size_t SlGetBytesRead();
size_t SlGetBytesWritten();
diff --git a/src/saveload/saveload_buffer.h b/src/saveload/saveload_buffer.h
new file mode 100644
index 0000000000..1ca754cead
--- /dev/null
+++ b/src/saveload/saveload_buffer.h
@@ -0,0 +1,266 @@
+/* $Id$ */
+
+/*
+ * This file is part of OpenTTD.
+ * OpenTTD 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, version 2.
+ * OpenTTD 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 OpenTTD. If not, see .
+ */
+
+/** @file saveload_buffer.h Functions/types related to buffers used for saving and loading games. */
+
+#ifndef SAVELOAD_BUFFER_H
+#define SAVELOAD_BUFFER_H
+
+#include "../core/alloc_func.hpp"
+#include "../core/endian_type.hpp"
+#include "../core/endian_func.hpp"
+#include "../core/math_func.hpp"
+
+#include
+#include
+
+struct LoadFilter;
+struct SaveFilter;
+
+/** 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(NULL), bufe(NULL), reader(reader), read(0)
+ {
+ }
+
+ static ReadBuffer *GetCurrent();
+
+ void SkipBytesSlowPath(size_t bytes);
+ void AcquireBytes();
+
+ inline void SkipBytes(size_t bytes)
+ {
+ byte *b = this->bufp + bytes;
+ if (likely(b <= this->bufe)) {
+ this->bufp = b;
+ } else {
+ SkipBytesSlowPath(bytes);
+ }
+ }
+
+ inline byte RawReadByte()
+ {
+ return *this->bufp++;
+ }
+
+ inline byte ReadByte()
+ {
+ if (unlikely(this->bufp == this->bufe)) {
+ this->AcquireBytes();
+ }
+
+ return RawReadByte();
+ }
+
+ inline void CheckBytes(size_t bytes)
+ {
+ while (unlikely(this->bufp + bytes > this->bufe)) this->AcquireBytes();
+ }
+
+ inline int RawReadUint16()
+ {
+#if OTTD_ALIGNMENT == 0
+ int x = FROM_BE16(*((const uint16*) this->bufp));
+ this->bufp += 2;
+ return x;
+#else
+ int x = this->RawReadByte() << 8;
+ return x | this->RawReadByte();
+#endif
+ }
+
+ inline uint32 RawReadUint32()
+ {
+#if OTTD_ALIGNMENT == 0
+ uint32 x = FROM_BE32(*((const uint32*) this->bufp));
+ this->bufp += 4;
+ return x;
+#else
+ uint32 x = this->RawReadUint16() << 16;
+ return x | this->RawReadUint16();
+#endif
+ }
+
+ inline uint64 RawReadUint64()
+ {
+#if OTTD_ALIGNMENT == 0
+ uint64 x = FROM_BE64(*((const uint64*) this->bufp));
+ this->bufp += 8;
+ return x;
+#else
+ uint32 x = this->RawReadUint32();
+ uint32 y = this->RawReadUint32();
+ return (uint64)x << 32 | y;
+#endif
+ }
+
+ inline void CopyBytes(byte *ptr, size_t length)
+ {
+ while (length) {
+ if (unlikely(this->bufp == this->bufe)) {
+ this->AcquireBytes();
+ }
+ size_t to_copy = min(this->bufe - this->bufp, length);
+ memcpy(ptr, this->bufp, to_copy);
+ this->bufp += to_copy;
+ ptr += to_copy;
+ length -= to_copy;
+ }
+ }
+
+ /**
+ * Get the size of the memory dump made so far.
+ * @return The size.
+ */
+ inline size_t GetSize() const
+ {
+ return this->read - (this->bufe - this->bufp);
+ }
+};
+
+
+/** Container for dumping the savegame (quickly) to memory. */
+struct MemoryDumper {
+ struct BufferInfo {
+ byte *data;
+ size_t size = 0;
+
+ BufferInfo(byte *d) : data(d) {}
+ ~BufferInfo() { free(this->data); }
+
+ BufferInfo(const BufferInfo &) = delete;
+ BufferInfo(BufferInfo &&other) : data(other.data), size(other.size) { other.data = nullptr; };
+ };
+
+ std::vector blocks; ///< Buffer with blocks of allocated memory.
+ byte *buf = nullptr; ///< Buffer we're going to write to.
+ byte *bufe = nullptr; ///< End of the buffer we write to.
+ size_t completed_block_bytes = 0; ///< Total byte count of completed blocks.
+
+ byte *autolen_buf = nullptr;
+ byte *autolen_buf_end = nullptr;
+ byte *saved_buf = nullptr;
+ byte *saved_bufe = nullptr;
+
+ MemoryDumper()
+ {
+ const size_t size = 8192;
+ this->autolen_buf = CallocT(size);
+ this->autolen_buf_end = this->autolen_buf + size;
+ }
+
+ ~MemoryDumper()
+ {
+ free(this->autolen_buf);
+ }
+
+ static MemoryDumper *GetCurrent();
+
+ void FinaliseBlock();
+ void AllocateBuffer();
+
+ inline void CheckBytes(size_t bytes)
+ {
+ if (unlikely(this->buf + bytes > this->bufe)) this->AllocateBuffer();
+ }
+
+ /**
+ * 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 (unlikely(this->buf == this->bufe)) {
+ this->AllocateBuffer();
+ }
+
+ *this->buf++ = b;
+ }
+
+ inline void CopyBytes(byte *ptr, size_t length)
+ {
+ while (length) {
+ if (unlikely(this->buf == this->bufe)) {
+ this->AllocateBuffer();
+ }
+ size_t to_copy = min(this->bufe - this->buf, length);
+ memcpy(this->buf, ptr, to_copy);
+ this->buf += to_copy;
+ ptr += to_copy;
+ length -= to_copy;
+ }
+ }
+
+ inline void RawWriteByte(byte b)
+ {
+ *this->buf++ = b;
+ }
+
+ inline void RawWriteUint16(uint16 v)
+ {
+#if OTTD_ALIGNMENT == 0
+ *((uint16 *) this->buf) = TO_BE16(v);
+#else
+ this->buf[0] = GB(v, 8, 8));
+ this->buf[1] = GB(v, 0, 8));
+#endif
+ this->buf += 2;
+ }
+
+ inline void RawWriteUint32(uint32 v)
+ {
+#if OTTD_ALIGNMENT == 0
+ *((uint32 *) this->buf) = TO_BE32(v);
+#else
+ this->buf[0] = GB(v, 24, 8));
+ this->buf[1] = GB(v, 16, 8));
+ this->buf[2] = GB(v, 8, 8));
+ this->buf[3] = GB(v, 0, 8));
+#endif
+ this->buf += 4;
+ }
+
+ inline void RawWriteUint64(uint64 v)
+ {
+#if OTTD_ALIGNMENT == 0
+ *((uint64 *) this->buf) = TO_BE64(v);
+#else
+ this->buf[0] = GB(v, 56, 8));
+ this->buf[1] = GB(v, 48, 8));
+ this->buf[2] = GB(v, 40, 8));
+ this->buf[3] = GB(v, 32, 8));
+ this->buf[4] = GB(v, 24, 8));
+ this->buf[5] = GB(v, 16, 8));
+ this->buf[6] = GB(v, 8, 8));
+ this->buf[7] = GB(v, 0, 8));
+#endif
+ this->buf += 8;
+ }
+
+ void Flush(SaveFilter *writer);
+ size_t GetSize() const;
+ void StartAutoLength();
+ std::pair StopAutoLength();
+};
+
+#endif