From 0971db99c3278f718e52d5d9fefa52a9860eae03 Mon Sep 17 00:00:00 2001 From: Ted John Date: Thu, 8 Apr 2021 00:32:07 +0100 Subject: [PATCH] Use actual GZIP compression --- src/openrct2/core/OrcaStream.hpp | 11 ++-- src/openrct2/util/Util.cpp | 90 ++++++++++++++++++++++++++++++++ src/openrct2/util/Util.h | 6 ++- 3 files changed, 98 insertions(+), 9 deletions(-) diff --git a/src/openrct2/core/OrcaStream.hpp b/src/openrct2/core/OrcaStream.hpp index 08ecf84fcc..0bebc9058e 100644 --- a/src/openrct2/core/OrcaStream.hpp +++ b/src/openrct2/core/OrcaStream.hpp @@ -99,16 +99,13 @@ namespace OpenRCT2 // Uncompress if (_header.Compression == COMPRESSION_GZIP) { - size_t outUncompressedSize{}; - auto uncompressedData = util_zlib_inflate( - reinterpret_cast(_buffer.GetData()), _buffer.GetLength(), &outUncompressedSize); - if (_header.UncompressedSize != outUncompressedSize) + auto uncompressedData = Ungzip(_buffer.GetData(), _buffer.GetLength()); + if (_header.UncompressedSize != uncompressedData.size()) { // Warning? } _buffer.Clear(); - _buffer.Write(uncompressedData, outUncompressedSize); - std::free(uncompressedData); + _buffer.Write(uncompressedData.data(), uncompressedData.size()); } } else @@ -141,7 +138,7 @@ namespace OpenRCT2 std::optional> compressedBytes; if (_header.Compression == COMPRESSION_GZIP) { - compressedBytes = util_zlib_deflate(reinterpret_cast(uncompressedData), uncompressedSize); + compressedBytes = Gzip(uncompressedData, uncompressedSize); if (compressedBytes) { _header.CompressedSize = compressedBytes->size(); diff --git a/src/openrct2/util/Util.cpp b/src/openrct2/util/Util.cpp index a1308ce454..691a9c48c0 100644 --- a/src/openrct2/util/Util.cpp +++ b/src/openrct2/util/Util.cpp @@ -696,6 +696,96 @@ bool util_gzip_compress(FILE* source, FILE* dest) return true; } +std::vector Gzip(const void* data, size_t dataLen) +{ + assert(data != nullptr); + + std::vector output; + z_stream strm{}; + strm.zalloc = Z_NULL; + strm.zfree = Z_NULL; + strm.opaque = Z_NULL; + 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; + const auto* src = static_cast(data); + size_t srcRemaining = dataLen; + do + { + auto nextBlockSize = std::min(srcRemaining, CHUNK); + srcRemaining -= nextBlockSize; + + flush = srcRemaining == 0 ? Z_FINISH : Z_NO_FLUSH; + strm.avail_in = static_cast(nextBlockSize); + strm.next_in = const_cast(src); + do + { + output.resize(output.size() + nextBlockSize); + strm.avail_out = nextBlockSize; + strm.next_out = &output[output.size() - nextBlockSize]; + 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 Ungzip(const void* data, size_t dataLen) +{ + assert(data != nullptr); + + std::vector output; + z_stream strm{}; + strm.zalloc = Z_NULL; + strm.zfree = Z_NULL; + strm.opaque = Z_NULL; + auto ret = inflateInit2(&strm, 15 | 16); + if (ret != Z_OK) + { + throw std::runtime_error("inflateInit2 failed with error " + std::to_string(ret)); + } + + int flush; + const auto* src = static_cast(data); + size_t srcRemaining = dataLen; + do + { + auto nextBlockSize = std::min(srcRemaining, CHUNK); + srcRemaining -= nextBlockSize; + + flush = srcRemaining == 0 ? Z_FINISH : Z_NO_FLUSH; + strm.avail_in = static_cast(nextBlockSize); + strm.next_in = const_cast(src); + do + { + output.resize(output.size() + nextBlockSize); + strm.avail_out = nextBlockSize; + strm.next_out = &output[output.size() - nextBlockSize]; + 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); + deflateEnd(&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)))) \ diff --git a/src/openrct2/util/Util.h b/src/openrct2/util/Util.h index 0806c437db..d502cc8bce 100644 --- a/src/openrct2/util/Util.h +++ b/src/openrct2/util/Util.h @@ -59,6 +59,8 @@ uint32_t util_rand(); std::optional> 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 Gzip(const void* data, size_t dataLen); +std::vector Ungzip(const void* data, 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); @@ -71,13 +73,13 @@ uint8_t soft_light(uint8_t a, uint8_t b); size_t strcatftime(char* buffer, size_t bufferSize, const char* format, const struct tm* tp); -template[[nodiscard]] constexpr uint64_t EnumToFlag(T v) +template [[nodiscard]] constexpr uint64_t EnumToFlag(T v) { static_assert(std::is_enum_v); return 1ULL << static_cast>(v); } -template[[nodiscard]] constexpr uint64_t EnumsToFlags(T... types) +template [[nodiscard]] constexpr uint64_t EnumsToFlags(T... types) { return (EnumToFlag(types) | ...); }