From 4101fb65a982f00367b010fa0eff7845a2217308 Mon Sep 17 00:00:00 2001 From: LRFLEW Date: Fri, 4 Jul 2025 11:55:17 -0500 Subject: [PATCH] Refactor Compression and Streams, and Add IStream Direct Interface --- src/openrct2/ReplayManager.cpp | 144 ++++------- src/openrct2/core/ChecksumStream.cpp | 23 +- src/openrct2/core/ChecksumStream.h | 76 +++--- src/openrct2/core/Compression.cpp | 197 +++++++-------- src/openrct2/core/Compression.h | 24 +- src/openrct2/core/DataSerialiserTraits.h | 201 +++++++--------- src/openrct2/core/FileStream.cpp | 5 - src/openrct2/core/FileStream.h | 1 - src/openrct2/core/IStream.cpp | 44 ++-- src/openrct2/core/IStream.hpp | 125 +++++----- src/openrct2/core/MemoryStream.cpp | 237 +++++++------------ src/openrct2/core/MemoryStream.h | 132 +++++++---- src/openrct2/core/OrcaStream.hpp | 96 ++++---- src/openrct2/core/StreamBuffer.cpp | 161 +++++++++++++ src/openrct2/core/StreamBuffer.hpp | 78 ++++++ src/openrct2/core/Zip.cpp | 5 - src/openrct2/core/ZipStream.hpp | 15 ++ src/openrct2/libopenrct2.vcxproj | 2 + src/openrct2/network/ServerList.cpp | 6 +- src/openrct2/object/StringTable.cpp | 2 +- src/openrct2/platform/Crash.cpp | 9 +- src/openrct2/rct12/SawyerChunkReader.cpp | 7 +- src/openrct2/scenario/ScenarioRepository.cpp | 4 +- test/tests/IniWriterTest.cpp | 18 +- 24 files changed, 892 insertions(+), 720 deletions(-) create mode 100644 src/openrct2/core/StreamBuffer.cpp create mode 100644 src/openrct2/core/StreamBuffer.hpp diff --git a/src/openrct2/ReplayManager.cpp b/src/openrct2/ReplayManager.cpp index 9ac73ae5b6..fab261ad5a 100644 --- a/src/openrct2/ReplayManager.cpp +++ b/src/openrct2/ReplayManager.cpp @@ -25,7 +25,10 @@ #include "actions/TileModifyAction.h" #include "actions/TrackPlaceAction.h" #include "config/Config.h" +#include "core/Compression.h" #include "core/DataSerialiser.h" +#include "core/FileStream.h" +#include "core/FileSystem.hpp" #include "core/Path.hpp" #include "entity/EntityRegistry.h" #include "entity/EntityTweener.h" @@ -35,7 +38,6 @@ #include "object/ObjectRepository.h" #include "park/ParkFile.h" #include "world/Park.h" -#include "zlib.h" #include #include @@ -103,7 +105,7 @@ namespace OpenRCT2 { static constexpr uint16_t kReplayVersion = 10; static constexpr uint32_t kReplayMagic = 0x5243524F; // ORCR. - static constexpr int kReplayCompressionLevel = 9; + static constexpr int kReplayCompressionLevel = Compression::kZlibMaxCompressionLevel; static constexpr int kNormalRecordingChecksumTicks = 1; static constexpr int kSilentRecordingChecksumTicks = 40; // Same as network server @@ -307,44 +309,24 @@ namespace OpenRCT2 // Serialise Body. DataSerialiser recSerialiser(true); Serialise(recSerialiser, *_currentRecording); + auto& stream = recSerialiser.GetStream(); - const auto& stream = recSerialiser.GetStream(); - unsigned long streamLength = static_cast(stream.GetLength()); - unsigned long compressLength = compressBound(streamLength); - - MemoryStream data(compressLength); - - ReplayRecordFile file{ _currentRecording->magic, _currentRecording->version, streamLength, data }; - - auto compressBuf = std::make_unique(compressLength); - compress2( - compressBuf.get(), &compressLength, static_cast(stream.GetData()), stream.GetLength(), + MemoryStream compressed; + bool compressStatus = Compression::zlibCompress( + stream, static_cast(stream.GetLength()), compressed, Compression::ZlibHeaderType::zlib, kReplayCompressionLevel); - file.data.Write(compressBuf.get(), compressLength); + if (!compressStatus) + throw IOException("Compression Error"); - DataSerialiser fileSerialiser(true); - fileSerialiser << file.magic; - fileSerialiser << file.version; - fileSerialiser << file.uncompressedSize; - fileSerialiser << file.data; - - bool result = false; - - const std::string& outFile = _currentRecording->filePath; - - FILE* fp = fopen(outFile.c_str(), "wb"); - if (fp != nullptr) { - const auto& fileStream = fileSerialiser.GetStream(); - fwrite(fileStream.GetData(), 1, fileStream.GetLength(), fp); - fclose(fp); + ReplayRecordFile file{ _currentRecording->magic, _currentRecording->version, stream.GetLength(), compressed }; - result = true; - } - else - { - LOG_ERROR("Unable to write to file '%s'", outFile.c_str()); - result = false; + FileStream filestream(_currentRecording->filePath, FileMode::write); + DataSerialiser fileSerialiser(true, filestream); + fileSerialiser << file.magic; + fileSerialiser << file.version; + fileSerialiser << file.uncompressedSize; + fileSerialiser << file.data; } // When normalizing the output we don't touch the mode. @@ -356,7 +338,7 @@ namespace OpenRCT2 News::Item* news = News::AddItemToQueue(News::ItemType::blank, "Replay recording stopped", 0); news->setFlags(News::ItemFlags::hasButton); // Has no subject. - return result; + return true; } virtual bool GetCurrentReplayInfo(ReplayRecordInfo& info) const override @@ -561,35 +543,16 @@ namespace OpenRCT2 return true; } - bool ReadReplayFromFile(const std::string& file, MemoryStream& stream) - { - FILE* fp = fopen(file.c_str(), "rb"); - if (fp == nullptr) - return false; - - char buffer[128]; - while (feof(fp) == 0) - { - size_t numBytesRead = fread(buffer, 1, 128, fp); - if (numBytesRead == 0) - break; - stream.Write(buffer, numBytesRead); - } - - fclose(fp); - return true; - } - /** * Returns true if decompression was not needed or succeeded * @param stream * @return */ - bool TryDecompress(MemoryStream& stream) + MemoryStream DecompressFile(FileStream& fileStream) { ReplayRecordFile recFile; - stream.SetPosition(0); - DataSerialiser fileSerializer(false, stream); + fileStream.SetPosition(0); + DataSerialiser fileSerializer(false, fileStream); fileSerializer << recFile.magic; fileSerializer << recFile.version; @@ -598,52 +561,49 @@ namespace OpenRCT2 fileSerializer << recFile.uncompressedSize; fileSerializer << recFile.data; - auto buff = std::make_unique(recFile.uncompressedSize); - unsigned long outSize = recFile.uncompressedSize; - uncompress( - static_cast(buff.get()), &outSize, - static_cast(recFile.data.GetData()), recFile.data.GetLength()); - if (outSize != recFile.uncompressedSize) - { - return false; - } - stream.SetPosition(0); - stream.Write(buff.get(), outSize); + MemoryStream decompressed; + bool decompressStatus = true; + + recFile.data.SetPosition(0); + decompressStatus = Compression::zlibDecompress( + recFile.data, static_cast(recFile.data.GetLength()), decompressed, + static_cast(recFile.uncompressedSize), Compression::ZlibHeaderType::zlib); + + if (!decompressStatus) + throw IOException("Decompression Error"); + + recFile.data = std::move(decompressed); + } + else + { + // Read whole file into memory + fileStream.SetPosition(0); + recFile.data.CopyFromStream(fileStream, fileStream.GetLength()); } - return true; + return recFile.data; } bool ReadReplayData(const std::string& file, ReplayRecordData& data) { - MemoryStream stream; + fs::path filePath = file; + if (filePath.extension() != ".parkrep") + filePath += ".parkrep"; - std::string fileName = file; - if (fileName.size() < 5 || fileName.substr(fileName.size() - 5) != ".parkrep") + if (filePath.is_relative()) { - fileName += ".parkrep"; + fs::path replayPath = GetContext()->GetPlatformEnvironment().GetDirectoryPath( + DirBase::user, DirId::replayRecordings) + / filePath; + if (fs::is_regular_file(replayPath)) + filePath = replayPath; } - std::string outPath = GetContext()->GetPlatformEnvironment().GetDirectoryPath( - DirBase::user, DirId::replayRecordings); - std::string outFile = Path::Combine(outPath, fileName); - - bool loaded = false; - if (ReadReplayFromFile(outFile, stream)) - { - data.filePath = outFile; - loaded = true; - } - else if (ReadReplayFromFile(file, stream)) - { - data.filePath = file; - loaded = true; - } - if (!loaded) + if (!fs::is_regular_file(filePath)) return false; - if (!TryDecompress(stream)) - return false; + FileStream fileStream(filePath, FileMode::open); + MemoryStream stream = DecompressFile(fileStream); stream.SetPosition(0); DataSerialiser serialiser(false, stream); diff --git a/src/openrct2/core/ChecksumStream.cpp b/src/openrct2/core/ChecksumStream.cpp index 77a4c54391..477acb8eb0 100644 --- a/src/openrct2/core/ChecksumStream.cpp +++ b/src/openrct2/core/ChecksumStream.cpp @@ -11,8 +11,6 @@ #include "Endianness.h" -#include - namespace OpenRCT2 { #ifndef DISABLE_NETWORK @@ -25,23 +23,24 @@ namespace OpenRCT2 void ChecksumStream::Write(const void* buffer, uint64_t length) { - uint64_t* hash = reinterpret_cast(_checksum.data()); for (size_t i = 0; i < length; i += sizeof(uint64_t)) { const auto maxLen = std::min(sizeof(uint64_t), length - i); - uint64_t temp{}; - std::memcpy(&temp, reinterpret_cast(buffer) + i, maxLen); + uint64_t value{}; + std::memcpy(&value, reinterpret_cast(buffer) + i, maxLen); - // Always use value as little endian, most common systems are little. - #if defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) - temp = ByteSwapBE(temp); - #endif - - *hash ^= temp; - *hash *= kPrime; + Step(value); } } + void ChecksumStream::Step(uint64_t value) + { + uint64_t* hash = reinterpret_cast(_checksum.data()); + + *hash ^= value; + *hash *= kPrime; + } + #endif } // namespace OpenRCT2 diff --git a/src/openrct2/core/ChecksumStream.h b/src/openrct2/core/ChecksumStream.h index f9064e6c92..248e9fbc00 100644 --- a/src/openrct2/core/ChecksumStream.h +++ b/src/openrct2/core/ChecksumStream.h @@ -11,7 +11,9 @@ #include "IStream.hpp" +#include #include +#include namespace OpenRCT2 { @@ -34,7 +36,7 @@ namespace OpenRCT2 const void* GetData() const override { return _checksum.data(); - }; + } /////////////////////////////////////////////////////////////////////////// // ISteam methods @@ -72,41 +74,61 @@ namespace OpenRCT2 void Write(const void* buffer, uint64_t length) override; - void Write1(const void* buffer) override - { - Write<1>(buffer); - } - - void Write2(const void* buffer) override - { - Write<2>(buffer); - } - - void Write4(const void* buffer) override - { - Write<4>(buffer); - } - - void Write8(const void* buffer) override - { - Write<8>(buffer); - } - - void Write16(const void* buffer) override - { - Write<16>(buffer); - } - template void Write(const void* buffer) { - Write(buffer, N); + for (size_t i = 0; i < N; i += sizeof(uint64_t)) + { + const auto maxLen = std::min(sizeof(uint64_t), N - i); + + uint64_t value{}; + std::memcpy(&value, reinterpret_cast(buffer) + i, maxLen); + + Step(value); + } } uint64_t TryRead(void* buffer, uint64_t length) override { return 0; } + + private: + void Write1(const void* buffer) override + { + Step(*static_cast(buffer)); + } + + void Write2(const void* buffer) override + { + WriteUnaligned(buffer); + } + + void Write4(const void* buffer) override + { + WriteUnaligned(buffer); + } + + void Write8(const void* buffer) override + { + WriteUnaligned(buffer); + } + + void Write16(const void* buffer) override + { + WriteUnaligned(static_cast(buffer) + 0); + WriteUnaligned(static_cast(buffer) + 8); + } + + template + void WriteUnaligned(const void* buffer) + { + T value; + std::memcpy(&value, buffer, sizeof(T)); + Step(value); + } + + void Step(uint64_t value); }; } // namespace OpenRCT2 diff --git a/src/openrct2/core/Compression.cpp b/src/openrct2/core/Compression.cpp index 03b78aebbf..c0e0990850 100644 --- a/src/openrct2/core/Compression.cpp +++ b/src/openrct2/core/Compression.cpp @@ -10,167 +10,132 @@ #include "Compression.h" #include "../Diagnostic.h" -#include "zlib.h" +#include "Guard.hpp" +#include "StreamBuffer.hpp" -#include -#include -#include +#ifndef ZLIB_CONST + #define ZLIB_CONST +#endif + +#include +#include namespace OpenRCT2::Compression { - constexpr size_t kChunkSize = 128 * 1024; + constexpr size_t kZlibChunkSize = 128 * 1024; + constexpr size_t kZlibMaxChunkSize = static_cast(std::numeric_limits::max()); + constexpr int kZlibWindowBits[] = { -15, 15, 15 + 16 }; - // Compress the source to gzip-compatible stream, write to dest. - // Mainly used for compressing the crashdumps - bool gzipCompress(FILE* source, FILE* dest) + /* + * Modified copy of compressBound() from zlib 1.3.1, with the argument type changed from ULong + * (which may be only 32 bits) to uint64_t, and adds space for the default gzip header, + */ + static uint64_t zlibCompressBound(uint64_t length) { - if (source == nullptr || dest == nullptr) - { - return false; - } - int ret, flush; - size_t have; + return length + (length >> 12) + (length >> 14) + (length >> 25) + 13 + (18 - 6); + } + + bool zlibCompress(IStream& source, uint64_t sourceLength, IStream& dest, ZlibHeaderType header, int16_t level) + { + Guard::Assert(sourceLength <= source.GetLength() - source.GetPosition()); + + int ret; + StreamReadBuffer sourceBuf(source, sourceLength, kZlibChunkSize); + StreamWriteBuffer destBuf(dest, zlibCompressBound(sourceLength), kZlibChunkSize); + z_stream strm{}; - strm.zalloc = Z_NULL; - strm.zfree = Z_NULL; - strm.opaque = Z_NULL; - unsigned char in[kChunkSize]; - unsigned char out[kChunkSize]; - int windowBits = 15; - int GZIP_ENCODING = 16; - ret = deflateInit2(&strm, Z_DEFAULT_COMPRESSION, Z_DEFLATED, windowBits | GZIP_ENCODING, 8, Z_DEFAULT_STRATEGY); + ret = deflateInit2(&strm, level, Z_DEFLATED, kZlibWindowBits[static_cast(header)], 8, Z_DEFAULT_STRATEGY); if (ret != Z_OK) { LOG_ERROR("Failed to initialise stream"); return false; } + do { - strm.avail_in = uInt(fread(in, 1, kChunkSize, source)); - if (ferror(source)) - { - deflateEnd(&strm); - LOG_ERROR("Failed to read data from source"); - return false; - } - flush = feof(source) ? Z_FINISH : Z_NO_FLUSH; - strm.next_in = in; + auto readBlock = sourceBuf.ReadBlock(source, kZlibMaxChunkSize); + strm.next_in = static_cast(readBlock.first); + strm.avail_in = static_cast(readBlock.second); + do { - strm.avail_out = kChunkSize; - strm.next_out = out; - ret = deflate(&strm, flush); + Guard::Assert(destBuf, "Compression Overruns Ouput Size"); + + auto writeBlock = destBuf.WriteBlockStart(kZlibMaxChunkSize); + strm.next_out = static_cast(writeBlock.first); + strm.avail_out = static_cast(writeBlock.second); + + ret = deflate(&strm, sourceBuf ? Z_NO_FLUSH : Z_FINISH); if (ret == Z_STREAM_ERROR) { LOG_ERROR("Failed to compress data"); - return false; - } - have = kChunkSize - strm.avail_out; - if (fwrite(out, 1, have, dest) != have || ferror(dest)) - { deflateEnd(&strm); - LOG_ERROR("Failed to write data to destination"); return false; } - } while (strm.avail_out == 0); - } while (flush != Z_FINISH); + + destBuf.WriteBlockCommit(dest, writeBlock.second - strm.avail_out); + } while (strm.avail_in > 0); + } while (sourceBuf); + deflateEnd(&strm); return true; } - std::vector gzip(const void* data, const size_t dataLen) + bool zlibDecompress(IStream& source, uint64_t sourceLength, IStream& dest, uint64_t decompressLength, ZlibHeaderType header) { - assert(data != nullptr); + Guard::Assert(sourceLength <= source.GetLength() - source.GetPosition()); + + int ret; + StreamReadBuffer sourceBuf(source, sourceLength, kZlibChunkSize); + StreamWriteBuffer destBuf(dest, decompressLength, kZlibChunkSize); - std::vector output; z_stream strm{}; - strm.zalloc = Z_NULL; - strm.zfree = Z_NULL; - strm.opaque = Z_NULL; - + ret = inflateInit2(&strm, kZlibWindowBits[static_cast(header)]); + if (ret != Z_OK) { - const auto ret = deflateInit2(&strm, Z_DEFAULT_COMPRESSION, Z_DEFLATED, 15 | 16, 8, Z_DEFAULT_STRATEGY); - if (ret != Z_OK) - { - throw std::runtime_error("deflateInit2 failed with error " + std::to_string(ret)); - } + LOG_ERROR("Failed to initialise stream"); + return false; } - int flush = 0; - const auto* src = static_cast(data); - size_t srcRemaining = dataLen; do { - const auto nextBlockSize = std::min(srcRemaining, kChunkSize); - srcRemaining -= nextBlockSize; + auto readBlock = sourceBuf.ReadBlock(source, kZlibMaxChunkSize); + strm.next_in = static_cast(readBlock.first); + strm.avail_in = static_cast(readBlock.second); - flush = srcRemaining == 0 ? Z_FINISH : Z_NO_FLUSH; - strm.avail_in = static_cast(nextBlockSize); - strm.next_in = const_cast(src); do { - output.resize(output.size() + nextBlockSize); - strm.avail_out = static_cast(nextBlockSize); - strm.next_out = &output[output.size() - nextBlockSize]; - const auto ret = deflate(&strm, flush); + if (!destBuf) + { + LOG_ERROR("Decompressed data larger than expected"); + inflateEnd(&strm); + return false; + } + + auto writeBlock = destBuf.WriteBlockStart(kZlibMaxChunkSize); + strm.next_out = static_cast(writeBlock.first); + strm.avail_out = static_cast(writeBlock.second); + + ret = inflate(&strm, sourceBuf ? Z_NO_FLUSH : Z_FINISH); if (ret == Z_STREAM_ERROR) { - throw std::runtime_error("deflate failed with error " + std::to_string(ret)); + LOG_ERROR("Failed to decompress data"); + inflateEnd(&strm); + return false; } - output.resize(output.size() - strm.avail_out); - } while (strm.avail_out == 0); - src += nextBlockSize; - } while (flush != Z_FINISH); - deflateEnd(&strm); - return output; - } - - std::vector ungzip(const void* data, const size_t dataLen) - { - assert(data != nullptr); - - std::vector output; - z_stream strm{}; - strm.zalloc = Z_NULL; - strm.zfree = Z_NULL; - strm.opaque = Z_NULL; + destBuf.WriteBlockCommit(dest, writeBlock.second - strm.avail_out); + } while (strm.avail_in > 0); + } while (sourceBuf); + if (destBuf) { - const auto ret = inflateInit2(&strm, 15 | 16); - if (ret != Z_OK) - { - throw std::runtime_error("inflateInit2 failed with error " + std::to_string(ret)); - } + LOG_ERROR("Decompressed data smaller than expected"); + inflateEnd(&strm); + return false; } - int flush = 0; - const auto* src = static_cast(data); - size_t srcRemaining = dataLen; - do - { - const auto nextBlockSize = std::min(srcRemaining, kChunkSize); - srcRemaining -= nextBlockSize; - - flush = srcRemaining == 0 ? Z_FINISH : Z_NO_FLUSH; - strm.avail_in = static_cast(nextBlockSize); - strm.next_in = const_cast(src); - do - { - output.resize(output.size() + nextBlockSize); - strm.avail_out = static_cast(nextBlockSize); - strm.next_out = &output[output.size() - nextBlockSize]; - const auto ret = inflate(&strm, flush); - if (ret == Z_STREAM_ERROR) - { - throw std::runtime_error("deflate failed with error " + std::to_string(ret)); - } - output.resize(output.size() - strm.avail_out); - } while (strm.avail_out == 0); - - src += nextBlockSize; - } while (flush != Z_FINISH); inflateEnd(&strm); - return output; + return true; } } // namespace OpenRCT2::Compression diff --git a/src/openrct2/core/Compression.h b/src/openrct2/core/Compression.h index d66946f4e1..8ab7b2eea5 100644 --- a/src/openrct2/core/Compression.h +++ b/src/openrct2/core/Compression.h @@ -9,13 +9,31 @@ #pragma once +#include "IStream.hpp" + #include #include #include namespace OpenRCT2::Compression { - bool gzipCompress(FILE* source, FILE* dest); - std::vector gzip(const void* data, const size_t dataLen); - std::vector ungzip(const void* data, const size_t dataLen); + // zlib doesn't use 0 as a real compression level, so use it to mean no compression + constexpr int16_t kNoCompressionLevel = 0; + + // Zlib methods, using the DEFLATE compression algorithm + constexpr int16_t kZlibDefaultCompressionLevel = -1; // zlib value for "default level" + constexpr int16_t kZlibMaxCompressionLevel = 9; + + enum class ZlibHeaderType + { + none = 0, + zlib = 1, + gzip = 2, + }; + + bool zlibCompress( + IStream& source, uint64_t sourceLength, IStream& dest, ZlibHeaderType header, + int16_t level = kZlibDefaultCompressionLevel); + bool zlibDecompress( + IStream& source, uint64_t sourceLength, IStream& dest, uint64_t decompressLength, ZlibHeaderType header); } // namespace OpenRCT2::Compression diff --git a/src/openrct2/core/DataSerialiserTraits.h b/src/openrct2/core/DataSerialiserTraits.h index b6ae77ea6f..fceac73229 100644 --- a/src/openrct2/core/DataSerialiserTraits.h +++ b/src/openrct2/core/DataSerialiserTraits.h @@ -45,14 +45,11 @@ struct DataSerializerTraitsEnum static void encode(OpenRCT2::IStream* stream, const T& val) { - TUnderlying temp = ByteSwapBE(static_cast(val)); - stream->Write(&temp); + stream->WriteValue(ByteSwapBE(static_cast(val))); } static void decode(OpenRCT2::IStream* stream, T& val) { - TUnderlying temp; - stream->Read(&temp); - val = static_cast(ByteSwapBE(temp)); + val = static_cast(ByteSwapBE(stream->ReadValue())); } static void log(OpenRCT2::IStream* stream, const T& val) { @@ -72,14 +69,11 @@ struct DataSerializerTraitsIntegral { static void encode(OpenRCT2::IStream* stream, const T& val) { - T temp = ByteSwapBE(val); - stream->Write(&temp); + stream->WriteValue(ByteSwapBE(val)); } static void decode(OpenRCT2::IStream* stream, T& val) { - T temp; - stream->Read(&temp); - val = ByteSwapBE(temp); + val = ByteSwapBE(stream->ReadValue()); } static void log(OpenRCT2::IStream* stream, const T& val) { @@ -96,11 +90,11 @@ struct DataSerializerTraitsT { static void encode(OpenRCT2::IStream* stream, const bool& val) { - stream->Write(&val); + stream->WriteValue(val); } static void decode(OpenRCT2::IStream* stream, bool& val) { - stream->Read(&val); + stream->ReadValue(val); } static void log(OpenRCT2::IStream* stream, const bool& val) { @@ -162,19 +156,16 @@ struct DataSerializerTraitsT static void encode(OpenRCT2::IStream* stream, const std::string& str) { uint16_t len = static_cast(str.size()); - uint16_t swapped = ByteSwapBE(len); - stream->Write(&swapped); + stream->WriteValue(ByteSwapBE(len)); if (len == 0) { return; } - stream->WriteArray(str.c_str(), len); + stream->Write(str.c_str(), len); } static void decode(OpenRCT2::IStream* stream, std::string& res) { - uint16_t len; - stream->Read(&len); - len = ByteSwapBE(len); + uint16_t len = ByteSwapBE(stream->ReadValue()); if (len == 0) { res.clear(); @@ -199,15 +190,11 @@ struct DataSerializerTraitsT { static void encode(OpenRCT2::IStream* stream, const NetworkPlayerId_t& val) { - uint32_t temp = static_cast(val.id); - temp = ByteSwapBE(temp); - stream->Write(&temp); + stream->WriteValue(ByteSwapBE(val.id)); } static void decode(OpenRCT2::IStream* stream, NetworkPlayerId_t& val) { - uint32_t temp; - stream->Read(&temp); - val.id = static_cast(ByteSwapBE(temp)); + val.id = ByteSwapBE(stream->ReadValue()); } static void log(OpenRCT2::IStream* stream, const NetworkPlayerId_t& val) { @@ -288,9 +275,7 @@ struct DataSerializerTraitsPODArray { static void encode(OpenRCT2::IStream* stream, const _Ty (&val)[_Size]) { - uint16_t len = static_cast(_Size); - uint16_t swapped = ByteSwapBE(len); - stream->Write(&swapped); + stream->WriteValue(ByteSwapBE(static_cast(_Size))); DataSerializerTraits<_Ty> s; for (auto&& sub : val) @@ -300,9 +285,7 @@ struct DataSerializerTraitsPODArray } static void decode(OpenRCT2::IStream* stream, _Ty (&val)[_Size]) { - uint16_t len; - stream->Read(&len); - len = ByteSwapBE(len); + uint16_t len = ByteSwapBE(stream->ReadValue()); if (len != _Size) throw std::runtime_error("Invalid size, can't decode"); @@ -362,9 +345,7 @@ struct DataSerializerTraitsT> { static void encode(OpenRCT2::IStream* stream, const std::array<_Ty, _Size>& val) { - uint16_t len = static_cast(_Size); - uint16_t swapped = ByteSwapBE(len); - stream->Write(&swapped); + stream->WriteValue(ByteSwapBE(static_cast(_Size))); DataSerializerTraits<_Ty> s; for (auto&& sub : val) @@ -374,9 +355,7 @@ struct DataSerializerTraitsT> } static void decode(OpenRCT2::IStream* stream, std::array<_Ty, _Size>& val) { - uint16_t len; - stream->Read(&len); - len = ByteSwapBE(len); + uint16_t len = ByteSwapBE(stream->ReadValue()); if (len != _Size) throw std::runtime_error("Invalid size, can't decode"); @@ -405,9 +384,7 @@ struct DataSerializerTraitsT> { static void encode(OpenRCT2::IStream* stream, const std::vector<_Ty>& val) { - uint16_t len = static_cast(val.size()); - uint16_t swapped = ByteSwapBE(len); - stream->Write(&swapped); + stream->WriteValue(ByteSwapBE(static_cast(val.size()))); DataSerializerTraits<_Ty> s; for (auto&& sub : val) @@ -417,9 +394,7 @@ struct DataSerializerTraitsT> } static void decode(OpenRCT2::IStream* stream, std::vector<_Ty>& val) { - uint16_t len; - stream->Read(&len); - len = ByteSwapBE(len); + uint16_t len = ByteSwapBE(stream->ReadValue()); DataSerializerTraits<_Ty> s; for (auto i = 0; i < len; ++i) @@ -620,14 +595,11 @@ struct DataSerializerTraitsT { static void encode(OpenRCT2::IStream* stream, const NetworkCheatType_t& val) { - uint32_t temp = ByteSwapBE(val.id); - stream->Write(&temp); + stream->WriteValue(ByteSwapBE(val.id)); } static void decode(OpenRCT2::IStream* stream, NetworkCheatType_t& val) { - uint32_t temp; - stream->Read(&temp); - val.id = ByteSwapBE(temp); + val.id = ByteSwapBE(stream->ReadValue()); } static void log(OpenRCT2::IStream* stream, const NetworkCheatType_t& val) { @@ -641,15 +613,12 @@ struct DataSerializerTraitsT { static void encode(OpenRCT2::IStream* stream, const RCTObjectEntry& val) { - uint32_t temp = ByteSwapBE(val.flags); - stream->Write(&temp); + stream->WriteValue(ByteSwapBE(val.flags)); stream->WriteArray(val.nameWOC, 12); } static void decode(OpenRCT2::IStream* stream, RCTObjectEntry& val) { - uint32_t temp; - stream->Read(&temp); - val.flags = ByteSwapBE(temp); + val.flags = ByteSwapBE(stream->ReadValue()); auto str = stream->ReadArray(12); memcpy(val.nameWOC, str.get(), 12); } @@ -690,7 +659,7 @@ struct DataSerializerTraitsT else { auto type = static_cast(stream->ReadValue()); - auto identifier = stream->ReadStdString(); + auto identifier = stream->ReadString(); val = ObjectEntryDescriptor(type, identifier); } } @@ -709,21 +678,21 @@ struct DataSerializerTraitsT { static void encode(OpenRCT2::IStream* stream, const TrackDesignTrackElement& val) { - stream->Write(&val.type); - stream->Write(&val.flags); - stream->Write(&val.colourScheme); - stream->Write(&val.stationIndex); - stream->Write(&val.brakeBoosterSpeed); - stream->Write(&val.seatRotation); + stream->WriteValue(val.type); + stream->WriteValue(val.flags); + stream->WriteValue(val.colourScheme); + stream->WriteValue(val.stationIndex); + stream->WriteValue(val.brakeBoosterSpeed); + stream->WriteValue(val.seatRotation); } static void decode(OpenRCT2::IStream* stream, TrackDesignTrackElement& val) { - stream->Read(&val.type); - stream->Read(&val.flags); - stream->Read(&val.colourScheme); - stream->Read(&val.stationIndex); - stream->Read(&val.brakeBoosterSpeed); - stream->Read(&val.seatRotation); + stream->ReadValue(val.type); + stream->ReadValue(val.flags); + stream->ReadValue(val.colourScheme); + stream->ReadValue(val.stationIndex); + stream->ReadValue(val.brakeBoosterSpeed); + stream->ReadValue(val.seatRotation); } static void log(OpenRCT2::IStream* stream, const TrackDesignTrackElement& val) { @@ -738,13 +707,13 @@ struct DataSerializerTraitsT { static void encode(OpenRCT2::IStream* stream, const TrackDesignMazeElement& val) { - stream->Write(&val.location); - stream->Write(&val.mazeEntry); + stream->WriteValue(val.location); + stream->WriteValue(val.mazeEntry); } static void decode(OpenRCT2::IStream* stream, TrackDesignMazeElement& val) { - stream->Read(&val.location); - stream->Read(&val.mazeEntry); + stream->ReadValue(val.location); + stream->ReadValue(val.mazeEntry); } static void log(OpenRCT2::IStream* stream, const TrackDesignMazeElement& val) { @@ -761,13 +730,13 @@ struct DataSerializerTraitsT { static void encode(OpenRCT2::IStream* stream, const TrackDesignEntranceElement& val) { - stream->Write(&val.location); - stream->Write(&val.isExit); + stream->WriteValue(val.location); + stream->WriteValue(val.isExit); } static void decode(OpenRCT2::IStream* stream, TrackDesignEntranceElement& val) { - stream->Read(&val.location); - stream->Read(&val.isExit); + stream->ReadValue(val.location); + stream->ReadValue(val.isExit); } static void log(OpenRCT2::IStream* stream, const TrackDesignEntranceElement& val) { @@ -784,21 +753,21 @@ struct DataSerializerTraitsT { static void encode(OpenRCT2::IStream* stream, const TrackDesignSceneryElement& val) { - stream->Write(&val.loc); - stream->Write(&val.flags); - stream->Write(&val.primaryColour); - stream->Write(&val.secondaryColour); - stream->Write(&val.tertiaryColour); + stream->WriteValue(val.loc); + stream->WriteValue(val.flags); + stream->WriteValue(val.primaryColour); + stream->WriteValue(val.secondaryColour); + stream->WriteValue(val.tertiaryColour); DataSerializerTraits s; s.encode(stream, val.sceneryObject); } static void decode(OpenRCT2::IStream* stream, TrackDesignSceneryElement& val) { - stream->Read(&val.loc); - stream->Read(&val.flags); - stream->Read(&val.primaryColour); - stream->Read(&val.secondaryColour); - stream->Read(&val.tertiaryColour); + stream->ReadValue(val.loc); + stream->ReadValue(val.flags); + stream->ReadValue(val.primaryColour); + stream->ReadValue(val.secondaryColour); + stream->ReadValue(val.tertiaryColour); DataSerializerTraits s; s.decode(stream, val.sceneryObject); } @@ -821,15 +790,15 @@ struct DataSerializerTraitsT { static void encode(OpenRCT2::IStream* stream, const TrackColour& val) { - stream->Write(&val.main); - stream->Write(&val.additional); - stream->Write(&val.supports); + stream->WriteValue(val.main); + stream->WriteValue(val.additional); + stream->WriteValue(val.supports); } static void decode(OpenRCT2::IStream* stream, TrackColour& val) { - stream->Read(&val.main); - stream->Read(&val.additional); - stream->Read(&val.supports); + stream->ReadValue(val.main); + stream->ReadValue(val.additional); + stream->ReadValue(val.supports); } static void log(OpenRCT2::IStream* stream, const TrackColour& val) { @@ -845,15 +814,15 @@ struct DataSerializerTraitsT { static void encode(OpenRCT2::IStream* stream, const VehicleColour& val) { - stream->Write(&val.Body); - stream->Write(&val.Trim); - stream->Write(&val.Tertiary); + stream->WriteValue(val.Body); + stream->WriteValue(val.Trim); + stream->WriteValue(val.Tertiary); } static void decode(OpenRCT2::IStream* stream, VehicleColour& val) { - stream->Read(&val.Body); - stream->Read(&val.Trim); - stream->Read(&val.Tertiary); + stream->ReadValue(val.Body); + stream->ReadValue(val.Trim); + stream->ReadValue(val.Tertiary); } static void log(OpenRCT2::IStream* stream, const VehicleColour& val) { @@ -868,15 +837,15 @@ struct DataSerializerTraitsT { static void encode(OpenRCT2::IStream* stream, const RatingTuple& val) { - stream->Write(&val.excitement); - stream->Write(&val.intensity); - stream->Write(&val.nausea); + stream->WriteValue(val.excitement); + stream->WriteValue(val.intensity); + stream->WriteValue(val.nausea); } static void decode(OpenRCT2::IStream* stream, RatingTuple& val) { - stream->Read(&val.excitement); - stream->Read(&val.intensity); - stream->Read(&val.nausea); + stream->ReadValue(val.excitement); + stream->ReadValue(val.intensity); + stream->ReadValue(val.nausea); } static void log(OpenRCT2::IStream* stream, const RatingTuple& val) { @@ -893,13 +862,11 @@ struct DataSerializerTraitsT { static void encode(OpenRCT2::IStream* stream, const IntensityRange& val) { - uint8_t temp = uint8_t(val); - stream->Write(&temp); + stream->WriteValue(static_cast(val)); } static void decode(OpenRCT2::IStream* stream, IntensityRange& val) { - auto temp = stream->ReadValue(); - val = IntensityRange(temp); + val = static_cast(stream->ReadValue()); } static void log(OpenRCT2::IStream* stream, const IntensityRange& val) { @@ -914,17 +881,17 @@ struct DataSerializerTraitsT { static void encode(OpenRCT2::IStream* stream, const PeepThought& val) { - stream->Write(&val.type); - stream->Write(&val.item); - stream->Write(&val.freshness); - stream->Write(&val.fresh_timeout); + stream->WriteValue(val.type); + stream->WriteValue(val.item); + stream->WriteValue(val.freshness); + stream->WriteValue(val.fresh_timeout); } static void decode(OpenRCT2::IStream* stream, PeepThought& val) { - stream->Read(&val.type); - stream->Read(&val.item); - stream->Read(&val.freshness); - stream->Read(&val.fresh_timeout); + stream->ReadValue(val.type); + stream->ReadValue(val.item); + stream->ReadValue(val.freshness); + stream->ReadValue(val.fresh_timeout); } static void log(OpenRCT2::IStream* stream, const PeepThought& val) { @@ -1007,11 +974,11 @@ struct DataSerializerTraitsT { DataSerializerTraits().decode(stream, banner.id); DataSerializerTraits().decode(stream, banner.type); - stream->Read(&banner.flags); - banner.text = stream->ReadStdString(); - stream->Read(&banner.colour); + stream->ReadValue(banner.flags); + banner.text = stream->ReadString(); + stream->ReadValue(banner.colour); DataSerializerTraits().decode(stream, banner.rideIndex); - stream->Read(&banner.textColour); + stream->ReadValue(banner.textColour); DataSerializerTraits().decode(stream, banner.position); } diff --git a/src/openrct2/core/FileStream.cpp b/src/openrct2/core/FileStream.cpp index 2a432cb5d9..7a0c551ddb 100644 --- a/src/openrct2/core/FileStream.cpp +++ b/src/openrct2/core/FileStream.cpp @@ -195,9 +195,4 @@ namespace OpenRCT2 return readBytes; } - const void* FileStream::GetData() const - { - return nullptr; - } - } // namespace OpenRCT2 diff --git a/src/openrct2/core/FileStream.h b/src/openrct2/core/FileStream.h index 2eff04acdd..ca11d8ac83 100644 --- a/src/openrct2/core/FileStream.h +++ b/src/openrct2/core/FileStream.h @@ -52,7 +52,6 @@ namespace OpenRCT2 void Read(void* buffer, uint64_t length) override; void Write(const void* buffer, uint64_t length) override; uint64_t TryRead(void* buffer, uint64_t length) override; - const void* GetData() const override; }; } // namespace OpenRCT2 diff --git a/src/openrct2/core/IStream.cpp b/src/openrct2/core/IStream.cpp index 733f858433..9824a3a202 100644 --- a/src/openrct2/core/IStream.cpp +++ b/src/openrct2/core/IStream.cpp @@ -9,13 +9,26 @@ #include "IStream.hpp" +#include "StreamBuffer.hpp" #include "String.hpp" #include namespace OpenRCT2 { - std::string IStream::ReadStdString() + constexpr size_t kIStreamCopyBufferLength = 16 * 1024; + + void IStream::CopyFromStream(IStream& stream, uint64_t length) + { + StreamReadBuffer buffer(stream, length, kIStreamCopyBufferLength); + while (buffer) + { + auto block = buffer.ReadBlock(stream); + this->Write(block.first, block.second); + } + } + + std::string IStream::ReadString() { std::string result; uint8_t ch; @@ -26,32 +39,11 @@ namespace OpenRCT2 return result; } - void IStream::WriteString(const utf8* str) + void IStream::WriteString(std::string_view str) { - if (str == nullptr) - { - WriteValue(0); - } - else - { - size_t numBytes = String::sizeOf(str) + 1; - Write(str, numBytes); - } - } - - void IStream::WriteString(const std::string_view str) - { - for (const auto c : str) - { - if (c == '\0') - break; - WriteValue(c); - } + // if the string contains any null characters, then stop the write at the first one + str = String::toStringView(str.data(), str.size()); + Write(str.data(), str.size()); WriteValue(0); } - - void IStream::WriteString(const std::string& str) - { - WriteString(str.c_str()); - } } // namespace OpenRCT2 diff --git a/src/openrct2/core/IStream.hpp b/src/openrct2/core/IStream.hpp index 60a1034fa3..fd0135d804 100644 --- a/src/openrct2/core/IStream.hpp +++ b/src/openrct2/core/IStream.hpp @@ -59,11 +59,34 @@ namespace OpenRCT2 virtual uint64_t TryRead(void* buffer, uint64_t length) = 0; - virtual const void* GetData() const = 0; + virtual const void* GetData() const + { + return nullptr; + } + + virtual void CopyFromStream(IStream& stream, uint64_t length); + + /////////////////////////////////////////////////////////////////////////// + // Direct Read/Write methods, class can override them if they're memory-backed. + /////////////////////////////////////////////////////////////////////////// + virtual const void* ReadDirect(size_t length) + { + return nullptr; + } + + virtual void* WriteDirectStart(size_t maxLength) + { + return nullptr; + } + + virtual void WriteDirectCommit(size_t length) + { + } /////////////////////////////////////////////////////////////////////////// // Fast path methods, class can override them to use specialised copies. /////////////////////////////////////////////////////////////////////////// + private: virtual void Read1(void* buffer) { Read(buffer, 1); @@ -109,90 +132,88 @@ namespace OpenRCT2 /////////////////////////////////////////////////////////////////////////// // Helper methods /////////////////////////////////////////////////////////////////////////// + public: /** - * Reads the size of the given type from the stream directly into the given address. + * Reads the given type from the stream */ template - void Read(T* value) + void ReadValue(T& value) { // Selects the best path at compile time if constexpr (sizeof(T) == 1) { - Read1(value); + Read1(&value); } else if constexpr (sizeof(T) == 2) { - Read2(value); + Read2(&value); } else if constexpr (sizeof(T) == 4) { - Read4(value); + Read4(&value); } else if constexpr (sizeof(T) == 8) { - Read8(value); + Read8(&value); } else if constexpr (sizeof(T) == 16) { - Read16(value); + Read16(&value); } else { - Read(value, sizeof(T)); + Read(&value, sizeof(T)); } } /** - * Writes the size of the given type to the stream directly from the given address. - */ - template - void Write(const T* value) - { - // Selects the best path at compile time - if constexpr (sizeof(T) == 1) - { - Write1(value); - } - else if constexpr (sizeof(T) == 2) - { - Write2(value); - } - else if constexpr (sizeof(T) == 4) - { - Write4(value); - } - else if constexpr (sizeof(T) == 8) - { - Write8(value); - } - else if constexpr (sizeof(T) == 16) - { - Write16(value); - } - else - { - Write(value, sizeof(T)); - } - } - - /** - * Reads the given type from the stream. Use this only for small types (e.g. int8_t, int64_t, double) + * Reads the given type from the stream and returns the value directly */ template T ReadValue() { - T buffer; - Read(&buffer); - return buffer; + T value; + ReadValue(value); + return value; } /** - * Writes the given type to the stream. Use this only for small types (e.g. int8_t, int64_t, double) + * Writes the given type to the stream */ template - void WriteValue(const T value) + void WriteValue(const T& value) { - Write(&value); + // Selects the best path at compile time + if constexpr (sizeof(T) == 1) + { + Write1(&value); + } + else if constexpr (sizeof(T) == 2) + { + Write2(&value); + } + else if constexpr (sizeof(T) == 4) + { + Write4(&value); + } + else if constexpr (sizeof(T) == 8) + { + Write8(&value); + } + else if constexpr (sizeof(T) == 16) + { + Write16(&value); + } + else + { + Write(&value, sizeof(T)); + } + } + + template + void ReadArray(T* buffer, size_t count) + { + Read(buffer, sizeof(T) * count); } template @@ -209,10 +230,8 @@ namespace OpenRCT2 Write(buffer, sizeof(T) * count); } - std::string ReadStdString(); - void WriteString(const utf8* str); - void WriteString(const std::string_view string); - void WriteString(const std::string& string); + std::string ReadString(); + void WriteString(std::string_view string); }; } // namespace OpenRCT2 diff --git a/src/openrct2/core/MemoryStream.cpp b/src/openrct2/core/MemoryStream.cpp index cda05e4aec..527409c288 100644 --- a/src/openrct2/core/MemoryStream.cpp +++ b/src/openrct2/core/MemoryStream.cpp @@ -9,6 +9,7 @@ #include "MemoryStream.h" +#include "Guard.hpp" #include "Memory.hpp" #include @@ -23,17 +24,23 @@ namespace OpenRCT2 if (_access & MEMORY_ACCESS::OWNER) { - _data = Memory::Allocate(_dataCapacity); + _data = Memory::Allocate(_dataCapacity); std::memcpy(_data, copy._data, _dataCapacity); - _position = reinterpret_cast(reinterpret_cast(_data) + copy.GetPosition()); } } MemoryStream::MemoryStream(size_t capacity) { _dataCapacity = capacity; - _data = Memory::Allocate(capacity); - _position = _data; + _data = Memory::Allocate(capacity); + } + + MemoryStream::MemoryStream(const std::vector& v) + { + _dataCapacity = v.size(); + _dataSize = v.size(); + _data = Memory::Allocate(v.size()); + std::memcpy(_data, v.data(), v.size()); } MemoryStream::MemoryStream(void* data, size_t dataSize, uint8_t access) @@ -41,23 +48,7 @@ namespace OpenRCT2 _access = access; _dataCapacity = dataSize; _dataSize = dataSize; - _data = data; - _position = _data; - } - - MemoryStream::MemoryStream(const void* data, size_t dataSize) - : MemoryStream(const_cast(data), dataSize, MEMORY_ACCESS::READ) - { - } - - MemoryStream::MemoryStream(std::vector&& v) - { - _access = MEMORY_ACCESS::OWNER; - _dataCapacity = v.size(); - _dataSize = v.size(); - _data = Memory::Allocate(v.size()); - _position = _data; - std::memcpy(_data, v.data(), v.size()); + _data = static_cast(data); } MemoryStream::MemoryStream(MemoryStream&& mv) noexcept @@ -68,7 +59,7 @@ namespace OpenRCT2 , _position(mv._position) { mv._data = nullptr; - mv._position = nullptr; + mv._position = 0; mv._dataCapacity = 0; mv._dataSize = 0; } @@ -76,18 +67,21 @@ namespace OpenRCT2 MemoryStream::~MemoryStream() { if (_access & MEMORY_ACCESS::OWNER) - { Memory::Free(_data); - } + + _data = nullptr; + _position = 0; _dataCapacity = 0; _dataSize = 0; - _data = nullptr; } MemoryStream& MemoryStream::operator=(MemoryStream&& mv) noexcept { if (this != &mv) { + if (_access & MEMORY_ACCESS::OWNER) + Memory::Free(_data); + _access = mv._access; _dataCapacity = mv._dataCapacity; _data = mv._data; @@ -95,7 +89,7 @@ namespace OpenRCT2 _position = mv._position; mv._data = nullptr; - mv._position = nullptr; + mv._position = 0; mv._dataCapacity = 0; mv._dataSize = 0; } @@ -103,172 +97,111 @@ namespace OpenRCT2 return *this; } - const void* MemoryStream::GetData() const - { - return _data; - } - - bool MemoryStream::CanRead() const - { - return (_access & MEMORY_ACCESS::READ) != 0; - } - - bool MemoryStream::CanWrite() const - { - return (_access & MEMORY_ACCESS::WRITE) != 0; - } - - uint64_t MemoryStream::GetLength() const - { - return _dataSize; - } - - uint64_t MemoryStream::GetPosition() const - { - return static_cast(reinterpret_cast(_position) - reinterpret_cast(_data)); - } - void MemoryStream::SetPosition(uint64_t position) { - Seek(position, STREAM_SEEK_BEGIN); + if (position > _dataSize) + throw IOException("New position out of bounds."); + _position = static_cast(position); } void MemoryStream::Seek(int64_t offset, int32_t origin) { - uint64_t newPosition; switch (origin) { default: case STREAM_SEEK_BEGIN: - newPosition = offset; + SetPosition(offset); break; case STREAM_SEEK_CURRENT: - newPosition = GetPosition() + offset; + SetPosition(_position + offset); break; case STREAM_SEEK_END: - newPosition = _dataSize + offset; + SetPosition(_dataSize + offset); break; } - - if (newPosition > _dataSize) - { - throw IOException("New position out of bounds."); - } - _position = reinterpret_cast(reinterpret_cast(_data) + static_cast(newPosition)); } void MemoryStream::Read(void* buffer, uint64_t length) { - uint64_t position = GetPosition(); - if (position + length > _dataSize) - { + if (_position + length > _dataSize) throw IOException("Attempted to read past end of stream."); - } - std::memcpy(buffer, _position, length); - _position = reinterpret_cast(reinterpret_cast(_position) + length); - } - - void MemoryStream::Read1(void* buffer) - { - Read<1>(buffer); - } - - void MemoryStream::Read2(void* buffer) - { - Read<2>(buffer); - } - - void MemoryStream::Read4(void* buffer) - { - Read<4>(buffer); - } - - void MemoryStream::Read8(void* buffer) - { - Read<8>(buffer); - } - - void MemoryStream::Read16(void* buffer) - { - Read<16>(buffer); + std::memcpy(buffer, _data + _position, length); + _position += static_cast(length); } uint64_t MemoryStream::TryRead(void* buffer, uint64_t length) { - uint64_t remainingBytes = GetLength() - GetPosition(); - uint64_t bytesToRead = std::min(length, remainingBytes); - Read(buffer, bytesToRead); + uint64_t bytesToRead = std::min(length, static_cast(_dataSize - _position)); + std::memcpy(buffer, _data + _position, static_cast(bytesToRead)); + _position += static_cast(bytesToRead); return bytesToRead; } void MemoryStream::Write(const void* buffer, uint64_t length) { - uint64_t position = GetPosition(); - uint64_t nextPosition = position + length; - if (nextPosition > _dataCapacity) + EnsureWriteCapacity(length); + std::memcpy(_data + _position, buffer, length); + _position += static_cast(length); + _dataSize = std::max(_dataSize, _position); + } + + void MemoryStream::Clear() + { + _dataSize = 0; + _position = 0; + } + + const void* MemoryStream::ReadDirect(size_t length) + { + if (_position + length > _dataSize) + throw IOException("Attempted to read past end of stream."); + + const void* readPosition = _data + _position; + _position += length; + return readPosition; + } + + void* MemoryStream::WriteDirectStart(size_t maxLength) + { + EnsureWriteCapacity(maxLength); + return _data + _position; + } + + void MemoryStream::WriteDirectCommit(size_t length) + { + Guard::Assert(_dataCapacity >= _position + length); + _position += length; + _dataSize = std::max(_dataSize, _position); + } + + void MemoryStream::CopyFromStream(IStream& stream, uint64_t length) + { + EnsureWriteCapacity(length); + // Copy directly into storage to avoid intermediate buffer + stream.Read(_data + _position, length); + _position += static_cast(length); + _dataSize = std::max(_dataSize, _position); + } + + void MemoryStream::EnsureWriteCapacity(uint64_t length) + { + if (_position + static_cast(length) > _dataCapacity) { if (_access & MEMORY_ACCESS::OWNER) { - EnsureCapacity(static_cast(nextPosition)); + // resize to the larger of the requested capacity and double the current capacity, with a minimum of 8. + size_t newCapacity = std::max(_position + static_cast(length), _dataCapacity * 2); + newCapacity = std::max(newCapacity, 8); + + _dataCapacity = newCapacity; + _data = Memory::Reallocate(_data, _dataCapacity); } else { throw IOException("Attempted to write past end of stream."); } } - - std::memcpy(_position, buffer, length); - _position = reinterpret_cast(reinterpret_cast(_position) + length); - _dataSize = std::max(_dataSize, static_cast(nextPosition)); - } - - void MemoryStream::Write1(const void* buffer) - { - Write<1>(buffer); - } - - void MemoryStream::Write2(const void* buffer) - { - Write<2>(buffer); - } - - void MemoryStream::Write4(const void* buffer) - { - Write<4>(buffer); - } - - void MemoryStream::Write8(const void* buffer) - { - Write<8>(buffer); - } - - void MemoryStream::Write16(const void* buffer) - { - Write<16>(buffer); - } - - void MemoryStream::Clear() - { - _dataSize = 0; - SetPosition(0); - } - - void MemoryStream::EnsureCapacity(size_t capacity) - { - if (_dataCapacity < capacity) - { - size_t newCapacity = std::max(8, _dataCapacity); - while (newCapacity < capacity) - { - newCapacity *= 2; - } - - uint64_t position = GetPosition(); - _dataCapacity = newCapacity; - _data = Memory::Reallocate(_data, _dataCapacity); - _position = reinterpret_cast(reinterpret_cast(_data) + static_cast(position)); - } } } // namespace OpenRCT2 diff --git a/src/openrct2/core/MemoryStream.h b/src/openrct2/core/MemoryStream.h index 71e3e48315..212afecf46 100644 --- a/src/openrct2/core/MemoryStream.h +++ b/src/openrct2/core/MemoryStream.h @@ -11,6 +11,7 @@ #include "IStream.hpp" +#include #include #include @@ -32,89 +33,130 @@ namespace OpenRCT2 uint8_t _access = MEMORY_ACCESS::READ | MEMORY_ACCESS::WRITE | MEMORY_ACCESS::OWNER; size_t _dataCapacity = 0; size_t _dataSize = 0; - void* _data = nullptr; - void* _position = nullptr; + uint8_t* _data = nullptr; + size_t _position = 0; public: MemoryStream() = default; MemoryStream(const MemoryStream& copy); MemoryStream(MemoryStream&& mv) noexcept; explicit MemoryStream(size_t capacity); + MemoryStream(const std::vector& v); MemoryStream(void* data, size_t dataSize, uint8_t access = MEMORY_ACCESS::READ); - MemoryStream(const void* data, size_t dataSize); - MemoryStream(std::vector&& v); + MemoryStream(const void* data, size_t dataSize) + : MemoryStream(const_cast(data), dataSize, MEMORY_ACCESS::READ) + { + } virtual ~MemoryStream(); MemoryStream& operator=(MemoryStream&& mv) noexcept; - const void* GetData() const override; - /////////////////////////////////////////////////////////////////////////// // ISteam methods /////////////////////////////////////////////////////////////////////////// - bool CanRead() const override; - bool CanWrite() const override; - uint64_t GetLength() const override; - uint64_t GetPosition() const override; + const void* GetData() const override + { + return _data; + } + + bool CanRead() const override + { + return (_access & MEMORY_ACCESS::READ) != 0; + } + bool CanWrite() const override + { + return (_access & MEMORY_ACCESS::WRITE) != 0; + } + + uint64_t GetLength() const override + { + return _dataSize; + } + uint64_t GetPosition() const override + { + return _position; + } + void SetPosition(uint64_t position) override; void Seek(int64_t offset, int32_t origin) override; void Read(void* buffer, uint64_t length) override; - void Read1(void* buffer) override; - void Read2(void* buffer) override; - void Read4(void* buffer) override; - void Read8(void* buffer) override; - void Read16(void* buffer) override; + uint64_t TryRead(void* buffer, uint64_t length) override; template void Read(void* buffer) { - uint64_t position = GetPosition(); - if (position + N > _dataSize) - { + if (_position + N > _dataSize) throw IOException("Attempted to read past end of stream."); - } - std::memcpy(buffer, _position, N); - _position = reinterpret_cast(reinterpret_cast(_position) + N); + std::memcpy(buffer, _data + _position, N); + _position += static_cast(N); } void Write(const void* buffer, uint64_t length) override; - void Write1(const void* buffer) override; - void Write2(const void* buffer) override; - void Write4(const void* buffer) override; - void Write8(const void* buffer) override; - void Write16(const void* buffer) override; template void Write(const void* buffer) { - uint64_t position = GetPosition(); - uint64_t nextPosition = position + N; - if (nextPosition > _dataCapacity) - { - if (_access & MEMORY_ACCESS::OWNER) - { - EnsureCapacity(static_cast(nextPosition)); - } - else - { - throw IOException("Attempted to write past end of stream."); - } - } - - std::memcpy(_position, buffer, N); - _position = reinterpret_cast(reinterpret_cast(_position) + N); - _dataSize = std::max(_dataSize, static_cast(nextPosition)); + EnsureWriteCapacity(N); + std::memcpy(_data + _position, buffer, N); + _position += static_cast(N); + _dataSize = std::max(_dataSize, _position); } - uint64_t TryRead(void* buffer, uint64_t length) override; + const void* ReadDirect(size_t length) override; + void* WriteDirectStart(size_t maxLength) override; + void WriteDirectCommit(size_t length) override; + + void CopyFromStream(IStream& stream, uint64_t length) override; void Clear(); private: - void EnsureCapacity(size_t capacity); + void Read1(void* buffer) override + { + Read<1>(buffer); + } + void Read2(void* buffer) override + { + Read<2>(buffer); + } + void Read4(void* buffer) override + { + Read<4>(buffer); + } + void Read8(void* buffer) override + { + Read<8>(buffer); + } + void Read16(void* buffer) override + { + Read<16>(buffer); + } + + void Write1(const void* buffer) override + { + Write<1>(buffer); + } + void Write2(const void* buffer) override + { + Write<2>(buffer); + } + void Write4(const void* buffer) override + { + Write<4>(buffer); + } + void Write8(const void* buffer) override + { + Write<8>(buffer); + } + void Write16(const void* buffer) override + { + Write<16>(buffer); + } + + void EnsureWriteCapacity(uint64_t length); }; } // namespace OpenRCT2 diff --git a/src/openrct2/core/OrcaStream.hpp b/src/openrct2/core/OrcaStream.hpp index 701a4a18d9..bbeb8b0ef8 100644 --- a/src/openrct2/core/OrcaStream.hpp +++ b/src/openrct2/core/OrcaStream.hpp @@ -90,35 +90,46 @@ namespace OpenRCT2 _chunks.push_back(entry); } - // Read compressed data into buffer (read in blocks) - _buffer = MemoryStream{}; - uint8_t temp[2048]; - uint64_t bytesLeft = _header.CompressedSize; - do - { - auto readLen = std::min(size_t(bytesLeft), sizeof(temp)); - _stream->Read(temp, readLen); - _buffer.Write(temp, readLen); - bytesLeft -= readLen; - } while (bytesLeft > 0); - // Uncompress - if (_header.Compression == CompressionType::gzip) + if (_header.Compression != CompressionType::none) { - auto uncompressedData = Compression::ungzip(_buffer.GetData(), _buffer.GetLength()); - if (_header.UncompressedSize != uncompressedData.size()) + size_t compressedSize = static_cast(_header.CompressedSize); + size_t uncompressedSize = static_cast(_header.UncompressedSize); + bool decompressStatus = false; + + switch (_header.Compression) { - // Warning? + case CompressionType::gzip: + decompressStatus = Compression::zlibDecompress( + *_stream, compressedSize, _buffer, uncompressedSize, Compression::ZlibHeaderType::gzip); + break; + default: + throw IOException("Unknown Park Compression Type"); } - _buffer.Clear(); - _buffer.Write(uncompressedData.data(), uncompressedData.size()); + + if (!decompressStatus) + throw IOException("Decompression Error"); + } + else + { + if (_header.UncompressedSize != _header.CompressedSize) + throw IOException("None Compression Sizes Don't Match"); + _buffer.CopyFromStream(*_stream, _header.UncompressedSize); + } + + // early in-dev versions used SHA1 instead of FNV1a, so just assume any file + // with a verison number of 0 may be one of these, and don't check their hashes. + if (_header.TargetVersion > 0) + { + auto checksum = Crypt::FNV1a(_buffer.GetData(), _buffer.GetLength()); + if (checksum != _header.FNV1a) + throw IOException("Checksum Is Not Valid"); } } else { _header = {}; _header.Compression = CompressionType::gzip; - _buffer = MemoryStream{}; } } @@ -129,26 +140,37 @@ namespace OpenRCT2 { if (_mode == Mode::WRITING) { - const void* uncompressedData = _buffer.GetData(); - const uint64_t uncompressedSize = _buffer.GetLength(); - _header.NumChunks = static_cast(_chunks.size()); - _header.UncompressedSize = uncompressedSize; - _header.CompressedSize = uncompressedSize; - _header.FNV1a = Crypt::FNV1a(uncompressedData, uncompressedSize); + _header.UncompressedSize = _buffer.GetLength(); + _header.CompressedSize = _buffer.GetLength(); + _header.FNV1a = Crypt::FNV1a(_buffer.GetData(), _buffer.GetLength()); // Compress data - std::optional> compressedBytes; - if (_header.Compression == CompressionType::gzip) + if (_header.Compression != CompressionType::none) { - compressedBytes = Compression::gzip(uncompressedData, uncompressedSize); - if (compressedBytes) + MemoryStream compressed; + size_t bufferLength = static_cast(_buffer.GetLength()); + bool compressStatus = false; + + _buffer.SetPosition(0); + switch (_header.Compression) { - _header.CompressedSize = compressedBytes->size(); + case CompressionType::gzip: + compressStatus = Compression::zlibCompress( + _buffer, bufferLength, compressed, Compression::ZlibHeaderType::gzip); + break; + default: + break; + } + + if (compressStatus && compressed.GetLength() < _buffer.GetLength()) + { + _buffer = std::move(compressed); + _header.CompressedSize = _buffer.GetLength(); } else { - // Compression failed + // Compression increases filesize, so just store uncompressed data _header.Compression = CompressionType::none; } } @@ -156,19 +178,9 @@ namespace OpenRCT2 // Write header and chunk table _stream->WriteValue(_header); for (const auto& chunk : _chunks) - { _stream->WriteValue(chunk); - } - // Write chunk data - if (compressedBytes) - { - _stream->Write(compressedBytes->data(), compressedBytes->size()); - } - else - { - _stream->Write(uncompressedData, uncompressedSize); - } + _stream->Write(_buffer.GetData(), _buffer.GetLength()); } } diff --git a/src/openrct2/core/StreamBuffer.cpp b/src/openrct2/core/StreamBuffer.cpp new file mode 100644 index 0000000000..9378bee940 --- /dev/null +++ b/src/openrct2/core/StreamBuffer.cpp @@ -0,0 +1,161 @@ +/***************************************************************************** + * Copyright (c) 2014-2025 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 "StreamBuffer.hpp" + +#include "Guard.hpp" +#include "Memory.hpp" + +namespace OpenRCT2 +{ + StreamReadBuffer::StreamReadBuffer(IStream& stream, uint64_t length, size_t bufferLength) + { + OpenRCT2::Guard::Assert(bufferLength > 0); + + _remaining = length; + if (length <= std::numeric_limits::max()) + _buffer = static_cast(stream.ReadDirect(static_cast(length))); + + if (_buffer == nullptr) + { + if (length > stream.GetLength() - stream.GetPosition()) + throw IOException("Buffer Will Read Past the End of the Stream"); + + _bufferLength = bufferLength; + _buffer = OpenRCT2::Memory::Allocate(_bufferLength); + } + } + + StreamReadBuffer::~StreamReadBuffer() + { + if (_bufferLength > 0) + OpenRCT2::Memory::Free(_buffer); + + _buffer = nullptr; + _bufferLength = 0; + _remaining = 0; + } + + StreamReadBuffer::StreamReadBuffer(StreamReadBuffer&& other) noexcept + { + _buffer = other._buffer; + _bufferLength = other._bufferLength; + _remaining = other._remaining; + + other._buffer = nullptr; + other._bufferLength = 0; + other._remaining = 0; + } + + StreamReadBuffer& StreamReadBuffer::operator=(StreamReadBuffer&& other) noexcept + { + if (&other != this) + { + _buffer = other._buffer; + _bufferLength = other._bufferLength; + _remaining = other._remaining; + + other._buffer = nullptr; + other._bufferLength = 0; + other._remaining = 0; + } + return *this; + } + + std::pair StreamReadBuffer::ReadBlock(IStream& stream, size_t maxLength) + { + const uint8_t* ptr = _buffer; + size_t len = static_cast(std::min(_remaining, maxLength)); + if (_bufferLength == 0) + { + _buffer += len; + } + else + { + len = static_cast(std::min(len, _bufferLength)); + stream.Read(const_cast(ptr), len); + } + _remaining -= len; + return { ptr, len }; + } + + StreamWriteBuffer::StreamWriteBuffer(IStream& stream, uint64_t length, size_t bufferLength) + { + OpenRCT2::Guard::Assert(bufferLength > 0); + + _remaining = length; + if (length <= std::numeric_limits::max()) + _buffer = static_cast(stream.WriteDirectStart(static_cast(length))); + + if (_buffer == nullptr) + { + _bufferLength = bufferLength; + _buffer = OpenRCT2::Memory::Allocate(_bufferLength); + } + } + + StreamWriteBuffer::~StreamWriteBuffer() + { + if (_bufferLength > 0) + OpenRCT2::Memory::Free(_buffer); + + _buffer = nullptr; + _bufferLength = 0; + _remaining = 0; + } + + StreamWriteBuffer::StreamWriteBuffer(StreamWriteBuffer&& other) noexcept + { + _buffer = other._buffer; + _bufferLength = other._bufferLength; + _remaining = other._remaining; + + other._buffer = nullptr; + other._bufferLength = 0; + other._remaining = 0; + } + + StreamWriteBuffer& StreamWriteBuffer::operator=(StreamWriteBuffer&& other) noexcept + { + if (&other != this) + { + _buffer = other._buffer; + _bufferLength = other._bufferLength; + _remaining = other._remaining; + + other._buffer = nullptr; + other._bufferLength = 0; + other._remaining = 0; + } + return *this; + } + + std::pair StreamWriteBuffer::WriteBlockStart(size_t maxLength) + { + size_t len = static_cast(std::min(_remaining, maxLength)); + if (_bufferLength > 0) + len = std::min(len, _bufferLength); + + return { _buffer, len }; + } + + void StreamWriteBuffer::WriteBlockCommit(IStream& stream, size_t length) + { + if (_bufferLength == 0) + { + stream.WriteDirectCommit(length); + _buffer += length; + } + else + { + stream.Write(_buffer, length); + } + _remaining -= length; + } +} // namespace OpenRCT2 diff --git a/src/openrct2/core/StreamBuffer.hpp b/src/openrct2/core/StreamBuffer.hpp new file mode 100644 index 0000000000..48d0a89732 --- /dev/null +++ b/src/openrct2/core/StreamBuffer.hpp @@ -0,0 +1,78 @@ +/***************************************************************************** + * Copyright (c) 2014-2025 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 "IStream.hpp" + +#include +#include +#include + +namespace OpenRCT2 +{ + struct StreamReadBuffer + { + StreamReadBuffer(IStream& stream, uint64_t length, size_t bufferLength); + + ~StreamReadBuffer(); + // not copyable + StreamReadBuffer(const StreamReadBuffer&) = delete; + StreamReadBuffer& operator=(const StreamReadBuffer&) = delete; + // moveable + StreamReadBuffer(StreamReadBuffer&&) noexcept; + StreamReadBuffer& operator=(StreamReadBuffer&&) noexcept; + + std::pair ReadBlock(IStream& stream, size_t maxLength = std::numeric_limits::max()); + + uint64_t GetRemaining() const + { + return _remaining; + } + operator bool() const + { + return _remaining > 0; + } + + private: + const uint8_t* _buffer = nullptr; + size_t _bufferLength = 0; + uint64_t _remaining = 0; + }; + + struct StreamWriteBuffer + { + StreamWriteBuffer(IStream& stream, uint64_t length, size_t bufferLength); + + ~StreamWriteBuffer(); + // not copyable + StreamWriteBuffer(const StreamWriteBuffer&) = delete; + StreamWriteBuffer& operator=(const StreamWriteBuffer&) = delete; + // moveable + StreamWriteBuffer(StreamWriteBuffer&&) noexcept; + StreamWriteBuffer& operator=(StreamWriteBuffer&&) noexcept; + + std::pair WriteBlockStart(size_t maxLength = std::numeric_limits::max()); + void WriteBlockCommit(IStream& stream, size_t length); + + uint64_t GetRemaining() const + { + return _remaining; + } + operator bool() const + { + return _remaining > 0; + } + + private: + uint8_t* _buffer = nullptr; + size_t _bufferLength = 0; + uint64_t _remaining = 0; + }; +} // namespace OpenRCT2 diff --git a/src/openrct2/core/Zip.cpp b/src/openrct2/core/Zip.cpp index 30a5e065d3..a8baf7756c 100644 --- a/src/openrct2/core/Zip.cpp +++ b/src/openrct2/core/Zip.cpp @@ -319,11 +319,6 @@ private: return static_cast(readBytes); } - const void* GetData() const override - { - return nullptr; - } - private: void Close() { diff --git a/src/openrct2/core/ZipStream.hpp b/src/openrct2/core/ZipStream.hpp index 88bda263ad..aada34d302 100644 --- a/src/openrct2/core/ZipStream.hpp +++ b/src/openrct2/core/ZipStream.hpp @@ -81,6 +81,21 @@ namespace OpenRCT2 { return _base->GetData(); } + + const void* ReadDirect(size_t length) override + { + return _base->ReadDirect(length); + } + + void* WriteDirectStart(size_t maxLength) override + { + return _base->WriteDirectStart(maxLength); + } + + void WriteDirectCommit(size_t length) override + { + _base->WriteDirectCommit(length); + } }; } // namespace OpenRCT2 diff --git a/src/openrct2/libopenrct2.vcxproj b/src/openrct2/libopenrct2.vcxproj index 61d9945d55..5fe918158a 100644 --- a/src/openrct2/libopenrct2.vcxproj +++ b/src/openrct2/libopenrct2.vcxproj @@ -229,6 +229,7 @@ + @@ -790,6 +791,7 @@ + diff --git a/src/openrct2/network/ServerList.cpp b/src/openrct2/network/ServerList.cpp index d68179bc32..1d56884d8d 100644 --- a/src/openrct2/network/ServerList.cpp +++ b/src/openrct2/network/ServerList.cpp @@ -198,10 +198,10 @@ std::vector ServerList::ReadFavourites() const for (size_t i = 0; i < numEntries; i++) { ServerListEntry serverInfo; - serverInfo.Address = fs.ReadStdString(); - serverInfo.Name = fs.ReadStdString(); + serverInfo.Address = fs.ReadString(); + serverInfo.Name = fs.ReadString(); serverInfo.RequiresPassword = false; - serverInfo.Description = fs.ReadStdString(); + serverInfo.Description = fs.ReadString(); serverInfo.Version.clear(); serverInfo.Favourite = true; serverInfo.Players = 0; diff --git a/src/openrct2/object/StringTable.cpp b/src/openrct2/object/StringTable.cpp index 179d011cbc..7957780d26 100644 --- a/src/openrct2/object/StringTable.cpp +++ b/src/openrct2/object/StringTable.cpp @@ -60,7 +60,7 @@ void StringTable::Read(IReadObjectContext* context, OpenRCT2::IStream* stream, O uint8_t languageId = (EnumValue(rct2LanguageId) <= EnumValue(RCT2LanguageId::Portuguese)) ? RCT2ToOpenRCT2LanguageId[EnumValue(rct2LanguageId)] : static_cast(LANGUAGE_UNDEFINED); - std::string stringAsWin1252 = stream->ReadStdString(); + std::string stringAsWin1252 = stream->ReadString(); auto stringAsUtf8 = RCT2StringToUTF8(stringAsWin1252, rct2LanguageId); if (!StringIsBlank(stringAsUtf8.data())) diff --git a/src/openrct2/platform/Crash.cpp b/src/openrct2/platform/Crash.cpp index 521aab5192..8a2bf0b89f 100644 --- a/src/openrct2/platform/Crash.cpp +++ b/src/openrct2/platform/Crash.cpp @@ -33,6 +33,7 @@ #include "../config/Config.h" #include "../core/Compression.h" #include "../core/Console.hpp" + #include "../core/FileStream.h" #include "../core/Guard.hpp" #include "../core/Path.hpp" #include "../core/SawyerCoding.h" @@ -134,10 +135,10 @@ static bool OnCrash( // Compress the dump { - FILE* input = _wfopen(dumpFilePath, L"rb"); - FILE* dest = _wfopen(dumpFilePathGZIP, L"wb"); + FileStream source(dumpFilePath, FileMode::open); + FileStream dest(dumpFilePathGZIP, FileMode::write); - if (Compression::gzipCompress(input, dest)) + if (Compression::zlibCompress(source, source.GetLength(), dest, Compression::ZlibHeaderType::gzip)) { // TODO: enable upload of gzip-compressed dumps once supported on // backtrace.io (uncomment the line below). For now leave compression @@ -148,8 +149,6 @@ static bool OnCrash( _uploadFiles[L"upload_file_minidump"] = dumpFilePathGZIP; */ } - fclose(input); - fclose(dest); } bool with_record = StopSilentRecord(); diff --git a/src/openrct2/rct12/SawyerChunkReader.cpp b/src/openrct2/rct12/SawyerChunkReader.cpp index e6cf481417..ac70de20bf 100644 --- a/src/openrct2/rct12/SawyerChunkReader.cpp +++ b/src/openrct2/rct12/SawyerChunkReader.cpp @@ -169,7 +169,7 @@ namespace OpenRCT2 for (size_t n = 0; n < count; n++) { - buf.Write1(src8 + i); + buf.WriteValue(src8[i]); } } else @@ -213,7 +213,7 @@ namespace OpenRCT2 throw SawyerChunkException(EXCEPTION_MSG_CORRUPT_RLE); } i++; - buf.Write1(src8 + i); + buf.WriteValue(src8[i]); } else { @@ -253,8 +253,7 @@ namespace OpenRCT2 uint8_t code = 1; for (size_t i = 0; i < srcLength; i++) { - uint8_t temp = Numerics::ror8(src8[i], code); - buf.Write1(&temp); + buf.WriteValue(Numerics::ror8(src8[i], code)); code = (code + 2) % 8; } diff --git a/src/openrct2/scenario/ScenarioRepository.cpp b/src/openrct2/scenario/ScenarioRepository.cpp index 1ce622101e..b480eb9529 100644 --- a/src/openrct2/scenario/ScenarioRepository.cpp +++ b/src/openrct2/scenario/ScenarioRepository.cpp @@ -537,8 +537,8 @@ private: for (uint32_t i = 0; i < numHighscores; i++) { ScenarioHighscoreEntry* highscore = InsertHighscore(); - highscore->fileName = fs.ReadStdString(); - highscore->name = fs.ReadStdString(); + highscore->fileName = fs.ReadString(); + highscore->name = fs.ReadString(); highscore->company_value = fileVersion == 1 ? fs.ReadValue() : fs.ReadValue(); highscore->timestamp = fs.ReadValue(); } diff --git a/test/tests/IniWriterTest.cpp b/test/tests/IniWriterTest.cpp index 760ed6dde8..8283baf1fd 100644 --- a/test/tests/IniWriterTest.cpp +++ b/test/tests/IniWriterTest.cpp @@ -47,7 +47,7 @@ TEST_F(IniWriterTest, create_one_section) ASSERT_LE(ms.GetPosition(), 13); // Accommodate for varying-sized newline (Windows) ASSERT_EQ(ms.GetLength(), ms.GetPosition()); ms.SetPosition(0); - auto ini = ms.ReadStdString(); + auto ini = ms.ReadString(); ASSERT_STREQ(ini.c_str(), "[OpenRCT2]" PLATFORM_NEWLINE); } @@ -66,7 +66,7 @@ TEST_F(IniWriterTest, create_multiple_sections) ASSERT_LE(ms.GetPosition(), 55); // Accommodate for varying-sized newline (Windows) ASSERT_EQ(ms.GetLength(), ms.GetPosition()); ms.SetPosition(0); - auto ini = ms.ReadStdString(); + auto ini = ms.ReadString(); ASSERT_STREQ( ini.c_str(), "[OpenRCT1]" PLATFORM_NEWLINE PLATFORM_NEWLINE "[OpenRCT2]" PLATFORM_NEWLINE PLATFORM_NEWLINE @@ -85,7 +85,7 @@ TEST_F(IniWriterTest, create_loose_bool_entry) ASSERT_LE(ms.GetPosition(), 17); // Accommodate for varying-sized newline (Windows) ASSERT_EQ(ms.GetLength(), ms.GetPosition()); ms.SetPosition(0); - auto ini = ms.ReadStdString(); + auto ini = ms.ReadString(); ASSERT_STREQ(ini.c_str(), "boolval = true" PLATFORM_NEWLINE); } @@ -102,7 +102,7 @@ TEST_F(IniWriterTest, create_loose_enum_entry) ASSERT_LE(ms.GetPosition(), 37); // Accommodate for varying-sized newline (Windows) ASSERT_EQ(ms.GetLength(), ms.GetPosition()); ms.SetPosition(0); - auto ini = ms.ReadStdString(); + auto ini = ms.ReadString(); ASSERT_STREQ(ini.c_str(), "by_string = stringval" PLATFORM_NEWLINE "int32_t = 0" PLATFORM_NEWLINE); } @@ -118,7 +118,7 @@ TEST_F(IniWriterTest, create_loose_float_entry) ASSERT_LE(ms.GetPosition(), 17); // Accommodate for varying-sized newline (Windows) ASSERT_EQ(ms.GetLength(), ms.GetPosition()); ms.SetPosition(0); - auto ini = ms.ReadStdString(); + auto ini = ms.ReadString(); // This will be non-fatal due to float. EXPECT_STREQ(ini.c_str(), "one = 1.000000" PLATFORM_NEWLINE); } @@ -139,7 +139,7 @@ TEST_F(IniWriterTest, create_loose_int32_t_entry) ASSERT_LE(ms.GetPosition(), 78); // Accommodate for varying-sized newline (Windows) ASSERT_EQ(ms.GetLength(), ms.GetPosition()); ms.SetPosition(0); - auto ini = ms.ReadStdString(); + auto ini = ms.ReadString(); ASSERT_STREQ( ini.c_str(), "one = 1" PLATFORM_NEWLINE "zero = 0" PLATFORM_NEWLINE "minusone = -1" PLATFORM_NEWLINE @@ -158,7 +158,7 @@ TEST_F(IniWriterTest, create_loose_string_entry) ASSERT_LE(ms.GetPosition(), 44); // Accommodate for varying-sized newline (Windows) ASSERT_EQ(ms.GetLength(), ms.GetPosition()); ms.SetPosition(0); - auto ini = ms.ReadStdString(); + auto ini = ms.ReadString(); ASSERT_STREQ( ini.c_str(), "path = \"C:'\\\\some/dir\\\\here/\xE7\xA5\x9E\xE9\xB7\xB9\xE6\x9A\xA2\xE9\x81\x8A\"" PLATFORM_NEWLINE); } @@ -181,7 +181,7 @@ TEST_F(IniWriterTest, create_multiple_section_with_values) ASSERT_LE(ms.GetPosition(), 108); // Accommodate for varying-sized newline (Windows) ASSERT_EQ(ms.GetLength(), ms.GetPosition()); ms.SetPosition(0); - auto ini = ms.ReadStdString(); + auto ini = ms.ReadString(); ASSERT_STREQ( ini.c_str(), "[bool]" PLATFORM_NEWLINE "boolval = true" PLATFORM_NEWLINE PLATFORM_NEWLINE "[int]" PLATFORM_NEWLINE @@ -203,7 +203,7 @@ TEST_F(IniWriterTest, create_duplicate_sections) ASSERT_LE(ms.GetPosition(), 43); // Accommodate for varying-sized newline (Windows) ASSERT_EQ(ms.GetLength(), ms.GetPosition()); ms.SetPosition(0); - auto ini = ms.ReadStdString(); + auto ini = ms.ReadString(); ASSERT_STREQ( ini.c_str(), "[section]" PLATFORM_NEWLINE PLATFORM_NEWLINE "[section]" PLATFORM_NEWLINE PLATFORM_NEWLINE