2014-04-10 01:22:57 +02:00
|
|
|
/*****************************************************************************
|
2020-07-21 15:04:34 +02:00
|
|
|
* Copyright (c) 2014-2020 OpenRCT2 developers
|
2015-10-12 22:18:14 +02:00
|
|
|
*
|
2018-06-15 14:07:34 +02:00
|
|
|
* For a complete list of all authors, please refer to contributors.md
|
|
|
|
* Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2
|
2015-10-12 22:18:14 +02:00
|
|
|
*
|
2018-06-15 14:07:34 +02:00
|
|
|
* OpenRCT2 is licensed under the GNU General Public License version 3.
|
2014-04-10 01:22:57 +02:00
|
|
|
*****************************************************************************/
|
|
|
|
|
2018-06-22 23:16:39 +02:00
|
|
|
#include "SawyerCoding.h"
|
2018-03-14 12:44:16 +01:00
|
|
|
|
2021-09-11 18:53:14 +02:00
|
|
|
#include "../core/Numerics.hpp"
|
2015-07-05 06:36:25 +02:00
|
|
|
#include "../platform/platform.h"
|
2018-01-02 18:58:43 +01:00
|
|
|
#include "../scenario/Scenario.h"
|
2017-12-13 13:02:24 +01:00
|
|
|
#include "Util.h"
|
2014-04-10 01:22:57 +02:00
|
|
|
|
2018-08-12 13:50:40 +02:00
|
|
|
#include <algorithm>
|
2018-06-22 23:16:39 +02:00
|
|
|
#include <cstring>
|
|
|
|
|
2018-06-20 17:28:51 +02:00
|
|
|
static size_t decode_chunk_rle(const uint8_t* src_buffer, uint8_t* dst_buffer, size_t length);
|
|
|
|
static size_t decode_chunk_rle_with_size(const uint8_t* src_buffer, uint8_t* dst_buffer, size_t length, size_t dstSize);
|
2014-04-10 01:22:57 +02:00
|
|
|
|
2018-06-22 23:16:39 +02:00
|
|
|
static size_t encode_chunk_rle(const uint8_t* src_buffer, uint8_t* dst_buffer, size_t length);
|
|
|
|
static size_t encode_chunk_repeat(const uint8_t* src_buffer, uint8_t* dst_buffer, size_t length);
|
|
|
|
static void encode_chunk_rotate(uint8_t* buffer, size_t length);
|
2014-10-04 12:41:16 +02:00
|
|
|
|
2016-11-28 10:59:01 +01:00
|
|
|
bool gUseRLE = true;
|
|
|
|
|
2018-06-20 17:28:51 +02:00
|
|
|
uint32_t sawyercoding_calculate_checksum(const uint8_t* buffer, size_t length)
|
2014-11-26 01:51:26 +01:00
|
|
|
{
|
2018-06-20 17:28:51 +02:00
|
|
|
uint32_t checksum = 0;
|
2021-01-06 22:29:40 +01:00
|
|
|
for (size_t i = 0; i < length; i++)
|
2017-06-06 23:24:18 +02:00
|
|
|
checksum += buffer[i];
|
2014-10-04 12:41:16 +02:00
|
|
|
|
2017-06-06 23:24:18 +02:00
|
|
|
return checksum;
|
2014-10-04 12:41:16 +02:00
|
|
|
}
|
|
|
|
|
2014-11-26 17:27:21 +01:00
|
|
|
/**
|
2018-06-22 23:16:39 +02:00
|
|
|
*
|
|
|
|
* rct2: 0x006762E1
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
size_t sawyercoding_write_chunk_buffer(uint8_t* dst_file, const uint8_t* buffer, sawyercoding_chunk_header chunkHeader)
|
|
|
|
{
|
2018-06-20 17:28:51 +02:00
|
|
|
uint8_t *encode_buffer, *encode_buffer2;
|
2017-06-06 23:24:18 +02:00
|
|
|
|
2019-05-10 22:00:38 +02:00
|
|
|
if (!gUseRLE)
|
2018-06-22 23:16:39 +02:00
|
|
|
{
|
|
|
|
if (chunkHeader.encoding == CHUNK_ENCODING_RLE || chunkHeader.encoding == CHUNK_ENCODING_RLECOMPRESSED)
|
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
chunkHeader.encoding = CHUNK_ENCODING_NONE;
|
|
|
|
}
|
|
|
|
}
|
2018-06-22 23:16:39 +02:00
|
|
|
switch (chunkHeader.encoding)
|
|
|
|
{
|
|
|
|
case CHUNK_ENCODING_NONE:
|
2018-12-15 22:23:31 +01:00
|
|
|
std::memcpy(dst_file, &chunkHeader, sizeof(sawyercoding_chunk_header));
|
2018-06-22 23:16:39 +02:00
|
|
|
dst_file += sizeof(sawyercoding_chunk_header);
|
2018-12-15 22:23:31 +01:00
|
|
|
std::memcpy(dst_file, buffer, chunkHeader.length);
|
2018-06-22 23:16:39 +02:00
|
|
|
// fwrite(&chunkHeader, sizeof(sawyercoding_chunk_header), 1, file);
|
|
|
|
// fwrite(buffer, 1, chunkHeader.length, file);
|
|
|
|
break;
|
|
|
|
case CHUNK_ENCODING_RLE:
|
2020-04-17 19:45:19 +02:00
|
|
|
encode_buffer = static_cast<uint8_t*>(malloc(0x600000));
|
|
|
|
chunkHeader.length = static_cast<uint32_t>(encode_chunk_rle(buffer, encode_buffer, chunkHeader.length));
|
2018-12-15 22:23:31 +01:00
|
|
|
std::memcpy(dst_file, &chunkHeader, sizeof(sawyercoding_chunk_header));
|
2018-06-22 23:16:39 +02:00
|
|
|
dst_file += sizeof(sawyercoding_chunk_header);
|
2018-12-15 22:23:31 +01:00
|
|
|
std::memcpy(dst_file, encode_buffer, chunkHeader.length);
|
2018-06-22 23:16:39 +02:00
|
|
|
|
|
|
|
free(encode_buffer);
|
|
|
|
break;
|
|
|
|
case CHUNK_ENCODING_RLECOMPRESSED:
|
2020-04-17 19:45:19 +02:00
|
|
|
encode_buffer = static_cast<uint8_t*>(malloc(chunkHeader.length * 2));
|
|
|
|
encode_buffer2 = static_cast<uint8_t*>(malloc(0x600000));
|
|
|
|
chunkHeader.length = static_cast<uint32_t>(encode_chunk_repeat(buffer, encode_buffer, chunkHeader.length));
|
|
|
|
chunkHeader.length = static_cast<uint32_t>(encode_chunk_rle(encode_buffer, encode_buffer2, chunkHeader.length));
|
2018-12-15 22:23:31 +01:00
|
|
|
std::memcpy(dst_file, &chunkHeader, sizeof(sawyercoding_chunk_header));
|
2018-06-22 23:16:39 +02:00
|
|
|
dst_file += sizeof(sawyercoding_chunk_header);
|
2018-12-15 22:23:31 +01:00
|
|
|
std::memcpy(dst_file, encode_buffer2, chunkHeader.length);
|
2018-06-22 23:16:39 +02:00
|
|
|
|
|
|
|
free(encode_buffer2);
|
|
|
|
free(encode_buffer);
|
|
|
|
break;
|
|
|
|
case CHUNK_ENCODING_ROTATE:
|
2020-04-17 19:45:19 +02:00
|
|
|
encode_buffer = static_cast<uint8_t*>(malloc(chunkHeader.length));
|
2018-12-15 22:23:31 +01:00
|
|
|
std::memcpy(encode_buffer, buffer, chunkHeader.length);
|
2018-06-22 23:16:39 +02:00
|
|
|
encode_chunk_rotate(encode_buffer, chunkHeader.length);
|
2018-12-15 22:23:31 +01:00
|
|
|
std::memcpy(dst_file, &chunkHeader, sizeof(sawyercoding_chunk_header));
|
2018-06-22 23:16:39 +02:00
|
|
|
dst_file += sizeof(sawyercoding_chunk_header);
|
2018-12-15 22:23:31 +01:00
|
|
|
std::memcpy(dst_file, encode_buffer, chunkHeader.length);
|
2018-06-22 23:16:39 +02:00
|
|
|
|
|
|
|
free(encode_buffer);
|
|
|
|
break;
|
2017-06-06 23:24:18 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return chunkHeader.length + sizeof(sawyercoding_chunk_header);
|
2014-11-26 17:27:21 +01:00
|
|
|
}
|
|
|
|
|
2018-06-22 23:16:39 +02:00
|
|
|
size_t sawyercoding_decode_sv4(const uint8_t* src, uint8_t* dst, size_t length, size_t bufferLength)
|
2014-11-26 17:27:21 +01:00
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
// (0 to length - 4): RLE chunk
|
|
|
|
// (length - 4 to length): checksum
|
|
|
|
return decode_chunk_rle_with_size(src, dst, length - 4, bufferLength);
|
2014-11-26 17:27:21 +01:00
|
|
|
}
|
|
|
|
|
2018-06-22 23:16:39 +02:00
|
|
|
size_t sawyercoding_decode_sc4(const uint8_t* src, uint8_t* dst, size_t length, size_t bufferLength)
|
2014-11-26 17:27:21 +01:00
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
// Uncompress
|
|
|
|
size_t decodedLength = decode_chunk_rle_with_size(src, dst, length - 4, bufferLength);
|
2014-11-26 17:27:21 +01:00
|
|
|
|
2017-06-06 23:24:18 +02:00
|
|
|
// Decode
|
2020-04-17 19:45:19 +02:00
|
|
|
for (size_t i = 0x60018; i <= std::min(decodedLength - 1, static_cast<size_t>(0x1F8353)); i++)
|
2017-06-06 23:24:18 +02:00
|
|
|
dst[i] = dst[i] ^ 0x9C;
|
2014-11-26 17:27:21 +01:00
|
|
|
|
2020-04-17 19:45:19 +02:00
|
|
|
for (size_t i = 0x60018; i <= std::min(decodedLength - 1, static_cast<size_t>(0x1F8350)); i += 4)
|
2018-06-22 23:16:39 +02:00
|
|
|
{
|
2021-09-11 18:53:14 +02:00
|
|
|
dst[i + 1] = Numerics::ror8(dst[i + 1], 3);
|
2014-11-26 17:27:21 +01:00
|
|
|
|
2020-04-17 19:45:19 +02:00
|
|
|
uint32_t* code = reinterpret_cast<uint32_t*>(&dst[i]);
|
2021-09-11 18:53:14 +02:00
|
|
|
*code = Numerics::rol32(*code, 9);
|
2017-06-06 23:24:18 +02:00
|
|
|
}
|
2014-11-26 17:27:21 +01:00
|
|
|
|
2017-06-06 23:24:18 +02:00
|
|
|
return decodedLength;
|
2014-11-26 17:27:21 +01:00
|
|
|
}
|
|
|
|
|
2018-06-22 23:16:39 +02:00
|
|
|
size_t sawyercoding_encode_sv4(const uint8_t* src, uint8_t* dst, size_t length)
|
2014-11-26 17:27:21 +01:00
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
// Encode
|
2021-01-06 22:29:40 +01:00
|
|
|
size_t encodedLength = encode_chunk_rle(src, dst, length);
|
2014-11-26 17:27:21 +01:00
|
|
|
|
2017-06-06 23:24:18 +02:00
|
|
|
// Append checksum
|
2021-01-06 22:29:40 +01:00
|
|
|
uint32_t checksum = sawyercoding_calculate_checksum(dst, encodedLength);
|
2020-04-17 19:45:19 +02:00
|
|
|
*(reinterpret_cast<uint32_t*>(&dst[encodedLength])) = checksum;
|
2014-11-26 17:27:21 +01:00
|
|
|
|
2017-06-06 23:24:18 +02:00
|
|
|
return encodedLength + 4;
|
2014-11-26 17:27:21 +01:00
|
|
|
}
|
|
|
|
|
2018-06-22 23:16:39 +02:00
|
|
|
size_t sawyercoding_decode_td6(const uint8_t* src, uint8_t* dst, size_t length)
|
2014-11-26 17:27:21 +01:00
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
return decode_chunk_rle(src, dst, length - 4);
|
2014-11-26 17:27:21 +01:00
|
|
|
}
|
|
|
|
|
2018-06-22 23:16:39 +02:00
|
|
|
size_t sawyercoding_encode_td6(const uint8_t* src, uint8_t* dst, size_t length)
|
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
size_t output_length = encode_chunk_rle(src, dst, length);
|
|
|
|
|
2018-06-20 17:28:51 +02:00
|
|
|
uint32_t checksum = 0;
|
2018-06-22 23:16:39 +02:00
|
|
|
for (size_t i = 0; i < output_length; i++)
|
|
|
|
{
|
2018-06-20 17:28:51 +02:00
|
|
|
uint8_t new_byte = ((checksum & 0xFF) + dst[i]) & 0xFF;
|
2017-06-06 23:24:18 +02:00
|
|
|
checksum = (checksum & 0xFFFFFF00) + new_byte;
|
2021-09-11 18:53:14 +02:00
|
|
|
checksum = Numerics::rol32(checksum, 3);
|
2017-06-06 23:24:18 +02:00
|
|
|
}
|
|
|
|
checksum -= 0x1D4C1;
|
|
|
|
|
2020-04-17 19:45:19 +02:00
|
|
|
*(reinterpret_cast<uint32_t*>(&dst[output_length])) = checksum;
|
2017-06-06 23:24:18 +02:00
|
|
|
output_length += 4;
|
|
|
|
return output_length;
|
2015-04-28 21:29:03 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Based off of rct2: 0x006770C1 */
|
2018-06-22 23:16:39 +02:00
|
|
|
int32_t sawyercoding_validate_track_checksum(const uint8_t* src, size_t length)
|
|
|
|
{
|
2020-04-17 19:45:19 +02:00
|
|
|
uint32_t file_checksum = *(reinterpret_cast<const uint32_t*>(&src[length - 4]));
|
2017-06-06 23:24:18 +02:00
|
|
|
|
2018-06-20 17:28:51 +02:00
|
|
|
uint32_t checksum = 0;
|
2018-06-22 23:16:39 +02:00
|
|
|
for (size_t i = 0; i < length - 4; i++)
|
|
|
|
{
|
2018-06-20 17:28:51 +02:00
|
|
|
uint8_t new_byte = ((checksum & 0xFF) + src[i]) & 0xFF;
|
2017-06-06 23:24:18 +02:00
|
|
|
checksum = (checksum & 0xFFFFFF00) + new_byte;
|
2021-09-11 18:53:14 +02:00
|
|
|
checksum = Numerics::rol32(checksum, 3);
|
2017-06-06 23:24:18 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (checksum - 0x1D4C1 == file_checksum)
|
2018-06-22 23:16:39 +02:00
|
|
|
return 1; // .TD6
|
2017-06-06 23:24:18 +02:00
|
|
|
else if (checksum - 0x1A67C == file_checksum)
|
2018-06-22 23:16:39 +02:00
|
|
|
return 1; // .TD4
|
2017-06-06 23:24:18 +02:00
|
|
|
else if (checksum - 0x1A650 == file_checksum)
|
2018-06-22 23:16:39 +02:00
|
|
|
return 1; // .TD4
|
2017-06-06 23:24:18 +02:00
|
|
|
else
|
|
|
|
return 0;
|
2015-04-28 21:29:03 +02:00
|
|
|
}
|
|
|
|
|
2014-11-26 17:27:21 +01:00
|
|
|
#pragma region Decoding
|
|
|
|
|
2014-04-10 01:22:57 +02:00
|
|
|
/**
|
2015-10-12 22:18:14 +02:00
|
|
|
*
|
2014-04-10 01:22:57 +02:00
|
|
|
* rct2: 0x0067693A
|
|
|
|
*/
|
2018-06-20 17:28:51 +02:00
|
|
|
static size_t decode_chunk_rle(const uint8_t* src_buffer, uint8_t* dst_buffer, size_t length)
|
2014-04-10 01:22:57 +02:00
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
size_t count;
|
2018-06-20 17:28:51 +02:00
|
|
|
uint8_t *dst, rleCodeByte;
|
2017-06-06 23:24:18 +02:00
|
|
|
|
|
|
|
dst = dst_buffer;
|
|
|
|
|
2018-06-22 23:16:39 +02:00
|
|
|
for (size_t i = 0; i < length; i++)
|
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
rleCodeByte = src_buffer[i];
|
2018-06-22 23:16:39 +02:00
|
|
|
if (rleCodeByte & 128)
|
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
i++;
|
|
|
|
count = 257 - rleCodeByte;
|
2018-12-15 22:23:31 +01:00
|
|
|
std::fill_n(dst, count, src_buffer[i]);
|
2020-04-17 19:45:19 +02:00
|
|
|
dst = reinterpret_cast<uint8_t*>(reinterpret_cast<uintptr_t>(dst) + count);
|
2018-06-22 23:16:39 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2018-12-15 22:23:31 +01:00
|
|
|
std::memcpy(dst, src_buffer + i + 1, rleCodeByte + 1);
|
2020-04-17 19:45:19 +02:00
|
|
|
dst = reinterpret_cast<uint8_t*>(reinterpret_cast<uintptr_t>(dst) + rleCodeByte + 1);
|
2017-06-06 23:24:18 +02:00
|
|
|
i += rleCodeByte + 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Return final size
|
|
|
|
return dst - dst_buffer;
|
2014-04-10 01:22:57 +02:00
|
|
|
}
|
|
|
|
|
2016-06-23 22:48:46 +02:00
|
|
|
/**
|
|
|
|
*
|
|
|
|
* rct2: 0x0067693A
|
|
|
|
*/
|
2018-06-20 17:28:51 +02:00
|
|
|
static size_t decode_chunk_rle_with_size(const uint8_t* src_buffer, uint8_t* dst_buffer, size_t length, size_t dstSize)
|
2016-06-23 22:48:46 +02:00
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
size_t count;
|
2018-06-20 17:28:51 +02:00
|
|
|
uint8_t *dst, rleCodeByte;
|
2017-06-06 23:24:18 +02:00
|
|
|
|
|
|
|
dst = dst_buffer;
|
|
|
|
|
2017-07-31 22:13:24 +02:00
|
|
|
assert(length > 0);
|
|
|
|
assert(dstSize > 0);
|
2018-06-22 23:16:39 +02:00
|
|
|
for (size_t i = 0; i < length; i++)
|
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
rleCodeByte = src_buffer[i];
|
2018-06-22 23:16:39 +02:00
|
|
|
if (rleCodeByte & 128)
|
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
i++;
|
|
|
|
count = 257 - rleCodeByte;
|
|
|
|
assert(dst + count <= dst_buffer + dstSize);
|
2017-07-31 22:13:24 +02:00
|
|
|
assert(i < length);
|
2018-12-15 22:23:31 +01:00
|
|
|
std::fill_n(dst, count, src_buffer[i]);
|
2020-04-17 19:45:19 +02:00
|
|
|
dst = reinterpret_cast<uint8_t*>(reinterpret_cast<uintptr_t>(dst) + count);
|
2018-06-22 23:16:39 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
assert(dst + rleCodeByte + 1 <= dst_buffer + dstSize);
|
2017-07-31 22:13:24 +02:00
|
|
|
assert(i + 1 < length);
|
2018-12-15 22:23:31 +01:00
|
|
|
std::memcpy(dst, src_buffer + i + 1, rleCodeByte + 1);
|
2020-04-17 19:45:19 +02:00
|
|
|
dst = reinterpret_cast<uint8_t*>(reinterpret_cast<uintptr_t>(dst) + rleCodeByte + 1);
|
2017-06-06 23:24:18 +02:00
|
|
|
i += rleCodeByte + 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Return final size
|
|
|
|
return dst - dst_buffer;
|
2016-06-23 22:48:46 +02:00
|
|
|
}
|
|
|
|
|
2014-11-26 17:27:21 +01:00
|
|
|
#pragma endregion
|
2014-10-04 12:41:16 +02:00
|
|
|
|
2014-11-26 17:27:21 +01:00
|
|
|
#pragma region Encoding
|
2014-10-03 21:07:21 +02:00
|
|
|
|
|
|
|
/**
|
2014-11-26 17:27:21 +01:00
|
|
|
* Ensure dst_buffer is bigger than src_buffer then resize afterwards
|
|
|
|
* returns length of dst_buffer
|
|
|
|
*/
|
2018-06-22 23:16:39 +02:00
|
|
|
static size_t encode_chunk_rle(const uint8_t* src_buffer, uint8_t* dst_buffer, size_t length)
|
2014-10-03 21:07:21 +02:00
|
|
|
{
|
2018-06-20 17:28:51 +02:00
|
|
|
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;
|
2017-06-06 23:24:18 +02:00
|
|
|
|
2018-06-22 23:16:39 +02:00
|
|
|
while (src < end_src - 1)
|
|
|
|
{
|
|
|
|
if ((count && *src == src[1]) || count > 125)
|
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
*dst++ = count - 1;
|
2018-12-15 22:23:31 +01:00
|
|
|
std::memcpy(dst, src_norm_start, count);
|
2017-06-06 23:24:18 +02:00
|
|
|
dst += count;
|
|
|
|
src_norm_start += count;
|
|
|
|
count = 0;
|
|
|
|
}
|
2018-06-22 23:16:39 +02:00
|
|
|
if (*src == src[1])
|
|
|
|
{
|
|
|
|
for (; (count < 125) && ((src + count) < end_src); count++)
|
|
|
|
{
|
|
|
|
if (*src != src[count])
|
|
|
|
break;
|
2017-06-06 23:24:18 +02:00
|
|
|
}
|
|
|
|
*dst++ = 257 - count;
|
|
|
|
*dst++ = *src;
|
|
|
|
src += count;
|
|
|
|
src_norm_start = src;
|
|
|
|
count = 0;
|
|
|
|
}
|
2018-06-22 23:16:39 +02:00
|
|
|
else
|
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
count++;
|
|
|
|
src++;
|
|
|
|
}
|
|
|
|
}
|
2018-06-22 23:16:39 +02:00
|
|
|
if (src == end_src - 1)
|
|
|
|
count++;
|
|
|
|
if (count)
|
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
*dst++ = count - 1;
|
2018-12-15 22:23:31 +01:00
|
|
|
std::memcpy(dst, src_norm_start, count);
|
2017-06-06 23:24:18 +02:00
|
|
|
dst += count;
|
|
|
|
}
|
|
|
|
return dst - dst_buffer;
|
2014-10-03 21:07:21 +02:00
|
|
|
}
|
|
|
|
|
2018-06-22 23:16:39 +02:00
|
|
|
static size_t encode_chunk_repeat(const uint8_t* src_buffer, uint8_t* dst_buffer, size_t length)
|
2014-10-03 21:07:21 +02:00
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
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
|
2018-06-22 23:16:39 +02:00
|
|
|
for (size_t i = 1; i < length;)
|
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
size_t searchIndex = (i < 32) ? 0 : (i - 32);
|
|
|
|
size_t searchEnd = i - 1;
|
|
|
|
|
|
|
|
size_t bestRepeatIndex = 0;
|
|
|
|
size_t bestRepeatCount = 0;
|
2018-06-22 23:16:39 +02:00
|
|
|
for (size_t repeatIndex = searchIndex; repeatIndex <= searchEnd; repeatIndex++)
|
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
size_t repeatCount = 0;
|
2020-04-17 19:45:19 +02:00
|
|
|
size_t maxRepeatCount = std::min(std::min(static_cast<size_t>(7), searchEnd - repeatIndex), length - i - 1);
|
2017-06-06 23:24:18 +02:00
|
|
|
// maxRepeatCount should not exceed length
|
|
|
|
assert(repeatIndex + maxRepeatCount < length);
|
|
|
|
assert(i + maxRepeatCount < length);
|
2018-06-22 23:16:39 +02:00
|
|
|
for (size_t j = 0; j <= maxRepeatCount; j++)
|
|
|
|
{
|
|
|
|
if (src_buffer[repeatIndex + j] == src_buffer[i + j])
|
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
repeatCount++;
|
2018-06-22 23:16:39 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2018-06-22 23:16:39 +02:00
|
|
|
if (repeatCount > bestRepeatCount)
|
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
bestRepeatIndex = repeatIndex;
|
|
|
|
bestRepeatCount = repeatCount;
|
|
|
|
|
|
|
|
// Maximum repeat count is 8
|
|
|
|
if (repeatCount == 8)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-06-22 23:16:39 +02:00
|
|
|
if (bestRepeatCount == 0)
|
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
*dst_buffer++ = 255;
|
|
|
|
*dst_buffer++ = src_buffer[i];
|
|
|
|
outLength += 2;
|
|
|
|
i++;
|
2018-06-22 23:16:39 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2020-04-17 19:45:19 +02:00
|
|
|
*dst_buffer++ = static_cast<uint8_t>((bestRepeatCount - 1) | ((32 - (i - bestRepeatIndex)) << 3));
|
2017-06-06 23:24:18 +02:00
|
|
|
outLength++;
|
|
|
|
i += bestRepeatCount;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return outLength;
|
2014-10-10 23:50:22 +02:00
|
|
|
}
|
|
|
|
|
2018-06-22 23:16:39 +02:00
|
|
|
static void encode_chunk_rotate(uint8_t* buffer, size_t length)
|
2014-10-10 23:50:22 +02:00
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
size_t i;
|
2018-06-20 17:28:51 +02:00
|
|
|
uint8_t code = 1;
|
2018-06-22 23:16:39 +02:00
|
|
|
for (i = 0; i < length; i++)
|
|
|
|
{
|
2021-09-11 18:53:14 +02:00
|
|
|
buffer[i] = Numerics::rol8(buffer[i], code);
|
2017-06-06 23:24:18 +02:00
|
|
|
code = (code + 2) % 8;
|
|
|
|
}
|
2014-10-11 02:18:08 +02:00
|
|
|
}
|
|
|
|
|
2014-11-26 17:27:21 +01:00
|
|
|
#pragma endregion
|
2015-01-20 02:04:28 +01:00
|
|
|
|
2018-06-22 23:16:39 +02:00
|
|
|
int32_t sawyercoding_detect_file_type(const uint8_t* src, size_t length)
|
2015-01-20 02:04:28 +01:00
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
size_t i;
|
2015-01-20 02:04:28 +01:00
|
|
|
|
2017-06-06 23:24:18 +02:00
|
|
|
// Currently can't detect TD4, as the checksum is the same as SC4 (need alternative method)
|
2015-01-20 02:04:28 +01:00
|
|
|
|
2020-04-17 19:45:19 +02:00
|
|
|
uint32_t checksum = *(reinterpret_cast<const uint32_t*>(&src[length - 4]));
|
2018-06-20 17:28:51 +02:00
|
|
|
uint32_t actualChecksum = 0;
|
2018-06-22 23:16:39 +02:00
|
|
|
for (i = 0; i < length - 4; i++)
|
|
|
|
{
|
2020-04-17 19:45:19 +02:00
|
|
|
actualChecksum = (actualChecksum & 0xFFFFFF00) | (((actualChecksum & 0xFF) + static_cast<uint8_t>(src[i])) & 0xFF);
|
2021-09-11 18:53:14 +02:00
|
|
|
actualChecksum = Numerics::rol32(actualChecksum, 3);
|
2017-06-06 23:24:18 +02:00
|
|
|
}
|
2015-01-20 02:04:28 +01:00
|
|
|
|
2017-06-06 23:24:18 +02:00
|
|
|
return sawyercoding_detect_rct1_version(checksum - actualChecksum);
|
2015-08-19 20:34:26 +02:00
|
|
|
}
|
|
|
|
|
2018-06-20 17:28:51 +02:00
|
|
|
int32_t sawyercoding_detect_rct1_version(int32_t gameVersion)
|
2015-08-19 20:34:26 +02:00
|
|
|
{
|
2018-06-20 17:28:51 +02:00
|
|
|
int32_t fileType = (gameVersion) > 0 ? FILE_TYPE_SV4 : FILE_TYPE_SC4;
|
2017-06-06 23:24:18 +02:00
|
|
|
gameVersion = abs(gameVersion);
|
|
|
|
|
|
|
|
if (gameVersion >= 108000 && gameVersion < 110000)
|
|
|
|
return (FILE_VERSION_RCT1 | fileType);
|
2021-09-15 22:22:15 +02:00
|
|
|
if (gameVersion >= 110000 && gameVersion < 120000)
|
2017-06-06 23:24:18 +02:00
|
|
|
return (FILE_VERSION_RCT1_AA | fileType);
|
2021-09-15 22:22:15 +02:00
|
|
|
if (gameVersion >= 120000 && gameVersion < 130000)
|
2017-06-06 23:24:18 +02:00
|
|
|
return (FILE_VERSION_RCT1_LL | fileType);
|
|
|
|
// RCTOA Acres sets this, and possibly some other user-created scenarios as well
|
2021-09-15 22:22:15 +02:00
|
|
|
if (gameVersion == 0)
|
2017-06-06 23:24:18 +02:00
|
|
|
return (FILE_VERSION_RCT1_LL | fileType);
|
|
|
|
|
|
|
|
return -1;
|
2015-10-12 22:18:14 +02:00
|
|
|
}
|