From 61e726478d9ac1041cd25732f505f02bcc47b94c Mon Sep 17 00:00:00 2001 From: Ted John Date: Sun, 30 Dec 2018 01:59:49 +0000 Subject: [PATCH] Refactor OrcaStream to new file --- src/openrct2/ParkFile.cpp | 425 ++---------------------------- src/openrct2/core/OrcaStream.hpp | 433 +++++++++++++++++++++++++++++++ 2 files changed, 449 insertions(+), 409 deletions(-) create mode 100644 src/openrct2/core/OrcaStream.hpp diff --git a/src/openrct2/ParkFile.cpp b/src/openrct2/ParkFile.cpp index 30b3ab42c8..fba962b762 100644 --- a/src/openrct2/ParkFile.cpp +++ b/src/openrct2/ParkFile.cpp @@ -1,9 +1,19 @@ +/***************************************************************************** + * Copyright (c) 2014-2019 OpenRCT2 developers + * + * For a complete list of all authors, please refer to contributors.md + * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 + * + * OpenRCT2 is licensed under the GNU General Public License version 3. + *****************************************************************************/ + #include "Context.h" #include "GameState.h" #include "OpenRCT2.h" #include "ParkImporter.h" #include "Version.h" #include "core/Crypt.h" +#include "core/OrcaStream.hpp" #include "drawing/Drawing.h" #include "interface/Viewport.h" #include "interface/Window.h" @@ -22,13 +32,9 @@ #include "world/Map.h" #include "world/Park.h" -#include #include #include -#include -#include #include -#include #include #include @@ -42,9 +48,6 @@ namespace OpenRCT2 // The minimum version that is forwards compatible with the current version. constexpr uint32_t PARK_FILE_MIN_VERSION = 0x0; - constexpr uint32_t COMPRESSION_NONE = 0; - constexpr uint32_t COMPRESSION_GZIP = 1; - namespace ParkFileChunkType { // clang-format off @@ -75,408 +78,6 @@ namespace OpenRCT2 // clang-format on }; // namespace ParkFileChunkType - class OrcaStream - { - public: - enum class Mode - { - READING, - WRITING, - }; - - private: -#pragma pack(push, 1) - struct Header - { - uint32_t Magic{}; - uint32_t TargetVersion{}; - uint32_t MinVersion{}; - uint32_t NumChunks{}; - uint64_t UncompressedSize{}; - uint32_t Compression{}; - std::array Sha1{}; - }; - - struct ChunkEntry - { - uint32_t Id{}; - uint64_t Offset{}; - uint64_t Length{}; - }; -#pragma pack(pop) - - std::string _path; - Mode _mode; - Header _header; - std::vector _chunks; - std::stringstream _buffer; - ChunkEntry _currentChunk; - - public: - OrcaStream(const std::string_view& path, Mode mode) - { - _path = path; - _mode = mode; - if (mode == Mode::READING) - { - std::ifstream fs(std::string(path).c_str(), std::ios::binary); - fs.read((char*)&_header, sizeof(_header)); - - _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); - } - else - { - _header = {}; - _header.Magic = PARK_FILE_MAGIC; - _header.TargetVersion = PARK_FILE_CURRENT_VERSION; - _header.MinVersion = PARK_FILE_MIN_VERSION; - _header.Compression = COMPRESSION_NONE; - - _buffer = std::stringstream(std::ios::out | std::ios::binary); - } - } - - OrcaStream(const OrcaStream&) = delete; - - ~OrcaStream() - { - if (_mode == Mode::READING) - { - } - else - { - // TODO avoid copying the buffer - auto uncompressedData = _buffer.str(); - - _header.NumChunks = (uint32_t)_chunks.size(); - _header.UncompressedSize = _buffer.tellp(); - _header.Sha1 = Crypt::SHA1(uncompressedData.data(), uncompressedData.size()); - - std::ofstream fs(_path.c_str(), std::ios::binary); - - // Write header - fs.seekp(0); - fs.write((const char*)&_header, sizeof(_header)); - for (const auto& chunk : _chunks) - { - fs.write((const char*)&chunk, sizeof(chunk)); - } - - // Write chunk data - fs.write(uncompressedData.data(), uncompressedData.size()); - } - } - - Mode GetMode() const - { - return _mode; - } - - template bool ReadWriteChunk(uint32_t chunkId, TFunc f) - { - if (_mode == Mode::READING) - { - if (SeekChunk(chunkId)) - { - ChunkStream stream(_buffer, _mode); - f(stream); - return true; - } - else - { - return false; - } - } - else - { - _currentChunk.Id = chunkId; - _currentChunk.Offset = _buffer.tellp(); - _currentChunk.Length = 0; - ChunkStream stream(_buffer, _mode); - f(stream); - _currentChunk.Length = (uint64_t)_buffer.tellp() - _currentChunk.Offset; - _chunks.push_back(_currentChunk); - return true; - } - } - - private: - bool 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; - } - - public: - class ChunkStream - { - private: - std::stringstream& _buffer; - Mode _mode; - std::streampos _currentArrayStartPos; - std::streampos _currentArrayLastPos; - size_t _currentArrayCount; - size_t _currentArrayElementSize; - - public: - ChunkStream(std::stringstream& buffer, Mode mode) - : _buffer(buffer) - , _mode(mode) - { - } - - Mode GetMode() const - { - return _mode; - } - - void ReadWrite(void* addr, size_t len) - { - if (_mode == Mode::READING) - { - ReadBuffer(addr, len); - } - else - { - WriteBuffer(addr, len); - } - } - - template void ReadWrite(T& v) - { - ReadWrite((void*)&v, sizeof(T)); - } - - template void ReadWriteAs(TMem& v) - { - TSave sv; - if (_mode != Mode::READING) - { - sv = v; - } - ReadWrite((void*)&sv, sizeof(TSave)); - if (_mode == Mode::READING) - { - v = static_cast(sv); - } - } - - template T Read() - { - T v{}; - ReadWrite(v); - return v; - } - - template<> void ReadWrite(std::string_view& v) = delete; - - template<> void ReadWrite(std::string& v) - { - if (_mode == Mode::READING) - { - v = ReadString(); - } - else - { - WriteString(v); - } - } - - template void Write(const T& v) - { - if (_mode == Mode::READING) - { - T temp; - ReadWrite(temp); - } - else - { - ReadWrite(v); - } - } - - template<> void Write(const std::string_view& v) - { - if (_mode == Mode::READING) - { - std::string temp; - ReadWrite(temp); - } - else - { - WriteString(v); - } - } - - template void ReadWriteArray(TArr& arr, TFunc f) - { - if (_mode == Mode::READING) - { - auto count = BeginArray(); - arr.clear(); - for (size_t i = 0; i < count; i++) - { - auto& el = arr.emplace_back(); - f(el); - NextArrayElement(); - } - EndArray(); - } - else - { - BeginArray(); - for (auto& el : arr) - { - f(el); - NextArrayElement(); - } - EndArray(); - } - } - - private: - void ReadBuffer(void* dst, size_t len) - { - _buffer.read((char*)dst, len); - } - - void WriteBuffer(const void* buffer, size_t len) - { - _buffer.write((char*)buffer, len); - } - - std::string ReadString() - { - std::string buffer; - buffer.reserve(64); - while (true) - { - char c; - ReadBuffer(&c, sizeof(c)); - if (c == 0) - { - break; - } - buffer.push_back(c); - } - buffer.shrink_to_fit(); - return buffer; - } - - void WriteString(const std::string_view& s) - { - char nullt = '\0'; - auto len = s.find('\0'); - if (len == std::string_view::npos) - { - len = s.size(); - } - _buffer.write(s.data(), len); - _buffer.write(&nullt, sizeof(nullt)); - } - - size_t BeginArray() - { - if (_mode == Mode::READING) - { - _currentArrayCount = Read(); - _currentArrayElementSize = Read(); - _currentArrayLastPos = _buffer.tellg(); - return _currentArrayCount; - } - else - { - _currentArrayCount = 0; - _currentArrayElementSize = 0; - _currentArrayStartPos = _buffer.tellp(); - Write(0); - Write(0); - _currentArrayLastPos = _buffer.tellp(); - return 0; - } - } - - bool NextArrayElement() - { - if (_mode == Mode::READING) - { - if (_currentArrayCount == 0) - { - return false; - } - if (_currentArrayElementSize != 0) - { - _currentArrayLastPos += _currentArrayElementSize; - _buffer.seekg(_currentArrayLastPos); - } - _currentArrayCount--; - return _currentArrayCount == 0; - } - else - { - auto lastElSize = (size_t)_buffer.tellp() - _currentArrayLastPos; - if (_currentArrayCount == 0) - { - // Set array element size based on first element size - _currentArrayElementSize = lastElSize; - } - else if (_currentArrayElementSize != lastElSize) - { - // Array element size was different from first element so reset it - // to dynamic - _currentArrayElementSize = 0; - } - _currentArrayCount++; - _currentArrayLastPos = _buffer.tellp(); - return true; - } - } - - void EndArray() - { - if (_mode == Mode::READING) - { - } - else - { - 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); - Write((uint32_t)_currentArrayCount); - Write((uint32_t)_currentArrayElementSize); - _buffer.seekp(backupPos); - } - } - }; - }; - class ParkFile { public: @@ -514,6 +115,12 @@ namespace OpenRCT2 void Save(const std::string_view& path) { OrcaStream blob(path, OrcaStream::Mode::WRITING); + + auto& header = blob.GetHeader(); + header.Magic = PARK_FILE_MAGIC; + header.TargetVersion = PARK_FILE_CURRENT_VERSION; + header.MinVersion = PARK_FILE_MIN_VERSION; + WriteAuthoringChunk(blob); WriteObjectsChunk(blob); WriteTilesChunk(blob); diff --git a/src/openrct2/core/OrcaStream.hpp b/src/openrct2/core/OrcaStream.hpp new file mode 100644 index 0000000000..8d72ceff97 --- /dev/null +++ b/src/openrct2/core/OrcaStream.hpp @@ -0,0 +1,433 @@ +/***************************************************************************** + * Copyright (c) 2014-2019 OpenRCT2 developers + * + * For a complete list of all authors, please refer to contributors.md + * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 + * + * OpenRCT2 is licensed under the GNU General Public License version 3. + *****************************************************************************/ + +#pragma once + +#include "Crypt.h" + +#include +#include +#include +#include +#include + +namespace OpenRCT2 +{ + class OrcaStream + { + public: + enum class Mode + { + READING, + WRITING, + }; + + static constexpr uint32_t COMPRESSION_NONE = 0; + static constexpr uint32_t COMPRESSION_GZIP = 1; + + private: +#pragma pack(push, 1) + struct Header + { + uint32_t Magic{}; + uint32_t TargetVersion{}; + uint32_t MinVersion{}; + uint32_t NumChunks{}; + uint64_t UncompressedSize{}; + uint32_t Compression{}; + std::array Sha1{}; + }; + + struct ChunkEntry + { + uint32_t Id{}; + uint64_t Offset{}; + uint64_t Length{}; + }; +#pragma pack(pop) + + std::string _path; + Mode _mode; + Header _header; + std::vector _chunks; + std::stringstream _buffer; + ChunkEntry _currentChunk; + + public: + OrcaStream(const std::string_view& path, Mode mode) + { + _path = path; + _mode = mode; + if (mode == Mode::READING) + { + std::ifstream fs(std::string(path).c_str(), std::ios::binary); + fs.read((char*)&_header, sizeof(_header)); + + _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); + } + else + { + _header = {}; + _header.Compression = COMPRESSION_NONE; + + _buffer = std::stringstream(std::ios::out | std::ios::binary); + } + } + + OrcaStream(const OrcaStream&) = delete; + + ~OrcaStream() + { + if (_mode == Mode::READING) + { + } + else + { + // TODO avoid copying the buffer + auto uncompressedData = _buffer.str(); + + _header.NumChunks = (uint32_t)_chunks.size(); + _header.UncompressedSize = _buffer.tellp(); + _header.Sha1 = Crypt::SHA1(uncompressedData.data(), uncompressedData.size()); + + std::ofstream fs(_path.c_str(), std::ios::binary); + + // Write header + fs.seekp(0); + fs.write((const char*)&_header, sizeof(_header)); + for (const auto& chunk : _chunks) + { + fs.write((const char*)&chunk, sizeof(chunk)); + } + + // Write chunk data + fs.write(uncompressedData.data(), uncompressedData.size()); + } + } + + Mode GetMode() const + { + return _mode; + } + + Header& GetHeader() + { + return _header; + } + + const Header& GetHeader() const + { + return _header; + } + + template bool ReadWriteChunk(uint32_t chunkId, TFunc f) + { + if (_mode == Mode::READING) + { + if (SeekChunk(chunkId)) + { + ChunkStream stream(_buffer, _mode); + f(stream); + return true; + } + else + { + return false; + } + } + else + { + _currentChunk.Id = chunkId; + _currentChunk.Offset = _buffer.tellp(); + _currentChunk.Length = 0; + ChunkStream stream(_buffer, _mode); + f(stream); + _currentChunk.Length = (uint64_t)_buffer.tellp() - _currentChunk.Offset; + _chunks.push_back(_currentChunk); + return true; + } + } + + private: + bool 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; + } + + public: + class ChunkStream + { + private: + std::stringstream& _buffer; + Mode _mode; + std::streampos _currentArrayStartPos; + std::streampos _currentArrayLastPos; + size_t _currentArrayCount; + size_t _currentArrayElementSize; + + public: + ChunkStream(std::stringstream& buffer, Mode mode) + : _buffer(buffer) + , _mode(mode) + { + } + + Mode GetMode() const + { + return _mode; + } + + void ReadWrite(void* addr, size_t len) + { + if (_mode == Mode::READING) + { + ReadBuffer(addr, len); + } + else + { + WriteBuffer(addr, len); + } + } + + template void ReadWrite(T& v) + { + ReadWrite((void*)&v, sizeof(T)); + } + + template void ReadWriteAs(TMem& v) + { + TSave sv; + if (_mode != Mode::READING) + { + sv = v; + } + ReadWrite((void*)&sv, sizeof(TSave)); + if (_mode == Mode::READING) + { + v = static_cast(sv); + } + } + + template T Read() + { + T v{}; + ReadWrite(v); + return v; + } + + template<> void ReadWrite(std::string_view& v) = delete; + + template<> void ReadWrite(std::string& v) + { + if (_mode == Mode::READING) + { + v = ReadString(); + } + else + { + WriteString(v); + } + } + + template void Write(const T& v) + { + if (_mode == Mode::READING) + { + T temp; + ReadWrite(temp); + } + else + { + ReadWrite(v); + } + } + + template<> void Write(const std::string_view& v) + { + if (_mode == Mode::READING) + { + std::string temp; + ReadWrite(temp); + } + else + { + WriteString(v); + } + } + + template void ReadWriteArray(TArr& arr, TFunc f) + { + if (_mode == Mode::READING) + { + auto count = BeginArray(); + arr.clear(); + for (size_t i = 0; i < count; i++) + { + auto& el = arr.emplace_back(); + f(el); + NextArrayElement(); + } + EndArray(); + } + else + { + BeginArray(); + for (auto& el : arr) + { + f(el); + NextArrayElement(); + } + EndArray(); + } + } + + private: + void ReadBuffer(void* dst, size_t len) + { + _buffer.read((char*)dst, len); + } + + void WriteBuffer(const void* buffer, size_t len) + { + _buffer.write((char*)buffer, len); + } + + std::string ReadString() + { + std::string buffer; + buffer.reserve(64); + while (true) + { + char c; + ReadBuffer(&c, sizeof(c)); + if (c == 0) + { + break; + } + buffer.push_back(c); + } + buffer.shrink_to_fit(); + return buffer; + } + + void WriteString(const std::string_view& s) + { + char nullt = '\0'; + auto len = s.find('\0'); + if (len == std::string_view::npos) + { + len = s.size(); + } + _buffer.write(s.data(), len); + _buffer.write(&nullt, sizeof(nullt)); + } + + size_t BeginArray() + { + if (_mode == Mode::READING) + { + _currentArrayCount = Read(); + _currentArrayElementSize = Read(); + _currentArrayLastPos = _buffer.tellg(); + return _currentArrayCount; + } + else + { + _currentArrayCount = 0; + _currentArrayElementSize = 0; + _currentArrayStartPos = _buffer.tellp(); + Write(0); + Write(0); + _currentArrayLastPos = _buffer.tellp(); + return 0; + } + } + + bool NextArrayElement() + { + if (_mode == Mode::READING) + { + if (_currentArrayCount == 0) + { + return false; + } + if (_currentArrayElementSize != 0) + { + _currentArrayLastPos += _currentArrayElementSize; + _buffer.seekg(_currentArrayLastPos); + } + _currentArrayCount--; + return _currentArrayCount == 0; + } + else + { + auto lastElSize = (size_t)_buffer.tellp() - _currentArrayLastPos; + if (_currentArrayCount == 0) + { + // Set array element size based on first element size + _currentArrayElementSize = lastElSize; + } + else if (_currentArrayElementSize != lastElSize) + { + // Array element size was different from first element so reset it + // to dynamic + _currentArrayElementSize = 0; + } + _currentArrayCount++; + _currentArrayLastPos = _buffer.tellp(); + return true; + } + } + + void EndArray() + { + if (_mode == Mode::READING) + { + } + else + { + 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); + Write((uint32_t)_currentArrayCount); + Write((uint32_t)_currentArrayElementSize); + _buffer.seekp(backupPos); + } + } + }; + }; +} // namespace OpenRCT2