From f30a3b315cbdb3a949f0afc025e57a4e70ebfe7d Mon Sep 17 00:00:00 2001 From: Ted John Date: Mon, 30 Jan 2017 22:31:43 +0000 Subject: [PATCH 1/9] Add common load method for S4 importer --- src/openrct2/rct1/S4Importer.cpp | 17 +++++++++++++++++ src/openrct2/rct1/S4Importer.h | 1 + src/openrct2/title/TitleSequencePlayer.cpp | 12 ++---------- 3 files changed, 20 insertions(+), 10 deletions(-) diff --git a/src/openrct2/rct1/S4Importer.cpp b/src/openrct2/rct1/S4Importer.cpp index 389e507cf5..aa840eca83 100644 --- a/src/openrct2/rct1/S4Importer.cpp +++ b/src/openrct2/rct1/S4Importer.cpp @@ -138,6 +138,23 @@ public: _s4Path = path; } + void Load(const utf8 * path) override + { + const utf8 * extension = Path::GetExtension(path); + if (String::Equals(extension, ".sc4")) + { + LoadScenario(path); + } + else if (String::Equals(extension, ".sv4")) + { + LoadSavedGame(path); + } + else + { + throw Exception("Invalid RCT1 park extension."); + } + } + void Import() override { Initialise(); diff --git a/src/openrct2/rct1/S4Importer.h b/src/openrct2/rct1/S4Importer.h index 64bb5df230..91d67965bb 100644 --- a/src/openrct2/rct1/S4Importer.h +++ b/src/openrct2/rct1/S4Importer.h @@ -28,6 +28,7 @@ public: virtual ~IS4Importer() { } virtual void LoadSavedGame(const utf8 * path) abstract; virtual void LoadScenario(const utf8 * path) abstract; + virtual void Load(const utf8 * path) abstract; virtual void Import() abstract; virtual bool GetDetails(scenario_index_entry * dst) abstract; }; diff --git a/src/openrct2/title/TitleSequencePlayer.cpp b/src/openrct2/title/TitleSequencePlayer.cpp index 0505b531ef..49df5c4c75 100644 --- a/src/openrct2/title/TitleSequencePlayer.cpp +++ b/src/openrct2/title/TitleSequencePlayer.cpp @@ -361,16 +361,8 @@ private: { bool isScenario = String::Equals(extension, ".sc4", true); IS4Importer * s4Importer = CreateS4Importer(); - if (isScenario) - { - s4Importer->LoadScenario(path); - s4Importer->Import(); - } - else - { - s4Importer->LoadSavedGame(path); - s4Importer->Import(); - } + s4Importer->Load(path); + s4Importer->Import(); PrepareParkForPlayback(isScenario); success = true; } From 2b045ddb9b870ca7d444a2e63a7a9cb31b52cfce Mon Sep 17 00:00:00 2001 From: Ted John Date: Mon, 30 Jan 2017 22:58:48 +0000 Subject: [PATCH 2/9] Use streams for reading parks in S4 importer --- src/openrct2/rct1.c | 56 ---------------- src/openrct2/rct1.h | 4 -- src/openrct2/rct1/S4Importer.cpp | 68 ++++++++++++++------ src/openrct2/rct1/S4Importer.h | 7 +- src/openrct2/scenario/ScenarioRepository.cpp | 16 +++-- 5 files changed, 63 insertions(+), 88 deletions(-) diff --git a/src/openrct2/rct1.c b/src/openrct2/rct1.c index 3efc42f7a5..087dbfc049 100644 --- a/src/openrct2/rct1.c +++ b/src/openrct2/rct1.c @@ -21,62 +21,6 @@ #include "util/sawyercoding.h" #include "util/util.h" -bool rct1_read_sc4(const char *path, rct1_s4 *s4) -{ - uint8 *buffer, *decodedBuffer; - size_t length, decodedLength; - bool success; - - if (!readentirefile(path, (void**)&buffer, &length)) { - gErrorType = ERROR_TYPE_FILE_LOAD; - gErrorStringId = STR_FILE_CONTAINS_INVALID_DATA; - return 0; - } - - sint32 fileType = sawyercoding_detect_file_type(buffer, length); - - decodedBuffer = malloc(sizeof(rct1_s4)); - decodedLength = (fileType & FILE_VERSION_MASK) == FILE_VERSION_RCT1 ? - sawyercoding_decode_sv4(buffer, decodedBuffer, length, sizeof(rct1_s4)) : - sawyercoding_decode_sc4(buffer, decodedBuffer, length, sizeof(rct1_s4)); - if (decodedLength == sizeof(rct1_s4)) { - memcpy(s4, decodedBuffer, sizeof(rct1_s4)); - success = true; - } else { - success = false; - } - - free(buffer); - free(decodedBuffer); - return success; -} - -bool rct1_read_sv4(const char *path, rct1_s4 *s4) -{ - uint8 *buffer, *decodedBuffer; - size_t length, decodedLength; - bool success; - - if (!readentirefile(path, (void**)&buffer, &length)) { - gErrorType = ERROR_TYPE_FILE_LOAD; - gErrorStringId = STR_FILE_CONTAINS_INVALID_DATA; - return 0; - } - - decodedBuffer = malloc(sizeof(rct1_s4)); - decodedLength = sawyercoding_decode_sv4(buffer, decodedBuffer, length, sizeof(rct1_s4)); - if (decodedLength == sizeof(rct1_s4)) { - memcpy(s4, decodedBuffer, sizeof(rct1_s4)); - success = true; - } else { - success = false; - } - - free(buffer); - free(decodedBuffer); - return success; -} - bool rideTypeShouldLoseSeparateFlag(const rct_ride_entry *rideEntry) { if (!gConfigInterface.select_by_track_type) { diff --git a/src/openrct2/rct1.h b/src/openrct2/rct1.h index d67e1fc677..da53723295 100644 --- a/src/openrct2/rct1.h +++ b/src/openrct2/rct1.h @@ -1189,10 +1189,6 @@ enum { extern const uint8 gRideCategories[0x60]; -bool rct1_read_sc4(const char *path, rct1_s4 *s4); -bool rct1_read_sv4(const char *path, rct1_s4 *s4); -void rct1_import_s4(rct1_s4 *s4); -void rct1_fix_landscape(); sint32 vehicle_preference_compare(uint8 rideType, const char * a, const char * b); bool rideTypeShouldLoseSeparateFlag(const rct_ride_entry *rideEntry); diff --git a/src/openrct2/rct1/S4Importer.cpp b/src/openrct2/rct1/S4Importer.cpp index aa840eca83..6e497d3e4f 100644 --- a/src/openrct2/rct1/S4Importer.cpp +++ b/src/openrct2/rct1/S4Importer.cpp @@ -14,11 +14,14 @@ *****************************************************************************/ #pragma endregion +#include #include #include "../core/Collections.hpp" #include "../core/Console.hpp" #include "../core/Exception.hpp" +#include "../core/FileStream.hpp" #include "../core/Guard.hpp" +#include "../core/IStream.hpp" #include "../core/Memory.hpp" #include "../core/Path.hpp" #include "../core/String.hpp" @@ -91,9 +94,9 @@ public: class S4Importer final : public IS4Importer { private: - const utf8 * _s4Path; - rct1_s4 _s4; - uint8 _gameVersion; + const utf8 * _s4Path = nullptr; + rct1_s4 _s4 = { 0 }; + uint8 _gameVersion = 0; // Lists of dynamic object entries EntryList _rideEntries; @@ -120,24 +123,6 @@ private: uint8 _researchRideTypeUsed[128]; public: - void LoadSavedGame(const utf8 * path) override - { - if (!rct1_read_sv4(path, &_s4)) - { - throw Exception("Unable to load SV4."); - } - _s4Path = path; - } - - void LoadScenario(const utf8 * path) override - { - if (!rct1_read_sc4(path, &_s4)) - { - throw Exception("Unable to load SC4."); - } - _s4Path = path; - } - void Load(const utf8 * path) override { const utf8 * extension = Path::GetExtension(path); @@ -155,6 +140,47 @@ public: } } + void LoadSavedGame(const utf8 * path) override + { + auto fs = FileStream(path, FILE_MODE_OPEN); + LoadFromStream(&fs, false); + _s4Path = path; + } + + void LoadScenario(const utf8 * path) override + { + auto fs = FileStream(path, FILE_MODE_OPEN); + LoadFromStream(&fs, true); + _s4Path = path; + } + + void LoadFromStream(IStream * stream, bool isScenario) override + { + size_t dataSize = stream->GetLength() - stream->GetPosition(); + std::unique_ptr data = std::unique_ptr(stream->ReadArray(dataSize)); + std::unique_ptr decodedData = std::unique_ptr(Memory::Allocate(sizeof(rct1_s4))); + + size_t decodedSize; + if (isScenario) + { + decodedSize = sawyercoding_decode_sc4(data.get(), decodedData.get(), dataSize, sizeof(rct1_s4)); + } + else + { + decodedSize = sawyercoding_decode_sv4(data.get(), decodedData.get(), dataSize, sizeof(rct1_s4)); + } + + if (decodedSize == sizeof(rct1_s4)) + { + Memory::Copy(&_s4, decodedData.get(), sizeof(rct1_s4)); + _s4Path = ""; + } + else + { + throw Exception("Unable to decode park."); + } + } + void Import() override { Initialise(); diff --git a/src/openrct2/rct1/S4Importer.h b/src/openrct2/rct1/S4Importer.h index 91d67965bb..78a7d95037 100644 --- a/src/openrct2/rct1/S4Importer.h +++ b/src/openrct2/rct1/S4Importer.h @@ -19,16 +19,19 @@ #include "../common.h" #include "../scenario/ScenarioRepository.h" +interface IStream; + /** * Interface to import RollerCoaster Tycoon 1 scenarios (*.SC4) and saved games (*.SV4). */ interface IS4Importer { public: - virtual ~IS4Importer() { } + virtual ~IS4Importer() = default; + virtual void Load(const utf8 * path) abstract; virtual void LoadSavedGame(const utf8 * path) abstract; virtual void LoadScenario(const utf8 * path) abstract; - virtual void Load(const utf8 * path) abstract; + virtual void LoadFromStream(IStream * stream, bool isScenario) abstract; virtual void Import() abstract; virtual bool GetDetails(scenario_index_entry * dst) abstract; }; diff --git a/src/openrct2/scenario/ScenarioRepository.cpp b/src/openrct2/scenario/ScenarioRepository.cpp index c37881ec27..430ab503ac 100644 --- a/src/openrct2/scenario/ScenarioRepository.cpp +++ b/src/openrct2/scenario/ScenarioRepository.cpp @@ -317,12 +317,18 @@ private: // RCT1 scenario bool result = false; IS4Importer * s4Importer = CreateS4Importer(); - s4Importer->LoadScenario(path.c_str()); - if (s4Importer->GetDetails(entry)) + try + { + s4Importer->LoadScenario(path.c_str()); + if (s4Importer->GetDetails(entry)) + { + String::Set(entry->path, sizeof(entry->path), path.c_str()); + entry->timestamp = timestamp; + result = true; + } + } + catch (Exception) { - String::Set(entry->path, sizeof(entry->path), path.c_str()); - entry->timestamp = timestamp; - result = true; } delete s4Importer; return result; From 5c1f2f4c43a61e25395232c99cf0443aee8075fb Mon Sep 17 00:00:00 2001 From: Ted John Date: Mon, 30 Jan 2017 23:08:04 +0000 Subject: [PATCH 3/9] Replace IS4Importer with IParkImporter --- src/openrct2/{rct1/S4Importer.h => ParkImporter.h} | 13 +++++++------ src/openrct2/cmdline/ConvertCommand.cpp | 2 +- src/openrct2/libopenrct2.vcxproj | 2 +- src/openrct2/rct1/S4Importer.cpp | 10 +++++----- src/openrct2/scenario/ScenarioRepository.cpp | 4 ++-- src/openrct2/title/TitleSequencePlayer.cpp | 4 ++-- 6 files changed, 18 insertions(+), 17 deletions(-) rename src/openrct2/{rct1/S4Importer.h => ParkImporter.h} (82%) diff --git a/src/openrct2/rct1/S4Importer.h b/src/openrct2/ParkImporter.h similarity index 82% rename from src/openrct2/rct1/S4Importer.h rename to src/openrct2/ParkImporter.h index 78a7d95037..5a3a495b46 100644 --- a/src/openrct2/rct1/S4Importer.h +++ b/src/openrct2/ParkImporter.h @@ -16,18 +16,18 @@ #pragma once -#include "../common.h" -#include "../scenario/ScenarioRepository.h" +#include "common.h" +#include "scenario/ScenarioRepository.h" interface IStream; /** - * Interface to import RollerCoaster Tycoon 1 scenarios (*.SC4) and saved games (*.SV4). + * Interface to import scenarios and saved games. */ -interface IS4Importer +interface IParkImporter { public: - virtual ~IS4Importer() = default; + virtual ~IParkImporter() = default; virtual void Load(const utf8 * path) abstract; virtual void LoadSavedGame(const utf8 * path) abstract; virtual void LoadScenario(const utf8 * path) abstract; @@ -36,4 +36,5 @@ public: virtual bool GetDetails(scenario_index_entry * dst) abstract; }; -IS4Importer * CreateS4Importer(); +IParkImporter * CreateS4Importer(); +IParkImporter * CreateS6Importer(); diff --git a/src/openrct2/cmdline/ConvertCommand.cpp b/src/openrct2/cmdline/ConvertCommand.cpp index 77d86057d5..8ea4ca937e 100644 --- a/src/openrct2/cmdline/ConvertCommand.cpp +++ b/src/openrct2/cmdline/ConvertCommand.cpp @@ -19,7 +19,7 @@ #include "../core/Exception.hpp" #include "../core/Guard.hpp" #include "../core/Path.hpp" -#include "../rct1/S4Importer.h" +#include "../ParkImporter.h" #include "CommandLine.hpp" extern "C" diff --git a/src/openrct2/libopenrct2.vcxproj b/src/openrct2/libopenrct2.vcxproj index 9cf193573e..d9c83a17b6 100644 --- a/src/openrct2/libopenrct2.vcxproj +++ b/src/openrct2/libopenrct2.vcxproj @@ -415,6 +415,7 @@ + @@ -543,7 +544,6 @@ - diff --git a/src/openrct2/rct1/S4Importer.cpp b/src/openrct2/rct1/S4Importer.cpp index 6e497d3e4f..11026d6860 100644 --- a/src/openrct2/rct1/S4Importer.cpp +++ b/src/openrct2/rct1/S4Importer.cpp @@ -27,8 +27,8 @@ #include "../core/String.hpp" #include "../core/Util.hpp" #include "../object/ObjectManager.h" +#include "../ParkImporter.h" #include "../scenario/ScenarioSources.h" -#include "S4Importer.h" #include "Tables.h" extern "C" @@ -91,7 +91,7 @@ public: } }; -class S4Importer final : public IS4Importer +class S4Importer final : public IParkImporter { private: const utf8 * _s4Path = nullptr; @@ -126,11 +126,11 @@ public: void Load(const utf8 * path) override { const utf8 * extension = Path::GetExtension(path); - if (String::Equals(extension, ".sc4")) + if (String::Equals(extension, ".sc4", true)) { LoadScenario(path); } - else if (String::Equals(extension, ".sv4")) + else if (String::Equals(extension, ".sv4", true)) { LoadSavedGame(path); } @@ -2479,7 +2479,7 @@ private: } }; -IS4Importer * CreateS4Importer() +IParkImporter * CreateS4Importer() { return new S4Importer(); } diff --git a/src/openrct2/scenario/ScenarioRepository.cpp b/src/openrct2/scenario/ScenarioRepository.cpp index 430ab503ac..66273a1b88 100644 --- a/src/openrct2/scenario/ScenarioRepository.cpp +++ b/src/openrct2/scenario/ScenarioRepository.cpp @@ -24,8 +24,8 @@ #include "../core/Path.hpp" #include "../core/String.hpp" #include "../core/Util.hpp" +#include "../ParkImporter.h" #include "../PlatformEnvironment.h" -#include "../rct1/S4Importer.h" #include "../rct12/SawyerEncoding.h" #include "ScenarioRepository.h" #include "ScenarioSources.h" @@ -316,7 +316,7 @@ private: { // RCT1 scenario bool result = false; - IS4Importer * s4Importer = CreateS4Importer(); + IParkImporter * s4Importer = CreateS4Importer(); try { s4Importer->LoadScenario(path.c_str()); diff --git a/src/openrct2/title/TitleSequencePlayer.cpp b/src/openrct2/title/TitleSequencePlayer.cpp index 49df5c4c75..915f6a8951 100644 --- a/src/openrct2/title/TitleSequencePlayer.cpp +++ b/src/openrct2/title/TitleSequencePlayer.cpp @@ -22,7 +22,7 @@ #include "../core/Math.hpp" #include "../core/Path.hpp" #include "../core/String.hpp" -#include "../rct1/S4Importer.h" +#include "../ParkImporter.h" #include "../scenario/ScenarioRepository.h" #include "../scenario/ScenarioSources.h" #include "TitleSequence.h" @@ -360,7 +360,7 @@ private: try { bool isScenario = String::Equals(extension, ".sc4", true); - IS4Importer * s4Importer = CreateS4Importer(); + IParkImporter * s4Importer = CreateS4Importer(); s4Importer->Load(path); s4Importer->Import(); PrepareParkForPlayback(isScenario); From 8998b2ae1858f3aa3db10ebbb7996f134741c4eb Mon Sep 17 00:00:00 2001 From: Ted John Date: Mon, 30 Jan 2017 23:56:10 +0000 Subject: [PATCH 4/9] Refactor S6 importer to use IParkImporter interface --- src/openrct2/core/FileStream.hpp | 20 + src/openrct2/libopenrct2.vcxproj | 1 - src/openrct2/rct1/S4Importer.cpp | 3 +- src/openrct2/rct12/SawyerEncoding.cpp | 43 ++ src/openrct2/rct12/SawyerEncoding.h | 2 + src/openrct2/rct2/S6Importer.cpp | 665 +++++++++++++------------- src/openrct2/rct2/S6Importer.h | 48 -- 7 files changed, 407 insertions(+), 375 deletions(-) delete mode 100644 src/openrct2/rct2/S6Importer.h diff --git a/src/openrct2/core/FileStream.hpp b/src/openrct2/core/FileStream.hpp index f9a0660e0b..de10f7857e 100644 --- a/src/openrct2/core/FileStream.hpp +++ b/src/openrct2/core/FileStream.hpp @@ -73,6 +73,26 @@ public: _fileSize = SDL_RWsize(_file); } + FileStream(SDL_RWops * ops, sint32 fileMode) + { + _file = ops; + switch (fileMode) { + case FILE_MODE_OPEN: + _canRead = true; + _canWrite = false; + break; + case FILE_MODE_WRITE: + _canRead = false; + _canWrite = true; + break; + default: + throw; + } + + _disposed = false; + _fileSize = SDL_RWsize(_file); + } + ~FileStream() { if (!_disposed) diff --git a/src/openrct2/libopenrct2.vcxproj b/src/openrct2/libopenrct2.vcxproj index d9c83a17b6..06650fd1a9 100644 --- a/src/openrct2/libopenrct2.vcxproj +++ b/src/openrct2/libopenrct2.vcxproj @@ -547,7 +547,6 @@ - diff --git a/src/openrct2/rct1/S4Importer.cpp b/src/openrct2/rct1/S4Importer.cpp index 11026d6860..7738f3ef2b 100644 --- a/src/openrct2/rct1/S4Importer.cpp +++ b/src/openrct2/rct1/S4Importer.cpp @@ -161,7 +161,8 @@ public: std::unique_ptr decodedData = std::unique_ptr(Memory::Allocate(sizeof(rct1_s4))); size_t decodedSize; - if (isScenario) + sint32 fileType = sawyercoding_detect_file_type(data.get(), dataSize); + if (isScenario && (fileType & FILE_VERSION_MASK) != FILE_VERSION_RCT1) { decodedSize = sawyercoding_decode_sc4(data.get(), decodedData.get(), dataSize, sizeof(rct1_s4)); } diff --git a/src/openrct2/rct12/SawyerEncoding.cpp b/src/openrct2/rct12/SawyerEncoding.cpp index 68971a2b59..d1b161010e 100644 --- a/src/openrct2/rct12/SawyerEncoding.cpp +++ b/src/openrct2/rct12/SawyerEncoding.cpp @@ -14,7 +14,9 @@ *****************************************************************************/ #pragma endregion +#include #include "../core/IStream.hpp" +#include "../core/Math.hpp" #include "SawyerEncoding.h" extern "C" @@ -24,6 +26,47 @@ extern "C" namespace SawyerEncoding { + void ReadChunk(void * dst, size_t expectedSize, IStream * stream) + { + if (!TryReadChunk(dst, expectedSize, stream)) + { + throw IOException("Invalid or incorrect chunk size."); + } + } + + void ReadChunkTolerant(void * dst, size_t expectedSize, IStream * stream) + { + 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; + std::unique_ptr buffer = std::unique_ptr(Memory::Allocate(bufferSize)); + size_t uncompressedLength = sawyercoding_read_chunk_buffer(buffer.get(), compressedData.get(), header, bufferSize); + size_t copyLength = Math::Min(uncompressedLength, expectedSize); + + Memory::Set(dst, 0, expectedSize); + Memory::Copy(dst, buffer.get(), copyLength); + break; + } + default: + stream->SetPosition(originalPosition); + throw IOException("Invalid chunk encoding."); + } + } + bool TryReadChunk(void * dst, size_t expectedSize, IStream * stream) { uint64 originalPosition = stream->GetPosition(); diff --git a/src/openrct2/rct12/SawyerEncoding.h b/src/openrct2/rct12/SawyerEncoding.h index 188ebed2db..ace15db533 100644 --- a/src/openrct2/rct12/SawyerEncoding.h +++ b/src/openrct2/rct12/SawyerEncoding.h @@ -22,6 +22,8 @@ interface IStream; namespace SawyerEncoding { + 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); template diff --git a/src/openrct2/rct2/S6Importer.cpp b/src/openrct2/rct2/S6Importer.cpp index dddb46ed79..b0f74d93d3 100644 --- a/src/openrct2/rct2/S6Importer.cpp +++ b/src/openrct2/rct2/S6Importer.cpp @@ -15,10 +15,14 @@ #pragma endregion #include "../core/Exception.hpp" +#include "../core/FileStream.hpp" #include "../core/IStream.hpp" +#include "../core/Path.hpp" +#include "../core/String.hpp" #include "../management/award.h" #include "../network/network.h" -#include "S6Importer.h" +#include "../ParkImporter.h" +#include "../rct12/SawyerEncoding.h" extern "C" { @@ -50,350 +54,358 @@ public: explicit ObjectLoadException(const char * message) : Exception(message) { } }; -S6Importer::S6Importer() +/** + * Class to import RollerCoaster Tycoon 2 scenarios (*.SC6) and saved games (*.SV6). + */ +class S6Importer final : public IParkImporter { - FixIssues = false; - memset(&_s6, 0, sizeof(_s6)); -} +private: + const utf8 * _s6Path = nullptr; + rct_s6_data _s6 = { 0 }; + uint8 _gameVersion = 0; -void S6Importer::LoadSavedGame(const utf8 * path) -{ - SDL_RWops * rw = SDL_RWFromFile(path, "rb"); - if (rw == nullptr) +public: + void Load(const utf8 * path) override { - throw IOException("Unable to open SV6."); + const utf8 * extension = Path::GetExtension(path); + if (String::Equals(extension, ".sc6", true)) + { + LoadScenario(path); + } + else if (String::Equals(extension, ".sv6", true)) + { + LoadSavedGame(path); + } + else + { + throw Exception("Invalid RCT2 park extension."); + } } - if (!sawyercoding_validate_checksum(rw)) + void LoadSavedGame(const utf8 * path) override { - gErrorType = ERROR_TYPE_FILE_LOAD; - gGameCommandErrorTitle = STR_FILE_CONTAINS_INVALID_DATA; + // if (!sawyercoding_validate_checksum(rw)) + // { + // gErrorType = ERROR_TYPE_FILE_LOAD; + // gGameCommandErrorTitle = STR_FILE_CONTAINS_INVALID_DATA; + // + // log_error("failed to load saved game, invalid checksum"); + // throw IOException("Invalid SV6 checksum."); + // } - log_error("failed to load saved game, invalid checksum"); - throw IOException("Invalid SV6 checksum."); + auto fs = FileStream(path, FILE_MODE_OPEN); + LoadFromStream(&fs, false); + _s6Path = path; } - LoadSavedGame(rw); - - SDL_RWclose(rw); - - _s6Path = path; -} - -void S6Importer::LoadScenario(const utf8 * path) -{ - SDL_RWops * rw = SDL_RWFromFile(path, "rb"); - if (rw == nullptr) + void LoadScenario(const utf8 * path) override { - throw IOException("Unable to open SV6."); + // if (!gConfigGeneral.allow_loading_with_incorrect_checksum && !sawyercoding_validate_checksum(rw)) + // { + // SDL_RWclose(rw); + // + // gErrorType = ERROR_TYPE_FILE_LOAD; + // gErrorStringId = STR_FILE_CONTAINS_INVALID_DATA; + // + // log_error("failed to load scenario, invalid checksum"); + // throw IOException("Invalid SC6 checksum."); + // } + + auto fs = FileStream(path, FILE_MODE_OPEN); + LoadFromStream(&fs, true); + _s6Path = path; } - if (!gConfigGeneral.allow_loading_with_incorrect_checksum && !sawyercoding_validate_checksum(rw)) + void LoadFromStream(IStream * stream, bool isScenario) { - SDL_RWclose(rw); + SawyerEncoding::ReadChunkTolerant(&_s6.header, sizeof(_s6.header), stream); - gErrorType = ERROR_TYPE_FILE_LOAD; - gErrorStringId = STR_FILE_CONTAINS_INVALID_DATA; + log_verbose("saved game classic_flag = 0x%02x\n", _s6.header.classic_flag); + if (isScenario) + { + if (_s6.header.type != S6_TYPE_SCENARIO) + { + throw Exception("Park is not a scenario."); + } + } + else + { + if (_s6.header.type != S6_TYPE_SAVEDGAME) + { + throw Exception("Park is not a saved game."); + } + } - log_error("failed to load scenario, invalid checksum"); - throw IOException("Invalid SC6 checksum."); + SawyerEncoding::ReadChunkTolerant(&_s6.info, sizeof(_s6.info), stream); + + // Read packed objects + // TODO try to contain this more and not store objects until later + for (uint16 i = 0; i < _s6.header.num_packed_objects; i++) + { + // object_load_packed(rw); + } + + if (isScenario) + { + SawyerEncoding::ReadChunkTolerant(&_s6.objects, sizeof(_s6.objects), stream); + SawyerEncoding::ReadChunkTolerant(&_s6.elapsed_months, 16, stream); + SawyerEncoding::ReadChunkTolerant(&_s6.map_elements, sizeof(_s6.map_elements), stream); + SawyerEncoding::ReadChunkTolerant(&_s6.next_free_map_element_pointer_index, 2560076, stream); + SawyerEncoding::ReadChunkTolerant(&_s6.guests_in_park, 4, stream); + SawyerEncoding::ReadChunkTolerant(&_s6.last_guests_in_park, 8, stream); + SawyerEncoding::ReadChunkTolerant(&_s6.park_rating, 2, stream); + SawyerEncoding::ReadChunkTolerant(&_s6.active_research_types, 1082, stream); + SawyerEncoding::ReadChunkTolerant(&_s6.current_expenditure, 16, stream); + SawyerEncoding::ReadChunkTolerant(&_s6.park_value, 4, stream); + SawyerEncoding::ReadChunkTolerant(&_s6.completed_company_value, 483816, stream); + } + else + { + SawyerEncoding::ReadChunkTolerant(&_s6.objects, sizeof(_s6.objects), stream); + SawyerEncoding::ReadChunkTolerant(&_s6.elapsed_months, 16, stream); + SawyerEncoding::ReadChunkTolerant(&_s6.map_elements, sizeof(_s6.map_elements), stream); + SawyerEncoding::ReadChunkTolerant(&_s6.next_free_map_element_pointer_index, 3048816, stream); + } } - LoadScenario(rw); - - SDL_RWclose(rw); - - _s6Path = path; -} - -void S6Importer::LoadSavedGame(SDL_RWops *rw) -{ - sawyercoding_read_chunk_safe(rw, &_s6.header, sizeof(_s6.header)); - if (_s6.header.type != S6_TYPE_SAVEDGAME) + bool GetDetails(scenario_index_entry * dst) override { - throw Exception("Data is not a saved game."); - } - log_verbose("saved game classic_flag = 0x%02x\n", _s6.header.classic_flag); - - // Read packed objects - // TODO try to contain this more and not store objects until later - for (uint16 i = 0; i < _s6.header.num_packed_objects; i++) - { - object_load_packed(rw); + Memory::Set(dst, 0, sizeof(scenario_index_entry)); + return false; } - sawyercoding_read_chunk_safe(rw, &_s6.objects, sizeof(_s6.objects)); - sawyercoding_read_chunk_safe(rw, &_s6.elapsed_months, 16); - sawyercoding_read_chunk_safe(rw, &_s6.map_elements, sizeof(_s6.map_elements)); - sawyercoding_read_chunk_safe(rw, &_s6.next_free_map_element_pointer_index, 3048816); -} - -void S6Importer::LoadScenario(SDL_RWops *rw) -{ - sawyercoding_read_chunk_safe(rw, &_s6.header, sizeof(_s6.header)); - if (_s6.header.type != S6_TYPE_SCENARIO) + void Import() { - throw Exception("Data is not a scenario."); - } - log_verbose("scenario classic_flag = 0x%02x\n", _s6.header.classic_flag); + Initialise(); - sawyercoding_read_chunk_safe(rw, &_s6.info, sizeof(_s6.info)); + // _s6.header + gS6Info = _s6.info; - // Read packed objects - // TODO try to contain this more and not store objects until later - for (uint16 i = 0; i < _s6.header.num_packed_objects; i++) - { - object_load_packed(rw); + gDateMonthsElapsed = _s6.elapsed_months; + gDateMonthTicks = _s6.current_day; + gScenarioTicks = _s6.scenario_ticks; + gScenarioSrand0 = _s6.scenario_srand_0; + gScenarioSrand1 = _s6.scenario_srand_1; + + memcpy(gMapElements, _s6.map_elements, sizeof(_s6.map_elements)); + + gNextFreeMapElementPointerIndex = _s6.next_free_map_element_pointer_index; + for (sint32 i = 0; i < MAX_SPRITES; i++) + { + memcpy(get_sprite(i), &_s6.sprites[i], sizeof(rct_sprite)); + } + + for (sint32 i = 0; i < NUM_SPRITE_LISTS; i++) + { + gSpriteListHead[i] = _s6.sprite_lists_head[i]; + gSpriteListCount[i] = _s6.sprite_lists_count[i]; + } + gParkName = _s6.park_name; + // pad_013573D6 + gParkNameArgs = _s6.park_name_args; + gInitialCash = _s6.initial_cash; + gBankLoan = _s6.current_loan; + gParkFlags = _s6.park_flags; + gParkEntranceFee = _s6.park_entrance_fee; + // rct1_park_entrance_x + // rct1_park_entrance_y + // pad_013573EE + // rct1_park_entrance_z + memcpy(gPeepSpawns, _s6.peep_spawns, sizeof(_s6.peep_spawns)); + gGuestChangeModifier = _s6.guest_count_change_modifier; + gResearchFundingLevel = _s6.current_research_level; + // pad_01357400 + memcpy(gResearchedRideTypes, _s6.researched_ride_types, sizeof(_s6.researched_ride_types)); + memcpy(gResearchedRideEntries, _s6.researched_ride_entries, sizeof(_s6.researched_ride_entries)); + memcpy(gResearchedTrackTypesA, _s6.researched_track_types_a, sizeof(_s6.researched_track_types_a)); + memcpy(gResearchedTrackTypesB, _s6.researched_track_types_b, sizeof(_s6.researched_track_types_b)); + + gNumGuestsInPark = _s6.guests_in_park; + gNumGuestsHeadingForPark = _s6.guests_heading_for_park; + + memcpy(gExpenditureTable, _s6.expenditure_table, sizeof(_s6.expenditure_table)); + + gNumGuestsInParkLastWeek = _s6.last_guests_in_park; + // pad_01357BCA + gStaffHandymanColour = _s6.handyman_colour; + gStaffMechanicColour = _s6.mechanic_colour; + gStaffSecurityColour = _s6.security_colour; + + memcpy(gResearchedSceneryItems, _s6.researched_scenery_items, sizeof(_s6.researched_scenery_items)); + + gParkRating = _s6.park_rating; + + memcpy(gParkRatingHistory, _s6.park_rating_history, sizeof(_s6.park_rating_history)); + memcpy(gGuestsInParkHistory, _s6.guests_in_park_history, sizeof(_s6.guests_in_park_history)); + + gResearchPriorities = _s6.active_research_types; + gResearchProgressStage = _s6.research_progress_stage; + gResearchLastItemSubject = _s6.last_researched_item_subject; + // pad_01357CF8 + gResearchNextItem = _s6.next_research_item; + gResearchProgress = _s6.research_progress; + gResearchNextCategory = _s6.next_research_category; + gResearchExpectedDay = _s6.next_research_expected_day; + gResearchExpectedMonth = _s6.next_research_expected_month; + gGuestInitialHappiness = _s6.guest_initial_happiness; + gParkSize = _s6.park_size; + _guestGenerationProbability = _s6.guest_generation_probability; + gTotalRideValue = _s6.total_ride_value; + gMaxBankLoan = _s6.maximum_loan; + gGuestInitialCash = _s6.guest_initial_cash; + gGuestInitialHunger = _s6.guest_initial_hunger; + gGuestInitialThirst = _s6.guest_initial_thirst; + gScenarioObjectiveType = _s6.objective_type; + gScenarioObjectiveYear = _s6.objective_year; + // pad_013580FA + gScenarioObjectiveCurrency = _s6.objective_currency; + gScenarioObjectiveNumGuests = _s6.objective_guests; + memcpy(gMarketingCampaignDaysLeft, _s6.campaign_weeks_left, sizeof(_s6.campaign_weeks_left)); + memcpy(gMarketingCampaignRideIndex, _s6.campaign_ride_index, sizeof(_s6.campaign_ride_index)); + + memcpy(gCashHistory, _s6.balance_history, sizeof(_s6.balance_history)); + + gCurrentExpenditure = _s6.current_expenditure; + gCurrentProfit = _s6.current_profit; + gWeeklyProfitAverageDividend = _s6.weekly_profit_average_dividend; + gWeeklyProfitAverageDivisor = _s6.weekly_profit_average_divisor; + // pad_0135833A + + memcpy(gWeeklyProfitHistory, _s6.weekly_profit_history, sizeof(_s6.weekly_profit_history)); + + gParkValue = _s6.park_value; + + memcpy(gParkValueHistory, _s6.park_value_history, sizeof(_s6.park_value_history)); + + gScenarioCompletedCompanyValue = _s6.completed_company_value; + gTotalAdmissions = _s6.total_admissions; + gTotalIncomeFromAdmissions = _s6.income_from_admissions; + gCompanyValue = _s6.company_value; + memcpy(gPeepWarningThrottle, _s6.peep_warning_throttle, sizeof(_s6.peep_warning_throttle)); + + // Awards + for (sint32 i = 0; i < RCT12_MAX_AWARDS; i++) + { + rct12_award * src = &_s6.awards[i]; + Award * dst = &gCurrentAwards[i]; + dst->Time = src->time; + dst->Type = src->type; + } + + gLandPrice = _s6.land_price; + gConstructionRightsPrice = _s6.construction_rights_price; + // unk_01358774 + // pad_01358776 + // _s6.cd_key + _gameVersion = _s6.game_version_number; + gScenarioCompanyValueRecord = _s6.completed_company_value_record; + // _s6.loan_hash; + gRideCount = _s6.ride_count; + // pad_013587CA + gHistoricalProfit = _s6.historical_profit; + // pad_013587D4 + memcpy(gScenarioCompletedBy, _s6.scenario_completed_name, sizeof(_s6.scenario_completed_name)); + gCashEncrypted = _s6.cash; + // pad_013587FC + gParkRatingCasualtyPenalty = _s6.park_rating_casualty_penalty; + gMapSizeUnits = _s6.map_size_units; + gMapSizeMinus2 = _s6.map_size_minus_2; + gMapSize = _s6.map_size; + gMapSizeMaxXY = _s6.map_max_xy; + gSamePriceThroughoutParkA = _s6.same_price_throughout; + _suggestedGuestMaximum = _s6.suggested_max_guests; + gScenarioParkRatingWarningDays = _s6.park_rating_warning_days; + gLastEntranceStyle = _s6.last_entrance_style; + // rct1_water_colour + // pad_01358842 + memcpy(gResearchItems, _s6.research_items, sizeof(_s6.research_items)); + gMapBaseZ = _s6.map_base_z; + memcpy(gScenarioName, _s6.scenario_name, sizeof(_s6.scenario_name)); + memcpy(gScenarioDetails, _s6.scenario_description, sizeof(_s6.scenario_description)); + gBankLoanInterestRate = _s6.current_interest_rate; + // pad_0135934B + gSamePriceThroughoutParkB = _s6.same_price_throughout_extended; + memcpy(gParkEntranceX, _s6.park_entrance_x, sizeof(_s6.park_entrance_x)); + memcpy(gParkEntranceY, _s6.park_entrance_y, sizeof(_s6.park_entrance_y)); + memcpy(gParkEntranceZ, _s6.park_entrance_z, sizeof(_s6.park_entrance_z)); + memcpy(gParkEntranceDirection, _s6.park_entrance_direction, sizeof(_s6.park_entrance_direction)); + scenario_set_filename(_s6.scenario_filename); + memcpy(gScenarioExpansionPacks, _s6.saved_expansion_pack_names, sizeof(_s6.saved_expansion_pack_names)); + memcpy(gBanners, _s6.banners, sizeof(_s6.banners)); + memcpy(gUserStrings, _s6.custom_strings, sizeof(_s6.custom_strings)); + gCurrentTicks = _s6.game_ticks_1; + memcpy(gRideList, _s6.rides, sizeof(_s6.rides)); + gSavedAge = _s6.saved_age; + gSavedViewX = _s6.saved_view_x; + gSavedViewY = _s6.saved_view_y; + gSavedViewZoom = _s6.saved_view_zoom; + gSavedViewRotation = _s6.saved_view_rotation; + memcpy(gAnimatedObjects, _s6.map_animations, sizeof(_s6.map_animations)); + gNumMapAnimations = _s6.num_map_animations; + // pad_0138B582 + + gRideRatingsCalcData = _s6.ride_ratings_calc_data; + memcpy(gRideMeasurements, _s6.ride_measurements, sizeof(_s6.ride_measurements)); + gNextGuestNumber = _s6.next_guest_index; + gGrassSceneryTileLoopPosition = _s6.grass_and_scenery_tilepos; + memcpy(gStaffPatrolAreas, _s6.patrol_areas, sizeof(_s6.patrol_areas)); + memcpy(gStaffModes, _s6.staff_modes, sizeof(_s6.staff_modes)); + // unk_13CA73E + // pad_13CA73F + gUnk13CA740 = _s6.byte_13CA740; + gClimate = _s6.climate; + // pad_13CA741; + // byte_13CA742 + // pad_013CA747 + gClimateUpdateTimer = _s6.climate_update_timer; + gClimateCurrentWeather = _s6.current_weather; + gClimateNextWeather = _s6.next_weather; + gClimateCurrentTemperature = _s6.temperature; + gClimateNextTemperature = _s6.next_temperature; + gClimateCurrentWeatherEffect = _s6.current_weather_effect; + gClimateNextWeatherEffect = _s6.next_weather_effect; + gClimateCurrentWeatherGloom = _s6.current_weather_gloom; + gClimateNextWeatherGloom = _s6.next_weather_gloom; + gClimateCurrentRainLevel = _s6.current_rain_level; + gClimateNextRainLevel = _s6.next_rain_level; + + // News items + for (size_t i = 0; i < RCT12_MAX_NEWS_ITEMS; i++) + { + const rct12_news_item * src = &_s6.news_items[i]; + NewsItem * dst = &gNewsItems[i]; + + dst->Type = src->Type; + dst->Flags = src->Flags; + dst->Assoc = src->Assoc; + dst->Ticks = src->Ticks; + dst->MonthYear = src->MonthYear; + dst->Day = src->Day; + memcpy(dst->Text, src->Text, sizeof(src->Text)); + } + + // pad_13CE730 + // rct1_scenario_flags + gWidePathTileLoopX = _s6.wide_path_tile_loop_x; + gWidePathTileLoopY = _s6.wide_path_tile_loop_y; + // pad_13CE778 + + // Fix and set dynamic variables + if (!object_load_entries(_s6.objects)) + { + throw ObjectLoadException(); + } + map_strip_ghost_flag_from_elements(); + map_update_tile_pointers(); + game_convert_strings_to_utf8(); + map_count_remaining_land_rights(); } - sawyercoding_read_chunk_safe(rw, &_s6.objects, sizeof(_s6.objects)); - sawyercoding_read_chunk_safe(rw, &_s6.elapsed_months, 16); - sawyercoding_read_chunk_safe(rw, &_s6.map_elements, sizeof(_s6.map_elements)); - sawyercoding_read_chunk_safe(rw, &_s6.next_free_map_element_pointer_index, 2560076); - sawyercoding_read_chunk_safe(rw, &_s6.guests_in_park, 4); - sawyercoding_read_chunk_safe(rw, &_s6.last_guests_in_park, 8); - sawyercoding_read_chunk_safe(rw, &_s6.park_rating, 2); - sawyercoding_read_chunk_safe(rw, &_s6.active_research_types, 1082); - sawyercoding_read_chunk_safe(rw, &_s6.current_expenditure, 16); - sawyercoding_read_chunk_safe(rw, &_s6.park_value, 4); - sawyercoding_read_chunk_safe(rw, &_s6.completed_company_value, 483816); -} - -void S6Importer::Import() -{ - Initialise(); - - // _s6.header - gS6Info = _s6.info; - - gDateMonthsElapsed = _s6.elapsed_months; - gDateMonthTicks = _s6.current_day; - gScenarioTicks = _s6.scenario_ticks; - gScenarioSrand0 = _s6.scenario_srand_0; - gScenarioSrand1 = _s6.scenario_srand_1; - - memcpy(gMapElements, _s6.map_elements, sizeof(_s6.map_elements)); - - gNextFreeMapElementPointerIndex = _s6.next_free_map_element_pointer_index; - for (sint32 i = 0; i < MAX_SPRITES; i++) + void Initialise() { - memcpy(get_sprite(i), &_s6.sprites[i], sizeof(rct_sprite)); + game_init_all(_s6.map_size); } - - for (sint32 i = 0; i < NUM_SPRITE_LISTS; i++) - { - gSpriteListHead[i] = _s6.sprite_lists_head[i]; - gSpriteListCount[i] = _s6.sprite_lists_count[i]; - } - gParkName = _s6.park_name; - // pad_013573D6 - gParkNameArgs = _s6.park_name_args; - gInitialCash = _s6.initial_cash; - gBankLoan = _s6.current_loan; - gParkFlags = _s6.park_flags; - gParkEntranceFee = _s6.park_entrance_fee; - // rct1_park_entrance_x - // rct1_park_entrance_y - // pad_013573EE - // rct1_park_entrance_z - memcpy(gPeepSpawns, _s6.peep_spawns, sizeof(_s6.peep_spawns)); - gGuestChangeModifier = _s6.guest_count_change_modifier; - gResearchFundingLevel = _s6.current_research_level; - // pad_01357400 - memcpy(gResearchedRideTypes, _s6.researched_ride_types, sizeof(_s6.researched_ride_types)); - memcpy(gResearchedRideEntries, _s6.researched_ride_entries, sizeof(_s6.researched_ride_entries)); - memcpy(gResearchedTrackTypesA, _s6.researched_track_types_a, sizeof(_s6.researched_track_types_a)); - memcpy(gResearchedTrackTypesB, _s6.researched_track_types_b, sizeof(_s6.researched_track_types_b)); - - gNumGuestsInPark = _s6.guests_in_park; - gNumGuestsHeadingForPark = _s6.guests_heading_for_park; - - memcpy(gExpenditureTable, _s6.expenditure_table, sizeof(_s6.expenditure_table)); - - gNumGuestsInParkLastWeek = _s6.last_guests_in_park; - // pad_01357BCA - gStaffHandymanColour = _s6.handyman_colour; - gStaffMechanicColour = _s6.mechanic_colour; - gStaffSecurityColour = _s6.security_colour; - - memcpy(gResearchedSceneryItems, _s6.researched_scenery_items, sizeof(_s6.researched_scenery_items)); - - gParkRating = _s6.park_rating; - - memcpy(gParkRatingHistory, _s6.park_rating_history, sizeof(_s6.park_rating_history)); - memcpy(gGuestsInParkHistory, _s6.guests_in_park_history, sizeof(_s6.guests_in_park_history)); - - gResearchPriorities = _s6.active_research_types; - gResearchProgressStage = _s6.research_progress_stage; - gResearchLastItemSubject = _s6.last_researched_item_subject; - // pad_01357CF8 - gResearchNextItem = _s6.next_research_item; - gResearchProgress = _s6.research_progress; - gResearchNextCategory = _s6.next_research_category; - gResearchExpectedDay = _s6.next_research_expected_day; - gResearchExpectedMonth = _s6.next_research_expected_month; - gGuestInitialHappiness = _s6.guest_initial_happiness; - gParkSize = _s6.park_size; - _guestGenerationProbability = _s6.guest_generation_probability; - gTotalRideValue = _s6.total_ride_value; - gMaxBankLoan = _s6.maximum_loan; - gGuestInitialCash = _s6.guest_initial_cash; - gGuestInitialHunger = _s6.guest_initial_hunger; - gGuestInitialThirst = _s6.guest_initial_thirst; - gScenarioObjectiveType = _s6.objective_type; - gScenarioObjectiveYear = _s6.objective_year; - // pad_013580FA - gScenarioObjectiveCurrency = _s6.objective_currency; - gScenarioObjectiveNumGuests = _s6.objective_guests; - memcpy(gMarketingCampaignDaysLeft, _s6.campaign_weeks_left, sizeof(_s6.campaign_weeks_left)); - memcpy(gMarketingCampaignRideIndex, _s6.campaign_ride_index, sizeof(_s6.campaign_ride_index)); - - memcpy(gCashHistory, _s6.balance_history, sizeof(_s6.balance_history)); - - gCurrentExpenditure = _s6.current_expenditure; - gCurrentProfit = _s6.current_profit; - gWeeklyProfitAverageDividend = _s6.weekly_profit_average_dividend; - gWeeklyProfitAverageDivisor = _s6.weekly_profit_average_divisor; - // pad_0135833A - - memcpy(gWeeklyProfitHistory, _s6.weekly_profit_history, sizeof(_s6.weekly_profit_history)); - - gParkValue = _s6.park_value; - - memcpy(gParkValueHistory, _s6.park_value_history, sizeof(_s6.park_value_history)); - - gScenarioCompletedCompanyValue = _s6.completed_company_value; - gTotalAdmissions = _s6.total_admissions; - gTotalIncomeFromAdmissions = _s6.income_from_admissions; - gCompanyValue = _s6.company_value; - memcpy(gPeepWarningThrottle, _s6.peep_warning_throttle, sizeof(_s6.peep_warning_throttle)); - - // Awards - for (sint32 i = 0; i < RCT12_MAX_AWARDS; i++) - { - rct12_award * src = &_s6.awards[i]; - Award * dst = &gCurrentAwards[i]; - dst->Time = src->time; - dst->Type = src->type; - } - - gLandPrice = _s6.land_price; - gConstructionRightsPrice = _s6.construction_rights_price; - // unk_01358774 - // pad_01358776 - // _s6.cd_key - _gameVersion = _s6.game_version_number; - gScenarioCompanyValueRecord = _s6.completed_company_value_record; - // _s6.loan_hash; - gRideCount = _s6.ride_count; - // pad_013587CA - gHistoricalProfit = _s6.historical_profit; - // pad_013587D4 - memcpy(gScenarioCompletedBy, _s6.scenario_completed_name, sizeof(_s6.scenario_completed_name)); - gCashEncrypted = _s6.cash; - // pad_013587FC - gParkRatingCasualtyPenalty = _s6.park_rating_casualty_penalty; - gMapSizeUnits = _s6.map_size_units; - gMapSizeMinus2 = _s6.map_size_minus_2; - gMapSize = _s6.map_size; - gMapSizeMaxXY = _s6.map_max_xy; - gSamePriceThroughoutParkA = _s6.same_price_throughout; - _suggestedGuestMaximum = _s6.suggested_max_guests; - gScenarioParkRatingWarningDays = _s6.park_rating_warning_days; - gLastEntranceStyle = _s6.last_entrance_style; - // rct1_water_colour - // pad_01358842 - memcpy(gResearchItems, _s6.research_items, sizeof(_s6.research_items)); - gMapBaseZ = _s6.map_base_z; - memcpy(gScenarioName, _s6.scenario_name, sizeof(_s6.scenario_name)); - memcpy(gScenarioDetails, _s6.scenario_description, sizeof(_s6.scenario_description)); - gBankLoanInterestRate = _s6.current_interest_rate; - // pad_0135934B - gSamePriceThroughoutParkB = _s6.same_price_throughout_extended; - memcpy(gParkEntranceX, _s6.park_entrance_x, sizeof(_s6.park_entrance_x)); - memcpy(gParkEntranceY, _s6.park_entrance_y, sizeof(_s6.park_entrance_y)); - memcpy(gParkEntranceZ, _s6.park_entrance_z, sizeof(_s6.park_entrance_z)); - memcpy(gParkEntranceDirection, _s6.park_entrance_direction, sizeof(_s6.park_entrance_direction)); - scenario_set_filename(_s6.scenario_filename); - memcpy(gScenarioExpansionPacks, _s6.saved_expansion_pack_names, sizeof(_s6.saved_expansion_pack_names)); - memcpy(gBanners, _s6.banners, sizeof(_s6.banners)); - memcpy(gUserStrings, _s6.custom_strings, sizeof(_s6.custom_strings)); - gCurrentTicks = _s6.game_ticks_1; - memcpy(gRideList, _s6.rides, sizeof(_s6.rides)); - gSavedAge = _s6.saved_age; - gSavedViewX = _s6.saved_view_x; - gSavedViewY = _s6.saved_view_y; - gSavedViewZoom = _s6.saved_view_zoom; - gSavedViewRotation = _s6.saved_view_rotation; - memcpy(gAnimatedObjects, _s6.map_animations, sizeof(_s6.map_animations)); - gNumMapAnimations = _s6.num_map_animations; - // pad_0138B582 - - gRideRatingsCalcData = _s6.ride_ratings_calc_data; - memcpy(gRideMeasurements, _s6.ride_measurements, sizeof(_s6.ride_measurements)); - gNextGuestNumber = _s6.next_guest_index; - gGrassSceneryTileLoopPosition = _s6.grass_and_scenery_tilepos; - memcpy(gStaffPatrolAreas, _s6.patrol_areas, sizeof(_s6.patrol_areas)); - memcpy(gStaffModes, _s6.staff_modes, sizeof(_s6.staff_modes)); - // unk_13CA73E - // pad_13CA73F - gUnk13CA740 = _s6.byte_13CA740; - gClimate = _s6.climate; - // pad_13CA741; - // byte_13CA742 - // pad_013CA747 - gClimateUpdateTimer = _s6.climate_update_timer; - gClimateCurrentWeather = _s6.current_weather; - gClimateNextWeather = _s6.next_weather; - gClimateCurrentTemperature = _s6.temperature; - gClimateNextTemperature = _s6.next_temperature; - gClimateCurrentWeatherEffect = _s6.current_weather_effect; - gClimateNextWeatherEffect = _s6.next_weather_effect; - gClimateCurrentWeatherGloom = _s6.current_weather_gloom; - gClimateNextWeatherGloom = _s6.next_weather_gloom; - gClimateCurrentRainLevel = _s6.current_rain_level; - gClimateNextRainLevel = _s6.next_rain_level; - - // News items - for (size_t i = 0; i < RCT12_MAX_NEWS_ITEMS; i++) - { - const rct12_news_item * src = &_s6.news_items[i]; - NewsItem * dst = &gNewsItems[i]; - - dst->Type = src->Type; - dst->Flags = src->Flags; - dst->Assoc = src->Assoc; - dst->Ticks = src->Ticks; - dst->MonthYear = src->MonthYear; - dst->Day = src->Day; - memcpy(dst->Text, src->Text, sizeof(src->Text)); - } - - // pad_13CE730 - // rct1_scenario_flags - gWidePathTileLoopX = _s6.wide_path_tile_loop_x; - gWidePathTileLoopY = _s6.wide_path_tile_loop_y; - // pad_13CE778 - - // Fix and set dynamic variables - if (!object_load_entries(_s6.objects)) - { - throw ObjectLoadException(); - } - map_strip_ghost_flag_from_elements(); - map_update_tile_pointers(); - game_convert_strings_to_utf8(); - map_count_remaining_land_rights(); - if (FixIssues) - { - game_fix_save_vars(); - } -} - -void S6Importer::Initialise() -{ - game_init_all(_s6.map_size); -} +}; extern "C" { @@ -413,13 +425,14 @@ extern "C" } bool result = false; + auto stream = FileStream(rw, FILE_MODE_OPEN); auto s6Importer = new S6Importer(); try { - s6Importer->FixIssues = true; - s6Importer->LoadSavedGame(rw); + s6Importer->LoadFromStream(&stream, false); s6Importer->Import(); + game_fix_save_vars(); sprite_position_tween_reset(); result = true; } @@ -443,10 +456,10 @@ extern "C" auto s6Importer = new S6Importer(); try { - s6Importer->FixIssues = true; s6Importer->LoadSavedGame(path); s6Importer->Import(); + game_fix_save_vars(); sprite_position_tween_reset(); result = true; } @@ -475,13 +488,14 @@ extern "C" bool scenario_load_rw(SDL_RWops * rw) { bool result = false; + auto stream = FileStream(rw, FILE_MODE_OPEN); auto s6Importer = new S6Importer(); try { - s6Importer->FixIssues = true; - s6Importer->LoadScenario(rw); + s6Importer->LoadFromStream(&stream, true); s6Importer->Import(); + game_fix_save_vars(); sprite_position_tween_reset(); result = true; } @@ -518,10 +532,10 @@ extern "C" auto s6Importer = new S6Importer(); try { - s6Importer->FixIssues = true; s6Importer->LoadScenario(path); s6Importer->Import(); + game_fix_save_vars(); sprite_position_tween_reset(); result = true; } @@ -550,10 +564,11 @@ extern "C" sint32 game_load_network(SDL_RWops* rw) { bool result = false; + auto stream = FileStream(rw, FILE_MODE_OPEN); auto s6Importer = new S6Importer(); try { - s6Importer->LoadSavedGame(rw); + s6Importer->LoadFromStream(&stream, false); s6Importer->Import(); sprite_position_tween_reset(); diff --git a/src/openrct2/rct2/S6Importer.h b/src/openrct2/rct2/S6Importer.h deleted file mode 100644 index 29e1f0f423..0000000000 --- a/src/openrct2/rct2/S6Importer.h +++ /dev/null @@ -1,48 +0,0 @@ -#pragma region Copyright (c) 2014-2016 OpenRCT2 Developers -/***************************************************************************** - * OpenRCT2, an open source clone of Roller Coaster Tycoon 2. - * - * 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 - * - * 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 - *****************************************************************************/ -#pragma endregion - -#pragma once - -#include "../common.h" - -extern "C" -{ - #include "../scenario/scenario.h" -} - -/** - * Class to import RollerCoaster Tycoon 2 scenarios (*.SC6) and saved games (*.SV6). - */ -class S6Importer final -{ -public: - bool FixIssues; - - S6Importer(); - - void LoadSavedGame(const utf8 * path); - void LoadSavedGame(SDL_RWops *rw); - void LoadScenario(const utf8 * path); - void LoadScenario(SDL_RWops *rw); - void Import(); - -private: - const utf8 * _s6Path = nullptr; - rct_s6_data _s6; - uint8 _gameVersion = 0; - - void Initialise(); -}; From 5d7a71267205b385ab5b9e9e1df63f15f8076462 Mon Sep 17 00:00:00 2001 From: Ted John Date: Tue, 31 Jan 2017 18:37:55 +0000 Subject: [PATCH 5/9] Use new S6 importer for title screen and fix issues --- src/openrct2/ParkImporter.cpp | 35 ++++++++++++++++++ src/openrct2/ParkImporter.h | 2 ++ src/openrct2/core/FileStream.hpp | 21 +++++------ src/openrct2/libopenrct2.vcxproj | 1 + src/openrct2/rct2/S6Importer.cpp | 15 +++++--- src/openrct2/title/TitleSequencePlayer.cpp | 41 +++++++--------------- 6 files changed, 72 insertions(+), 43 deletions(-) create mode 100644 src/openrct2/ParkImporter.cpp diff --git a/src/openrct2/ParkImporter.cpp b/src/openrct2/ParkImporter.cpp new file mode 100644 index 0000000000..9e1ca85930 --- /dev/null +++ b/src/openrct2/ParkImporter.cpp @@ -0,0 +1,35 @@ +#pragma region Copyright (c) 2014-2016 OpenRCT2 Developers +/***************************************************************************** + * OpenRCT2, an open source clone of Roller Coaster Tycoon 2. + * + * 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 + * + * 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 + *****************************************************************************/ +#pragma endregion + +#include "core/Path.hpp" +#include "core/String.hpp" +#include "ParkImporter.h" + +IParkImporter * CreateParkImporterForPath(const std::string &path) +{ + IParkImporter * parkImporter = nullptr; + std::string extension = Path::GetExtension(path); + if (String::Equals(extension, ".sc4", true) || + String::Equals(extension, ".sv4", true)) + { + parkImporter = CreateS4Importer(); + } + else + { + parkImporter = CreateS6Importer(); + } + return parkImporter; +} diff --git a/src/openrct2/ParkImporter.h b/src/openrct2/ParkImporter.h index 5a3a495b46..1d5cac6938 100644 --- a/src/openrct2/ParkImporter.h +++ b/src/openrct2/ParkImporter.h @@ -16,6 +16,7 @@ #pragma once +#include #include "common.h" #include "scenario/ScenarioRepository.h" @@ -38,3 +39,4 @@ public: IParkImporter * CreateS4Importer(); IParkImporter * CreateS6Importer(); +IParkImporter * CreateParkImporterForPath(const std::string &path); diff --git a/src/openrct2/core/FileStream.hpp b/src/openrct2/core/FileStream.hpp index de10f7857e..0b17273324 100644 --- a/src/openrct2/core/FileStream.hpp +++ b/src/openrct2/core/FileStream.hpp @@ -33,11 +33,12 @@ enum class FileStream final : public IStream { private: - SDL_RWops * _file; - bool _canRead; - bool _canWrite; - bool _disposed; - uint64 _fileSize; + SDL_RWops * _file = 0; + bool _ownsFilePtr = false; + bool _canRead = false; + bool _canWrite = false; + bool _disposed = false; + uint64 _fileSize = 0; public: FileStream(const std::string &path, sint32 fileMode) : @@ -68,9 +69,8 @@ public: { throw IOException(SDL_GetError()); } - - _disposed = false; _fileSize = SDL_RWsize(_file); + _ownsFilePtr = true; } FileStream(SDL_RWops * ops, sint32 fileMode) @@ -88,8 +88,6 @@ public: default: throw; } - - _disposed = false; _fileSize = SDL_RWsize(_file); } @@ -98,7 +96,10 @@ public: if (!_disposed) { _disposed = true; - SDL_RWclose(_file); + if (_ownsFilePtr) + { + SDL_RWclose(_file); + } } } diff --git a/src/openrct2/libopenrct2.vcxproj b/src/openrct2/libopenrct2.vcxproj index 06650fd1a9..3d9c3089a1 100644 --- a/src/openrct2/libopenrct2.vcxproj +++ b/src/openrct2/libopenrct2.vcxproj @@ -73,6 +73,7 @@ + diff --git a/src/openrct2/rct2/S6Importer.cpp b/src/openrct2/rct2/S6Importer.cpp index b0f74d93d3..791d5a9aa8 100644 --- a/src/openrct2/rct2/S6Importer.cpp +++ b/src/openrct2/rct2/S6Importer.cpp @@ -14,6 +14,7 @@ *****************************************************************************/ #pragma endregion +#include "../core/Console.hpp" #include "../core/Exception.hpp" #include "../core/FileStream.hpp" #include "../core/IStream.hpp" @@ -127,6 +128,7 @@ public: { throw Exception("Park is not a scenario."); } + SawyerEncoding::ReadChunkTolerant(&_s6.info, sizeof(_s6.info), stream); } else { @@ -136,8 +138,6 @@ public: } } - SawyerEncoding::ReadChunkTolerant(&_s6.info, sizeof(_s6.info), stream); - // Read packed objects // TODO try to contain this more and not store objects until later for (uint16 i = 0; i < _s6.header.num_packed_objects; i++) @@ -407,6 +407,11 @@ public: } }; +IParkImporter * CreateS6Importer() +{ + return new S6Importer(); +} + extern "C" { /** @@ -436,11 +441,13 @@ extern "C" sprite_position_tween_reset(); result = true; } - catch (const ObjectLoadException &) + catch (const ObjectLoadException &ex) { + Console::Error::WriteLine(ex.GetMessage()); } - catch (const Exception &) + catch (const Exception &ex) { + Console::Error::WriteLine(ex.GetMessage()); } delete s6Importer; diff --git a/src/openrct2/title/TitleSequencePlayer.cpp b/src/openrct2/title/TitleSequencePlayer.cpp index 915f6a8951..72a27c98ca 100644 --- a/src/openrct2/title/TitleSequencePlayer.cpp +++ b/src/openrct2/title/TitleSequencePlayer.cpp @@ -14,6 +14,7 @@ *****************************************************************************/ #pragma endregion +#include #include "../common.h" #include #include "../core/Console.hpp" @@ -352,33 +353,19 @@ private: bool LoadParkFromFile(const utf8 * path) { + log_verbose("TitleSequencePlayer::LoadParkFromFile(%s)", path); bool success = false; - const utf8 * extension = Path::GetExtension(path); - if (String::Equals(extension, ".sc4", true) || - String::Equals(extension, ".sv4", true)) + try { - try - { - bool isScenario = String::Equals(extension, ".sc4", true); - IParkImporter * s4Importer = CreateS4Importer(); - s4Importer->Load(path); - s4Importer->Import(); - PrepareParkForPlayback(isScenario); - success = true; - } - catch (Exception) - { - } + auto parkImporter = std::unique_ptr(CreateParkImporterForPath(path)); + parkImporter->Load(path); + parkImporter->Import(); + PrepareParkForPlayback(); + success = true; } - else + catch (Exception) { - bool isScenario = String::Equals(extension, ".sc6", true); - SDL_RWops * rw = SDL_RWFromFile(path, "rb"); - if (rw != nullptr) - { - success = LoadParkFromRW(rw, isScenario); - SDL_RWclose(rw); - } + Console::Error::WriteLine("Unable to load park: %s", path); } return success; } @@ -389,12 +376,12 @@ private: game_load_sv6(rw); if (successfulLoad) { - PrepareParkForPlayback(isScenario); + PrepareParkForPlayback(); } return successfulLoad; } - void PrepareParkForPlayback(bool isScenario) + void PrepareParkForPlayback() { rct_window * w = window_get_main(); w->viewport_target_sprite = -1; @@ -425,10 +412,6 @@ private: reset_sprite_spatial_index(); reset_all_sprite_quadrant_placements(); window_new_ride_init_vars(); - if (!isScenario) - { - sub_684AC3(); - } scenery_set_default_placement_configuration(); news_item_init_queue(); load_palette(); From fed4c248e302cc9a00b6d2dd0de8ce677779b3eb Mon Sep 17 00:00:00 2001 From: Ted John Date: Tue, 31 Jan 2017 19:15:48 +0000 Subject: [PATCH 6/9] Remove use of SDL RWOps from title sequence player --- src/openrct2/ParkImporter.cpp | 62 +++++++++++++++++--- src/openrct2/ParkImporter.h | 30 ++++++++-- src/openrct2/cmdline/ConvertCommand.cpp | 3 +- src/openrct2/core/Zip.cpp | 13 ++++ src/openrct2/core/Zip.h | 1 + src/openrct2/rct1/S4Importer.cpp | 2 +- src/openrct2/rct2/S6Importer.cpp | 2 +- src/openrct2/scenario/ScenarioRepository.cpp | 5 +- src/openrct2/title/TitleSequence.cpp | 16 +++-- src/openrct2/title/TitleSequence.h | 6 +- src/openrct2/title/TitleSequencePlayer.cpp | 29 ++++++--- src/openrct2/title/TitleSequencePlayer.h | 2 - src/openrct2/windows/title_editor.c | 8 ++- 13 files changed, 134 insertions(+), 45 deletions(-) diff --git a/src/openrct2/ParkImporter.cpp b/src/openrct2/ParkImporter.cpp index 9e1ca85930..72d5bbd41a 100644 --- a/src/openrct2/ParkImporter.cpp +++ b/src/openrct2/ParkImporter.cpp @@ -14,22 +14,66 @@ *****************************************************************************/ #pragma endregion +#include #include "core/Path.hpp" #include "core/String.hpp" #include "ParkImporter.h" -IParkImporter * CreateParkImporterForPath(const std::string &path) +namespace ParkImporter { - IParkImporter * parkImporter = nullptr; - std::string extension = Path::GetExtension(path); - if (String::Equals(extension, ".sc4", true) || - String::Equals(extension, ".sv4", true)) + IParkImporter * Create(const std::string &hintPath) { - parkImporter = CreateS4Importer(); + IParkImporter * parkImporter = nullptr; + std::string extension = Path::GetExtension(hintPath); + if (ExtensionIsRCT1(extension)) + { + parkImporter = CreateS4(); + } + else + { + parkImporter = CreateS6(); + } + return parkImporter; } - else + + bool ExtensionIsRCT1(const std::string &extension) { - parkImporter = CreateS6Importer(); + if (String::Equals(extension, ".sc4", true) || + String::Equals(extension, ".sv4", true)) + { + return true; + } + return false; + } + + bool ExtensionIsScenario(const std::string &extension) + { + if (String::Equals(extension, ".sc4", true) || + String::Equals(extension, ".sc6", true)) + { + return true; + } + return false; + } +} + +extern "C" +{ + void park_importer_load_from_stream(void * stream_c, const utf8 * hintPath_c) + { + IStream * stream = (IStream *)stream_c; + std::string hintPath = String::ToStd(hintPath_c); + + std::string extension = Path::GetExtension(hintPath); + bool isScenario = ParkImporter::ExtensionIsScenario(hintPath); + + auto parkImporter = std::unique_ptr(ParkImporter::Create(hintPath)); + parkImporter->LoadFromStream((IStream *)stream, isScenario); + parkImporter->Import(); + } + + bool park_importer_extension_is_scenario(const utf8 * extension) + { + return ParkImporter::ExtensionIsScenario(String::ToStd(extension)); } - return parkImporter; } diff --git a/src/openrct2/ParkImporter.h b/src/openrct2/ParkImporter.h index 1d5cac6938..eb9957fa46 100644 --- a/src/openrct2/ParkImporter.h +++ b/src/openrct2/ParkImporter.h @@ -16,8 +16,11 @@ #pragma once -#include #include "common.h" + +#ifdef __cplusplus + +#include #include "scenario/ScenarioRepository.h" interface IStream; @@ -37,6 +40,25 @@ public: virtual bool GetDetails(scenario_index_entry * dst) abstract; }; -IParkImporter * CreateS4Importer(); -IParkImporter * CreateS6Importer(); -IParkImporter * CreateParkImporterForPath(const std::string &path); +namespace ParkImporter +{ + IParkImporter * Create(const std::string &hintPath); + IParkImporter * CreateS4(); + IParkImporter * CreateS6(); + + bool ExtensionIsRCT1(const std::string &extension); + bool ExtensionIsScenario(const std::string &extension); +} + +#endif + +#ifdef __cplusplus +extern "C" +{ +#endif + void park_importer_load_from_stream(void * stream, const utf8 * hintPath); + bool park_importer_extension_is_scenario(const utf8 * extension); + +#ifdef __cplusplus +} +#endif diff --git a/src/openrct2/cmdline/ConvertCommand.cpp b/src/openrct2/cmdline/ConvertCommand.cpp index 8ea4ca937e..9baecaee3b 100644 --- a/src/openrct2/cmdline/ConvertCommand.cpp +++ b/src/openrct2/cmdline/ConvertCommand.cpp @@ -14,6 +14,7 @@ *****************************************************************************/ #pragma endregion +#include #include "../common.h" #include "../core/Console.hpp" #include "../core/Exception.hpp" @@ -109,9 +110,9 @@ exitcode_t CommandLine::HandleCommandConvert(CommandLineArgEnumerator * enumerat if (sourceFileType == FILE_EXTENSION_SV4 || sourceFileType == FILE_EXTENSION_SC4) { - auto s4Importer = CreateS4Importer(); try { + auto s4Importer = std::unique_ptr(ParkImporter::CreateS4()); if (sourceFileType == FILE_EXTENSION_SC4) { s4Importer->LoadScenario(sourcePath); diff --git a/src/openrct2/core/Zip.cpp b/src/openrct2/core/Zip.cpp index cb98bb4851..161c22116e 100644 --- a/src/openrct2/core/Zip.cpp +++ b/src/openrct2/core/Zip.cpp @@ -16,6 +16,7 @@ #include #include "IStream.hpp" +#include "MemoryStream.h" #include "Zip.h" class ZipArchive final : public IZipArchive @@ -99,6 +100,18 @@ public: return data; } + IStream * GetFileStream(const utf8 * path) const override + { + IStream * stream = nullptr; + size_t dataSize; + void * data = GetFileData(path, &dataSize); + if (data != nullptr) + { + stream = new MemoryStream(data, dataSize, MEMORY_ACCESS_READ | MEMORY_ACCESS_OWNER); + } + return stream; + } + void SetFileData(const utf8 * path, void * data, size_t dataSize) override { zip_source_t * source = zip_source_buffer(_zip, data, dataSize, 0); diff --git a/src/openrct2/core/Zip.h b/src/openrct2/core/Zip.h index 8e3c8798ca..677110e78c 100644 --- a/src/openrct2/core/Zip.h +++ b/src/openrct2/core/Zip.h @@ -29,6 +29,7 @@ interface IZipArchive virtual const utf8 * GetFileName(size_t index) const abstract; virtual uint64 GetFileSize(size_t index) const abstract; virtual void * GetFileData(const utf8 * path, size_t * outSize) const abstract; + virtual IStream * GetFileStream(const utf8 * path) const abstract; /** * Creates or overwrites a file within the zip archive to the given data buffer. diff --git a/src/openrct2/rct1/S4Importer.cpp b/src/openrct2/rct1/S4Importer.cpp index 7738f3ef2b..f7c662e184 100644 --- a/src/openrct2/rct1/S4Importer.cpp +++ b/src/openrct2/rct1/S4Importer.cpp @@ -2480,7 +2480,7 @@ private: } }; -IParkImporter * CreateS4Importer() +IParkImporter * ParkImporter::CreateS4() { return new S4Importer(); } diff --git a/src/openrct2/rct2/S6Importer.cpp b/src/openrct2/rct2/S6Importer.cpp index 791d5a9aa8..923efed6e7 100644 --- a/src/openrct2/rct2/S6Importer.cpp +++ b/src/openrct2/rct2/S6Importer.cpp @@ -407,7 +407,7 @@ public: } }; -IParkImporter * CreateS6Importer() +IParkImporter * ParkImporter::CreateS6() { return new S6Importer(); } diff --git a/src/openrct2/scenario/ScenarioRepository.cpp b/src/openrct2/scenario/ScenarioRepository.cpp index 66273a1b88..a5f394ed16 100644 --- a/src/openrct2/scenario/ScenarioRepository.cpp +++ b/src/openrct2/scenario/ScenarioRepository.cpp @@ -308,7 +308,7 @@ private: */ bool GetScenarioInfo(const std::string &path, uint64 timestamp, scenario_index_entry * entry) { - log_verbose("GetScenarioInfo(%s, ...)", path.c_str()); + log_verbose("GetScenarioInfo(%s, %d, ...)", path.c_str(), timestamp); try { std::string extension = Path::GetExtension(path); @@ -316,9 +316,9 @@ private: { // RCT1 scenario bool result = false; - IParkImporter * s4Importer = CreateS4Importer(); try { + auto s4Importer = std::unique_ptr(ParkImporter::CreateS4()); s4Importer->LoadScenario(path.c_str()); if (s4Importer->GetDetails(entry)) { @@ -330,7 +330,6 @@ private: catch (Exception) { } - delete s4Importer; return result; } else diff --git a/src/openrct2/title/TitleSequence.cpp b/src/openrct2/title/TitleSequence.cpp index 68cccd8993..29107f7643 100644 --- a/src/openrct2/title/TitleSequence.cpp +++ b/src/openrct2/title/TitleSequence.cpp @@ -25,6 +25,7 @@ #include "../core/Guard.hpp" #include "../core/Math.hpp" #include "../core/Memory.hpp" +#include "../core/MemoryStream.h" #include "../core/Path.hpp" #include "../core/String.hpp" #include "../core/StringBuilder.hpp" @@ -140,9 +141,8 @@ extern "C" if (zip != nullptr) { handle = Memory::Allocate(); - handle->Data = zip->GetFileData(filename, &handle->DataSize); - handle->RWOps = SDL_RWFromMem(handle->Data, (sint32)handle->DataSize); - handle->IsScenario = String::Equals(Path::GetExtension(filename), ".sc6", true); + handle->Stream = zip->GetFileStream(filename); + handle->HintPath = String::Duplicate(filename); delete zip; } } @@ -153,9 +153,8 @@ extern "C" Path::Append(absolutePath, sizeof(absolutePath), filename); handle = Memory::Allocate(); - handle->Data = nullptr; - handle->RWOps = SDL_RWFromFile(absolutePath, "rb"); - handle->IsScenario = String::Equals(Path::GetExtension(filename), ".sc6", true); + handle->Stream = new FileStream(absolutePath, FILE_MODE_OPEN); + handle->HintPath = String::Duplicate(filename); } } return handle; @@ -165,9 +164,8 @@ extern "C" { if (handle != nullptr) { - SDL_RWclose(handle->RWOps); - Memory::Free(handle->Data); - Memory::Free(handle); + Memory::Free(handle->HintPath); + delete handle->Stream; } } diff --git a/src/openrct2/title/TitleSequence.h b/src/openrct2/title/TitleSequence.h index c6237cb492..924adb687e 100644 --- a/src/openrct2/title/TitleSequence.h +++ b/src/openrct2/title/TitleSequence.h @@ -53,10 +53,8 @@ typedef struct TitleSequence typedef struct TitleSequenceParkHandle { - size_t DataSize; - void * Data; - struct SDL_RWops * RWOps; - bool IsScenario; + const utf8 * HintPath; + void * Stream; } TitleSequenceParkHandle; enum TITLE_SCRIPT diff --git a/src/openrct2/title/TitleSequencePlayer.cpp b/src/openrct2/title/TitleSequencePlayer.cpp index 72a27c98ca..69d5569a1a 100644 --- a/src/openrct2/title/TitleSequencePlayer.cpp +++ b/src/openrct2/title/TitleSequencePlayer.cpp @@ -16,7 +16,6 @@ #include #include "../common.h" -#include #include "../core/Console.hpp" #include "../core/Exception.hpp" #include "../core/Guard.hpp" @@ -286,7 +285,7 @@ private: TitleSequenceParkHandle * parkHandle = TitleSequenceGetParkHandle(_sequence, saveIndex); if (parkHandle != nullptr) { - loadSuccess = LoadParkFromRW(parkHandle->RWOps, parkHandle->IsScenario); + loadSuccess = LoadParkFromStream((IStream *)parkHandle->Stream, parkHandle->HintPath); TitleSequenceCloseParkHandle(parkHandle); } if (!loadSuccess) @@ -357,7 +356,7 @@ private: bool success = false; try { - auto parkImporter = std::unique_ptr(CreateParkImporterForPath(path)); + auto parkImporter = std::unique_ptr(ParkImporter::Create(path)); parkImporter->Load(path); parkImporter->Import(); PrepareParkForPlayback(); @@ -370,15 +369,29 @@ private: return success; } - bool LoadParkFromRW(SDL_RWops * rw, bool isScenario) + /** + * @param stream The stream to read the park data from. + * @param pathHint Hint path, the extension is grabbed to determine what importer to use. + */ + bool LoadParkFromStream(IStream * stream, const std::string &hintPath) { - bool successfulLoad = isScenario ? scenario_load_rw(rw) : - game_load_sv6(rw); - if (successfulLoad) + log_verbose("TitleSequencePlayer::LoadParkFromStream(%s)", hintPath); + bool success = false; + try { + std::string extension = Path::GetExtension(hintPath); + bool isScenario = ParkImporter::ExtensionIsScenario(hintPath); + auto parkImporter = std::unique_ptr(ParkImporter::Create(hintPath)); + parkImporter->LoadFromStream(stream, isScenario); + parkImporter->Import(); PrepareParkForPlayback(); + success = true; } - return successfulLoad; + catch (Exception) + { + Console::Error::WriteLine("Unable to load park: %s", hintPath.c_str()); + } + return success; } void PrepareParkForPlayback() diff --git a/src/openrct2/title/TitleSequencePlayer.h b/src/openrct2/title/TitleSequencePlayer.h index 15ab54f83d..2ac2f1b6d7 100644 --- a/src/openrct2/title/TitleSequencePlayer.h +++ b/src/openrct2/title/TitleSequencePlayer.h @@ -16,8 +16,6 @@ #pragma once -#include - #ifdef __cplusplus interface IScenarioRepository; diff --git a/src/openrct2/windows/title_editor.c b/src/openrct2/windows/title_editor.c index fbf6124264..76c0e5f188 100644 --- a/src/openrct2/windows/title_editor.c +++ b/src/openrct2/windows/title_editor.c @@ -23,6 +23,7 @@ #include "../interface/widget.h" #include "../interface/window.h" #include "../localisation/localisation.h" +#include "../ParkImporter.h" #include "../peep/peep.h" #include "../peep/staff.h" #include "../scenario/scenario.h" @@ -358,11 +359,12 @@ static void window_title_editor_mouseup(rct_window *w, sint32 widgetIndex) case WIDX_TITLE_EDITOR_LOAD_SAVE: if (w->selected_list_item >= 0 && w->selected_list_item < (sint16)_editingTitleSequence->NumSaves) { TitleSequenceParkHandle * handle = TitleSequenceGetParkHandle(_editingTitleSequence, w->selected_list_item); - if (handle->IsScenario) { - scenario_load_rw(handle->RWOps); + const utf8 * extension = path_get_extension(handle->HintPath); + bool isScenario = park_importer_extension_is_scenario(extension); + park_importer_load_from_stream(handle->Stream, handle->HintPath); + if (isScenario) { scenario_begin(); } else { - game_load_sv6(handle->RWOps); game_load_init(); } TitleSequenceCloseParkHandle(handle); From bb62bbebb30eb53e8ebe17e29e54e558a4a074a1 Mon Sep 17 00:00:00 2001 From: Ted John Date: Tue, 31 Jan 2017 20:19:26 +0000 Subject: [PATCH 7/9] Re-introduce checksum validation --- src/openrct2/rct12/SawyerEncoding.cpp | 37 +++++++++++++++++++++++++++ src/openrct2/rct12/SawyerEncoding.h | 2 ++ src/openrct2/rct2/S6Importer.cpp | 25 ++++-------------- 3 files changed, 44 insertions(+), 20 deletions(-) diff --git a/src/openrct2/rct12/SawyerEncoding.cpp b/src/openrct2/rct12/SawyerEncoding.cpp index d1b161010e..fa0fa45141 100644 --- a/src/openrct2/rct12/SawyerEncoding.cpp +++ b/src/openrct2/rct12/SawyerEncoding.cpp @@ -97,4 +97,41 @@ namespace SawyerEncoding } return success; } + + bool ValidateChecksum(IStream * stream) + { + // Get data size + uint64 initialPosition = stream->GetPosition(); + uint64 dataSize = stream->GetLength() - initialPosition; + if (dataSize < 8) + { + return false; + } + dataSize -= 4; + + // Calculate checksum + uint32 checksum = 0; + do + { + uint8 buffer[4096]; + uint64 bufferSize = Math::Min(dataSize, sizeof(buffer)); + stream->Read(buffer, bufferSize); + + for (uint64 i = 0; i < bufferSize; i++) + { + checksum += buffer[i]; + } + + dataSize -= bufferSize; + } + while (dataSize != 0); + + // Read file checksum + uint32 fileChecksum = stream->ReadValue(); + + // Rewind + stream->SetPosition(initialPosition); + + return checksum == fileChecksum; + } } diff --git a/src/openrct2/rct12/SawyerEncoding.h b/src/openrct2/rct12/SawyerEncoding.h index ace15db533..bf25b6824b 100644 --- a/src/openrct2/rct12/SawyerEncoding.h +++ b/src/openrct2/rct12/SawyerEncoding.h @@ -31,4 +31,6 @@ namespace SawyerEncoding { return TryReadChunk(dst, sizeof(T), stream); } + + bool ValidateChecksum(IStream * stream); } diff --git a/src/openrct2/rct2/S6Importer.cpp b/src/openrct2/rct2/S6Importer.cpp index 923efed6e7..8eca777a28 100644 --- a/src/openrct2/rct2/S6Importer.cpp +++ b/src/openrct2/rct2/S6Importer.cpp @@ -85,15 +85,6 @@ public: void LoadSavedGame(const utf8 * path) override { - // if (!sawyercoding_validate_checksum(rw)) - // { - // gErrorType = ERROR_TYPE_FILE_LOAD; - // gGameCommandErrorTitle = STR_FILE_CONTAINS_INVALID_DATA; - // - // log_error("failed to load saved game, invalid checksum"); - // throw IOException("Invalid SV6 checksum."); - // } - auto fs = FileStream(path, FILE_MODE_OPEN); LoadFromStream(&fs, false); _s6Path = path; @@ -101,17 +92,6 @@ public: void LoadScenario(const utf8 * path) override { - // if (!gConfigGeneral.allow_loading_with_incorrect_checksum && !sawyercoding_validate_checksum(rw)) - // { - // SDL_RWclose(rw); - // - // gErrorType = ERROR_TYPE_FILE_LOAD; - // gErrorStringId = STR_FILE_CONTAINS_INVALID_DATA; - // - // log_error("failed to load scenario, invalid checksum"); - // throw IOException("Invalid SC6 checksum."); - // } - auto fs = FileStream(path, FILE_MODE_OPEN); LoadFromStream(&fs, true); _s6Path = path; @@ -119,6 +99,11 @@ public: void LoadFromStream(IStream * stream, bool isScenario) { + if (!gConfigGeneral.allow_loading_with_incorrect_checksum && !SawyerEncoding::ValidateChecksum(stream)) + { + throw IOException("Invalid checksum."); + } + SawyerEncoding::ReadChunkTolerant(&_s6.header, sizeof(_s6.header), stream); log_verbose("saved game classic_flag = 0x%02x\n", _s6.header.classic_flag); From 44120b2e7ec76e84e7aa746f39890e5830159506 Mon Sep 17 00:00:00 2001 From: Ted John Date: Tue, 31 Jan 2017 20:27:53 +0000 Subject: [PATCH 8/9] Fix build issues --- src/openrct2/rct12/SawyerEncoding.cpp | 2 +- src/openrct2/rct2/S6Importer.cpp | 11 ++++++++--- src/openrct2/title/TitleSequence.cpp | 2 +- src/openrct2/title/TitleSequencePlayer.cpp | 2 +- 4 files changed, 11 insertions(+), 6 deletions(-) diff --git a/src/openrct2/rct12/SawyerEncoding.cpp b/src/openrct2/rct12/SawyerEncoding.cpp index fa0fa45141..956ff32f17 100644 --- a/src/openrct2/rct12/SawyerEncoding.cpp +++ b/src/openrct2/rct12/SawyerEncoding.cpp @@ -114,7 +114,7 @@ namespace SawyerEncoding do { uint8 buffer[4096]; - uint64 bufferSize = Math::Min(dataSize, sizeof(buffer)); + uint64 bufferSize = Math::Min(dataSize, sizeof(buffer)); stream->Read(buffer, bufferSize); for (uint64 i = 0; i < bufferSize; i++) diff --git a/src/openrct2/rct2/S6Importer.cpp b/src/openrct2/rct2/S6Importer.cpp index 8eca777a28..6ba9e2ce2d 100644 --- a/src/openrct2/rct2/S6Importer.cpp +++ b/src/openrct2/rct2/S6Importer.cpp @@ -62,10 +62,15 @@ class S6Importer final : public IParkImporter { private: const utf8 * _s6Path = nullptr; - rct_s6_data _s6 = { 0 }; + rct_s6_data _s6; uint8 _gameVersion = 0; public: + S6Importer() + { + Memory::Set(&_s6, 0, sizeof(_s6)); + } + void Load(const utf8 * path) override { const utf8 * extension = Path::GetExtension(path); @@ -97,7 +102,7 @@ public: _s6Path = path; } - void LoadFromStream(IStream * stream, bool isScenario) + void LoadFromStream(IStream * stream, bool isScenario) override { if (!gConfigGeneral.allow_loading_with_incorrect_checksum && !SawyerEncoding::ValidateChecksum(stream)) { @@ -159,7 +164,7 @@ public: return false; } - void Import() + void Import() override { Initialise(); diff --git a/src/openrct2/title/TitleSequence.cpp b/src/openrct2/title/TitleSequence.cpp index 29107f7643..2181254a74 100644 --- a/src/openrct2/title/TitleSequence.cpp +++ b/src/openrct2/title/TitleSequence.cpp @@ -165,7 +165,7 @@ extern "C" if (handle != nullptr) { Memory::Free(handle->HintPath); - delete handle->Stream; + delete ((IStream *)handle->Stream); } } diff --git a/src/openrct2/title/TitleSequencePlayer.cpp b/src/openrct2/title/TitleSequencePlayer.cpp index 69d5569a1a..c113fe55ce 100644 --- a/src/openrct2/title/TitleSequencePlayer.cpp +++ b/src/openrct2/title/TitleSequencePlayer.cpp @@ -375,7 +375,7 @@ private: */ bool LoadParkFromStream(IStream * stream, const std::string &hintPath) { - log_verbose("TitleSequencePlayer::LoadParkFromStream(%s)", hintPath); + log_verbose("TitleSequencePlayer::LoadParkFromStream(%s)", hintPath.c_str()); bool success = false; try { From e5fbf5bfe4bf321f40450ce1a315b2544a19c1ea Mon Sep 17 00:00:00 2001 From: danidoedel Date: Wed, 1 Feb 2017 09:37:36 +0100 Subject: [PATCH 9/9] Update Xcode project --- OpenRCT2.xcodeproj/project.pbxproj | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/OpenRCT2.xcodeproj/project.pbxproj b/OpenRCT2.xcodeproj/project.pbxproj index 78fa044bbc..f63bd2b3c1 100644 --- a/OpenRCT2.xcodeproj/project.pbxproj +++ b/OpenRCT2.xcodeproj/project.pbxproj @@ -17,6 +17,7 @@ 00EFEE721CF1D80B0035213B /* NetworkKey.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 00EFEE701CF1D80B0035213B /* NetworkKey.cpp */; }; 652076321E22EFE7000D0C04 /* Imaging.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 652076301E22EFE7000D0C04 /* Imaging.cpp */; }; 652747EC1E41CE1B000F36FD /* SawyerEncoding.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 652747EA1E41CE1B000F36FD /* SawyerEncoding.cpp */; }; + 658F3D911E44A6C200388550 /* ParkImporter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 658F3D8F1E44A6C200388550 /* ParkImporter.cpp */; }; 791166FB1D7486EF005912EA /* NetworkServerAdvertiser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 791166F91D7486EF005912EA /* NetworkServerAdvertiser.cpp */; }; 85060FD31D8C17CC00DFA2B3 /* track_data_old.c in Sources */ = {isa = PBXBuildFile; fileRef = 8594C05F1D885CF600235E93 /* track_data_old.c */; }; 8594C0601D885CF600235E93 /* track_data_old.c in Sources */ = {isa = PBXBuildFile; fileRef = 8594C05F1D885CF600235E93 /* track_data_old.c */; }; @@ -521,6 +522,8 @@ 652076311E22EFE7000D0C04 /* Imaging.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Imaging.h; sourceTree = ""; }; 652747EA1E41CE1B000F36FD /* SawyerEncoding.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SawyerEncoding.cpp; path = rct12/SawyerEncoding.cpp; sourceTree = ""; }; 652747EB1E41CE1B000F36FD /* SawyerEncoding.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SawyerEncoding.h; path = rct12/SawyerEncoding.h; sourceTree = ""; }; + 658F3D8F1E44A6C200388550 /* ParkImporter.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ParkImporter.cpp; sourceTree = ""; }; + 658F3D901E44A6C200388550 /* ParkImporter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ParkImporter.h; sourceTree = ""; }; 791166F91D7486EF005912EA /* NetworkServerAdvertiser.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = NetworkServerAdvertiser.cpp; sourceTree = ""; }; 791166FA1D7486EF005912EA /* NetworkServerAdvertiser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NetworkServerAdvertiser.h; sourceTree = ""; }; 8594C05F1D885CF600235E93 /* track_data_old.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = track_data_old.c; sourceTree = ""; }; @@ -562,7 +565,6 @@ C649B3D31DF04ED2008AC826 /* format_codes.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = format_codes.c; sourceTree = ""; }; C64FDA5D1D6D99F400F259B9 /* PaintTest */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = PaintTest; sourceTree = BUILT_PRODUCTS_DIR; }; C650B2151CCABBDD00B4D91C /* S4Importer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = S4Importer.cpp; sourceTree = ""; usesTabs = 0; }; - C650B2161CCABBDD00B4D91C /* S4Importer.h */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.h; fileEncoding = 4; path = S4Importer.h; sourceTree = ""; usesTabs = 0; }; C650B2171CCABBDD00B4D91C /* tables.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = tables.cpp; sourceTree = ""; usesTabs = 0; }; C650B2181CCABBDD00B4D91C /* Tables.h */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.h; fileEncoding = 4; path = Tables.h; sourceTree = ""; usesTabs = 0; }; C650B21B1CCABC4400B4D91C /* ConvertCommand.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ConvertCommand.cpp; sourceTree = ""; usesTabs = 0; }; @@ -676,7 +678,6 @@ C6B5A7D01CDFE4CB00C9C006 /* S6Exporter.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = S6Exporter.cpp; sourceTree = ""; }; C6B5A7D11CDFE4CB00C9C006 /* S6Exporter.h */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.h; fileEncoding = 4; path = S6Exporter.h; sourceTree = ""; }; C6B5A7D21CDFE4CB00C9C006 /* S6Importer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = S6Importer.cpp; sourceTree = ""; }; - C6B5A7D31CDFE4CB00C9C006 /* S6Importer.h */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.h; fileEncoding = 4; path = S6Importer.h; sourceTree = ""; }; C6CABA801E1466D600D33A6B /* FileClassifier.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FileClassifier.cpp; sourceTree = ""; }; C6CABA811E1466D600D33A6B /* FileClassifier.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FileClassifier.h; sourceTree = ""; }; C6E96E101E04067A0076A04F /* File.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = File.cpp; sourceTree = ""; }; @@ -1294,7 +1295,6 @@ isa = PBXGroup; children = ( C650B2151CCABBDD00B4D91C /* S4Importer.cpp */, - C650B2161CCABBDD00B4D91C /* S4Importer.h */, C650B2171CCABBDD00B4D91C /* tables.cpp */, C650B2181CCABBDD00B4D91C /* Tables.h */, ); @@ -1475,7 +1475,6 @@ C6B5A7D01CDFE4CB00C9C006 /* S6Exporter.cpp */, C6B5A7D11CDFE4CB00C9C006 /* S6Exporter.h */, C6B5A7D21CDFE4CB00C9C006 /* S6Importer.cpp */, - C6B5A7D31CDFE4CB00C9C006 /* S6Importer.h */, ); path = rct2; sourceTree = ""; @@ -1557,6 +1556,8 @@ D44271581CC81B3200D84D28 /* object.h */, D460DFD01E01239D007BA2FE /* OpenRCT2.cpp */, D460DFD21E0123B5007BA2FE /* OpenRCT2.h */, + 658F3D8F1E44A6C200388550 /* ParkImporter.cpp */, + 658F3D901E44A6C200388550 /* ParkImporter.h */, D460DFD31E0123D1007BA2FE /* PlatformEnvironment.cpp */, D460DFD51E0123DB007BA2FE /* PlatformEnvironment.h */, D44271691CC81B3200D84D28 /* rct1.c */, @@ -2661,6 +2662,7 @@ D44272211CC81B3200D84D28 /* viewport_interaction.c in Sources */, D442721B1CC81B3200D84D28 /* graph.c in Sources */, C686F9581CDBC4C7009F9BFC /* vehicle_paint.c in Sources */, + 658F3D911E44A6C200388550 /* ParkImporter.cpp in Sources */, 007A05D11CFB2C8B00F419C3 /* NetworkPacket.cpp in Sources */, D44272101CC81B3200D84D28 /* sprite.c in Sources */, 007A05CD1CFB2C8B00F419C3 /* NetworkAction.cpp in Sources */,