From d9b8413ec40478b4237b2bd51708026aeb8c3c53 Mon Sep 17 00:00:00 2001 From: Ted John Date: Wed, 20 Jan 2021 20:42:03 +0000 Subject: [PATCH] Use IStream for zip streaming --- src/openrct2-ui/audio/AudioContext.cpp | 2 +- src/openrct2-ui/audio/AudioContext.h | 3 +- src/openrct2-ui/audio/FileAudioSource.cpp | 33 +-- src/openrct2/audio/AudioContext.h | 4 +- src/openrct2/audio/AudioMixer.cpp | 2 +- src/openrct2/audio/AudioMixer.h | 4 +- src/openrct2/audio/DummyAudioContext.cpp | 2 +- src/openrct2/core/MemoryStream.cpp | 10 + src/openrct2/core/MemoryStream.h | 2 + src/openrct2/core/Zip.cpp | 292 ++++++++++++---------- src/openrct2/core/Zip.h | 7 +- src/openrct2/core/ZipAndroid.cpp | 35 +-- src/openrct2/object/Object.cpp | 72 +++++- src/openrct2/object/Object.h | 4 +- 14 files changed, 263 insertions(+), 209 deletions(-) diff --git a/src/openrct2-ui/audio/AudioContext.cpp b/src/openrct2-ui/audio/AudioContext.cpp index 35d2a254f6..6384c5ac46 100644 --- a/src/openrct2-ui/audio/AudioContext.cpp +++ b/src/openrct2-ui/audio/AudioContext.cpp @@ -70,7 +70,7 @@ namespace OpenRCT2::Audio return AudioSource::CreateStreamFromWAV(path); } - IAudioSource* CreateStreamFromWAV(std::unique_ptr stream) override + IAudioSource* CreateStreamFromWAV(std::unique_ptr stream) override { return AudioSource::CreateStreamFromWAV(std::move(stream)); } diff --git a/src/openrct2-ui/audio/AudioContext.h b/src/openrct2-ui/audio/AudioContext.h index 9babdb4635..7f4a48ebd6 100644 --- a/src/openrct2-ui/audio/AudioContext.h +++ b/src/openrct2-ui/audio/AudioContext.h @@ -9,7 +9,6 @@ #pragma once -#include #include #include #include @@ -67,7 +66,7 @@ namespace OpenRCT2::Audio IAudioSource* CreateMemoryFromWAV(const std::string& path, const AudioFormat* targetFormat = nullptr); IAudioSource* CreateStreamFromWAV(const std::string& path); IAudioSource* CreateStreamFromWAV(SDL_RWops* rw); - IAudioSource* CreateStreamFromWAV(std::unique_ptr stream); + IAudioSource* CreateStreamFromWAV(std::unique_ptr stream); } // namespace AudioSource namespace AudioChannel diff --git a/src/openrct2-ui/audio/FileAudioSource.cpp b/src/openrct2-ui/audio/FileAudioSource.cpp index e9a504a845..60f4087b25 100644 --- a/src/openrct2-ui/audio/FileAudioSource.cpp +++ b/src/openrct2-ui/audio/FileAudioSource.cpp @@ -203,40 +203,27 @@ namespace OpenRCT2::Audio return source; } - IAudioSource* AudioSource::CreateStreamFromWAV(std::unique_ptr stream) + IAudioSource* AudioSource::CreateStreamFromWAV(std::unique_ptr stream) { - using streamptr = std::unique_ptr*; - auto data = new std::unique_ptr(std::move(stream)); - auto rw = new SDL_RWops(); *rw = {}; rw->type = SDL_RWOPS_UNKNOWN; - rw->hidden.unknown.data1 = data; + rw->hidden.unknown.data1 = stream.release(); rw->seek = [](SDL_RWops* ctx, Sint64 offset, int whence) { - auto ptr = static_cast(ctx->hidden.unknown.data1); - auto dir = std::ios_base::beg; - if (whence == RW_SEEK_CUR) - { - dir = std::ios_base::cur; - } - else if (whence == RW_SEEK_END) - { - dir = std::ios_base::end; - } - (*ptr)->seekg(offset, dir); - return (*ptr)->fail() ? -1 : static_cast((*ptr)->tellg()); + auto ptr = static_cast(ctx->hidden.unknown.data1); + ptr->Seek(offset, whence); + return static_cast(ptr->GetPosition()); }; rw->read = [](SDL_RWops* ctx, void* buf, size_t size, size_t maxnum) { - auto ptr = static_cast(ctx->hidden.unknown.data1); - (*ptr)->read(static_cast(buf), size * maxnum); - return static_cast((*ptr)->gcount() / size); + auto ptr = static_cast(ctx->hidden.unknown.data1); + return static_cast(ptr->TryRead(buf, size * maxnum) / size); }; rw->size = [](SDL_RWops* ctx) { - auto ptr = static_cast(ctx->hidden.unknown.data1); - return static_cast((*ptr)->tellg()); + auto ptr = static_cast(ctx->hidden.unknown.data1); + return static_cast(ptr->GetLength()); }; rw->close = [](SDL_RWops* ctx) { - auto ptr = static_cast(ctx->hidden.unknown.data1); + auto ptr = static_cast(ctx->hidden.unknown.data1); delete ptr; ctx->hidden.unknown.data1 = nullptr; delete ctx; diff --git a/src/openrct2/audio/AudioContext.h b/src/openrct2/audio/AudioContext.h index b4495cfd33..af7ab7bfdb 100644 --- a/src/openrct2/audio/AudioContext.h +++ b/src/openrct2/audio/AudioContext.h @@ -10,8 +10,8 @@ #pragma once #include "../common.h" +#include "../core/IStream.hpp" -#include #include #include #include @@ -35,7 +35,7 @@ namespace OpenRCT2::Audio virtual void SetOutputDevice(const std::string& deviceName) abstract; virtual IAudioSource* CreateStreamFromWAV(const std::string& path) abstract; - virtual IAudioSource* CreateStreamFromWAV(std::unique_ptr stream) abstract; + virtual IAudioSource* CreateStreamFromWAV(std::unique_ptr stream) abstract; virtual void StartTitleMusic() abstract; diff --git a/src/openrct2/audio/AudioMixer.cpp b/src/openrct2/audio/AudioMixer.cpp index 67b96d06ed..0e1e6be809 100644 --- a/src/openrct2/audio/AudioMixer.cpp +++ b/src/openrct2/audio/AudioMixer.cpp @@ -190,7 +190,7 @@ void* Mixer_Play_Music(const char* path, int32_t loop) return channel; } -void* Mixer_Play_Music(std::unique_ptr stream, int32_t loop) +void* Mixer_Play_Music(std::unique_ptr stream, int32_t loop) { IAudioChannel* channel = nullptr; IAudioMixer* mixer = GetMixer(); diff --git a/src/openrct2/audio/AudioMixer.h b/src/openrct2/audio/AudioMixer.h index e844713e8e..fffb0bd447 100644 --- a/src/openrct2/audio/AudioMixer.h +++ b/src/openrct2/audio/AudioMixer.h @@ -10,8 +10,8 @@ #pragma once #include "../common.h" +#include "../core/IStream.hpp" -#include #include #define MIXER_VOLUME_MAX 128 @@ -73,7 +73,7 @@ int32_t Mixer_Channel_SetOffset(void* channel, uint64_t offset); void Mixer_Channel_SetGroup(void* channel, OpenRCT2::Audio::MixerGroup group); void* Mixer_Play_Music(int32_t pathId, int32_t loop, int32_t streaming); void* Mixer_Play_Music(const char* path, int32_t loop); -void* Mixer_Play_Music(std::unique_ptr stream, int32_t loop); +void* Mixer_Play_Music(std::unique_ptr stream, int32_t loop); void Mixer_SetVolume(float volume); int32_t DStoMixerVolume(int32_t volume); diff --git a/src/openrct2/audio/DummyAudioContext.cpp b/src/openrct2/audio/DummyAudioContext.cpp index abc1d79aac..09d05904fd 100644 --- a/src/openrct2/audio/DummyAudioContext.cpp +++ b/src/openrct2/audio/DummyAudioContext.cpp @@ -31,7 +31,7 @@ namespace OpenRCT2::Audio return nullptr; } - IAudioSource* CreateStreamFromWAV(std::unique_ptr) override + IAudioSource* CreateStreamFromWAV(std::unique_ptr) override { return nullptr; } diff --git a/src/openrct2/core/MemoryStream.cpp b/src/openrct2/core/MemoryStream.cpp index c305403bdc..79329aaf7b 100644 --- a/src/openrct2/core/MemoryStream.cpp +++ b/src/openrct2/core/MemoryStream.cpp @@ -51,6 +51,16 @@ namespace OpenRCT2 { } + 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()); + } + MemoryStream::MemoryStream(MemoryStream&& mv) noexcept : _access(mv._access) , _dataCapacity(mv._dataCapacity) diff --git a/src/openrct2/core/MemoryStream.h b/src/openrct2/core/MemoryStream.h index 0f34e973d9..918d0a6f6d 100644 --- a/src/openrct2/core/MemoryStream.h +++ b/src/openrct2/core/MemoryStream.h @@ -13,6 +13,7 @@ #include "IStream.hpp" #include +#include namespace OpenRCT2 { @@ -42,6 +43,7 @@ namespace OpenRCT2 explicit MemoryStream(size_t capacity); MemoryStream(void* data, size_t dataSize, uint8_t access = MEMORY_ACCESS::READ); MemoryStream(const void* data, size_t dataSize); + MemoryStream(std::vector&& v); virtual ~MemoryStream(); MemoryStream& operator=(MemoryStream&& mv) noexcept; diff --git a/src/openrct2/core/Zip.cpp b/src/openrct2/core/Zip.cpp index 92ee14d472..039dc31661 100644 --- a/src/openrct2/core/Zip.cpp +++ b/src/openrct2/core/Zip.cpp @@ -16,6 +16,8 @@ # include #endif +using namespace OpenRCT2; + static std::string NormalisePath(std::string_view path) { std::string result; @@ -148,12 +150,12 @@ public: return result; } - std::unique_ptr GetFileStream(std::string_view path) const override + std::unique_ptr GetFileStream(std::string_view path) const override { auto index = GetIndexFromPath(path); if (index) { - return std::make_unique(_zip, *index); + return std::make_unique(_zip, *index); } return {}; } @@ -204,145 +206,165 @@ public: } private: - class ifilestream final : public std::istream + class ZipItemStream final : public IStream { private: - class ifilestreambuf final : public std::streambuf - { - private: - zip* _zip; - zip_int64_t _index; - zip_file_t* _zipFile{}; - zip_int64_t _pos{}; - zip_int64_t _maxLen{}; - - public: - ifilestreambuf(zip* zip, zip_int64_t index) - : _zip(zip) - , _index(index) - { - } - - ifilestreambuf(const ifilestreambuf&) = delete; - - ~ifilestreambuf() override - { - close(); - } - - private: - void close() - { - if (_zipFile != nullptr) - { - zip_fclose(_zipFile); - _zipFile = nullptr; - } - } - - bool reset() - { - close(); - - _pos = 0; - _maxLen = 0; - _zipFile = zip_fopen_index(_zip, _index, 0); - if (_zipFile == nullptr) - { - return false; - } - - zip_stat_t zipFileStat{}; - if (zip_stat_index(_zip, _index, 0, &zipFileStat) != ZIP_ER_OK) - { - return false; - } - - _maxLen = zipFileStat.size; - return true; - } - - std::streamsize xsgetn(char_type* dst, std::streamsize len) override - { - if (_zipFile == nullptr && !reset()) - { - return 0; - } - - auto read = zip_fread(_zipFile, dst, len); - if (read <= 0) - { - return 0; - } - _pos += read; - return read; - } - - void skip(zip_int64_t len) - { - if (_zipFile != nullptr || reset()) - { - char buffer[2048]{}; - while (len > 0) - { - auto readLen = std::min(len, sizeof(buffer)); - auto read = zip_fread(_zipFile, buffer, readLen); - if (read <= 0) - { - break; - } - _pos += read; - len -= read; - } - } - } - - pos_type seekpos(pos_type pos, ios_base::openmode mode) override final - { - if (pos > _pos) - { - // Read to seek fowards - skip(pos - _pos); - } - else if (pos < _pos) - { - // Can not seek backwards, start from the beginning - reset(); - skip(pos); - } - return std::clamp(pos, 0, _maxLen); - } - - pos_type seekoff(off_type off, ios_base::seekdir dir, ios_base::openmode mode) override - { - if (dir == std::ios::beg) - { - return seekpos(off, std::ios::in); - } - else if (dir == std::ios::cur) - { - return seekpos(_pos + off, std::ios::in); - } - else if (dir == std::ios::end) - { - return seekpos(_maxLen - off, std::ios::in); - } - else - { - return std::streampos(-1); - } - } - }; - - private: - ifilestreambuf _streambuf; + zip* _zip; + zip_int64_t _index; + zip_file_t* _zipFile{}; + zip_uint64_t _len{}; + zip_uint64_t _pos{}; public: - ifilestream(zip* zip, zip_int64_t index) - : std::istream(&_streambuf) - , _streambuf(zip, index) + ZipItemStream(zip* zip, zip_int64_t index) + : _zip(zip) + , _index(index) { } + + ~ZipItemStream() override + { + Close(); + } + + bool CanRead() const override + { + return true; + } + + bool CanWrite() const override + { + return false; + } + + uint64_t GetLength() const override + { + return _len; + } + + uint64_t GetPosition() const override + { + return _pos; + } + + void SetPosition(uint64_t position) override + { + if (position > _pos) + { + // Read to seek forwards + Skip(position - _pos); + } + else if (position < _pos) + { + // Can not seek backwards, start from the beginning + Reset(); + Skip(position); + } + } + + void Seek(int64_t offset, int32_t origin) override + { + switch (origin) + { + case STREAM_SEEK_BEGIN: + SetPosition(offset); + break; + case STREAM_SEEK_CURRENT: + SetPosition(_pos + offset); + break; + case STREAM_SEEK_END: + SetPosition(_len - offset); + break; + } + } + + void Read(void* buffer, uint64_t length) override + { + size_t readBytes = TryRead(buffer, length); + if (readBytes != length) + { + throw IOException("Attempted to read past end of file."); + } + } + + void Write(const void* buffer, uint64_t length) override + { + throw IOException("Stream is read-only."); + } + + uint64_t TryRead(void* buffer, uint64_t length) override + { + if (_zipFile == nullptr && !Reset()) + { + return 0; + } + + auto readBytes = zip_fread(_zipFile, buffer, length); + if (readBytes < 0) + { + return 0; + } + else + { + _pos += readBytes; + return static_cast(readBytes); + } + } + + const void* GetData() const override + { + return nullptr; + } + + private: + void Close() + { + if (_zipFile != nullptr) + { + zip_fclose(_zipFile); + _zipFile = nullptr; + } + } + + bool Reset() + { + Close(); + + _pos = 0; + _len = 0; + _zipFile = zip_fopen_index(_zip, _index, 0); + if (_zipFile == nullptr) + { + return false; + } + + zip_stat_t zipFileStat{}; + if (zip_stat_index(_zip, _index, 0, &zipFileStat) != ZIP_ER_OK) + { + return false; + } + + _len = zipFileStat.size; + return true; + } + + void Skip(zip_int64_t len) + { + // zip_fseek can not be used on compressed data, so skip bytes by + // reading into a temporary buffer + char buffer[2048]{}; + while (len > 0) + { + auto readLen = std::min(len, sizeof(buffer)); + auto read = zip_fread(_zipFile, buffer, readLen); + if (read <= 0) + { + break; + } + _pos += read; + len -= read; + } + } }; }; diff --git a/src/openrct2/core/Zip.h b/src/openrct2/core/Zip.h index 7582cffebf..01d5e8b17b 100644 --- a/src/openrct2/core/Zip.h +++ b/src/openrct2/core/Zip.h @@ -17,6 +17,11 @@ #include #include +namespace OpenRCT2 +{ + struct IStream; +} + /** * Represents a zip file. */ @@ -30,7 +35,7 @@ struct IZipArchive virtual std::string GetFileName(size_t index) const abstract; virtual uint64_t GetFileSize(size_t index) const abstract; virtual std::vector GetFileData(std::string_view path) const abstract; - virtual std::unique_ptr GetFileStream(std::string_view path) const abstract; + virtual std::unique_ptr GetFileStream(std::string_view path) const abstract; /** * Creates or overwrites a file within the zip archive to the given data buffer. diff --git a/src/openrct2/core/ZipAndroid.cpp b/src/openrct2/core/ZipAndroid.cpp index ff8b1e3ae1..c891b7023b 100644 --- a/src/openrct2/core/ZipAndroid.cpp +++ b/src/openrct2/core/ZipAndroid.cpp @@ -11,11 +11,14 @@ # include "../platform/platform.h" # include "IStream.hpp" +# include "MemoryStream.h" # include "Zip.h" # include # include +using namespace OpenRCT2; + class ZipArchive final : public IZipArchive { private: @@ -113,10 +116,10 @@ public: return std::vector(dataPtr, dataPtr + dataSize); } - std::unique_ptr GetFileStream(std::string_view path) const override + std::unique_ptr GetFileStream(std::string_view path) const override { auto data = GetFileData(path); - return std::make_unique(std::move(data)); + return std::make_unique(std::move(data)); } void SetFileData(std::string_view path, std::vector&& data) override @@ -133,34 +136,6 @@ public: { STUB(); } - -private: - class memstream final : public std::istream - { - private: - class vector_streambuf : public std::basic_streambuf> - { - public: - explicit vector_streambuf(const std::vector& vec) - { - this->setg( - reinterpret_cast(const_cast(vec.data())), - reinterpret_cast(const_cast(vec.data())), - reinterpret_cast(const_cast(vec.data() + vec.size()))); - } - }; - - std::vector _data; - vector_streambuf _streambuf; - - public: - memstream(std::vector&& data) - : std::istream(&_streambuf) - , _data(data) - , _streambuf(_data) - { - } - }; }; namespace Zip diff --git a/src/openrct2/object/Object.cpp b/src/openrct2/object/Object.cpp index 433d2aad14..547ad8f996 100644 --- a/src/openrct2/object/Object.cpp +++ b/src/openrct2/object/Object.cpp @@ -11,6 +11,7 @@ #include "../Context.h" #include "../core/File.h" +#include "../core/FileStream.h" #include "../core/Memory.hpp" #include "../core/String.hpp" #include "../core/Zip.h" @@ -24,6 +25,8 @@ #include #include +using namespace OpenRCT2; + ObjectType& operator++(ObjectType& d, int) { return d = (d == ObjectType::Count) ? ObjectType::Ride : static_cast(static_cast(d) + 1); @@ -167,19 +170,72 @@ std::optional rct_object_entry::GetSceneryType() const } } -class zipstreamwrapper final : public std::istream +/** + * Couples a zip archive and a zip item stream to ensure the lifetime of the zip archive is maintained + * for the lifetime of the stream. + */ +class ZipStreamWrapper final : public IStream { private: std::unique_ptr _zipArchive; - std::unique_ptr _base; + std::unique_ptr _base; public: - zipstreamwrapper(std::unique_ptr zipArchive, std::unique_ptr base) - : std::istream(base->rdbuf()) - , _zipArchive(std::move(zipArchive)) + ZipStreamWrapper(std::unique_ptr zipArchive, std::unique_ptr base) + : _zipArchive(std::move(zipArchive)) , _base(std::move(base)) { } + + bool CanRead() const override + { + return _base->CanRead(); + } + + bool CanWrite() const override + { + return _base->CanWrite(); + } + + uint64_t GetLength() const override + { + return _base->GetLength(); + } + + uint64_t GetPosition() const override + { + return _base->GetPosition(); + } + + void SetPosition(uint64_t position) override + { + _base->SetPosition(position); + } + + void Seek(int64_t offset, int32_t origin) override + { + _base->Seek(offset, origin); + } + + void Read(void* buffer, uint64_t length) override + { + _base->Read(buffer, length); + } + + void Write(const void* buffer, uint64_t length) override + { + _base->Write(buffer, length); + } + + uint64_t TryRead(void* buffer, uint64_t length) override + { + return _base->TryRead(buffer, length); + } + + const void* GetData() const override + { + return _base->GetData(); + } }; bool ObjectAsset::IsAvailable() const @@ -224,11 +280,11 @@ size_t ObjectAsset::GetLength() const return 0; } -std::unique_ptr ObjectAsset::GetStream() const +std::unique_ptr ObjectAsset::GetStream() const { if (_zipPath.empty()) { - return std::make_unique(_path, std::ios::binary); + return std::make_unique(_path, FILE_MODE_OPEN); } else { @@ -238,7 +294,7 @@ std::unique_ptr ObjectAsset::GetStream() const auto stream = zipArchive->GetFileStream(_path); if (stream != nullptr) { - return std::make_unique(std::move(zipArchive), std::move(stream)); + return std::make_unique(std::move(zipArchive), std::move(stream)); } } } diff --git a/src/openrct2/object/Object.h b/src/openrct2/object/Object.h index 41a9cdb826..0578fef392 100644 --- a/src/openrct2/object/Object.h +++ b/src/openrct2/object/Object.h @@ -15,8 +15,6 @@ #include "ImageTable.h" #include "StringTable.h" -#include -#include #include #include #include @@ -223,7 +221,7 @@ public: bool IsAvailable() const; size_t GetLength() const; - std::unique_ptr GetStream() const; + std::unique_ptr GetStream() const; }; struct IReadObjectContext