Browse Source

Project Library save/load (Untested)

Marko Pintera 11 years ago
parent
commit
fc478f1322

+ 3 - 0
CamelotClient/CamelotClient.vcxproj

@@ -263,6 +263,7 @@
     <ClInclude Include="Include\BsEditorApplication.h" />
     <ClInclude Include="Include\BsEditorCommand.h" />
     <ClInclude Include="Include\BsEditorGUI.h" />
+    <ClInclude Include="Include\BsProjectLibraryEntriesRTTI.h" />
     <ClInclude Include="Include\BsEditorPrerequisites.h" />
     <ClInclude Include="Include\BsEditorWidget.h" />
     <ClInclude Include="Include\BsEditorWidgetContainer.h" />
@@ -282,6 +283,7 @@
     <ClInclude Include="Include\BsGUIWindowDropArea.h" />
     <ClInclude Include="Include\BsMainEditorWindow.h" />
     <ClInclude Include="Include\BsProjectLibrary.h" />
+    <ClInclude Include="Include\BsProjectLibraryEntries.h" />
     <ClInclude Include="Include\BsResourceMeta.h" />
     <ClInclude Include="Include\BsResourceMetaRTTI.h" />
     <ClInclude Include="Include\CmDebugCamera.h" />
@@ -317,6 +319,7 @@
     <ClCompile Include="Source\BsGUIWindowDropArea.cpp" />
     <ClCompile Include="Source\BsMainEditorWindow.cpp" />
     <ClCompile Include="Source\BsProjectLibrary.cpp" />
+    <ClCompile Include="Source\BsProjectLibraryEntries.cpp" />
     <ClCompile Include="Source\BsResourceMeta.cpp" />
     <ClCompile Include="Source\BsUndoRedo.cpp" />
     <ClCompile Include="Source\CmDebugCamera.cpp" />

+ 9 - 0
CamelotClient/CamelotClient.vcxproj.filters

@@ -138,6 +138,12 @@
     <ClInclude Include="Include\BsGUIResourceTreeView.h">
       <Filter>Header Files\Editor</Filter>
     </ClInclude>
+    <ClInclude Include="Include\BsProjectLibraryEntries.h">
+      <Filter>Header Files\Editor</Filter>
+    </ClInclude>
+    <ClInclude Include="Include\BsProjectLibraryEntriesRTTI.h">
+      <Filter>Header Files\Editor</Filter>
+    </ClInclude>
   </ItemGroup>
   <ItemGroup>
     <ClCompile Include="stdafx.cpp">
@@ -236,5 +242,8 @@
     <ClCompile Include="Source\BsGUIResourceTreeView.cpp">
       <Filter>Source Files\Editor</Filter>
     </ClCompile>
+    <ClCompile Include="Source\BsProjectLibraryEntries.cpp">
+      <Filter>Source Files\Editor</Filter>
+    </ClCompile>
   </ItemGroup>
 </Project>

+ 4 - 1
CamelotClient/Include/BsEditorPrerequisites.h

@@ -36,6 +36,9 @@ namespace BansheeEditor
 
 	enum TypeID_BansheeEditor
 	{
-		TID_ResourceMeta = 40000
+		TID_ResourceMeta = 40000,
+		TID_ProjectLibraryEntries = 40001,
+		TID_ProjectLibraryResEntry = 40002,
+		TID_ProjectLibraryDirEntry = 40003
 	};
 }

+ 8 - 0
CamelotClient/Include/BsProjectLibrary.h

@@ -20,6 +20,7 @@ namespace BansheeEditor
 
 		struct LibraryEntry
 		{
+			LibraryEntry() {}
 			LibraryEntry(const CM::WString& path, const CM::WString& name, DirectoryEntry* parent, LibraryEntryType type);
 
 			LibraryEntryType type;
@@ -31,6 +32,7 @@ namespace BansheeEditor
 
 		struct ResourceEntry : public LibraryEntry
 		{
+			ResourceEntry() {}
 			ResourceEntry(const CM::WString& path, const CM::WString& name, DirectoryEntry* parent);
 
 			ResourceMetaPtr meta;
@@ -39,6 +41,7 @@ namespace BansheeEditor
 
 		struct DirectoryEntry : public LibraryEntry
 		{
+			DirectoryEntry() {}
 			DirectoryEntry(const CM::WString& path, const CM::WString& name, DirectoryEntry* parent);
 
 			CM::Vector<LibraryEntry*>::type mChildren;
@@ -61,10 +64,15 @@ namespace BansheeEditor
 		boost::signal<void(const CM::WString&)> onEntryAdded;
 	private:
 		static const CM::WString INTERNAL_RESOURCES_DIR;
+		static const CM::WString LIBRARY_ENTRIES_FILENAME;
+		static const CM::WString RESOURCE_MANIFEST_FILENAME;
 
 		DirectoryEntry* mRootEntry;
 		CM::FolderMonitor* mMonitor;
 
+		void save();
+		void load();
+
 		ResourceEntry* addResourceInternal(DirectoryEntry* parent, const CM::WString& filePath);
 		DirectoryEntry* addDirectoryInternal(DirectoryEntry* parent, const CM::WString& dirPath);
 

+ 34 - 0
CamelotClient/Include/BsProjectLibraryEntries.h

@@ -0,0 +1,34 @@
+#pragma once
+
+#include "BsEditorPrerequisites.h"
+#include "BsProjectLibrary.h"
+#include "CmIReflectable.h"
+
+namespace BansheeEditor
+{
+	class ProjectLibraryEntries : public CM::IReflectable
+	{
+		struct ConstructPrivately { };
+
+	public:
+		explicit ProjectLibraryEntries(const ConstructPrivately& dummy);
+		ProjectLibraryEntries(const ProjectLibrary::DirectoryEntry& rootEntry);
+
+		static std::shared_ptr<ProjectLibraryEntries> create(const ProjectLibrary::DirectoryEntry& rootEntry);
+		static std::shared_ptr<ProjectLibraryEntries> createEmpty();
+
+		const ProjectLibrary::DirectoryEntry& getRootEntry() const { return mRootEntry; }
+
+	private:
+		ProjectLibrary::DirectoryEntry mRootEntry;
+
+		/************************************************************************/
+		/* 								SERIALIZATION                      		*/
+		/************************************************************************/
+
+	public:
+		friend class ProjectLibraryEntriesRTTI;
+		static CM::RTTITypeBase* getRTTIStatic();
+		virtual CM::RTTITypeBase* getRTTI() const;
+	};
+}

+ 173 - 0
CamelotClient/Include/BsProjectLibraryEntriesRTTI.h

@@ -0,0 +1,173 @@
+#pragma once
+
+#include "BsEditorPrerequisites.h"
+#include "CmRTTIType.h"
+#include "BsProjectLibraryEntries.h"
+
+namespace BansheeEditor
+{
+	class ProjectLibraryEntriesRTTI : public CM::RTTIType<ProjectLibraryEntries, CM::IReflectable, ProjectLibraryEntriesRTTI>
+	{
+	private:
+		ProjectLibrary::DirectoryEntry& getRootElement(ProjectLibraryEntries* obj) { return obj->mRootEntry; }
+		void setRootElement(ProjectLibraryEntries* obj, ProjectLibrary::DirectoryEntry& val) { obj->mRootEntry = val; } 
+
+	public:
+		ProjectLibraryEntriesRTTI()
+		{
+			addPlainField("mRootElement", 0, &ProjectLibraryEntriesRTTI::getRootElement, &ProjectLibraryEntriesRTTI::setRootElement);
+		}
+
+		virtual const CM::String& getRTTIName()
+		{
+			static CM::String name = "ProjectLibraryEntries";
+			return name;
+		}
+
+		virtual CM::UINT32 getRTTIId()
+		{
+			return TID_ProjectLibraryEntries;
+		}
+
+		virtual std::shared_ptr<CM::IReflectable> newRTTIObject()
+		{
+			return ProjectLibraryEntries::createEmpty();
+		}
+	};
+}
+
+namespace CamelotFramework
+{
+	template<> struct CM::RTTIPlainType<BansheeEditor::ProjectLibrary::ResourceEntry>
+	{	
+		enum { id = BansheeEditor::TID_ProjectLibraryResEntry }; enum { hasDynamicSize = 1 };
+
+		static void toMemory(const BansheeEditor::ProjectLibrary::ResourceEntry& data, char* memory)
+		{ 
+			memory = rttiWriteElem(data.type, memory);
+			memory = rttiWriteElem(data.path, memory);
+			memory = rttiWriteElem(data.elementName, memory);
+			memory = rttiWriteElem(data.lastUpdateTime, memory);
+		}
+
+		static UINT32 fromMemory(BansheeEditor::ProjectLibrary::ResourceEntry& data, char* memory)
+		{ 
+			UINT32 size = 0;
+
+			memory = rttiReadElem(data.type, memory, size);
+			memory = rttiReadElem(data.path, memory, size);
+			memory = rttiReadElem(data.elementName, memory, size);
+			memory = rttiReadElem(data.lastUpdateTime, memory, size);
+
+			return size;
+		}
+
+		static UINT32 getDynamicSize(const BansheeEditor::ProjectLibrary::ResourceEntry& data)	
+		{ 
+			UINT64 dataSize = rttiGetElemSize(data.type) + rttiGetElemSize(data.path) + rttiGetElemSize(data.elementName) +
+				rttiGetElemSize(data.lastUpdateTime);
+
+#if CM_DEBUG_MODE
+			if(dataSize > std::numeric_limits<UINT32>::max())
+			{
+				__string_throwDataOverflowException();
+			}
+#endif
+
+			return (UINT32)dataSize;
+		}	
+	}; 
+
+	template<> struct CM::RTTIPlainType<BansheeEditor::ProjectLibrary::DirectoryEntry>
+	{	
+		enum { id = BansheeEditor::TID_ProjectLibraryDirEntry }; enum { hasDynamicSize = 1 };
+
+		static void toMemory(const BansheeEditor::ProjectLibrary::DirectoryEntry& data, char* memory)
+		{ 
+			memory = rttiWriteElem(data.type, memory);
+			memory = rttiWriteElem(data.path, memory);
+			memory = rttiWriteElem(data.elementName, memory);
+
+			memory = rttiWriteElem((UINT32)data.mChildren.size(), memory);
+
+			for(auto& child : data.mChildren)
+			{
+				if(child->type == BansheeEditor::ProjectLibrary::LibraryEntryType::File)
+				{
+					BansheeEditor::ProjectLibrary::ResourceEntry* childResEntry = static_cast<BansheeEditor::ProjectLibrary::ResourceEntry*>(child);
+					memory = rttiWriteElem(*childResEntry, memory);
+				}
+				else if(child->type == BansheeEditor::ProjectLibrary::LibraryEntryType::Directory)
+				{
+					BansheeEditor::ProjectLibrary::DirectoryEntry* childDirEntry = static_cast<BansheeEditor::ProjectLibrary::DirectoryEntry*>(child);
+					memory = rttiWriteElem(*childDirEntry, memory);
+				}
+			}
+		}
+
+		static UINT32 fromMemory(BansheeEditor::ProjectLibrary::DirectoryEntry& data, char* memory)
+		{ 
+			UINT32 size = 0;
+
+			memory = rttiReadElem(data.type, memory, size);
+			memory = rttiReadElem(data.path, memory, size);
+			memory = rttiReadElem(data.elementName, memory, size);
+
+			UINT32 numChildren = 0;
+			memory = rttiReadElem(numChildren, memory, size);
+
+			for(UINT32 i = 0; i < numChildren; i++)
+			{
+				BansheeEditor::ProjectLibrary::LibraryEntryType childType = BansheeEditor::ProjectLibrary::LibraryEntryType::File;
+				rttiReadElem(childType, memory, size);
+
+				if(childType == BansheeEditor::ProjectLibrary::LibraryEntryType::File)
+				{
+					BansheeEditor::ProjectLibrary::ResourceEntry* childResEntry = cm_new<BansheeEditor::ProjectLibrary::ResourceEntry>(); // Note: Assumes that ProjectLibrary takes care of the cleanup
+					memory = rttiReadElem(*childResEntry, memory, size);
+
+					data.mChildren.push_back(childResEntry);
+				}
+				else if(childType == BansheeEditor::ProjectLibrary::LibraryEntryType::Directory)
+				{
+					BansheeEditor::ProjectLibrary::DirectoryEntry* childDirEntry = cm_new<BansheeEditor::ProjectLibrary::DirectoryEntry>(); // Note: Assumes that ProjectLibrary takes care of the cleanup
+					memory = rttiReadElem(*childDirEntry, memory, size);
+
+					data.mChildren.push_back(childDirEntry);
+				}
+			}
+
+			return size;
+		}
+
+		static UINT32 getDynamicSize(const BansheeEditor::ProjectLibrary::DirectoryEntry& data)	
+		{ 
+			UINT64 dataSize = rttiGetElemSize(data.type) + rttiGetElemSize(data.path) + rttiGetElemSize(data.elementName);
+
+			dataSize += rttiGetElemSize((UINT32)data.mChildren.size());
+
+			for(auto& child : data.mChildren)
+			{
+				if(child->type == BansheeEditor::ProjectLibrary::LibraryEntryType::File)
+				{
+					BansheeEditor::ProjectLibrary::ResourceEntry* childResEntry = static_cast<BansheeEditor::ProjectLibrary::ResourceEntry*>(child);
+					dataSize += rttiGetElemSize(*childResEntry);
+				}
+				else if(child->type == BansheeEditor::ProjectLibrary::LibraryEntryType::Directory)
+				{
+					BansheeEditor::ProjectLibrary::DirectoryEntry* childDirEntry = static_cast<BansheeEditor::ProjectLibrary::DirectoryEntry*>(child);
+					dataSize += rttiGetElemSize(*childDirEntry);
+				}
+			}
+
+#if CM_DEBUG_MODE
+			if(dataSize > std::numeric_limits<UINT32>::max())
+			{
+				__string_throwDataOverflowException();
+			}
+#endif
+
+			return (UINT32)dataSize;
+		}	
+	}; 
+}

+ 97 - 2
CamelotClient/Source/BsProjectLibrary.cpp

@@ -13,6 +13,7 @@
 #include "CmFileSerializer.h"
 #include "CmFolderMonitor.h"
 #include "CmDebug.h"
+#include "BsProjectLibraryEntries.h"
 
 using namespace CamelotFramework;
 using namespace BansheeEngine;
@@ -20,6 +21,8 @@ using namespace BansheeEngine;
 namespace BansheeEditor
 {
 	const WString ProjectLibrary::INTERNAL_RESOURCES_DIR = L"Internal\\Resources";
+	const WString ProjectLibrary::LIBRARY_ENTRIES_FILENAME = L"ProjectLibrary.asset";
+	const WString ProjectLibrary::RESOURCE_MANIFEST_FILENAME = L"ResourceManifest.asset";
 
 	ProjectLibrary::LibraryEntry::LibraryEntry(const CM::WString& path, const CM::WString& name, DirectoryEntry* parent, LibraryEntryType type)
 		:path(path), parent(parent), type(type), elementName(name)
@@ -46,13 +49,13 @@ namespace BansheeEditor
 		mMonitor->onRemoved.connect(boost::bind(&ProjectLibrary::onMonitorFileModified, this, _1));
 		mMonitor->onModified.connect(boost::bind(&ProjectLibrary::onMonitorFileModified, this, _1));
 
-		// TODO - Load
+		load();
 		checkForModifications(EditorApplication::instance().getResourcesFolderPath());
 	}
 
 	ProjectLibrary::~ProjectLibrary()
 	{
-		// TODO - Save
+		save();
 
 		mMonitor->stopMonitorAll();
 		cm_delete(mMonitor);
@@ -626,4 +629,96 @@ namespace BansheeEditor
 			checkForModifications(resourcePath);
 		}
 	}
+
+	void ProjectLibrary::save()
+	{
+		std::shared_ptr<ProjectLibraryEntries> libEntries = ProjectLibraryEntries::create(*mRootEntry);
+
+		WString libraryEntriesPath = Path::combine(EditorApplication::instance().getActiveProjectPath(), INTERNAL_RESOURCES_DIR);
+		libraryEntriesPath = Path::combine(libraryEntriesPath, LIBRARY_ENTRIES_FILENAME);
+
+		FileSerializer fs;
+		fs.encode(libEntries.get(), libraryEntriesPath);
+
+		ResourceManifestPtr manifest = gResources().getResourceManifest();
+
+		WString resourceManifestPath = Path::combine(EditorApplication::instance().getActiveProjectPath(), INTERNAL_RESOURCES_DIR);
+		resourceManifestPath = Path::combine(resourceManifestPath, RESOURCE_MANIFEST_FILENAME);
+
+		fs.encode(manifest.get(), resourceManifestPath);
+	}
+
+	void ProjectLibrary::load()
+	{
+		if(mRootEntry != nullptr)
+		{
+			deleteDirectoryInternal(mRootEntry);
+			mRootEntry = nullptr;
+		}
+
+		WString resPath = EditorApplication::instance().getResourcesFolderPath();
+		mRootEntry = cm_new<DirectoryEntry>(resPath, Path::getFilename(resPath), nullptr);
+
+		WString libraryEntriesPath = Path::combine(EditorApplication::instance().getActiveProjectPath(), INTERNAL_RESOURCES_DIR);
+		libraryEntriesPath = Path::combine(libraryEntriesPath, LIBRARY_ENTRIES_FILENAME);
+
+		if(FileSystem::exists(libraryEntriesPath))
+		{
+			FileSerializer fs;
+			std::shared_ptr<ProjectLibraryEntries> libEntries = std::static_pointer_cast<ProjectLibraryEntries>(fs.decode(libraryEntriesPath));
+
+			*mRootEntry = libEntries->getRootEntry();
+		}
+
+		// Load all meta files
+		Stack<DirectoryEntry*>::type todo;
+		todo.push(mRootEntry);
+
+		while(!todo.empty())
+		{
+			DirectoryEntry* curDir = todo.top();
+			todo.pop();
+
+			for(auto& child : curDir->mChildren)
+			{
+				if(child->type == LibraryEntryType::File)
+				{
+					ResourceEntry* resEntry = static_cast<ResourceEntry*>(child);
+
+					if(resEntry->meta == nullptr)
+					{
+						WString metaPath = resEntry->path + L".meta";
+
+						FileSerializer fs;
+						if(FileSystem::isFile(metaPath))
+						{
+							std::shared_ptr<IReflectable> loadedMeta = fs.decode(metaPath);
+
+							if(loadedMeta != nullptr && loadedMeta->isDerivedFrom(ResourceMeta::getRTTIStatic()))
+							{
+								ResourceMetaPtr resourceMeta = std::static_pointer_cast<ResourceMeta>(loadedMeta);
+								resEntry->meta = resourceMeta;
+							}
+						}
+					}
+				}
+				else if(child->type == LibraryEntryType::Directory)
+				{
+					todo.push(static_cast<DirectoryEntry*>(child));
+				}
+			}
+		}
+
+		// Load resource manifest
+		WString resourceManifestPath = Path::combine(EditorApplication::instance().getActiveProjectPath(), INTERNAL_RESOURCES_DIR);
+		resourceManifestPath = Path::combine(resourceManifestPath, RESOURCE_MANIFEST_FILENAME);
+
+		if(FileSystem::exists(resourceManifestPath))
+		{
+			FileSerializer fs;
+			ResourceManifestPtr manifest = std::static_pointer_cast<ResourceManifest>(fs.decode(resourceManifestPath));
+
+			gResources().setResourceManifest(manifest);
+		}
+	}
 }

+ 35 - 0
CamelotClient/Source/BsProjectLibraryEntries.cpp

@@ -0,0 +1,35 @@
+#include "BsProjectLibraryEntries.h"
+#include "BsProjectLibraryEntriesRTTI.h"
+
+using namespace CamelotFramework;
+using namespace BansheeEngine;
+
+namespace BansheeEditor
+{
+	ProjectLibraryEntries::ProjectLibraryEntries(const ProjectLibrary::DirectoryEntry& rootEntry)
+		:mRootEntry(rootEntry)
+	{ }
+
+	ProjectLibraryEntries::ProjectLibraryEntries(const ConstructPrivately& dummy)
+	{ }
+
+	std::shared_ptr<ProjectLibraryEntries> ProjectLibraryEntries::create(const ProjectLibrary::DirectoryEntry& rootEntry)
+	{
+		return cm_shared_ptr<ProjectLibraryEntries>(rootEntry);
+	}
+
+	std::shared_ptr<ProjectLibraryEntries> ProjectLibraryEntries::createEmpty()
+	{
+		return cm_shared_ptr<ProjectLibraryEntries>(ConstructPrivately());
+	}
+
+	RTTITypeBase* ProjectLibraryEntries::getRTTIStatic()
+	{
+		return ProjectLibraryEntriesRTTI::instance();
+	}
+
+	RTTITypeBase* ProjectLibraryEntries::getRTTI() const
+	{
+		return ProjectLibraryEntries::getRTTIStatic();
+	}
+}

+ 19 - 0
CamelotUtility/Include/CmRTTIPrerequisites.h

@@ -49,6 +49,25 @@ namespace CamelotFramework
 		return memory + rttiGetElemSize(data);
 	}
 
+	/**
+	 * @brief	Helper method when serializing known data types that have valid
+	 * 			RTTIPlainType specializations.
+	 * 			
+	 *			Reads the specified data into memory, advances the memory pointer by the
+	 *			bytes read and returns pointer to new memory. Also increases the size 
+	 *			value by the size of the read element.
+	 */
+	template<class ElemType>
+	char* rttiReadElem(ElemType& data, char* memory, UINT32& size)
+	{
+		RTTIPlainType<ElemType>::fromMemory(data, memory);
+
+		UINT32 elemSize = rttiGetElemSize(data);
+		size += elemSize;
+
+		return memory + elemSize;
+	}
+
 	template<class T>
 	struct RTTIPlainType 
 	{