From f40da09ab818ce80e5fe9fe2a202763ea43ada33 Mon Sep 17 00:00:00 2001 From: Aaron van Geffen Date: Tue, 29 Oct 2024 21:59:28 +0100 Subject: [PATCH] Move SawyerCoding from util into core, and into its own namespace --- src/openrct2/FileClassifier.cpp | 8 +- src/openrct2/Game.cpp | 2 +- src/openrct2/core/SawyerCoding.cpp | 428 +++++++++++++++++++++++ src/openrct2/core/SawyerCoding.h | 63 ++++ src/openrct2/libopenrct2.vcxproj | 10 +- src/openrct2/network/NetworkBase.cpp | 2 +- src/openrct2/object/ObjectList.cpp | 2 +- src/openrct2/object/ObjectRepository.cpp | 6 +- src/openrct2/platform/Crash.cpp | 2 +- src/openrct2/rct1/S4Importer.cpp | 10 +- src/openrct2/rct12/SawyerChunkReader.cpp | 10 +- src/openrct2/rct12/SawyerChunkReader.h | 2 +- src/openrct2/rct12/SawyerChunkWriter.cpp | 6 +- src/openrct2/rct2/S6Importer.cpp | 2 +- src/openrct2/ride/Track.cpp | 2 +- src/openrct2/ride/TrackDesign.cpp | 2 +- src/openrct2/ride/TrackDesignSave.cpp | 2 +- src/openrct2/scenario/Scenario.cpp | 2 +- src/openrct2/util/SawyerCoding.cpp | 428 ----------------------- src/openrct2/util/SawyerCoding.h | 55 --- test/tests/SawyerCodingTest.cpp | 14 +- 21 files changed, 534 insertions(+), 524 deletions(-) create mode 100644 src/openrct2/core/SawyerCoding.cpp create mode 100644 src/openrct2/core/SawyerCoding.h delete mode 100644 src/openrct2/util/SawyerCoding.cpp delete mode 100644 src/openrct2/util/SawyerCoding.h diff --git a/src/openrct2/FileClassifier.cpp b/src/openrct2/FileClassifier.cpp index 788e6ea97a..e91a7c37d4 100644 --- a/src/openrct2/FileClassifier.cpp +++ b/src/openrct2/FileClassifier.cpp @@ -14,12 +14,12 @@ #include "core/FileStream.h" #include "core/Memory.hpp" #include "core/Path.hpp" +#include "core/SawyerCoding.h" #include "core/String.hpp" #include "park/ParkFile.h" #include "rct12/SawyerChunkReader.h" #include "rct2/RCT2.h" #include "scenario/Scenario.h" -#include "util/SawyerCoding.h" using namespace OpenRCT2; @@ -135,7 +135,7 @@ static bool TryClassifyAsS4(OpenRCT2::IStream* stream, ClassifiedFileInfo* resul size_t dataLength = static_cast(stream->GetLength()); auto data = stream->ReadArray(dataLength); stream->SetPosition(originalPosition); - int32_t fileTypeVersion = SawyerCodingDetectFileType(data.get(), dataLength); + int32_t fileTypeVersion = SawyerCoding::DetectFileType(data.get(), dataLength); int32_t type = fileTypeVersion & FILE_TYPE_MASK; int32_t version = fileTypeVersion & FILE_VERSION_MASK; @@ -173,11 +173,11 @@ static bool TryClassifyAsTD4_TD6(OpenRCT2::IStream* stream, ClassifiedFileInfo* auto data = stream->ReadArray(dataLength); stream->SetPosition(originalPosition); - if (SawyerCodingValidateTrackChecksum(data.get(), dataLength)) + if (SawyerCoding::ValidateTrackChecksum(data.get(), dataLength)) { std::unique_ptr)> td6data( Memory::Allocate(0x10000), &Memory::Free); - size_t td6len = SawyerCodingDecodeTD6(data.get(), td6data.get(), dataLength); + size_t td6len = SawyerCoding::DecodeTD6(data.get(), td6data.get(), dataLength); if (td6data != nullptr && td6len >= 8) { uint8_t version = (td6data.get()[7] >> 2) & 3; diff --git a/src/openrct2/Game.cpp b/src/openrct2/Game.cpp index b4de059728..8940bfca52 100644 --- a/src/openrct2/Game.cpp +++ b/src/openrct2/Game.cpp @@ -30,6 +30,7 @@ #include "core/FileScanner.h" #include "core/Money.hpp" #include "core/Path.hpp" +#include "core/SawyerCoding.h" #include "entity/EntityRegistry.h" #include "entity/PatrolArea.h" #include "entity/Peep.h" @@ -59,7 +60,6 @@ #include "scripting/ScriptEngine.h" #include "ui/UiContext.h" #include "ui/WindowManager.h" -#include "util/SawyerCoding.h" #include "util/Util.h" #include "windows/Intent.h" #include "world/Banner.h" diff --git a/src/openrct2/core/SawyerCoding.cpp b/src/openrct2/core/SawyerCoding.cpp new file mode 100644 index 0000000000..7d735c7bd3 --- /dev/null +++ b/src/openrct2/core/SawyerCoding.cpp @@ -0,0 +1,428 @@ +/***************************************************************************** + * Copyright (c) 2014-2024 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. + *****************************************************************************/ + +#include "SawyerCoding.h" + +#include "../platform/Platform.h" +#include "../scenario/Scenario.h" +#include "Numerics.hpp" + +#include +#include +#include + +namespace OpenRCT2::SawyerCoding +{ + static size_t DecodeChunkRLE(const uint8_t* src_buffer, uint8_t* dst_buffer, size_t length); + static size_t DecodeChunkRLEWithSize(const uint8_t* src_buffer, uint8_t* dst_buffer, size_t length, size_t dstSize); + + static size_t EncodeChunkRLE(const uint8_t* src_buffer, uint8_t* dst_buffer, size_t length); + static size_t EncodeChunkRepeat(const uint8_t* src_buffer, uint8_t* dst_buffer, size_t length); + static void EncodeChunkRotate(uint8_t* buffer, size_t length); + + uint32_t CalculateChecksum(const uint8_t* buffer, size_t length) + { + uint32_t checksum = 0; + for (size_t i = 0; i < length; i++) + checksum += buffer[i]; + + return checksum; + } + + /** + * + * rct2: 0x006762E1 + * + */ + size_t WriteChunkBuffer(uint8_t* dst_file, const uint8_t* buffer, ChunkHeader chunkHeader) + { + switch (chunkHeader.encoding) + { + case CHUNK_ENCODING_NONE: + std::memcpy(dst_file, &chunkHeader, sizeof(ChunkHeader)); + dst_file += sizeof(ChunkHeader); + std::memcpy(dst_file, buffer, chunkHeader.length); + // fwrite(&chunkHeader, sizeof(ChunkHeader), 1, file); + // fwrite(buffer, 1, chunkHeader.length, file); + break; + case CHUNK_ENCODING_RLE: + { + auto encode_buffer = std::make_unique(0x600000); + chunkHeader.length = static_cast(EncodeChunkRLE(buffer, encode_buffer.get(), chunkHeader.length)); + std::memcpy(dst_file, &chunkHeader, sizeof(ChunkHeader)); + dst_file += sizeof(ChunkHeader); + std::memcpy(dst_file, encode_buffer.get(), chunkHeader.length); + } + break; + case CHUNK_ENCODING_RLECOMPRESSED: + { + auto encode_buffer = std::make_unique(chunkHeader.length * 2); + auto encode_buffer2 = std::make_unique(0x600000); + chunkHeader.length = static_cast(EncodeChunkRepeat(buffer, encode_buffer.get(), chunkHeader.length)); + chunkHeader.length = static_cast( + EncodeChunkRLE(encode_buffer.get(), encode_buffer2.get(), chunkHeader.length)); + std::memcpy(dst_file, &chunkHeader, sizeof(ChunkHeader)); + dst_file += sizeof(ChunkHeader); + std::memcpy(dst_file, encode_buffer2.get(), chunkHeader.length); + } + break; + case CHUNK_ENCODING_ROTATE: + { + auto encode_buffer = std::make_unique(chunkHeader.length); + std::memcpy(encode_buffer.get(), buffer, chunkHeader.length); + EncodeChunkRotate(encode_buffer.get(), chunkHeader.length); + std::memcpy(dst_file, &chunkHeader, sizeof(ChunkHeader)); + dst_file += sizeof(ChunkHeader); + std::memcpy(dst_file, encode_buffer.get(), chunkHeader.length); + } + break; + } + + return chunkHeader.length + sizeof(ChunkHeader); + } + + size_t DecodeSV4(const uint8_t* src, uint8_t* dst, size_t length, size_t bufferLength) + { + // (0 to length - 4): RLE chunk + // (length - 4 to length): checksum + return DecodeChunkRLEWithSize(src, dst, length - 4, bufferLength); + } + + size_t DecodeSC4(const uint8_t* src, uint8_t* dst, size_t length, size_t bufferLength) + { + // Uncompress + size_t decodedLength = DecodeChunkRLEWithSize(src, dst, length - 4, bufferLength); + + // Decode + for (size_t i = 0x60018; i <= std::min(decodedLength - 1, static_cast(0x1F8353)); i++) + dst[i] = dst[i] ^ 0x9C; + + for (size_t i = 0x60018; i <= std::min(decodedLength - 1, static_cast(0x1F8350)); i += 4) + { + dst[i + 1] = Numerics::ror8(dst[i + 1], 3); + + uint32_t* code = reinterpret_cast(&dst[i]); + *code = Numerics::rol32(*code, 9); + } + + return decodedLength; + } + + size_t EncodeSV4(const uint8_t* src, uint8_t* dst, size_t length) + { + // Encode + size_t encodedLength = EncodeChunkRLE(src, dst, length); + + // Append checksum + uint32_t checksum = CalculateChecksum(dst, encodedLength); + *(reinterpret_cast(&dst[encodedLength])) = checksum; + + return encodedLength + 4; + } + + size_t DecodeTD6(const uint8_t* src, uint8_t* dst, size_t length) + { + return DecodeChunkRLE(src, dst, length - 4); + } + + size_t EncodeTD6(const uint8_t* src, uint8_t* dst, size_t length) + { + size_t output_length = EncodeChunkRLE(src, dst, length); + + uint32_t checksum = 0; + for (size_t i = 0; i < output_length; i++) + { + uint8_t new_byte = ((checksum & 0xFF) + dst[i]) & 0xFF; + checksum = (checksum & 0xFFFFFF00) + new_byte; + checksum = Numerics::rol32(checksum, 3); + } + checksum -= 0x1D4C1; + + *(reinterpret_cast(&dst[output_length])) = checksum; + output_length += 4; + return output_length; + } + + /* Based off of rct2: 0x006770C1 */ + int32_t ValidateTrackChecksum(const uint8_t* src, size_t length) + { + if (length < 4) + return 0; + + uint32_t file_checksum = *(reinterpret_cast(&src[length - 4])); + + uint32_t checksum = 0; + for (size_t i = 0; i < length - 4; i++) + { + uint8_t new_byte = ((checksum & 0xFF) + src[i]) & 0xFF; + checksum = (checksum & 0xFFFFFF00) + new_byte; + checksum = Numerics::rol32(checksum, 3); + } + + if (checksum - 0x1D4C1 == file_checksum) + return 1; // .TD6 + else if (checksum - 0x1A67C == file_checksum) + return 1; // .TD4 + else if (checksum - 0x1A650 == file_checksum) + return 1; // .TD4 + else + return 0; + } + +#pragma region Decoding + + /** + * + * rct2: 0x0067693A + */ + static size_t DecodeChunkRLE(const uint8_t* src_buffer, uint8_t* dst_buffer, size_t length) + { + size_t count; + uint8_t *dst, rleCodeByte; + + dst = dst_buffer; + + for (size_t i = 0; i < length; i++) + { + rleCodeByte = src_buffer[i]; + if (rleCodeByte & 128) + { + i++; + count = 257 - rleCodeByte; + std::fill_n(dst, count, src_buffer[i]); + dst = reinterpret_cast(reinterpret_cast(dst) + count); + } + else + { + std::memcpy(dst, src_buffer + i + 1, rleCodeByte + 1); + dst = reinterpret_cast(reinterpret_cast(dst) + rleCodeByte + 1); + i += rleCodeByte + 1; + } + } + + // Return final size + return dst - dst_buffer; + } + + /** + * + * rct2: 0x0067693A + */ + static size_t DecodeChunkRLEWithSize(const uint8_t* src_buffer, uint8_t* dst_buffer, size_t length, size_t dstSize) + { + size_t count; + uint8_t *dst, rleCodeByte; + + dst = dst_buffer; + + if (length <= 0 || dstSize <= 0) + throw std::out_of_range("Invalid RLE string!"); + + for (size_t i = 0; i < length; i++) + { + rleCodeByte = src_buffer[i]; + if (rleCodeByte & 128) + { + i++; + count = 257 - rleCodeByte; + assert(dst + count <= dst_buffer + dstSize); + assert(i < length); + std::fill_n(dst, count, src_buffer[i]); + dst = reinterpret_cast(reinterpret_cast(dst) + count); + } + else + { + if ((dst + rleCodeByte + 1 > dst_buffer + dstSize) || (i + 1 >= length)) + throw std::out_of_range("Invalid RLE string!"); + std::memcpy(dst, src_buffer + i + 1, rleCodeByte + 1); + dst = reinterpret_cast(reinterpret_cast(dst) + rleCodeByte + 1); + i += rleCodeByte + 1; + } + } + + // Return final size + return dst - dst_buffer; + } + +#pragma endregion + +#pragma region Encoding + + /** + * 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; + } + return dst - dst_buffer; + } + + static size_t EncodeChunkRepeat(const uint8_t* src_buffer, uint8_t* dst_buffer, size_t length) + { + if (length == 0) + return 0; + + size_t outLength = 0; + + // Need to emit at least one byte, otherwise there is nothing to repeat + *dst_buffer++ = 255; + *dst_buffer++ = src_buffer[0]; + outLength += 2; + + // Iterate through remainder of the source buffer + for (size_t i = 1; i < length;) + { + size_t searchIndex = (i < 32) ? 0 : (i - 32); + size_t searchEnd = i - 1; + + size_t bestRepeatIndex = 0; + size_t bestRepeatCount = 0; + for (size_t repeatIndex = searchIndex; repeatIndex <= searchEnd; repeatIndex++) + { + size_t repeatCount = 0; + size_t maxRepeatCount = std::min(std::min(static_cast(7), searchEnd - repeatIndex), length - i - 1); + // maxRepeatCount should not exceed length + assert(repeatIndex + maxRepeatCount < length); + assert(i + maxRepeatCount < length); + for (size_t j = 0; j <= maxRepeatCount; j++) + { + if (src_buffer[repeatIndex + j] == src_buffer[i + j]) + { + repeatCount++; + } + else + { + break; + } + } + if (repeatCount > bestRepeatCount) + { + bestRepeatIndex = repeatIndex; + bestRepeatCount = repeatCount; + + // Maximum repeat count is 8 + if (repeatCount == 8) + break; + } + } + + if (bestRepeatCount == 0) + { + *dst_buffer++ = 255; + *dst_buffer++ = src_buffer[i]; + outLength += 2; + i++; + } + else + { + *dst_buffer++ = static_cast((bestRepeatCount - 1) | ((32 - (i - bestRepeatIndex)) << 3)); + outLength++; + i += bestRepeatCount; + } + } + + return outLength; + } + + static void EncodeChunkRotate(uint8_t* buffer, size_t length) + { + size_t i; + uint8_t code = 1; + for (i = 0; i < length; i++) + { + buffer[i] = Numerics::rol8(buffer[i], code); + code = (code + 2) % 8; + } + } + +#pragma endregion + + int32_t DetectFileType(const uint8_t* src, size_t length) + { + if (length < 4) + { + throw std::length_error("Stream is (nearly) empty!"); + } + + size_t i; + + // Currently can't detect TD4, as the checksum is the same as SC4 (need alternative method) + + uint32_t checksum = *(reinterpret_cast(&src[length - 4])); + uint32_t actualChecksum = 0; + for (i = 0; i < length - 4; i++) + { + actualChecksum = (actualChecksum & 0xFFFFFF00) | (((actualChecksum & 0xFF) + static_cast(src[i])) & 0xFF); + actualChecksum = Numerics::rol32(actualChecksum, 3); + } + + return DetectRCT1Version(checksum - actualChecksum); + } + + int32_t DetectRCT1Version(int32_t gameVersion) + { + int32_t fileType = (gameVersion) > 0 ? FILE_TYPE_SV4 : FILE_TYPE_SC4; + gameVersion = abs(gameVersion); + + if (gameVersion >= 108000 && gameVersion < 110000) + return (FILE_VERSION_RCT1 | fileType); + if (gameVersion >= 110000 && gameVersion < 120000) + return (FILE_VERSION_RCT1_AA | fileType); + if (gameVersion >= 120000 && gameVersion < 130000) + return (FILE_VERSION_RCT1_LL | fileType); + // RCTOA Acres sets this, and possibly some other user-created scenarios as well + if (gameVersion == 0) + return (FILE_VERSION_RCT1_LL | fileType); + + return -1; + } +} // namespace OpenRCT2::SawyerCoding diff --git a/src/openrct2/core/SawyerCoding.h b/src/openrct2/core/SawyerCoding.h new file mode 100644 index 0000000000..16f357eb46 --- /dev/null +++ b/src/openrct2/core/SawyerCoding.h @@ -0,0 +1,63 @@ +/***************************************************************************** + * Copyright (c) 2014-2024 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 +#include + +namespace OpenRCT2 +{ + // TODO: make enum class in SawyerCoding namespace + enum + { + CHUNK_ENCODING_NONE, + CHUNK_ENCODING_RLE, + CHUNK_ENCODING_RLECOMPRESSED, + CHUNK_ENCODING_ROTATE + }; + + // TODO: make enum class in SawyerCoding namespace + enum + { + FILE_VERSION_MASK = (3 << 0), + FILE_VERSION_RCT1 = (0 << 0), + FILE_VERSION_RCT1_AA = (1 << 0), + FILE_VERSION_RCT1_LL = (2 << 0), + + FILE_TYPE_MASK = (3 << 2), + FILE_TYPE_TD4 = (0 << 2), + FILE_TYPE_SV4 = (1 << 2), + FILE_TYPE_SC4 = (2 << 2) + }; +} // namespace OpenRCT2 + +namespace OpenRCT2::SawyerCoding +{ +#pragma pack(push, 1) + struct ChunkHeader + { + uint8_t encoding; + uint32_t length; + }; + static_assert(sizeof(ChunkHeader) == 5); +#pragma pack(pop) + + uint32_t CalculateChecksum(const uint8_t* buffer, size_t length); + size_t WriteChunkBuffer(uint8_t* dst_file, const uint8_t* src_buffer, ChunkHeader chunkHeader); + size_t DecodeSV4(const uint8_t* src, uint8_t* dst, size_t length, size_t bufferLength); + size_t DecodeSC4(const uint8_t* src, uint8_t* dst, size_t length, size_t bufferLength); + size_t EncodeSV4(const uint8_t* src, uint8_t* dst, size_t length); + size_t DecodeTD6(const uint8_t* src, uint8_t* dst, size_t length); + size_t EncodeTD6(const uint8_t* src, uint8_t* dst, size_t length); + int32_t ValidateTrackChecksum(const uint8_t* src, size_t length); + + int32_t DetectFileType(const uint8_t* src, size_t length); + int32_t DetectRCT1Version(int32_t gameVersion); +} // namespace OpenRCT2::SawyerCoding diff --git a/src/openrct2/libopenrct2.vcxproj b/src/openrct2/libopenrct2.vcxproj index 95e2371cd2..dcfd4acf7c 100644 --- a/src/openrct2/libopenrct2.vcxproj +++ b/src/openrct2/libopenrct2.vcxproj @@ -203,6 +203,8 @@ + + @@ -222,8 +224,7 @@ - - + @@ -595,8 +596,7 @@ - - + @@ -768,6 +768,7 @@ + @@ -1091,7 +1092,6 @@ - diff --git a/src/openrct2/network/NetworkBase.cpp b/src/openrct2/network/NetworkBase.cpp index d4cb290212..8dc1032cee 100644 --- a/src/openrct2/network/NetworkBase.cpp +++ b/src/openrct2/network/NetworkBase.cpp @@ -23,6 +23,7 @@ #include "../core/File.h" #include "../core/Guard.hpp" #include "../core/Json.hpp" +#include "../core/SawyerCoding.h" #include "../entity/EntityList.h" #include "../entity/EntityRegistry.h" #include "../entity/EntityTweener.h" @@ -35,7 +36,6 @@ #include "../scripting/ScriptEngine.h" #include "../ui/UiContext.h" #include "../ui/WindowManager.h" -#include "../util/SawyerCoding.h" #include "../world/Location.hpp" #include "network.h" diff --git a/src/openrct2/object/ObjectList.cpp b/src/openrct2/object/ObjectList.cpp index 59d50d887b..27ebb3c8d9 100644 --- a/src/openrct2/object/ObjectList.cpp +++ b/src/openrct2/object/ObjectList.cpp @@ -11,8 +11,8 @@ #include "../Context.h" #include "../Game.h" +#include "../core/SawyerCoding.h" #include "../object/Object.h" -#include "../util/SawyerCoding.h" #include "../util/Util.h" #include "ObjectLimits.h" #include "ObjectManager.h" diff --git a/src/openrct2/object/ObjectRepository.cpp b/src/openrct2/object/ObjectRepository.cpp index 5be0e5eae8..ee90794869 100644 --- a/src/openrct2/object/ObjectRepository.cpp +++ b/src/openrct2/object/ObjectRepository.cpp @@ -23,6 +23,7 @@ #include "../core/MemoryStream.h" #include "../core/Numerics.hpp" #include "../core/Path.hpp" +#include "../core/SawyerCoding.h" #include "../core/String.hpp" #include "../localisation/LocalisationService.h" #include "../object/Object.h" @@ -31,7 +32,6 @@ #include "../rct12/SawyerChunkReader.h" #include "../rct12/SawyerChunkWriter.h" #include "../scenario/ScenarioRepository.h" -#include "../util/SawyerCoding.h" #include "../util/Util.h" #include "Object.h" #include "ObjectFactory.h" @@ -528,11 +528,11 @@ private: // Encode data ObjectType objectType = entry->GetType(); - SawyerCodingChunkHeader chunkHeader; + SawyerCoding::ChunkHeader chunkHeader; chunkHeader.encoding = kLegacyObjectEntryGroupEncoding[EnumValue(objectType)]; chunkHeader.length = static_cast(dataSize); uint8_t* encodedDataBuffer = Memory::Allocate(0x600000); - size_t encodedDataSize = SawyerCodingWriteChunkBuffer( + size_t encodedDataSize = SawyerCoding::WriteChunkBuffer( encodedDataBuffer, reinterpret_cast(data), chunkHeader); // Save to file diff --git a/src/openrct2/platform/Crash.cpp b/src/openrct2/platform/Crash.cpp index b999eeb3de..69734b4377 100644 --- a/src/openrct2/platform/Crash.cpp +++ b/src/openrct2/platform/Crash.cpp @@ -35,6 +35,7 @@ # include "../core/Console.hpp" # include "../core/Guard.hpp" # include "../core/Path.hpp" +# include "../core/SawyerCoding.h" # include "../core/String.hpp" # include "../drawing/IDrawingEngine.h" # include "../interface/Screenshot.h" @@ -42,7 +43,6 @@ # include "../object/ObjectManager.h" # include "../park/ParkFile.h" # include "../scenario/Scenario.h" -# include "../util/SawyerCoding.h" # include "Platform.h" # define WSZ(x) L"" x diff --git a/src/openrct2/rct1/S4Importer.cpp b/src/openrct2/rct1/S4Importer.cpp index f9f6362ac2..548241ceb6 100644 --- a/src/openrct2/rct1/S4Importer.cpp +++ b/src/openrct2/rct1/S4Importer.cpp @@ -24,6 +24,7 @@ #include "../core/IStream.hpp" #include "../core/Memory.hpp" #include "../core/Path.hpp" +#include "../core/SawyerCoding.h" #include "../core/String.hpp" #include "../entity/Balloon.h" #include "../entity/Duck.h" @@ -61,7 +62,6 @@ #include "../scenario/Scenario.h" #include "../scenario/ScenarioRepository.h" #include "../scenario/ScenarioSources.h" -#include "../util/SawyerCoding.h" #include "../util/Util.h" #include "../world/Climate.h" #include "../world/Entrance.h" @@ -175,7 +175,7 @@ namespace OpenRCT2::RCT1 _s4 = *ReadAndDecodeS4(stream, isScenario); _s4Path = path; _isScenario = isScenario; - _gameVersion = SawyerCodingDetectRCT1Version(_s4.GameVersion) & FILE_VERSION_MASK; + _gameVersion = SawyerCoding::DetectRCT1Version(_s4.GameVersion) & FILE_VERSION_MASK; // Only determine what objects we required to import this saved game InitialiseEntryMaps(); @@ -309,14 +309,14 @@ namespace OpenRCT2::RCT1 auto decodedData = std::make_unique(sizeof(S4)); size_t decodedSize; - int32_t fileType = SawyerCodingDetectFileType(data.get(), dataSize); + int32_t fileType = SawyerCoding::DetectFileType(data.get(), dataSize); if (isScenario && (fileType & FILE_VERSION_MASK) != FILE_VERSION_RCT1) { - decodedSize = SawyerCodingDecodeSC4(data.get(), decodedData.get(), dataSize, sizeof(S4)); + decodedSize = SawyerCoding::DecodeSC4(data.get(), decodedData.get(), dataSize, sizeof(S4)); } else { - decodedSize = SawyerCodingDecodeSV4(data.get(), decodedData.get(), dataSize, sizeof(S4)); + decodedSize = SawyerCoding::DecodeSV4(data.get(), decodedData.get(), dataSize, sizeof(S4)); } if (decodedSize == sizeof(S4)) diff --git a/src/openrct2/rct12/SawyerChunkReader.cpp b/src/openrct2/rct12/SawyerChunkReader.cpp index 1e93b99298..6af96998f7 100644 --- a/src/openrct2/rct12/SawyerChunkReader.cpp +++ b/src/openrct2/rct12/SawyerChunkReader.cpp @@ -24,7 +24,7 @@ constexpr const char* EXCEPTION_MSG_DESTINATION_TOO_SMALL = "Chunk data larger t 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 SawyerCodingChunkHeader& header); +static MemoryStream DecodeChunk(const void* src, const SawyerCoding::ChunkHeader& header); SawyerChunkReader::SawyerChunkReader(OpenRCT2::IStream* stream) : _stream(stream) @@ -36,7 +36,7 @@ void SawyerChunkReader::SkipChunk() uint64_t originalPosition = _stream->GetPosition(); try { - auto header = _stream->ReadValue(); + auto header = _stream->ReadValue(); _stream->Seek(header.length, OpenRCT2::STREAM_SEEK_CURRENT); } catch (const std::exception&) @@ -52,7 +52,7 @@ std::shared_ptr SawyerChunkReader::ReadChunk() uint64_t originalPosition = _stream->GetPosition(); try { - auto header = _stream->ReadValue(); + auto header = _stream->ReadValue(); if (header.length >= MAX_UNCOMPRESSED_CHUNK_SIZE) throw SawyerChunkException(EXCEPTION_MSG_CORRUPT_CHUNK_SIZE); @@ -108,7 +108,7 @@ std::shared_ptr SawyerChunkReader::ReadChunkTrack() throw SawyerChunkException(EXCEPTION_MSG_CORRUPT_CHUNK_SIZE); } - SawyerCodingChunkHeader header{ CHUNK_ENCODING_RLE, compressedDataLength }; + SawyerCoding::ChunkHeader header{ CHUNK_ENCODING_RLE, compressedDataLength }; auto buffer = DecodeChunk(compressedData.get(), header); if (buffer.GetLength() == 0) { @@ -261,7 +261,7 @@ static MemoryStream DecodeChunkRotate(const void* src, size_t srcLength) return buf; } -static MemoryStream DecodeChunk(const void* src, const SawyerCodingChunkHeader& header) +static MemoryStream DecodeChunk(const void* src, const SawyerCoding::ChunkHeader& header) { MemoryStream buf; diff --git a/src/openrct2/rct12/SawyerChunkReader.h b/src/openrct2/rct12/SawyerChunkReader.h index 8ca5543e0c..2ebf43dbe7 100644 --- a/src/openrct2/rct12/SawyerChunkReader.h +++ b/src/openrct2/rct12/SawyerChunkReader.h @@ -10,7 +10,7 @@ #pragma once #include "../core/IStream.hpp" -#include "../util/SawyerCoding.h" +#include "../core/SawyerCoding.h" #include "SawyerChunk.h" #include diff --git a/src/openrct2/rct12/SawyerChunkWriter.cpp b/src/openrct2/rct12/SawyerChunkWriter.cpp index 765319666a..96bcc90f04 100644 --- a/src/openrct2/rct12/SawyerChunkWriter.cpp +++ b/src/openrct2/rct12/SawyerChunkWriter.cpp @@ -11,7 +11,7 @@ #include "../core/IStream.hpp" #include "../core/Numerics.hpp" -#include "../util/SawyerCoding.h" +#include "../core/SawyerCoding.h" using namespace OpenRCT2; @@ -30,12 +30,12 @@ void SawyerChunkWriter::WriteChunk(const SawyerChunk* chunk) void SawyerChunkWriter::WriteChunk(const void* src, size_t length, SAWYER_ENCODING encoding) { - SawyerCodingChunkHeader header; + SawyerCoding::ChunkHeader header; header.encoding = static_cast(encoding); header.length = static_cast(length); auto data = std::make_unique(MAX_COMPRESSED_CHUNK_SIZE); - size_t dataLength = SawyerCodingWriteChunkBuffer(data.get(), static_cast(src), header); + size_t dataLength = SawyerCoding::WriteChunkBuffer(data.get(), static_cast(src), header); _stream->Write(data.get(), dataLength); } diff --git a/src/openrct2/rct2/S6Importer.cpp b/src/openrct2/rct2/S6Importer.cpp index fc05b5568a..3ba6acb201 100644 --- a/src/openrct2/rct2/S6Importer.cpp +++ b/src/openrct2/rct2/S6Importer.cpp @@ -21,6 +21,7 @@ #include "../core/Numerics.hpp" #include "../core/Path.hpp" #include "../core/Random.hpp" +#include "../core/SawyerCoding.h" #include "../core/String.hpp" #include "../entity/Balloon.h" #include "../entity/Duck.h" @@ -68,7 +69,6 @@ #include "../scenario/Scenario.h" #include "../scenario/ScenarioRepository.h" #include "../scenario/ScenarioSources.h" -#include "../util/SawyerCoding.h" #include "../util/Util.h" #include "../world/Climate.h" #include "../world/Entrance.h" diff --git a/src/openrct2/ride/Track.cpp b/src/openrct2/ride/Track.cpp index c432f8ca85..78fd353d65 100644 --- a/src/openrct2/ride/Track.cpp +++ b/src/openrct2/ride/Track.cpp @@ -15,13 +15,13 @@ #include "../GameState.h" #include "../audio/audio.h" #include "../config/Config.h" +#include "../core/SawyerCoding.h" #include "../interface/Viewport.h" #include "../management/Finance.h" #include "../network/network.h" #include "../platform/Platform.h" #include "../rct1/RCT1.h" #include "../ride/RideColour.h" -#include "../util/SawyerCoding.h" #include "../world/Footpath.h" #include "../world/Map.h" #include "../world/MapAnimation.h" diff --git a/src/openrct2/ride/TrackDesign.cpp b/src/openrct2/ride/TrackDesign.cpp index 118283752a..817a86ec89 100644 --- a/src/openrct2/ride/TrackDesign.cpp +++ b/src/openrct2/ride/TrackDesign.cpp @@ -34,6 +34,7 @@ #include "../core/DataSerialiser.h" #include "../core/File.h" #include "../core/Numerics.hpp" +#include "../core/SawyerCoding.h" #include "../core/String.hpp" #include "../core/UnitConversion.h" #include "../drawing/X8DrawingEngine.h" @@ -52,7 +53,6 @@ #include "../object/StationObject.h" #include "../rct2/RCT2.h" #include "../ride/RideConstruction.h" -#include "../util/SawyerCoding.h" #include "../util/Util.h" #include "../world/Footpath.h" #include "../world/Park.h" diff --git a/src/openrct2/ride/TrackDesignSave.cpp b/src/openrct2/ride/TrackDesignSave.cpp index 11ea57859a..cef4e8582c 100644 --- a/src/openrct2/ride/TrackDesignSave.cpp +++ b/src/openrct2/ride/TrackDesignSave.cpp @@ -11,6 +11,7 @@ #include "../Diagnostic.h" #include "../Game.h" #include "../audio/audio.h" +#include "../core/SawyerCoding.h" #include "../interface/Viewport.h" #include "../localisation/StringIds.h" #include "../object/FootpathObject.h" @@ -20,7 +21,6 @@ #include "../object/ObjectList.h" #include "../object/ObjectManager.h" #include "../rct2/RCT2.h" -#include "../util/SawyerCoding.h" #include "../windows/Intent.h" #include "../world/Footpath.h" #include "../world/Scenery.h" diff --git a/src/openrct2/scenario/Scenario.cpp b/src/openrct2/scenario/Scenario.cpp index c99959915f..6b6382c9bc 100644 --- a/src/openrct2/scenario/Scenario.cpp +++ b/src/openrct2/scenario/Scenario.cpp @@ -24,6 +24,7 @@ #include "../core/Guard.hpp" #include "../core/Path.hpp" #include "../core/Random.hpp" +#include "../core/SawyerCoding.h" #include "../core/UnitConversion.h" #include "../entity/Duck.h" #include "../entity/Guest.h" @@ -47,7 +48,6 @@ #include "../rct12/RCT12.h" #include "../ride/Ride.h" #include "../ride/Track.h" -#include "../util/SawyerCoding.h" #include "../util/Util.h" #include "../windows/Intent.h" #include "../world/Climate.h" diff --git a/src/openrct2/util/SawyerCoding.cpp b/src/openrct2/util/SawyerCoding.cpp deleted file mode 100644 index 76de8d4b0c..0000000000 --- a/src/openrct2/util/SawyerCoding.cpp +++ /dev/null @@ -1,428 +0,0 @@ -/***************************************************************************** - * Copyright (c) 2014-2024 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. - *****************************************************************************/ - -#include "SawyerCoding.h" - -#include "../core/Numerics.hpp" -#include "../platform/Platform.h" -#include "../scenario/Scenario.h" -#include "Util.h" - -#include -#include -#include - -using namespace OpenRCT2; - -static size_t DecodeChunkRLE(const uint8_t* src_buffer, uint8_t* dst_buffer, size_t length); -static size_t DecodeChunkRLEWithSize(const uint8_t* src_buffer, uint8_t* dst_buffer, size_t length, size_t dstSize); - -static size_t EncodeChunkRLE(const uint8_t* src_buffer, uint8_t* dst_buffer, size_t length); -static size_t EncodeChunkRepeat(const uint8_t* src_buffer, uint8_t* dst_buffer, size_t length); -static void EncodeChunkRotate(uint8_t* buffer, size_t length); - -uint32_t SawyerCodingCalculateChecksum(const uint8_t* buffer, size_t length) -{ - uint32_t checksum = 0; - for (size_t i = 0; i < length; i++) - checksum += buffer[i]; - - return checksum; -} - -/** - * - * rct2: 0x006762E1 - * - */ -size_t SawyerCodingWriteChunkBuffer(uint8_t* dst_file, const uint8_t* buffer, SawyerCodingChunkHeader chunkHeader) -{ - switch (chunkHeader.encoding) - { - case CHUNK_ENCODING_NONE: - std::memcpy(dst_file, &chunkHeader, sizeof(SawyerCodingChunkHeader)); - dst_file += sizeof(SawyerCodingChunkHeader); - std::memcpy(dst_file, buffer, chunkHeader.length); - // fwrite(&chunkHeader, sizeof(SawyerCodingChunkHeader), 1, file); - // fwrite(buffer, 1, chunkHeader.length, file); - break; - case CHUNK_ENCODING_RLE: - { - auto encode_buffer = std::make_unique(0x600000); - chunkHeader.length = static_cast(EncodeChunkRLE(buffer, encode_buffer.get(), chunkHeader.length)); - std::memcpy(dst_file, &chunkHeader, sizeof(SawyerCodingChunkHeader)); - dst_file += sizeof(SawyerCodingChunkHeader); - std::memcpy(dst_file, encode_buffer.get(), chunkHeader.length); - } - break; - case CHUNK_ENCODING_RLECOMPRESSED: - { - auto encode_buffer = std::make_unique(chunkHeader.length * 2); - auto encode_buffer2 = std::make_unique(0x600000); - chunkHeader.length = static_cast(EncodeChunkRepeat(buffer, encode_buffer.get(), chunkHeader.length)); - chunkHeader.length = static_cast( - EncodeChunkRLE(encode_buffer.get(), encode_buffer2.get(), chunkHeader.length)); - std::memcpy(dst_file, &chunkHeader, sizeof(SawyerCodingChunkHeader)); - dst_file += sizeof(SawyerCodingChunkHeader); - std::memcpy(dst_file, encode_buffer2.get(), chunkHeader.length); - } - break; - case CHUNK_ENCODING_ROTATE: - { - auto encode_buffer = std::make_unique(chunkHeader.length); - std::memcpy(encode_buffer.get(), buffer, chunkHeader.length); - EncodeChunkRotate(encode_buffer.get(), chunkHeader.length); - std::memcpy(dst_file, &chunkHeader, sizeof(SawyerCodingChunkHeader)); - dst_file += sizeof(SawyerCodingChunkHeader); - std::memcpy(dst_file, encode_buffer.get(), chunkHeader.length); - } - break; - } - - return chunkHeader.length + sizeof(SawyerCodingChunkHeader); -} - -size_t SawyerCodingDecodeSV4(const uint8_t* src, uint8_t* dst, size_t length, size_t bufferLength) -{ - // (0 to length - 4): RLE chunk - // (length - 4 to length): checksum - return DecodeChunkRLEWithSize(src, dst, length - 4, bufferLength); -} - -size_t SawyerCodingDecodeSC4(const uint8_t* src, uint8_t* dst, size_t length, size_t bufferLength) -{ - // Uncompress - size_t decodedLength = DecodeChunkRLEWithSize(src, dst, length - 4, bufferLength); - - // Decode - for (size_t i = 0x60018; i <= std::min(decodedLength - 1, static_cast(0x1F8353)); i++) - dst[i] = dst[i] ^ 0x9C; - - for (size_t i = 0x60018; i <= std::min(decodedLength - 1, static_cast(0x1F8350)); i += 4) - { - dst[i + 1] = Numerics::ror8(dst[i + 1], 3); - - uint32_t* code = reinterpret_cast(&dst[i]); - *code = Numerics::rol32(*code, 9); - } - - return decodedLength; -} - -size_t SawyerCodingEencodeSV4(const uint8_t* src, uint8_t* dst, size_t length) -{ - // Encode - size_t encodedLength = EncodeChunkRLE(src, dst, length); - - // Append checksum - uint32_t checksum = SawyerCodingCalculateChecksum(dst, encodedLength); - *(reinterpret_cast(&dst[encodedLength])) = checksum; - - return encodedLength + 4; -} - -size_t SawyerCodingDecodeTD6(const uint8_t* src, uint8_t* dst, size_t length) -{ - return DecodeChunkRLE(src, dst, length - 4); -} - -size_t SawyerCodingEncodeTD6(const uint8_t* src, uint8_t* dst, size_t length) -{ - size_t output_length = EncodeChunkRLE(src, dst, length); - - uint32_t checksum = 0; - for (size_t i = 0; i < output_length; i++) - { - uint8_t new_byte = ((checksum & 0xFF) + dst[i]) & 0xFF; - checksum = (checksum & 0xFFFFFF00) + new_byte; - checksum = Numerics::rol32(checksum, 3); - } - checksum -= 0x1D4C1; - - *(reinterpret_cast(&dst[output_length])) = checksum; - output_length += 4; - return output_length; -} - -/* Based off of rct2: 0x006770C1 */ -int32_t SawyerCodingValidateTrackChecksum(const uint8_t* src, size_t length) -{ - if (length < 4) - return 0; - - uint32_t file_checksum = *(reinterpret_cast(&src[length - 4])); - - uint32_t checksum = 0; - for (size_t i = 0; i < length - 4; i++) - { - uint8_t new_byte = ((checksum & 0xFF) + src[i]) & 0xFF; - checksum = (checksum & 0xFFFFFF00) + new_byte; - checksum = Numerics::rol32(checksum, 3); - } - - if (checksum - 0x1D4C1 == file_checksum) - return 1; // .TD6 - else if (checksum - 0x1A67C == file_checksum) - return 1; // .TD4 - else if (checksum - 0x1A650 == file_checksum) - return 1; // .TD4 - else - return 0; -} - -#pragma region Decoding - -/** - * - * rct2: 0x0067693A - */ -static size_t DecodeChunkRLE(const uint8_t* src_buffer, uint8_t* dst_buffer, size_t length) -{ - size_t count; - uint8_t *dst, rleCodeByte; - - dst = dst_buffer; - - for (size_t i = 0; i < length; i++) - { - rleCodeByte = src_buffer[i]; - if (rleCodeByte & 128) - { - i++; - count = 257 - rleCodeByte; - std::fill_n(dst, count, src_buffer[i]); - dst = reinterpret_cast(reinterpret_cast(dst) + count); - } - else - { - std::memcpy(dst, src_buffer + i + 1, rleCodeByte + 1); - dst = reinterpret_cast(reinterpret_cast(dst) + rleCodeByte + 1); - i += rleCodeByte + 1; - } - } - - // Return final size - return dst - dst_buffer; -} - -/** - * - * rct2: 0x0067693A - */ -static size_t DecodeChunkRLEWithSize(const uint8_t* src_buffer, uint8_t* dst_buffer, size_t length, size_t dstSize) -{ - size_t count; - uint8_t *dst, rleCodeByte; - - dst = dst_buffer; - - if (length <= 0 || dstSize <= 0) - throw std::out_of_range("Invalid RLE string!"); - - for (size_t i = 0; i < length; i++) - { - rleCodeByte = src_buffer[i]; - if (rleCodeByte & 128) - { - i++; - count = 257 - rleCodeByte; - assert(dst + count <= dst_buffer + dstSize); - assert(i < length); - std::fill_n(dst, count, src_buffer[i]); - dst = reinterpret_cast(reinterpret_cast(dst) + count); - } - else - { - if ((dst + rleCodeByte + 1 > dst_buffer + dstSize) || (i + 1 >= length)) - throw std::out_of_range("Invalid RLE string!"); - std::memcpy(dst, src_buffer + i + 1, rleCodeByte + 1); - dst = reinterpret_cast(reinterpret_cast(dst) + rleCodeByte + 1); - i += rleCodeByte + 1; - } - } - - // Return final size - return dst - dst_buffer; -} - -#pragma endregion - -#pragma region Encoding - -/** - * 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; - } - return dst - dst_buffer; -} - -static size_t EncodeChunkRepeat(const uint8_t* src_buffer, uint8_t* dst_buffer, size_t length) -{ - if (length == 0) - return 0; - - size_t outLength = 0; - - // Need to emit at least one byte, otherwise there is nothing to repeat - *dst_buffer++ = 255; - *dst_buffer++ = src_buffer[0]; - outLength += 2; - - // Iterate through remainder of the source buffer - for (size_t i = 1; i < length;) - { - size_t searchIndex = (i < 32) ? 0 : (i - 32); - size_t searchEnd = i - 1; - - size_t bestRepeatIndex = 0; - size_t bestRepeatCount = 0; - for (size_t repeatIndex = searchIndex; repeatIndex <= searchEnd; repeatIndex++) - { - size_t repeatCount = 0; - size_t maxRepeatCount = std::min(std::min(static_cast(7), searchEnd - repeatIndex), length - i - 1); - // maxRepeatCount should not exceed length - assert(repeatIndex + maxRepeatCount < length); - assert(i + maxRepeatCount < length); - for (size_t j = 0; j <= maxRepeatCount; j++) - { - if (src_buffer[repeatIndex + j] == src_buffer[i + j]) - { - repeatCount++; - } - else - { - break; - } - } - if (repeatCount > bestRepeatCount) - { - bestRepeatIndex = repeatIndex; - bestRepeatCount = repeatCount; - - // Maximum repeat count is 8 - if (repeatCount == 8) - break; - } - } - - if (bestRepeatCount == 0) - { - *dst_buffer++ = 255; - *dst_buffer++ = src_buffer[i]; - outLength += 2; - i++; - } - else - { - *dst_buffer++ = static_cast((bestRepeatCount - 1) | ((32 - (i - bestRepeatIndex)) << 3)); - outLength++; - i += bestRepeatCount; - } - } - - return outLength; -} - -static void EncodeChunkRotate(uint8_t* buffer, size_t length) -{ - size_t i; - uint8_t code = 1; - for (i = 0; i < length; i++) - { - buffer[i] = Numerics::rol8(buffer[i], code); - code = (code + 2) % 8; - } -} - -#pragma endregion - -int32_t SawyerCodingDetectFileType(const uint8_t* src, size_t length) -{ - if (length < 4) - { - throw std::length_error("Stream is (nearly) empty!"); - } - - size_t i; - - // Currently can't detect TD4, as the checksum is the same as SC4 (need alternative method) - - uint32_t checksum = *(reinterpret_cast(&src[length - 4])); - uint32_t actualChecksum = 0; - for (i = 0; i < length - 4; i++) - { - actualChecksum = (actualChecksum & 0xFFFFFF00) | (((actualChecksum & 0xFF) + static_cast(src[i])) & 0xFF); - actualChecksum = Numerics::rol32(actualChecksum, 3); - } - - return SawyerCodingDetectRCT1Version(checksum - actualChecksum); -} - -int32_t SawyerCodingDetectRCT1Version(int32_t gameVersion) -{ - int32_t fileType = (gameVersion) > 0 ? FILE_TYPE_SV4 : FILE_TYPE_SC4; - gameVersion = abs(gameVersion); - - if (gameVersion >= 108000 && gameVersion < 110000) - return (FILE_VERSION_RCT1 | fileType); - if (gameVersion >= 110000 && gameVersion < 120000) - return (FILE_VERSION_RCT1_AA | fileType); - if (gameVersion >= 120000 && gameVersion < 130000) - return (FILE_VERSION_RCT1_LL | fileType); - // RCTOA Acres sets this, and possibly some other user-created scenarios as well - if (gameVersion == 0) - return (FILE_VERSION_RCT1_LL | fileType); - - return -1; -} diff --git a/src/openrct2/util/SawyerCoding.h b/src/openrct2/util/SawyerCoding.h deleted file mode 100644 index 2997d587a4..0000000000 --- a/src/openrct2/util/SawyerCoding.h +++ /dev/null @@ -1,55 +0,0 @@ -/***************************************************************************** - * Copyright (c) 2014-2024 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 -#include - -#pragma pack(push, 1) -struct SawyerCodingChunkHeader -{ - uint8_t encoding; - uint32_t length; -}; -static_assert(sizeof(SawyerCodingChunkHeader) == 5); -#pragma pack(pop) - -enum -{ - CHUNK_ENCODING_NONE, - CHUNK_ENCODING_RLE, - CHUNK_ENCODING_RLECOMPRESSED, - CHUNK_ENCODING_ROTATE -}; - -enum -{ - FILE_VERSION_MASK = (3 << 0), - FILE_VERSION_RCT1 = (0 << 0), - FILE_VERSION_RCT1_AA = (1 << 0), - FILE_VERSION_RCT1_LL = (2 << 0), - - FILE_TYPE_MASK = (3 << 2), - FILE_TYPE_TD4 = (0 << 2), - FILE_TYPE_SV4 = (1 << 2), - FILE_TYPE_SC4 = (2 << 2) -}; - -uint32_t SawyerCodingCalculateChecksum(const uint8_t* buffer, size_t length); -size_t SawyerCodingWriteChunkBuffer(uint8_t* dst_file, const uint8_t* src_buffer, SawyerCodingChunkHeader chunkHeader); -size_t SawyerCodingDecodeSV4(const uint8_t* src, uint8_t* dst, size_t length, size_t bufferLength); -size_t SawyerCodingDecodeSC4(const uint8_t* src, uint8_t* dst, size_t length, size_t bufferLength); -size_t SawyerCodingEencodeSV4(const uint8_t* src, uint8_t* dst, size_t length); -size_t SawyerCodingDecodeTD6(const uint8_t* src, uint8_t* dst, size_t length); -size_t SawyerCodingEncodeTD6(const uint8_t* src, uint8_t* dst, size_t length); -int32_t SawyerCodingValidateTrackChecksum(const uint8_t* src, size_t length); - -int32_t SawyerCodingDetectFileType(const uint8_t* src, size_t length); -int32_t SawyerCodingDetectRCT1Version(int32_t gameVersion); diff --git a/test/tests/SawyerCodingTest.cpp b/test/tests/SawyerCodingTest.cpp index 4df48d448e..05dff2888a 100644 --- a/test/tests/SawyerCodingTest.cpp +++ b/test/tests/SawyerCodingTest.cpp @@ -9,11 +9,13 @@ #include #include +#include #include -#include constexpr size_t BUFFER_SIZE = 0x600000; +using namespace OpenRCT2; + class SawyerCodingTest : public testing::Test { protected: @@ -34,13 +36,13 @@ protected: void TestEncodeDecode(uint8_t encoding_type) { // Encode - SawyerCodingChunkHeader chdr_in; + SawyerCoding::ChunkHeader chdr_in; chdr_in.encoding = encoding_type; chdr_in.length = sizeof(randomdata); uint8_t* encodedDataBuffer = new uint8_t[BUFFER_SIZE]; - size_t encodedDataSize = SawyerCodingWriteChunkBuffer( + size_t encodedDataSize = SawyerCoding::WriteChunkBuffer( encodedDataBuffer, reinterpret_cast(randomdata), chdr_in); - ASSERT_GT(encodedDataSize, sizeof(SawyerCodingChunkHeader)); + ASSERT_GT(encodedDataSize, sizeof(SawyerCoding::ChunkHeader)); // Decode OpenRCT2::MemoryStream ms(encodedDataBuffer, encodedDataSize); @@ -56,8 +58,8 @@ protected: void TestDecode(const uint8_t* data, size_t size) { - auto expectedLength = size - sizeof(SawyerCodingChunkHeader); - auto chdr_in = reinterpret_cast(data); + auto expectedLength = size - sizeof(SawyerCoding::ChunkHeader); + auto chdr_in = reinterpret_cast(data); ASSERT_EQ(chdr_in->length, expectedLength); OpenRCT2::MemoryStream ms(data, size);