Browse Source

Merge branch 'almightykiwi-linux'

BearishSun 9 years ago
parent
commit
ab3821628a
54 changed files with 1328 additions and 365 deletions
  1. 3 1
      .gitignore
  2. 0 0
      Data/Engine/.gitkeep
  3. 0 0
      Dependencies/.gitkeep
  4. 28 17
      Source/BansheeCore/CMakeSources.cmake
  5. 2 2
      Source/BansheeCore/Include/BsAnimationManager.h
  6. 1 16
      Source/BansheeCore/Include/BsGpuProgram.h
  7. 3 1
      Source/BansheeCore/Include/BsPhysics.h
  8. 2 0
      Source/BansheeCore/Include/BsSamplerState.h
  9. 3 3
      Source/BansheeCore/Source/BsTexture.cpp
  10. 24 10
      Source/BansheeEditor/CMakeSources.cmake
  11. 3 1
      Source/BansheeEngine/Source/BsGUIDropDownContent.cpp
  12. 8 3
      Source/BansheeGLRenderAPI/CMakeSources.cmake
  13. 31 19
      Source/BansheeSL/CMakeLists.txt
  14. 1 1
      Source/BansheeSL/CMakeSources.cmake
  15. 5 2
      Source/BansheeUtility/CMakeLists.txt
  16. 14 0
      Source/BansheeUtility/CMakeSources.cmake
  17. 26 0
      Source/BansheeUtility/Include/BsConsoleTestOutput.h
  18. 29 10
      Source/BansheeUtility/Include/BsCrashHandler.h
  19. 4 3
      Source/BansheeUtility/Include/BsDegree.h
  20. 25 0
      Source/BansheeUtility/Include/BsEnumClassHash.h
  21. 18 9
      Source/BansheeUtility/Include/BsFileSystem.h
  22. 41 0
      Source/BansheeUtility/Include/BsFileSystemTestSuite.h
  23. 3 3
      Source/BansheeUtility/Include/BsFwdDeclUtil.h
  24. 3 3
      Source/BansheeUtility/Include/BsMatrix3.h
  25. 3 3
      Source/BansheeUtility/Include/BsMatrix4.h
  26. 7 0
      Source/BansheeUtility/Include/BsMemoryAllocator.h
  27. 4 1
      Source/BansheeUtility/Include/BsMessageHandlerFwd.h
  28. 4 0
      Source/BansheeUtility/Include/BsPath.h
  29. 5 1
      Source/BansheeUtility/Include/BsPlatformUtility.h
  30. 3 3
      Source/BansheeUtility/Include/BsQuaternion.h
  31. 9 0
      Source/BansheeUtility/Include/BsRTTIPrerequisites.h
  32. 5 3
      Source/BansheeUtility/Include/BsRadian.h
  33. 8 3
      Source/BansheeUtility/Include/BsStdHeaders.h
  34. 3 0
      Source/BansheeUtility/Include/BsString.h
  35. 2 2
      Source/BansheeUtility/Include/BsTestSuite.h
  36. 6 2
      Source/BansheeUtility/Include/BsUtil.h
  37. 2 2
      Source/BansheeUtility/Include/BsVector2.h
  38. 1 1
      Source/BansheeUtility/Include/BsVector3.h
  39. 1 1
      Source/BansheeUtility/Include/BsVector4.h
  40. 16 0
      Source/BansheeUtility/Source/BsConsoleTestOutput.cpp
  41. 64 0
      Source/BansheeUtility/Source/BsCrashHandler.cpp
  42. 2 5
      Source/BansheeUtility/Source/BsDataStream.cpp
  43. 2 2
      Source/BansheeUtility/Source/BsDebug.cpp
  44. 111 0
      Source/BansheeUtility/Source/BsFileSystem.cpp
  45. 302 0
      Source/BansheeUtility/Source/BsFileSystemTestSuite.cpp
  46. 2 2
      Source/BansheeUtility/Source/BsRadian.cpp
  47. 1 1
      Source/BansheeUtility/Source/BsTestOutput.cpp
  48. 1 1
      Source/BansheeUtility/Source/BsTestSuite.cpp
  49. 15 0
      Source/BansheeUtility/Source/BsUtilityTest.cpp
  50. 107 0
      Source/BansheeUtility/Source/Unix/BsUnixCrashHandler.cpp
  51. 282 0
      Source/BansheeUtility/Source/Unix/BsUnixFileSystem.cpp
  52. 13 0
      Source/BansheeUtility/Source/Unix/BsUnixPlatformUtility.cpp
  53. 40 76
      Source/BansheeUtility/Source/Win32/BsWin32CrashHandler.cpp
  54. 30 152
      Source/BansheeUtility/Source/Win32/BsWin32FileSystem.cpp

+ 3 - 1
.gitignore

@@ -1,4 +1,5 @@
 # Ignored files
+*~
 *.suo
 *.sdf
 *.opensdf
@@ -10,6 +11,7 @@ Builds
 Data/Binaries
 Data/Settings.asset
 Data/Engine/*
+!Data/Engine/.gitkeep
 Data/Editor/*
 !Data/Editor/EmptyCSScriptText.txt
 !Data/Editor/EmptyShaderText.txt
@@ -18,4 +20,4 @@ Documentation/html
 Build
 *.aps
 *.opendb
-BsEngineConfig.h
+BsEngineConfig.h

+ 0 - 0
Data/Engine/.gitkeep


+ 0 - 0
Dependencies/.gitkeep


+ 28 - 17
Source/BansheeCore/CMakeSources.cmake

@@ -82,16 +82,6 @@ set(BS_BANSHEECORE_INC_INPUT
 	"Include/BsInput.h"
 )
 
-set(BS_BANSHEECORE_INC_PLATFORM
-	"Include/Win32/BsWin32FolderMonitor.h"
-	"Include/Win32/BsWin32DropTarget.h"
-	"Include/BsPlatform.h"
-	"Include/BsFolderMonitor.h"
-	"Include/Win32/BsWin32Defs.h"
-	"Include/Win32/BSWin32PlatformData.h"
-	"Include/Win32/BsWin32Platform.h"
-)
-
 set(BS_BANSHEECORE_INC_RENDERER
 	"Include/BsRendererManager.h"
 	"Include/BsRendererFactory.h"
@@ -256,13 +246,6 @@ set(BS_BANSHEECORE_SRC_COMPONENTS
 	"Source/BsCCamera.cpp"
 )
 
-set(BS_BANSHEECORE_SRC_PLATFORM
-	"Source/Win32/BsWin32FolderMonitor.cpp"
-	"Source/BsPlatform.cpp"
-	"Source/Win32/BsWin32Platform.cpp"
-	"Source/Win32/BsWin32BrowseDialogs.cpp"
-)
-
 set(BS_BANSHEECORE_SRC_IMPORTER
 	"Source/BsImporter.cpp"
 	"Source/BsImportOptions.cpp"
@@ -536,6 +519,34 @@ set(BS_BANSHEECORE_SRC_ANIMATION
 	"Source/BsMorphShapes.cpp"
 )
 
+set(BS_BANSHEECORE_INC_PLATFORM
+	"Include/BsPlatform.h"
+	"Include/BsFolderMonitor.h"
+)
+
+set(BS_BANSHEECORE_INC_PLATFORM_WIN32
+	"Include/Win32/BsWin32FolderMonitor.h"
+	"Include/Win32/BsWin32DropTarget.h"
+	"Include/Win32/BsWin32Defs.h"
+	"Include/Win32/BSWin32PlatformData.h"
+	"Include/Win32/BsWin32Platform.h"
+)
+
+set(BS_BANSHEECORE_SRC_PLATFORM
+	"Source/BsPlatform.cpp"
+)
+
+set(BS_BANSHEECORE_SRC_PLATFORM_WIN32
+	"Source/Win32/BsWin32FolderMonitor.cpp"
+	"Source/Win32/BsWin32Platform.cpp"
+	"Source/Win32/BsWin32BrowseDialogs.cpp"
+)
+
+if(WIN32)
+	list(APPEND BS_BANSHEECORE_INC_PLATFORM ${BS_BANSHEECORE_INC_PLATFORM_WIN32})
+	list(APPEND BS_BANSHEECORE_SRC_PLATFORM ${BS_BANSHEECORE_SRC_PLATFORM_WIN32})
+endif()
+
 source_group("Header Files\\Components" FILES ${BS_BANSHEECORE_INC_COMPONENTS})
 source_group("Header Files\\Physics" FILES ${BS_BANSHEECORE_INC_PHYSICS})
 source_group("Header Files\\CoreThread" FILES ${BS_BANSHEECORE_INC_CORETHREAD})

+ 2 - 2
Source/BansheeCore/Include/BsAnimationManager.h

@@ -88,7 +88,7 @@ namespace BansheeEngine
 		 *
 		 * @note	Core thread only.
 		 */
-		void AnimationManager::waitUntilComplete();
+		void waitUntilComplete();
 
 		/** 
 		 * Gets skeleton poses required by the renderer to display all the animations. The returned data can be referenced, 
@@ -150,4 +150,4 @@ namespace BansheeEngine
 	BS_CORE_EXPORT AnimationManager& gAnimation();
 
 	/** @} */
-}
+}

+ 1 - 16
Source/BansheeCore/Include/BsGpuProgram.h

@@ -224,19 +224,4 @@ namespace BansheeEngine
 	};
 
 	/** @} */
-}
-
-namespace std
-{
-/** Hash value generator for GpuProgramProfile. */
-template<>
-struct hash<BansheeEngine::GpuProgramProfile>
-{
-	size_t operator()(const BansheeEngine::GpuProgramProfile& profile) const
-	{
-		size_t hash = 0;
-		BansheeEngine::hash_combine(hash, (int)profile);
-		return hash;
-	}
-};
-}
+}

+ 3 - 1
Source/BansheeCore/Include/BsPhysics.h

@@ -2,6 +2,8 @@
 //**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
 #pragma once
 
+#include <cfloat>
+
 #include "BsCorePrerequisites.h"
 #include "BsPhysicsCommon.h"
 #include "BsModule.h"
@@ -575,4 +577,4 @@ namespace BansheeEngine
 	};
 
 	/** @} */
-}
+}

+ 2 - 0
Source/BansheeCore/Include/BsSamplerState.h

@@ -7,6 +7,8 @@
 #include "BsIReflectable.h"
 #include "BsCoreObject.h"
 
+#include <cfloat>
+
 namespace BansheeEngine 
 {
 	/** @addtogroup RenderAPI

+ 3 - 3
Source/BansheeCore/Source/BsTexture.cpp

@@ -407,7 +407,7 @@ namespace BansheeEngine
 
 		if (subresourceIdx >= (UINT32)mCPUSubresourceData.size())
 		{
-			LOGERR("Invalid subresource index: " + toString(subresourceIdx) + ". Supported range: 0 .. " + toString(mCPUSubresourceData.size()));
+			LOGERR("Invalid subresource index: " + toString(subresourceIdx) + ". Supported range: 0 .. " + toString((UINT32)mCPUSubresourceData.size()));
 			return;
 		}
 
@@ -457,7 +457,7 @@ namespace BansheeEngine
 		UINT32 subresourceIdx = mProperties.mapToSubresourceIdx(face, mipLevel);
 		if (subresourceIdx >= (UINT32)mCPUSubresourceData.size())
 		{
-			LOGERR("Invalid subresource index: " + toString(subresourceIdx) + ". Supported range: 0 .. " + toString(mCPUSubresourceData.size()));
+			LOGERR("Invalid subresource index: " + toString(subresourceIdx) + ". Supported range: 0 .. " + toString((UINT32)mCPUSubresourceData.size()));
 			return;
 		}
 
@@ -557,4 +557,4 @@ namespace BansheeEngine
 
 		return TextureManager::instance().createTexture(desc, pixelData);
 	}
-}
+}

+ 24 - 10
Source/BansheeEditor/CMakeSources.cmake

@@ -27,11 +27,6 @@ set(BS_BANSHEEEDITOR_INC_EDITORWINDOW
 	"Include/BsModalWindow.h"
 )
 
-set(BS_BANSHEEEDITOR_SRC_CODEEDITOR
-	"Source/BsCodeEditor.cpp"
-	"Source/Win32/BsVSCodeEditor.cpp"
-)
-
 set(BS_BANSHEEEDITOR_INC_SCENE
 	"Include/BsGizmoManager.h"
 	"Include/BsSceneGrid.h"
@@ -203,11 +198,6 @@ set(BS_BANSHEEEDITOR_SRC_UNDOREDO
 	"Source/BsUndoRedo.cpp"
 )
 
-set(BS_BANSHEEEDITOR_INC_CODEEDITOR
-	"Include/BsCodeEditor.h"
-	"Include/Win32/BsVSCodeEditor.h"
-)
-
 set(BS_BANSHEEEDITOR_INC_BUILD
 	"Include/BsBuildManager.h"
 	"Include/BsPlatformInfo.h"
@@ -233,6 +223,30 @@ set(BS_BANSHEEEDITOR_INC_TESTING
 	"Include/BsEditorTestSuite.h"
 )
 
+set(BS_BANSHEEEDITOR_INC_CODEEDITOR
+	"Include/BsCodeEditor.h"
+)
+
+set(BS_BANSHEEEDITOR_SRC_CODEEDITOR
+	"Source/BsCodeEditor.cpp"
+)
+
+set(BS_BANSHEEEDITOR_INC_CODEEDITOR_WIN32
+	"Include/Win32/BsVSCodeEditor.h"
+)
+
+set(BS_BANSHEEEDITOR_SRC_CODEEDITOR_WIN32
+	"Source/Win32/BsVSCodeEditor.cpp"
+)
+
+if(WIN32)
+	list(APPEND BS_BANSHEEEDITOR_INC_PLATFORM ${BS_BANSHEEEDITOR_INC_PLATFORM_WIN32})
+	list(APPEND BS_BANSHEEEDITOR_SRC_PLATFORM ${BS_BANSHEEEDITOR_SRC_PLATFORM_WIN32})
+	
+	list(APPEND BS_BANSHEEEDITOR_INC_CODEEDITOR ${BS_BANSHEEEDITOR_INC_CODEEDITOR_WIN32})
+	list(APPEND BS_BANSHEEEDITOR_SRC_CODEEDITOR ${BS_BANSHEEEDITOR_SRC_CODEEDITOR_WIN32})	
+endif()
+
 source_group("Header Files\\Settings" FILES ${BS_BANSHEEEDITOR_INC_SETTINGS})
 source_group("Source Files\\Library" FILES ${BS_BANSHEEEDITOR_SRC_LIBRARY})
 source_group("Header Files\\EditorWindow" FILES ${BS_BANSHEEEDITOR_INC_EDITORWINDOW})

+ 3 - 1
Source/BansheeEngine/Source/BsGUIDropDownContent.cpp

@@ -10,6 +10,8 @@
 #include "BsGUIMouseEvent.h"
 #include "BsGUICommandEvent.h"
 
+#include <climits>
+
 using namespace std::placeholders;
 
 namespace BansheeEngine
@@ -418,4 +420,4 @@ namespace BansheeEngine
 		static String typeName = "GUIDropDownContent";
 		return typeName;
 	}
-}
+}

+ 8 - 3
Source/BansheeGLRenderAPI/CMakeSources.cmake

@@ -95,8 +95,13 @@ source_group("Header Files\\Win32" FILES ${BS_BANSHEEGLRENDERAPI_INC_WIN32})
 set(BS_BANSHEEGLRENDERAPI_SRC
 	${BS_BANSHEEGLRENDERAPI_SRC_GLSL}
 	${BS_BANSHEEGLRENDERAPI_INC_NOFILTER}
-	${BS_BANSHEEGLRENDERAPI_SRC_WIN32}
 	${BS_BANSHEEGLRENDERAPI_SRC_NOFILTER}
 	${BS_BANSHEEGLRENDERAPI_INC_GLSL}
-	${BS_BANSHEEGLRENDERAPI_INC_WIN32}
-)
+)
+
+if(WIN32)
+	list(APPEND BS_BANSHEEGLRENDERAPI_SRC
+		${BS_BANSHEEGLRENDERAPI_INC_WIN32}
+		${BS_BANSHEEGLRENDERAPI_SRC_WIN32}
+	)
+endif()

+ 31 - 19
Source/BansheeSL/CMakeLists.txt

@@ -1,27 +1,41 @@
-execute_process(
-	COMMAND ${PROJECT_SOURCE_DIR}/../Dependencies/tools/flex/flex.exe --wincompat ${PROJECT_SOURCE_DIR}/BansheeSL/BsLexerFX.l 
-	COMMAND ${PROJECT_SOURCE_DIR}/../Dependencies/tools/bison/bison.exe ${PROJECT_SOURCE_DIR}/BansheeSL/BsParserFX.y
-	WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/BansheeSL
-	RESULT_VARIABLE rv
- )
- 
-file(RENAME "${PROJECT_SOURCE_DIR}/BansheeSL/BsLexerFX.h" "${PROJECT_SOURCE_DIR}/BansheeSL/Include/BsLexerFX.h")
-file(RENAME "${PROJECT_SOURCE_DIR}/BansheeSL/BsLexerFX.c" "${PROJECT_SOURCE_DIR}/BansheeSL/Source/BsLexerFX.c")
-file(RENAME "${PROJECT_SOURCE_DIR}/BansheeSL/BsParserFX.h" "${PROJECT_SOURCE_DIR}/BansheeSL/Include/BsParserFX.h")
-file(RENAME "${PROJECT_SOURCE_DIR}/BansheeSL/BsParserFX.c" "${PROJECT_SOURCE_DIR}/BansheeSL/Source/BsParserFX.c")
+if(WIN32)
+	execute_process(
+		COMMAND ${PROJECT_SOURCE_DIR}/../Dependencies/tools/flex/flex.exe --wincompat ${PROJECT_SOURCE_DIR}/BansheeSL/BsLexerFX.l 
+		COMMAND ${PROJECT_SOURCE_DIR}/../Dependencies/tools/bison/bison.exe ${PROJECT_SOURCE_DIR}/BansheeSL/BsParserFX.y
+		WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/BansheeSL
+		RESULT_VARIABLE rv
+	 )
+	 
+	file(RENAME "${PROJECT_SOURCE_DIR}/BansheeSL/BsLexerFX.h" "${PROJECT_SOURCE_DIR}/BansheeSL/Include/BsLexerFX.h")
+	file(RENAME "${PROJECT_SOURCE_DIR}/BansheeSL/BsLexerFX.c" "${PROJECT_SOURCE_DIR}/BansheeSL/Source/BsLexerFX.c")
+	file(RENAME "${PROJECT_SOURCE_DIR}/BansheeSL/BsParserFX.h" "${PROJECT_SOURCE_DIR}/BansheeSL/Include/BsParserFX.h")
+	file(RENAME "${PROJECT_SOURCE_DIR}/BansheeSL/BsParserFX.c" "${PROJECT_SOURCE_DIR}/BansheeSL/Source/BsParserFX.c")
+else()
+	find_package(BISON REQUIRED)
+	find_package(FLEX REQUIRED)
+	message("Building lexer/parser...")
+	execute_process(
+		COMMAND ${FLEX_EXECUTABLE} ${BS_FLEX_OPTIONS} ${PROJECT_SOURCE_DIR}/BansheeSL/BsLexerFX.l
+		COMMAND ${BISON_EXECUTABLE} ${PROJECT_SOURCE_DIR}/BansheeSL/BsParserFX.y)
+	execute_process(
+		COMMAND mv BsLexerFX.h ${PROJECT_SOURCE_DIR}/BansheeSL/Include
+		COMMAND mv BsLexerFX.c ${PROJECT_SOURCE_DIR}/BansheeSL/Source
+		COMMAND mv BsParserFX.h ${PROJECT_SOURCE_DIR}/BansheeSL/Include
+		COMMAND mv BsParserFX.c ${PROJECT_SOURCE_DIR}/BansheeSL/Source)
+endif()
 
 # Source files and their filters
 include(CMakeSources.cmake)
 
 # Includes
-set(BansheeSL_INC 
+set(BansheeSL_INC
 	"Include"
-	"../BansheeUtility/Include" 
+	"../BansheeUtility/Include"
 	"../BansheeCore/Include"
 	"../BansheeEngine/Include")
 
-include_directories(${BansheeSL_INC})	
-	
+include_directories(${BansheeSL_INC})
+
 # Target
 add_library(BansheeSL SHARED ${BS_BANSHEESL_SRC})
 
@@ -30,7 +44,7 @@ target_compile_definitions(BansheeSL PRIVATE -DBS_SL_EXPORTS)
 
 # Pre-build step
 if(WIN32)
-	add_custom_command(TARGET BansheeSL PRE_BUILD 
+	add_custom_command(TARGET BansheeSL PRE_BUILD
 		COMMAND ${PROJECT_SOURCE_DIR}/../Dependencies/tools/flex/flex.exe --wincompat ${PROJECT_SOURCE_DIR}/BansheeSL/BsLexerFX.l
 		COMMAND ${PROJECT_SOURCE_DIR}/../Dependencies/tools/bison/bison.exe ${PROJECT_SOURCE_DIR}/BansheeSL/BsParserFX.y
 		COMMAND xcopy /Y /I BsLexerFX.h \"${PROJECT_SOURCE_DIR}/BansheeSL/Include\"
@@ -39,8 +53,6 @@ if(WIN32)
 		COMMAND xcopy /Y /I BsParserFX.c \"${PROJECT_SOURCE_DIR}/BansheeSL/Source\"
 		COMMAND del BsLexerFX.h BsLexerFX.c BsParserFX.h BsParserFX.c
 		COMMENT "Building lexer/parser...")
-else()
-# TODO_OTHER_PLATFORMS_GO_HERE
 endif()
 
 # Libraries
@@ -48,4 +60,4 @@ endif()
 target_link_libraries(BansheeSL BansheeEngine BansheeUtility BansheeCore)
 
 # IDE specific
-set_property(TARGET BansheeSL PROPERTY FOLDER Plugins)
+set_property(TARGET BansheeSL PROPERTY FOLDER Plugins)

+ 1 - 1
Source/BansheeSL/CMakeSources.cmake

@@ -26,4 +26,4 @@ source_group("Source Files" FILES ${BS_BANSHEESL_SRC_NOFILTER})
 set(BS_BANSHEESL_SRC
 	${BS_BANSHEESL_INC_NOFILTER}
 	${BS_BANSHEESL_SRC_NOFILTER}
-)
+)

+ 5 - 2
Source/BansheeUtility/CMakeLists.txt

@@ -17,6 +17,9 @@ include_directories(${BansheeUtility_INC})
 # Target
 add_library(BansheeUtility SHARED ${BS_BANSHEEUTILITY_SRC})
 
+add_executable(BansheeUtilityTest Source/BsUtilityTest.cpp)
+target_link_libraries(BansheeUtilityTest BansheeUtility)
+
 # Defines
 target_compile_definitions(BansheeUtility PRIVATE -DBS_UTILITY_EXPORTS)
 
@@ -29,8 +32,8 @@ target_link_libraries(BansheeUtility
 	Rpcrt4
 	)
 else()
-	# TODO_OTHER_PLATFORMS_GO_HERE
+	target_link_libraries(BansheeUtility dl)
 endif()
 
 # IDE specific
-set_property(TARGET BansheeUtility PROPERTY FOLDER Layers)
+set_property(TARGET BansheeUtility PROPERTY FOLDER Layers)

+ 14 - 0
Source/BansheeUtility/CMakeSources.cmake

@@ -26,6 +26,12 @@ set(BS_BANSHEEUTILITY_SRC_WIN32
 	"Source/Win32/BsWin32Window.cpp"
 )
 
+set(BS_BANSHEEUTILITY_SRC_UNIX
+	"Source/Unix/BsUnixCrashHandler.cpp"
+	"Source/Unix/BsUnixFileSystem.cpp"
+	"Source/Unix/BsUnixPlatformUtility.cpp"
+)
+
 set(BS_BANSHEEUTILITY_INC_IMAGE
 	"Include/BsColor.h"
 	"Include/BsTexAtlasGenerator.h"
@@ -65,6 +71,7 @@ set(BS_BANSHEEUTILITY_INC_FILESYSTEM
 
 set(BS_BANSHEEUTILITY_SRC_FILESYSTEM
 	"Source/BsDataStream.cpp"
+	"Source/BsFileSystem.cpp"
 	"Source/BsPath.cpp"
 )
 
@@ -161,13 +168,17 @@ set(BS_BANSHEEUTILITY_SRC_MATH
 )
 
 set(BS_BANSHEEUTILITY_INC_TESTING
+	"Include/BsFileSystemTestSuite.h"
 	"Include/BsTestSuite.h"
 	"Include/BsTestOutput.h"
+	"Include/BsConsoleTestOutput.h"
 )
 
 set(BS_BANSHEEUTILITY_SRC_TESTING
+	"Source/BsFileSystemTestSuite.cpp"
 	"Source/BsTestSuite.cpp"
 	"Source/BsTestOutput.cpp"
+	"Source/BsConsoleTestOutput.cpp"
 )
 
 set(BS_BANSHEEUTILITY_SRC_SERIALIZATION
@@ -209,6 +220,7 @@ set(BS_BANSHEEUTILITY_INC_MATH
 
 set(BS_BANSHEEUTILITY_SRC_ERROR
 	"Source/BsException.cpp"
+	"Source/BsCrashHandler.cpp"
 )
 
 set(BS_BANSHEEUTILITY_INC_SERIALIZATION
@@ -305,4 +317,6 @@ set(BS_BANSHEEUTILITY_SRC
 if(WIN32)
 	list(APPEND BS_BANSHEEUTILITY_SRC ${BS_BANSHEEUTILITY_SRC_WIN32})
 	list(APPEND BS_BANSHEEUTILITY_SRC ${BS_BANSHEEUTILITY_INC_WIN32})
+else()
+	list(APPEND BS_BANSHEEUTILITY_SRC ${BS_BANSHEEUTILITY_SRC_UNIX})
 endif()

+ 26 - 0
Source/BansheeUtility/Include/BsConsoleTestOutput.h

@@ -0,0 +1,26 @@
+//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
+//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
+#pragma once
+
+#include "BsTestOutput.h"
+#include "BsPrerequisitesUtil.h"
+
+namespace BansheeEngine
+{
+	/** @addtogroup Testing
+	 *  @{
+	 */
+
+	/** Outputs unit test failures to stdout. */
+	class BS_UTILITY_EXPORT ConsoleTestOutput : public TestOutput
+	{
+	public:
+		/** @copydoc TestOutput::outputFail */
+		void outputFail(const String& desc,
+		                const String& function,
+		                const String& file,
+		                long line) final override;
+	};
+
+	/** @} */
+}

+ 29 - 10
Source/BansheeUtility/Include/BsCrashHandler.h

@@ -38,7 +38,7 @@ namespace BansheeEngine
 
 		/**
 		 * Records a crash with a custom error message.
-		 * 			
+		 *
 		 * @param[in]	type		Type of the crash that occurred. For example "InvalidParameter".
 		 * @param[in]	description	More detailed description of the issue that caused the crash.
 		 * @param[in]	function	Optional name of the function where the error occurred.
@@ -50,8 +50,8 @@ namespace BansheeEngine
 
 #if BS_PLATFORM == BS_PLATFORM_WIN32
 		/**
-		 * Records a crash resulting from a Windows-specific SEH exception. 
-		 * 			
+		 * Records a crash resulting from a Windows-specific SEH exception.
+		 *
 		 * @param[in]	exceptionData	Exception data returned from GetExceptionInformation()
 		 * @return						Code that signals the __except exception handler on how to proceed.
 		 *
@@ -61,24 +61,43 @@ namespace BansheeEngine
 #endif
 
 		/**
-		 * Returns a string containing a current stack trace. If function can be found in the symbol table its readable 
+		 * Returns a string containing a current stack trace. If function can be found in the symbol table its readable
 		 * name will be present in the stack trace, otherwise just its address.
-		 * 						
+		 *
 		 * @return	String containing the call stack with each function on its own line.
 		 */
 		static String getStackTrace();
 	private:
-		/** Returns path to the folder into which to store the crash reports. */
-		Path getCrashFolder() const;
+		/** Does what it says. Internal utility function used by reportCrash(). */
+		void logErrorAndStackTrace(const String& message, const String& stackTrace) const;
+		/** Does what it says. Internal utility function used by reportCrash(). */
+		void logErrorAndStackTrace(const String& type,
+		                           const String& description,
+		                           const String& function,
+		                           const String& file,
+		                           UINT32 line) const;
+		/** Does what it says. Internal utility function used by reportCrash(). */
+		void saveCrashLog() const;
+		/** Creates the crash report directory and returns its path. */
+		static const Path& getCrashFolder();
+		/** Returns the current time as a string timestamp.  This is used
+		 * to name the crash report directory.. */
+		static String getCrashTimestamp();
 
 		/** Returns a singleton instance of this module. */
 		static CrashHandler*& _instance() { static CrashHandler* inst = nullptr; return inst; }
 
-		static const wchar_t* CrashReportFolder;
-		static const wchar_t* CrashLogName;
+		/** The name of the crash reports directory. */
+		static const String sCrashReportFolder;
+		/** The name of the HTML crash log file. */
+		static const String sCrashLogName;
+		/** Error message to display on program failure. */
+		static const String sFatalErrorMsg;
 
+#if BS_PLATFORM == BS_PLATFORM_WIN32
 		struct Data;
 		Data* m;
+#endif
 	};
 
 	/** Easier way of accessing the CrashHandler. */
@@ -86,4 +105,4 @@ namespace BansheeEngine
 
 	/** @} */
 	/** @} */
-}
+}

+ 4 - 3
Source/BansheeUtility/Include/BsDegree.h

@@ -2,11 +2,12 @@
 //**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
 #pragma once
 
-#include "BsPrerequisitesUtil.h"
-#include "BsRadian.h"
+#include "BsPlatformDefines.h"
+#include "BsRTTIPrerequisites.h"
 
 namespace BansheeEngine
 {
+	class Radian;
 	/** @addtogroup Math
 	 *  @{
 	 */
@@ -74,4 +75,4 @@ namespace BansheeEngine
 	/** @cond SPECIALIZATIONS */
 	BS_ALLOW_MEMCPY_SERIALIZATION(Degree);
 	/** @endcond */
-}
+}

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

@@ -0,0 +1,25 @@
+//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
+//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
+#pragma once
+
+namespace BansheeEngine
+{
+	/**
+	 * Hash for enum types, to be used instead of std::hash<T> when T is an enum.
+	 *
+	 * Until C++14, std::hash<T> is not defined if T is a enum (see
+	 * http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-defects.html#2148).  But
+	 * even with C++14, as of october 2016, std::hash for enums is not widely
+	 * implemented by compilers, so here when T is a enum, we use EnumClassHash
+	 * instead of std::hash. (For instance, in BansheeEngine::hash_combine(), or
+	 * BansheeEngine::UnorderedMap.)
+	 */
+	struct EnumClassHash
+	{
+		template <typename T>
+		std::size_t operator()(T t) const
+		{
+			return static_cast<std::size_t>(t);
+		}
+	};
+}

+ 18 - 9
Source/BansheeUtility/Include/BsFileSystem.h

@@ -23,7 +23,7 @@ namespace BansheeEngine
 		static SPtr<DataStream> openFile(const Path& fullPath, bool readOnly = true);
 
 		/**
-		 * Opens a file and returns a data stream capable of reading and writing to that file. If file doesn't exist new 
+		 * Opens a file and returns a data stream capable of reading and writing to that file. If file doesn't exist new
 		 * one will be created.
 		 *
 		 * @param[in]	fullPath	Full path to a file.
@@ -41,7 +41,7 @@ namespace BansheeEngine
 		 * Deletes a file or a folder at the specified path.
 		 *
 		 * @param[in]	fullPath   	Full path to a file or a folder..
-		 * @param[in]	recursively	(optional) If true, folders will have their contents deleted as well. Otherwise an 
+		 * @param[in]	recursively	(optional) If true, folders will have their contents deleted as well. Otherwise an
 		 *							exception will be thrown for non-empty folders.
 		 */
 		static void remove(const Path& fullPath, bool recursively = true);
@@ -51,17 +51,17 @@ namespace BansheeEngine
 		 *
 		 * @param[in]	oldPath			 	Full path to the old file/folder.
 		 * @param[in]	newPath			 	Full path to the new file/folder.
-		 * @param[in]	overwriteExisting	(optional) If true, any existing file/folder at the new location will be 
+		 * @param[in]	overwriteExisting	(optional) If true, any existing file/folder at the new location will be
 		 *									overwritten, otherwise an exception will be thrown if a file/folder already exists.
 		 */
 		static void move(const Path& oldPath, const Path& newPath, bool overwriteExisting = true);
 
 		/**
-		 * Makes a copy of a file or a folder in the specified path. 
+		 * Makes a copy of a file or a folder in the specified path.
 		 *
 		 * @param[in]	oldPath			 	Full path to the old file/folder.
 		 * @param[in]	newPath			 	Full path to the new file/folder.
-		 * @param[in]	overwriteExisting	(optional) If true, any existing file/folder at the new location will be 
+		 * @param[in]	overwriteExisting	(optional) If true, any existing file/folder at the new location will be
 		 *									overwritten, otherwise an exception will be thrown if a file/folder already exists.
 		 */
 		static void copy(const Path& oldPath, const Path& newPath, bool overwriteExisting = true);
@@ -104,15 +104,15 @@ namespace BansheeEngine
 		static void getChildren(const Path& dirPath, Vector<Path>& files, Vector<Path>& directories);
 
 		/**
-		 * Iterates over all files and directories in the specified folder and calls the provided callback when a 
+		 * Iterates over all files and directories in the specified folder and calls the provided callback when a
 		 * file/folder is iterated over.
 		 *
 		 * @param[in]	dirPath			Directory over which to iterate
 		 * @param[in]	fileCallback	Callback to call whenever a file is found. If callback returns false iteration stops. Can be null.
 		 * @param[in]	dirCallback		Callback to call whenever a directory is found. If callback returns false iteration stops. Can be null.
-		 * @param[in]	recursive		If false then only the direct children of the provided folder will be iterated over, 
+		 * @param[in]	recursive		If false then only the direct children of the provided folder will be iterated over,
 		 *								and if true then child directories will be recursively visited as well.
-		 * @return						True if iteration finished iterating over all files/folders, or false if it was 
+		 * @return						True if iteration finished iterating over all files/folders, or false if it was
 		 *								interrupted by a callback returning false.
 		 */
 		static bool iterate(const Path& dirPath, std::function<bool(const Path&)> fileCallback,
@@ -130,7 +130,16 @@ namespace BansheeEngine
 
 		/** Returns the path to a directory where temporary files may be stored. */
 		static Path getTempDirectoryPath();
+
+	private:
+		/** Copy a single file. Internal function used by copy(). */
+		static void copyFile(const Path& oldPath, const Path& newPath);
+		/** Remove a single file. Internal function used by remove(). */
+		static void removeFile(const Path& path);
+		/** Move a single file. Internal function used by move(). */
+		static void moveFile(const Path& oldPath, const Path& newPath);
+
 	};
 
 	/** @} */
-}
+}

+ 41 - 0
Source/BansheeUtility/Include/BsFileSystemTestSuite.h

@@ -0,0 +1,41 @@
+//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
+//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
+#pragma once
+
+#include "BsTestSuite.h"
+
+namespace BansheeEngine
+{
+	class FileSystemTestSuite : public TestSuite
+	{
+	public:
+		FileSystemTestSuite();
+		void startUp() override;
+		void shutDown() override;
+
+	private:
+		void testExists_yes_file();
+		void testExists_yes_dir();
+		void testExists_no();
+		void testGetFileSize_zero();
+		void testGetFileSize_not_zero();
+		void testIsFile_yes();
+		void testIsFile_no();
+		void testIsDirectory_yes();
+		void testIsDirectory_no();
+		void testRemove_file();
+		void testRemove_directory();
+		void testMove();
+		void testMove_overwrite_existing();
+		void testMove_no_overwrite_existing();
+		void testCopy();
+		void testCopy_recursive();
+		void testCopy_overwrite_existing();
+		void testCopy_no_overwrite_existing();
+		void testGetChildren();
+		void testGetLastModifiedTime();
+		void testGetTempDirectoryPath();
+
+		Path mTestDirectory;
+	};
+}

+ 3 - 3
Source/BansheeUtility/Include/BsFwdDeclUtil.h

@@ -22,10 +22,10 @@ namespace BansheeEngine
 	};
 
 	/** Enum used for object construction specifying the object should be zero initializes. */
-	enum ZERO { BsZero };
+	enum BS_ZERO { BsZero };
 
 	/** Enum used for matrix/quaternion constructor specifying it should be initialized with an identity value. */
-	enum IDENTITY { BsIdentity };
+	enum BS_IDENTITY { BsIdentity };
 
 	/** @} */
 
@@ -121,4 +121,4 @@ namespace BansheeEngine
 		TID_Flags = 68,
 		TID_IReflectable = 69
 	};
-}
+}

+ 3 - 3
Source/BansheeUtility/Include/BsMatrix3.h

@@ -24,11 +24,11 @@ namespace BansheeEngine
     public:
 		Matrix3() {}
 
-		Matrix3(ZERO zero)
+		Matrix3(BS_ZERO zero)
 			:Matrix3(Matrix3::ZERO)
 		{ }
 
-		Matrix3(IDENTITY identity)
+		Matrix3(BS_IDENTITY identity)
 			:Matrix3(Matrix3::IDENTITY)
 		{ }
 
@@ -304,4 +304,4 @@ namespace BansheeEngine
 	/** @cond SPECIALIZATIONS */
 	BS_ALLOW_MEMCPY_SERIALIZATION(Matrix3);
 	/** @endcond */
-}
+}

+ 3 - 3
Source/BansheeUtility/Include/BsMatrix4.h

@@ -29,11 +29,11 @@ namespace BansheeEngine
         Matrix4()
         { }
 
-		Matrix4(ZERO zero)
+		Matrix4(BS_ZERO zero)
 			:Matrix4(Matrix3::ZERO)
 		{ }
 
-		Matrix4(IDENTITY identity)
+		Matrix4(BS_IDENTITY identity)
 			:Matrix4(Matrix3::IDENTITY)
 		{ }
 
@@ -476,4 +476,4 @@ namespace BansheeEngine
 	/** @cond SPECIALIZATIONS */
 	BS_ALLOW_MEMCPY_SERIALIZATION(Matrix4);
 	/** @endcond */
-}
+}

+ 7 - 0
Source/BansheeUtility/Include/BsMemoryAllocator.h

@@ -4,6 +4,8 @@
 #undef min
 #undef max
 
+#include "BsTypes.h"	        // for UINT64
+
 #include <atomic>
 #include <limits>
 #include <new>                  /* For 'placement new' */
@@ -422,8 +424,13 @@ namespace BansheeEngine
 		size_t max_size() const { return std::numeric_limits<size_type>::max() / sizeof(T); }
 		void construct(pointer p, const_reference t) { new (p) T(t); }
 		void destroy(pointer p) { p->~T(); }
+		/* This version of construct() (with a varying number of parameters)
+		 * seems necessary in order to use some STL data structures from
+		 * libstdc++-4.8, but compilation fails on OS X, hence the #if. */
+#if BS_PLATFORM == BS_PLATFORM_LINUX || BS_PLATFORM == BS_PLATFORM_WIN32
 		template<class U, class... Args>
 		void construct(U* p, Args&&... args) { new(p) U(std::forward<Args>(args)...); }
+#endif
 	};
 
 	/** @} */

+ 4 - 1
Source/BansheeUtility/Include/BsMessageHandlerFwd.h

@@ -2,6 +2,9 @@
 //**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
 #pragma once
 
+#include "BsTypes.h"
+#include "BsString.h"
+
 namespace BansheeEngine
 {
 	/** @addtogroup General
@@ -63,4 +66,4 @@ namespace BansheeEngine
 	class MessageHandler;
 
 	/** @} */
-}
+}

+ 4 - 0
Source/BansheeUtility/Include/BsPath.h

@@ -2,6 +2,10 @@
 //**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
 #pragma once
 
+#include "BsPlatformDefines.h"
+#include "BsString.h"
+#include "BsUtil.h"                // For hash_combine()
+
 namespace BansheeEngine
 {
 	/** @addtogroup Filesystem

+ 5 - 1
Source/BansheeUtility/Include/BsPlatformUtility.h

@@ -2,6 +2,10 @@
 //**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
 #pragma once
 
+#include "BsPlatformDefines.h"
+#include "BsString.h"
+#include "BsTypes.h"
+
 namespace BansheeEngine
 {
 	/** @addtogroup General
@@ -83,4 +87,4 @@ namespace BansheeEngine
 	};
 
 	/** @} */
-}
+}

+ 3 - 3
Source/BansheeUtility/Include/BsQuaternion.h

@@ -25,11 +25,11 @@ namespace BansheeEngine
 		Quaternion()
 		{ }
 
-		Quaternion(ZERO zero)
+		Quaternion(BS_ZERO zero)
 			:x(0.0f), y(0.0f), z(0.0f), w(0.0f)
 		{ }
 
-		Quaternion(IDENTITY identity)
+		Quaternion(BS_IDENTITY identity)
 			:x(0.0f), y(0.0f), z(0.0f), w(1.0f)
 		{ }
 
@@ -376,4 +376,4 @@ namespace BansheeEngine
 	/** @cond SPECIALIZATIONS */
 	BS_ALLOW_MEMCPY_SERIALIZATION(Quaternion);
 	/** @endcond */
-}
+}

+ 9 - 0
Source/BansheeUtility/Include/BsRTTIPrerequisites.h

@@ -2,6 +2,15 @@
 //**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
 #pragma once
 
+#include "BsMemoryAllocator.h"
+#include "BsFwdDeclUtil.h"      // For TIDs
+#include "BsTypes.h"            // For UINT32
+
+#include <limits>
+#include <type_traits>          // For std::is_pod
+#include <utility>              // For std::pair
+#include <vector>
+
 namespace BansheeEngine
 {
 	/** @addtogroup Utility 

+ 5 - 3
Source/BansheeUtility/Include/BsRadian.h

@@ -2,11 +2,13 @@
 //**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
 #pragma once
 
-#include "BsPrerequisitesUtil.h"
-#include "BsDegree.h"
+#include "BsPlatformDefines.h"
+#include "BsRTTIPrerequisites.h"
 
 namespace BansheeEngine
 {
+	class Degree;
+
 	/** @addtogroup Math
 	 *  @{
 	 */
@@ -74,4 +76,4 @@ namespace BansheeEngine
 	/** @cond SPECIALIZATIONS */
 	BS_ALLOW_MEMCPY_SERIALIZATION(Radian);
 	/** @endcond */
-}
+}

+ 8 - 3
Source/BansheeUtility/Include/BsStdHeaders.h

@@ -77,12 +77,17 @@ extern "C" {
 }
 #endif
 
+#include <BsEnumClassHash.h>
+
 namespace BansheeEngine
 {
 	/** @addtogroup Containers
 	 *  @{
 	 */
 
+	template <typename Key>
+	using HashType = typename std::conditional<std::is_enum<Key>::value, EnumClassHash, std::hash<Key>>::type;
+
 	/** Double ended queue. Allows for fast insertion and removal at both its beggining and end. */
 	template <typename T, typename A = StdAlloc<T>> 
 	using Deque = std::deque<T, A>;
@@ -116,18 +121,18 @@ namespace BansheeEngine
 	using MultiMap = std::multimap<K, V, P, A>;
 
 	/** An associative container containing an unordered set of elements. Usually faster than Set for larger data sets. */
-	template <typename T, typename H = std::hash<T>, typename C = std::equal_to<T>, typename A = StdAlloc<T>> 
+	template <typename T, typename H = HashType<T>, typename C = std::equal_to<T>, typename A = StdAlloc<T>> 
 	using UnorderedSet = std::unordered_set<T, H, C, A>;
 
 	/** An associative container containing an ordered set of key-value pairs. Usually faster than Map for larger data sets. */
-	template <typename K, typename V, typename H = std::hash<K>, typename C = std::equal_to<K>, typename A = StdAlloc<std::pair<const K, V>>> 
+	template <typename K, typename V, typename H = HashType<K>, typename C = std::equal_to<K>, typename A = StdAlloc<std::pair<const K, V>>> 
 	using UnorderedMap = std::unordered_map<K, V, H, C, A>;
 
 	/** 
 	 * An associative container containing an ordered set of key-value pairs where multiple elements can have the same key.
 	 * Usually faster than MultiMap for larger data sets.
 	 */
-	template <typename K, typename V, typename H = std::hash<K>, typename C = std::equal_to<K>, typename A = StdAlloc<std::pair<const K, V>>> 
+	template <typename K, typename V, typename H = HashType<K>, typename C = std::equal_to<K>, typename A = StdAlloc<std::pair<const K, V>>> 
 	using UnorderedMultimap = std::unordered_multimap<K, V, H, C, A>;
 
 	/** @} */

+ 3 - 0
Source/BansheeUtility/Include/BsString.h

@@ -2,7 +2,10 @@
 //**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
 #pragma once
 
+#include "BsMemoryAllocator.h"
+#include "BsRadian.h"
 
+#include <string>
 
 namespace BansheeEngine
 {

+ 2 - 2
Source/BansheeUtility/Include/BsTestSuite.h

@@ -13,7 +13,7 @@ namespace BansheeEngine
 	 */
 
 /** Tests if condition is true, and reports unit test failure if it fails. */
-#define BS_TEST_ASSERT(expr) assertment((expr), "", __FILE__, __LINE__); 
+#define BS_TEST_ASSERT(expr) assertment((expr), __FUNCTION__, __FILE__, __LINE__); 
 
 /** Tests if condition is true, and reports unit test failure with a message if it fails. */
 #define BS_TEST_ASSERT_MSG(expr, msg) assertment((expr), msg, __FILE__, __LINE__); 
@@ -94,4 +94,4 @@ namespace BansheeEngine
 #define BS_ADD_TEST(func) addTest(static_cast<Func>(&func), #func);
 
 	/** @} */
-}
+}

+ 6 - 2
Source/BansheeUtility/Include/BsUtil.h

@@ -2,6 +2,8 @@
 //**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
 #pragma once
 
+#include <BsEnumClassHash.h>
+
 namespace BansheeEngine
 {
 	/** @addtogroup General
@@ -12,7 +14,9 @@ namespace BansheeEngine
 	template <class T>
 	inline void hash_combine(std::size_t& seed, const T& v)
 	{
-		std::hash<T> hasher;
+		using HashType = typename std::conditional<std::is_enum<T>::value, EnumClassHash, std::hash<T>>::type;
+
+		HashType hasher;
 		seed ^= hasher(v) + 0x9e3779b9 + (seed<<6) + (seed>>2);
 	}
 
@@ -23,4 +27,4 @@ namespace BansheeEngine
 	String BS_UTILITY_EXPORT md5(const String& source);
 
 	/** @} */
-}
+}

+ 2 - 2
Source/BansheeUtility/Include/BsVector2.h

@@ -21,7 +21,7 @@ namespace BansheeEngine
 		Vector2()
 		{ }
 
-        Vector2(ZERO zero)
+        Vector2(BS_ZERO zero)
 			:x(0.0f), y(0.0f)
         { }
 
@@ -374,4 +374,4 @@ namespace BansheeEngine
 	/** @cond SPECIALIZATIONS */
 	BS_ALLOW_MEMCPY_SERIALIZATION(Vector2);
 	/** @endcond */
-}
+}

+ 1 - 1
Source/BansheeUtility/Include/BsVector3.h

@@ -23,7 +23,7 @@ namespace BansheeEngine
 		Vector3()
 		{ }
 
-        Vector3(ZERO zero)
+        Vector3(BS_ZERO zero)
 			:x(0.0f), y(0.0f), z(0.0f)
         { }
 

+ 1 - 1
Source/BansheeUtility/Include/BsVector4.h

@@ -21,7 +21,7 @@ namespace BansheeEngine
 		Vector4()
 		{ }
 
-        Vector4(ZERO zero)
+        Vector4(BS_ZERO zero)
 			:x(0.0f), y(0.0f), z(0.0f), w(0.0f)
         { }
 

+ 16 - 0
Source/BansheeUtility/Source/BsConsoleTestOutput.cpp

@@ -0,0 +1,16 @@
+//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
+//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
+#include "BsConsoleTestOutput.h"
+
+#include <iostream>
+
+namespace BansheeEngine
+{
+	void ConsoleTestOutput::outputFail(const String& desc,
+	                                   const String& function,
+	                                   const String& file,
+	                                   long line)
+	{
+		std::cout << file << ":" << line << ": failure: " << desc << std::endl;
+	}
+}

+ 64 - 0
Source/BansheeUtility/Source/BsCrashHandler.cpp

@@ -0,0 +1,64 @@
+//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
+//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
+#include "BsPrerequisitesUtil.h"
+#include "BsDebug.h"
+#include "BsFileSystem.h"
+#include "BsPath.h"
+
+namespace BansheeEngine
+{
+	const String CrashHandler::sCrashReportFolder = "CrashReports";
+	const String CrashHandler::sCrashLogName = "log.html";
+	const String CrashHandler::sFatalErrorMsg =
+	    "A fatal error occurred and the program has to terminate!";
+
+	CrashHandler& gCrashHandler()
+	{
+		return CrashHandler::instance();
+	}
+
+	const Path& CrashHandler::getCrashFolder()
+	{
+		static const Path path =
+			FileSystem::getWorkingDirectoryPath()
+			+ Path(sCrashReportFolder)
+			+ Path(getCrashTimestamp());
+		static bool first = true;
+		if (first) {
+			FileSystem::createDir(path);
+			first = false;
+		}
+		return path;
+	}
+
+	void CrashHandler::logErrorAndStackTrace(const String& errorMsg,
+	                                         const String& stackTrace) const
+	{
+		StringStream errorMessage;
+		errorMessage << sFatalErrorMsg << std::endl;
+		errorMessage << errorMsg;
+		errorMessage << "\n\nStack trace: \n";
+		errorMessage << stackTrace;
+
+		gDebug().logError(errorMessage.str());
+	}
+
+	void CrashHandler::logErrorAndStackTrace(const String& type,
+	                                         const String& description,
+	                                         const String& function,
+	                                         const String& file,
+	                                         UINT32 line) const
+	{
+		StringStream errorMessage;
+		errorMessage << "  - Error: " << type << std::endl;
+		errorMessage << "  - Description: " << description << std::endl;
+		errorMessage << "  - In function: " << function << std::endl;
+		errorMessage << "  - In file: " << file << ":" << line;
+		logErrorAndStackTrace(errorMessage.str(), getStackTrace());
+	}
+
+	void CrashHandler::saveCrashLog() const
+	{
+		gDebug().saveLog(getCrashFolder() + toWString(sCrashLogName));
+	}
+}

+ 2 - 5
Source/BansheeUtility/Source/BsDataStream.cpp

@@ -371,9 +371,6 @@ namespace BansheeEngine
     FileDataStream::FileDataStream(const Path& path, AccessMode accessMode, bool freeOnClose)
         : DataStream(accessMode), mPath(path), mFreeOnClose(freeOnClose)
     {
-		WString pathWString = path.toWString();
-		const wchar_t* pathString = pathWString.c_str();
-
 		// Always open in binary mode
 		// Also, always include reading
 		std::ios::openmode mode = std::ios::binary;
@@ -385,13 +382,13 @@ namespace BansheeEngine
 		{
 			mode |= std::ios::out;
 			mFStream = bs_shared_ptr_new<std::fstream>();
-			mFStream->open(pathString, mode);
+			mFStream->open(path.toPlatformString().c_str(), mode);
 			mInStream = mFStream;
 		}
 		else
 		{
 			mFStreamRO = bs_shared_ptr_new<std::ifstream>();
-			mFStreamRO->open(pathString, mode);
+			mFStreamRO->open(path.toPlatformString().c_str(), mode);
 			mInStream = mFStreamRO;
 		}
 

+ 2 - 2
Source/BansheeUtility/Source/BsDebug.cpp

@@ -22,7 +22,7 @@ void logToIDEConsole(const BansheeEngine::String& message)
 #else
 void logToIDEConsole(const BansheeEngine::String& message)
 {
-	// Do nothing
+	std::cout << message << std::endl;
 }
 #endif
 
@@ -264,4 +264,4 @@ table td
 		static Debug debug;
 		return debug;
 	}
-}
+}

+ 111 - 0
Source/BansheeUtility/Source/BsFileSystem.cpp

@@ -0,0 +1,111 @@
+//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
+//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
+
+#include "BsFileSystem.h"
+#include "BsDebug.h"
+
+namespace BansheeEngine
+{
+	void FileSystem::copy(const Path& oldPath, const Path& newPath, bool overwriteExisting)
+	{
+		Stack<std::tuple<Path, Path>> todo;
+		todo.push(std::make_tuple(oldPath, newPath));
+
+		while (!todo.empty())
+		{
+			auto current = todo.top();
+			todo.pop();
+
+			Path sourcePath = std::get<0>(current);
+			if (!FileSystem::exists(sourcePath))
+				continue;
+
+			bool srcIsFile = FileSystem::isFile(sourcePath);
+			Path destinationPath = std::get<1>(current);
+			bool destExists = FileSystem::exists(destinationPath);
+
+			if (destExists)
+			{
+				if (FileSystem::isFile(destinationPath))
+				{
+					if (overwriteExisting)
+						FileSystem::remove(destinationPath);
+					else
+					{
+						LOGWRN("Copy operation failed because another file already exists at the new path: \"" + destinationPath.toString() + "\"");
+						return;
+					}
+				}
+			}
+
+			if (srcIsFile)
+			{
+				FileSystem::copyFile(sourcePath, destinationPath);
+			}
+			else
+			{
+				if (!destExists)
+					FileSystem::createDir(destinationPath);
+
+				Vector<Path> files;
+				Vector<Path> directories;
+				getChildren(destinationPath, files, directories);
+
+				for (auto& file : files)
+				{
+					Path fileDestPath = destinationPath;
+					fileDestPath.append(file.getWTail());
+
+					todo.push(std::make_tuple(file, fileDestPath));
+				}
+
+				for (auto& dir : directories)
+				{
+					Path dirDestPath = destinationPath;
+					dirDestPath.append(dir.getWTail());
+
+					todo.push(std::make_tuple(dir, dirDestPath));
+				}
+			}
+		}
+	}
+
+	void FileSystem::remove(const Path& path, bool recursively)
+	{
+		if (!FileSystem::exists(path))
+			return;
+
+		if (recursively)
+		{
+			Vector<Path> files;
+			Vector<Path> directories;
+
+			getChildren(path, files, directories);
+
+			for (auto& file : files)
+				remove(file, false);
+
+			for (auto& dir : directories)
+				remove(dir, true);
+		}
+
+		FileSystem::removeFile(path);
+	}
+
+	void FileSystem::move(const Path& oldPath, const Path& newPath, bool overwriteExisting)
+	{
+		if (FileSystem::exists(newPath))
+		{
+			if (overwriteExisting)
+				FileSystem::remove(newPath);
+			else
+			{
+				LOGWRN("Move operation failed because another file already exists at the new path: \"" + newPath.toString() + "\"");
+				return;
+			}
+		}
+
+		FileSystem::moveFile(oldPath, newPath);
+	}
+
+}

+ 302 - 0
Source/BansheeUtility/Source/BsFileSystemTestSuite.cpp

@@ -0,0 +1,302 @@
+//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
+//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
+#include "BsFileSystemTestSuite.h"
+
+#include "BsDebug.h"
+#include "BsException.h"
+#include "BsFileSystem.h"
+
+#include <algorithm>
+#include <fstream>
+
+namespace BansheeEngine
+{
+	const String testDirectoryName = "FileSystemTestDirectory/";
+
+	void createFile(Path path, String content)
+	{
+		std::ofstream fs;
+		fs.open(path.toPlatformString().c_str());
+		fs << content;
+		fs.close();
+	}
+
+	void createEmptyFile(Path path)
+	{
+		createFile(path, "");
+	}
+
+	String readFile(Path path)
+	{
+		String content;
+		std::ifstream fs;
+		fs.open(path.toPlatformString().c_str());
+		fs >> content;
+		fs.close();
+		return content;
+	}
+
+	void FileSystemTestSuite::startUp()
+	{
+		mTestDirectory = FileSystem::getWorkingDirectoryPath() + testDirectoryName;
+		if (FileSystem::exists(mTestDirectory))
+		{
+			BS_EXCEPT(InternalErrorException,
+			          String("Directory '") + testDirectoryName
+			          + "' should not already exist; you should remove it manually.");
+		}
+		else
+		{
+			FileSystem::createDir(mTestDirectory);
+			BS_TEST_ASSERT_MSG(FileSystem::exists(mTestDirectory), "FileSystemTestSuite::startUp(): test directory creation failed");
+		}
+	}
+
+	void FileSystemTestSuite::shutDown()
+	{
+		FileSystem::remove(mTestDirectory, true);
+		if (FileSystem::exists(mTestDirectory))
+		{
+			LOGERR("FileSystemTestSuite failed to delete '" + mTestDirectory.toString()
+				   + "', you should remove it manually.");
+		}
+	}
+
+	FileSystemTestSuite::FileSystemTestSuite()
+	{
+		BS_ADD_TEST(FileSystemTestSuite::testExists_yes_file);
+		BS_ADD_TEST(FileSystemTestSuite::testExists_yes_dir);
+		BS_ADD_TEST(FileSystemTestSuite::testExists_no);
+		BS_ADD_TEST(FileSystemTestSuite::testGetFileSize_zero);
+		BS_ADD_TEST(FileSystemTestSuite::testGetFileSize_not_zero);
+		BS_ADD_TEST(FileSystemTestSuite::testIsFile_yes);
+		BS_ADD_TEST(FileSystemTestSuite::testIsFile_no);
+		BS_ADD_TEST(FileSystemTestSuite::testIsDirectory_yes);
+		BS_ADD_TEST(FileSystemTestSuite::testIsDirectory_no);
+		BS_ADD_TEST(FileSystemTestSuite::testRemove_file);
+		BS_ADD_TEST(FileSystemTestSuite::testRemove_directory);
+		BS_ADD_TEST(FileSystemTestSuite::testMove);
+		BS_ADD_TEST(FileSystemTestSuite::testMove_overwrite_existing);
+		BS_ADD_TEST(FileSystemTestSuite::testMove_no_overwrite_existing);
+		BS_ADD_TEST(FileSystemTestSuite::testCopy);
+		BS_ADD_TEST(FileSystemTestSuite::testCopy_overwrite_existing);
+		BS_ADD_TEST(FileSystemTestSuite::testCopy_no_overwrite_existing);
+		BS_ADD_TEST(FileSystemTestSuite::testGetChildren);
+		BS_ADD_TEST(FileSystemTestSuite::testGetLastModifiedTime);
+		BS_ADD_TEST(FileSystemTestSuite::testGetTempDirectoryPath);
+	}
+
+	void FileSystemTestSuite::testExists_yes_file()
+	{
+		Path path = mTestDirectory + "plop";
+		createEmptyFile(path);
+		BS_TEST_ASSERT(FileSystem::exists(path));
+		FileSystem::remove(path);
+	}
+
+	void FileSystemTestSuite::testExists_yes_dir()
+	{
+		Path path = mTestDirectory + "plop/";
+		FileSystem::createDir(path);
+		BS_TEST_ASSERT(FileSystem::exists(path));
+		FileSystem::remove(path);
+	}
+
+	void FileSystemTestSuite::testExists_no()
+	{
+		BS_TEST_ASSERT(!FileSystem::exists(Path("this-file-does-not-exist")));
+	}
+
+	void FileSystemTestSuite::testGetFileSize_zero()
+	{
+		Path path = mTestDirectory + "file-size-test-1";
+		createEmptyFile(path);
+		BS_TEST_ASSERT(FileSystem::getFileSize(path) == 0);
+		FileSystem::remove(path);
+	}
+
+	void FileSystemTestSuite::testGetFileSize_not_zero()
+	{
+		Path path = mTestDirectory + "file-size-test-2";
+		createFile(path, "0123456789");
+		BS_TEST_ASSERT(FileSystem::getFileSize(path) == 10);
+		FileSystem::remove(path);
+	}
+
+	void FileSystemTestSuite::testIsFile_yes()
+	{
+		Path path = mTestDirectory + "some-file-1";
+		createEmptyFile(path);
+		BS_TEST_ASSERT(FileSystem::isFile(path));
+	}
+
+	void FileSystemTestSuite::testIsFile_no()
+	{
+		Path path = mTestDirectory + "some-directory-1/";
+		FileSystem::createDir(path);
+		BS_TEST_ASSERT(!FileSystem::isFile(path));
+	}
+
+	void FileSystemTestSuite::testIsDirectory_yes()
+	{
+		Path path = mTestDirectory + "some-directory-2/";
+		FileSystem::createDir(path);
+		BS_TEST_ASSERT(FileSystem::isDirectory(path));
+	}
+
+	void FileSystemTestSuite::testIsDirectory_no()
+	{
+		Path path = mTestDirectory + "some-file-2";
+		createEmptyFile(path);
+		BS_TEST_ASSERT(!FileSystem::isDirectory(path));
+	}
+
+	void FileSystemTestSuite::testRemove_file()
+	{
+		Path path = mTestDirectory + "file-to-remove";
+		createEmptyFile(path);
+		BS_TEST_ASSERT(FileSystem::exists(path));
+		FileSystem::remove(path);
+		BS_TEST_ASSERT(!FileSystem::exists(path));
+	}
+
+	void FileSystemTestSuite::testRemove_directory()
+	{
+		Path path = mTestDirectory + "directory-to-remove/";
+		FileSystem::createDir(path);
+		BS_TEST_ASSERT(FileSystem::exists(path));
+		FileSystem::remove(path, true);
+		BS_TEST_ASSERT(!FileSystem::exists(path));
+	}
+
+	void FileSystemTestSuite::testMove()
+	{
+		Path source = mTestDirectory + "move-source-1";
+		Path destination = mTestDirectory + "move-destination-1";
+		createFile(source, "move-data-source-1");
+		BS_TEST_ASSERT(FileSystem::exists(source));
+		BS_TEST_ASSERT(!FileSystem::exists(destination));
+		FileSystem::move(source, destination);
+		BS_TEST_ASSERT(!FileSystem::exists(source));
+		BS_TEST_ASSERT(FileSystem::exists(destination));
+		BS_TEST_ASSERT(readFile(destination) == "move-data-source-1");
+	}
+
+	void FileSystemTestSuite::testMove_overwrite_existing()
+	{
+		Path source = mTestDirectory + "move-source-2";
+		Path destination = mTestDirectory + "move-destination-2";
+		createFile(source, "move-data-source-2");
+		createFile(destination, "move-data-destination-2");
+		BS_TEST_ASSERT(FileSystem::exists(source));
+		BS_TEST_ASSERT(FileSystem::exists(destination));
+		FileSystem::move(source, destination, true);
+		BS_TEST_ASSERT(!FileSystem::exists(source));
+		BS_TEST_ASSERT(FileSystem::exists(destination));
+		BS_TEST_ASSERT(readFile(destination) == "move-data-source-2");
+	}
+
+	void FileSystemTestSuite::testMove_no_overwrite_existing()
+	{
+		Path source = mTestDirectory + "move-source-3";
+		Path destination = mTestDirectory + "move-destination-3";
+		createFile(source, "move-data-source-3");
+		createFile(destination, "move-data-destination-3");
+		BS_TEST_ASSERT(FileSystem::exists(source));
+		BS_TEST_ASSERT(FileSystem::exists(destination));
+		FileSystem::move(source, destination, false);
+		BS_TEST_ASSERT(FileSystem::exists(source));
+		BS_TEST_ASSERT(FileSystem::exists(destination));
+		BS_TEST_ASSERT(readFile(destination) == "move-data-destination-3");
+	}
+
+	void FileSystemTestSuite::testCopy()
+	{
+		Path source = mTestDirectory + "copy-source-1";
+		Path destination = mTestDirectory + "copy-destination-1";
+		createFile(source, "copy-data-source-1");
+		BS_TEST_ASSERT(FileSystem::exists(source));
+		BS_TEST_ASSERT(!FileSystem::exists(destination));
+		FileSystem::copy(source, destination);
+		BS_TEST_ASSERT(FileSystem::exists(source));
+		BS_TEST_ASSERT(FileSystem::exists(destination));
+		BS_TEST_ASSERT(readFile(source) == "copy-data-source-1");
+		BS_TEST_ASSERT(readFile(destination) == "copy-data-source-1");
+	}
+
+	void FileSystemTestSuite::testCopy_overwrite_existing()
+	{
+		Path source = mTestDirectory + "copy-source-2";
+		Path destination = mTestDirectory + "copy-destination-2";
+		createFile(source, "copy-data-source-2");
+		createFile(destination, "copy-data-destination-2");
+		BS_TEST_ASSERT(FileSystem::exists(source));
+		BS_TEST_ASSERT(FileSystem::exists(destination));
+		FileSystem::copy(source, destination, true);
+		BS_TEST_ASSERT(FileSystem::exists(source));
+		BS_TEST_ASSERT(FileSystem::exists(destination));
+		BS_TEST_ASSERT(readFile(source) == "copy-data-source-2");
+		BS_TEST_ASSERT(readFile(destination) == "copy-data-source-2");
+	}
+
+	void FileSystemTestSuite::testCopy_no_overwrite_existing()
+	{
+		Path source = mTestDirectory + "copy-source-3";
+		Path destination = mTestDirectory + "copy-destination-3";
+		createFile(source, "copy-data-source-3");
+		createFile(destination, "copy-data-destination-3");
+		BS_TEST_ASSERT(FileSystem::exists(source));
+		BS_TEST_ASSERT(FileSystem::exists(destination));
+		FileSystem::copy(source, destination, false);
+		BS_TEST_ASSERT(FileSystem::exists(source));
+		BS_TEST_ASSERT(FileSystem::exists(destination));
+		BS_TEST_ASSERT(readFile(source) == "copy-data-source-3");
+		BS_TEST_ASSERT(readFile(destination) == "copy-data-destination-3");
+	}
+
+
+#define CONTAINS(v, e) (std::find(v.begin(), v.end(), e) != v.end())
+
+	void FileSystemTestSuite::testGetChildren()
+	{
+		Path path = mTestDirectory + "get-children-test/";
+		FileSystem::createDir(path);
+		FileSystem::createDir(path + "foo/");
+		FileSystem::createDir(path + "bar/");
+		FileSystem::createDir(path + "baz/");
+		createEmptyFile(path + "ga");
+		createEmptyFile(path + "bu");
+		createEmptyFile(path + "zo");
+		createEmptyFile(path + "meu");
+		Vector<Path> files, directories;
+		FileSystem::getChildren(path, files, directories);
+		BS_TEST_ASSERT(files.size() == 4);
+		BS_TEST_ASSERT(CONTAINS(files, path + "ga"));
+		BS_TEST_ASSERT(CONTAINS(files, path + "bu"));
+		BS_TEST_ASSERT(CONTAINS(files, path + "zo"));
+		BS_TEST_ASSERT(CONTAINS(files, path + "meu"));
+		BS_TEST_ASSERT(directories.size() == 3);
+		BS_TEST_ASSERT(CONTAINS(directories, path + "foo"));
+		BS_TEST_ASSERT(CONTAINS(directories, path + "bar"));
+		BS_TEST_ASSERT(CONTAINS(directories, path + "baz"));
+	}
+
+	void FileSystemTestSuite::testGetLastModifiedTime()
+	{
+		std::time_t beforeTime;
+		time(&beforeTime);
+
+		Path path = mTestDirectory + "blah1234";
+		createFile(path, "blah");
+		std::time_t mtime = FileSystem::getLastModifiedTime(path);
+		BS_TEST_ASSERT(mtime >= beforeTime);
+		BS_TEST_ASSERT(mtime <= beforeTime + 10);
+	}
+
+	void FileSystemTestSuite::testGetTempDirectoryPath()
+	{
+		Path path = FileSystem::getTempDirectoryPath();
+		/* No judging. */
+		BS_TEST_ASSERT(!path.toString().empty());
+	}
+}

+ 2 - 2
Source/BansheeUtility/Source/BsRadian.cpp

@@ -46,8 +46,8 @@ namespace BansheeEngine
 		return *this;
 	}
 
-	inline float Radian::valueDegrees() const
+	float Radian::valueDegrees() const
 	{
 		return mRad * Math::RAD2DEG;
 	}
-}
+}

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

@@ -9,4 +9,4 @@ namespace BansheeEngine
 	{
 		BS_EXCEPT(UnitTestException, desc);
 	}
-}
+}

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

@@ -49,4 +49,4 @@ namespace BansheeEngine
 		if (!success)
 			mOutput->outputFail(desc, mActiveTestName, file, line);
 	}
-}
+}

+ 15 - 0
Source/BansheeUtility/Source/BsUtilityTest.cpp

@@ -0,0 +1,15 @@
+//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
+//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
+#include "BsFileSystemTestSuite.h"
+#include "BsConsoleTestOutput.h"
+
+using namespace BansheeEngine;
+
+int main()
+{
+	SPtr<TestSuite> tests = FileSystemTestSuite::create<FileSystemTestSuite>();
+	ConsoleTestOutput testOutput;
+	tests->run(testOutput);
+
+	return 0;
+}

+ 107 - 0
Source/BansheeUtility/Source/Unix/BsUnixCrashHandler.cpp

@@ -0,0 +1,107 @@
+//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
+//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
+#include "BsPrerequisitesUtil.h"
+
+#include "BsDebug.h"
+#include "BsFileSystem.h"
+
+#include <cxxabi.h>
+#include <execinfo.h>
+
+#include <ctime>
+#include <sstream>
+
+namespace BansheeEngine
+{
+	CrashHandler::CrashHandler() {}
+	CrashHandler::~CrashHandler() {}
+
+	String CrashHandler::getCrashTimestamp()
+	{
+		std::time_t t = time(0);
+		struct tm *now = localtime(&t);
+
+		String timeStamp = "{0}{1}{2}_{3}{4}";
+		String strYear = toString(now->tm_year, 4, '0');
+		String strMonth = toString(now->tm_mon, 2, '0');
+		String strDay = toString(now->tm_mday, 2, '0');
+		String strHour = toString(now->tm_hour, 2, '0');
+		String strMinute = toString(now->tm_min, 2, '0');
+		return StringUtil::format(timeStamp, strYear, strMonth, strDay, strHour, strMinute);
+	}
+
+	String CrashHandler::getStackTrace()
+	{
+		StringStream stackTrace;
+		void *trace[BS_MAX_STACKTRACE_DEPTH];
+
+		int trace_size = backtrace(trace, BS_MAX_STACKTRACE_DEPTH);
+		char **messages = backtrace_symbols(trace, trace_size);
+
+		// Most lines returned by backtrace_symbols() look like this:
+		//
+		//     <path/to/binary>(mangled_symbol+offset) [address]
+		//
+		// For those lines, we demangle the symbol with abi::__cxa_demangle(),
+		// others are good as is.
+
+		for (int i = 0; i < trace_size && messages != NULL; ++i)
+		{
+			// Try to find the characters surrounding the mangled name: '(' and '+'
+			char *mangled_name = NULL, *offset_begin = NULL, *offset_end = NULL;
+			for (char *p = messages[i]; *p; ++p)
+			{
+				if (*p == '(')
+					mangled_name = p;
+				else if (*p == '+')
+					offset_begin = p;
+				else if (*p == ')') {
+					offset_end = p;
+					break;
+				}
+			}
+
+			bool lineContainsMangledSymbol = mangled_name != NULL
+			                                 && offset_begin != NULL
+			                                 && offset_end != NULL
+			                                 && mangled_name < offset_begin;
+
+			stackTrace << toString(i) << ") ";
+
+			if (lineContainsMangledSymbol)
+			{
+				*mangled_name++ = '\0';
+				*offset_begin++ = '\0';
+				*offset_end++ = '\0';
+
+				int status;
+				char *real_name = abi::__cxa_demangle(mangled_name, 0, 0, &status);
+				char *output_name = status == 0 /* Demangling successful */? real_name : mangled_name;
+				stackTrace << String(messages[i])
+				           << ": " << output_name
+				           << "+" << offset_begin << offset_end;
+
+				free(real_name);
+			}
+			else
+				stackTrace << String(messages[i]);
+
+			if (i < trace_size - 1)
+				stackTrace << "\n";
+		}
+
+		free(messages);
+
+		return stackTrace.str();
+	}
+
+	void CrashHandler::reportCrash(const String& type,
+	                               const String& description,
+	                               const String& function,
+	                               const String& file,
+	                               UINT32 line) const
+	{
+		logErrorAndStackTrace(type, description, function, file, line);
+		saveCrashLog();
+	}
+}

+ 282 - 0
Source/BansheeUtility/Source/Unix/BsUnixFileSystem.cpp

@@ -0,0 +1,282 @@
+//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
+//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
+#include "BsFileSystem.h"
+
+#include "BsException.h"
+#include "BsDataStream.h"
+#include "BsDebug.h"
+
+#include <dirent.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <climits>
+#include <cstring>
+#include <cstdio>
+#include <cstdlib>
+#include <fstream>
+
+#define HANDLE_PATH_ERROR(path__, errno__) \
+	LOGERR(String(__FUNCTION__) + ": " + (path__) + ": " + (strerror(errno__)));
+
+namespace BansheeEngine
+{
+	bool unix_pathExists(const String& path)
+	{
+		struct stat st_buf;
+		if (stat(path.c_str(), &st_buf) == 0)
+			return true;
+		else
+			if (errno == ENOENT)    // No such file or directory
+				return false;
+			else
+			{
+				HANDLE_PATH_ERROR(path, errno);
+				return false;
+			}
+	}
+
+	bool unix_stat(const String& path, struct stat *st_buf)
+	{
+		if (stat(path.c_str(), st_buf) != 0)
+		{
+			HANDLE_PATH_ERROR(path, errno);
+			return false;
+		}
+		return true;
+	}
+
+	bool unix_isFile(const String& path)
+	{
+		struct stat st_buf;
+		if (unix_stat(path, &st_buf))
+		{
+			return S_ISREG(st_buf.st_mode);
+		}
+	}
+
+	bool unix_isDirectory(const String& path)
+	{
+		struct stat st_buf;
+		if (unix_stat(path, &st_buf))
+		{
+			return S_ISDIR(st_buf.st_mode);
+		}
+		return false;
+	}
+
+	bool unix_createDirectory(const String& path)
+	{
+		if (unix_pathExists(path) && unix_isDirectory(path))
+			return false;
+
+		if (mkdir(path.c_str(), 0755))
+		{
+			HANDLE_PATH_ERROR(path, errno);
+			return false;
+		}
+
+		return true;
+	}
+
+	void FileSystem::removeFile(const Path& path)
+	{
+		String pathStr = path.toString();
+		if (unix_isDirectory(pathStr))
+		{
+			if (rmdir(pathStr.c_str()))
+				HANDLE_PATH_ERROR(pathStr, errno);
+		}
+		else
+		{
+			if (unlink(pathStr.c_str()))
+				HANDLE_PATH_ERROR(pathStr, errno);
+		}
+	}
+
+	void FileSystem::copyFile(const Path& source, const Path& destination)
+	{
+		std::ifstream sourceStream(source.toString().c_str(), std::ios::binary);
+		std::ofstream destinationStream(destination.toString().c_str(), std::ios::binary);
+
+		destinationStream << sourceStream.rdbuf();
+		sourceStream.close();
+		destinationStream.close();
+	}
+
+	void FileSystem::moveFile(const Path& oldPath, const Path& newPath)
+	{
+		String oldPathStr = oldPath.toString();
+		String newPathStr = newPath.toString();
+		if (std::rename(oldPathStr.c_str(), newPathStr.c_str()) == -1)
+		{
+			LOGERR(String(__FUNCTION__) + ": renaming " + oldPathStr + " to " + newPathStr +
+			       ": " + strerror(errno));
+		}
+	}
+
+	SPtr<DataStream> FileSystem::openFile(const Path& path, bool readOnly)
+	{
+		String pathString = path.toString();
+
+		DataStream::AccessMode accessMode = DataStream::READ;
+		if (!readOnly)
+			accessMode = (DataStream::AccessMode)(accessMode | (UINT32)DataStream::WRITE);
+
+		return bs_shared_ptr_new<FileDataStream>(path, accessMode, true);
+	}
+
+	SPtr<DataStream> FileSystem::createAndOpenFile(const Path& path)
+	{
+		return bs_shared_ptr_new<FileDataStream>(path, DataStream::AccessMode::WRITE, true);
+	}
+
+	UINT64 FileSystem::getFileSize(const Path& path)
+	{
+		struct stat st_buf;
+
+		if (stat(path.toString().c_str(), &st_buf) == 0)
+		{
+			return st_buf.st_size;
+		}
+		else
+		{
+			HANDLE_PATH_ERROR(path.toString(), errno);
+			return -1;
+		}
+	}
+
+	bool FileSystem::exists(const Path& path)
+	{
+		return unix_pathExists(path.toString());
+	}
+
+	bool FileSystem::isFile(const Path& path)
+	{
+		String pathStr = path.toString();
+		return unix_pathExists(pathStr) && unix_isFile(pathStr);
+	}
+
+	bool FileSystem::isDirectory(const Path& path)
+	{
+		String pathStr = path.toString();
+		return unix_pathExists(pathStr) && unix_isDirectory(pathStr);
+	}
+
+	void FileSystem::createDir(const Path& path)
+	{
+		Path parentPath = path;
+		while (!exists(parentPath) && parentPath.getNumDirectories() > 0)
+		{
+			parentPath = parentPath.getParent();
+		}
+
+		for (UINT32 i = parentPath.getNumDirectories(); i < path.getNumDirectories(); i++)
+		{
+			parentPath.append(path[i]);
+			unix_createDirectory(parentPath.toString());
+		}
+	}
+
+	void FileSystem::getChildren(const Path& dirPath, Vector<Path>& files, Vector<Path>& directories)
+	{
+		const String pathStr = dirPath.toString();
+
+		if (unix_isFile(pathStr))
+			return;
+
+
+		DIR *dp = opendir(pathStr.c_str());
+		if (dp == NULL)
+		{
+			HANDLE_PATH_ERROR(pathStr, errno);
+			return;
+		}
+
+		struct dirent *ep;
+		while ( (ep = readdir(dp)) )
+		{
+			const String filename(ep->d_name);
+			if (filename != "." && filename != "..")
+			{
+				if (unix_isDirectory(pathStr + "/" + filename))
+				{
+					directories.push_back(dirPath + (filename + "/"));
+				}
+				else
+				{
+					files.push_back(dirPath + filename);
+				}
+			}
+		}
+		closedir(dp);
+	}
+
+	std::time_t FileSystem::getLastModifiedTime(const Path& path)
+	{
+		struct stat st_buf;
+		stat(path.toString().c_str(), &st_buf);
+		std::time_t time = st_buf.st_mtime;
+		return time;
+	}
+
+	Path FileSystem::getWorkingDirectoryPath()
+	{
+		char *buffer = bs_newN<char>(PATH_MAX);
+		String wd;
+
+		if (getcwd(buffer, PATH_MAX) != NULL)
+			wd = buffer;
+		bs_free(buffer);
+
+		const int error = errno;
+		if (error)
+			LOGERR(String("Error when calling getcwd(): ") + strerror(error));
+
+		return Path(wd);
+	}
+
+	bool FileSystem::iterate(const Path& dirPath, std::function<bool(const Path&)> fileCallback,
+		std::function<bool(const Path&)> dirCallback, bool recursive)
+	{
+		BS_ASSERT(!"TODO: implement FileSystem::iterate()");
+		return true;
+	}
+
+	Path FileSystem::getTempDirectoryPath()
+	{
+		String tmpdir;
+
+		// Try different things:
+		// 1) If defined, honor the TMPDIR environnement variable
+		char *TMPDIR = getenv("TMPDIR");
+		if (TMPDIR != NULL)
+				tmpdir = TMPDIR;
+		else
+		{
+		// 2) If defined, honor the P_tmpdir macro
+#ifdef P_tmpdir
+		tmpdir = String(P_tmpdir);
+#else
+		// 3) If everything else fails, simply default to /tmp
+		tmpdir = String("/tmp");
+#endif
+		}
+
+		tmpdir.append("/banshee-XXXXXX");
+		size_t len = tmpdir.size()+1;
+		char *nameTemplate = bs_newN<char>(len);
+		snprintf(nameTemplate, len, "%s", tmpdir.c_str());
+
+		char *directoryName = mkdtemp(nameTemplate);
+		if (directoryName == NULL)
+		{
+			LOGERR(String(__FUNCTION__) + ": " + strerror(errno));
+			return Path(StringUtil::BLANK);
+		}
+
+		return Path(String(directoryName) + "/");
+	}
+}

+ 13 - 0
Source/BansheeUtility/Source/Unix/BsUnixPlatformUtility.cpp

@@ -0,0 +1,13 @@
+//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
+//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
+#include "BsPlatformUtility.h"
+
+#include <stdlib.h>
+
+namespace BansheeEngine
+{
+	void PlatformUtility::terminate(bool force)
+	{
+		exit(0);
+	}
+}

+ 40 - 76
Source/BansheeUtility/Source/Win32/BsWin32CrashHandler.cpp

@@ -12,8 +12,20 @@
 #include "DbgHelp.h"
 #pragma warning(default : 4091)
 
+static const char* sMiniDumpName = "minidump.dmp";
+
 namespace BansheeEngine
 {
+	CrashHandler::CrashHandler()
+	{
+		m = bs_new<Data>();
+	}
+
+	CrashHandler::~CrashHandler()
+	{
+		bs_delete(m);
+	}
+
 	/**
 	 * Returns the raw stack trace using the provided context. Raw stack trace contains only function addresses.
 	 * 			
@@ -431,56 +443,33 @@ namespace BansheeEngine
 		CloseHandle(hThread);
 	}
 
-	static const wchar_t* gMiniDumpName = L"minidump.dmp";
-	const wchar_t* CrashHandler::CrashReportFolder = L"CrashReports/{0}/";
-	const wchar_t* CrashHandler::CrashLogName = L"log.html";
-
 	struct CrashHandler::Data
 	{
 		Mutex mutex;
 	};
 
-	CrashHandler::CrashHandler()
+	void win32_popupErrorMessageBox(const WString& msg, const Path& folder)
 	{
-		m = bs_new<Data>();
-	}
-
-	CrashHandler::~CrashHandler()
-	{
-		win32_unloadPSAPI();
+		WString simpleErrorMessage = msg
+			+ L"\n\nFor more information check the crash report located at:\n "
+			+ folder.toWString();
+		MessageBoxW(nullptr, simpleErrorMessage.c_str(), L"Banshee fatal error!", MB_OK);
 
-		bs_delete(m);
 	}
-
-	void CrashHandler::reportCrash(const String& type, const String& description, const String& function,
-		const String& file, UINT32 line) const
+	void CrashHandler::reportCrash(const String& type,
+	                               const String& description,
+	                               const String& function,
+	                               const String& file,
+	                               UINT32 line) const
 	{
 		// Win32 debug methods are not thread safe
 		Lock(m->mutex);
 
-		String stackTrace = getStackTrace();
-
-		StringStream errorMessageStream;
-		errorMessageStream << "Fatal error occurred and the program has to terminate!" << std::endl;
-		errorMessageStream << "\t\t" << type << " - " << description << std::endl;
-		errorMessageStream << "\t\t in " << function << " [" << file << ":" << line << "]" << std::endl;
-		errorMessageStream << std::endl;
-		errorMessageStream << "Stack trace: " << std::endl;
-		errorMessageStream << stackTrace;
-
-		String errorMessage = errorMessageStream.str();
-		gDebug().logError(errorMessage);
-
-		Path crashFolder = getCrashFolder();
-		FileSystem::createDir(crashFolder);
+		logErrorAndStackTrace(type, description, function, file, line);
+		saveCrashLog();
 
-		gDebug().saveLog(crashFolder + WString(CrashLogName));
-		win32_writeMiniDump(crashFolder + WString(gMiniDumpName), nullptr);
-
-		WString simpleErrorMessage = L"Fatal error occurred and the program has to terminate! " \
-			L"\n\nFor more information check the crash report located at:\n " + crashFolder.toWString();
-
-		MessageBoxW(nullptr, simpleErrorMessage.c_str(), L"Banshee fatal error!", MB_OK);
+		win32_writeMiniDump(getCrashFolder() + String(sMiniDumpName), nullptr);
+		win32_popupErrorMessageBox(toWString(sFatalErrorMsg), getCrashFolder());
 
 		// Note: Potentially also log Windows Error Report and/or send crash data to server
 	}
@@ -494,51 +483,31 @@ namespace BansheeEngine
 
 		win32_initPSAPI();
 		win32_loadSymbols();
-		String stackTrace = win32_getStackTrace(*exceptionData->ContextRecord, 0);
-
-		StringStream errorMessageStream;
-		errorMessageStream << "Fatal error occurred and the program has to terminate!" << std::endl;
-		errorMessageStream << "\t\t" << win32_getExceptionMessage(exceptionData->ExceptionRecord) << std::endl;;
-		errorMessageStream << std::endl;
-		errorMessageStream << "Stack trace: " << std::endl;
-		errorMessageStream << stackTrace;
-
-		String errorMessage = errorMessageStream.str();
-		gDebug().logError(errorMessage);
 
-		Path crashFolder = getCrashFolder();
-		FileSystem::createDir(crashFolder);
+		logErrorAndStackTrace(win32_getExceptionMessage(exceptionData->ExceptionRecord),
+		                      win32_getStackTrace(*exceptionData->ContextRecord, 0));
+		saveCrashLog();
 
-		gDebug().saveLog(crashFolder + WString(CrashLogName));
-		win32_writeMiniDump(crashFolder + WString(gMiniDumpName), exceptionData);
-
-		WString simpleErrorMessage = L"Fatal error occurred and the program has to terminate! " \
-			L"\n\nFor more information check the crash report located at:\n" + crashFolder.toWString();
-
-		MessageBoxW(nullptr, simpleErrorMessage.c_str(), L"Banshee fatal error!", MB_OK);
+		win32_writeMiniDump(getCrashFolder() + String(sMiniDumpName), exceptionData);
+		win32_popupErrorMessageBox(toWString(sFatalErrorMsg), getCrashFolder());
 
 		// Note: Potentially also log Windows Error Report and/or send crash data to server
 
 		return EXCEPTION_EXECUTE_HANDLER;
 	}
 
-	Path CrashHandler::getCrashFolder() const
+	String CrashHandler::getCrashTimestamp()
 	{
 		SYSTEMTIME systemTime;
 		GetLocalTime(&systemTime);
 
-		WString timeStamp = L"{0}{1}{2}_{3}{4}";
-		WString strYear = toWString(systemTime.wYear, 4, '0');
-		WString strMonth = toWString(systemTime.wMonth, 2, '0');
-		WString strDay = toWString(systemTime.wDay, 2, '0');
-		WString strHour = toWString(systemTime.wHour, 2, '0');
-		WString strMinute = toWString(systemTime.wMinute, 2, '0');
-
-		timeStamp = StringUtil::format(timeStamp, strYear, strMonth, strDay, strHour, strMinute);
-
-		WString folderName = StringUtil::format(CrashReportFolder, timeStamp);
-
-		return FileSystem::getWorkingDirectoryPath() + folderName;
+		String timeStamp = "{0}{1}{2}_{3}{4}";
+		String strYear = toString(systemTime.wYear, 4, '0');
+		String strMonth = toString(systemTime.wMonth, 2, '0');
+		String strDay = toString(systemTime.wDay, 2, '0');
+		String strHour = toString(systemTime.wHour, 2, '0');
+		String strMinute = toString(systemTime.wMinute, 2, '0');
+		return StringUtil::format(timeStamp, strYear, strMonth, strDay, strHour, strMinute);
 	}
 
 	String CrashHandler::getStackTrace()
@@ -550,9 +519,4 @@ namespace BansheeEngine
 		win32_loadSymbols();
 		return win32_getStackTrace(context, 2);
 	}
-
-	CrashHandler& gCrashHandler()
-	{
-		return CrashHandler::instance();
-	}
-}
+}

+ 30 - 152
Source/BansheeUtility/Source/Win32/BsWin32FileSystem.cpp

@@ -200,7 +200,6 @@ namespace BansheeEngine
 		return false;
 	}
 
-
 	bool win32_createDirectory(const WString& path)
 	{
 		if (win32_pathExists(path) && win32_isDirectory(path))
@@ -212,32 +211,6 @@ namespace BansheeEngine
 		return true;
 	}
 
-	void win32_remove(const WString& path)
-	{
-		if (win32_isDirectory(path))
-		{
-			if (RemoveDirectoryW(path.c_str()) == 0)
-				win32_handleError(GetLastError(), path);
-		}
-		else
-		{
-			if (DeleteFileW(path.c_str()) == 0)
-				win32_handleError(GetLastError(), path);
-		}
-	}
-
-	void win32_copyFile(const WString& from, const WString& to)
-	{
-		if (CopyFileW(from.c_str(), to.c_str(), FALSE) == FALSE)
-			win32_handleError(GetLastError(), from);
-	}
-
-	void win32_rename(const WString& oldPath, const WString& newPath)
-	{
-		if (MoveFileW(oldPath.c_str(), newPath.c_str()) == 0)
-			win32_handleError(GetLastError(), oldPath);
-	}
-
 	UINT64 win32_getFileSize(const WString& path)
 	{
 		WIN32_FILE_ATTRIBUTE_DATA attrData;
@@ -263,6 +236,35 @@ namespace BansheeEngine
 		return (std::time_t) ((ull.QuadPart / 10000000ULL) - 11644473600ULL);
 	}
 
+	void FileSystem::removeFile(const Path& path)
+	{
+		WString pathStr = path.toWString();
+		if (win32_isDirectory(pathStr))
+		{
+			if (RemoveDirectoryW(pathStr.c_str()) == 0)
+				win32_handleError(GetLastError(), pathStr);
+		}
+		else
+		{
+			if (DeleteFileW(pathStr.c_str()) == 0)
+				win32_handleError(GetLastError(), pathStr);
+		}
+	}
+
+	void FileSystem::copyFile(const Path& from, const Path& to)
+	{
+		if (CopyFileW(from.toWString().c_str(), to.toWString().c_str(), FALSE) == FALSE)
+			win32_handleError(GetLastError(), from.toWString());
+	}
+
+	void FileSystem::moveFile(const Path& oldPath, const Path& newPath)
+	{
+		WString oldPathStr = oldPath.toWString();
+		WString newPathStr = newPath.toWString();
+		if (MoveFileW(oldPathStr.c_str(), newPathStr.c_str()) == 0)
+			win32_handleError(GetLastError(), oldPathStr);
+	}
+
 	SPtr<DataStream> FileSystem::openFile(const Path& fullPath, bool readOnly)
 	{
 		WString pathWString = fullPath.toWString();
@@ -291,130 +293,6 @@ namespace BansheeEngine
 		return win32_getFileSize(fullPath.toWString());
 	}
 
-	void FileSystem::remove(const Path& fullPath, bool recursively)
-	{
-		WString fullPathStr = fullPath.toWString();
-
-		if (!FileSystem::exists(fullPath))
-			return;
-
-		if (recursively)
-		{
-			Vector<Path> files;
-			Vector<Path> directories;
-
-			getChildren(fullPath, files, directories);
-
-			for (auto& file : files)
-				remove(file, false);
-
-			for (auto& dir : directories)
-				remove(dir, true);
-		}
-
-		win32_remove(fullPathStr);
-	}
-
-	void FileSystem::move(const Path& oldPath, const Path& newPath, bool overwriteExisting)
-	{
-		WString oldPathStr = oldPath.toWString();
-		WString newPathStr = newPath.toWString();
-
-		if (win32_pathExists(newPathStr))
-		{
-			if (overwriteExisting)
-				win32_remove(newPathStr);
-			else
-			{
-				LOGWRN("Move operation failed because another file already exists at the new path: \"" + toString(newPathStr) + "\"");
-				return;
-			}
-		}
-
-		win32_rename(oldPathStr, newPathStr);
-	}
-
-	void FileSystem::copy(const Path& oldPath, const Path& newPath, bool overwriteExisting)
-	{
-		Stack<std::tuple<Path, Path>> todo;
-		todo.push(std::make_tuple(oldPath, newPath));
-
-		while (!todo.empty())
-		{
-			auto current = todo.top();
-			todo.pop();
-
-			Path sourcePath = std::get<0>(current);
-			WString sourcePathStr = sourcePath.toWString();
-			if (!win32_pathExists(sourcePathStr))
-				continue;
-
-			bool srcIsFile = win32_isFile(sourcePathStr);
-
-			Path destinationPath = std::get<1>(current);
-			WString destPathStr = destinationPath.toWString();
-
-			bool destExists = win32_pathExists(destPathStr);
-			if (destExists)
-			{
-				if (win32_isFile(destPathStr))
-				{
-					if (overwriteExisting)
-						win32_remove(destPathStr);
-					else
-					{
-						LOGWRN("Copy operation failed because another file already exists at the new path: \"" + toString(destPathStr) + "\"");
-						return;
-					}
-				}
-			}
-
-			bool destIsFile = !destinationPath.getWExtension().empty();
-
-			if (!srcIsFile && destIsFile)
-			{
-				LOGWRN("Cannot copy a source folder to a destination file.");
-				return;
-			}
-			else if (srcIsFile && !destIsFile)
-			{
-				Path destinationFilePath = destinationPath;
-				destinationFilePath.append(sourcePath.getWTail());
-
-				win32_copyFile(sourcePathStr, destinationFilePath.toWString());
-			}
-			else if (srcIsFile && destIsFile)
-			{
-				win32_copyFile(sourcePathStr, destPathStr);
-			}
-			else if (!srcIsFile && !destIsFile)
-			{
-				if (!destExists)
-					win32_createDirectory(destPathStr);
-
-				Vector<Path> files;
-				Vector<Path> directories;
-				getChildren(destinationPath, files, directories);
-
-				for (auto& file : files)
-				{
-					Path fileDestPath = destinationPath;
-					fileDestPath.append(file.getWTail());
-
-					todo.push(std::make_tuple(file, fileDestPath));
-				}
-
-				for (auto& dir : directories)
-				{
-					Path dirDestPath = destinationPath;
-					dirDestPath.append(dir.getWTail());
-
-					todo.push(std::make_tuple(dir, dirDestPath));
-				}
-			}
-		}
-	}
-
 	bool FileSystem::exists(const Path& fullPath)
 	{
 		return win32_pathExists(fullPath.toWString());
@@ -589,4 +467,4 @@ namespace BansheeEngine
 	{
 		return Path(win32_getTempDirectory());
 	}
-}
+}