From afc8943e34ad83507d5dc7f6505c060700f0155a Mon Sep 17 00:00:00 2001 From: Ted John Date: Sat, 4 Feb 2017 00:34:14 +0000 Subject: [PATCH] Fix #5169: Parks containing packed objects fail to open Regression from 3b02b05dc63d0a50f1a1cc10c4a8bfb08e211f8a. Forgot to implement reading packed objects with IStream. --- src/openrct2/object.h | 1 - src/openrct2/object/ObjectRepository.cpp | 63 ++++++++++-------------- src/openrct2/object/ObjectRepository.h | 3 ++ src/openrct2/rct12/SawyerEncoding.cpp | 47 ++++++++++++++++++ src/openrct2/rct12/SawyerEncoding.h | 2 + src/openrct2/rct2/S6Importer.cpp | 4 +- 6 files changed, 81 insertions(+), 39 deletions(-) diff --git a/src/openrct2/object.h b/src/openrct2/object.h index b03361d828..53606e34ab 100644 --- a/src/openrct2/object.h +++ b/src/openrct2/object.h @@ -109,7 +109,6 @@ extern const rct_object_entry_group object_entry_groups[]; void object_list_load(); bool object_read_and_load_entries(SDL_RWops* rw); bool object_load_entries(rct_object_entry* entries); -sint32 object_load_packed(SDL_RWops* rw); bool object_saved_packed(SDL_RWops* rw, const rct_object_entry * entry); bool object_entry_is_empty(const rct_object_entry *entry); diff --git a/src/openrct2/object/ObjectRepository.cpp b/src/openrct2/object/ObjectRepository.cpp index 362c6a2546..10b3153100 100644 --- a/src/openrct2/object/ObjectRepository.cpp +++ b/src/openrct2/object/ObjectRepository.cpp @@ -31,6 +31,7 @@ #include "../core/Stopwatch.hpp" #include "../core/String.hpp" #include "../PlatformEnvironment.h" +#include "../rct12/SawyerEncoding.h" #include "../scenario/ScenarioRepository.h" #include "Object.h" #include "ObjectFactory.h" @@ -223,6 +224,31 @@ public: } } + bool TryExportPackedObject(IStream * stream) override + { + // Check if we already have this object + rct_object_entry entry = stream->ReadValue(); + if (FindObject(&entry) != nullptr) + { + SawyerEncoding::SkipChunk(stream); + } + else + { + // Read object and save to new file + size_t chunkSize; + void * chunk = SawyerEncoding::ReadChunk(stream, &chunkSize); + if (chunk == nullptr) + { + log_error("Failed to reallocate buffer for packed object."); + return false; + } + + AddObject(&entry, chunk, chunkSize); + Memory::Free(chunk); + } + return true; + } + private: void ClearItems() { @@ -735,43 +761,6 @@ extern "C" } } - sint32 object_load_packed(SDL_RWops * rw) - { - IObjectRepository * objRepo = GetObjectRepository(); - - rct_object_entry entry; - SDL_RWread(rw, &entry, 16, 1); - - // Check if we already have this object - if (objRepo->FindObject(&entry) != nullptr) - { - sawyercoding_skip_chunk(rw); - } - else - { - // Read object and save to new file - uint8 * chunk = Memory::Allocate(0x600000); - if (chunk == nullptr) - { - log_error("Failed to allocate buffer for packed object."); - return 0; - } - - size_t chunkSize = sawyercoding_read_chunk_with_size(rw, chunk, 0x600000); - chunk = Memory::Reallocate(chunk, chunkSize); - if (chunk == nullptr) - { - log_error("Failed to reallocate buffer for packed object."); - return 0; - } - - objRepo->AddObject(&entry, chunk, chunkSize); - - Memory::Free(chunk); - } - return 1; - } - bool object_saved_packed(SDL_RWops * rw, const rct_object_entry * entry) { IObjectRepository * objectRepository = GetObjectRepository(); diff --git a/src/openrct2/object/ObjectRepository.h b/src/openrct2/object/ObjectRepository.h index 2e968886b8..7cd23f4758 100644 --- a/src/openrct2/object/ObjectRepository.h +++ b/src/openrct2/object/ObjectRepository.h @@ -29,6 +29,7 @@ extern "C" #ifdef __cplusplus interface IPlatformEnvironment; + interface IStream; class Object; #else typedef struct Object Object; @@ -77,6 +78,8 @@ interface IObjectRepository virtual void AddObject(const rct_object_entry * objectEntry, const void * data, size_t dataSize) abstract; + + virtual bool TryExportPackedObject(IStream * stream) abstract; }; IObjectRepository * CreateObjectRepository(IPlatformEnvironment * env); diff --git a/src/openrct2/rct12/SawyerEncoding.cpp b/src/openrct2/rct12/SawyerEncoding.cpp index 956ff32f17..edc55ce3ab 100644 --- a/src/openrct2/rct12/SawyerEncoding.cpp +++ b/src/openrct2/rct12/SawyerEncoding.cpp @@ -26,6 +26,53 @@ extern "C" namespace SawyerEncoding { + void SkipChunk(IStream * stream) + { + auto header = stream->ReadValue(); + stream->Seek(header.length, STREAM_SEEK_CURRENT); + } + + void * ReadChunk(IStream * stream, size_t * outSize) + { + uint64 originalPosition = stream->GetPosition(); + + auto header = stream->ReadValue(); + switch (header.encoding) { + case CHUNK_ENCODING_NONE: + case CHUNK_ENCODING_RLE: + case CHUNK_ENCODING_RLECOMPRESSED: + case CHUNK_ENCODING_ROTATE: + { + std::unique_ptr compressedData = std::unique_ptr(Memory::Allocate(header.length)); + if (stream->TryRead(compressedData.get(), header.length) != header.length) + { + throw IOException("Corrupt chunk size."); + } + + // Allow 16MiB for chunk data + size_t bufferSize = 16 * 1024 * 1024; + uint8 * buffer = Memory::Allocate(bufferSize); + if (buffer == nullptr) + { + throw Exception("Unable to allocate buffer."); + } + + size_t uncompressedLength = sawyercoding_read_chunk_buffer(buffer, compressedData.get(), header, bufferSize); + buffer = Memory::Reallocate(buffer, uncompressedLength); + if (buffer == nullptr) + { + throw Exception("Unable to reallocate buffer."); + } + + if (outSize != nullptr) *outSize = uncompressedLength; + return buffer; + } + default: + stream->SetPosition(originalPosition); + throw IOException("Invalid chunk encoding."); + } + } + void ReadChunk(void * dst, size_t expectedSize, IStream * stream) { if (!TryReadChunk(dst, expectedSize, stream)) diff --git a/src/openrct2/rct12/SawyerEncoding.h b/src/openrct2/rct12/SawyerEncoding.h index bf25b6824b..10ba234a28 100644 --- a/src/openrct2/rct12/SawyerEncoding.h +++ b/src/openrct2/rct12/SawyerEncoding.h @@ -22,6 +22,8 @@ interface IStream; namespace SawyerEncoding { + void SkipChunk(IStream * stream); + void * ReadChunk(IStream * stream, size_t * outSize); void ReadChunk(void * dst, size_t expectedSize, IStream * stream); void ReadChunkTolerant(void * dst, size_t expectedSize, IStream * stream); bool TryReadChunk(void * dst, size_t expectedSize, IStream * stream); diff --git a/src/openrct2/rct2/S6Importer.cpp b/src/openrct2/rct2/S6Importer.cpp index 6ba9e2ce2d..2042c733f7 100644 --- a/src/openrct2/rct2/S6Importer.cpp +++ b/src/openrct2/rct2/S6Importer.cpp @@ -22,6 +22,7 @@ #include "../core/String.hpp" #include "../management/award.h" #include "../network/network.h" +#include "../object/ObjectRepository.h" #include "../ParkImporter.h" #include "../rct12/SawyerEncoding.h" @@ -130,9 +131,10 @@ public: // Read packed objects // TODO try to contain this more and not store objects until later + IObjectRepository * objectRepo = GetObjectRepository(); for (uint16 i = 0; i < _s6.header.num_packed_objects; i++) { - // object_load_packed(rw); + objectRepo->TryExportPackedObject(stream); } if (isScenario)