Use IStream for zip streaming

This commit is contained in:
Ted John 2021-01-20 20:42:03 +00:00
parent 2aa2a94a7e
commit d9b8413ec4
14 changed files with 263 additions and 209 deletions

View File

@ -70,7 +70,7 @@ namespace OpenRCT2::Audio
return AudioSource::CreateStreamFromWAV(path);
}
IAudioSource* CreateStreamFromWAV(std::unique_ptr<std::istream> stream) override
IAudioSource* CreateStreamFromWAV(std::unique_ptr<IStream> stream) override
{
return AudioSource::CreateStreamFromWAV(std::move(stream));
}

View File

@ -9,7 +9,6 @@
#pragma once
#include <istream>
#include <memory>
#include <openrct2/audio/AudioChannel.h>
#include <openrct2/audio/AudioSource.h>
@ -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<std::istream> stream);
IAudioSource* CreateStreamFromWAV(std::unique_ptr<IStream> stream);
} // namespace AudioSource
namespace AudioChannel

View File

@ -203,40 +203,27 @@ namespace OpenRCT2::Audio
return source;
}
IAudioSource* AudioSource::CreateStreamFromWAV(std::unique_ptr<std::istream> stream)
IAudioSource* AudioSource::CreateStreamFromWAV(std::unique_ptr<IStream> stream)
{
using streamptr = std::unique_ptr<std::istream>*;
auto data = new std::unique_ptr<std::istream>(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<streamptr>(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<Sint64>((*ptr)->tellg());
auto ptr = static_cast<IStream*>(ctx->hidden.unknown.data1);
ptr->Seek(offset, whence);
return static_cast<Sint64>(ptr->GetPosition());
};
rw->read = [](SDL_RWops* ctx, void* buf, size_t size, size_t maxnum) {
auto ptr = static_cast<streamptr>(ctx->hidden.unknown.data1);
(*ptr)->read(static_cast<char*>(buf), size * maxnum);
return static_cast<size_t>((*ptr)->gcount() / size);
auto ptr = static_cast<IStream*>(ctx->hidden.unknown.data1);
return static_cast<size_t>(ptr->TryRead(buf, size * maxnum) / size);
};
rw->size = [](SDL_RWops* ctx) {
auto ptr = static_cast<streamptr>(ctx->hidden.unknown.data1);
return static_cast<Sint64>((*ptr)->tellg());
auto ptr = static_cast<IStream*>(ctx->hidden.unknown.data1);
return static_cast<Sint64>(ptr->GetLength());
};
rw->close = [](SDL_RWops* ctx) {
auto ptr = static_cast<streamptr>(ctx->hidden.unknown.data1);
auto ptr = static_cast<IStream*>(ctx->hidden.unknown.data1);
delete ptr;
ctx->hidden.unknown.data1 = nullptr;
delete ctx;

View File

@ -10,8 +10,8 @@
#pragma once
#include "../common.h"
#include "../core/IStream.hpp"
#include <istream>
#include <memory>
#include <string>
#include <vector>
@ -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<std::istream> stream) abstract;
virtual IAudioSource* CreateStreamFromWAV(std::unique_ptr<IStream> stream) abstract;
virtual void StartTitleMusic() abstract;

View File

@ -190,7 +190,7 @@ void* Mixer_Play_Music(const char* path, int32_t loop)
return channel;
}
void* Mixer_Play_Music(std::unique_ptr<std::istream> stream, int32_t loop)
void* Mixer_Play_Music(std::unique_ptr<IStream> stream, int32_t loop)
{
IAudioChannel* channel = nullptr;
IAudioMixer* mixer = GetMixer();

View File

@ -10,8 +10,8 @@
#pragma once
#include "../common.h"
#include "../core/IStream.hpp"
#include <istream>
#include <memory>
#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<std::istream> stream, int32_t loop);
void* Mixer_Play_Music(std::unique_ptr<OpenRCT2::IStream> stream, int32_t loop);
void Mixer_SetVolume(float volume);
int32_t DStoMixerVolume(int32_t volume);

View File

@ -31,7 +31,7 @@ namespace OpenRCT2::Audio
return nullptr;
}
IAudioSource* CreateStreamFromWAV(std::unique_ptr<std::istream>) override
IAudioSource* CreateStreamFromWAV(std::unique_ptr<IStream>) override
{
return nullptr;
}

View File

@ -51,6 +51,16 @@ namespace OpenRCT2
{
}
MemoryStream::MemoryStream(std::vector<uint8_t>&& v)
{
_access = MEMORY_ACCESS::OWNER;
_dataCapacity = v.size();
_dataSize = v.size();
_data = Memory::Allocate<void>(v.size());
_position = _data;
std::memcpy(_data, v.data(), v.size());
}
MemoryStream::MemoryStream(MemoryStream&& mv) noexcept
: _access(mv._access)
, _dataCapacity(mv._dataCapacity)

View File

@ -13,6 +13,7 @@
#include "IStream.hpp"
#include <algorithm>
#include <vector>
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<uint8_t>&& v);
virtual ~MemoryStream();
MemoryStream& operator=(MemoryStream&& mv) noexcept;

View File

@ -16,6 +16,8 @@
# include <zip.h>
#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<std::istream> GetFileStream(std::string_view path) const override
std::unique_ptr<IStream> GetFileStream(std::string_view path) const override
{
auto index = GetIndexFromPath(path);
if (index)
{
return std::make_unique<ifilestream>(_zip, *index);
return std::make_unique<ZipItemStream>(_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<zip_int64_t>(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_type>(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<uint64_t>(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<zip_int64_t>(len, sizeof(buffer));
auto read = zip_fread(_zipFile, buffer, readLen);
if (read <= 0)
{
break;
}
_pos += read;
len -= read;
}
}
};
};

View File

@ -17,6 +17,11 @@
#include <string_view>
#include <vector>
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<uint8_t> GetFileData(std::string_view path) const abstract;
virtual std::unique_ptr<std::istream> GetFileStream(std::string_view path) const abstract;
virtual std::unique_ptr<OpenRCT2::IStream> GetFileStream(std::string_view path) const abstract;
/**
* Creates or overwrites a file within the zip archive to the given data buffer.

View File

@ -11,11 +11,14 @@
# include "../platform/platform.h"
# include "IStream.hpp"
# include "MemoryStream.h"
# include "Zip.h"
# include <SDL.h>
# include <jni.h>
using namespace OpenRCT2;
class ZipArchive final : public IZipArchive
{
private:
@ -113,10 +116,10 @@ public:
return std::vector<uint8_t>(dataPtr, dataPtr + dataSize);
}
std::unique_ptr<std::istream> GetFileStream(std::string_view path) const override
std::unique_ptr<IStream> GetFileStream(std::string_view path) const override
{
auto data = GetFileData(path);
return std::make_unique<memstream>(std::move(data));
return std::make_unique<MemoryStream>(std::move(data));
}
void SetFileData(std::string_view path, std::vector<uint8_t>&& data) override
@ -133,34 +136,6 @@ public:
{
STUB();
}
private:
class memstream final : public std::istream
{
private:
class vector_streambuf : public std::basic_streambuf<char, std::char_traits<char>>
{
public:
explicit vector_streambuf(const std::vector<uint8_t>& vec)
{
this->setg(
reinterpret_cast<char*>(const_cast<unsigned char*>(vec.data())),
reinterpret_cast<char*>(const_cast<unsigned char*>(vec.data())),
reinterpret_cast<char*>(const_cast<unsigned char*>(vec.data() + vec.size())));
}
};
std::vector<uint8_t> _data;
vector_streambuf _streambuf;
public:
memstream(std::vector<uint8_t>&& data)
: std::istream(&_streambuf)
, _data(data)
, _streambuf(_data)
{
}
};
};
namespace Zip

View File

@ -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 <cstring>
#include <stdexcept>
using namespace OpenRCT2;
ObjectType& operator++(ObjectType& d, int)
{
return d = (d == ObjectType::Count) ? ObjectType::Ride : static_cast<ObjectType>(static_cast<uint8_t>(d) + 1);
@ -167,19 +170,72 @@ std::optional<uint8_t> 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<IZipArchive> _zipArchive;
std::unique_ptr<std::istream> _base;
std::unique_ptr<IStream> _base;
public:
zipstreamwrapper(std::unique_ptr<IZipArchive> zipArchive, std::unique_ptr<std::istream> base)
: std::istream(base->rdbuf())
, _zipArchive(std::move(zipArchive))
ZipStreamWrapper(std::unique_ptr<IZipArchive> zipArchive, std::unique_ptr<IStream> 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<std::istream> ObjectAsset::GetStream() const
std::unique_ptr<IStream> ObjectAsset::GetStream() const
{
if (_zipPath.empty())
{
return std::make_unique<std::ifstream>(_path, std::ios::binary);
return std::make_unique<FileStream>(_path, FILE_MODE_OPEN);
}
else
{
@ -238,7 +294,7 @@ std::unique_ptr<std::istream> ObjectAsset::GetStream() const
auto stream = zipArchive->GetFileStream(_path);
if (stream != nullptr)
{
return std::make_unique<zipstreamwrapper>(std::move(zipArchive), std::move(stream));
return std::make_unique<ZipStreamWrapper>(std::move(zipArchive), std::move(stream));
}
}
}

View File

@ -15,8 +15,6 @@
#include "ImageTable.h"
#include "StringTable.h"
#include <fstream>
#include <istream>
#include <memory>
#include <optional>
#include <string_view>
@ -223,7 +221,7 @@ public:
bool IsAvailable() const;
size_t GetLength() const;
std::unique_ptr<std::istream> GetStream() const;
std::unique_ptr<OpenRCT2::IStream> GetStream() const;
};
struct IReadObjectContext