diff --git a/src/openrct2/rct12/SawyerChunk.cpp b/src/openrct2/rct12/SawyerChunk.cpp index 1f77f6b551..4c73858f83 100644 --- a/src/openrct2/rct12/SawyerChunk.cpp +++ b/src/openrct2/rct12/SawyerChunk.cpp @@ -12,9 +12,8 @@ #include "../core/Memory.hpp" #include "SawyerChunkReader.h" -SawyerChunk::SawyerChunk(SAWYER_ENCODING encoding, std::unique_ptr data, size_t length) +SawyerChunk::SawyerChunk(SAWYER_ENCODING encoding, std::vector&& data) + : _data(std::move(data)) + , _encoding(encoding) { - _encoding = encoding; - _data = std::move(data); - _length = length; } diff --git a/src/openrct2/rct12/SawyerChunk.h b/src/openrct2/rct12/SawyerChunk.h index 4da6f864b5..93ea2c3068 100644 --- a/src/openrct2/rct12/SawyerChunk.h +++ b/src/openrct2/rct12/SawyerChunk.h @@ -10,6 +10,7 @@ #pragma once #include +#include /** * The type of encoding / compression for a sawyer encoded chunk. @@ -28,23 +29,22 @@ enum class SAWYER_ENCODING : uint8_t class SawyerChunk final { private: - std::unique_ptr _data; - size_t _length = 0; + std::vector _data; SAWYER_ENCODING _encoding = SAWYER_ENCODING::NONE; public: const void* GetData() const { - return _data.get(); + return _data.data(); } size_t GetLength() const { - return _length; + return _data.size(); } SAWYER_ENCODING GetEncoding() const { return _encoding; } - SawyerChunk(SAWYER_ENCODING encoding, std::unique_ptr data, size_t length); + SawyerChunk(SAWYER_ENCODING encoding, std::vector&& data); }; diff --git a/src/openrct2/rct12/SawyerChunkReader.cpp b/src/openrct2/rct12/SawyerChunkReader.cpp index 1a2e815ca8..d654ea5777 100644 --- a/src/openrct2/rct12/SawyerChunkReader.cpp +++ b/src/openrct2/rct12/SawyerChunkReader.cpp @@ -76,15 +76,12 @@ std::shared_ptr SawyerChunkReader::ReadChunk() throw SawyerChunkException(EXCEPTION_MSG_CORRUPT_CHUNK_SIZE); } - auto buffer = std::make_unique(MAX_UNCOMPRESSED_CHUNK_SIZE); - size_t uncompressedLength = DecodeChunk( - buffer.get(), MAX_UNCOMPRESSED_CHUNK_SIZE, compressedData.get(), header); - if (uncompressedLength == 0) + auto buffer = DecodeChunk(compressedData.get(), header); + if (buffer.empty()) { throw SawyerChunkException(EXCEPTION_MSG_ZERO_SIZED_CHUNK); } - return std::make_shared( - static_cast(header.encoding), std::move(buffer), uncompressedLength); + return std::make_shared(static_cast(header.encoding), std::move(buffer)); } default: throw SawyerChunkException(EXCEPTION_MSG_INVALID_CHUNK_ENCODING); @@ -117,14 +114,13 @@ std::shared_ptr SawyerChunkReader::ReadChunkTrack() throw SawyerChunkException(EXCEPTION_MSG_CORRUPT_CHUNK_SIZE); } - auto buffer = std::make_unique(MAX_UNCOMPRESSED_CHUNK_SIZE); SawyerCodingChunkHeader header{ CHUNK_ENCODING_RLE, compressedDataLength }; - size_t uncompressedLength = DecodeChunk(buffer.get(), MAX_UNCOMPRESSED_CHUNK_SIZE, compressedData.get(), header); - if (uncompressedLength == 0) + auto buffer = DecodeChunk(compressedData.get(), header); + if (buffer.empty()) { throw SawyerChunkException(EXCEPTION_MSG_ZERO_SIZED_CHUNK); } - return std::make_shared(SAWYER_ENCODING::RLE, std::move(buffer), uncompressedLength); + return std::make_shared(SAWYER_ENCODING::RLE, std::move(buffer)); } catch (const std::exception&) { @@ -155,47 +151,42 @@ void SawyerChunkReader::ReadChunk(void* dst, size_t length) } } -size_t SawyerChunkReader::DecodeChunk(void* dst, size_t dstCapacity, const void* src, const SawyerCodingChunkHeader& header) +std::vector SawyerChunkReader::DecodeChunk(const void* src, const SawyerCodingChunkHeader& header) { - size_t resultLength; + std::vector buf; switch (header.encoding) { case CHUNK_ENCODING_NONE: - if (header.length > dstCapacity) - { - throw SawyerChunkException(EXCEPTION_MSG_DESTINATION_TOO_SMALL); - } - std::memcpy(dst, src, header.length); - resultLength = header.length; + buf.resize(header.length); + std::memcpy(buf.data(), src, header.length); break; case CHUNK_ENCODING_RLE: - resultLength = DecodeChunkRLE(dst, dstCapacity, src, header.length); + buf = DecodeChunkRLE(src, header.length); break; case CHUNK_ENCODING_RLECOMPRESSED: - resultLength = DecodeChunkRLERepeat(dst, dstCapacity, src, header.length); + buf = DecodeChunkRLERepeat(src, header.length); break; case CHUNK_ENCODING_ROTATE: - resultLength = DecodeChunkRotate(dst, dstCapacity, src, header.length); + buf = DecodeChunkRotate(src, header.length); break; default: throw SawyerChunkException(EXCEPTION_MSG_INVALID_CHUNK_ENCODING); } - return resultLength; + return buf; } -size_t SawyerChunkReader::DecodeChunkRLERepeat(void* dst, size_t dstCapacity, const void* src, size_t srcLength) +std::vector SawyerChunkReader::DecodeChunkRLERepeat(const void* src, size_t srcLength) { - auto immBuffer = std::make_unique(MAX_UNCOMPRESSED_CHUNK_SIZE); - auto immLength = DecodeChunkRLE(immBuffer.get(), MAX_UNCOMPRESSED_CHUNK_SIZE, src, srcLength); - auto size = DecodeChunkRepeat(dst, dstCapacity, immBuffer.get(), immLength); - return size; + auto tempBuf = DecodeChunkRLE(src, srcLength); + return DecodeChunkRepeat(tempBuf.data(), tempBuf.size()); } -size_t SawyerChunkReader::DecodeChunkRLE(void* dst, size_t dstCapacity, const void* src, size_t srcLength) +std::vector SawyerChunkReader::DecodeChunkRLE(const void* src, size_t srcLength) { + std::vector buf; + buf.reserve(srcLength); + auto src8 = static_cast(src); - auto dst8 = static_cast(dst); - auto dstEnd = dst8 + dstCapacity; for (size_t i = 0; i < srcLength; i++) { uint8_t rleCodeByte = src8[i]; @@ -208,88 +199,91 @@ size_t SawyerChunkReader::DecodeChunkRLE(void* dst, size_t dstCapacity, const vo { throw SawyerChunkException(EXCEPTION_MSG_CORRUPT_RLE); } - if (dst8 + count > dstEnd) + if (buf.size() + count > MAX_UNCOMPRESSED_CHUNK_SIZE) { throw SawyerChunkException(EXCEPTION_MSG_DESTINATION_TOO_SMALL); } - std::fill_n(dst8, count, src8[i]); - dst8 += count; + buf.insert(buf.end(), count, src8[i]); } else + { + const auto len = rleCodeByte + 1; + + if (i + 1 >= srcLength) + { + throw SawyerChunkException(EXCEPTION_MSG_CORRUPT_RLE); + } + if (buf.size() + len > MAX_UNCOMPRESSED_CHUNK_SIZE) + { + throw SawyerChunkException(EXCEPTION_MSG_DESTINATION_TOO_SMALL); + } + if (i + 1 + len > srcLength) + { + throw SawyerChunkException(EXCEPTION_MSG_CORRUPT_RLE); + } + + const auto* pos = src8 + i + 1; + buf.insert(buf.end(), pos, pos + len); + + i += len; + } + } + + return buf; +} + +std::vector SawyerChunkReader::DecodeChunkRepeat(const void* src, size_t srcLength) +{ + std::vector buf; + buf.reserve(srcLength); + + auto src8 = static_cast(src); + for (size_t i = 0; i < srcLength; i++) + { + if (src8[i] == 0xFF) { if (i + 1 >= srcLength) { throw SawyerChunkException(EXCEPTION_MSG_CORRUPT_RLE); } - if (dst8 + rleCodeByte + 1 > dstEnd) - { - throw SawyerChunkException(EXCEPTION_MSG_DESTINATION_TOO_SMALL); - } - if (i + 1 + rleCodeByte + 1 > srcLength) - { - throw SawyerChunkException(EXCEPTION_MSG_CORRUPT_RLE); - } - - std::memcpy(dst8, src8 + i + 1, rleCodeByte + 1); - dst8 += rleCodeByte + 1; - i += rleCodeByte + 1; - } - } - return reinterpret_cast(dst8) - reinterpret_cast(dst); -} - -size_t SawyerChunkReader::DecodeChunkRepeat(void* dst, size_t dstCapacity, const void* src, size_t srcLength) -{ - auto src8 = static_cast(src); - auto dst8 = static_cast(dst); - auto dstEnd = dst8 + dstCapacity; - for (size_t i = 0; i < srcLength; i++) - { - if (src8[i] == 0xFF) - { - *dst8++ = src8[++i]; + buf.push_back(src8[++i]); } else { size_t count = (src8[i] & 7) + 1; - const uint8_t* copySrc = dst8 + static_cast(src8[i] >> 3) - 32; + int32_t offset = static_cast(src8[i] >> 3) - 32; + const uint8_t* copySrc = buf.data() + (buf.size() + offset); - if (dst8 + count >= dstEnd || copySrc + count >= dstEnd) - { - throw SawyerChunkException(EXCEPTION_MSG_DESTINATION_TOO_SMALL); - } - if (copySrc < dst) - { - throw SawyerChunkException(EXCEPTION_MSG_CORRUPT_RLE); - } - if ((copySrc < (dst8 + count) && copySrc >= dst8) - || ((copySrc + count) <= (dst8 + count) && (copySrc + count) > dst8)) + if (copySrc < buf.data() || copySrc + count > buf.data() + buf.size()) { throw SawyerChunkException(EXCEPTION_MSG_CORRUPT_RLE); } - std::memcpy(dst8, copySrc, count); - dst8 += count; + // We need a temporary buffer as the vector might invalidate the pointer. + uint8_t temp[16]; + std::memcpy(temp, copySrc, count); + + buf.insert(buf.end(), std::begin(temp), std::begin(temp) + count); } } - return reinterpret_cast(dst8) - reinterpret_cast(dst); + + return buf; } -size_t SawyerChunkReader::DecodeChunkRotate(void* dst, size_t dstCapacity, const void* src, size_t srcLength) +std::vector SawyerChunkReader::DecodeChunkRotate(const void* src, size_t srcLength) { - if (srcLength > dstCapacity) - { - throw SawyerChunkException(EXCEPTION_MSG_DESTINATION_TOO_SMALL); - } + std::vector buf; + buf.reserve(srcLength); auto src8 = static_cast(src); - auto dst8 = static_cast(dst); + uint8_t code = 1; for (size_t i = 0; i < srcLength; i++) { - dst8[i] = Numerics::ror8(src8[i], code); + buf.push_back(Numerics::ror8(src8[i], code)); code = (code + 2) % 8; } - return srcLength; + + return buf; } diff --git a/src/openrct2/rct12/SawyerChunkReader.h b/src/openrct2/rct12/SawyerChunkReader.h index b42b549116..cb878e5b3c 100644 --- a/src/openrct2/rct12/SawyerChunkReader.h +++ b/src/openrct2/rct12/SawyerChunkReader.h @@ -13,7 +13,9 @@ #include "../util/SawyerCoding.h" #include "SawyerChunk.h" +#include #include +#include class SawyerChunkException : public IOException { @@ -85,9 +87,9 @@ public: } private: - static size_t DecodeChunk(void* dst, size_t dstCapacity, const void* src, const SawyerCodingChunkHeader& header); - static size_t DecodeChunkRLERepeat(void* dst, size_t dstCapacity, const void* src, size_t srcLength); - static size_t DecodeChunkRLE(void* dst, size_t dstCapacity, const void* src, size_t srcLength); - static size_t DecodeChunkRepeat(void* dst, size_t dstCapacity, const void* src, size_t srcLength); - static size_t DecodeChunkRotate(void* dst, size_t dstCapacity, const void* src, size_t srcLength); + static std::vector DecodeChunk(const void* src, const SawyerCodingChunkHeader& header); + static std::vector DecodeChunkRLERepeat(const void* src, size_t srcLength); + static std::vector DecodeChunkRLE(const void* src, size_t srcLength); + static std::vector DecodeChunkRepeat(const void* src, size_t srcLength); + static std::vector DecodeChunkRotate(const void* src, size_t srcLength); };