diff --git a/openrct2.vcxproj b/openrct2.vcxproj
index 9fec386430..641b84d74f 100644
--- a/openrct2.vcxproj
+++ b/openrct2.vcxproj
@@ -45,6 +45,7 @@
+
@@ -345,12 +346,12 @@
-
+
diff --git a/src/core/FileStream.hpp b/src/core/FileStream.hpp
index dce4ee866c..9a0bca777d 100644
--- a/src/core/FileStream.hpp
+++ b/src/core/FileStream.hpp
@@ -67,11 +67,6 @@ public:
}
~FileStream()
- {
- Dispose();
- }
-
- void Dispose() override
{
if (!_disposed)
{
diff --git a/src/core/IDisposable.hpp b/src/core/IDisposable.hpp
deleted file mode 100644
index 7a0492f877..0000000000
--- a/src/core/IDisposable.hpp
+++ /dev/null
@@ -1,27 +0,0 @@
-#pragma region Copyright (c) 2014-2016 OpenRCT2 Developers
-/*****************************************************************************
- * OpenRCT2, an open source clone of Roller Coaster Tycoon 2.
- *
- * OpenRCT2 is the work of many authors, a full list can be found in contributors.md
- * For more information, visit https://github.com/OpenRCT2/OpenRCT2
- *
- * OpenRCT2 is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * A full copy of the GNU General Public License can be found in licence.txt
- *****************************************************************************/
-#pragma endregion
-
-#pragma once
-
-#include "../common.h"
-
-/**
- * Represents an object that can be disposed. So things can explicitly close resources before the destructor kicks in.
- */
-interface IDisposable
-{
- virtual void Dispose() abstract;
-};
diff --git a/src/core/IStream.hpp b/src/core/IStream.hpp
index 9f85e1b229..577f1d29e8 100644
--- a/src/core/IStream.hpp
+++ b/src/core/IStream.hpp
@@ -17,9 +17,7 @@
#pragma once
#include "../common.h"
-
#include "Exception.hpp"
-#include "IDisposable.hpp"
enum {
STREAM_SEEK_BEGIN,
@@ -30,12 +28,12 @@ enum {
/**
* Represents a stream that can be read or written to. Implemented by types such as FileStream, NetworkStream or MemoryStream.
*/
-interface IStream : public IDisposable
+interface IStream
{
///////////////////////////////////////////////////////////////////////////
// Interface methods
///////////////////////////////////////////////////////////////////////////
- // virtual ~IStream() abstract;
+ virtual ~IStream() { }
virtual bool CanRead() const abstract;
virtual bool CanWrite() const abstract;
diff --git a/src/core/MemoryStream.cpp b/src/core/MemoryStream.cpp
new file mode 100644
index 0000000000..2e6d46f525
--- /dev/null
+++ b/src/core/MemoryStream.cpp
@@ -0,0 +1,186 @@
+#pragma region Copyright (c) 2014-2016 OpenRCT2 Developers
+/*****************************************************************************
+ * OpenRCT2, an open source clone of Roller Coaster Tycoon 2.
+ *
+ * OpenRCT2 is the work of many authors, a full list can be found in contributors.md
+ * For more information, visit https://github.com/OpenRCT2/OpenRCT2
+ *
+ * OpenRCT2 is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * A full copy of the GNU General Public License can be found in licence.txt
+ *****************************************************************************/
+#pragma endregion
+
+#include "Math.hpp"
+#include "Memory.hpp"
+#include "MemoryStream.h"
+
+MemoryStream::MemoryStream()
+{
+ _access = MEMORY_ACCESS_READ |
+ MEMORY_ACCESS_WRITE |
+ MEMORY_ACCESS_OWNER;
+ _dataCapacity = 0;
+ _dataSize = 0;
+ _data = nullptr;
+ _position = nullptr;
+}
+
+MemoryStream::MemoryStream(const MemoryStream ©)
+{
+ _access = copy._access;
+ _dataCapacity = copy._dataCapacity;
+ _dataSize = copy._dataSize;
+
+ if (_access == MEMORY_ACCESS_OWNER)
+ {
+ _data = Memory::Duplicate(copy._data, _dataCapacity);
+ _position = (void*)((uintptr_t)_data + copy.GetPosition());
+ }
+}
+
+MemoryStream::MemoryStream(size_t capacity)
+{
+ _access = MEMORY_ACCESS_READ |
+ MEMORY_ACCESS_WRITE |
+ MEMORY_ACCESS_OWNER;
+ _dataCapacity = capacity;
+ _dataSize = 0;
+ _data = Memory::Allocate(capacity);
+ _position = _data;
+}
+
+MemoryStream::MemoryStream(void * data, size_t dataSize, MEMORY_ACCESS access)
+{
+ _access = access;
+ _dataCapacity = dataSize;
+ _dataSize = dataSize;
+ _data = data;
+ _position = _data;
+}
+
+MemoryStream::MemoryStream(const void * data, size_t dataSize)
+ : MemoryStream((void *)data, dataSize, MEMORY_ACCESS_READ)
+{
+}
+
+MemoryStream::~MemoryStream()
+{
+ if (MEMORY_ACCESS_OWNER)
+ {
+ Memory::Free(_data);
+ }
+ _dataCapacity = 0;
+ _dataSize = 0;
+ _data = nullptr;
+}
+
+void * MemoryStream::GetData() const
+{
+ return Memory::Duplicate(_data, _dataSize);
+}
+
+void * MemoryStream::TakeData()
+{
+ _access &= ~MEMORY_ACCESS_OWNER;
+ return _data;
+}
+
+bool MemoryStream::CanRead() const
+{
+ return (_access & MEMORY_ACCESS_READ) != 0;
+}
+
+bool MemoryStream::CanWrite() const
+{
+ return (_access & MEMORY_ACCESS_WRITE) != 0;
+}
+
+uint64 MemoryStream::GetLength() const
+{
+ return _dataSize;
+}
+
+uint64 MemoryStream::GetPosition() const
+{
+ return (uint64)((uintptr_t)_position - (uintptr_t)_data);
+}
+
+void MemoryStream::SetPosition(uint64 position)
+{
+ Seek(position, STREAM_SEEK_BEGIN);
+}
+
+void MemoryStream::Seek(sint64 offset, int origin)
+{
+ uint64 newPosition;
+ switch (origin) {
+ default:
+ case STREAM_SEEK_BEGIN:
+ newPosition = offset;
+ break;
+ case STREAM_SEEK_CURRENT:
+ newPosition = GetPosition() + offset;
+ break;
+ case STREAM_SEEK_END:
+ newPosition = _dataSize + offset;
+ break;
+ }
+
+ if (newPosition > _dataSize)
+ {
+ throw IOException("New position out of bounds.");
+ }
+ _position = (void*)((uintptr_t)_data + (uintptr_t)newPosition);
+}
+
+void MemoryStream::Read(void * buffer, uint64 length)
+{
+ uint64 position = GetPosition();
+ if (position + length > _dataSize)
+ {
+ throw IOException("Attempted to read past end of stream.");
+ }
+
+ Memory::Copy(buffer, _position, (size_t)length);
+ _position = (void*)((uintptr_t)_position + length);
+}
+
+void MemoryStream::Write(const void * buffer, uint64 length)
+{
+ uint64 position = GetPosition();
+ uint64 nextPosition = position + length;
+ if (nextPosition > _dataCapacity)
+ {
+ if (_access & MEMORY_ACCESS_OWNER)
+ {
+ EnsureCapacity((size_t)nextPosition);
+ }
+ else
+ {
+ throw IOException("Attempted to write past end of stream.");
+ }
+ }
+
+ Memory::Copy(_position, buffer, (size_t)length);
+ _position = (void*)((uintptr_t)_position + length);
+ _dataSize = Math::Max(_dataSize, (size_t)nextPosition);
+}
+
+void MemoryStream::EnsureCapacity(size_t capacity)
+{
+ if (_dataCapacity < capacity)
+ {
+ size_t newCapacity = Math::Max(8, _dataCapacity);
+ while (newCapacity < capacity)
+ {
+ newCapacity *= 2;
+ }
+
+ _dataCapacity = newCapacity;
+ Memory::Reallocate(_data, _dataCapacity);
+ }
+}
diff --git a/src/core/MemoryStream.h b/src/core/MemoryStream.h
new file mode 100644
index 0000000000..c7a2a0763b
--- /dev/null
+++ b/src/core/MemoryStream.h
@@ -0,0 +1,68 @@
+#pragma region Copyright (c) 2014-2016 OpenRCT2 Developers
+/*****************************************************************************
+ * OpenRCT2, an open source clone of Roller Coaster Tycoon 2.
+ *
+ * OpenRCT2 is the work of many authors, a full list can be found in contributors.md
+ * For more information, visit https://github.com/OpenRCT2/OpenRCT2
+ *
+ * OpenRCT2 is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * A full copy of the GNU General Public License can be found in licence.txt
+ *****************************************************************************/
+#pragma endregion
+
+#pragma once
+
+#include "../common.h"
+#include "IStream.hpp"
+
+enum MEMORY_ACCESS
+{
+ MEMORY_ACCESS_READ = 1 << 0,
+ MEMORY_ACCESS_WRITE = 1 << 1,
+ MEMORY_ACCESS_OWNER = 1 << 2,
+};
+
+/**
+ * A stream for reading and writing to files. Wraps an SDL_RWops, SDL2's cross platform file stream.
+ */
+class MemoryStream : public IStream
+{
+private:
+ uint16 _access;
+ size_t _dataCapacity;
+ size_t _dataSize;
+ void * _data;
+ void * _position;
+
+public:
+ MemoryStream();
+ MemoryStream(const MemoryStream ©);
+ explicit MemoryStream(size_t capacity);
+ MemoryStream(void * data, size_t dataSize, MEMORY_ACCESS access = MEMORY_ACCESS_READ);
+ MemoryStream(const void * data, size_t dataSize);
+ virtual ~MemoryStream();
+
+ void * GetData() const;
+ void * TakeData();
+
+ ///////////////////////////////////////////////////////////////////////////
+ // ISteam methods
+ ///////////////////////////////////////////////////////////////////////////
+ bool CanRead() const override;
+ bool CanWrite() const override;
+
+ uint64 GetLength() const override;
+ uint64 GetPosition() const override;
+ void SetPosition(uint64 position) override;
+ void Seek(sint64 offset, int origin) override;
+
+ void Read(void * buffer, uint64 length) override;
+ void Write(const void * buffer, uint64 length) override;
+
+private:
+ void EnsureCapacity(size_t capacity);
+};