mirror of https://github.com/OpenRCT2/OpenRCT2.git
Stream changes from NSF (#15446)
* Bring over NSF changes to stream classes * Add orca stream to project files * Bring over util changes as well. * Add const to util and fix util ungzip * Add const and apply review comments * Apply review comments
This commit is contained in:
parent
bfe7caacd7
commit
e01c9a3afa
|
@ -805,6 +805,7 @@
|
|||
7CDC7EE9B12E40FB9FE78546 /* Crypt.OpenRCT2.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4248E4E4394842D4AF6119DA /* Crypt.OpenRCT2.cpp */; };
|
||||
4653963391E945D397BCCA0C /* ChangeMapSizeAction.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 76CE81CEAF7F4538976F7C4C /* ChangeMapSizeAction.cpp */; };
|
||||
F1BE1CB5525C4FF794A3F3CE /* ChangeMapSizeAction.h in Headers */ = {isa = PBXBuildFile; fileRef = C49050C073DB4CB980E1EC5A /* ChangeMapSizeAction.h */; };
|
||||
B769EFE0AA1149AE93C15DED /* OrcaStream.hpp in Headers */ = {isa = PBXBuildFile; fileRef = F9061C16C67A45D7915FA229 /* OrcaStream.hpp */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXContainerItemProxy section */
|
||||
|
@ -1913,6 +1914,7 @@
|
|||
4248E4E4394842D4AF6119DA /* Crypt.OpenRCT2.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Crypt.OpenRCT2.cpp; path = src/openrct2/core/Crypt.OpenRCT2.cpp; sourceTree = SOURCE_ROOT; };
|
||||
76CE81CEAF7F4538976F7C4C /* ChangeMapSizeAction.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ChangeMapSizeAction.cpp; path = src/openrct2/actions/ChangeMapSizeAction.cpp; sourceTree = SOURCE_ROOT; };
|
||||
C49050C073DB4CB980E1EC5A /* ChangeMapSizeAction.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ChangeMapSizeAction.h; path = src/openrct2/actions/ChangeMapSizeAction.h; sourceTree = SOURCE_ROOT; };
|
||||
F9061C16C67A45D7915FA229 /* OrcaStream.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = OrcaStream.hpp; path = src/openrct2/core/OrcaStream.hpp; sourceTree = SOURCE_ROOT; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
|
@ -2567,7 +2569,6 @@
|
|||
F76C838C1EC4E7CC00FA49E2 /* MemoryStream.cpp */,
|
||||
F76C838D1EC4E7CC00FA49E2 /* MemoryStream.h */,
|
||||
2ADE2F24224418B2002598AF /* Meta.hpp */,
|
||||
F76C838E1EC4E7CC00FA49E2 /* Nullable.hpp */,
|
||||
2ADE2F23224418B1002598AF /* Numerics.hpp */,
|
||||
F76C838F1EC4E7CC00FA49E2 /* Path.cpp */,
|
||||
F76C83901EC4E7CC00FA49E2 /* Path.hpp */,
|
||||
|
@ -2587,6 +2588,7 @@
|
|||
F4D523B8782E4C458AF1490D /* GroupVector.hpp */,
|
||||
F28A181D311D4E078FDB090C /* ZipStream.hpp */,
|
||||
4248E4E4394842D4AF6119DA /* Crypt.OpenRCT2.cpp */,
|
||||
F9061C16C67A45D7915FA229 /* OrcaStream.hpp */,
|
||||
);
|
||||
path = core;
|
||||
sourceTree = "<group>";
|
||||
|
@ -3672,6 +3674,7 @@
|
|||
C8D612EB56BD4214BEC0F7FF /* GroupVector.hpp in Headers */,
|
||||
B2F44E535BD14A03BE8B9D14 /* ZipStream.hpp in Headers */,
|
||||
F1BE1CB5525C4FF794A3F3CE /* ChangeMapSizeAction.h in Headers */,
|
||||
B769EFE0AA1149AE93C15DED /* OrcaStream.hpp in Headers */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
#include "String.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <string_view>
|
||||
|
||||
#ifndef _WIN32
|
||||
# include <sys/stat.h>
|
||||
|
@ -42,6 +43,11 @@ namespace OpenRCT2
|
|||
{
|
||||
}
|
||||
|
||||
FileStream::FileStream(std::string_view path, int32_t fileMode)
|
||||
: FileStream(std::string(path), fileMode)
|
||||
{
|
||||
}
|
||||
|
||||
FileStream::FileStream(const utf8* path, int32_t fileMode)
|
||||
{
|
||||
const char* mode;
|
||||
|
|
|
@ -38,6 +38,7 @@ namespace OpenRCT2
|
|||
public:
|
||||
FileStream(const fs::path& path, int32_t fileMode);
|
||||
FileStream(const std::string& path, int32_t fileMode);
|
||||
FileStream(std::string_view path, int32_t fileMode);
|
||||
FileStream(const utf8* path, int32_t fileMode);
|
||||
~FileStream() override;
|
||||
|
||||
|
|
|
@ -57,6 +57,17 @@ namespace OpenRCT2
|
|||
}
|
||||
}
|
||||
|
||||
void IStream::WriteString(const std::string_view str)
|
||||
{
|
||||
for (const auto c : str)
|
||||
{
|
||||
if (c == '\0')
|
||||
break;
|
||||
WriteValue<uint8_t>(c);
|
||||
}
|
||||
WriteValue<uint8_t>(0);
|
||||
}
|
||||
|
||||
void IStream::WriteString(const std::string& str)
|
||||
{
|
||||
WriteString(str.c_str());
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
#include <memory>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
#ifdef __WARN_SUGGEST_FINAL_METHODS__
|
||||
|
@ -207,6 +208,7 @@ namespace OpenRCT2
|
|||
utf8* ReadString();
|
||||
std::string ReadStdString();
|
||||
void WriteString(const utf8* str);
|
||||
void WriteString(const std::string_view string);
|
||||
void WriteString(const std::string& string);
|
||||
};
|
||||
|
||||
|
|
|
@ -262,6 +262,12 @@ namespace OpenRCT2
|
|||
Write<16>(buffer);
|
||||
}
|
||||
|
||||
void MemoryStream::Clear()
|
||||
{
|
||||
_dataSize = 0;
|
||||
SetPosition(0);
|
||||
}
|
||||
|
||||
void MemoryStream::EnsureCapacity(size_t capacity)
|
||||
{
|
||||
if (_dataCapacity < capacity)
|
||||
|
|
|
@ -112,6 +112,8 @@ namespace OpenRCT2
|
|||
|
||||
uint64_t TryRead(void* buffer, uint64_t length) override;
|
||||
|
||||
void Clear();
|
||||
|
||||
private:
|
||||
void EnsureCapacity(size_t capacity);
|
||||
};
|
||||
|
|
|
@ -0,0 +1,679 @@
|
|||
/*****************************************************************************
|
||||
* Copyright (c) 2014-2019 OpenRCT2 developers
|
||||
*
|
||||
* For a complete list of all authors, please refer to contributors.md
|
||||
* Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2
|
||||
*
|
||||
* OpenRCT2 is licensed under the GNU General Public License version 3.
|
||||
*****************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../world/Location.hpp"
|
||||
#include "Crypt.h"
|
||||
#include "FileStream.h"
|
||||
#include "MemoryStream.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#include <stack>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
|
||||
namespace OpenRCT2
|
||||
{
|
||||
class OrcaStream
|
||||
{
|
||||
public:
|
||||
enum class Mode
|
||||
{
|
||||
READING,
|
||||
WRITING,
|
||||
};
|
||||
|
||||
static constexpr uint32_t COMPRESSION_NONE = 0;
|
||||
static constexpr uint32_t COMPRESSION_GZIP = 1;
|
||||
|
||||
private:
|
||||
#pragma pack(push, 1)
|
||||
struct Header
|
||||
{
|
||||
uint32_t Magic{};
|
||||
uint32_t TargetVersion{};
|
||||
uint32_t MinVersion{};
|
||||
uint32_t NumChunks{};
|
||||
uint64_t UncompressedSize{};
|
||||
uint32_t Compression{};
|
||||
uint64_t CompressedSize{};
|
||||
std::array<uint8_t, 8> FNV1a{};
|
||||
uint8_t padding[20];
|
||||
};
|
||||
static_assert(sizeof(Header) == 64, "Header should be 64 bytes");
|
||||
|
||||
struct ChunkEntry
|
||||
{
|
||||
uint32_t Id{};
|
||||
uint64_t Offset{};
|
||||
uint64_t Length{};
|
||||
};
|
||||
#pragma pack(pop)
|
||||
|
||||
IStream* _stream;
|
||||
Mode _mode;
|
||||
Header _header;
|
||||
std::vector<ChunkEntry> _chunks;
|
||||
MemoryStream _buffer;
|
||||
ChunkEntry _currentChunk;
|
||||
|
||||
public:
|
||||
OrcaStream(IStream& stream, const Mode mode)
|
||||
{
|
||||
_stream = &stream;
|
||||
_mode = mode;
|
||||
if (mode == Mode::READING)
|
||||
{
|
||||
_header = _stream->ReadValue<Header>();
|
||||
|
||||
_chunks.clear();
|
||||
for (uint32_t i = 0; i < _header.NumChunks; i++)
|
||||
{
|
||||
auto entry = _stream->ReadValue<ChunkEntry>();
|
||||
_chunks.push_back(entry);
|
||||
}
|
||||
|
||||
// Read compressed data into buffer (read in blocks)
|
||||
_buffer = MemoryStream{};
|
||||
uint8_t temp[2048];
|
||||
uint64_t bytesLeft = _header.CompressedSize;
|
||||
do
|
||||
{
|
||||
auto readLen = std::min(size_t(bytesLeft), sizeof(temp));
|
||||
_stream->Read(temp, readLen);
|
||||
_buffer.Write(temp, readLen);
|
||||
bytesLeft -= readLen;
|
||||
} while (bytesLeft > 0);
|
||||
|
||||
// Uncompress
|
||||
if (_header.Compression == COMPRESSION_GZIP)
|
||||
{
|
||||
auto uncompressedData = Ungzip(_buffer.GetData(), _buffer.GetLength());
|
||||
if (_header.UncompressedSize != uncompressedData.size())
|
||||
{
|
||||
// Warning?
|
||||
}
|
||||
_buffer.Clear();
|
||||
_buffer.Write(uncompressedData.data(), uncompressedData.size());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_header = {};
|
||||
_header.Compression = COMPRESSION_GZIP;
|
||||
|
||||
_buffer = MemoryStream{};
|
||||
}
|
||||
}
|
||||
|
||||
OrcaStream(const OrcaStream&) = delete;
|
||||
|
||||
~OrcaStream()
|
||||
{
|
||||
if (_mode == Mode::WRITING)
|
||||
{
|
||||
const void* uncompressedData = _buffer.GetData();
|
||||
const uint64_t uncompressedSize = _buffer.GetLength();
|
||||
|
||||
_header.NumChunks = static_cast<uint32_t>(_chunks.size());
|
||||
_header.UncompressedSize = uncompressedSize;
|
||||
_header.CompressedSize = uncompressedSize;
|
||||
_header.FNV1a = Crypt::FNV1a(uncompressedData, uncompressedSize);
|
||||
|
||||
// Compress data
|
||||
std::optional<std::vector<uint8_t>> compressedBytes;
|
||||
if (_header.Compression == COMPRESSION_GZIP)
|
||||
{
|
||||
compressedBytes = Gzip(uncompressedData, uncompressedSize);
|
||||
if (compressedBytes)
|
||||
{
|
||||
_header.CompressedSize = compressedBytes->size();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Compression failed
|
||||
_header.Compression = COMPRESSION_NONE;
|
||||
}
|
||||
}
|
||||
|
||||
// Write header and chunk table
|
||||
_stream->WriteValue(_header);
|
||||
for (const auto& chunk : _chunks)
|
||||
{
|
||||
_stream->WriteValue(chunk);
|
||||
}
|
||||
|
||||
// Write chunk data
|
||||
if (compressedBytes)
|
||||
{
|
||||
_stream->Write(compressedBytes->data(), compressedBytes->size());
|
||||
}
|
||||
else
|
||||
{
|
||||
_stream->Write(uncompressedData, uncompressedSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Mode GetMode() const
|
||||
{
|
||||
return _mode;
|
||||
}
|
||||
|
||||
Header& GetHeader()
|
||||
{
|
||||
return _header;
|
||||
}
|
||||
|
||||
const Header& GetHeader() const
|
||||
{
|
||||
return _header;
|
||||
}
|
||||
|
||||
template<typename TFunc> bool ReadWriteChunk(const uint32_t chunkId, TFunc f)
|
||||
{
|
||||
if (_mode == Mode::READING)
|
||||
{
|
||||
if (SeekChunk(chunkId))
|
||||
{
|
||||
ChunkStream stream(_buffer, _mode);
|
||||
f(stream);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
_currentChunk.Id = chunkId;
|
||||
_currentChunk.Offset = _buffer.GetPosition();
|
||||
_currentChunk.Length = 0;
|
||||
ChunkStream stream(_buffer, _mode);
|
||||
f(stream);
|
||||
_currentChunk.Length = static_cast<uint64_t>(_buffer.GetPosition()) - _currentChunk.Offset;
|
||||
_chunks.push_back(_currentChunk);
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
bool SeekChunk(const uint32_t id)
|
||||
{
|
||||
const auto result = std::find_if(_chunks.begin(), _chunks.end(), [id](const ChunkEntry& e) { return e.Id == id; });
|
||||
if (result != _chunks.end())
|
||||
{
|
||||
const auto offset = result->Offset;
|
||||
_buffer.SetPosition(offset);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public:
|
||||
class ChunkStream
|
||||
{
|
||||
private:
|
||||
struct ArrayState
|
||||
{
|
||||
std::streampos StartPos{};
|
||||
std::streampos LastPos{};
|
||||
size_t Count{};
|
||||
size_t ElementSize{};
|
||||
};
|
||||
|
||||
MemoryStream& _buffer;
|
||||
Mode _mode;
|
||||
std::stack<ArrayState> _arrayStack;
|
||||
|
||||
public:
|
||||
ChunkStream(MemoryStream& buffer, const Mode mode)
|
||||
: _buffer(buffer)
|
||||
, _mode(mode)
|
||||
{
|
||||
}
|
||||
|
||||
Mode GetMode() const
|
||||
{
|
||||
return _mode;
|
||||
}
|
||||
|
||||
MemoryStream& GetStream()
|
||||
{
|
||||
return _buffer;
|
||||
}
|
||||
|
||||
void ReadWrite(void* addr, const size_t len)
|
||||
{
|
||||
if (_mode == Mode::READING)
|
||||
{
|
||||
ReadBuffer(addr, len);
|
||||
}
|
||||
else
|
||||
{
|
||||
WriteBuffer(addr, len);
|
||||
}
|
||||
}
|
||||
|
||||
void Read(void* addr, const size_t len)
|
||||
{
|
||||
if (_mode == Mode::READING)
|
||||
{
|
||||
ReadBuffer(addr, len);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw std::runtime_error("Incorrect mode");
|
||||
}
|
||||
}
|
||||
|
||||
void Write(const void* addr, const size_t len)
|
||||
{
|
||||
if (_mode == Mode::READING)
|
||||
{
|
||||
throw std::runtime_error("Incorrect mode");
|
||||
}
|
||||
else
|
||||
{
|
||||
WriteBuffer(addr, len);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T, std::enable_if_t<std::is_integral<T>::value, bool> = true> void ReadWrite(T& v)
|
||||
{
|
||||
if (_mode == Mode::READING)
|
||||
{
|
||||
v = ReadInteger<T>();
|
||||
}
|
||||
else
|
||||
{
|
||||
WriteInteger(v);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T, std::enable_if_t<std::is_enum<T>::value, bool> = true> void ReadWrite(T& v)
|
||||
{
|
||||
using underlying = typename std::underlying_type<T>::type;
|
||||
if (_mode == Mode::READING)
|
||||
{
|
||||
v = static_cast<T>(ReadInteger<underlying>());
|
||||
}
|
||||
else
|
||||
{
|
||||
WriteInteger(static_cast<underlying>(v));
|
||||
}
|
||||
}
|
||||
|
||||
void ReadWrite(bool& value)
|
||||
{
|
||||
uint8_t value8 = value ? 1 : 0;
|
||||
ReadWrite(&value8, sizeof(value8));
|
||||
value = value8 != 0;
|
||||
}
|
||||
|
||||
void ReadWrite(CoordsXY& coords)
|
||||
{
|
||||
ReadWrite(coords.x);
|
||||
ReadWrite(coords.y);
|
||||
}
|
||||
|
||||
void ReadWrite(CoordsXYZ& coords)
|
||||
{
|
||||
ReadWrite(coords.x);
|
||||
ReadWrite(coords.y);
|
||||
ReadWrite(coords.z);
|
||||
}
|
||||
|
||||
void ReadWrite(CoordsXYZD& coords)
|
||||
{
|
||||
ReadWrite(coords.x);
|
||||
ReadWrite(coords.y);
|
||||
ReadWrite(coords.z);
|
||||
ReadWrite(coords.direction);
|
||||
}
|
||||
|
||||
void ReadWrite(TileCoordsXY& coords)
|
||||
{
|
||||
ReadWrite(coords.x);
|
||||
ReadWrite(coords.y);
|
||||
}
|
||||
|
||||
void ReadWrite(TileCoordsXYZ& coords)
|
||||
{
|
||||
ReadWrite(coords.x);
|
||||
ReadWrite(coords.y);
|
||||
ReadWrite(coords.z);
|
||||
}
|
||||
|
||||
void ReadWrite(TileCoordsXYZD& coords)
|
||||
{
|
||||
ReadWrite(coords.x);
|
||||
ReadWrite(coords.y);
|
||||
ReadWrite(coords.z);
|
||||
ReadWrite(coords.direction);
|
||||
}
|
||||
|
||||
template<typename T, typename = std::enable_if<std::is_integral<T>::value>> T Read()
|
||||
{
|
||||
T v{};
|
||||
ReadWrite(v);
|
||||
return v;
|
||||
}
|
||||
|
||||
void ReadWrite(std::string& v)
|
||||
{
|
||||
if (_mode == Mode::READING)
|
||||
{
|
||||
v = ReadString();
|
||||
}
|
||||
else
|
||||
{
|
||||
WriteString(v);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T, typename = std::enable_if<std::is_integral<T>::value>> void Write(T v)
|
||||
{
|
||||
if (_mode == Mode::READING)
|
||||
{
|
||||
T temp{};
|
||||
ReadWrite(temp);
|
||||
}
|
||||
else
|
||||
{
|
||||
ReadWrite(v);
|
||||
}
|
||||
}
|
||||
|
||||
void Write(const char* v)
|
||||
{
|
||||
std::string_view sv;
|
||||
if (v != nullptr)
|
||||
sv = v;
|
||||
Write(sv);
|
||||
}
|
||||
|
||||
void Write(const std::string_view v)
|
||||
{
|
||||
if (_mode == Mode::READING)
|
||||
{
|
||||
std::string temp;
|
||||
ReadWrite(temp);
|
||||
}
|
||||
else
|
||||
{
|
||||
WriteString(v);
|
||||
}
|
||||
}
|
||||
|
||||
void Write(const std::string& v)
|
||||
{
|
||||
Write(std::string_view(v));
|
||||
}
|
||||
|
||||
template<typename TVec, typename TFunc> void ReadWriteVector(TVec& vec, TFunc f)
|
||||
{
|
||||
if (_mode == Mode::READING)
|
||||
{
|
||||
const auto count = BeginArray();
|
||||
vec.clear();
|
||||
for (size_t i = 0; i < count; i++)
|
||||
{
|
||||
auto& el = vec.emplace_back();
|
||||
f(el);
|
||||
NextArrayElement();
|
||||
}
|
||||
EndArray();
|
||||
}
|
||||
else
|
||||
{
|
||||
BeginArray();
|
||||
for (auto& el : vec)
|
||||
{
|
||||
f(el);
|
||||
NextArrayElement();
|
||||
}
|
||||
EndArray();
|
||||
}
|
||||
}
|
||||
|
||||
template<typename TArr, size_t TArrSize, typename TFunc> void ReadWriteArray(TArr (&arr)[TArrSize], TFunc f)
|
||||
{
|
||||
auto& arr2 = *(reinterpret_cast<std::array<TArr, TArrSize>*>(arr));
|
||||
ReadWriteArray(arr2, f);
|
||||
}
|
||||
|
||||
template<typename TArr, size_t TArrSize, typename TFunc>
|
||||
void ReadWriteArray(std::array<TArr, TArrSize>& arr, TFunc f)
|
||||
{
|
||||
if (_mode == Mode::READING)
|
||||
{
|
||||
const auto count = BeginArray();
|
||||
for (auto& el : arr)
|
||||
{
|
||||
el = {};
|
||||
}
|
||||
for (size_t i = 0; i < count; i++)
|
||||
{
|
||||
if (i < TArrSize)
|
||||
{
|
||||
f(arr[i]);
|
||||
}
|
||||
NextArrayElement();
|
||||
}
|
||||
EndArray();
|
||||
}
|
||||
else
|
||||
{
|
||||
BeginArray();
|
||||
for (auto& el : arr)
|
||||
{
|
||||
if (f(el))
|
||||
{
|
||||
NextArrayElement();
|
||||
}
|
||||
}
|
||||
EndArray();
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T> void Ignore()
|
||||
{
|
||||
T value{};
|
||||
ReadWrite(value);
|
||||
}
|
||||
|
||||
private:
|
||||
void ReadBuffer(void* dst, const size_t len)
|
||||
{
|
||||
_buffer.Read(dst, len);
|
||||
}
|
||||
|
||||
void WriteBuffer(const void* buffer, const size_t len)
|
||||
{
|
||||
_buffer.Write(buffer, len);
|
||||
}
|
||||
|
||||
template<typename T, typename = std::enable_if<std::is_integral<T>::value>> T ReadInteger()
|
||||
{
|
||||
if constexpr (sizeof(T) > 4)
|
||||
{
|
||||
if constexpr (std::is_signed<T>())
|
||||
{
|
||||
int64_t raw{};
|
||||
Read(&raw, sizeof(raw));
|
||||
return static_cast<T>(raw);
|
||||
}
|
||||
|
||||
uint64_t raw{};
|
||||
Read(&raw, sizeof(raw));
|
||||
return static_cast<T>(raw);
|
||||
}
|
||||
else
|
||||
{
|
||||
if constexpr (std::is_signed<T>())
|
||||
{
|
||||
int32_t raw{};
|
||||
Read(&raw, sizeof(raw));
|
||||
if (raw < std::numeric_limits<T>::min() || raw > std::numeric_limits<T>::max())
|
||||
{
|
||||
throw std::runtime_error("Value is incompatible with internal type.");
|
||||
}
|
||||
return static_cast<T>(raw);
|
||||
}
|
||||
else
|
||||
{
|
||||
uint32_t raw{};
|
||||
Read(&raw, sizeof(raw));
|
||||
if (raw > std::numeric_limits<T>::max())
|
||||
{
|
||||
throw std::runtime_error("Value is incompatible with internal type.");
|
||||
}
|
||||
return static_cast<T>(raw);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T, typename = std::enable_if<std::is_integral<T>::value>> void WriteInteger(const T value)
|
||||
{
|
||||
if constexpr (sizeof(T) > 4)
|
||||
{
|
||||
if constexpr (std::is_signed<T>())
|
||||
{
|
||||
auto raw = static_cast<int64_t>(value);
|
||||
Write(&raw, sizeof(raw));
|
||||
}
|
||||
else
|
||||
{
|
||||
auto raw = static_cast<uint64_t>(value);
|
||||
Write(&raw, sizeof(raw));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if constexpr (std::is_signed<T>())
|
||||
{
|
||||
auto raw = static_cast<int32_t>(value);
|
||||
Write(&raw, sizeof(raw));
|
||||
}
|
||||
else
|
||||
{
|
||||
auto raw = static_cast<uint32_t>(value);
|
||||
Write(&raw, sizeof(raw));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::string ReadString()
|
||||
{
|
||||
std::string buffer;
|
||||
buffer.reserve(64);
|
||||
while (true)
|
||||
{
|
||||
char c{};
|
||||
ReadBuffer(&c, sizeof(c));
|
||||
if (c == '\0')
|
||||
{
|
||||
break;
|
||||
}
|
||||
buffer.push_back(c);
|
||||
}
|
||||
buffer.shrink_to_fit();
|
||||
return buffer;
|
||||
}
|
||||
|
||||
void WriteString(const std::string_view s)
|
||||
{
|
||||
const char nullt = '\0';
|
||||
auto len = s.find('\0');
|
||||
if (len == std::string_view::npos)
|
||||
{
|
||||
len = s.size();
|
||||
}
|
||||
_buffer.Write(s.data(), len);
|
||||
_buffer.Write(&nullt, sizeof(nullt));
|
||||
}
|
||||
|
||||
size_t BeginArray()
|
||||
{
|
||||
auto& arrayState = _arrayStack.emplace();
|
||||
if (_mode == Mode::READING)
|
||||
{
|
||||
arrayState.Count = Read<uint32_t>();
|
||||
arrayState.ElementSize = Read<uint32_t>();
|
||||
arrayState.LastPos = _buffer.GetPosition();
|
||||
return arrayState.Count;
|
||||
}
|
||||
|
||||
arrayState.Count = 0;
|
||||
arrayState.ElementSize = 0;
|
||||
arrayState.StartPos = _buffer.GetPosition();
|
||||
Write<uint32_t>(0);
|
||||
Write<uint32_t>(0);
|
||||
arrayState.LastPos = _buffer.GetPosition();
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool NextArrayElement()
|
||||
{
|
||||
auto& arrayState = _arrayStack.top();
|
||||
if (_mode == Mode::READING)
|
||||
{
|
||||
if (arrayState.Count == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (arrayState.ElementSize != 0)
|
||||
{
|
||||
arrayState.LastPos += arrayState.ElementSize;
|
||||
_buffer.SetPosition(arrayState.LastPos);
|
||||
}
|
||||
arrayState.Count--;
|
||||
return arrayState.Count == 0;
|
||||
}
|
||||
|
||||
const auto lastElSize = static_cast<size_t>(_buffer.GetPosition()) - arrayState.LastPos;
|
||||
if (arrayState.Count == 0)
|
||||
{
|
||||
// Set array element size based on first element size
|
||||
arrayState.ElementSize = lastElSize;
|
||||
}
|
||||
else if (arrayState.ElementSize != lastElSize)
|
||||
{
|
||||
// Array element size was different from first element so reset it
|
||||
// to dynamic
|
||||
arrayState.ElementSize = 0;
|
||||
}
|
||||
arrayState.Count++;
|
||||
arrayState.LastPos = _buffer.GetPosition();
|
||||
return true;
|
||||
}
|
||||
|
||||
void EndArray()
|
||||
{
|
||||
auto& arrayState = _arrayStack.top();
|
||||
if (_mode == Mode::WRITING)
|
||||
{
|
||||
const size_t backupPos = _buffer.GetPosition();
|
||||
if (backupPos != static_cast<size_t>(arrayState.StartPos) + 8 && arrayState.Count == 0)
|
||||
{
|
||||
throw std::runtime_error("Array data was written but no elements were added.");
|
||||
}
|
||||
_buffer.SetPosition(arrayState.StartPos);
|
||||
Write(static_cast<uint32_t>(arrayState.Count));
|
||||
Write(static_cast<uint32_t>(arrayState.ElementSize));
|
||||
_buffer.SetPosition(backupPos);
|
||||
}
|
||||
_arrayStack.pop();
|
||||
}
|
||||
};
|
||||
};
|
||||
} // namespace OpenRCT2
|
|
@ -177,6 +177,7 @@
|
|||
<ClInclude Include="core\MemoryStream.h" />
|
||||
<ClInclude Include="core\Meta.hpp" />
|
||||
<ClInclude Include="core\Numerics.hpp" />
|
||||
<ClInclude Include="core\OrcaStream.hpp" />
|
||||
<ClInclude Include="core\Path.hpp" />
|
||||
<ClInclude Include="core\Random.hpp" />
|
||||
<ClInclude Include="core\RTL.h" />
|
||||
|
|
|
@ -693,6 +693,102 @@ bool util_gzip_compress(FILE* source, FILE* dest)
|
|||
return true;
|
||||
}
|
||||
|
||||
std::vector<uint8_t> Gzip(const void* data, const size_t dataLen)
|
||||
{
|
||||
assert(data != nullptr);
|
||||
|
||||
std::vector<uint8_t> output;
|
||||
z_stream strm{};
|
||||
strm.zalloc = Z_NULL;
|
||||
strm.zfree = Z_NULL;
|
||||
strm.opaque = Z_NULL;
|
||||
|
||||
{
|
||||
const auto ret = deflateInit2(&strm, Z_DEFAULT_COMPRESSION, Z_DEFLATED, 15 | 16, 8, Z_DEFAULT_STRATEGY);
|
||||
if (ret != Z_OK)
|
||||
{
|
||||
throw std::runtime_error("deflateInit2 failed with error " + std::to_string(ret));
|
||||
}
|
||||
}
|
||||
|
||||
int flush = 0;
|
||||
const auto* src = static_cast<const Bytef*>(data);
|
||||
size_t srcRemaining = dataLen;
|
||||
do
|
||||
{
|
||||
const auto nextBlockSize = std::min(srcRemaining, CHUNK);
|
||||
srcRemaining -= nextBlockSize;
|
||||
|
||||
flush = srcRemaining == 0 ? Z_FINISH : Z_NO_FLUSH;
|
||||
strm.avail_in = static_cast<uInt>(nextBlockSize);
|
||||
strm.next_in = const_cast<Bytef*>(src);
|
||||
do
|
||||
{
|
||||
output.resize(output.size() + nextBlockSize);
|
||||
strm.avail_out = static_cast<uInt>(nextBlockSize);
|
||||
strm.next_out = &output[output.size() - nextBlockSize];
|
||||
const auto ret = deflate(&strm, flush);
|
||||
if (ret == Z_STREAM_ERROR)
|
||||
{
|
||||
throw std::runtime_error("deflate failed with error " + std::to_string(ret));
|
||||
}
|
||||
output.resize(output.size() - strm.avail_out);
|
||||
} while (strm.avail_out == 0);
|
||||
|
||||
src += nextBlockSize;
|
||||
} while (flush != Z_FINISH);
|
||||
deflateEnd(&strm);
|
||||
return output;
|
||||
}
|
||||
|
||||
std::vector<uint8_t> Ungzip(const void* data, const size_t dataLen)
|
||||
{
|
||||
assert(data != nullptr);
|
||||
|
||||
std::vector<uint8_t> output;
|
||||
z_stream strm{};
|
||||
strm.zalloc = Z_NULL;
|
||||
strm.zfree = Z_NULL;
|
||||
strm.opaque = Z_NULL;
|
||||
|
||||
{
|
||||
const auto ret = inflateInit2(&strm, 15 | 16);
|
||||
if (ret != Z_OK)
|
||||
{
|
||||
throw std::runtime_error("inflateInit2 failed with error " + std::to_string(ret));
|
||||
}
|
||||
}
|
||||
|
||||
int flush = 0;
|
||||
const auto* src = static_cast<const Bytef*>(data);
|
||||
size_t srcRemaining = dataLen;
|
||||
do
|
||||
{
|
||||
const auto nextBlockSize = std::min(srcRemaining, CHUNK);
|
||||
srcRemaining -= nextBlockSize;
|
||||
|
||||
flush = srcRemaining == 0 ? Z_FINISH : Z_NO_FLUSH;
|
||||
strm.avail_in = static_cast<uInt>(nextBlockSize);
|
||||
strm.next_in = const_cast<Bytef*>(src);
|
||||
do
|
||||
{
|
||||
output.resize(output.size() + nextBlockSize);
|
||||
strm.avail_out = static_cast<uInt>(nextBlockSize);
|
||||
strm.next_out = &output[output.size() - nextBlockSize];
|
||||
const auto ret = inflate(&strm, flush);
|
||||
if (ret == Z_STREAM_ERROR)
|
||||
{
|
||||
throw std::runtime_error("deflate failed with error " + std::to_string(ret));
|
||||
}
|
||||
output.resize(output.size() - strm.avail_out);
|
||||
} while (strm.avail_out == 0);
|
||||
|
||||
src += nextBlockSize;
|
||||
} while (flush != Z_FINISH);
|
||||
inflateEnd(&strm);
|
||||
return output;
|
||||
}
|
||||
|
||||
// Type-independent code left as macro to reduce duplicate code.
|
||||
#define add_clamp_body(value, value_to_add, min_cap, max_cap) \
|
||||
if ((value_to_add > 0) && (value > (max_cap - (value_to_add)))) \
|
||||
|
|
|
@ -58,6 +58,8 @@ uint32_t util_rand();
|
|||
std::optional<std::vector<uint8_t>> util_zlib_deflate(const uint8_t* data, size_t data_in_size);
|
||||
uint8_t* util_zlib_inflate(const uint8_t* data, size_t data_in_size, size_t* data_out_size);
|
||||
bool util_gzip_compress(FILE* source, FILE* dest);
|
||||
std::vector<uint8_t> Gzip(const void* data, const size_t dataLen);
|
||||
std::vector<uint8_t> Ungzip(const void* data, const size_t dataLen);
|
||||
|
||||
int8_t add_clamp_int8_t(int8_t value, int8_t value_to_add);
|
||||
int16_t add_clamp_int16_t(int16_t value, int16_t value_to_add);
|
||||
|
|
Loading…
Reference in New Issue