1
0
mirror of https://github.com/OpenRCT2/OpenRCT2 synced 2026-01-24 15:24:30 +01:00

Move SawyerChunk units into OpenRCT2 namespace

This commit is contained in:
Aaron van Geffen
2024-10-29 22:02:41 +01:00
parent f40da09ab8
commit 487886695e
6 changed files with 491 additions and 485 deletions

View File

@@ -11,10 +11,11 @@
#include "SawyerChunkReader.h"
using namespace OpenRCT2;
SawyerChunk::SawyerChunk(SAWYER_ENCODING encoding, MemoryStream&& data)
: _data(std::move(data))
, _encoding(encoding)
namespace OpenRCT2
{
}
SawyerChunk::SawyerChunk(SAWYER_ENCODING encoding, MemoryStream&& data)
: _data(std::move(data))
, _encoding(encoding)
{
}
} // namespace OpenRCT2

View File

@@ -13,39 +13,42 @@
#include <cstdint>
/**
* The type of encoding / compression for a sawyer encoded chunk.
*/
enum class SAWYER_ENCODING : uint8_t
namespace OpenRCT2
{
NONE,
RLE,
RLECOMPRESSED,
ROTATE,
};
/**
* Represents a sawyer encoded chunk.
*/
class SawyerChunk final
{
private:
OpenRCT2::MemoryStream _data;
SAWYER_ENCODING _encoding = SAWYER_ENCODING::NONE;
public:
const void* GetData() const
/**
* The type of encoding / compression for a sawyer encoded chunk.
*/
enum class SAWYER_ENCODING : uint8_t
{
return _data.GetData();
}
size_t GetLength() const
{
return _data.GetLength();
}
SAWYER_ENCODING GetEncoding() const
{
return _encoding;
}
NONE,
RLE,
RLECOMPRESSED,
ROTATE,
};
SawyerChunk(SAWYER_ENCODING encoding, OpenRCT2::MemoryStream&& data);
};
/**
* Represents a sawyer encoded chunk.
*/
class SawyerChunk final
{
private:
OpenRCT2::MemoryStream _data;
SAWYER_ENCODING _encoding = SAWYER_ENCODING::NONE;
public:
const void* GetData() const
{
return _data.GetData();
}
size_t GetLength() const
{
return _data.GetLength();
}
SAWYER_ENCODING GetEncoding() const
{
return _encoding;
}
SawyerChunk(SAWYER_ENCODING encoding, OpenRCT2::MemoryStream&& data);
};
} // namespace OpenRCT2

View File

@@ -13,278 +13,279 @@
#include "../core/MemoryStream.h"
#include "../core/Numerics.hpp"
using namespace OpenRCT2;
// Allow chunks to be uncompressed to a maximum of 16 MiB
constexpr size_t MAX_UNCOMPRESSED_CHUNK_SIZE = 16 * 1024 * 1024;
constexpr const char* EXCEPTION_MSG_CORRUPT_CHUNK_SIZE = "Corrupt chunk size.";
constexpr const char* EXCEPTION_MSG_CORRUPT_RLE = "Corrupt RLE compression data.";
constexpr const char* EXCEPTION_MSG_DESTINATION_TOO_SMALL = "Chunk data larger than allocated destination capacity.";
constexpr const char* EXCEPTION_MSG_INVALID_CHUNK_ENCODING = "Invalid chunk encoding.";
constexpr const char* EXCEPTION_MSG_ZERO_SIZED_CHUNK = "Encountered zero-sized chunk.";
static MemoryStream DecodeChunk(const void* src, const SawyerCoding::ChunkHeader& header);
SawyerChunkReader::SawyerChunkReader(OpenRCT2::IStream* stream)
: _stream(stream)
namespace OpenRCT2
{
}
// Allow chunks to be uncompressed to a maximum of 16 MiB
constexpr size_t MAX_UNCOMPRESSED_CHUNK_SIZE = 16 * 1024 * 1024;
void SawyerChunkReader::SkipChunk()
{
uint64_t originalPosition = _stream->GetPosition();
try
constexpr const char* EXCEPTION_MSG_CORRUPT_CHUNK_SIZE = "Corrupt chunk size.";
constexpr const char* EXCEPTION_MSG_CORRUPT_RLE = "Corrupt RLE compression data.";
constexpr const char* EXCEPTION_MSG_DESTINATION_TOO_SMALL = "Chunk data larger than allocated destination capacity.";
constexpr const char* EXCEPTION_MSG_INVALID_CHUNK_ENCODING = "Invalid chunk encoding.";
constexpr const char* EXCEPTION_MSG_ZERO_SIZED_CHUNK = "Encountered zero-sized chunk.";
static MemoryStream DecodeChunk(const void* src, const SawyerCoding::ChunkHeader& header);
SawyerChunkReader::SawyerChunkReader(OpenRCT2::IStream* stream)
: _stream(stream)
{
auto header = _stream->ReadValue<SawyerCoding::ChunkHeader>();
_stream->Seek(header.length, OpenRCT2::STREAM_SEEK_CURRENT);
}
catch (const std::exception&)
{
// Rewind stream back to original position
_stream->SetPosition(originalPosition);
throw;
}
}
std::shared_ptr<SawyerChunk> SawyerChunkReader::ReadChunk()
{
uint64_t originalPosition = _stream->GetPosition();
try
void SawyerChunkReader::SkipChunk()
{
auto header = _stream->ReadValue<SawyerCoding::ChunkHeader>();
if (header.length >= MAX_UNCOMPRESSED_CHUNK_SIZE)
throw SawyerChunkException(EXCEPTION_MSG_CORRUPT_CHUNK_SIZE);
uint64_t originalPosition = _stream->GetPosition();
try
{
auto header = _stream->ReadValue<SawyerCoding::ChunkHeader>();
_stream->Seek(header.length, OpenRCT2::STREAM_SEEK_CURRENT);
}
catch (const std::exception&)
{
// Rewind stream back to original position
_stream->SetPosition(originalPosition);
throw;
}
}
std::shared_ptr<SawyerChunk> SawyerChunkReader::ReadChunk()
{
uint64_t originalPosition = _stream->GetPosition();
try
{
auto header = _stream->ReadValue<SawyerCoding::ChunkHeader>();
if (header.length >= MAX_UNCOMPRESSED_CHUNK_SIZE)
throw SawyerChunkException(EXCEPTION_MSG_CORRUPT_CHUNK_SIZE);
switch (header.encoding)
{
case CHUNK_ENCODING_NONE:
case CHUNK_ENCODING_RLE:
case CHUNK_ENCODING_RLECOMPRESSED:
case CHUNK_ENCODING_ROTATE:
{
auto compressedData = std::make_unique<uint8_t[]>(header.length);
if (_stream->TryRead(compressedData.get(), header.length) != header.length)
{
throw SawyerChunkException(EXCEPTION_MSG_CORRUPT_CHUNK_SIZE);
}
auto buffer = DecodeChunk(compressedData.get(), header);
if (buffer.GetLength() == 0)
{
throw SawyerChunkException(EXCEPTION_MSG_ZERO_SIZED_CHUNK);
}
return std::make_shared<SawyerChunk>(static_cast<SAWYER_ENCODING>(header.encoding), std::move(buffer));
}
default:
throw SawyerChunkException(EXCEPTION_MSG_INVALID_CHUNK_ENCODING);
}
}
catch (const std::exception&)
{
// Rewind stream back to original position
_stream->SetPosition(originalPosition);
throw;
}
}
std::shared_ptr<SawyerChunk> SawyerChunkReader::ReadChunkTrack()
{
uint64_t originalPosition = _stream->GetPosition();
try
{
// Remove 4 as we don't want to touch the checksum at the end of the file
int64_t compressedDataLength64 = _stream->GetLength() - _stream->GetPosition() - 4;
if (compressedDataLength64 < 0 || compressedDataLength64 > std::numeric_limits<uint32_t>::max())
{
throw SawyerChunkException(EXCEPTION_MSG_ZERO_SIZED_CHUNK);
}
uint32_t compressedDataLength = compressedDataLength64;
auto compressedData = std::make_unique<uint8_t[]>(compressedDataLength);
if (_stream->TryRead(compressedData.get(), compressedDataLength) != compressedDataLength)
{
throw SawyerChunkException(EXCEPTION_MSG_CORRUPT_CHUNK_SIZE);
}
SawyerCoding::ChunkHeader header{ CHUNK_ENCODING_RLE, compressedDataLength };
auto buffer = DecodeChunk(compressedData.get(), header);
if (buffer.GetLength() == 0)
{
throw SawyerChunkException(EXCEPTION_MSG_ZERO_SIZED_CHUNK);
}
return std::make_shared<SawyerChunk>(SAWYER_ENCODING::RLE, std::move(buffer));
}
catch (const std::exception&)
{
// Rewind stream back to original position
_stream->SetPosition(originalPosition);
throw;
}
}
void SawyerChunkReader::ReadChunk(void* dst, size_t length)
{
auto chunk = ReadChunk();
auto chunkData = static_cast<const uint8_t*>(chunk->GetData());
auto chunkLength = chunk->GetLength();
if (chunkLength > length)
{
std::memcpy(dst, chunkData, length);
}
else
{
std::memcpy(dst, chunkData, chunkLength);
auto remainingLength = length - chunkLength;
if (remainingLength > 0)
{
auto offset = static_cast<uint8_t*>(dst) + chunkLength;
std::fill_n(offset, remainingLength, 0x00);
}
}
}
static MemoryStream DecodeChunkRLE(const void* src, size_t srcLength)
{
MemoryStream buf;
auto src8 = static_cast<const uint8_t*>(src);
for (size_t i = 0; i < srcLength; i++)
{
uint8_t rleCodeByte = src8[i];
if (rleCodeByte & 128)
{
i++;
size_t count = 257 - rleCodeByte;
if (i >= srcLength)
{
throw SawyerChunkException(EXCEPTION_MSG_CORRUPT_RLE);
}
if (buf.GetLength() + count > MAX_UNCOMPRESSED_CHUNK_SIZE)
{
throw SawyerChunkException(EXCEPTION_MSG_DESTINATION_TOO_SMALL);
}
for (size_t n = 0; n < count; n++)
{
buf.Write1(src8 + i);
}
}
else
{
const auto len = rleCodeByte + 1;
if (i + 1 >= srcLength)
{
throw SawyerChunkException(EXCEPTION_MSG_CORRUPT_RLE);
}
if (buf.GetLength() + 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.Write(pos, len);
i += len;
}
}
return buf;
}
static MemoryStream DecodeChunkRepeat(const void* src, size_t srcLength)
{
MemoryStream buf;
auto src8 = static_cast<const uint8_t*>(src);
for (size_t i = 0; i < srcLength; i++)
{
if (src8[i] == 0xFF)
{
if (i + 1 >= srcLength)
{
throw SawyerChunkException(EXCEPTION_MSG_CORRUPT_RLE);
}
i++;
buf.Write1(src8 + i);
}
else
{
size_t count = (src8[i] & 7) + 1;
int32_t offset = static_cast<int32_t>(src8[i] >> 3) - 32;
const uint8_t* copySrc = static_cast<const uint8_t*>(buf.GetData()) + (buf.GetLength() + offset);
if (copySrc < static_cast<const uint8_t*>(buf.GetData())
|| copySrc + count > static_cast<const uint8_t*>(buf.GetData()) + buf.GetLength())
{
throw SawyerChunkException(EXCEPTION_MSG_CORRUPT_RLE);
}
// We need a temporary buffer as the vector might invalidate the pointer.
uint8_t temp[16];
std::memcpy(temp, copySrc, count);
buf.Write(temp, count);
}
}
return buf;
}
static MemoryStream DecodeChunkRLERepeat(const void* src, size_t srcLength)
{
auto tempBuf = DecodeChunkRLE(src, srcLength);
return DecodeChunkRepeat(tempBuf.GetData(), tempBuf.GetLength());
}
static MemoryStream DecodeChunkRotate(const void* src, size_t srcLength)
{
MemoryStream buf;
auto src8 = static_cast<const uint8_t*>(src);
uint8_t code = 1;
for (size_t i = 0; i < srcLength; i++)
{
uint8_t temp = Numerics::ror8(src8[i], code);
buf.Write1(&temp);
code = (code + 2) % 8;
}
return buf;
}
static MemoryStream DecodeChunk(const void* src, const SawyerCoding::ChunkHeader& header)
{
MemoryStream buf;
switch (header.encoding)
{
case CHUNK_ENCODING_NONE:
buf.Write(src, header.length);
break;
case CHUNK_ENCODING_RLE:
buf = DecodeChunkRLE(src, header.length);
break;
case CHUNK_ENCODING_RLECOMPRESSED:
buf = DecodeChunkRLERepeat(src, header.length);
break;
case CHUNK_ENCODING_ROTATE:
{
auto compressedData = std::make_unique<uint8_t[]>(header.length);
if (_stream->TryRead(compressedData.get(), header.length) != header.length)
{
throw SawyerChunkException(EXCEPTION_MSG_CORRUPT_CHUNK_SIZE);
}
auto buffer = DecodeChunk(compressedData.get(), header);
if (buffer.GetLength() == 0)
{
throw SawyerChunkException(EXCEPTION_MSG_ZERO_SIZED_CHUNK);
}
return std::make_shared<SawyerChunk>(static_cast<SAWYER_ENCODING>(header.encoding), std::move(buffer));
}
buf = DecodeChunkRotate(src, header.length);
break;
default:
throw SawyerChunkException(EXCEPTION_MSG_INVALID_CHUNK_ENCODING);
}
// Return the stream with the position at the beginning.
buf.SetPosition(0);
return buf;
}
catch (const std::exception&)
{
// Rewind stream back to original position
_stream->SetPosition(originalPosition);
throw;
}
}
std::shared_ptr<SawyerChunk> SawyerChunkReader::ReadChunkTrack()
{
uint64_t originalPosition = _stream->GetPosition();
try
{
// Remove 4 as we don't want to touch the checksum at the end of the file
int64_t compressedDataLength64 = _stream->GetLength() - _stream->GetPosition() - 4;
if (compressedDataLength64 < 0 || compressedDataLength64 > std::numeric_limits<uint32_t>::max())
{
throw SawyerChunkException(EXCEPTION_MSG_ZERO_SIZED_CHUNK);
}
uint32_t compressedDataLength = compressedDataLength64;
auto compressedData = std::make_unique<uint8_t[]>(compressedDataLength);
if (_stream->TryRead(compressedData.get(), compressedDataLength) != compressedDataLength)
{
throw SawyerChunkException(EXCEPTION_MSG_CORRUPT_CHUNK_SIZE);
}
SawyerCoding::ChunkHeader header{ CHUNK_ENCODING_RLE, compressedDataLength };
auto buffer = DecodeChunk(compressedData.get(), header);
if (buffer.GetLength() == 0)
{
throw SawyerChunkException(EXCEPTION_MSG_ZERO_SIZED_CHUNK);
}
return std::make_shared<SawyerChunk>(SAWYER_ENCODING::RLE, std::move(buffer));
}
catch (const std::exception&)
{
// Rewind stream back to original position
_stream->SetPosition(originalPosition);
throw;
}
}
void SawyerChunkReader::ReadChunk(void* dst, size_t length)
{
auto chunk = ReadChunk();
auto chunkData = static_cast<const uint8_t*>(chunk->GetData());
auto chunkLength = chunk->GetLength();
if (chunkLength > length)
{
std::memcpy(dst, chunkData, length);
}
else
{
std::memcpy(dst, chunkData, chunkLength);
auto remainingLength = length - chunkLength;
if (remainingLength > 0)
{
auto offset = static_cast<uint8_t*>(dst) + chunkLength;
std::fill_n(offset, remainingLength, 0x00);
}
}
}
static MemoryStream DecodeChunkRLE(const void* src, size_t srcLength)
{
MemoryStream buf;
auto src8 = static_cast<const uint8_t*>(src);
for (size_t i = 0; i < srcLength; i++)
{
uint8_t rleCodeByte = src8[i];
if (rleCodeByte & 128)
{
i++;
size_t count = 257 - rleCodeByte;
if (i >= srcLength)
{
throw SawyerChunkException(EXCEPTION_MSG_CORRUPT_RLE);
}
if (buf.GetLength() + count > MAX_UNCOMPRESSED_CHUNK_SIZE)
{
throw SawyerChunkException(EXCEPTION_MSG_DESTINATION_TOO_SMALL);
}
for (size_t n = 0; n < count; n++)
{
buf.Write1(src8 + i);
}
}
else
{
const auto len = rleCodeByte + 1;
if (i + 1 >= srcLength)
{
throw SawyerChunkException(EXCEPTION_MSG_CORRUPT_RLE);
}
if (buf.GetLength() + 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.Write(pos, len);
i += len;
}
}
return buf;
}
static MemoryStream DecodeChunkRepeat(const void* src, size_t srcLength)
{
MemoryStream buf;
auto src8 = static_cast<const uint8_t*>(src);
for (size_t i = 0; i < srcLength; i++)
{
if (src8[i] == 0xFF)
{
if (i + 1 >= srcLength)
{
throw SawyerChunkException(EXCEPTION_MSG_CORRUPT_RLE);
}
i++;
buf.Write1(src8 + i);
}
else
{
size_t count = (src8[i] & 7) + 1;
int32_t offset = static_cast<int32_t>(src8[i] >> 3) - 32;
const uint8_t* copySrc = static_cast<const uint8_t*>(buf.GetData()) + (buf.GetLength() + offset);
if (copySrc < static_cast<const uint8_t*>(buf.GetData())
|| copySrc + count > static_cast<const uint8_t*>(buf.GetData()) + buf.GetLength())
{
throw SawyerChunkException(EXCEPTION_MSG_CORRUPT_RLE);
}
// We need a temporary buffer as the vector might invalidate the pointer.
uint8_t temp[16];
std::memcpy(temp, copySrc, count);
buf.Write(temp, count);
}
}
return buf;
}
static MemoryStream DecodeChunkRLERepeat(const void* src, size_t srcLength)
{
auto tempBuf = DecodeChunkRLE(src, srcLength);
return DecodeChunkRepeat(tempBuf.GetData(), tempBuf.GetLength());
}
static MemoryStream DecodeChunkRotate(const void* src, size_t srcLength)
{
MemoryStream buf;
auto src8 = static_cast<const uint8_t*>(src);
uint8_t code = 1;
for (size_t i = 0; i < srcLength; i++)
{
uint8_t temp = Numerics::ror8(src8[i], code);
buf.Write1(&temp);
code = (code + 2) % 8;
}
return buf;
}
static MemoryStream DecodeChunk(const void* src, const SawyerCoding::ChunkHeader& header)
{
MemoryStream buf;
switch (header.encoding)
{
case CHUNK_ENCODING_NONE:
buf.Write(src, header.length);
break;
case CHUNK_ENCODING_RLE:
buf = DecodeChunkRLE(src, header.length);
break;
case CHUNK_ENCODING_RLECOMPRESSED:
buf = DecodeChunkRLERepeat(src, header.length);
break;
case CHUNK_ENCODING_ROTATE:
buf = DecodeChunkRotate(src, header.length);
break;
default:
throw SawyerChunkException(EXCEPTION_MSG_INVALID_CHUNK_ENCODING);
}
// Return the stream with the position at the beginning.
buf.SetPosition(0);
return buf;
}
} // namespace OpenRCT2

View File

@@ -17,73 +17,73 @@
#include <memory>
#include <vector>
class SawyerChunkException : public IOException
{
public:
explicit SawyerChunkException(const char* message)
: IOException(message)
{
}
explicit SawyerChunkException(const std::string& message)
: IOException(message)
{
}
};
namespace OpenRCT2
{
struct IStream;
}
/**
* Reads sawyer encoding chunks from a data stream. This can be used to read
* SC6, SV6 and RCT2 objects. persistentChunks is a hint to the reader that the chunk will be preserved,
* and thus the chunk memory should be shrunk.
*/
class SawyerChunkReader final
{
private:
OpenRCT2::IStream* const _stream = nullptr;
public:
explicit SawyerChunkReader(OpenRCT2::IStream* stream);
/**
* Skips the next chunk in the stream without decoding or reading its data
* into RAM.
*/
void SkipChunk();
/**
* Reads the next chunk from the stream.
*/
[[nodiscard]] std::shared_ptr<SawyerChunk> ReadChunk();
/**
* As above but for chunks without a header
*/
[[nodiscard]] std::shared_ptr<SawyerChunk> ReadChunkTrack();
/**
* Reads the next chunk from the stream and copies it directly to the
* destination buffer. If the chunk is larger than length, only length
* is copied. If the chunk is smaller than length, the remaining space
* is padded with zero.
* @param dst The destination buffer.
* @param length The size of the destination buffer.
*/
void ReadChunk(void* dst, size_t length);
/**
* Reads the next chunk from the stream into a buffer returned as the
* specified type. If the chunk is smaller than the size of the type
* then the remaining space is padded with zero.
*/
template<typename T>
T ReadChunkAs()
class SawyerChunkException : public IOException
{
T result;
ReadChunk(&result, sizeof(result));
return result;
}
};
public:
explicit SawyerChunkException(const char* message)
: IOException(message)
{
}
explicit SawyerChunkException(const std::string& message)
: IOException(message)
{
}
};
/**
* Reads sawyer encoding chunks from a data stream. This can be used to read
* SC6, SV6 and RCT2 objects. persistentChunks is a hint to the reader that the chunk will be preserved,
* and thus the chunk memory should be shrunk.
*/
class SawyerChunkReader final
{
private:
OpenRCT2::IStream* const _stream = nullptr;
public:
explicit SawyerChunkReader(OpenRCT2::IStream* stream);
/**
* Skips the next chunk in the stream without decoding or reading its data
* into RAM.
*/
void SkipChunk();
/**
* Reads the next chunk from the stream.
*/
[[nodiscard]] std::shared_ptr<SawyerChunk> ReadChunk();
/**
* As above but for chunks without a header
*/
[[nodiscard]] std::shared_ptr<SawyerChunk> ReadChunkTrack();
/**
* Reads the next chunk from the stream and copies it directly to the
* destination buffer. If the chunk is larger than length, only length
* is copied. If the chunk is smaller than length, the remaining space
* is padded with zero.
* @param dst The destination buffer.
* @param length The size of the destination buffer.
*/
void ReadChunk(void* dst, size_t length);
/**
* Reads the next chunk from the stream into a buffer returned as the
* specified type. If the chunk is smaller than the size of the type
* then the remaining space is padded with zero.
*/
template<typename T>
T ReadChunkAs()
{
T result;
ReadChunk(&result, sizeof(result));
return result;
}
};
} // namespace OpenRCT2

View File

@@ -13,99 +13,100 @@
#include "../core/Numerics.hpp"
#include "../core/SawyerCoding.h"
using namespace OpenRCT2;
// Maximum buffer size to store compressed data, maximum of 16 MiB
constexpr size_t MAX_COMPRESSED_CHUNK_SIZE = 16 * 1024 * 1024;
SawyerChunkWriter::SawyerChunkWriter(OpenRCT2::IStream* stream)
: _stream(stream)
namespace OpenRCT2
{
}
// Maximum buffer size to store compressed data, maximum of 16 MiB
constexpr size_t MAX_COMPRESSED_CHUNK_SIZE = 16 * 1024 * 1024;
void SawyerChunkWriter::WriteChunk(const SawyerChunk* chunk)
{
WriteChunk(chunk->GetData(), chunk->GetLength(), chunk->GetEncoding());
}
void SawyerChunkWriter::WriteChunk(const void* src, size_t length, SAWYER_ENCODING encoding)
{
SawyerCoding::ChunkHeader header;
header.encoding = static_cast<uint8_t>(encoding);
header.length = static_cast<uint32_t>(length);
auto data = std::make_unique<uint8_t[]>(MAX_COMPRESSED_CHUNK_SIZE);
size_t dataLength = SawyerCoding::WriteChunkBuffer(data.get(), static_cast<const uint8_t*>(src), header);
_stream->Write(data.get(), dataLength);
}
/**
* Ensure dst_buffer is bigger than src_buffer then resize afterwards
* returns length of dst_buffer
*/
static size_t EncodeChunkRLE(const uint8_t* src_buffer, uint8_t* dst_buffer, size_t length)
{
const uint8_t* src = src_buffer;
uint8_t* dst = dst_buffer;
const uint8_t* end_src = src + length;
uint8_t count = 0;
const uint8_t* src_norm_start = src;
while (src < end_src - 1)
SawyerChunkWriter::SawyerChunkWriter(OpenRCT2::IStream* stream)
: _stream(stream)
{
if ((count && *src == src[1]) || count > 125)
}
void SawyerChunkWriter::WriteChunk(const SawyerChunk* chunk)
{
WriteChunk(chunk->GetData(), chunk->GetLength(), chunk->GetEncoding());
}
void SawyerChunkWriter::WriteChunk(const void* src, size_t length, SAWYER_ENCODING encoding)
{
SawyerCoding::ChunkHeader header;
header.encoding = static_cast<uint8_t>(encoding);
header.length = static_cast<uint32_t>(length);
auto data = std::make_unique<uint8_t[]>(MAX_COMPRESSED_CHUNK_SIZE);
size_t dataLength = SawyerCoding::WriteChunkBuffer(data.get(), static_cast<const uint8_t*>(src), header);
_stream->Write(data.get(), dataLength);
}
/**
* Ensure dst_buffer is bigger than src_buffer then resize afterwards
* returns length of dst_buffer
*/
static size_t EncodeChunkRLE(const uint8_t* src_buffer, uint8_t* dst_buffer, size_t length)
{
const uint8_t* src = src_buffer;
uint8_t* dst = dst_buffer;
const uint8_t* end_src = src + length;
uint8_t count = 0;
const uint8_t* src_norm_start = src;
while (src < end_src - 1)
{
if ((count && *src == src[1]) || count > 125)
{
*dst++ = count - 1;
std::memcpy(dst, src_norm_start, count);
dst += count;
src_norm_start += count;
count = 0;
}
if (*src == src[1])
{
for (; (count < 125) && ((src + count) < end_src); count++)
{
if (*src != src[count])
break;
}
*dst++ = 257 - count;
*dst++ = *src;
src += count;
src_norm_start = src;
count = 0;
}
else
{
count++;
src++;
}
}
if (src == end_src - 1)
count++;
if (count)
{
*dst++ = count - 1;
std::memcpy(dst, src_norm_start, count);
dst += count;
src_norm_start += count;
count = 0;
}
if (*src == src[1])
{
for (; (count < 125) && ((src + count) < end_src); count++)
{
if (*src != src[count])
break;
}
*dst++ = 257 - count;
*dst++ = *src;
src += count;
src_norm_start = src;
count = 0;
}
else
{
count++;
src++;
}
return dst - dst_buffer;
}
if (src == end_src - 1)
count++;
if (count)
void SawyerChunkWriter::WriteChunkTrack(const void* src, size_t length)
{
*dst++ = count - 1;
std::memcpy(dst, src_norm_start, count);
dst += count;
auto data = std::make_unique<uint8_t[]>(MAX_COMPRESSED_CHUNK_SIZE);
size_t dataLength = EncodeChunkRLE(static_cast<const uint8_t*>(src), data.get(), length);
uint32_t checksum = 0;
for (size_t i = 0; i < dataLength; i++)
{
uint8_t newByte = ((checksum & 0xFF) + data[i]) & 0xFF;
checksum = (checksum & 0xFFFFFF00) + newByte;
checksum = Numerics::rol32(checksum, 3);
}
checksum -= 0x1D4C1;
_stream->Write(data.get(), dataLength);
_stream->WriteValue<uint32_t>(checksum);
}
return dst - dst_buffer;
}
void SawyerChunkWriter::WriteChunkTrack(const void* src, size_t length)
{
auto data = std::make_unique<uint8_t[]>(MAX_COMPRESSED_CHUNK_SIZE);
size_t dataLength = EncodeChunkRLE(static_cast<const uint8_t*>(src), data.get(), length);
uint32_t checksum = 0;
for (size_t i = 0; i < dataLength; i++)
{
uint8_t newByte = ((checksum & 0xFF) + data[i]) & 0xFF;
checksum = (checksum & 0xFFFFFF00) + newByte;
checksum = Numerics::rol32(checksum, 3);
}
checksum -= 0x1D4C1;
_stream->Write(data.get(), dataLength);
_stream->WriteValue<uint32_t>(checksum);
}
} // namespace OpenRCT2

View File

@@ -16,45 +16,45 @@
namespace OpenRCT2
{
struct IStream;
}
/**
* Writes sawyer encoding chunks to a data stream. This can be used to write
* SC6 and SV6 files.
*/
class SawyerChunkWriter final
{
private:
OpenRCT2::IStream* const _stream = nullptr;
public:
explicit SawyerChunkWriter(OpenRCT2::IStream* stream);
/**
* Writes a chunk to the stream.
* Writes sawyer encoding chunks to a data stream. This can be used to write
* SC6 and SV6 files.
*/
void WriteChunk(const SawyerChunk* chunk);
/**
* Writes a chunk to the stream containing the given buffer.
* @param src The source buffer.
* @param length The size of the source buffer.
*/
void WriteChunk(const void* src, size_t length, SAWYER_ENCODING encoding);
/**
* Writes a track chunk to the stream containing the given buffer.
* @param src The source buffer.
* @param length The size of the source buffer.
*/
void WriteChunkTrack(const void* src, size_t length);
/**
* Writes a chunk to the stream containing the given type.
*/
template<typename T>
void WriteChunk(const T* src, SAWYER_ENCODING encoding)
class SawyerChunkWriter final
{
WriteChunk(src, sizeof(T), encoding);
}
};
private:
OpenRCT2::IStream* const _stream = nullptr;
public:
explicit SawyerChunkWriter(OpenRCT2::IStream* stream);
/**
* Writes a chunk to the stream.
*/
void WriteChunk(const SawyerChunk* chunk);
/**
* Writes a chunk to the stream containing the given buffer.
* @param src The source buffer.
* @param length The size of the source buffer.
*/
void WriteChunk(const void* src, size_t length, SAWYER_ENCODING encoding);
/**
* Writes a track chunk to the stream containing the given buffer.
* @param src The source buffer.
* @param length The size of the source buffer.
*/
void WriteChunkTrack(const void* src, size_t length);
/**
* Writes a chunk to the stream containing the given type.
*/
template<typename T>
void WriteChunk(const T* src, SAWYER_ENCODING encoding)
{
WriteChunk(src, sizeof(T), encoding);
}
};
} // namespace OpenRCT2