/* $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(nullptr), bufe(nullptr), 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 unaligned_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 unaligned_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 unaligned_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 = std::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 = std::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 *((unaligned_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 *((unaligned_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 *((unaligned_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