From fbc120feba3d68c09797aaeb951c4da130a989fc Mon Sep 17 00:00:00 2001 From: Ted John Date: Sat, 15 Dec 2018 12:46:32 +0000 Subject: [PATCH] Start work on loading --- src/openrct2-ui/windows/LoadSave.cpp | 4 +- src/openrct2/Context.cpp | 8 +- src/openrct2/FileClassifier.cpp | 30 +++++ src/openrct2/FileClassifier.h | 1 + src/openrct2/ParkFile.cpp | 177 ++++++++++++++++++++++++++- src/openrct2/ParkFile.h | 22 ++++ src/openrct2/ParkImporter.h | 1 + 7 files changed, 236 insertions(+), 7 deletions(-) diff --git a/src/openrct2-ui/windows/LoadSave.cpp b/src/openrct2-ui/windows/LoadSave.cpp index 030b9e7531..5d535bc4a9 100644 --- a/src/openrct2-ui/windows/LoadSave.cpp +++ b/src/openrct2-ui/windows/LoadSave.cpp @@ -201,10 +201,10 @@ static const char* getFilterPatternByType(const int32_t type, const bool isSave) switch (type & 0x0E) { case LOADSAVETYPE_GAME: - return isSave ? "*.park" : "*.sv6;*.sc6;*.sc4;*.sv4;*.sv7;*.sea;"; + return isSave ? "*.park" : "*.park;*.sv6;*.sc6;*.sc4;*.sv4;*.sv7;*.sea"; case LOADSAVETYPE_LANDSCAPE: - return isSave ? "*.park" : "*.sc6;*.sv6;*.sc4;*.sv4;*.sv7;*.sea;"; + return isSave ? "*.park" : "*.park;*.sc6;*.sv6;*.sc4;*.sv4;*.sv7;*.sea"; case LOADSAVETYPE_SCENARIO: return "*.park"; diff --git a/src/openrct2/Context.cpp b/src/openrct2/Context.cpp index 97caa7c4f9..f4da301a48 100644 --- a/src/openrct2/Context.cpp +++ b/src/openrct2/Context.cpp @@ -603,13 +603,17 @@ namespace OpenRCT2 throw std::runtime_error("Unable to detect file type"); } - if (info.Type != FILE_TYPE::SAVED_GAME && info.Type != FILE_TYPE::SCENARIO) + if (info.Type != FILE_TYPE::PARK && info.Type != FILE_TYPE::SAVED_GAME && info.Type != FILE_TYPE::SCENARIO) { throw std::runtime_error("Invalid file type."); } std::unique_ptr parkImporter; - if (info.Version <= FILE_TYPE_S4_CUTOFF) + if (info.Type == FILE_TYPE::PARK) + { + parkImporter = ParkImporter::CreateParkFile(*_objectRepository); + } + else if (info.Version <= FILE_TYPE_S4_CUTOFF) { // Save is an S4 (RCT1 format) parkImporter = ParkImporter::CreateS4(); diff --git a/src/openrct2/FileClassifier.cpp b/src/openrct2/FileClassifier.cpp index 6aa2b7b3f9..0461420511 100644 --- a/src/openrct2/FileClassifier.cpp +++ b/src/openrct2/FileClassifier.cpp @@ -17,6 +17,7 @@ #include "scenario/Scenario.h" #include "util/SawyerCoding.h" +static bool TryClassifyAsPark(OpenRCT2::IStream* stream, ClassifiedFileInfo* result); static bool TryClassifyAsS6(OpenRCT2::IStream* stream, ClassifiedFileInfo* result); static bool TryClassifyAsS4(OpenRCT2::IStream* stream, ClassifiedFileInfo* result); static bool TryClassifyAsTD4_TD6(OpenRCT2::IStream* stream, ClassifiedFileInfo* result); @@ -41,6 +42,12 @@ bool TryClassifyFile(OpenRCT2::IStream* stream, ClassifiedFileInfo* result) // between them is to decode it. Decoding however is currently not protected // against invalid compression data for that decoding algorithm and will crash. + // Park detection + if (TryClassifyAsPark(stream, result)) + { + return true; + } + // S6 detection if (TryClassifyAsS6(stream, result)) { @@ -62,6 +69,29 @@ bool TryClassifyFile(OpenRCT2::IStream* stream, ClassifiedFileInfo* result) return false; } +static bool TryClassifyAsPark(OpenRCT2::IStream* stream, ClassifiedFileInfo* result) +{ + bool success = false; + uint64_t originalPosition = stream->GetPosition(); + try + { + auto magic = stream->ReadValue(); + if (magic == 0x4B524150) + { + result->Type = FILE_TYPE::PARK; + result->Version = 0; + success = true; + } + } + catch (const std::exception& e) + { + success = false; + log_verbose(e.what()); + } + stream->SetPosition(originalPosition); + return success; +} + static bool TryClassifyAsS6(OpenRCT2::IStream* stream, ClassifiedFileInfo* result) { bool success = false; diff --git a/src/openrct2/FileClassifier.h b/src/openrct2/FileClassifier.h index ecf9ced3b2..94f06bbd8a 100644 --- a/src/openrct2/FileClassifier.h +++ b/src/openrct2/FileClassifier.h @@ -37,6 +37,7 @@ enum class FILE_TYPE SAVED_GAME, SCENARIO, TRACK_DESIGN, + PARK, }; struct ClassifiedFileInfo diff --git a/src/openrct2/ParkFile.cpp b/src/openrct2/ParkFile.cpp index ebec01b35d..bdfada731f 100644 --- a/src/openrct2/ParkFile.cpp +++ b/src/openrct2/ParkFile.cpp @@ -1,15 +1,16 @@ #include "ParkFile.h" #include "Context.h" -#include "object/ObjectManager.h" -#include "object/ObjectRepository.h" #include "OpenRCT2.h" +#include "ParkImporter.h" #include "Version.h" #include "core/Crypt.h" #include "drawing/Drawing.h" #include "interface/Viewport.h" #include "interface/Window.h" #include "localisation/Date.h" +#include "object/ObjectManager.h" +#include "object/ObjectRepository.h" #include "scenario/Scenario.h" #include "world/Map.h" #include "world/Park.h" @@ -60,6 +61,8 @@ void ParkFile::Save(const std::string_view& path) _header.MinVersion = PARK_FILE_MIN_VERSION; _header.Compression = COMPRESSION_NONE; + _buffer = std::stringstream(std::ios::out | std::ios::binary); + WriteAuthoringChunk(); WriteObjectsChunk(); WriteGeneralChunk(); @@ -131,6 +134,10 @@ void ParkFile::NextArrayElement() void ParkFile::EndArray() { auto backupPos = _buffer.tellp(); + if ((size_t)backupPos != (size_t)_currentArrayStartPos + 8 && _currentArrayCount == 0) + { + throw std::runtime_error("Array data was written but no elements were added."); + } _buffer.seekp(_currentArrayStartPos); WriteValue((uint32_t)_currentArrayCount); WriteValue((uint32_t)_currentArrayElementSize); @@ -145,7 +152,12 @@ void ParkFile::WriteBuffer(const void* buffer, size_t len) void ParkFile::WriteString(const std::string_view& s) { char nullt = '\0'; - _buffer.write(s.data(), s.size()); + auto len = s.find('\0'); + if (len == std::string_view::npos) + { + len = s.size(); + } + _buffer.write(s.data(), len); _buffer.write(&nullt, sizeof(nullt)); } @@ -181,6 +193,7 @@ void ParkFile::WriteObjectsChunk() WriteString(""); WriteString(""); } + NextArrayElement(); } EndArray(); EndChunk(); @@ -234,6 +247,115 @@ void ParkFile::WriteTilesChunk() EndChunk(); } +void ParkFile::Load(const std::string_view& path) +{ + std::ifstream fs(std::string(path).c_str(), std::ios::binary); + + _header = ReadHeader(fs); + + _chunks.clear(); + for (uint32_t i = 0; i < _header.NumChunks; i++) + { + ChunkEntry entry; + fs.read((char*)&entry, sizeof(entry)); + _chunks.push_back(entry); + } + + _buffer = std::stringstream(std::ios::in | std::ios::out | std::ios::binary); + _buffer.clear(); + + char temp[2048]; + size_t read = 0; + do + { + fs.read(temp, sizeof(temp)); + read = fs.gcount(); + _buffer.write(temp, read); + } while (read != 0); + + RequiredObjects.clear(); + if (SeekChunk(ParkFileChunkType::OBJECTS)) + { + auto len = ReadArray(); + for (size_t i = 0; i < len; i++) + { + auto type = ReadValue(); + auto id = ReadString(); + auto version = ReadString(); + + rct_object_entry entry{}; + entry.flags = type & 0x7FFF; + strncpy(entry.name, id.c_str(), 8); + RequiredObjects.push_back(entry); + + ReadNextArrayElement(); + } + } +} + +void ParkFile::Import() +{ +} + +ParkFile::Header ParkFile::ReadHeader(std::istream& fs) +{ + Header header; + fs.read((char*)&header, sizeof(header)); + return header; +} + +bool ParkFile::SeekChunk(uint32_t id) +{ + auto result = std::find_if(_chunks.begin(), _chunks.end(), [id](const ChunkEntry& e) { return e.Id == id; }); + if (result != _chunks.end()) + { + auto offset = result->Offset; + _buffer.seekg(offset); + return true; + } + return false; +} + +size_t ParkFile::ReadArray() +{ + _currentArrayCount = ReadValue(); + _currentArrayElementSize = ReadValue(); + _currentArrayLastPos = _buffer.tellg(); + return _currentArrayCount; +} + +bool ParkFile::ReadNextArrayElement() +{ + if (_currentArrayCount == 0) + { + return false; + } + if (_currentArrayElementSize != 0) + { + _buffer.seekg((size_t)_currentArrayLastPos + _currentArrayElementSize); + } + _currentArrayCount--; + return _currentArrayCount == 0; +} + +void ParkFile::ReadBuffer(void* dst, size_t len) +{ + _buffer.read((char*)dst, len); +} + +std::string ParkFile::ReadString() +{ + std::string buffer; + buffer.reserve(64); + char c; + while ((c = ReadValue()) != 0) + { + buffer.push_back(c); + } + buffer.shrink_to_fit(); + return buffer; +} + enum : uint32_t { S6_SAVE_FLAG_EXPORT = 1 << 0, @@ -294,3 +416,52 @@ int32_t scenario_save(const utf8* path, int32_t flags) } return result; } + +class ParkFileImporter : public IParkImporter +{ +private: + const IObjectRepository& _objectRepository; + +public: + ParkFileImporter(IObjectRepository& objectRepository) + : _objectRepository(objectRepository) + { + } + + ParkLoadResult Load(const utf8* path) override + { + auto parkFile = std::make_unique(); + parkFile->Load(path); + return ParkLoadResult(std::move(parkFile->RequiredObjects)); + } + + ParkLoadResult LoadSavedGame(const utf8* path, bool skipObjectCheck = false) override + { + return Load(path); + } + + ParkLoadResult LoadScenario(const utf8* path, bool skipObjectCheck = false) override + { + return Load(path); + } + + ParkLoadResult LoadFromStream( + IStream* stream, bool isScenario, bool skipObjectCheck = false, const utf8* path = String::Empty) override + { + return Load(path); + } + + void Import() override + { + } + + bool GetDetails(scenario_index_entry* dst) override + { + return false; + } +}; + +std::unique_ptr ParkImporter::CreateParkFile(IObjectRepository& objectRepository) +{ + return std::make_unique(objectRepository); +} diff --git a/src/openrct2/ParkFile.h b/src/openrct2/ParkFile.h index fb8bb24084..943ede2014 100644 --- a/src/openrct2/ParkFile.h +++ b/src/openrct2/ParkFile.h @@ -1,16 +1,23 @@ +#pragma once + #include #include #include #include #include #include +#include "object/Object.h" namespace OpenRCT2 { class ParkFile { public: + std::vector RequiredObjects; + + void Load(const std::string_view& path); void Save(const std::string_view& path); + void Import(); private: #pragma pack(push, 1) @@ -62,5 +69,20 @@ namespace OpenRCT2 void WriteObjectsChunk(); void WriteGeneralChunk(); void WriteTilesChunk(); + + Header ReadHeader(std::istream& fs); + bool SeekChunk(uint32_t id); + size_t ReadArray(); + bool ReadNextArrayElement(); + void ReadBuffer(void* dst, size_t len); + std::string ReadString(); + + template + T ReadValue() + { + T v{}; + ReadBuffer(&v, sizeof(v)); + return v; + } }; } // namespace OpenRCT2 diff --git a/src/openrct2/ParkImporter.h b/src/openrct2/ParkImporter.h index 1edcc8b146..6cf8f61795 100644 --- a/src/openrct2/ParkImporter.h +++ b/src/openrct2/ParkImporter.h @@ -60,6 +60,7 @@ namespace ParkImporter std::unique_ptr Create(const std::string& hintPath); std::unique_ptr CreateS4(); std::unique_ptr CreateS6(IObjectRepository& objectRepository); + std::unique_ptr CreateParkFile(IObjectRepository& objectRepository); bool ExtensionIsRCT1(const std::string& extension); bool ExtensionIsScenario(const std::string& extension);