From c79eb0609b4db4e60d0587493215eab52a5e34a2 Mon Sep 17 00:00:00 2001 From: LRFLEW Date: Sun, 20 Jul 2025 19:03:52 -0500 Subject: [PATCH] Don't Double-Compress Park Chunks in Replay File (#24728) --- src/openrct2/ReplayManager.cpp | 9 ++++---- src/openrct2/core/OrcaStream.hpp | 28 ++++++++++++++++++------- src/openrct2/park/ParkFile.cpp | 36 ++++++++++++++++---------------- src/openrct2/park/ParkFile.h | 24 +++++++++++++-------- 4 files changed, 58 insertions(+), 39 deletions(-) diff --git a/src/openrct2/ReplayManager.cpp b/src/openrct2/ReplayManager.cpp index 7eef78f284..03cbc01a99 100644 --- a/src/openrct2/ReplayManager.cpp +++ b/src/openrct2/ReplayManager.cpp @@ -261,7 +261,7 @@ namespace OpenRCT2 auto exporter = std::make_unique(); exporter->ExportObjectsList = objects; - exporter->Export(gameState, replayData->parkData); + exporter->Export(gameState, replayData->parkData, Compression::kNoCompressionLevel); replayData->timeRecorded = std::chrono::seconds(std::time(nullptr)).count(); @@ -314,8 +314,7 @@ namespace OpenRCT2 MemoryStream compressed; stream.SetPosition(0); bool compressStatus = Compression::zlibCompress( - stream, static_cast(stream.GetLength()), compressed, Compression::ZlibHeaderType::zlib, - kReplayCompressionLevel); + stream, stream.GetLength(), compressed, Compression::ZlibHeaderType::zlib, kReplayCompressionLevel); if (!compressStatus) throw IOException("Compression Error"); @@ -567,8 +566,8 @@ namespace OpenRCT2 recFile.data.SetPosition(0); decompressStatus = Compression::zlibDecompress( - recFile.data, static_cast(recFile.data.GetLength()), decompressed, - static_cast(recFile.uncompressedSize), Compression::ZlibHeaderType::zlib); + recFile.data, recFile.data.GetLength(), decompressed, recFile.uncompressedSize, + Compression::ZlibHeaderType::zlib); if (!decompressStatus) throw IOException("Decompression Error"); diff --git a/src/openrct2/core/OrcaStream.hpp b/src/openrct2/core/OrcaStream.hpp index bbeb8b0ef8..86b98d722a 100644 --- a/src/openrct2/core/OrcaStream.hpp +++ b/src/openrct2/core/OrcaStream.hpp @@ -73,12 +73,14 @@ namespace OpenRCT2 sfl::small_vector _chunks; MemoryStream _buffer; ChunkEntry _currentChunk; + int16_t _compressionLevel; public: - OrcaStream(IStream& stream, const Mode mode) + OrcaStream(IStream& stream, const Mode mode, int16_t compressionLevel = Compression::kZlibDefaultCompressionLevel) { _stream = &stream; _mode = mode; + _compressionLevel = compressionLevel; if (mode == Mode::READING) { _header = _stream->ReadValue
(); @@ -93,15 +95,14 @@ namespace OpenRCT2 // Uncompress if (_header.Compression != CompressionType::none) { - size_t compressedSize = static_cast(_header.CompressedSize); - size_t uncompressedSize = static_cast(_header.UncompressedSize); bool decompressStatus = false; switch (_header.Compression) { case CompressionType::gzip: decompressStatus = Compression::zlibDecompress( - *_stream, compressedSize, _buffer, uncompressedSize, Compression::ZlibHeaderType::gzip); + *_stream, _header.CompressedSize, _buffer, _header.UncompressedSize, + Compression::ZlibHeaderType::gzip); break; default: throw IOException("Unknown Park Compression Type"); @@ -129,7 +130,8 @@ namespace OpenRCT2 else { _header = {}; - _header.Compression = CompressionType::gzip; + _header.Compression = _compressionLevel == Compression::kNoCompressionLevel ? CompressionType::none + : CompressionType::gzip; _buffer = MemoryStream{}; } } @@ -145,11 +147,13 @@ namespace OpenRCT2 _header.CompressedSize = _buffer.GetLength(); _header.FNV1a = Crypt::FNV1a(_buffer.GetData(), _buffer.GetLength()); + if (_compressionLevel == Compression::kNoCompressionLevel) + _header.Compression = CompressionType::none; + // Compress data if (_header.Compression != CompressionType::none) { MemoryStream compressed; - size_t bufferLength = static_cast(_buffer.GetLength()); bool compressStatus = false; _buffer.SetPosition(0); @@ -157,7 +161,7 @@ namespace OpenRCT2 { case CompressionType::gzip: compressStatus = Compression::zlibCompress( - _buffer, bufferLength, compressed, Compression::ZlibHeaderType::gzip); + _buffer, _buffer.GetLength(), compressed, Compression::ZlibHeaderType::gzip, _compressionLevel); break; default: break; @@ -199,6 +203,16 @@ namespace OpenRCT2 return _header; } + int16_t GetCompressionLevel() const + { + return _compressionLevel; + } + + void SetCompressionLevel(int16_t compressionLevel) + { + _compressionLevel = compressionLevel; + } + template bool ReadWriteChunk(const uint32_t chunkId, TFunc f) { diff --git a/src/openrct2/park/ParkFile.cpp b/src/openrct2/park/ParkFile.cpp index 9f06105455..306f797cba 100644 --- a/src/openrct2/park/ParkFile.cpp +++ b/src/openrct2/park/ParkFile.cpp @@ -177,9 +177,9 @@ namespace OpenRCT2 gameState.initialCash = gameState.cash; } - void Save(GameState_t& gameState, IStream& stream) + void Save(GameState_t& gameState, IStream& stream, int16_t compressionLevel) { - OrcaStream os(stream, OrcaStream::Mode::WRITING); + OrcaStream os(stream, OrcaStream::Mode::WRITING, compressionLevel); auto& header = os.GetHeader(); header.Magic = kParkFileMagic; @@ -206,10 +206,10 @@ namespace OpenRCT2 ReadWritePackedObjectsChunk(os); } - void Save(GameState_t& gameState, const std::string_view path) + void Save(GameState_t& gameState, const std::string_view path, int16_t compressionLevel) { FileStream fs(path, FileMode::write); - Save(gameState, fs); + Save(gameState, fs, compressionLevel); } ScenarioIndexEntry ReadScenarioChunk() @@ -2706,21 +2706,21 @@ namespace OpenRCT2 } }); } + + void ParkFileExporter::Export(GameState_t& gameState, std::string_view path, int16_t compressionLevel) + { + auto parkFile = std::make_unique(); + parkFile->Save(gameState, path, compressionLevel); + } + + void ParkFileExporter::Export(GameState_t& gameState, IStream& stream, int16_t compressionLevel) + { + auto parkFile = std::make_unique(); + parkFile->ExportObjectsList = ExportObjectsList; + parkFile->Save(gameState, stream, compressionLevel); + } } // namespace OpenRCT2 -void ParkFileExporter::Export(GameState_t& gameState, std::string_view path) -{ - auto parkFile = std::make_unique(); - parkFile->Save(gameState, path); -} - -void ParkFileExporter::Export(GameState_t& gameState, IStream& stream) -{ - auto parkFile = std::make_unique(); - parkFile->ExportObjectsList = ExportObjectsList; - parkFile->Save(gameState, stream); -} - enum : uint32_t { S6_SAVE_FLAG_EXPORT = 1 << 0, @@ -2766,7 +2766,7 @@ int32_t ScenarioSave(GameState_t& gameState, u8string_view path, int32_t flags) { // s6exporter->SaveGame(path); } - parkFile->Save(gameState, path); + parkFile->Save(gameState, path, Compression::kZlibDefaultCompressionLevel); result = true; } catch (const std::exception& e) diff --git a/src/openrct2/park/ParkFile.h b/src/openrct2/park/ParkFile.h index d432103f30..e6475ce67f 100644 --- a/src/openrct2/park/ParkFile.h +++ b/src/openrct2/park/ParkFile.h @@ -9,6 +9,8 @@ #pragma once +#include "../core/Compression.h" + #include #include #include @@ -54,13 +56,17 @@ namespace OpenRCT2 constexpr uint16_t kExtendedGoKartsVersion = 54; constexpr uint16_t kHigherInversionsHolesHelicesStatsVersion = 55; constexpr uint16_t kFixedObsoleteFootpathsVersion = 56; + + class ParkFileExporter + { + public: + std::vector ExportObjectsList; + + void Export( + OpenRCT2::GameState_t& gameState, std::string_view path, + int16_t compressionLevel = Compression::kZlibDefaultCompressionLevel); + void Export( + OpenRCT2::GameState_t& gameState, OpenRCT2::IStream& stream, + int16_t compressionLevel = Compression::kZlibDefaultCompressionLevel); + }; } // namespace OpenRCT2 - -class ParkFileExporter -{ -public: - std::vector ExportObjectsList; - - void Export(OpenRCT2::GameState_t& gameState, std::string_view path); - void Export(OpenRCT2::GameState_t& gameState, OpenRCT2::IStream& stream); -};