Browse Source

OpenAudio AudioClip mostly implemented (needs testing)

BearishSun 9 years ago
parent
commit
b95bf2b243

+ 3 - 2
Documentation/CompilingDependenciesManually.txt

@@ -70,9 +70,10 @@ BansheePhysX (optional) relies on:
 BansheeOpenAudio (optional) relies on:
 BansheeOpenAudio (optional) relies on:
  - libogg v1.3.2
  - libogg v1.3.2
   - https://xiph.org/downloads/
   - https://xiph.org/downloads/
-  - Required in order to build libflac & libvorbis
+  - Build as static library with separate debug & release files
+   - Switch runtime library to dynamic to avoid linker warnings when adding it to Banshee
+  - Also required in order to build libflac & libvorbis
     - See readme files included with those libraries as to how to set up libogg library and includes
     - See readme files included with those libraries as to how to set up libogg library and includes
-  - Use non-debug build for all Banshee configurations for performance reasons
  - libvorbis commit:5bbe49dc947ea9f1532273644348c53054ca214a
  - libvorbis commit:5bbe49dc947ea9f1532273644348c53054ca214a
   - https://git.xiph.org/?p=vorbis.git
   - https://git.xiph.org/?p=vorbis.git
   - Before compiling follow steps for libogg above
   - Before compiling follow steps for libogg above

+ 16 - 4
Source/BansheeCore/Include/BsAudioClip.h

@@ -73,14 +73,25 @@ namespace BansheeEngine
 		UINT32 getNumSamples() const { return mNumSamples; }
 		UINT32 getNumSamples() const { return mNumSamples; }
 
 
 		/** 
 		/** 
-		 * Returns audio samples in PCM format, channel data inerleaved. 
+		 * Returns audio samples in PCM format, channel data interleaved. Sample read pointer is advanced by the read 
+		 * amount.
 		 *
 		 *
 		 * @param[in]	samples		Previously allocated buffer to contain the samples.
 		 * @param[in]	samples		Previously allocated buffer to contain the samples.
 		 * @param[in]	count		Number of samples to read (should be a multiple of number of channels).
 		 * @param[in]	count		Number of samples to read (should be a multiple of number of channels).
-		 * @param[in]	offset		Offset in number of samples at which to start reading (should be a multiple of number
-		 *							of channels).
+		 *
+		 * @note	Implementation must be thread safe as this will get called from audio streaming thread.
+		 */
+		virtual void getSamples(UINT8* samples, UINT32 count) const = 0;
+
+		/**
+		 * Moves the read location from which the getSamples method retrieves samples.
+		 *
+		 * @param[in]	offset	Offset in number of samples at which to start reading (should be a multiple of number
+		 *						of channels).
+		 *
+		 * @note	Implementation must be thread safe as this will get called from audio streaming thread.
 		 */
 		 */
-		virtual void getSamples(UINT8* samples, UINT32 count, UINT32 offset = 0) const = 0;
+		virtual void seekSamples(UINT32 offset) = 0;
 
 
 		static HAudioClip create(UINT32 streamSize, UINT32 numSamples, const AUDIO_CLIP_DESC& desc);
 		static HAudioClip create(UINT32 streamSize, UINT32 numSamples, const AUDIO_CLIP_DESC& desc);
 		static HAudioClip create(const SPtr<DataStream>& samples, UINT32 streamSize, UINT32 numSamples,
 		static HAudioClip create(const SPtr<DataStream>& samples, UINT32 streamSize, UINT32 numSamples,
@@ -105,6 +116,7 @@ namespace BansheeEngine
 		AUDIO_CLIP_DESC mDesc;
 		AUDIO_CLIP_DESC mDesc;
 		UINT32 mNumSamples;
 		UINT32 mNumSamples;
 		UINT32 mStreamSize;
 		UINT32 mStreamSize;
+		UINT32 mStreamOffset;
 		SPtr<DataStream> mStreamData;
 		SPtr<DataStream> mStreamData;
 
 
 		/************************************************************************/
 		/************************************************************************/

+ 4 - 3
Source/BansheeCore/Include/BsAudioClipRTTI.h

@@ -25,13 +25,13 @@ namespace BansheeEngine
 			BS_RTTI_MEMBER_PLAIN_NAMED(numChannels, mDesc.numChannels, 4)
 			BS_RTTI_MEMBER_PLAIN_NAMED(numChannels, mDesc.numChannels, 4)
 			BS_RTTI_MEMBER_PLAIN(mNumSamples, 5)
 			BS_RTTI_MEMBER_PLAIN(mNumSamples, 5)
 			BS_RTTI_MEMBER_PLAIN(mStreamSize, 7)
 			BS_RTTI_MEMBER_PLAIN(mStreamSize, 7)
+			BS_RTTI_MEMBER_PLAIN(mStreamOffset, 8)
 		BS_END_RTTI_MEMBERS
 		BS_END_RTTI_MEMBERS
 
 
 		SPtr<DataStream> getData(AudioClip* obj, UINT32& size)
 		SPtr<DataStream> getData(AudioClip* obj, UINT32& size)
 		{
 		{
 			SPtr<DataStream> stream = obj->getSourceFormatData(size);
 			SPtr<DataStream> stream = obj->getSourceFormatData(size);
-
-			if (stream->isFile())
+			if (stream != nullptr && stream->isFile())
 				LOGWRN("Saving an AudioClip which uses streaming data. Streaming data might not be available if saving to the same file.");
 				LOGWRN("Saving an AudioClip which uses streaming data. Streaming data might not be available if saving to the same file.");
 
 
 			return stream;
 			return stream;
@@ -39,8 +39,9 @@ namespace BansheeEngine
 
 
 		void setData(AudioClip* obj, const SPtr<DataStream>& val, UINT32 size)
 		void setData(AudioClip* obj, const SPtr<DataStream>& val, UINT32 size)
 		{
 		{
-			obj->mStreamData = val;
+			obj->mStreamData = val->clone(); // Making sure that the AudioClip cannot modify the source stream, which is still used by the deserializer
 			obj->mStreamSize = size;
 			obj->mStreamSize = size;
+			obj->mStreamOffset = (UINT32)val->tell();
 		}
 		}
 
 
 	public:
 	public:

+ 5 - 2
Source/BansheeCore/Source/BsAudioClip.cpp

@@ -8,8 +8,11 @@
 namespace BansheeEngine
 namespace BansheeEngine
 {
 {
 	AudioClip::AudioClip(const SPtr<DataStream>& samples, UINT32 streamSize, UINT32 numSamples, const AUDIO_CLIP_DESC& desc)
 	AudioClip::AudioClip(const SPtr<DataStream>& samples, UINT32 streamSize, UINT32 numSamples, const AUDIO_CLIP_DESC& desc)
-		:Resource(false), mDesc(desc), mNumSamples(numSamples), mStreamData(samples), mStreamSize(streamSize)
-	{ }
+		:Resource(false), mDesc(desc), mNumSamples(numSamples), mStreamData(samples), mStreamSize(streamSize), mStreamOffset(0)
+	{
+		if (samples != nullptr)
+			mStreamOffset = (UINT32)samples->tell();
+	}
 
 
 	HAudioClip AudioClip::create(UINT32 streamSize, UINT32 numSamples, const AUDIO_CLIP_DESC& desc)
 	HAudioClip AudioClip::create(UINT32 streamSize, UINT32 numSamples, const AUDIO_CLIP_DESC& desc)
 	{
 	{

+ 10 - 2
Source/BansheeOpenAudio/Include/BsOAAudioClip.h

@@ -4,6 +4,7 @@
 
 
 #include "BsOAPrerequisites.h"
 #include "BsOAPrerequisites.h"
 #include "BsAudioClip.h"
 #include "BsAudioClip.h"
+#include "BsOAOggVorbisReader.h"
 
 
 namespace BansheeEngine
 namespace BansheeEngine
 {
 {
@@ -15,16 +16,23 @@ namespace BansheeEngine
 	{
 	{
 	public:
 	public:
 		/** @copydoc AudioClip::getSamples */
 		/** @copydoc AudioClip::getSamples */
-		virtual void getSamples(UINT8* samples, UINT32 count, UINT32 offset = 0) const = 0;
+		void getSamples(UINT8* samples, UINT32 count) const override;
+
+		/** @copydoc AudioClip::seekSamples */
+		void seekSamples(UINT32 offset) override;
 
 
 	protected:
 	protected:
 		/** @copydoc Resource::initialize */
 		/** @copydoc Resource::initialize */
 		void initialize() override;
 		void initialize() override;
 
 
 		/** @copydoc AudioClip::getSourceFormatData */
 		/** @copydoc AudioClip::getSourceFormatData */
-		virtual SPtr<DataStream> getSourceFormatData(UINT32& size) = 0;
+		SPtr<DataStream> getSourceFormatData(UINT32& size) override;
 	private:
 	private:
 		OAAudioClip(const SPtr<DataStream>& samples, UINT32 streamSize, UINT32 numSamples, const AUDIO_CLIP_DESC& desc);
 		OAAudioClip(const SPtr<DataStream>& samples, UINT32 streamSize, UINT32 numSamples, const AUDIO_CLIP_DESC& desc);
+
+		mutable Mutex mMutex;
+		mutable OAOggVorbisReader mVorbisReader;
+		bool mNeedsDecompression;
 	};
 	};
 
 
 	/** @} */
 	/** @} */

+ 110 - 21
Source/BansheeOpenAudio/Source/BsOAAudioClip.cpp

@@ -2,60 +2,143 @@
 //**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
 //**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
 #include "BsOAAudioClip.h"
 #include "BsOAAudioClip.h"
 #include "BsOAOggVorbisWriter.h"
 #include "BsOAOggVorbisWriter.h"
+#include "BsOAOggVorbisReader.h"
 #include "BsDataStream.h"
 #include "BsDataStream.h"
 
 
 namespace BansheeEngine
 namespace BansheeEngine
 {
 {
 	OAAudioClip::OAAudioClip(const SPtr<DataStream>& samples, UINT32 streamSize, UINT32 numSamples, const AUDIO_CLIP_DESC& desc)
 	OAAudioClip::OAAudioClip(const SPtr<DataStream>& samples, UINT32 streamSize, UINT32 numSamples, const AUDIO_CLIP_DESC& desc)
-		:AudioClip(samples, streamSize, numSamples, desc)
+		:AudioClip(samples, streamSize, numSamples, desc), mNeedsDecompression(false)
 	{ }
 	{ }
 
 
 	void OAAudioClip::initialize()
 	void OAAudioClip::initialize()
 	{
 	{
-		if (mDesc.readMode != AudioReadMode::Stream)
 		{
 		{
-			// If not streaming, but input stream is a file, read all of it into an internal memory stream
-			if (mStreamData != nullptr)
+			Lock lock(mMutex); // Needs to be called even if stream data is null, to ensure memory fence is added so the
+							   // other thread sees properly initialized AudioClip members
+
+			AudioFileInfo info;
+			info.bitDepth = mDesc.bitDepth;
+			info.numChannels = mDesc.numChannels;
+			info.numSamples = mNumSamples;
+			info.sampleRate = mDesc.frequency;
+
+			if (mDesc.readMode != AudioReadMode::Stream)
 			{
 			{
-				// Decompress if needed
-				if (mDesc.format == AudioFormat::VORBIS && mDesc.readMode == AudioReadMode::LoadDecompressed)
+				// If not streaming, but input stream is a file, read all of it into an internal memory stream
+				if (mStreamData != nullptr)
 				{
 				{
-					// TODO - Read entire stream and decompress into a new stream
+					// Decompress if needed
+					if (mDesc.format == AudioFormat::VORBIS && mDesc.readMode == AudioReadMode::LoadDecompressed)
+					{
+						mStreamData->seek(mStreamOffset);
+
+						OAOggVorbisReader reader;
+						if (reader.open(mStreamData, info))
+						{
+							UINT32 bufferSize = info.numSamples * info.bitDepth;
+							UINT8* sampleBuffer = (UINT8*)bs_alloc(bufferSize);
+
+							reader.read(sampleBuffer, info.numSamples);
+
+							mStreamData = bs_shared_ptr_new<MemoryDataStream>(sampleBuffer, bufferSize);
+							mStreamOffset = 0;
+							mStreamSize = bufferSize;
+						}
+						else
+							LOGERR("Failed decompressing AudioClip stream.");
+					}
+					else
+					{
+						if (mStreamData->isFile()) // If reading from file, make a copy of data in memory
+						{
+							UINT8* data = (UINT8*)bs_alloc(mStreamSize);
+
+							mStreamData->seek(mStreamOffset);
+							mStreamData->read(data, mStreamSize);
+
+							mStreamData = bs_shared_ptr_new<MemoryDataStream>(data, mStreamSize);
+							mStreamOffset = 0;
+						}
+					}
 				}
 				}
-				else
+			}
+
+			if (mDesc.format == AudioFormat::VORBIS && mDesc.readMode != AudioReadMode::LoadDecompressed)
+			{
+				mNeedsDecompression = true;
+
+				if (mStreamData != nullptr)
 				{
 				{
-					if(mStreamData->isFile()) // If reading from file, make a copy of data in memory
-						mStreamData = bs_shared_ptr_new<MemoryDataStream>(mStreamData);
+					if (!mVorbisReader.open(mStreamData, info))
+						LOGERR("Failed decompressing AudioClip stream.");
 				}
 				}
 			}
 			}
+
+			if (mStreamData != nullptr)
+				mStreamData->seek(mStreamOffset);
 		}
 		}
 
 
 		AudioClip::initialize();
 		AudioClip::initialize();
 	}
 	}
 
 
-	void OAAudioClip::getSamples(UINT8* samples, UINT32 count, UINT32 offset) const
+	void OAAudioClip::getSamples(UINT8* samples, UINT32 count) const
 	{
 	{
-		// TODO - Read from stream, and decompress as needed
+		Lock lock(mMutex);
 
 
-		// TODO - This must be thread safe as it will be called from other threads. Note this in the docs.
+		if (mStreamData == nullptr)
+			return;
+
+		if (mNeedsDecompression)
+			mVorbisReader.read(samples, count);
+		else
+		{
+			UINT32 bytesPerSample = mDesc.bitDepth / 8;
+			UINT32 size = count * bytesPerSample;
+
+			mStreamData->read(samples, size);
+		}
+	}
+
+	void OAAudioClip::seekSamples(UINT32 offset)
+	{
+		Lock lock(mMutex);
+
+		if (mStreamData == nullptr)
+			return;
+
+		if (mNeedsDecompression)
+			mVorbisReader.seek(offset);
+		else
+		{
+			UINT32 bytesPerSample = mDesc.bitDepth / 8;
+			UINT32 streamOffset = mStreamOffset + offset * bytesPerSample;
+
+			mStreamData->seek(streamOffset);
+		}
 	}
 	}
 
 
 	SPtr<DataStream> OAAudioClip::getSourceFormatData(UINT32& size)
 	SPtr<DataStream> OAAudioClip::getSourceFormatData(UINT32& size)
 	{
 	{
+		Lock lock(mMutex);
+
+		if (mStreamData == nullptr)
+		{
+			size = 0;
+			return nullptr;
+		}
+
 		if(mDesc.format == AudioFormat::PCM || mDesc.readMode == AudioReadMode::LoadCompressed || 
 		if(mDesc.format == AudioFormat::PCM || mDesc.readMode == AudioReadMode::LoadCompressed || 
 			mDesc.readMode == AudioReadMode::Stream)
 			mDesc.readMode == AudioReadMode::Stream)
 		{
 		{
+			SPtr<DataStream> outStream = mStreamData->clone(false);
+			outStream->seek(mStreamOffset);
+
 			size = mStreamSize;
 			size = mStreamSize;
-			return mStreamData;
+			return outStream;
 		}
 		}
 		else // Data has been decompressed, we need to re-compress it to get the original stream
 		else // Data has been decompressed, we need to re-compress it to get the original stream
 		{
 		{
-			if (mStreamData == nullptr)
-			{
-				size = 0;
-				return nullptr;
-			}
-
 			assert(!mStreamData->isFile());
 			assert(!mStreamData->isFile());
 			SPtr<MemoryDataStream> memStream = std::static_pointer_cast<MemoryDataStream>(mStreamData);
 			SPtr<MemoryDataStream> memStream = std::static_pointer_cast<MemoryDataStream>(mStreamData);
 
 
@@ -65,9 +148,15 @@ namespace BansheeEngine
 			info.numSamples = mNumSamples;
 			info.numSamples = mNumSamples;
 			info.sampleRate = mDesc.frequency;
 			info.sampleRate = mDesc.frequency;
 
 
+			UINT32 orgOffset = (UINT32)memStream->tell();
+			memStream->seek(mStreamOffset);
+
 			UINT32 bufferSize = 0;
 			UINT32 bufferSize = 0;
-			UINT8* encodedSamples = OAOggVorbisWriter::PCMToOggVorbis(memStream->getPtr(), info, bufferSize);
+			UINT8* encodedSamples = OAOggVorbisWriter::PCMToOggVorbis(memStream->getCurrentPtr(), info, bufferSize);
+
+			memStream->seek(orgOffset);
 
 
+			size = bufferSize;
 			return bs_shared_ptr_new<MemoryDataStream>(encodedSamples, bufferSize);
 			return bs_shared_ptr_new<MemoryDataStream>(encodedSamples, bufferSize);
 		}
 		}
 	}
 	}

+ 26 - 33
Source/BansheeUtility/Include/BsDataStream.h

@@ -130,6 +130,15 @@ namespace BansheeEngine
         /** Returns the total size of the data to be read from the stream, or 0 if this is indeterminate for this stream. */
         /** Returns the total size of the data to be read from the stream, or 0 if this is indeterminate for this stream. */
         size_t size() const { return mSize; }
         size_t size() const { return mSize; }
 
 
+		/** 
+		 * Creates a copy of this stream. 
+		 *
+		 * @param[in]	copyData	If true the internal stream data will be copied as well, otherwise it will just 
+		 *							reference the data from the original stream (in which case the caller must ensure the
+		 *							original stream outlives the clone). This is not relevant for file streams.
+		 */
+		virtual SPtr<DataStream> clone(bool copyData = true) const = 0;
+
         /** Close the stream. This makes further operations invalid. */
         /** Close the stream. This makes further operations invalid. */
         virtual void close() = 0;
         virtual void close() = 0;
 		
 		
@@ -198,6 +207,9 @@ namespace BansheeEngine
         /** @copydoc DataStream::eof */
         /** @copydoc DataStream::eof */
 		bool eof() const override;
 		bool eof() const override;
 
 
+		/** @copydoc DataStream::clone */
+		SPtr<DataStream> clone(bool copyData = true) const override;
+
         /** @copydoc DataStream::close */
         /** @copydoc DataStream::close */
 		void close() override;
 		void close() override;
 
 
@@ -214,38 +226,15 @@ namespace BansheeEngine
 	{
 	{
 	public:
 	public:
 		/**
 		/**
-		 * Construct read-only stream from an standard stream.
+		 * Construct a file stream.
 		 *
 		 *
-		 * If @p freeOnClose is true, the STL stream will be freed once the data stream is closed.
+		 * @param[in]	filePath	Path of the file to open.
+		 * @param[in]	readOnly	Determines should the file be opened in read-only mode, or should writing be supported 
+		 *							as well.
+		 * @param[in]	freeOnClose	Determines should the internal stream be freed once the data stream is closed or goes 
+		 *							out of scope.
 		 */
 		 */
-		FileDataStream(SPtr<std::ifstream> s, bool freeOnClose = true);
-
-		/**
-		 * Construct read-write stream from an standard stream.
-		 * 			
-		 * If @p freeOnClose is true, the STL stream will be freed once the data stream is closed.
-		 */
-		FileDataStream(SPtr<std::fstream> s, bool freeOnClose = true);
-
-		/**
-		 * Construct read-only stream from an standard stream, and tell it the size.
-		 * 			
-		 * Size parameter allows you to specify the size without requiring us to seek to the end of the stream to find 
-		 * the size.
-		 *			
-		 * If @p freeOnClose is true, the STL stream will be freed once the data stream is closed.
-		 */
-		FileDataStream(SPtr<std::ifstream> s, size_t size, bool freeOnClose = true);
-
-		/**
-		 * Construct read-write stream from an standard stream, and tell it the size.
-		 * 			
-		 * Size parameter allows you to specify the size without requiring us to seek to the end of the stream to find 
-		 * the size.
-		 *			
-		 * If @p freeOnClose is true, the STL stream will be freed once the data stream is closed.
-		 */
-		FileDataStream(SPtr<std::fstream> s, size_t size, bool freeOnClose = true);
+		FileDataStream(const Path& filePath, bool readOnly = true, bool freeOnClose = true);
 
 
 		~FileDataStream();
 		~FileDataStream();
 
 
@@ -269,13 +258,17 @@ namespace BansheeEngine
         /** @copydoc DataStream::eof */
         /** @copydoc DataStream::eof */
 		bool eof() const override;
 		bool eof() const override;
 
 
+		/** @copydoc DataStream::clone */
+		SPtr<DataStream> clone(bool copyData = true) const override;
+
         /** @copydoc DataStream::close */
         /** @copydoc DataStream::close */
 		void close() override;
 		void close() override;
 
 
 	protected:
 	protected:
-		SPtr<std::istream> mpInStream;
-		SPtr<std::ifstream> mpFStreamRO;
-		SPtr<std::fstream> mpFStream;
+		Path mPath;
+		SPtr<std::istream> mInStream;
+		SPtr<std::ifstream> mFStreamRO;
+		SPtr<std::fstream> mFStream;
 		bool mFreeOnClose;	
 		bool mFreeOnClose;	
 
 
 		void determineAccess();
 		void determineAccess();

+ 71 - 53
Source/BansheeUtility/Source/BsDataStream.cpp

@@ -1,6 +1,7 @@
 //********************************** Banshee Engine (www.banshee3d.com) **************************************************//
 //********************************** Banshee Engine (www.banshee3d.com) **************************************************//
 //**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
 //**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
 #include "BsDataStream.h"
 #include "BsDataStream.h"
+#include "BsDebug.h"
 #include <codecvt>
 #include <codecvt>
 
 
 namespace BansheeEngine 
 namespace BansheeEngine 
@@ -348,6 +349,14 @@ namespace BansheeEngine
         return mPos >= mEnd;
         return mPos >= mEnd;
     }
     }
 
 
+	SPtr<DataStream> MemoryDataStream::clone(bool copyData) const
+	{
+		if (!copyData)
+			return bs_shared_ptr_new<MemoryDataStream>(mData, mSize, false);
+		
+		return bs_shared_ptr_new<MemoryDataStream>(*this);
+	}
+
     void MemoryDataStream::close()    
     void MemoryDataStream::close()    
     {
     {
         if (mData != nullptr)
         if (mData != nullptr)
@@ -359,50 +368,52 @@ namespace BansheeEngine
         }
         }
     }
     }
 
 
-    FileDataStream::FileDataStream(SPtr<std::ifstream> s, bool freeOnClose)
-        : DataStream(READ), mpInStream(s), mpFStreamRO(s), mpFStream(0), mFreeOnClose(freeOnClose)
+    FileDataStream::FileDataStream(const Path& path, bool readOnly, bool freeOnClose)
+        : DataStream(READ), mPath(path), mFreeOnClose(freeOnClose)
     {
     {
-        mpInStream->seekg(0, std::ios_base::end);
-        mSize = (size_t)mpInStream->tellg();
-        mpInStream->seekg(0, std::ios_base::beg);
-
-		determineAccess();
-    }
-
-    FileDataStream::FileDataStream(SPtr<std::ifstream> s, size_t inSize, bool freeOnClose)
-        : DataStream(READ), mpInStream(s), mpFStreamRO(s), mpFStream(0), mFreeOnClose(freeOnClose)
-    {
-        mSize = inSize;
+		WString pathWString = path.toWString();
+		const wchar_t* pathString = pathWString.c_str();
+
+		// Always open in binary mode
+		// Also, always include reading
+		std::ios::openmode mode = std::ios::in | std::ios::binary;
+
+		if (!readOnly)
+		{
+			mode |= std::ios::out;
+			mFStream = bs_shared_ptr_new<std::fstream>();
+			mFStream->open(pathString, mode);
+			mInStream = mFStream;
+		}
+		else
+		{
+			mFStreamRO = bs_shared_ptr_new<std::ifstream>();
+			mFStreamRO->open(pathString, mode);
+			mInStream = mFStreamRO;
+		}
+
+		// Should check ensure open succeeded, in case fail for some reason.
+		if (mInStream->fail())
+		{
+			LOGWRN("Cannot open file: " + path.toString());
+			return;
+		}
+		
+        mInStream->seekg(0, std::ios_base::end);
+        mSize = (size_t)mInStream->tellg();
+        mInStream->seekg(0, std::ios_base::beg);
 
 
 		determineAccess();
 		determineAccess();
     }
     }
 
 
-	FileDataStream::FileDataStream(SPtr<std::fstream> s, bool freeOnClose)
-		: DataStream(READ | WRITE), mpInStream(s), mpFStreamRO(0), mpFStream(s), mFreeOnClose(freeOnClose)
-	{
-		mpInStream->seekg(0, std::ios_base::end);
-		mSize = (size_t)mpInStream->tellg();
-		mpInStream->seekg(0, std::ios_base::beg);
-
-		determineAccess();
-	}
-
-	FileDataStream::FileDataStream(SPtr<std::fstream> s, size_t inSize, bool freeOnClose)
-		: DataStream(READ | WRITE), mpInStream(s), mpFStreamRO(0), mpFStream(s), mFreeOnClose(freeOnClose)
-	{
-		mSize = inSize;
-
-		determineAccess();
-	}
-
 	void FileDataStream::determineAccess()
 	void FileDataStream::determineAccess()
 	{
 	{
 		mAccess = 0;
 		mAccess = 0;
 
 
-		if (mpInStream)
+		if (mInStream)
 			mAccess |= READ;
 			mAccess |= READ;
 
 
-		if (mpFStream)
+		if (mFStream)
 			mAccess |= WRITE;
 			mAccess |= WRITE;
 	}
 	}
 
 
@@ -413,17 +424,17 @@ namespace BansheeEngine
 
 
     size_t FileDataStream::read(void* buf, size_t count)
     size_t FileDataStream::read(void* buf, size_t count)
     {
     {
-		mpInStream->read(static_cast<char*>(buf), static_cast<std::streamsize>(count));
+		mInStream->read(static_cast<char*>(buf), static_cast<std::streamsize>(count));
 
 
-        return (size_t)mpInStream->gcount();
+        return (size_t)mInStream->gcount();
     }
     }
 
 
 	size_t FileDataStream::write(const void* buf, size_t count)
 	size_t FileDataStream::write(const void* buf, size_t count)
 	{
 	{
 		size_t written = 0;
 		size_t written = 0;
-		if (isWriteable() && mpFStream)
+		if (isWriteable() && mFStream)
 		{
 		{
-			mpFStream->write(static_cast<const char*>(buf), static_cast<std::streamsize>(count));
+			mFStream->write(static_cast<const char*>(buf), static_cast<std::streamsize>(count));
 			written = count;
 			written = count;
 		}
 		}
 
 
@@ -431,46 +442,53 @@ namespace BansheeEngine
 	}
 	}
     void FileDataStream::skip(size_t count)
     void FileDataStream::skip(size_t count)
     {	
     {	
-		mpInStream->clear(); // Clear fail status in case eof was set
-		mpInStream->seekg(static_cast<std::ifstream::pos_type>(count), std::ios::cur);
+		mInStream->clear(); // Clear fail status in case eof was set
+		mInStream->seekg(static_cast<std::ifstream::pos_type>(count), std::ios::cur);
     }
     }
 
 
     void FileDataStream::seek(size_t pos)
     void FileDataStream::seek(size_t pos)
     {
     {
-		mpInStream->clear(); // Clear fail status in case eof was set
-		mpInStream->seekg(static_cast<std::streamoff>(pos), std::ios::beg);
+		mInStream->clear(); // Clear fail status in case eof was set
+		mInStream->seekg(static_cast<std::streamoff>(pos), std::ios::beg);
 	}
 	}
 
 
     size_t FileDataStream::tell() const
     size_t FileDataStream::tell() const
 	{
 	{
-		mpInStream->clear(); // Clear fail status in case eof was set
+		mInStream->clear(); // Clear fail status in case eof was set
 
 
-		return (size_t)mpInStream->tellg();
+		return (size_t)mInStream->tellg();
 	}
 	}
 
 
     bool FileDataStream::eof() const
     bool FileDataStream::eof() const
     {
     {
-        return mpInStream->eof();
+        return mInStream->eof();
     }
     }
 
 
+	SPtr<DataStream> FileDataStream::clone(bool copyData) const
+	{
+		bool readOnly = (getAccessMode() & WRITE) == 0;
+
+		return bs_shared_ptr_new<FileDataStream>(mPath, readOnly, true);		
+	}
+
     void FileDataStream::close()
     void FileDataStream::close()
     {
     {
-        if (mpInStream)
+        if (mInStream)
         {
         {
-			if (mpFStreamRO)
-	            mpFStreamRO->close();
+			if (mFStreamRO)
+	            mFStreamRO->close();
 
 
-			if (mpFStream)
+			if (mFStream)
 			{
 			{
-				mpFStream->flush();
-				mpFStream->close();
+				mFStream->flush();
+				mFStream->close();
 			}
 			}
 
 
             if (mFreeOnClose)
             if (mFreeOnClose)
             {
             {
-				mpInStream = nullptr;
-				mpFStreamRO = nullptr; 
-				mpFStream = nullptr; 
+				mInStream = nullptr;
+				mFStreamRO = nullptr; 
+				mFStream = nullptr; 
             }
             }
         }
         }
     }
     }

+ 2 - 56
Source/BansheeUtility/Source/Win32/BsWin32FileSystem.cpp

@@ -274,66 +274,12 @@ namespace BansheeEngine
 			return nullptr;
 			return nullptr;
 		}
 		}
 
 
-		UINT64 fileSize = getFileSize(fullPath);
-
-		// Always open in binary mode
-		// Also, always include reading
-		std::ios::openmode mode = std::ios::in | std::ios::binary;
-		SPtr<std::istream> baseStream = 0;
-		SPtr<std::ifstream> roStream = 0;
-		SPtr<std::fstream> rwStream = 0;
-
-		if (!readOnly)
-		{
-			mode |= std::ios::out;
-			rwStream = bs_shared_ptr_new<std::fstream>();
-			rwStream->open(pathString, mode);
-			baseStream = rwStream;
-		}
-		else
-		{
-			roStream = bs_shared_ptr_new<std::ifstream>();
-			roStream->open(pathString, mode);
-			baseStream = roStream;
-		}
-
-		// Should check ensure open succeeded, in case fail for some reason.
-		if (baseStream->fail())
-		{
-			LOGWRN("Cannot open file: " + fullPath.toString());
-			return nullptr;
-		}
-
-		/// Construct return stream, tell it to delete on destroy
-		FileDataStream* stream = 0;
-		if (rwStream)
-		{
-			// use the writeable stream 
-			stream = bs_new<FileDataStream>(rwStream, (size_t)fileSize, true);
-		}
-		else
-		{
-			// read-only stream
-			stream = bs_new<FileDataStream>(roStream, (size_t)fileSize, true);
-		}
-
-		return bs_shared_ptr<FileDataStream>(stream);
+		return bs_shared_ptr_new<FileDataStream>(fullPath, readOnly, true);
 	}
 	}
 
 
 	SPtr<DataStream> FileSystem::createAndOpenFile(const Path& fullPath)
 	SPtr<DataStream> FileSystem::createAndOpenFile(const Path& fullPath)
 	{
 	{
-		// Always open in binary mode
-		// Also, always include reading
-		std::ios::openmode mode = std::ios::out | std::ios::binary;
-		SPtr<std::fstream> rwStream = bs_shared_ptr_new<std::fstream>();
-		rwStream->open(fullPath.toWString().c_str(), mode);
-
-		// Should check ensure open succeeded, in case fail for some reason.
-		if (rwStream->fail())
-			BS_EXCEPT(FileNotFoundException, "Cannot open file: " + fullPath.toString());
-
-		/// Construct return stream, tell it to delete on destroy
-		return bs_shared_ptr_new<FileDataStream>(rwStream, 0, true);
+		return bs_shared_ptr_new<FileDataStream>(fullPath, false, true);
 	}
 	}
 
 
 	UINT64 FileSystem::getFileSize(const Path& fullPath)
 	UINT64 FileSystem::getFileSize(const Path& fullPath)