Explorar el Código

Added support for compression when saving resources

BearishSun hace 8 años
padre
commit
d0e98c524b

+ 10 - 1
Documentation/GitHub/dependencies.md

@@ -15,7 +15,7 @@ Each library below lists a set of outputs required by Banshee. After you compile
    
 Legend:
 - (BansheeSource) - root directory of Banshee's source code
-- (LibName) - name of the library (title of the each library shown below)
+- (LibName) - name of the library (title of each library shown below)
 - (Platform) - x86 for 32-bit builds, x64 for 64-bit builds
 - (Configuration) - Debug, OptimizedDebug or Release
    
@@ -23,6 +23,15 @@ If the library structure still isn't clear, download one of the pre-compiled dep
       
 ## Library list 
 	  
+**snappy**
+- Google's Snappy compressor/decompressor
+- https://github.com/BearishSun/snappy
+- Required by BansheeUtility
+- Outputs:
+  - Windows (Static library):
+    - /Debug/Snappy.lib (Debug configuration)
+    - /Release/Snappy.lib (Release configuration)
+	  
 **nvtt**
 - NVIDIA Texture Tools 2.1.0 (commit e85d851cd9502d77c6e20769d8c21baa3a94ac18)
 - https://github.com/castano/nvidia-texture-tools

+ 3 - 0
Source/BansheeCore/Include/BsAudioClip.h

@@ -141,6 +141,9 @@ namespace bs
 		/** @copydoc Resource::initialize */
 		void initialize() override;
 
+		/** @copydoc Resource::isCompressible */
+		bool isCompressible() const override { return false; } // Compression handled on a case by case basis manually by the audio system
+
 		/** Returns original audio data. Only available if @p keepSourceData has been provided on creation. */
 		virtual SPtr<DataStream> getSourceStream(UINT32& size) = 0;
 

+ 8 - 0
Source/BansheeCore/Include/BsResource.h

@@ -41,6 +41,14 @@ namespace bs
 		/**	Checks if all the resources this object is dependent on are fully loaded. */
 		bool areDependenciesLoaded() const;
 
+		/** 
+		 * Returns true if the resource can be compressed using a generic compression when saved on a storage device. 
+		 * Certain resources already have their contents compressed (like audio files) and will not benefit from further 
+		 * compression. Resources supporting streaming should never be compressed, instead such resources can handle
+		 * compression/decompression locally through their streams.
+		 */
+		virtual bool isCompressible() const { return true; }
+
 		UINT32 mSize;
 		SPtr<ResourceMetaData> mMetaData;
 

+ 7 - 3
Source/BansheeCore/Include/BsResources.h

@@ -167,7 +167,9 @@ namespace bs
 		 *
 		 * @param[in]	resource 	Handle to the resource.
 		 * @param[in]	filePath 	Full pathname of the file to save as.
-		 * @param[in]	overwrite	(optional) If true, any existing resource at the specified location will be overwritten.
+		 * @param[in]	overwrite	If true, any existing resource at the specified location will be overwritten.
+		 * @param[in]	compress	Should the resource be compressed before saving. Some resource have data that is already
+		 *							compressed and this option will be ignored for such resources.
 		 * 			
 		 * @note
 		 * If the resource is a GpuResource and you are in some way modifying it from the core thread, make sure all those
@@ -176,12 +178,14 @@ namespace bs
 		 * If saving a core thread resource this is a potentially very slow operation as we must wait on the core thread 
 		 * and the GPU in order to read the resource.
 		 */
-		void save(const HResource& resource, const Path& filePath, bool overwrite);
+		void save(const HResource& resource, const Path& filePath, bool overwrite, bool compress = false);
 
 		/**
 		 * Saves an existing resource to its previous location.
 		 *
 		 * @param[in]	resource 	Handle to the resource.
+		 * @param[in]	compress	Should the resource be compressed before saving. Some resource have data that is already
+		 *							compressed and this option will be ignored for such resources.
 		 *
 		 * @note	
 		 * If the resource is a GpuResource and you are in some way modifying it from the Core thread, make sure all those
@@ -190,7 +194,7 @@ namespace bs
 		 * If saving a core thread resource this is a potentially very slow operation as we must wait on the core thread 
 		 * and the GPU in order to read the resource.
 		 */
-		void save(const HResource& resource);
+		void save(const HResource& resource, bool compress = false);
 
 		/**
 		 * Updates an existing resource handle with a new resource. Caller must ensure that new resource type matches the 

+ 6 - 2
Source/BansheeCore/Include/BsSavedResourceData.h

@@ -20,7 +20,7 @@ namespace bs
 	{
 	public:
 		SavedResourceData();
-		SavedResourceData(const Vector<String>& dependencies, bool allowAsync);
+		SavedResourceData(const Vector<String>& dependencies, bool allowAsync, UINT32 compressionMethod);
 
 		/**	Returns a list of all resource dependencies. */
 		const Vector<String>& getDependencies() const { return mDependencies; }
@@ -28,9 +28,13 @@ namespace bs
 		/**	Returns true if this resource is allow to be asynchronously loaded. */
 		bool allowAsyncLoading() const { return mAllowAsync; }
 
+		/** Returns the method used for compressing the resource. 0 if none. */
+		UINT32 getCompressionMethod() const { return mCompressionMethod; }
+
 	private:
 		Vector<String> mDependencies;
 		bool mAllowAsync;
+		UINT32 mCompressionMethod;
 
 	/************************************************************************/
 	/* 								SERIALIZATION                      		*/
@@ -38,7 +42,7 @@ namespace bs
 	public:
 		friend class SavedResourceDataRTTI;
 		static RTTITypeBase* getRTTIStatic();
-		virtual RTTITypeBase* getRTTI() const override;
+		RTTITypeBase* getRTTI() const override;
 	};
 
 	/** @} */

+ 7 - 13
Source/BansheeCore/Include/BsSavedResourceDataRTTI.h

@@ -16,22 +16,16 @@ namespace bs
 	class BS_CORE_EXPORT SavedResourceDataRTTI : public RTTIType <SavedResourceData, IReflectable, SavedResourceDataRTTI>
 	{
 	private:
-		String& getDependency(SavedResourceData* obj, UINT32 arrayIdx) { return obj->mDependencies[arrayIdx]; }
-		void setDependency(SavedResourceData* obj, UINT32 arrayIdx, String& val) { obj->mDependencies[arrayIdx] = val; }
-
-		UINT32 getNumDependencies(SavedResourceData* obj) { return (UINT32)obj->mDependencies.size(); }
-		void setNumDependencies(SavedResourceData* obj, UINT32 numEntries) { obj->mDependencies = Vector<String>(numEntries); }
-
-		bool& getAllowAsyncLoading(SavedResourceData* obj) { return obj->mAllowAsync; }
-		void setAllowAsyncLoading(SavedResourceData* obj, bool& value) { obj->mAllowAsync = value; }
+		BS_BEGIN_RTTI_MEMBERS
+			BS_RTTI_MEMBER_PLAIN_ARRAY(mDependencies, 0)
+			BS_RTTI_MEMBER_PLAIN(mAllowAsync, 1)
+			BS_RTTI_MEMBER_PLAIN(mCompressionMethod, 2)
+		BS_END_RTTI_MEMBERS
 
 	public:
 		SavedResourceDataRTTI()
-		{
-			addPlainArrayField("mDependencies", 0, &SavedResourceDataRTTI::getDependency, &SavedResourceDataRTTI::getNumDependencies,
-				&SavedResourceDataRTTI::setDependency, &SavedResourceDataRTTI::setNumDependencies);
-			addPlainField("mAllowAsync", 1, &SavedResourceDataRTTI::getAllowAsyncLoading, &SavedResourceDataRTTI::setAllowAsyncLoading);
-		}
+			:mInitMembers(this)
+		{ }
 
 		const String& getRTTIName() override
 		{

+ 94 - 13
Source/BansheeCore/Source/BsResources.cpp

@@ -12,6 +12,10 @@
 #include "BsUtility.h"
 #include "BsSavedResourceData.h"
 #include "BsResourceListenerManager.h"
+#include "BsMemorySerializer.h"
+#include "BsCompression.h"
+#include "BsDataStream.h"
+#include "BsBinarySerializer.h"
 
 namespace bs
 {
@@ -355,14 +359,52 @@ namespace bs
 
 	SPtr<Resource> Resources::loadFromDiskAndDeserialize(const Path& filePath, bool loadWithSaveData)
 	{
-		FileDecoder fs(filePath);
-		fs.skip(); // Skipped over saved resource data
+		// Note: Called asynchronously over multiple resources this will cause performance issues on hard drives as they
+		// work best when they are reading one thing at a time, and it won't have benefits on an SSD either. Think about
+		// executing all file reads on a single thread, while decompression and similar operations can execute on multiple.
 
-		UnorderedMap<String, UINT64> loadParams;
+		SPtr<DataStream> stream = FileSystem::openFile(filePath, true);
+		if (stream == nullptr)
+			return nullptr;
+
+		if (stream->size() > std::numeric_limits<UINT32>::max())
+		{
+			BS_EXCEPT(InternalErrorException,
+				"File size is larger that UINT32 can hold. Ask a programmer to use a bigger data type.");
+		}
+
+		UnorderedMap<String, UINT64> params;
 		if(loadWithSaveData)
-			loadParams["keepSourceData"] = 1;
+			params["keepSourceData"] = 1;
+
+		// Read meta-data
+		SPtr<SavedResourceData> metaData;
+		{
+			if (!stream->eof())
+			{
+				UINT32 objectSize = 0;
+				stream->read(&objectSize, sizeof(objectSize));
+
+				BinarySerializer bs;
+				metaData = std::static_pointer_cast<SavedResourceData>(bs.decode(stream, objectSize, params));
+			}
+		}
+
+		// Read resource data
+		SPtr<IReflectable> loadedData;
+		{
+			if(metaData && !stream->eof())
+			{
+				UINT32 objectSize = 0;
+				stream->read(&objectSize, sizeof(objectSize));
+
+				if (metaData->getCompressionMethod() != 0)
+					stream = Compression::decompress(stream);
 
-		SPtr<IReflectable> loadedData = fs.decode(loadParams);
+				BinarySerializer bs;
+				loadedData = std::static_pointer_cast<SavedResourceData>(bs.decode(stream, objectSize, params));
+			}
+		}
 
 		if (loadedData == nullptr)
 		{
@@ -488,7 +530,7 @@ namespace bs
 		resource.setHandleData(nullptr, uuid);
 	}
 
-	void Resources::save(const HResource& resource, const Path& filePath, bool overwrite)
+	void Resources::save(const HResource& resource, const Path& filePath, bool overwrite, bool compress)
 	{
 		if (resource == nullptr)
 			return;
@@ -534,21 +576,60 @@ namespace bs
 		for (UINT32 i = 0; i < (UINT32)dependencyList.size(); i++)
 			dependencyUUIDs[i] = dependencyList[i].resource.getUUID();
 
-		SPtr<SavedResourceData> resourceData = bs_shared_ptr_new<SavedResourceData>(dependencyUUIDs, resource->allowAsyncLoading());
+		UINT32 compressionMethod = (compress && resource->isCompressible()) ? 1 : 0;
+		SPtr<SavedResourceData> resourceData = bs_shared_ptr_new<SavedResourceData>(dependencyUUIDs, 
+			resource->allowAsyncLoading(), compressionMethod);
+
+		Path parentDir = filePath.getDirectory();
+		if (!FileSystem::exists(parentDir))
+			FileSystem::createDir(parentDir);
+		
+		std::ofstream stream;
+		stream.open(filePath.toPlatformString().c_str(), std::ios::out | std::ios::binary);
+		if (stream.fail())
+			LOGWRN("Failed to save file: \"" + filePath.toString() + "\". Error: " + strerror(errno) + ".");
+	
+		// Write meta-data
+		{
+			MemorySerializer ms;
+			UINT32 numBytes = 0;
+			UINT8* bytes = ms.encode(resourceData.get(), numBytes);
+			
+			stream.write((char*)&numBytes, sizeof(numBytes));
+			stream.write((char*)bytes, numBytes);
+			
+			bs_free(bytes);
+		}
+
+		// Write object data
+		{
+			MemorySerializer ms;
+			UINT32 numBytes = 0;
+			UINT8* bytes = ms.encode(resource.get(), numBytes);
+
+			SPtr<MemoryDataStream> objStream = bs_shared_ptr_new<MemoryDataStream>(bytes, numBytes);
+			if (compressionMethod != 0)
+			{
+				SPtr<DataStream> srcStream = std::static_pointer_cast<DataStream>(objStream);
+				objStream = Compression::compress(srcStream);
+			}
+
+			stream.write((char*)&numBytes, sizeof(numBytes));
+			stream.write((char*)objStream->getPtr(), objStream->size());
+		}
 
-		FileEncoder fs(filePath);
-		fs.encode(resourceData.get());
-		fs.encode(resource.get());
+		stream.close();
+		stream.clear();
 	}
 
-	void Resources::save(const HResource& resource)
+	void Resources::save(const HResource& resource, bool compress)
 	{
 		if (resource == nullptr)
 			return;
 
 		Path path;
 		if (getFilePathFromUUID(resource.getUUID(), path))
-			save(resource, path, true);
+			save(resource, path, true, compress);
 	}
 
 	void Resources::update(HResource& handle, const SPtr<Resource>& resource)
@@ -784,4 +865,4 @@ namespace bs
 	{
 		return Resources::instance();
 	}
-}
+}

+ 4 - 6
Source/BansheeCore/Source/BsSavedResourceData.cpp

@@ -6,14 +6,12 @@
 namespace bs
 {
 	SavedResourceData::SavedResourceData()
-		:mAllowAsync(true)
+		:mAllowAsync(true), mCompressionMethod(0)
 	{ }
 
-	SavedResourceData::SavedResourceData(const Vector<String>& dependencies, bool allowAsync)
-		:mDependencies(dependencies), mAllowAsync(allowAsync)
-	{
-
-	}
+	SavedResourceData::SavedResourceData(const Vector<String>& dependencies, bool allowAsync, UINT32 compressionMethod)
+		:mDependencies(dependencies), mAllowAsync(allowAsync), mCompressionMethod(compressionMethod)
+	{ }
 
 	RTTITypeBase* SavedResourceData::getRTTIStatic()
 	{

+ 8 - 1
Source/BansheeUtility/CMakeLists.txt

@@ -1,10 +1,14 @@
 # Source files and their filters
 include(CMakeSources.cmake)
 
+# Packages
+find_package(Snappy REQUIRED)
+
 # Includes
 set(BansheeUtility_INC 
 	"Include" 
-	"Include/ThirdParty")
+	"Include/ThirdParty"
+	${Snappy_INCLUDE_DIRS})
 
 if(WIN32)
 	set(BansheeUtility_INC ${BansheeUtility_INC} "Include/Win32")
@@ -24,6 +28,9 @@ target_link_libraries(BansheeUtilityTest BansheeUtility)
 target_compile_definitions(BansheeUtility PRIVATE -DBS_UTILITY_EXPORTS)
 
 # Libraries
+## External lib: Snappy
+target_link_libraries(BansheeUtility ${Snappy_LIBRARIES})	
+
 ## OS libs
 if(WIN32)
 target_link_libraries(BansheeUtility 

+ 2 - 0
Source/BansheeUtility/CMakeSources.cmake

@@ -55,6 +55,7 @@ set(BS_BANSHEEUTILITY_SRC_GENERAL
 	"Source/BsTimer.cpp"
 	"Source/BsTime.cpp"
 	"Source/BsUtil.cpp"
+	"Source/BsCompression.cpp"
 )
 
 set(BS_BANSHEEUTILITY_INC_DEBUG
@@ -96,6 +97,7 @@ set(BS_BANSHEEUTILITY_INC_GENERAL
 	"Include/BsTimer.h"
 	"Include/BsUtil.h"
 	"Include/BsFlags.h"
+	"Include/BsCompression.h"
 )
 
 set(BS_BANSHEEUTILITY_SRC_ALLOCATORS

+ 25 - 0
Source/BansheeUtility/Include/BsCompression.h

@@ -0,0 +1,25 @@
+//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
+//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
+#pragma once
+
+#include "BsPrerequisitesUtil.h"
+
+namespace bs
+{
+	/** @addtogroup Utility-Core
+	 *  @{
+	 */
+
+	/** Performs generic compression and decompression on raw data. */
+	class BS_UTILITY_EXPORT Compression
+	{
+	public:
+		/** Compresses the data from the provided data stream and outputs the new stream with compressed data. */
+		static SPtr<MemoryDataStream> compress(SPtr<DataStream>& input);
+
+		/** Decompresses the data from the provided data stream and outputs the new stream with decompressed data. */
+		static SPtr<MemoryDataStream> decompress(SPtr<DataStream>& input);
+	};
+
+	/** @} */
+}

+ 8 - 1
Source/BansheeUtility/Include/BsDataStream.h

@@ -154,6 +154,13 @@ namespace bs
 	class BS_UTILITY_EXPORT MemoryDataStream : public DataStream
 	{		
 	public:
+		/**
+		 * Allocates a new chunk of memory and wraps it in a stream.
+		 *
+		 * @param[in]	size		Size of the memory chunk in bytes.
+		 */
+		MemoryDataStream(size_t size);
+
 		/**
 		 * Wrap an existing memory chunk in a stream.
 		 *
@@ -162,7 +169,7 @@ namespace bs
 		 * @param[in]	freeOnClose	Should the memory buffer be freed when the data stream goes out of scope.
 		 */
 		MemoryDataStream(void* memory, size_t size, bool freeOnClose = true);
-		
+
 		/**
 		 * Create a stream which pre-buffers the contents of another stream. Data from the other buffer will be entirely 
 		 * read and stored in an internal buffer.

+ 2 - 1
Source/BansheeUtility/Include/BsFileSerializer.h

@@ -14,9 +14,10 @@ namespace bs
 	// a generic Serializer interface so it may write both binary, plain-text or some other form of data.
 
 	/** Encodes the provided object to the specified file using the RTTI system. */
+	
 	class BS_UTILITY_EXPORT FileEncoder
 	{
-	public:
+		public:
 		FileEncoder(const Path& fileLocation);
 		~FileEncoder();
 

+ 194 - 0
Source/BansheeUtility/Source/BsCompression.cpp

@@ -0,0 +1,194 @@
+//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
+//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
+#include "BsCompression.h"
+#include "BsDataStream.h"
+
+// Third party
+#include "snappy.h"
+#include "snappy-sinksource.h"
+#include "BsDebug.h"
+
+namespace bs
+{
+	/** Source accepting a data stream. Used for Snappy compression library. */
+	class DataStreamSource : public snappy::Source
+	{
+	public:
+		DataStreamSource(const SPtr<DataStream>& stream)
+			: mStream(stream), mReadBuffer(nullptr), mReadBufferContentSize(0), mBufferOffset(0)
+		{
+			mRemaining = mStream->size() - mStream->tell();
+
+			if (mStream->isFile())
+				mReadBuffer = (char*)bs_alloc(2048);
+		}
+
+		virtual ~DataStreamSource()
+		{
+			if (mReadBuffer != nullptr)
+				bs_free(mReadBuffer);
+		}
+
+		size_t Available() const override
+		{
+			return mRemaining;
+		}
+
+		const char* Peek(size_t* len) override
+		{
+			if (!mStream->isFile())
+			{
+				SPtr<MemoryDataStream> memStream = std::static_pointer_cast<MemoryDataStream>(mStream);
+
+				*len = Available();
+				return (char*)memStream->getPtr() + mBufferOffset;
+			}
+			else
+			{
+				while (mBufferOffset >= mReadBufferContentSize)
+				{
+					mBufferOffset -= mReadBufferContentSize;
+					mReadBufferContentSize = mStream->read(mReadBuffer, 2048);
+
+					if (mReadBufferContentSize == 0)
+						break;
+				}
+
+				*len = mReadBufferContentSize - mBufferOffset;
+				return (char*)(mReadBuffer + mBufferOffset);
+			}
+		}
+
+		void Skip(size_t n) override
+		{
+			mBufferOffset += n;
+			mRemaining -= n;
+		}
+	private:
+		SPtr<DataStream> mStream;
+
+		// File streams only
+		char* mReadBuffer;
+		size_t mReadBufferContentSize;
+		size_t mRemaining;
+		size_t mBufferOffset;
+	};
+
+	/** Sink (destination) accepting a data stream. Used for Snappy compression library. */
+	class DataStreamSink : public snappy::Sink
+	{
+		struct BufferPiece
+		{
+			char* buffer;
+			size_t size;
+		};
+
+	public:
+		DataStreamSink() { }
+		virtual ~DataStreamSink()
+		{
+			for (auto& entry : mBufferPieces)
+				bs_free(entry.buffer);
+		}
+
+		void Append(const char* data, size_t n) override
+		{
+			if(mBufferPieces.size() == 0 || mBufferPieces.back().buffer != data)
+			{
+				BufferPiece piece;
+				piece.buffer = (char*)bs_alloc((UINT32)n);
+				piece.size = n;
+
+				memcpy(piece.buffer, data, n);
+				mBufferPieces.push_back(piece);
+			}
+			else
+			{
+				BufferPiece& piece = mBufferPieces.back();
+				assert(piece.buffer == data);
+
+				piece.size = n;
+			}
+		}
+
+		char* GetAppendBuffer(size_t len, char* scratch) override
+		{
+			BufferPiece piece;
+			piece.buffer = (char*)bs_alloc((UINT32)len);
+			piece.size = 0;
+
+			mBufferPieces.push_back(piece);
+			return piece.buffer;
+		}
+
+		char* GetAppendBufferVariable(size_t min_size, size_t desired_size_hint, char* scratch, size_t scratch_size,
+			size_t* allocated_size) override
+		{
+			BufferPiece piece;
+			piece.buffer = (char*)bs_alloc((UINT32)desired_size_hint);
+			piece.size = 0;
+
+			mBufferPieces.push_back(piece);
+
+			*allocated_size = desired_size_hint;
+			return piece.buffer;
+		}
+
+		void AppendAndTakeOwnership(char* bytes, size_t n, void(*deleter)(void*, const char*, size_t),
+			void *deleter_arg) override
+		{
+			BufferPiece& piece = mBufferPieces.back();
+
+			if (piece.buffer != bytes)
+			{
+				memcpy(piece.buffer, bytes, n);
+				(*deleter)(deleter_arg, bytes, n);
+			}
+
+			piece.size = n;
+		}
+
+		SPtr<MemoryDataStream> GetOutput()
+		{
+			size_t totalSize = 0;
+			for (auto& entry : mBufferPieces)
+				totalSize += entry.size;
+
+			SPtr<MemoryDataStream> ds = bs_shared_ptr_new<MemoryDataStream>(totalSize);
+			for (auto& entry : mBufferPieces)
+				ds->write(entry.buffer, entry.size);
+
+			ds->seek(0);
+			return ds;
+		}
+
+	private:
+		Vector<BufferPiece> mBufferPieces;
+	};
+
+	SPtr<MemoryDataStream> Compression::compress(SPtr<DataStream>& input)
+	{
+		DataStreamSource src(input);
+		DataStreamSink dst;
+
+		size_t bytesWritten = snappy::Compress(&src, &dst);
+		SPtr<MemoryDataStream> output = dst.GetOutput();
+		assert(output->size() == bytesWritten);
+
+		return output;
+	}
+
+	SPtr<MemoryDataStream> Compression::decompress(SPtr<DataStream>& input)
+	{
+		DataStreamSource src(input);
+		DataStreamSink dst;
+
+		if (!snappy::Uncompress(&src, &dst))
+		{
+			LOGERR("Decompression failed, corrupt data.");
+			return nullptr;
+		}
+
+		return dst.GetOutput();
+	}
+}

+ 112 - 102
Source/BansheeUtility/Source/BsDataStream.cpp

@@ -38,12 +38,12 @@ namespace bs
 		return (buffer[0] == 0xEF && buffer[1] == 0xBB && buffer[2] == 0xBF);
 	}
 
-    template <typename T> DataStream& DataStream::operator>> (T& val)
-    {
-        read(static_cast<void*>(&val), sizeof(T));
+	template <typename T> DataStream& DataStream::operator>> (T& val)
+	{
+		read(static_cast<void*>(&val), sizeof(T));
 
-        return *this;
-    }
+		return *this;
+	}
 
 	void DataStream::writeString(const String& string, StringEncoding encoding)
 	{
@@ -246,65 +246,75 @@ namespace bs
 		}
 	}
 
-    MemoryDataStream::MemoryDataStream(void* memory, size_t inSize, bool freeOnClose)
+	MemoryDataStream::MemoryDataStream(size_t size)
+		: DataStream(READ | WRITE), mData(nullptr), mFreeOnClose(true)
+	{
+		mData = mPos = (UINT8*)bs_alloc(size);
+		mSize = size;
+		mEnd = mData + mSize;
+
+		assert(mEnd >= mPos);
+	}
+
+	MemoryDataStream::MemoryDataStream(void* memory, size_t inSize, bool freeOnClose)
 		: DataStream(READ | WRITE), mData(nullptr), mFreeOnClose(freeOnClose)
-    {
-        mData = mPos = static_cast<UINT8*>(memory);
-        mSize = inSize;
-        mEnd = mData + mSize;
+	{
+		mData = mPos = static_cast<UINT8*>(memory);
+		mSize = inSize;
+		mEnd = mData + mSize;
 
-        assert(mEnd >= mPos);
-    }
+		assert(mEnd >= mPos);
+	}
 
-    MemoryDataStream::MemoryDataStream(DataStream& sourceStream)
-        : DataStream(READ | WRITE), mData(nullptr)
-    {
-        // Copy data from incoming stream
-        mSize = sourceStream.size();
+	MemoryDataStream::MemoryDataStream(DataStream& sourceStream)
+		: DataStream(READ | WRITE), mData(nullptr)
+	{
+		// Copy data from incoming stream
+		mSize = sourceStream.size();
 
 		mData = (UINT8*)bs_alloc((UINT32)mSize);
 		mPos = mData;
 		mEnd = mData + sourceStream.read(mData, mSize);
 		mFreeOnClose = true;
 
-        assert(mEnd >= mPos);
-    }
+		assert(mEnd >= mPos);
+	}
 
-    MemoryDataStream::MemoryDataStream(const SPtr<DataStream>& sourceStream)
-        :DataStream(READ | WRITE), mData(nullptr)
-    {
-        // Copy data from incoming stream
-        mSize = sourceStream->size();
+	MemoryDataStream::MemoryDataStream(const SPtr<DataStream>& sourceStream)
+		:DataStream(READ | WRITE), mData(nullptr)
+	{
+		// Copy data from incoming stream
+		mSize = sourceStream->size();
 
 		mData = (UINT8*)bs_alloc((UINT32)mSize);
 		mPos = mData;
 		mEnd = mData + sourceStream->read(mData, mSize);
 		mFreeOnClose = true;
 
-        assert(mEnd >= mPos);
-    }
+		assert(mEnd >= mPos);
+	}
 
-    MemoryDataStream::~MemoryDataStream()
-    {
-        close();
-    }
+	MemoryDataStream::~MemoryDataStream()
+	{
+		close();
+	}
 
-    size_t MemoryDataStream::read(void* buf, size_t count)
-    {
-        size_t cnt = count;
+	size_t MemoryDataStream::read(void* buf, size_t count)
+	{
+		size_t cnt = count;
 
-        if (mPos + cnt > mEnd)
-            cnt = mEnd - mPos;
-        if (cnt == 0)
-            return 0;
+		if (mPos + cnt > mEnd)
+			cnt = mEnd - mPos;
+		if (cnt == 0)
+			return 0;
 
-        assert (cnt <= count);
+		assert (cnt <= count);
 
-        memcpy(buf, mPos, cnt);
-        mPos += cnt;
+		memcpy(buf, mPos, cnt);
+		mPos += cnt;
 
-        return cnt;
-    }
+		return cnt;
+	}
 
 	size_t MemoryDataStream::write(const void* buf, size_t count)
 	{
@@ -325,29 +335,29 @@ namespace bs
 		return written;
 	}
 
-    void MemoryDataStream::skip(size_t count)
-    {
-        size_t newpos = (size_t)( (mPos - mData) + count );
-        assert(mData + newpos <= mEnd);        
+	void MemoryDataStream::skip(size_t count)
+	{
+		size_t newpos = (size_t)( (mPos - mData) + count );
+		assert(mData + newpos <= mEnd);        
 
-        mPos = mData + newpos;
-    }
+		mPos = mData + newpos;
+	}
 
-    void MemoryDataStream::seek(size_t pos)
-    {
-        assert(mData + pos <= mEnd);
-        mPos = mData + pos;
-    }
+	void MemoryDataStream::seek(size_t pos)
+	{
+		assert(mData + pos <= mEnd);
+		mPos = mData + pos;
+	}
 
-    size_t MemoryDataStream::tell() const
+	size_t MemoryDataStream::tell() const
 	{
 		return mPos - mData;
 	}
 
-    bool MemoryDataStream::eof() const
-    {
-        return mPos >= mEnd;
-    }
+	bool MemoryDataStream::eof() const
+	{
+		return mPos >= mEnd;
+	}
 
 	SPtr<DataStream> MemoryDataStream::clone(bool copyData) const
 	{
@@ -357,20 +367,20 @@ namespace bs
 		return bs_shared_ptr_new<MemoryDataStream>(*this);
 	}
 
-    void MemoryDataStream::close()    
-    {
-        if (mData != nullptr)
-        {
+	void MemoryDataStream::close()    
+	{
+		if (mData != nullptr)
+		{
 			if(mFreeOnClose)
 				bs_free(mData);
 
-            mData = nullptr;
-        }
-    }
+			mData = nullptr;
+		}
+	}
 
-    FileDataStream::FileDataStream(const Path& path, AccessMode accessMode, bool freeOnClose)
-        : DataStream(accessMode), mPath(path), mFreeOnClose(freeOnClose)
-    {
+	FileDataStream::FileDataStream(const Path& path, AccessMode accessMode, bool freeOnClose)
+		: DataStream(accessMode), mPath(path), mFreeOnClose(freeOnClose)
+	{
 		// Always open in binary mode
 		// Also, always include reading
 		std::ios::openmode mode = std::ios::binary;
@@ -399,22 +409,22 @@ namespace bs
 			return;
 		}
 		
-        mInStream->seekg(0, std::ios_base::end);
-        mSize = (size_t)mInStream->tellg();
-        mInStream->seekg(0, std::ios_base::beg);
-    }
-
-    FileDataStream::~FileDataStream()
-    {
-        close();
-    }
-
-    size_t FileDataStream::read(void* buf, size_t count)
-    {
+		mInStream->seekg(0, std::ios_base::end);
+		mSize = (size_t)mInStream->tellg();
+		mInStream->seekg(0, std::ios_base::beg);
+	}
+
+	FileDataStream::~FileDataStream()
+	{
+		close();
+	}
+
+	size_t FileDataStream::read(void* buf, size_t count)
+	{
 		mInStream->read(static_cast<char*>(buf), static_cast<std::streamsize>(count));
 
-        return (size_t)mInStream->gcount();
-    }
+		return (size_t)mInStream->gcount();
+	}
 
 	size_t FileDataStream::write(const void* buf, size_t count)
 	{
@@ -427,41 +437,41 @@ namespace bs
 
 		return written;
 	}
-    void FileDataStream::skip(size_t count)
-    {	
+	void FileDataStream::skip(size_t count)
+	{	
 		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)
+	{
 		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
 	{
 		mInStream->clear(); // Clear fail status in case eof was set
 
 		return (size_t)mInStream->tellg();
 	}
 
-    bool FileDataStream::eof() const
-    {
-        return mInStream->eof();
-    }
+	bool FileDataStream::eof() const
+	{
+		return mInStream->eof();
+	}
 
 	SPtr<DataStream> FileDataStream::clone(bool copyData) const
 	{
 		return bs_shared_ptr_new<FileDataStream>(mPath, (AccessMode)getAccessMode(), true);
 	}
 
-    void FileDataStream::close()
-    {
-        if (mInStream)
-        {
+	void FileDataStream::close()
+	{
+		if (mInStream)
+		{
 			if (mFStreamRO)
-	            mFStreamRO->close();
+				mFStreamRO->close();
 
 			if (mFStream)
 			{
@@ -469,12 +479,12 @@ namespace bs
 				mFStream->close();
 			}
 
-            if (mFreeOnClose)
-            {
+			if (mFreeOnClose)
+			{
 				mInStream = nullptr;
 				mFStreamRO = nullptr; 
 				mFStream = nullptr; 
-            }
-        }
-    }
+			}
+		}
+	}
 }

+ 1 - 1
Source/BansheeUtility/Source/BsFileSerializer.cpp

@@ -22,7 +22,7 @@ namespace bs
 		Path parentDir = fileLocation.getDirectory();
 		if (!FileSystem::exists(parentDir))
 			FileSystem::createDir(parentDir);
-
+		
 		mOutputStream.open(fileLocation.toPlatformString().c_str(), std::ios::out | std::ios::binary);
 		if (mOutputStream.fail())
 		{

+ 44 - 0
Source/CMake/Modules/FindSnappy.cmake

@@ -0,0 +1,44 @@
+# Find Snappy dependency
+#
+# This module defines
+#  Snappy_INCLUDE_DIRS
+#  Snappy_LIBRARIES
+#  Snappy_FOUND
+
+set(Snappy_INSTALL_DIR ${PROJECT_SOURCE_DIR}/../Dependencies/Snappy CACHE PATH "")
+set(Snappy_INCLUDE_SEARCH_DIRS "${Snappy_INSTALL_DIR}/include")
+
+if(BS_64BIT)
+	list(APPEND Snappy_LIBRARY_RELEASE_SEARCH_DIRS "${Snappy_INSTALL_DIR}/lib/x64/Release")
+	list(APPEND Snappy_LIBRARY_DEBUG_SEARCH_DIRS "${Snappy_INSTALL_DIR}/lib/x64/Debug")
+else()
+	list(APPEND Snappy_LIBRARY_RELEASE_SEARCH_DIRS "${Snappy_INSTALL_DIR}/lib/x86/Release")
+	list(APPEND Snappy_LIBRARY_DEBUG_SEARCH_DIRS "${Snappy_INSTALL_DIR}/lib/x86/Debug")
+endif()
+
+message(STATUS "Looking for Snappy installation...")
+	
+find_path(Snappy_INCLUDE_DIR snappy.h PATHS ${Snappy_INCLUDE_SEARCH_DIRS})	
+	
+if(Snappy_INCLUDE_DIR)
+	set(Snappy_FOUND TRUE)
+else()
+	set(Snappy_FOUND FALSE)
+endif()	
+	
+find_imported_library(Snappy Snappy)
+
+if(NOT Snappy_FOUND)
+	if(Snappy_FIND_REQUIRED)
+		message(FATAL_ERROR "Cannot find Snappy installation. Try modifying the Snappy_INSTALL_DIR path.")
+	elseif(NOT Snappy_FIND_QUIETLY)
+		message(WARNING "Cannot find Snappy installation. Try modifying the Snappy_INSTALL_DIR path.")
+	endif()
+else()
+	message(STATUS "...Snappy OK.")
+endif()
+
+mark_as_advanced(Snappy_INSTALL_DIR Snappy_INCLUDE_DIR)
+
+set(Snappy_INCLUDE_DIRS ${Snappy_INCLUDE_DIR})
+set(Snappy_LIBRARIES Snappy)

+ 3 - 3
Source/CMakeLists.txt

@@ -5,7 +5,7 @@ project (Banshee)
 set (BS_VERSION_MAJOR 0)
 set (BS_VERSION_MINOR 4)
 
-set (BS_PREBUILT_DEPENDENCIES_VERSION 1)
+set (BS_PREBUILT_DEPENDENCIES_VERSION 2)
 set (BS_SRC_DEPENDENCIES_VERSION 8)
 
 # Configuration types
@@ -65,11 +65,11 @@ mark_as_advanced(CMAKE_INSTALL_PREFIX)
 ## Check prebuilt dependencies that user downloads in a .zip
 set(BUILTIN_DEP_VERSION_FILE ${PROJECT_SOURCE_DIR}/../Dependencies/.version)
 if(NOT EXISTS ${BUILTIN_DEP_VERSION_FILE})
-	message(FATAL_ERROR "Precompiled dependencies package is missing our out of date. Re-run setup or manually re-download and extract dependencies.")
+	message(FATAL_ERROR "Precompiled dependencies package is missing our out of date. Re-run setup or manually download the latest dependencies package.")
 else()
 	file (STRINGS ${BUILTIN_DEP_VERSION_FILE} CURRENT_BUILTIN_DEP_VERSION)
 	if(${BS_PREBUILT_DEPENDENCIES_VERSION} GREATER ${CURRENT_BUILTIN_DEP_VERSION})
-		message(FATAL_ERROR "Your precomiled dependencies package is out of date. Re-run setup or manually re-download and extract dependencies.")
+		message(FATAL_ERROR "Your precomiled dependencies package is out of date. Re-run setup or manually download the latest dependencies package.")
 	endif()
 endif()