OpenRCT2/src/util/sawyercoding.c

562 lines
14 KiB
C
Raw Normal View History

#pragma region Copyright (c) 2014-2016 OpenRCT2 Developers
2014-04-10 01:22:57 +02:00
/*****************************************************************************
* OpenRCT2, an open source clone of Roller Coaster Tycoon 2.
2015-10-12 22:18:14 +02:00
*
* OpenRCT2 is the work of many authors, a full list can be found in contributors.md
* For more information, visit https://github.com/OpenRCT2/OpenRCT2
2015-10-12 22:18:14 +02:00
*
2014-04-10 01:22:57 +02:00
* OpenRCT2 is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* A full copy of the GNU General Public License can be found in licence.txt
2014-04-10 01:22:57 +02:00
*****************************************************************************/
#pragma endregion
2014-04-10 01:22:57 +02:00
2014-10-06 18:36:58 +02:00
#include "../addresses.h"
2015-07-05 06:36:25 +02:00
#include "../platform/platform.h"
2014-04-10 01:22:57 +02:00
#include "sawyercoding.h"
2016-01-16 00:18:21 +01:00
#include "../scenario.h"
#include "util.h"
2014-04-10 01:22:57 +02:00
2015-10-12 22:18:14 +02:00
static size_t decode_chunk_rle(const uint8* src_buffer, uint8* dst_buffer, size_t length);
static size_t decode_chunk_repeat(uint8 *buffer, size_t length);
static void decode_chunk_rotate(uint8 *buffer, size_t length);
2014-04-10 01:22:57 +02:00
2015-10-12 22:18:14 +02:00
static size_t encode_chunk_rle(const uint8 *src_buffer, uint8 *dst_buffer, size_t length);
static size_t encode_chunk_repeat(const uint8 *src_buffer, uint8 *dst_buffer, size_t length);
static void encode_chunk_rotate(uint8 *buffer, size_t length);
2015-10-12 22:18:14 +02:00
uint32 sawyercoding_calculate_checksum(const uint8* buffer, size_t length)
{
2015-10-12 22:18:14 +02:00
size_t i;
uint32 checksum = 0;
for (i = 0; i < length; i++)
checksum += buffer[i];
return checksum;
}
/**
2015-10-12 22:18:14 +02:00
*
* rct2: 0x00676FD2
*/
2015-07-05 06:36:25 +02:00
int sawyercoding_validate_checksum(SDL_RWops* rw)
{
2015-10-12 22:18:14 +02:00
size_t i, dataSize, bufferSize;
uint32 checksum, fileChecksum;
uint8 buffer[1024];
// Get data size
2015-07-05 06:36:25 +02:00
SDL_RWseek(rw, 0, RW_SEEK_END);
2015-10-12 22:18:14 +02:00
// for whatever the reason, SDL_RWtell return Sint64 instead of size_t.
dataSize = (size_t)SDL_RWtell(rw);
2014-05-24 23:14:42 +02:00
if (dataSize < 8)
2014-05-12 02:45:45 +02:00
return 0;
dataSize -= 4;
2015-10-12 22:18:14 +02:00
// Calculate checksum
2015-07-05 06:36:25 +02:00
SDL_RWseek(rw, 0, RW_SEEK_SET);
checksum = 0;
do {
bufferSize = min(dataSize, 1024);
2015-07-05 06:36:25 +02:00
if (SDL_RWread(rw, buffer, bufferSize, 1) != 1)
return 0;
for (i = 0; i < bufferSize; i++)
checksum += buffer[i];
dataSize -= bufferSize;
} while (dataSize != 0);
// Read file checksum
2015-07-05 06:36:25 +02:00
if (SDL_RWread(rw, &fileChecksum, sizeof(fileChecksum), 1) != 1)
return 0;
// Reset file position
2015-07-05 06:36:25 +02:00
SDL_RWseek(rw, 0, RW_SEEK_SET);
// Validate
return checksum == fileChecksum;
}
bool sawyercoding_read_chunk_safe(SDL_RWops *rw, void *dst, size_t dstLength)
{
// Allocate 16 MB to store uncompressed data
uint8 *tempBuffer = malloc(16 * 1024 * 1024);
size_t uncompressedLength = sawyercoding_read_chunk(rw, tempBuffer);
if (uncompressedLength == SIZE_MAX) {
free(tempBuffer);
return false;
} else {
memcpy(dst, tempBuffer, min(dstLength, uncompressedLength));
free(tempBuffer);
return true;
}
}
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: 0x0067685F
* buffer (esi)
*/
2015-10-12 22:18:14 +02:00
size_t sawyercoding_read_chunk(SDL_RWops* rw, uint8 *buffer)
2014-04-10 01:22:57 +02:00
{
2014-04-10 04:23:12 +02:00
sawyercoding_chunk_header chunkHeader;
2014-04-10 01:22:57 +02:00
2014-04-10 04:23:12 +02:00
// Read chunk header
2015-07-05 06:36:25 +02:00
if (SDL_RWread(rw, &chunkHeader, sizeof(sawyercoding_chunk_header), 1) != 1) {
log_error("Unable to read chunk header!");
2014-05-24 23:14:42 +02:00
return -1;
2014-05-21 00:07:57 +02:00
}
2014-04-10 01:22:57 +02:00
uint8* src_buffer = malloc(chunkHeader.length);
2014-04-10 01:22:57 +02:00
// Read chunk data
2015-07-05 06:36:25 +02:00
if (SDL_RWread(rw, src_buffer, chunkHeader.length, 1) != 1) {
free(src_buffer);
log_error("Unable to read chunk data!");
2014-05-24 23:14:42 +02:00
return -1;
2014-05-21 00:07:57 +02:00
}
2014-04-10 01:22:57 +02:00
// Decode chunk data
2014-04-10 04:23:12 +02:00
switch (chunkHeader.encoding) {
case CHUNK_ENCODING_NONE:
memcpy(buffer, src_buffer, chunkHeader.length);
break;
2014-04-10 01:22:57 +02:00
case CHUNK_ENCODING_RLE:
chunkHeader.length = decode_chunk_rle(src_buffer, buffer, chunkHeader.length);
2014-04-10 01:22:57 +02:00
break;
case CHUNK_ENCODING_RLECOMPRESSED:
chunkHeader.length = decode_chunk_rle(src_buffer, buffer, chunkHeader.length);
2014-04-10 04:23:12 +02:00
chunkHeader.length = decode_chunk_repeat(buffer, chunkHeader.length);
2014-04-10 01:22:57 +02:00
break;
case CHUNK_ENCODING_ROTATE:
memcpy(buffer, src_buffer, chunkHeader.length);
2014-04-10 04:23:12 +02:00
decode_chunk_rotate(buffer, chunkHeader.length);
2014-04-10 01:22:57 +02:00
break;
}
free(src_buffer);
2014-04-10 04:23:12 +02:00
return chunkHeader.length;
2014-04-10 01:22:57 +02:00
}
2016-06-22 22:20:27 +02:00
/**
*
* rct2: 0x0067685F
* buffer (esi)
*/
size_t sawyercoding_read_chunk_with_size(SDL_RWops* rw, uint8 *buffer, const size_t buffer_size)
{
sawyercoding_chunk_header chunkHeader;
// Read chunk header
if (SDL_RWread(rw, &chunkHeader, sizeof(sawyercoding_chunk_header), 1) != 1) {
log_error("Unable to read chunk header!");
return -1;
}
uint8* src_buffer = malloc(chunkHeader.length);
// Read chunk data
if (SDL_RWread(rw, src_buffer, chunkHeader.length, 1) != 1) {
free(src_buffer);
log_error("Unable to read chunk data!");
return -1;
}
// Decode chunk data
switch (chunkHeader.encoding) {
case CHUNK_ENCODING_NONE:
assert(chunkHeader.length <= buffer_size);
memcpy(buffer, src_buffer, chunkHeader.length);
break;
case CHUNK_ENCODING_RLE:
chunkHeader.length = decode_chunk_rle(src_buffer, buffer, chunkHeader.length);
break;
case CHUNK_ENCODING_RLECOMPRESSED:
chunkHeader.length = decode_chunk_rle(src_buffer, buffer, chunkHeader.length);
chunkHeader.length = decode_chunk_repeat(buffer, chunkHeader.length);
break;
case CHUNK_ENCODING_ROTATE:
memcpy(buffer, src_buffer, chunkHeader.length);
decode_chunk_rotate(buffer, chunkHeader.length);
break;
}
free(src_buffer);
return chunkHeader.length;
}
/**
*
* rct2: 0x006762E1
2015-10-12 22:18:14 +02:00
*
*/
2015-10-12 22:18:14 +02:00
size_t sawyercoding_write_chunk_buffer(uint8 *dst_file, uint8* buffer, sawyercoding_chunk_header chunkHeader){
uint8 *encode_buffer, *encode_buffer2;
if (gUseRLE == false) {
if (chunkHeader.encoding == CHUNK_ENCODING_RLE || chunkHeader.encoding == CHUNK_ENCODING_RLECOMPRESSED) {
chunkHeader.encoding = CHUNK_ENCODING_NONE;
}
}
switch (chunkHeader.encoding){
case CHUNK_ENCODING_NONE:
memcpy(dst_file, &chunkHeader, sizeof(sawyercoding_chunk_header));
dst_file += sizeof(sawyercoding_chunk_header);
memcpy(dst_file, buffer, chunkHeader.length);
//fwrite(&chunkHeader, sizeof(sawyercoding_chunk_header), 1, file);
//fwrite(buffer, 1, chunkHeader.length, file);
break;
case CHUNK_ENCODING_RLE:
encode_buffer = malloc(0x600000);
chunkHeader.length = encode_chunk_rle(buffer, encode_buffer, chunkHeader.length);
memcpy(dst_file, &chunkHeader, sizeof(sawyercoding_chunk_header));
dst_file += sizeof(sawyercoding_chunk_header);
memcpy(dst_file, encode_buffer, chunkHeader.length);
free(encode_buffer);
break;
case CHUNK_ENCODING_RLECOMPRESSED:
encode_buffer = malloc(chunkHeader.length * 2);
encode_buffer2 = malloc(0x600000);
chunkHeader.length = encode_chunk_repeat(buffer, encode_buffer, chunkHeader.length);
chunkHeader.length = encode_chunk_rle(encode_buffer, encode_buffer2, chunkHeader.length);
memcpy(dst_file, &chunkHeader, sizeof(sawyercoding_chunk_header));
dst_file += sizeof(sawyercoding_chunk_header);
memcpy(dst_file, encode_buffer2, chunkHeader.length);
free(encode_buffer2);
free(encode_buffer);
break;
case CHUNK_ENCODING_ROTATE:
2015-02-28 00:42:51 +01:00
encode_buffer = malloc(chunkHeader.length);
memcpy(encode_buffer, buffer, chunkHeader.length);
encode_chunk_rotate(encode_buffer, chunkHeader.length);
memcpy(dst_file, &chunkHeader, sizeof(sawyercoding_chunk_header));
dst_file += sizeof(sawyercoding_chunk_header);
2015-02-28 00:42:51 +01:00
memcpy(dst_file, encode_buffer, chunkHeader.length);
free(encode_buffer);
break;
}
return chunkHeader.length + sizeof(sawyercoding_chunk_header);
}
2015-10-12 22:18:14 +02:00
size_t sawyercoding_decode_sv4(const uint8 *src, uint8 *dst, size_t length)
{
// (0 to length - 4): RLE chunk
// (length - 4 to length): checksum
return decode_chunk_rle(src, dst, length - 4);
}
2015-10-12 22:18:14 +02:00
size_t sawyercoding_decode_sc4(const uint8 *src, uint8 *dst, size_t length)
{
2015-10-12 22:18:14 +02:00
size_t decodedLength, i;
uint32 *code;
// Uncompress
decodedLength = decode_chunk_rle(src, dst, length - 4);
// Decode
for (i = 0x60018; i <= min(decodedLength - 1, 0x1F8353); i++)
dst[i] = dst[i] ^ 0x9C;
for (i = 0x60018; i <= min(decodedLength - 1, 0x1F8350); i += 4) {
dst[i + 1] = ror8(dst[i + 1], 3);
code = (uint32*)&dst[i];
*code = rol32(*code, 9);
}
return decodedLength;
}
2015-10-12 22:18:14 +02:00
size_t sawyercoding_encode_sv4(const uint8 *src, uint8 *dst, size_t length)
{
2015-10-12 22:18:14 +02:00
size_t encodedLength;
uint32 checksum;
// Encode
encodedLength = encode_chunk_rle(src, dst, length);
// Append checksum
checksum = sawyercoding_calculate_checksum(dst, encodedLength);
*((uint32*)&dst[encodedLength]) = checksum;
return encodedLength + 4;
}
2015-10-12 22:18:14 +02:00
size_t sawyercoding_decode_td6(const uint8 *src, uint8 *dst, size_t length)
{
return decode_chunk_rle(src, dst, length - 4);
}
2015-10-12 22:18:14 +02:00
size_t sawyercoding_encode_td6(const uint8* src, uint8* dst, size_t length){
size_t output_length = encode_chunk_rle(src, dst, length);
uint32 checksum = 0;
2015-10-12 22:18:14 +02:00
for (size_t i = 0; i < output_length; i++){
uint8 new_byte = ((checksum & 0xFF) + dst[i]) & 0xFF;
checksum = (checksum & 0xFFFFFF00) + new_byte;
checksum = rol32(checksum, 3);
}
checksum -= 0x1D4C1;
*((uint32*)&dst[output_length]) = checksum;
output_length += 4;
return output_length;
}
/* Based off of rct2: 0x006770C1 */
2015-10-12 22:18:14 +02:00
int sawyercoding_validate_track_checksum(const uint8* src, size_t length){
uint32 file_checksum = *((uint32*)&src[length - 4]);
uint32 checksum = 0;
2015-10-12 22:18:14 +02:00
for (size_t i = 0; i < length - 4; i++){
uint8 new_byte = ((checksum & 0xFF) + src[i]) & 0xFF;
checksum = (checksum & 0xFFFFFF00) + new_byte;
checksum = 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)
2015-10-12 22:18:14 +02:00
return 1; // .TD4
else
return 0;
}
#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
*/
2015-10-12 22:18:14 +02:00
static size_t decode_chunk_rle(const uint8* src_buffer, uint8* dst_buffer, size_t length)
2014-04-10 01:22:57 +02:00
{
size_t count;
uint8 *dst, rleCodeByte;
2014-04-10 01:22:57 +02:00
dst = dst_buffer;
2014-04-10 01:22:57 +02:00
2016-05-15 01:10:37 +02:00
for (size_t i = 0; i < length; i++) {
rleCodeByte = src_buffer[i];
2014-04-10 01:22:57 +02:00
if (rleCodeByte & 128) {
i++;
2014-04-10 18:08:41 +02:00
count = 257 - rleCodeByte;
memset(dst, src_buffer[i], count);
dst = (uint8*)((uintptr_t)dst + count);
2014-04-10 01:22:57 +02:00
} else {
memcpy(dst, src_buffer + i + 1, rleCodeByte + 1);
dst = (uint8*)((uintptr_t)dst + rleCodeByte + 1);
i += rleCodeByte + 1;
2014-04-10 01:22:57 +02:00
}
}
2015-10-12 22:18:14 +02:00
2014-04-10 01:22:57 +02:00
// Return final size
2014-09-29 20:07:25 +02:00
return dst - dst_buffer;
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: 0x006769F1
*/
2015-10-12 22:18:14 +02:00
static size_t decode_chunk_repeat(uint8 *buffer, size_t length)
2014-04-10 01:22:57 +02:00
{
2016-05-14 11:54:19 +02:00
size_t i, count;
2015-10-12 22:18:14 +02:00
uint8 *src, *dst, *copyOffset;
2014-04-10 01:22:57 +02:00
// Backup buffer
src = malloc(length);
memcpy(src, buffer, length);
dst = buffer;
for (i = 0; i < length; i++) {
if (src[i] == 0xFF) {
*dst++ = src[++i];
} else {
2014-04-10 18:08:41 +02:00
count = (src[i] & 7) + 1;
2014-04-10 01:22:57 +02:00
copyOffset = dst + (int)(src[i] >> 3) - 32;
2016-05-14 11:54:19 +02:00
memcpy(dst, copyOffset, count);
dst = (uint8*)((uintptr_t)dst + count);
2014-04-10 01:22:57 +02:00
}
}
// Free backup buffer
free(src);
// Return final size
2015-10-12 22:18:14 +02:00
return dst - buffer;
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: 0x006768F4
*/
2015-10-12 22:18:14 +02:00
static void decode_chunk_rotate(uint8 *buffer, size_t length)
2014-04-10 01:22:57 +02:00
{
2015-10-12 22:18:14 +02:00
size_t i;
uint8 code = 1;
2014-04-10 01:22:57 +02:00
for (i = 0; i < length; i++) {
buffer[i] = ror8(buffer[i], code);
code = (code + 2) % 8;
}
}
2014-10-01 22:49:37 +02:00
#pragma endregion
#pragma region Encoding
/**
* Ensure dst_buffer is bigger than src_buffer then resize afterwards
* returns length of dst_buffer
*/
2015-10-12 22:18:14 +02:00
static size_t encode_chunk_rle(const uint8 *src_buffer, uint8 *dst_buffer, size_t length)
{
2015-10-12 22:18:14 +02:00
const uint8* src = src_buffer;
uint8* dst = dst_buffer;
const uint8* end_src = src + length;
uint8 count = 0;
2015-10-12 22:18:14 +02:00
const uint8* src_norm_start = src;
while (src < end_src - 1){
if ((count && *src == src[1]) || count > 125){
*dst++ = count - 1;
2016-05-14 11:54:19 +02:00
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;
2016-05-14 11:54:19 +02:00
memcpy(dst, src_norm_start, count);
dst += count;
src_norm_start += count;
count = 0;
}
return dst - dst_buffer;
}
2015-10-12 22:18:14 +02:00
static size_t encode_chunk_repeat(const uint8 *src_buffer, uint8 *dst_buffer, size_t length)
{
2015-10-12 22:18:14 +02:00
size_t i, j, outLength;
size_t searchIndex, searchEnd, maxRepeatCount;
size_t bestRepeatIndex, bestRepeatCount, repeatIndex, repeatCount;
if (length == 0)
return 0;
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;
2015-11-06 19:50:37 +01:00
// Iterate through remainder of the source buffer
for (i = 1; i < length; ) {
2016-05-14 11:54:19 +02:00
searchIndex = (i < 32) ? 0 : (i - 32);
searchEnd = i - 1;
bestRepeatCount = 0;
for (repeatIndex = searchIndex; repeatIndex <= searchEnd; repeatIndex++) {
repeatCount = 0;
maxRepeatCount = min(min(7, searchEnd - repeatIndex), length - i - 1);
2016-05-14 11:54:19 +02:00
// maxRepeatCount should not exceed length
assert(repeatIndex + maxRepeatCount < length);
assert(i + maxRepeatCount < length);
for (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 {
2015-11-06 19:50:37 +01:00
*dst_buffer++ = (uint8)((bestRepeatCount - 1) | ((32 - (i - bestRepeatIndex)) << 3));
outLength++;
i += bestRepeatCount;
}
}
return outLength;
}
2015-10-12 22:18:14 +02:00
static void encode_chunk_rotate(uint8 *buffer, size_t length)
{
2015-10-12 22:18:14 +02:00
size_t i;
uint8 code = 1;
for (i = 0; i < length; i++) {
buffer[i] = rol8(buffer[i], code);
code = (code + 2) % 8;
}
}
#pragma endregion
2015-10-12 22:18:14 +02:00
int sawyercoding_detect_file_type(const uint8 *src, size_t length)
{
2015-10-12 22:18:14 +02:00
size_t i;
// Currently can't detect TD4, as the checksum is the same as SC4 (need alternative method)
uint32 checksum = *((uint32*)&src[length - 4]);
uint32 actualChecksum = 0;
for (i = 0; i < length - 4; i++) {
actualChecksum = (actualChecksum & 0xFFFFFF00) | (((actualChecksum & 0xFF) + (uint8)src[i]) & 0xFF);
actualChecksum = rol32(actualChecksum, 3);
}
return sawyercoding_detect_rct1_version(checksum - actualChecksum);
}
int sawyercoding_detect_rct1_version(int gameVersion)
{
int fileType = (gameVersion) > 0 ? FILE_TYPE_SV4 : FILE_TYPE_SC4;
gameVersion=abs(gameVersion);
if (gameVersion >= 108000 && gameVersion < 110000)
return (FILE_VERSION_RCT1 | fileType);
else if (gameVersion >= 110000 && gameVersion < 120000)
return (FILE_VERSION_RCT1_AA | fileType);
else if (gameVersion >= 120000 && gameVersion < 130000)
return (FILE_VERSION_RCT1_LL | fileType);
return -1;
2015-10-12 22:18:14 +02:00
}