Просмотр исходного кода

Some more work on asset importers

Marko Pintera 13 лет назад
Родитель
Сommit
9b6218cf44

+ 10 - 8
CamelotFreeImgImporter/CamelotFreeImgImporter.vcxproj

@@ -11,9 +11,11 @@
     </ProjectConfiguration>
   </ItemGroup>
   <ItemGroup>
+    <ClInclude Include="Include\CmFreeImgImporter.h" />
     <ClInclude Include="Include\CmFreeImgPrerequisites.h" />
   </ItemGroup>
   <ItemGroup>
+    <ClCompile Include="Source\CmFreeImgImporter.cpp" />
     <ClCompile Include="Source\CmFreeImgPlugin.cpp" />
   </ItemGroup>
   <PropertyGroup Label="Globals">
@@ -45,22 +47,22 @@
   </ImportGroup>
   <PropertyGroup Label="UserMacros" />
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
-    <OutDir>..\bin\$(Configuration)</OutDir>
+    <OutDir>..\bin\$(Configuration)\</OutDir>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
-    <OutDir>..\bin\$(Configuration)</OutDir>
+    <OutDir>..\bin\$(Configuration)\</OutDir>
   </PropertyGroup>
   <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
     <ClCompile>
       <WarningLevel>Level3</WarningLevel>
       <Optimization>Disabled</Optimization>
       <PreprocessorDefinitions>CM_FREEIMG_EXPORTS;_MBCS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
-      <AdditionalIncludeDirectories>../CamelotRenderer/Include;../CamelotUtility/Include;./Include</AdditionalIncludeDirectories>
+      <AdditionalIncludeDirectories>../CamelotRenderer/Include;../CamelotUtility/Include;./Include;./Dependencies/Include;../Dependencies/Include</AdditionalIncludeDirectories>
     </ClCompile>
     <Link>
       <GenerateDebugInformation>true</GenerateDebugInformation>
-      <AdditionalDependencies>CamelotRenderer.lib;%(AdditionalDependencies)</AdditionalDependencies>
-      <AdditionalLibraryDirectories>../lib/$(Configuration)</AdditionalLibraryDirectories>
+      <AdditionalDependencies>CamelotRenderer.lib;FreeImaged.lib;libboost_signals-vc110-mt-gd-1_49.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <AdditionalLibraryDirectories>../lib/$(Configuration);./Dependencies/lib/Debug</AdditionalLibraryDirectories>
       <ImportLibrary>..\lib\$(Configuration)\$(TargetName).lib</ImportLibrary>
     </Link>
   </ItemDefinitionGroup>
@@ -71,14 +73,14 @@
       <FunctionLevelLinking>true</FunctionLevelLinking>
       <IntrinsicFunctions>true</IntrinsicFunctions>
       <PreprocessorDefinitions>CM_FREEIMG_EXPORTS;_MBCS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
-      <AdditionalIncludeDirectories>../CamelotRenderer/Include;../CamelotUtility/Include;./Include</AdditionalIncludeDirectories>
+      <AdditionalIncludeDirectories>../CamelotRenderer/Include;../CamelotUtility/Include;./Include;./Dependencies/Include;../Dependencies/Include</AdditionalIncludeDirectories>
     </ClCompile>
     <Link>
       <GenerateDebugInformation>true</GenerateDebugInformation>
       <EnableCOMDATFolding>true</EnableCOMDATFolding>
       <OptimizeReferences>true</OptimizeReferences>
-      <AdditionalDependencies>CamelotRenderer.lib;%(AdditionalDependencies)</AdditionalDependencies>
-      <AdditionalLibraryDirectories>../lib/$(Configuration)</AdditionalLibraryDirectories>
+      <AdditionalDependencies>CamelotRenderer.lib;FreeImage.lib;libboost_signals-vc110-mt-1_49.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <AdditionalLibraryDirectories>../lib/$(Configuration);./Dependencies/lib/Release</AdditionalLibraryDirectories>
       <ImportLibrary>..\lib\$(Configuration)\$(TargetName).lib</ImportLibrary>
     </Link>
   </ItemDefinitionGroup>

+ 6 - 0
CamelotFreeImgImporter/CamelotFreeImgImporter.vcxproj.filters

@@ -18,10 +18,16 @@
     <ClInclude Include="Include\CmFreeImgPrerequisites.h">
       <Filter>Header Files</Filter>
     </ClInclude>
+    <ClInclude Include="Include\CmFreeImgImporter.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
   </ItemGroup>
   <ItemGroup>
     <ClCompile Include="Source\CmFreeImgPlugin.cpp">
       <Filter>Source Files</Filter>
     </ClCompile>
+    <ClCompile Include="Source\CmFreeImgImporter.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
   </ItemGroup>
 </Project>

+ 38 - 0
CamelotFreeImgImporter/Include/CmFreeImgImporter.h

@@ -0,0 +1,38 @@
+#include "CmFreeImgPrerequisites.h"
+#include "CmSpecificImporter.h"
+#include "CmImporter.h"
+
+namespace CamelotEngine
+{
+	class FreeImgImporter : public SpecificImporter
+	{
+	public:
+		FreeImgImporter();
+		virtual ~FreeImgImporter();
+
+		/** Inherited from SpecificImporter */
+		virtual const std::vector<String>& extensions() const;
+
+		/** Inherited from SpecificImporter */
+		virtual ResourcePtr import(const String& filePath);
+
+	private:
+		vector<String>::type mExtensions;
+
+		class InitOnStart
+		{
+		public:
+			InitOnStart() 
+			{ 
+				static FreeImgImporter* importer = nullptr;
+				if(importer == nullptr)
+				{
+					importer = new FreeImgImporter();
+					Importer::instance().registerAssetImporter(importer);
+				}
+			}
+		};
+
+		static InitOnStart initOnStart; // Makes sure importer is registered on program start
+	};
+}

+ 243 - 0
CamelotFreeImgImporter/Source/CmFreeImgImporter.cpp

@@ -0,0 +1,243 @@
+#include "CmFreeImgImporter.h"
+#include "CmResource.h"
+#include "CmDebug.h"
+#include "CmPixelUtil.h"
+
+#include "FreeImage.h"
+
+namespace CamelotEngine
+{
+	void FreeImageLoadErrorHandler(FREE_IMAGE_FORMAT fif, const char *message) 
+	{
+		// Callback method as required by FreeImage to report problems
+		const char* typeName = FreeImage_GetFormatFromFIF(fif);
+		if (typeName)
+		{
+			gDebug().log("FreeImage error: '" + String(message) + "' when loading format " + typeName, "AssetImport");
+		}
+		else
+		{
+			gDebug().log("FreeImage error: '" + String(message) + "'", "AssetImport");
+		}
+	}
+
+	FreeImgImporter::FreeImgImporter()
+		:SpecificImporter() 
+	{
+		FreeImage_Initialise(false);
+
+		// Register codecs
+		StringUtil::StrStreamType strExt;
+		strExt << "Supported formats: ";
+		bool first = true;
+		for (int i = 0; i < FreeImage_GetFIFCount(); ++i)
+		{
+
+			// Skip DDS codec since FreeImage does not have the option 
+			// to keep DXT data compressed, we'll use our own codec
+			if ((FREE_IMAGE_FORMAT)i == FIF_DDS)
+				continue;
+
+			String exts(FreeImage_GetFIFExtensionList((FREE_IMAGE_FORMAT)i));
+			if (!first)
+			{
+				strExt << ",";
+			}
+			first = false;
+			strExt << exts;
+
+			// Pull off individual formats (separated by comma by FI)
+			vector<String>::type extsVector = StringUtil::split(exts, ",");
+			for (auto v = extsVector.begin(); v != extsVector.end(); ++v)
+			{
+				auto findIter = std::find(mExtensions.begin(), mExtensions.end(), *v);
+
+				if(findIter == mExtensions.end())
+				{
+					mExtensions.push_back(*v);
+				}
+			}
+		}
+
+		// Set error handler
+		FreeImage_SetOutputMessage(FreeImageLoadErrorHandler);
+	}
+
+	FreeImgImporter::~FreeImgImporter() 
+	{
+		FreeImage_DeInitialise();
+	}
+
+	const std::vector<String>& FreeImgImporter::extensions() const
+	{
+		return mExtensions;
+	}
+
+	ResourcePtr FreeImgImporter::import(const String& filePath)
+	{
+		// TODO - Magic number to extension conversion? Its probably needed so I can detect file types on Linux
+		
+//		// Set error handler
+//		FreeImage_SetOutputMessage(FreeImageLoadErrorHandler);
+//
+//		// Buffer stream into memory (TODO: override IO functions instead?)
+//		MemoryDataStream memStream(input, true);
+//
+//		FIMEMORY* fiMem = 
+//			FreeImage_OpenMemory(memStream.getPtr(), static_cast<DWORD>(memStream.size()));
+//
+//		FIBITMAP* fiBitmap = FreeImage_LoadFromMemory(
+//			(FREE_IMAGE_FORMAT)mFreeImageType, fiMem);
+//		if (!fiBitmap)
+//		{
+//			CM_EXCEPT(InternalErrorException, "Error decoding image");
+//		}
+//
+//
+//		ImageData* imgData = OGRE_NEW ImageData();
+//		MemoryDataStreamPtr output;
+//
+//		imgData->depth = 1; // only 2D formats handled by this codec
+//		imgData->width = FreeImage_GetWidth(fiBitmap);
+//		imgData->height = FreeImage_GetHeight(fiBitmap);
+//		imgData->num_mipmaps = 0; // no mipmaps in non-DDS 
+//		imgData->flags = 0;
+//
+//		// Must derive format first, this may perform conversions
+//
+//		FREE_IMAGE_TYPE imageType = FreeImage_GetImageType(fiBitmap);
+//		FREE_IMAGE_COLOR_TYPE colourType = FreeImage_GetColorType(fiBitmap);
+//		unsigned bpp = FreeImage_GetBPP(fiBitmap);
+//
+//		switch(imageType)
+//		{
+//		case FIT_UNKNOWN:
+//		case FIT_COMPLEX:
+//		case FIT_UINT32:
+//		case FIT_INT32:
+//		case FIT_DOUBLE:
+//		default:
+//			CM_EXCEPT(ItemIdentityException, "Unknown or unsupported image format");
+//
+//			break;
+//		case FIT_BITMAP:
+//			// Standard image type
+//			// Perform any colour conversions for greyscale
+//			if (colourType == FIC_MINISWHITE || colourType == FIC_MINISBLACK)
+//			{
+//				FIBITMAP* newBitmap = FreeImage_ConvertToGreyscale(fiBitmap);
+//				// free old bitmap and replace
+//				FreeImage_Unload(fiBitmap);
+//				fiBitmap = newBitmap;
+//				// get new formats
+//				bpp = FreeImage_GetBPP(fiBitmap);
+//				colourType = FreeImage_GetColorType(fiBitmap);
+//			}
+//			// Perform any colour conversions for RGB
+//			else if (bpp < 8 || colourType == FIC_PALETTE || colourType == FIC_CMYK)
+//			{
+//				FIBITMAP* newBitmap = FreeImage_ConvertTo24Bits(fiBitmap);
+//				// free old bitmap and replace
+//				FreeImage_Unload(fiBitmap);
+//				fiBitmap = newBitmap;
+//				// get new formats
+//				bpp = FreeImage_GetBPP(fiBitmap);
+//				colourType = FreeImage_GetColorType(fiBitmap);
+//			}
+//
+//			// by this stage, 8-bit is greyscale, 16/24/32 bit are RGB[A]
+//			switch(bpp)
+//			{
+//			case 8:
+//				imgData->format = PF_L8;
+//				break;
+//			case 16:
+//				// Determine 555 or 565 from green mask
+//				// cannot be 16-bit greyscale since that's FIT_UINT16
+//				if(FreeImage_GetGreenMask(fiBitmap) == FI16_565_GREEN_MASK)
+//				{
+//					imgData->format = PF_R5G6B5;
+//				}
+//				else
+//				{
+//					// FreeImage doesn't support 4444 format so must be 1555
+//					imgData->format = PF_A1R5G5B5;
+//				}
+//				break;
+//			case 24:
+//				// FreeImage differs per platform
+//				//     PF_BYTE_BGR[A] for little endian (== PF_ARGB native)
+//				//     PF_BYTE_RGB[A] for big endian (== PF_RGBA native)
+//#if FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_RGB
+//				imgData->format = PF_BYTE_RGB;
+//#else
+//				imgData->format = PF_BYTE_BGR;
+//#endif
+//				break;
+//			case 32:
+//#if FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_RGB
+//				imgData->format = PF_BYTE_RGBA;
+//#else
+//				imgData->format = PF_BYTE_BGRA;
+//#endif
+//				break;
+//
+//
+//			};
+//			break;
+//		case FIT_UINT16:
+//		case FIT_INT16:
+//			// 16-bit greyscale
+//			imgData->format = PF_L16;
+//			break;
+//		case FIT_FLOAT:
+//			// Single-component floating point data
+//			imgData->format = PF_FLOAT32_R;
+//			break;
+//		case FIT_RGB16:
+//			imgData->format = PF_SHORT_RGB;
+//			break;
+//		case FIT_RGBA16:
+//			imgData->format = PF_SHORT_RGBA;
+//			break;
+//		case FIT_RGBF:
+//			imgData->format = PF_FLOAT32_RGB;
+//			break;
+//		case FIT_RGBAF:
+//			imgData->format = PF_FLOAT32_RGBA;
+//			break;
+//
+//
+//		};
+//
+//		unsigned char* srcData = FreeImage_GetBits(fiBitmap);
+//		unsigned srcPitch = FreeImage_GetPitch(fiBitmap);
+//
+//		// Final data - invert image and trim pitch at the same time
+//		size_t dstPitch = imgData->width * PixelUtil::getNumElemBytes(imgData->format);
+//		imgData->size = dstPitch * imgData->height;
+//		// Bind output buffer
+//		output.bind(OGRE_NEW MemoryDataStream(imgData->size));
+//
+//		uchar* pSrc;
+//		uchar* pDst = output->getPtr();
+//		for (size_t y = 0; y < imgData->height; ++y)
+//		{
+//			pSrc = srcData + (imgData->height - y - 1) * srcPitch;
+//			memcpy(pDst, pSrc, dstPitch);
+//			pDst += dstPitch;
+//		}
+//
+//
+//		FreeImage_Unload(fiBitmap);
+//		FreeImage_CloseMemory(fiMem);
+//
+//		DecodeResult ret;
+//		ret.first = output;
+//		ret.second = CodecDataPtr(imgData);
+//		return ret;
+
+
+		return nullptr;
+	}
+}

+ 2 - 2
CamelotRenderer/CamelotRenderer.vcxproj

@@ -63,7 +63,7 @@
     <Link>
       <SubSystem>NotSet</SubSystem>
       <GenerateDebugInformation>true</GenerateDebugInformation>
-      <AdditionalDependencies>CamelotUtility.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <AdditionalDependencies>CamelotUtility.lib;libboost_signals-vc110-mt-gd-1_49.lib;%(AdditionalDependencies)</AdditionalDependencies>
       <AdditionalLibraryDirectories>../lib/$(Configuration);../Dependencies/lib/Debug</AdditionalLibraryDirectories>
       <ImportLibrary>..\lib\$(Configuration)\$(TargetName).lib</ImportLibrary>
     </Link>
@@ -84,7 +84,7 @@
       <GenerateDebugInformation>true</GenerateDebugInformation>
       <EnableCOMDATFolding>true</EnableCOMDATFolding>
       <OptimizeReferences>true</OptimizeReferences>
-      <AdditionalDependencies>CamelotUtility.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <AdditionalDependencies>CamelotUtility.lib;libboost_signals-vc110-mt-1_49.lib;%(AdditionalDependencies)</AdditionalDependencies>
       <AdditionalLibraryDirectories>../lib/$(Configuration);../Dependencies/lib/Release</AdditionalLibraryDirectories>
       <ImportLibrary>..\lib\$(Configuration)\$(TargetName).lib</ImportLibrary>
     </Link>

+ 0 - 40
CamelotRenderer/ReadMe.txt

@@ -1,40 +0,0 @@
-========================================================================
-    CONSOLE APPLICATION : CamelotRenderer Project Overview
-========================================================================
-
-AppWizard has created this CamelotRenderer application for you.
-
-This file contains a summary of what you will find in each of the files that
-make up your CamelotRenderer application.
-
-
-CamelotRenderer.vcxproj
-    This is the main project file for VC++ projects generated using an Application Wizard.
-    It contains information about the version of Visual C++ that generated the file, and
-    information about the platforms, configurations, and project features selected with the
-    Application Wizard.
-
-CamelotRenderer.vcxproj.filters
-    This is the filters file for VC++ projects generated using an Application Wizard. 
-    It contains information about the association between the files in your project 
-    and the filters. This association is used in the IDE to show grouping of files with
-    similar extensions under a specific node (for e.g. ".cpp" files are associated with the
-    "Source Files" filter).
-
-CamelotRenderer.cpp
-    This is the main application source file.
-
-/////////////////////////////////////////////////////////////////////////////
-Other standard files:
-
-StdAfx.h, StdAfx.cpp
-    These files are used to build a precompiled header (PCH) file
-    named CamelotRenderer.pch and a precompiled types file named StdAfx.obj.
-
-/////////////////////////////////////////////////////////////////////////////
-Other notes:
-
-AppWizard uses "TODO:" comments to indicate parts of the source code you
-should add to or customize.
-
-/////////////////////////////////////////////////////////////////////////////

+ 8 - 0
CamelotRenderer/TODO.txt

@@ -45,6 +45,14 @@ TODO:
  - OpenGL too
 
 TOMORROW:
+ Add wrapper around IO methods
+ Continue working on Importer, Resources (IMPORTANT - AssetDatabase in a single file probably won't work. Multiple people won't be able to work on it. Unless we make it part of a local cache?)
+ Try to fully implement free image and maybe FBX importers
+ Log class should be able to accept different channels, other than the predetermined ones
+ Extend the Path library
+  - Less important notes:
+    - All of this must be thread safe to allow for background loading
+    - Make sure all resources have default resource that will be used before actual resource is loaded
  - Add precompiled headers to all projects
  - Make sure they include Exception & Log classes
 

+ 2 - 0
CamelotUtility/CamelotUtility.vcxproj

@@ -102,6 +102,7 @@
     <ClInclude Include="Include\CmVector3.h" />
     <ClInclude Include="Include\CmVector4.h" />
     <ClInclude Include="Include\CmDynLib.h" />
+    <ClInclude Include="Include\CmDataStream.h" />
   </ItemGroup>
   <ItemGroup>
     <ClCompile Include="Include\CmAxisAlignedBox.cpp" />
@@ -121,6 +122,7 @@
     <ClCompile Include="Source\CmVector3.cpp" />
     <ClCompile Include="Source\CmVector4.cpp" />
     <ClCompile Include="Source\CmDynLib.cpp" />
+    <ClCompile Include="Source\CmDataStream.cpp" />
   </ItemGroup>
   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
   <ImportGroup Label="ExtensionTargets">

+ 6 - 0
CamelotUtility/CamelotUtility.vcxproj.filters

@@ -120,6 +120,9 @@
     <ClInclude Include="Include\CmDebug.h">
       <Filter>Header Files</Filter>
     </ClInclude>
+    <ClInclude Include="Include\CmDataStream.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
   </ItemGroup>
   <ItemGroup>
     <ClCompile Include="Include\CmAxisAlignedBox.cpp">
@@ -173,5 +176,8 @@
     <ClCompile Include="Source\CmDebug.cpp">
       <Filter>Source Files</Filter>
     </ClCompile>
+    <ClCompile Include="Source\CmDataStream.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
   </ItemGroup>
 </Project>

+ 499 - 0
CamelotUtility/Include/CmDataStream.h

@@ -0,0 +1,499 @@
+/*
+-----------------------------------------------------------------------------
+This source file is part of OGRE
+(Object-oriented Graphics Rendering Engine)
+For the latest info, see http://www.ogre3d.org/
+
+Copyright (c) 2000-2011 Torus Knot Software Ltd
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+-----------------------------------------------------------------------------
+*/
+#pragma once
+
+#include "CmPrerequisitesUtil.h"
+#include <istream>
+
+namespace CamelotEngine 
+{
+	/** \addtogroup Core
+	*  @{
+	*/
+	/** \addtogroup Resources
+	*  @{
+	*/
+
+	/** General purpose class used for encapsulating the reading and writing of data.
+	@remarks
+		This class performs basically the same tasks as std::basic_istream, 
+		except that it does not have any formatting capabilities, and is
+		designed to be subclassed to receive data from multiple sources,
+		including libraries which have no compatibility with the STL's
+		stream interfaces. As such, this is an abstraction of a set of 
+		wrapper classes which pretend to be standard stream classes but 
+		can actually be implemented quite differently. 
+	@par
+		Generally, if a plugin or application provides an ArchiveFactory, 
+		it should also provide a DataStream subclass which will be used
+		to stream data out of that Archive implementation, unless it can 
+		use one of the common implementations included.
+	@note
+		Ogre makes no guarantees about thread safety, for performance reasons.
+		If you wish to access stream data asynchronously then you should
+		organise your own mutexes to avoid race conditions. 
+	*/
+	class CM_EXPORT DataStream
+	{
+	public:
+		enum AccessMode
+		{
+			READ = 1, 
+			WRITE = 2
+		};
+	protected:
+		/// The name (e.g. resource name) that can be used to identify the source fot his data (optional)
+		String mName;		
+        /// Size of the data in the stream (may be 0 if size cannot be determined)
+        size_t mSize;
+		/// What type of access is allowed (AccessMode)
+		UINT16 mAccess;
+
+        #define OGRE_STREAM_TEMP_SIZE 128
+	public:
+		/// Constructor for creating unnamed streams
+        DataStream(UINT16 accessMode = READ) : mSize(0), mAccess(accessMode) {}
+		/// Constructor for creating named streams
+		DataStream(const String& name, UINT16 accessMode = READ) 
+			: mName(name), mSize(0), mAccess(accessMode) {}
+		/// Returns the name of the stream, if it has one.
+		const String& getName(void) { return mName; }
+		/// Gets the access mode of the stream
+		UINT16 getAccessMode() const { return mAccess; }
+		/** Reports whether this stream is readable. */
+		virtual bool isReadable() const { return (mAccess & READ) != 0; }
+		/** Reports whether this stream is writeable. */
+		virtual bool isWriteable() const { return (mAccess & WRITE) != 0; }
+        virtual ~DataStream() {}
+		// Streaming operators
+        template<typename T> DataStream& operator>>(T& val);
+		/** Read the requisite number of bytes from the stream, 
+			stopping at the end of the file.
+		@param buf Reference to a buffer pointer
+		@param count Number of bytes to read
+		@returns The number of bytes read
+		*/
+		virtual size_t read(void* buf, size_t count) = 0;
+		/** Write the requisite number of bytes from the stream (only applicable to 
+			streams that are not read-only)
+		@param buf Pointer to a buffer containing the bytes to write
+		@param count Number of bytes to write
+		@returns The number of bytes written
+		*/
+		virtual size_t write(const void* buf, size_t count)
+		{
+                        (void)buf;
+                        (void)count;
+			// default to not supported
+			return 0;
+		}
+
+		/** Get a single line from the stream.
+		@remarks
+			The delimiter character is not included in the data
+			returned, and it is skipped over so the next read will occur
+			after it. The buffer contents will include a
+			terminating character.
+        @note
+            If you used this function, you <b>must</b> open the stream in <b>binary mode</b>,
+            otherwise, it'll produce unexpected results.
+		@param buf Reference to a buffer pointer
+		@param maxCount The maximum length of data to be read, excluding the terminating character
+		@param delim The delimiter to stop at
+		@returns The number of bytes read, excluding the terminating character
+		*/
+		virtual size_t readLine(char* buf, size_t maxCount, const String& delim = "\n");
+		
+	    /** Returns a String containing the next line of data, optionally 
+		    trimmed for whitespace. 
+	    @remarks
+		    This is a convenience method for text streams only, allowing you to 
+		    retrieve a String object containing the next line of data. The data
+		    is read up to the next newline character and the result trimmed if
+		    required.
+        @note
+            If you used this function, you <b>must</b> open the stream in <b>binary mode</b>,
+            otherwise, it'll produce unexpected results.
+	    @param 
+		    trimAfter If true, the line is trimmed for whitespace (as in 
+		    String.trim(true,true))
+	    */
+	    virtual String getLine( bool trimAfter = true );
+
+	    /** Returns a String containing the entire stream. 
+	    @remarks
+		    This is a convenience method for text streams only, allowing you to 
+		    retrieve a String object containing all the data in the stream.
+	    */
+	    virtual String getAsString(void);
+
+		/** Skip a single line from the stream.
+        @note
+            If you used this function, you <b>must</b> open the stream in <b>binary mode</b>,
+            otherwise, it'll produce unexpected results.
+		@param delim The delimiter(s) to stop at
+		@returns The number of bytes skipped
+		*/
+		virtual size_t skipLine(const String& delim = "\n");
+
+		/** Skip a defined number of bytes. This can also be a negative value, in which case
+		the file pointer rewinds a defined number of bytes. */
+		virtual void skip(long count) = 0;
+	
+		/** Repositions the read point to a specified byte.
+	    */
+	    virtual void seek( size_t pos ) = 0;
+		
+		/** Returns the current byte offset from beginning */
+	    virtual size_t tell(void) const = 0;
+
+		/** Returns true if the stream has reached the end.
+	    */
+	    virtual bool eof(void) const = 0;
+
+		/** 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(void) const { return mSize; }
+
+        /** Close the stream; this makes further operations invalid. */
+        virtual void close(void) = 0;
+		
+
+	};
+
+	/** Shared pointer to allow data streams to be passed around without
+		worrying about deallocation
+	*/
+	typedef std::shared_ptr<DataStream> DataStreamPtr;
+
+	/// List of DataStream items
+	typedef list<DataStreamPtr>::type DataStreamList;
+	/// Shared pointer to list of DataStream items
+	typedef std::shared_ptr<DataStreamList> DataStreamListPtr;
+
+	/** Common subclass of DataStream for handling data from chunks of memory.
+	*/
+	class CM_EXPORT MemoryDataStream : public DataStream
+	{
+	protected:
+        /// Pointer to the start of the data area
+	    UINT8* mData;
+        /// Pointer to the current position in the memory
+	    UINT8* mPos;
+        /// Pointer to the end of the memory
+	    UINT8* mEnd;
+        /// Do we delete the memory on close
+		bool mFreeOnClose;			
+	public:
+		
+		/** Wrap an existing memory chunk in a stream.
+		@param pMem Pointer to the existing memory
+		@param size The size of the memory chunk in bytes
+		@param freeOnClose If true, the memory associated will be destroyed
+			when the stream is destroyed. Note: it's important that if you set
+			this option to true, that you allocated the memory using OGRE_ALLOC_T
+			with a category of MEMCATEGORY_GENERAL ensure the freeing of memory 
+			matches up.
+		@param readOnly Whether to make the stream on this memory read-only once created
+		*/
+		MemoryDataStream(void* pMem, size_t size, bool freeOnClose = false, bool readOnly = false);
+		
+		/** Wrap an existing memory chunk in a named stream.
+		@param name The name to give the stream
+		@param pMem Pointer to the existing memory
+		@param size The size of the memory chunk in bytes
+		@param freeOnClose If true, the memory associated will be destroyed
+			when the stream is destroyed. Note: it's important that if you set
+			this option to true, that you allocated the memory using OGRE_ALLOC_T
+			with a category of MEMCATEGORY_GENERAL ensure the freeing of memory 
+			matches up.
+		@param readOnly Whether to make the stream on this memory read-only once created
+		*/
+		MemoryDataStream(const String& name, void* pMem, size_t size, 
+				bool freeOnClose = false, bool readOnly = false);
+
+		/** Create a stream which pre-buffers the contents of another stream.
+		@remarks
+			This constructor can be used to intentionally read in the entire
+			contents of another stream, copying them to the internal buffer
+			and thus making them available in memory as a single unit.
+		@param sourceStream Another DataStream which will provide the source
+			of data
+		@param freeOnClose If true, the memory associated will be destroyed
+			when the stream is destroyed.
+		@param readOnly Whether to make the stream on this memory read-only once created
+		*/
+		MemoryDataStream(DataStream& sourceStream, 
+				bool freeOnClose = true, bool readOnly = false);
+		
+		/** Create a stream which pre-buffers the contents of another stream.
+		@remarks
+			This constructor can be used to intentionally read in the entire
+			contents of another stream, copying them to the internal buffer
+			and thus making them available in memory as a single unit.
+		@param sourceStream Weak reference to another DataStream which will provide the source
+			of data
+		@param freeOnClose If true, the memory associated will be destroyed
+			when the stream is destroyed.
+		@param readOnly Whether to make the stream on this memory read-only once created
+		*/
+		MemoryDataStream(DataStreamPtr& sourceStream, 
+				bool freeOnClose = true, bool readOnly = false);
+
+		/** Create a named stream which pre-buffers the contents of 
+			another stream.
+		@remarks
+			This constructor can be used to intentionally read in the entire
+			contents of another stream, copying them to the internal buffer
+			and thus making them available in memory as a single unit.
+		@param name The name to give the stream
+		@param sourceStream Another DataStream which will provide the source
+			of data
+		@param freeOnClose If true, the memory associated will be destroyed
+			when the stream is destroyed.
+		@param readOnly Whether to make the stream on this memory read-only once created
+		*/
+		MemoryDataStream(const String& name, DataStream& sourceStream, 
+				bool freeOnClose = true, bool readOnly = false);
+
+        /** Create a named stream which pre-buffers the contents of 
+        another stream.
+        @remarks
+        This constructor can be used to intentionally read in the entire
+        contents of another stream, copying them to the internal buffer
+        and thus making them available in memory as a single unit.
+        @param name The name to give the stream
+        @param sourceStream Another DataStream which will provide the source
+        of data
+        @param freeOnClose If true, the memory associated will be destroyed
+        when the stream is destroyed.
+		@param readOnly Whether to make the stream on this memory read-only once created
+        */
+        MemoryDataStream(const String& name, const DataStreamPtr& sourceStream, 
+            bool freeOnClose = true, bool readOnly = false);
+
+        /** Create a stream with a brand new empty memory chunk.
+		@param size The size of the memory chunk to create in bytes
+		@param freeOnClose If true, the memory associated will be destroyed
+			when the stream is destroyed.
+		@param readOnly Whether to make the stream on this memory read-only once created
+		*/
+		MemoryDataStream(size_t size, bool freeOnClose = true, bool readOnly = false);
+		/** Create a named stream with a brand new empty memory chunk.
+		@param name The name to give the stream
+		@param size The size of the memory chunk to create in bytes
+		@param freeOnClose If true, the memory associated will be destroyed
+			when the stream is destroyed.
+		@param readOnly Whether to make the stream on this memory read-only once created
+		*/
+		MemoryDataStream(const String& name, size_t size, 
+				bool freeOnClose = true, bool readOnly = false);
+
+		~MemoryDataStream();
+
+		/** Get a pointer to the start of the memory block this stream holds. */
+		UINT8* getPtr(void) { return mData; }
+		
+		/** Get a pointer to the current position in the memory block this stream holds. */
+		UINT8* getCurrentPtr(void) { return mPos; }
+		
+		/** @copydoc DataStream::read
+		*/
+		size_t read(void* buf, size_t count);
+
+		/** @copydoc DataStream::write
+		*/
+		size_t write(const void* buf, size_t count);
+
+		/** @copydoc DataStream::readLine
+		*/
+		size_t readLine(char* buf, size_t maxCount, const String& delim = "\n");
+		
+		/** @copydoc DataStream::skipLine
+		*/
+		size_t skipLine(const String& delim = "\n");
+
+		/** @copydoc DataStream::skip
+		*/
+		void skip(long count);
+	
+		/** @copydoc DataStream::seek
+		*/
+	    void seek( size_t pos );
+		
+		/** @copydoc DataStream::tell
+		*/
+	    size_t tell(void) const;
+
+		/** @copydoc DataStream::eof
+		*/
+	    bool eof(void) const;
+
+        /** @copydoc DataStream::close
+        */
+        void close(void);
+
+		/** Sets whether or not to free the encapsulated memory on close. */
+		void setFreeOnClose(bool free) { mFreeOnClose = free; }
+	};
+
+    /** Shared pointer to allow memory data streams to be passed around without
+    worrying about deallocation
+    */
+    typedef std::shared_ptr<MemoryDataStream> MemoryDataStreamPtr;
+
+    /** Common subclass of DataStream for handling data from 
+		std::basic_istream.
+	*/
+	class CM_EXPORT FileStreamDataStream : DataStream
+	{
+	protected:
+		/// Reference to source stream (read)
+		std::istream* mpInStream;
+		/// Reference to source file stream (read-only)
+		std::ifstream* mpFStreamRO;
+		/// Reference to source file stream (read-write)
+		std::fstream* mpFStream;
+        bool mFreeOnClose;	
+
+		void determineAccess();
+	public:
+		/** Construct a read-only stream from an STL stream
+        @param s Pointer to source stream
+        @param freeOnClose Whether to delete the underlying stream on 
+            destruction of this class
+        */
+		FileStreamDataStream(std::ifstream* s, 
+            bool freeOnClose = true);
+		/** Construct a read-write stream from an STL stream
+		@param s Pointer to source stream
+		@param freeOnClose Whether to delete the underlying stream on 
+		destruction of this class
+		*/
+		FileStreamDataStream(std::fstream* s, 
+			bool freeOnClose = true);
+
+		/** Construct named read-only stream from an STL stream
+        @param name The name to give this stream
+        @param s Pointer to source stream
+        @param freeOnClose Whether to delete the underlying stream on 
+            destruction of this class
+        */
+		FileStreamDataStream(const String& name, 
+            std::ifstream* s, 
+            bool freeOnClose = true);
+
+		/** Construct named read-write stream from an STL stream
+		@param name The name to give this stream
+		@param s Pointer to source stream
+		@param freeOnClose Whether to delete the underlying stream on 
+		destruction of this class
+		*/
+		FileStreamDataStream(const String& name, 
+			std::fstream* s, 
+			bool freeOnClose = true);
+
+		/** Construct named read-only stream from an STL stream, and tell it the size
+        @remarks
+            This variant tells the class the size of the stream too, which 
+            means this class does not need to seek to the end of the stream 
+            to determine the size up-front. This can be beneficial if you have
+            metadata about the contents of the stream already.
+        @param name The name to give this stream
+        @param s Pointer to source stream
+        @param size Size of the stream contents in bytes
+        @param freeOnClose Whether to delete the underlying stream on 
+            destruction of this class. If you specify 'true' for this you
+			must ensure that the stream was allocated using OGRE_NEW_T with 
+			MEMCATEGRORY_GENERAL.
+        */
+		FileStreamDataStream(const String& name, 
+            std::ifstream* s, 
+            size_t size, 
+            bool freeOnClose = true);
+
+		/** Construct named read-write stream from an STL stream, and tell it the size
+		@remarks
+		This variant tells the class the size of the stream too, which 
+		means this class does not need to seek to the end of the stream 
+		to determine the size up-front. This can be beneficial if you have
+		metadata about the contents of the stream already.
+		@param name The name to give this stream
+		@param s Pointer to source stream
+		@param size Size of the stream contents in bytes
+		@param freeOnClose Whether to delete the underlying stream on 
+		destruction of this class. If you specify 'true' for this you
+		must ensure that the stream was allocated using OGRE_NEW_T with 
+		MEMCATEGRORY_GENERAL.
+		*/
+		FileStreamDataStream(const String& name, 
+			std::fstream* s, 
+			size_t size, 
+			bool freeOnClose = true);
+
+		~FileStreamDataStream();
+
+		/** @copydoc DataStream::read
+		*/
+		size_t read(void* buf, size_t count);
+
+		/** @copydoc DataStream::write
+		*/
+		size_t write(const void* buf, size_t count);
+
+		/** @copydoc DataStream::readLine
+		*/
+        size_t readLine(char* buf, size_t maxCount, const String& delim = "\n");
+		
+		/** @copydoc DataStream::skip
+		*/
+		void skip(long count);
+	
+		/** @copydoc DataStream::seek
+		*/
+	    void seek( size_t pos );
+
+		/** @copydoc DataStream::tell
+		*/
+		size_t tell(void) const;
+
+		/** @copydoc DataStream::eof
+		*/
+	    bool eof(void) const;
+
+        /** @copydoc DataStream::close
+        */
+        void close(void);
+		
+		
+	};
+
+	/** @} */
+}
+

+ 1 - 0
CamelotUtility/Include/CmDebug.h

@@ -14,6 +14,7 @@ namespace CamelotEngine
 		void logInfo(std::string msg);
 		void logWarning(std::string msg);
 		void logError(std::string msg);
+		void log(const String& msg, const String& channel);
 
 		Log& getLog() { return mLog; }
 

+ 5 - 16
CamelotUtility/Include/CmLog.h

@@ -5,17 +5,6 @@
 
 namespace CamelotEngine
 {
-	/**	   
-	* @brief	Different log levels that log entries may be assigned.
-	 */
-	enum LogLevel
-	{
-		LL_DEBUG = 0x1,
-		LL_INFO = 0x2,
-		LL_WARNING = 0x4,
-		LL_ERROR = 0x8
-	};
-
 	/**
 	 * @brief	A single log entry, usually used in QtLogModel as
 	 * 			a representation of a log entry in the Console window.
@@ -23,14 +12,14 @@ namespace CamelotEngine
 	class CM_EXPORT LogEntry
 	{
 	public:
-		LogEntry(const String& msg, LogLevel level);
+		LogEntry(const String& msg, const String& channel);
 
-		LogLevel getLevel(void) { return mLevel; }
+		const String& getChannel(void) { return mChannel; }
 		const String& getMessage(void) { return mMsg; }
 
 	private:
 		String mMsg;
-		LogLevel mLevel;
+		String mChannel;
 	};
 
 	/**
@@ -59,9 +48,9 @@ namespace CamelotEngine
 		 * 			disk.
 		 *
 		 * @param	message	The message describing the log entry.
-		 * @param	level  	Severity of the log entry.
+		 * @param	channel Channel in which to store the log entry.
 		 */
-		void logMsg(const String& message, LogLevel level);
+		void logMsg(const String& message, const String& channel);
 
 		/**
 		 * @brief	Removes all log entries. If autoSave is enabled the file on disk will be cleared too.

+ 692 - 0
CamelotUtility/Source/CmDataStream.cpp

@@ -0,0 +1,692 @@
+/*
+-----------------------------------------------------------------------------
+This source file is part of OGRE
+(Object-oriented Graphics Rendering Engine)
+For the latest info, see http://www.ogre3d.org/
+
+Copyright (c) 2000-2011 Torus Knot Software Ltd
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+-----------------------------------------------------------------------------
+*/
+#include "CmDataStream.h"
+#include "CmDebug.h"
+#include "CmException.h"
+
+namespace CamelotEngine 
+{
+
+    //-----------------------------------------------------------------------
+    //-----------------------------------------------------------------------
+    template <typename T> DataStream& DataStream::operator >>(T& val)
+    {
+        read(static_cast<void*>(&val), sizeof(T));
+        return *this;
+    }
+    //-----------------------------------------------------------------------
+    String DataStream::getLine(bool trimAfter)
+    {
+        char tmpBuf[OGRE_STREAM_TEMP_SIZE];
+        String retString;
+        size_t readCount;
+        // Keep looping while not hitting delimiter
+        while ((readCount = read(tmpBuf, OGRE_STREAM_TEMP_SIZE-1)) != 0)
+        {
+            // Terminate string
+            tmpBuf[readCount] = '\0';
+
+            char* p = strchr(tmpBuf, '\n');
+            if (p != 0)
+            {
+                // Reposition backwards
+                skip((long)(p + 1 - tmpBuf - readCount));
+                *p = '\0';
+            }
+
+            retString += tmpBuf;
+
+            if (p != 0)
+            {
+                // Trim off trailing CR if this was a CR/LF entry
+                if (retString.length() && retString[retString.length()-1] == '\r')
+                {
+                    retString.erase(retString.length()-1, 1);
+                }
+
+                // Found terminator, break out
+                break;
+            }
+        }
+
+        if (trimAfter)
+        {
+            StringUtil::trim(retString);
+        }
+
+        return retString;
+    }
+    //-----------------------------------------------------------------------
+    size_t DataStream::readLine(char* buf, size_t maxCount, const String& delim)
+    {
+		// Deal with both Unix & Windows LFs
+		bool trimCR = false;
+		if (delim.find_first_of('\n') != String::npos)
+		{
+			trimCR = true;
+		}
+
+        char tmpBuf[OGRE_STREAM_TEMP_SIZE];
+        size_t chunkSize = std::min(maxCount, (size_t)OGRE_STREAM_TEMP_SIZE-1);
+        size_t totalCount = 0;
+        size_t readCount; 
+        while (chunkSize && (readCount = read(tmpBuf, chunkSize)) != 0)
+        {
+            // Terminate
+            tmpBuf[readCount] = '\0';
+
+            // Find first delimiter
+            size_t pos = strcspn(tmpBuf, delim.c_str());
+
+            if (pos < readCount)
+            {
+                // Found terminator, reposition backwards
+                skip((long)(pos + 1 - readCount));
+            }
+
+            // Are we genuinely copying?
+            if (buf)
+            {
+                memcpy(buf+totalCount, tmpBuf, pos);
+            }
+            totalCount += pos;
+
+            if (pos < readCount)
+            {
+                // Trim off trailing CR if this was a CR/LF entry
+                if (trimCR && totalCount && buf[totalCount-1] == '\r')
+                {
+                    --totalCount;
+                }
+
+                // Found terminator, break out
+                break;
+            }
+
+            // Adjust chunkSize for next time
+            chunkSize = std::min(maxCount-totalCount, (size_t)OGRE_STREAM_TEMP_SIZE-1);
+        }
+
+        // Terminate
+        buf[totalCount] = '\0';
+
+        return totalCount;
+    }
+    //-----------------------------------------------------------------------
+    size_t DataStream::skipLine(const String& delim)
+    {
+        char tmpBuf[OGRE_STREAM_TEMP_SIZE];
+        size_t total = 0;
+        size_t readCount;
+        // Keep looping while not hitting delimiter
+        while ((readCount = read(tmpBuf, OGRE_STREAM_TEMP_SIZE-1)) != 0)
+        {
+            // Terminate string
+            tmpBuf[readCount] = '\0';
+
+            // Find first delimiter
+            size_t pos = strcspn(tmpBuf, delim.c_str());
+
+            if (pos < readCount)
+            {
+                // Found terminator, reposition backwards
+                skip((long)(pos + 1 - readCount));
+
+                total += pos + 1;
+
+                // break out
+                break;
+            }
+
+            total += readCount;
+        }
+
+        return total;
+    }
+    //-----------------------------------------------------------------------
+    String DataStream::getAsString(void)
+    {
+        // Read the entire buffer - ideally in one read, but if the size of
+        // the buffer is unknown, do multiple fixed size reads.
+        size_t bufSize = (mSize > 0 ? mSize : 4096);
+        char* pBuf = (char*)malloc(sizeof(char) * bufSize);
+        // Ensure read from begin of stream
+        seek(0);
+        String result;
+        while (!eof())
+        {
+            size_t nr = read(pBuf, bufSize);
+            result.append(pBuf, nr);
+        }
+        free(pBuf);
+        return result;
+    }
+    //-----------------------------------------------------------------------
+    //-----------------------------------------------------------------------
+    MemoryDataStream::MemoryDataStream(void* pMem, size_t inSize, bool freeOnClose, bool readOnly)
+		: DataStream(static_cast<UINT16>(readOnly ? READ : (READ | WRITE)))
+    {
+        mData = mPos = static_cast<UINT8*>(pMem);
+        mSize = inSize;
+        mEnd = mData + mSize;
+        mFreeOnClose = freeOnClose;
+        assert(mEnd >= mPos);
+    }
+    //-----------------------------------------------------------------------
+    MemoryDataStream::MemoryDataStream(const String& name, void* pMem, size_t inSize, 
+        bool freeOnClose, bool readOnly)
+        : DataStream(name, static_cast<UINT16>(readOnly ? READ : (READ | WRITE)))
+    {
+        mData = mPos = static_cast<UINT8*>(pMem);
+        mSize = inSize;
+        mEnd = mData + mSize;
+        mFreeOnClose = freeOnClose;
+        assert(mEnd >= mPos);
+    }
+    //-----------------------------------------------------------------------
+    MemoryDataStream::MemoryDataStream(DataStream& sourceStream, 
+        bool freeOnClose, bool readOnly)
+        : DataStream(static_cast<UINT16>(readOnly ? READ : (READ | WRITE)))
+    {
+        // Copy data from incoming stream
+        mSize = sourceStream.size();
+        if (mSize == 0 && !sourceStream.eof())
+        {
+            // size of source is unknown, read all of it into memory
+            String contents = sourceStream.getAsString();
+            mSize = contents.size();
+            mData = (UINT8*)malloc(sizeof(UINT8) * mSize);
+            mPos = mData;
+            memcpy(mData, contents.data(), mSize);
+            mEnd = mData + mSize;
+        }
+        else
+        {
+            mData = (UINT8*)malloc(sizeof(UINT8) * mSize);
+            mPos = mData;
+            mEnd = mData + sourceStream.read(mData, mSize);
+            mFreeOnClose = freeOnClose;
+        }
+        assert(mEnd >= mPos);
+    }
+    //-----------------------------------------------------------------------
+    MemoryDataStream::MemoryDataStream(DataStreamPtr& sourceStream, 
+        bool freeOnClose, bool readOnly)
+        : DataStream(static_cast<UINT16>(readOnly ? READ : (READ | WRITE)))
+    {
+        // Copy data from incoming stream
+        mSize = sourceStream->size();
+        if (mSize == 0 && !sourceStream->eof())
+        {
+            // size of source is unknown, read all of it into memory
+            String contents = sourceStream->getAsString();
+            mSize = contents.size();
+            mData = (UINT8*)malloc(sizeof(UINT8) * mSize);
+            mPos = mData;
+            memcpy(mData, contents.data(), mSize);
+            mEnd = mData + mSize;
+        }
+        else
+        {
+            mData = (UINT8*)malloc(sizeof(UINT8) * mSize);
+            mPos = mData;
+            mEnd = mData + sourceStream->read(mData, mSize);
+            mFreeOnClose = freeOnClose;
+        }
+        assert(mEnd >= mPos);
+    }
+    //-----------------------------------------------------------------------
+    MemoryDataStream::MemoryDataStream(const String& name, DataStream& sourceStream, 
+        bool freeOnClose, bool readOnly)
+        : DataStream(name, static_cast<UINT16>(readOnly ? READ : (READ | WRITE)))
+    {
+        // Copy data from incoming stream
+        mSize = sourceStream.size();
+        if (mSize == 0 && !sourceStream.eof())
+        {
+            // size of source is unknown, read all of it into memory
+            String contents = sourceStream.getAsString();
+            mSize = contents.size();
+            mData = (UINT8*)malloc(sizeof(UINT8) * mSize);
+            mPos = mData;
+            memcpy(mData, contents.data(), mSize);
+            mEnd = mData + mSize;
+        }
+        else
+        {
+            mData = (UINT8*)malloc(sizeof(UINT8) * mSize);
+            mPos = mData;
+            mEnd = mData + sourceStream.read(mData, mSize);
+            mFreeOnClose = freeOnClose;
+        }
+        assert(mEnd >= mPos);
+    }
+    //-----------------------------------------------------------------------
+    MemoryDataStream::MemoryDataStream(const String& name, const DataStreamPtr& sourceStream, 
+        bool freeOnClose, bool readOnly)
+        : DataStream(name, static_cast<UINT16>(readOnly ? READ : (READ | WRITE)))
+    {
+        // Copy data from incoming stream
+        mSize = sourceStream->size();
+        if (mSize == 0 && !sourceStream->eof())
+        {
+            // size of source is unknown, read all of it into memory
+            String contents = sourceStream->getAsString();
+            mSize = contents.size();
+            mData = (UINT8*)malloc(sizeof(UINT8) * mSize);
+            mPos = mData;
+            memcpy(mData, contents.data(), mSize);
+            mEnd = mData + mSize;
+        }
+        else
+        {
+            mData = (UINT8*)malloc(sizeof(UINT8) * mSize);
+            mPos = mData;
+            mEnd = mData + sourceStream->read(mData, mSize);
+            mFreeOnClose = freeOnClose;
+        }
+        assert(mEnd >= mPos);
+    }
+    //-----------------------------------------------------------------------
+    MemoryDataStream::MemoryDataStream(size_t inSize, bool freeOnClose, bool readOnly)
+        : DataStream(static_cast<UINT16>(readOnly ? READ : (READ | WRITE)))
+    {
+        mSize = inSize;
+        mFreeOnClose = freeOnClose;
+        mData = (UINT8*)malloc(sizeof(UINT8) * mSize);
+        mPos = mData;
+        mEnd = mData + mSize;
+        assert(mEnd >= mPos);
+    }
+    //-----------------------------------------------------------------------
+    MemoryDataStream::MemoryDataStream(const String& name, size_t inSize, 
+        bool freeOnClose, bool readOnly)
+        : DataStream(name, static_cast<UINT16>(readOnly ? READ : (READ | WRITE)))
+    {
+        mSize = inSize;
+        mFreeOnClose = freeOnClose;
+        mData = (UINT8*)malloc(sizeof(UINT8) * mSize);
+        mPos = mData;
+        mEnd = mData + mSize;
+        assert(mEnd >= mPos);
+    }
+    //-----------------------------------------------------------------------
+    MemoryDataStream::~MemoryDataStream()
+    {
+        close();
+    }
+    //-----------------------------------------------------------------------
+    size_t MemoryDataStream::read(void* buf, size_t count)
+    {
+        size_t cnt = count;
+        // Read over end of memory?
+        if (mPos + cnt > mEnd)
+            cnt = mEnd - mPos;
+        if (cnt == 0)
+            return 0;
+
+        assert (cnt<=count);
+
+        memcpy(buf, mPos, cnt);
+        mPos += cnt;
+        return cnt;
+    }
+	//---------------------------------------------------------------------
+	size_t MemoryDataStream::write(const void* buf, size_t count)
+	{
+		size_t written = 0;
+		if (isWriteable())
+		{
+			written = count;
+			// we only allow writing within the extents of allocated memory
+			// check for buffer overrun & disallow
+			if (mPos + written > mEnd)
+				written = mEnd - mPos;
+			if (written == 0)
+				return 0;
+
+			memcpy(mPos, buf, written);
+			mPos += written;
+		}
+		return written;
+	}
+    //-----------------------------------------------------------------------
+    size_t MemoryDataStream::readLine(char* buf, size_t maxCount, 
+        const String& delim)
+    {
+        // Deal with both Unix & Windows LFs
+		bool trimCR = false;
+		if (delim.find_first_of('\n') != String::npos)
+		{
+			trimCR = true;
+		}
+
+        size_t pos = 0;
+
+        // Make sure pos can never go past the end of the data 
+        while (pos < maxCount && mPos < mEnd)
+        {
+            if (delim.find(*mPos) != String::npos)
+            {
+                // Trim off trailing CR if this was a CR/LF entry
+                if (trimCR && pos && buf[pos-1] == '\r')
+                {
+                    // terminate 1 character early
+                    --pos;
+                }
+
+                // Found terminator, skip and break out
+                ++mPos;
+                break;
+            }
+
+            buf[pos++] = *mPos++;
+        }
+
+        // terminate
+        buf[pos] = '\0';
+
+        return pos;
+    }
+    //-----------------------------------------------------------------------
+    size_t MemoryDataStream::skipLine(const String& delim)
+    {
+        size_t pos = 0;
+
+        // Make sure pos can never go past the end of the data 
+        while (mPos < mEnd)
+        {
+            ++pos;
+            if (delim.find(*mPos++) != String::npos)
+            {
+                // Found terminator, break out
+                break;
+            }
+        }
+
+        return pos;
+
+    }
+    //-----------------------------------------------------------------------
+    void MemoryDataStream::skip(long count)
+    {
+        size_t newpos = (size_t)( ( mPos - mData ) + count );
+        assert( mData + newpos <= mEnd );        
+
+        mPos = mData + newpos;
+    }
+    //-----------------------------------------------------------------------
+    void MemoryDataStream::seek( size_t pos )
+    {
+        assert( mData + pos <= mEnd );
+        mPos = mData + pos;
+    }
+    //-----------------------------------------------------------------------
+    size_t MemoryDataStream::tell(void) const
+	{
+		//mData is start, mPos is current location
+		return mPos - mData;
+	}
+	//-----------------------------------------------------------------------
+    bool MemoryDataStream::eof(void) const
+    {
+        return mPos >= mEnd;
+    }
+    //-----------------------------------------------------------------------
+    void MemoryDataStream::close(void)    
+    {
+        if (mFreeOnClose && mData)
+        {
+            free(mData);
+            mData = 0;
+        }
+
+    }
+    //-----------------------------------------------------------------------
+    //-----------------------------------------------------------------------
+    FileStreamDataStream::FileStreamDataStream(std::ifstream* s, bool freeOnClose)
+        : DataStream(), mpInStream(s), mpFStreamRO(s), mpFStream(0), mFreeOnClose(freeOnClose)
+    {
+        // calculate the size
+        mpInStream->seekg(0, std::ios_base::end);
+        mSize = (size_t)mpInStream->tellg();
+        mpInStream->seekg(0, std::ios_base::beg);
+		determineAccess();
+    }
+    //-----------------------------------------------------------------------
+    FileStreamDataStream::FileStreamDataStream(const String& name, 
+        std::ifstream* s, bool freeOnClose)
+        : DataStream(name), mpInStream(s), mpFStreamRO(s), mpFStream(0), mFreeOnClose(freeOnClose)
+    {
+        // calculate the size
+        mpInStream->seekg(0, std::ios_base::end);
+        mSize = (size_t)mpInStream->tellg();
+        mpInStream->seekg(0, std::ios_base::beg);
+		determineAccess();
+    }
+    //-----------------------------------------------------------------------
+    FileStreamDataStream::FileStreamDataStream(const String& name, 
+        std::ifstream* s, size_t inSize, bool freeOnClose)
+        : DataStream(name), mpInStream(s), mpFStreamRO(s), mpFStream(0), mFreeOnClose(freeOnClose)
+    {
+        // Size is passed in
+        mSize = inSize;
+		determineAccess();
+    }
+	//---------------------------------------------------------------------
+	FileStreamDataStream::FileStreamDataStream(std::fstream* s, bool freeOnClose)
+		: DataStream(false), mpInStream(s), mpFStreamRO(0), mpFStream(s), mFreeOnClose(freeOnClose)
+	{
+		// writeable!
+		// calculate the size
+		mpInStream->seekg(0, std::ios_base::end);
+		mSize = (size_t)mpInStream->tellg();
+		mpInStream->seekg(0, std::ios_base::beg);
+		determineAccess();
+
+	}
+	//-----------------------------------------------------------------------
+	FileStreamDataStream::FileStreamDataStream(const String& name, 
+		std::fstream* s, bool freeOnClose)
+		: DataStream(name, false), mpInStream(s), mpFStreamRO(0), mpFStream(s), mFreeOnClose(freeOnClose)
+	{
+		// writeable!
+		// calculate the size
+		mpInStream->seekg(0, std::ios_base::end);
+		mSize = (size_t)mpInStream->tellg();
+		mpInStream->seekg(0, std::ios_base::beg);
+		determineAccess();
+	}
+	//-----------------------------------------------------------------------
+	FileStreamDataStream::FileStreamDataStream(const String& name, 
+		std::fstream* s, size_t inSize, bool freeOnClose)
+		: DataStream(name, false), mpInStream(s), mpFStreamRO(0), mpFStream(s), mFreeOnClose(freeOnClose)
+	{
+		// writeable!
+		// Size is passed in
+		mSize = inSize;
+		determineAccess();
+	}
+	//---------------------------------------------------------------------
+	void FileStreamDataStream::determineAccess()
+	{
+		mAccess = 0;
+		if (mpInStream)
+			mAccess |= READ;
+		if (mpFStream)
+			mAccess |= WRITE;
+	}
+    //-----------------------------------------------------------------------
+    FileStreamDataStream::~FileStreamDataStream()
+    {
+        close();
+    }
+    //-----------------------------------------------------------------------
+    size_t FileStreamDataStream::read(void* buf, size_t count)
+    {
+		mpInStream->read(static_cast<char*>(buf), static_cast<std::streamsize>(count));
+        return mpInStream->gcount();
+    }
+	//-----------------------------------------------------------------------
+	size_t FileStreamDataStream::write(const void* buf, size_t count)
+	{
+		size_t written = 0;
+		if (isWriteable() && mpFStream)
+		{
+			mpFStream->write(static_cast<const char*>(buf), static_cast<std::streamsize>(count));
+			written = count;
+		}
+		return written;
+	}
+    //-----------------------------------------------------------------------
+    size_t FileStreamDataStream::readLine(char* buf, size_t maxCount, 
+        const String& delim)
+    {
+		if (delim.empty())
+		{
+			CM_EXCEPT(InvalidParametersException, "No delimiter provided");
+		}
+		if (delim.size() > 1)
+		{
+			gDebug().log("WARNING: FileStreamDataStream::readLine - using only first delimeter", "DataStream");
+		}
+		// Deal with both Unix & Windows LFs
+		bool trimCR = false;
+		if (delim.at(0) == '\n') 
+		{
+			trimCR = true;
+		}
+		// maxCount + 1 since count excludes terminator in getline
+		mpInStream->getline(buf, static_cast<std::streamsize>(maxCount+1), delim.at(0));
+		size_t ret = mpInStream->gcount();
+		// three options
+		// 1) we had an eof before we read a whole line
+		// 2) we ran out of buffer space
+		// 3) we read a whole line - in this case the delim character is taken from the stream but not written in the buffer so the read data is of length ret-1 and thus ends at index ret-2
+		// in all cases the buffer will be null terminated for us
+
+		if (mpInStream->eof()) 
+		{
+			// no problem
+		}
+		else if (mpInStream->fail())
+		{
+			// Did we fail because of maxCount hit? No - no terminating character
+			// in included in the count in this case
+			if (ret == maxCount)
+			{
+				// clear failbit for next time 
+				mpInStream->clear();
+			}
+			else
+			{
+				CM_EXCEPT(InternalErrorException, "Streaming error occurred");
+			}
+		}
+		else 
+		{
+			// we need to adjust ret because we want to use it as a
+			// pointer to the terminating null character and it is
+			// currently the length of the data read from the stream
+			// i.e. 1 more than the length of the data in the buffer and
+			// hence 1 more than the _index_ of the NULL character
+			--ret;
+		}
+
+		// trim off CR if we found CR/LF
+		if (trimCR && buf[ret-1] == '\r')
+		{
+			--ret;
+			buf[ret] = '\0';
+		}
+		return ret;
+	}
+    //-----------------------------------------------------------------------
+    void FileStreamDataStream::skip(long count)
+    {
+#if defined(STLPORT)
+		// Workaround for STLport issues: After reached eof of file stream,
+		// it's seems the stream was putted in intermediate state, and will be
+		// fail if try to repositioning relative to current position.
+		// Note: tellg() fail in this case too.
+		if (mpInStream->eof())
+		{
+			mpInStream->clear();
+			// Use seek relative to either begin or end to bring the stream
+			// back to normal state.
+			mpInStream->seekg(0, std::ios::end);
+		}
+#endif 		
+		mpInStream->clear(); //Clear fail status in case eof was set
+		mpInStream->seekg(static_cast<std::ifstream::pos_type>(count), std::ios::cur);
+    }
+    //-----------------------------------------------------------------------
+    void FileStreamDataStream::seek( size_t pos )
+    {
+		mpInStream->clear(); //Clear fail status in case eof was set
+		mpInStream->seekg(static_cast<std::streamoff>(pos), std::ios::beg);
+	}
+	//-----------------------------------------------------------------------
+    size_t FileStreamDataStream::tell(void) const
+	{
+		mpInStream->clear(); //Clear fail status in case eof was set
+		return (size_t)mpInStream->tellg();
+	}
+	//-----------------------------------------------------------------------
+    bool FileStreamDataStream::eof(void) const
+    {
+        return mpInStream->eof();
+    }
+    //-----------------------------------------------------------------------
+    void FileStreamDataStream::close(void)
+    {
+        if (mpInStream)
+        {
+			// Unfortunately, there is no file-specific shared class hierarchy between fstream and ifstream (!!)
+			if (mpFStreamRO)
+	            mpFStreamRO->close();
+			if (mpFStream)
+			{
+				mpFStream->flush();
+				mpFStream->close();
+			}
+
+            if (mFreeOnClose)
+            {
+                // delete the stream too
+				if (mpFStreamRO)
+					delete mpFStreamRO;
+				if (mpFStream)
+					delete mpFStream;
+				mpInStream = 0;
+				mpFStreamRO = 0; 
+				mpFStream = 0; 
+            }
+        }
+    }
+}

+ 9 - 4
CamelotUtility/Source/CmDebug.cpp

@@ -5,22 +5,27 @@ namespace CamelotEngine
 {
 	void Debug::logDebug(std::string msg)
 	{
-		mLog.logMsg(msg, LL_DEBUG);
+		mLog.logMsg(msg, "GlobalDebug");
 	}
 
 	void Debug::logInfo(std::string msg)
 	{
-		mLog.logMsg(msg, LL_INFO);
+		mLog.logMsg(msg, "GlobalInfo");
 	}
 
 	void Debug::logWarning(std::string msg)
 	{
-		mLog.logMsg(msg, LL_WARNING);
+		mLog.logMsg(msg, "GlobalWarning");
 	}
 
 	void Debug::logError(std::string msg)
 	{
-		mLog.logMsg(msg, LL_ERROR);
+		mLog.logMsg(msg, "GlobalError");
+	}
+
+	void Debug::log(const String& msg, const String& channel)
+	{
+		mLog.logMsg(msg, channel);
 	}
 
 	CM_EXPORT Debug& gDebug()

+ 3 - 3
CamelotUtility/Source/CmLog.cpp

@@ -2,8 +2,8 @@
 
 namespace CamelotEngine
 {
-	LogEntry::LogEntry(const String& msg, LogLevel level)
-		:mMsg(msg), mLevel(level)
+	LogEntry::LogEntry(const String& msg, const String& level)
+		:mMsg(msg), mChannel(level)
 	{ }
 
 	Log::Log(const String& logFilePath, bool autoSave, bool suppressFileOutput)
@@ -19,7 +19,7 @@ namespace CamelotEngine
 			delete *iter;
 	}
 
-	void Log::logMsg(const String& message, LogLevel level)
+	void Log::logMsg(const String& message, const String& level)
 	{
 		LogEntry* newEntry = new LogEntry(message, level);
 		mEntries.push_back(newEntry);