瀏覽代碼

ProjectLibrary save/load

Marko Pintera 11 年之前
父節點
當前提交
6a24e0d39a

+ 1 - 0
CamelotClient/Include/BsProjectLibrary.h

@@ -67,6 +67,7 @@ namespace BansheeEditor
 		static const CM::WString LIBRARY_ENTRIES_FILENAME;
 		static const CM::WString LIBRARY_ENTRIES_FILENAME;
 		static const CM::WString RESOURCE_MANIFEST_FILENAME;
 		static const CM::WString RESOURCE_MANIFEST_FILENAME;
 
 
+		CM::ResourceManifestPtr mResourceManifest;
 		DirectoryEntry* mRootEntry;
 		DirectoryEntry* mRootEntry;
 		CM::FolderMonitor* mMonitor;
 		CM::FolderMonitor* mMonitor;
 
 

+ 2 - 2
CamelotClient/Include/BsProjectLibraryEntries.h

@@ -15,8 +15,7 @@ namespace BansheeEditor
 		ProjectLibraryEntries(const ProjectLibrary::DirectoryEntry& rootEntry);
 		ProjectLibraryEntries(const ProjectLibrary::DirectoryEntry& rootEntry);
 
 
 		static std::shared_ptr<ProjectLibraryEntries> create(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; }
 		const ProjectLibrary::DirectoryEntry& getRootEntry() const { return mRootEntry; }
 
 
 	private:
 	private:
@@ -25,6 +24,7 @@ namespace BansheeEditor
 		/************************************************************************/
 		/************************************************************************/
 		/* 								SERIALIZATION                      		*/
 		/* 								SERIALIZATION                      		*/
 		/************************************************************************/
 		/************************************************************************/
+		static std::shared_ptr<ProjectLibraryEntries> createEmpty();
 
 
 	public:
 	public:
 		friend class ProjectLibraryEntriesRTTI;
 		friend class ProjectLibraryEntriesRTTI;

+ 46 - 23
CamelotClient/Include/BsProjectLibraryEntriesRTTI.h

@@ -44,27 +44,40 @@ namespace CamelotFramework
 
 
 		static void toMemory(const BansheeEditor::ProjectLibrary::ResourceEntry& data, char* memory)
 		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);
+			UINT32 size = 0;
+			char* memoryStart = memory;
+			memory += sizeof(UINT32);
+			size += sizeof(UINT32);
+
+			UINT32 type = (UINT32)data.type;
+			memory = rttiWriteElem(type, memory, size);
+			memory = rttiWriteElem(data.path, memory, size);
+			memory = rttiWriteElem(data.elementName, memory, size);
+			memory = rttiWriteElem(data.lastUpdateTime, memory, size);
+
+			memcpy(memoryStart, &size, sizeof(UINT32));
 		}
 		}
 
 
 		static UINT32 fromMemory(BansheeEditor::ProjectLibrary::ResourceEntry& data, char* memory)
 		static UINT32 fromMemory(BansheeEditor::ProjectLibrary::ResourceEntry& data, char* memory)
 		{ 
 		{ 
 			UINT32 size = 0;
 			UINT32 size = 0;
+			memcpy(&size, memory, sizeof(UINT32));
+			memory += sizeof(UINT32);
+
+			UINT32 type;
+			memory = rttiReadElem(type, memory);
+			data.type = (BansheeEditor::ProjectLibrary::LibraryEntryType)type;
 
 
-			memory = rttiReadElem(data.type, memory, size);
-			memory = rttiReadElem(data.path, memory, size);
-			memory = rttiReadElem(data.elementName, memory, size);
-			memory = rttiReadElem(data.lastUpdateTime, memory, size);
+			memory = rttiReadElem(data.path, memory);
+			memory = rttiReadElem(data.elementName, memory);
+			memory = rttiReadElem(data.lastUpdateTime, memory);
 
 
 			return size;
 			return size;
 		}
 		}
 
 
 		static UINT32 getDynamicSize(const BansheeEditor::ProjectLibrary::ResourceEntry& data)	
 		static UINT32 getDynamicSize(const BansheeEditor::ProjectLibrary::ResourceEntry& data)	
 		{ 
 		{ 
-			UINT64 dataSize = rttiGetElemSize(data.type) + rttiGetElemSize(data.path) + rttiGetElemSize(data.elementName) +
+			UINT64 dataSize = sizeof(UINT32) + rttiGetElemSize(data.type) + rttiGetElemSize(data.path) + rttiGetElemSize(data.elementName) +
 				rttiGetElemSize(data.lastUpdateTime);
 				rttiGetElemSize(data.lastUpdateTime);
 
 
 #if CM_DEBUG_MODE
 #if CM_DEBUG_MODE
@@ -84,54 +97,64 @@ namespace CamelotFramework
 
 
 		static void toMemory(const BansheeEditor::ProjectLibrary::DirectoryEntry& data, char* memory)
 		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);
+			UINT32 size = 0;
+			char* memoryStart = memory;
+			memory += sizeof(UINT32);
+			size += sizeof(UINT32);
+
+			memory = rttiWriteElem(data.type, memory, size);
+			memory = rttiWriteElem(data.path, memory, size);
+			memory = rttiWriteElem(data.elementName, memory, size);
 
 
-			memory = rttiWriteElem((UINT32)data.mChildren.size(), memory);
+			UINT32 numChildren = (UINT32)data.mChildren.size();
+			memory = rttiWriteElem(numChildren, memory, size);
 
 
 			for(auto& child : data.mChildren)
 			for(auto& child : data.mChildren)
 			{
 			{
 				if(child->type == BansheeEditor::ProjectLibrary::LibraryEntryType::File)
 				if(child->type == BansheeEditor::ProjectLibrary::LibraryEntryType::File)
 				{
 				{
 					BansheeEditor::ProjectLibrary::ResourceEntry* childResEntry = static_cast<BansheeEditor::ProjectLibrary::ResourceEntry*>(child);
 					BansheeEditor::ProjectLibrary::ResourceEntry* childResEntry = static_cast<BansheeEditor::ProjectLibrary::ResourceEntry*>(child);
-					memory = rttiWriteElem(*childResEntry, memory);
+					memory = rttiWriteElem(*childResEntry, memory, size);
 				}
 				}
 				else if(child->type == BansheeEditor::ProjectLibrary::LibraryEntryType::Directory)
 				else if(child->type == BansheeEditor::ProjectLibrary::LibraryEntryType::Directory)
 				{
 				{
 					BansheeEditor::ProjectLibrary::DirectoryEntry* childDirEntry = static_cast<BansheeEditor::ProjectLibrary::DirectoryEntry*>(child);
 					BansheeEditor::ProjectLibrary::DirectoryEntry* childDirEntry = static_cast<BansheeEditor::ProjectLibrary::DirectoryEntry*>(child);
-					memory = rttiWriteElem(*childDirEntry, memory);
+					memory = rttiWriteElem(*childDirEntry, memory, size);
 				}
 				}
 			}
 			}
+
+			memcpy(memoryStart, &size, sizeof(UINT32));
 		}
 		}
 
 
 		static UINT32 fromMemory(BansheeEditor::ProjectLibrary::DirectoryEntry& data, char* memory)
 		static UINT32 fromMemory(BansheeEditor::ProjectLibrary::DirectoryEntry& data, char* memory)
 		{ 
 		{ 
 			UINT32 size = 0;
 			UINT32 size = 0;
+			memcpy(&size, memory, sizeof(UINT32));
+			memory += sizeof(UINT32);
 
 
-			memory = rttiReadElem(data.type, memory, size);
-			memory = rttiReadElem(data.path, memory, size);
-			memory = rttiReadElem(data.elementName, memory, size);
+			memory = rttiReadElem(data.type, memory);
+			memory = rttiReadElem(data.path, memory);
+			memory = rttiReadElem(data.elementName, memory);
 
 
 			UINT32 numChildren = 0;
 			UINT32 numChildren = 0;
-			memory = rttiReadElem(numChildren, memory, size);
+			memory = rttiReadElem(numChildren, memory);
 
 
 			for(UINT32 i = 0; i < numChildren; i++)
 			for(UINT32 i = 0; i < numChildren; i++)
 			{
 			{
 				BansheeEditor::ProjectLibrary::LibraryEntryType childType = BansheeEditor::ProjectLibrary::LibraryEntryType::File;
 				BansheeEditor::ProjectLibrary::LibraryEntryType childType = BansheeEditor::ProjectLibrary::LibraryEntryType::File;
-				rttiReadElem(childType, memory, size);
+				rttiReadElem(childType, memory + sizeof(UINT32)); // Skip ahead to get type
 
 
 				if(childType == BansheeEditor::ProjectLibrary::LibraryEntryType::File)
 				if(childType == BansheeEditor::ProjectLibrary::LibraryEntryType::File)
 				{
 				{
 					BansheeEditor::ProjectLibrary::ResourceEntry* childResEntry = cm_new<BansheeEditor::ProjectLibrary::ResourceEntry>(); // Note: Assumes that ProjectLibrary takes care of the cleanup
 					BansheeEditor::ProjectLibrary::ResourceEntry* childResEntry = cm_new<BansheeEditor::ProjectLibrary::ResourceEntry>(); // Note: Assumes that ProjectLibrary takes care of the cleanup
-					memory = rttiReadElem(*childResEntry, memory, size);
+					memory = rttiReadElem(*childResEntry, memory);
 
 
 					data.mChildren.push_back(childResEntry);
 					data.mChildren.push_back(childResEntry);
 				}
 				}
 				else if(childType == BansheeEditor::ProjectLibrary::LibraryEntryType::Directory)
 				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
 					BansheeEditor::ProjectLibrary::DirectoryEntry* childDirEntry = cm_new<BansheeEditor::ProjectLibrary::DirectoryEntry>(); // Note: Assumes that ProjectLibrary takes care of the cleanup
-					memory = rttiReadElem(*childDirEntry, memory, size);
+					memory = rttiReadElem(*childDirEntry, memory);
 
 
 					data.mChildren.push_back(childDirEntry);
 					data.mChildren.push_back(childDirEntry);
 				}
 				}
@@ -142,7 +165,7 @@ namespace CamelotFramework
 
 
 		static UINT32 getDynamicSize(const BansheeEditor::ProjectLibrary::DirectoryEntry& data)	
 		static UINT32 getDynamicSize(const BansheeEditor::ProjectLibrary::DirectoryEntry& data)	
 		{ 
 		{ 
-			UINT64 dataSize = rttiGetElemSize(data.type) + rttiGetElemSize(data.path) + rttiGetElemSize(data.elementName);
+			UINT64 dataSize = sizeof(UINT32) + rttiGetElemSize(data.type) + rttiGetElemSize(data.path) + rttiGetElemSize(data.elementName);
 
 
 			dataSize += rttiGetElemSize((UINT32)data.mChildren.size());
 			dataSize += rttiGetElemSize((UINT32)data.mChildren.size());
 
 

+ 0 - 2
CamelotClient/Source/BsEditorApplication.cpp

@@ -278,8 +278,6 @@ namespace BansheeEditor
 		fragProgRef = nullptr;
 		fragProgRef = nullptr;
 		vertProgRef = nullptr;
 		vertProgRef = nullptr;
 
 
-		testModelGO->destroy();
-
 		newPassGL = nullptr;
 		newPassGL = nullptr;
 		newTechniqueGL = nullptr;
 		newTechniqueGL = nullptr;
 
 

+ 3 - 4
CamelotClient/Source/BsGUIResourceTreeView.cpp

@@ -401,16 +401,15 @@ namespace BansheeEditor
 		DraggedResources* draggedResources = cm_new<DraggedResources>();
 		DraggedResources* draggedResources = cm_new<DraggedResources>();
 		InternalDraggedResources* internalDraggedResources = cm_new<InternalDraggedResources>((UINT32)mSelectedElements.size());
 		InternalDraggedResources* internalDraggedResources = cm_new<InternalDraggedResources>((UINT32)mSelectedElements.size());
 
 
-		ResourceManifestPtr resourceManifest = gResources().getResourceManifest();
-
 		UINT32 cnt = 0;
 		UINT32 cnt = 0;
 		for(auto& selectedElement : mSelectedElements)
 		for(auto& selectedElement : mSelectedElements)
 		{
 		{
 			ResourceTreeElement* resourceTreeElement = static_cast<ResourceTreeElement*>(selectedElement.element);
 			ResourceTreeElement* resourceTreeElement = static_cast<ResourceTreeElement*>(selectedElement.element);
 			internalDraggedResources->resourcePaths[cnt] = resourceTreeElement->mFullPath; 
 			internalDraggedResources->resourcePaths[cnt] = resourceTreeElement->mFullPath; 
 
 
-			if(resourceManifest->filePathExists(internalDraggedResources->resourcePaths[cnt]))
-				draggedResources->resourceUUIDs.push_back(resourceManifest->filePathToUUID(internalDraggedResources->resourcePaths[cnt]));
+			String uuid;
+			if(gResources().getUUIDFromFilePath(internalDraggedResources->resourcePaths[cnt], uuid))
+				draggedResources->resourceUUIDs.push_back(uuid);
 
 
 			cnt++;
 			cnt++;
 		}
 		}

+ 28 - 25
CamelotClient/Source/BsProjectLibrary.cpp

@@ -50,6 +50,12 @@ namespace BansheeEditor
 		mMonitor->onModified.connect(boost::bind(&ProjectLibrary::onMonitorFileModified, this, _1));
 		mMonitor->onModified.connect(boost::bind(&ProjectLibrary::onMonitorFileModified, this, _1));
 
 
 		load();
 		load();
+
+		if(mResourceManifest == nullptr)
+			mResourceManifest = ResourceManifest::create("ProjectLibrary");
+
+		gResources().registerResourceManifest(mResourceManifest);
+
 		checkForModifications(EditorApplication::instance().getResourcesFolderPath());
 		checkForModifications(EditorApplication::instance().getResourcesFolderPath());
 	}
 	}
 
 
@@ -267,17 +273,15 @@ namespace BansheeEditor
 
 
 	void ProjectLibrary::deleteResourceInternal(ResourceEntry* resource)
 	void ProjectLibrary::deleteResourceInternal(ResourceEntry* resource)
 	{
 	{
-		ResourceManifestPtr manifest = gResources().getResourceManifest();
-
 		if(resource->meta != nullptr)
 		if(resource->meta != nullptr)
 		{
 		{
-			if(manifest->uuidExists(resource->meta->getUUID()))
+			WString path;
+			if(mResourceManifest->uuidToFilePath(resource->meta->getUUID(), path))
 			{
 			{
-				const WString& path = manifest->uuidToFilePath(resource->meta->getUUID());
 				if(FileSystem::isFile(path))
 				if(FileSystem::isFile(path))
 					FileSystem::remove(path);
 					FileSystem::remove(path);
 
 
-				manifest->unregisterResource(resource->meta->getUUID());
+				mResourceManifest->unregisterResource(resource->meta->getUUID());
 			}
 			}
 		}
 		}
 
 
@@ -308,10 +312,13 @@ namespace BansheeEditor
 		}
 		}
 
 
 		DirectoryEntry* parent = directory->parent;
 		DirectoryEntry* parent = directory->parent;
-		auto findIter = std::find_if(parent->mChildren.begin(), parent->mChildren.end(), 
-			[&] (const LibraryEntry* entry) { return entry == directory; });
+		if(parent != nullptr)
+		{
+			auto findIter = std::find_if(parent->mChildren.begin(), parent->mChildren.end(), 
+				[&] (const LibraryEntry* entry) { return entry == directory; });
 
 
-		parent->mChildren.erase(findIter);
+			parent->mChildren.erase(findIter);
+		}
 
 
 		if(!onEntryRemoved.empty())
 		if(!onEntryRemoved.empty())
 			onEntryRemoved(directory->path);
 			onEntryRemoved(directory->path);
@@ -377,8 +384,7 @@ namespace BansheeEditor
 			gResources().save(importedResource, internalResourcesPath, true);
 			gResources().save(importedResource, internalResourcesPath, true);
 			gResources().unload(importedResource);
 			gResources().unload(importedResource);
 
 
-			ResourceManifestPtr manifest = gResources().getResourceManifest();
-			manifest->registerResource(importedResource.getUUID(), internalResourcesPath);
+			mResourceManifest->registerResource(importedResource.getUUID(), internalResourcesPath);
 
 
 			resource->lastUpdateTime = std::time(nullptr);
 			resource->lastUpdateTime = std::time(nullptr);
 		}
 		}
@@ -389,12 +395,10 @@ namespace BansheeEditor
 		if(resource->meta == nullptr)
 		if(resource->meta == nullptr)
 			return false;
 			return false;
 
 
-		ResourceManifestPtr manifest = gResources().getResourceManifest();
-
-		if(!manifest->uuidExists(resource->meta->getUUID()))
+		WString path;
+		if(!mResourceManifest->uuidToFilePath(resource->meta->getUUID(), path))
 			return false;
 			return false;
 
 
-		const WString& path = manifest->uuidToFilePath(resource->meta->getUUID());
 		if(!FileSystem::isFile(path))
 		if(!FileSystem::isFile(path))
 			return false;
 			return false;
 
 
@@ -634,18 +638,18 @@ namespace BansheeEditor
 	{
 	{
 		std::shared_ptr<ProjectLibraryEntries> libEntries = ProjectLibraryEntries::create(*mRootEntry);
 		std::shared_ptr<ProjectLibraryEntries> libEntries = ProjectLibraryEntries::create(*mRootEntry);
 
 
-		WString libraryEntriesPath = Path::combine(EditorApplication::instance().getActiveProjectPath(), INTERNAL_RESOURCES_DIR);
+		WString projectPath = EditorApplication::instance().getActiveProjectPath();
+
+		WString libraryEntriesPath = Path::combine(projectPath, INTERNAL_RESOURCES_DIR);
 		libraryEntriesPath = Path::combine(libraryEntriesPath, LIBRARY_ENTRIES_FILENAME);
 		libraryEntriesPath = Path::combine(libraryEntriesPath, LIBRARY_ENTRIES_FILENAME);
 
 
 		FileSerializer fs;
 		FileSerializer fs;
 		fs.encode(libEntries.get(), libraryEntriesPath);
 		fs.encode(libEntries.get(), libraryEntriesPath);
 
 
-		ResourceManifestPtr manifest = gResources().getResourceManifest();
-
-		WString resourceManifestPath = Path::combine(EditorApplication::instance().getActiveProjectPath(), INTERNAL_RESOURCES_DIR);
+		WString resourceManifestPath = Path::combine(projectPath, INTERNAL_RESOURCES_DIR);
 		resourceManifestPath = Path::combine(resourceManifestPath, RESOURCE_MANIFEST_FILENAME);
 		resourceManifestPath = Path::combine(resourceManifestPath, RESOURCE_MANIFEST_FILENAME);
 
 
-		fs.encode(manifest.get(), resourceManifestPath);
+		ResourceManifest::save(mResourceManifest, resourceManifestPath, projectPath);
 	}
 	}
 
 
 	void ProjectLibrary::load()
 	void ProjectLibrary::load()
@@ -656,10 +660,12 @@ namespace BansheeEditor
 			mRootEntry = nullptr;
 			mRootEntry = nullptr;
 		}
 		}
 
 
+		WString projectPath = EditorApplication::instance().getActiveProjectPath();
+
 		WString resPath = EditorApplication::instance().getResourcesFolderPath();
 		WString resPath = EditorApplication::instance().getResourcesFolderPath();
 		mRootEntry = cm_new<DirectoryEntry>(resPath, Path::getFilename(resPath), nullptr);
 		mRootEntry = cm_new<DirectoryEntry>(resPath, Path::getFilename(resPath), nullptr);
 
 
-		WString libraryEntriesPath = Path::combine(EditorApplication::instance().getActiveProjectPath(), INTERNAL_RESOURCES_DIR);
+		WString libraryEntriesPath = Path::combine(projectPath, INTERNAL_RESOURCES_DIR);
 		libraryEntriesPath = Path::combine(libraryEntriesPath, LIBRARY_ENTRIES_FILENAME);
 		libraryEntriesPath = Path::combine(libraryEntriesPath, LIBRARY_ENTRIES_FILENAME);
 
 
 		if(FileSystem::exists(libraryEntriesPath))
 		if(FileSystem::exists(libraryEntriesPath))
@@ -710,15 +716,12 @@ namespace BansheeEditor
 		}
 		}
 
 
 		// Load resource manifest
 		// Load resource manifest
-		WString resourceManifestPath = Path::combine(EditorApplication::instance().getActiveProjectPath(), INTERNAL_RESOURCES_DIR);
+		WString resourceManifestPath = Path::combine(projectPath, INTERNAL_RESOURCES_DIR);
 		resourceManifestPath = Path::combine(resourceManifestPath, RESOURCE_MANIFEST_FILENAME);
 		resourceManifestPath = Path::combine(resourceManifestPath, RESOURCE_MANIFEST_FILENAME);
 
 
 		if(FileSystem::exists(resourceManifestPath))
 		if(FileSystem::exists(resourceManifestPath))
 		{
 		{
-			FileSerializer fs;
-			ResourceManifestPtr manifest = std::static_pointer_cast<ResourceManifest>(fs.decode(resourceManifestPath));
-
-			gResources().setResourceManifest(manifest);
+			mResourceManifest = ResourceManifest::load(resourceManifestPath, projectPath);
 		}
 		}
 	}
 	}
 }
 }

+ 32 - 2
CamelotCore/Include/CmResourceManifest.h

@@ -15,23 +15,53 @@ namespace CamelotFramework
 	 */
 	 */
 	class CM_EXPORT ResourceManifest : public IReflectable
 	class CM_EXPORT ResourceManifest : public IReflectable
 	{
 	{
+		struct ConstructPrivately {};
 	public:
 	public:
+		explicit ResourceManifest(const ConstructPrivately& dummy);
+		ResourceManifest(const String& name);
+
+		const String& getName() const { return mName; }
+
 		void registerResource(const String& uuid, const WString& filePath);
 		void registerResource(const String& uuid, const WString& filePath);
 		void unregisterResource(const String& uuid);
 		void unregisterResource(const String& uuid);
 
 
-		const WString& uuidToFilePath(const String& uuid) const;
-		const String& filePathToUUID(const WString& filePath) const;
+		bool uuidToFilePath(const String& uuid, WString& filePath) const;
+		bool filePathToUUID(const WString& filePath, String& outUUID) const;
 
 
 		bool uuidExists(const String& uuid) const;
 		bool uuidExists(const String& uuid) const;
 		bool filePathExists(const WString& filePath) const;
 		bool filePathExists(const WString& filePath) const;
 
 
+		/**
+		 * @brief	Saves the resource manifest to the specified location.
+		 *
+		 * @param	manifest		Manifest to save.
+		 * @param	path			Full pathname of the file.
+		 * @param	relativePath	If not empty, all pathnames in the manifest will be stored
+		 * 							as if relative to this path.
+		 */
+		static void save(const ResourceManifestPtr& manifest, const WString& path, const WString& relativePath);
+
+		/**
+		 * @brief	Loads the resource manifest from the specified location.
+		 *
+		 * @param	path			Full pathname of the file.
+		 * @param	relativePath	If not empty, all loaded pathnames will have this
+		 * 							path prepended.
+		 */
+		static ResourceManifestPtr load(const WString& path, const WString& relativePath);
+
+		static ResourceManifestPtr create(const String& name);
+
 	private:
 	private:
+		String mName;
 		Map<String, WString>::type mUUIDToFilePath;
 		Map<String, WString>::type mUUIDToFilePath;
 		Map<WString, String>::type mFilePathToUUID;
 		Map<WString, String>::type mFilePathToUUID;
 
 
 		/************************************************************************/
 		/************************************************************************/
 		/* 								RTTI		                     		*/
 		/* 								RTTI		                     		*/
 		/************************************************************************/
 		/************************************************************************/
+		static ResourceManifestPtr createEmpty();
+
 	public:
 	public:
 		friend class ResourceManifestRTTI;
 		friend class ResourceManifestRTTI;
 		static RTTITypeBase* getRTTIStatic();
 		static RTTITypeBase* getRTTIStatic();

+ 11 - 45
CamelotCore/Include/CmResourceManifestRTTI.h

@@ -6,52 +6,19 @@
 
 
 namespace CamelotFramework
 namespace CamelotFramework
 {
 {
-	struct ResourceManifestEntry
+	class CM_EXPORT ResourceManifestRTTI : public RTTIType<ResourceManifest, IReflectable, ResourceManifestRTTI>
 	{
 	{
-		String uuid;
-		WString path;
-	};
-
-	template<> struct RTTIPlainType<ResourceManifestEntry>
-	{	
-		enum { id = TID_ResourceManifestEntry }; enum { hasDynamicSize = 1 };
-
-		static void toMemory(const ResourceManifestEntry& data, char* memory)
-		{ 
-			UINT32 size = getDynamicSize(data);
-
-			memcpy(memory, &size, sizeof(UINT32));
-			memory += sizeof(UINT32);
-
-			rttiWriteElem(data.uuid, memory);
-			rttiWriteElem(data.path, memory);
+	private:
+		String& getName(ResourceManifest* obj)
+		{
+			return obj->mName;
 		}
 		}
 
 
-		static UINT32 fromMemory(ResourceManifestEntry& data, char* memory)
-		{ 
-			UINT32 size;
-			memcpy(&size, memory, sizeof(UINT32)); 
-			memory += sizeof(UINT32);
-
-			rttiReadElem(data.uuid, memory);
-			rttiReadElem(data.path, memory);
-
-			return size;
+		void setName(ResourceManifest* obj, String& val)
+		{
+			obj->mName = val;
 		}
 		}
 
 
-		static UINT32 getDynamicSize(const ResourceManifestEntry& data)	
-		{ 
-			UINT64 dataSize = sizeof(UINT32);
-			dataSize += rttiGetElemSize(data.uuid);
-			dataSize += rttiGetElemSize(data.path);
-
-			return (UINT32)dataSize;
-		}	
-	}; 
-
-	class CM_EXPORT ResourceManifestRTTI : public RTTIType<ResourceManifest, IReflectable, ResourceManifestRTTI>
-	{
-	private:
 		Map<String, WString>::type& getUUIDMap(ResourceManifest* obj) 
 		Map<String, WString>::type& getUUIDMap(ResourceManifest* obj) 
 		{ 
 		{ 
 			return obj->mUUIDToFilePath;
 			return obj->mUUIDToFilePath;
@@ -71,7 +38,8 @@ namespace CamelotFramework
 	public:
 	public:
 		ResourceManifestRTTI()
 		ResourceManifestRTTI()
 		{
 		{
-			addPlainField("mUUIDToFilePath", 0, &ResourceManifestRTTI::getUUIDMap, &ResourceManifestRTTI::setUUIDMap);
+			addPlainField("mName", 0, &ResourceManifestRTTI::getName, &ResourceManifestRTTI::setName);
+			addPlainField("mUUIDToFilePath", 1, &ResourceManifestRTTI::getUUIDMap, &ResourceManifestRTTI::setUUIDMap);
 		}
 		}
 
 
 		virtual const String& getRTTIName()
 		virtual const String& getRTTIName()
@@ -87,9 +55,7 @@ namespace CamelotFramework
 
 
 		virtual std::shared_ptr<IReflectable> newRTTIObject()
 		virtual std::shared_ptr<IReflectable> newRTTIObject()
 		{
 		{
-			std::shared_ptr<ResourceManifest> obj = cm_shared_ptr<ResourceManifest>();
-
-			return obj;
+			return ResourceManifest::createEmpty();
 		}
 		}
 	};
 	};
 }
 }

+ 11 - 7
CamelotCore/Include/CmResources.h

@@ -135,18 +135,25 @@ namespace CamelotFramework
 		 * 			manifest before closing the application and restore it upon startup.
 		 * 			manifest before closing the application and restore it upon startup.
 		 * 			Otherwise resources will be assigned brand new UUIDs and references will be broken.
 		 * 			Otherwise resources will be assigned brand new UUIDs and references will be broken.
 		 */
 		 */
-		void setResourceManifest(const ResourceManifestPtr& manifest) { mResourceManifest = manifest; }
+		void registerResourceManifest(const ResourceManifestPtr& manifest);
 
 
 		/**
 		/**
 		 * @brief	Allows you to retrieve resource manifest containing UUID <-> file path mapping that is
 		 * @brief	Allows you to retrieve resource manifest containing UUID <-> file path mapping that is
 		 * 			used when resolving resource references.
 		 * 			used when resolving resource references.
+		 * 			
+		 * @note	Resources module internally holds a "Default" manifest that it automatically updated whenever
+		 * 			a resource is saved.
 		 *
 		 *
-		 * @see		setResourceManifest
+		 * @see		registerResourceManifest
 		 */
 		 */
-		ResourceManifestPtr getResourceManifest() const { return mResourceManifest; }
+		ResourceManifestPtr getResourceManifest(const String& name) const;
+
+		bool getFilePathFromUUID(const String& uuid, WString& filePath) const;
+		bool getUUIDFromFilePath(const WString& path, String& uuid) const;
 
 
 	private:
 	private:
-		ResourceManifestPtr mResourceManifest;
+		Vector<ResourceManifestPtr>::type mResourceManifests;
+		ResourceManifestPtr mDefaultResourceManifest;
 
 
 		CM_MUTEX(mInProgressResourcesMutex);
 		CM_MUTEX(mInProgressResourcesMutex);
 		CM_MUTEX(mLoadedResourceMutex);
 		CM_MUTEX(mLoadedResourceMutex);
@@ -163,9 +170,6 @@ namespace CamelotFramework
 		HResource loadInternal(const WString& filePath, bool synchronous); 
 		HResource loadInternal(const WString& filePath, bool synchronous); 
 		ResourcePtr loadFromDiskAndDeserialize(const WString& filePath);
 		ResourcePtr loadFromDiskAndDeserialize(const WString& filePath);
 
 
-		const WString& getPathFromUUID(const String& uuid) const;
-		const String& getUUIDFromPath(const WString& path) const;
-
 		void notifyResourceLoadingFinished(HResource& handle);
 		void notifyResourceLoadingFinished(HResource& handle);
 		void notifyNewResourceLoaded(HResource& handle);
 		void notifyNewResourceLoaded(HResource& handle);
 	};
 	};

+ 103 - 6
CamelotCore/Source/CmResourceManifest.cpp

@@ -1,9 +1,32 @@
 #include "CmResourceManifest.h"
 #include "CmResourceManifest.h"
 #include "CmResourceManifestRTTI.h"
 #include "CmResourceManifestRTTI.h"
 #include "CmPath.h"
 #include "CmPath.h"
+#include "CmFileSerializer.h"
+#include "CmException.h"
 
 
 namespace CamelotFramework
 namespace CamelotFramework
 {
 {
+	ResourceManifest::ResourceManifest(const ConstructPrivately& dummy)
+	{
+
+	}
+
+	ResourceManifest::ResourceManifest(const String& name)
+		:mName(name)
+	{
+
+	}
+
+	ResourceManifestPtr ResourceManifest::create(const String& name)
+	{
+		return cm_shared_ptr<ResourceManifest>(name);
+	}
+
+	ResourceManifestPtr ResourceManifest::createEmpty()
+	{
+		return cm_shared_ptr<ResourceManifest>(ConstructPrivately());
+	}
+
 	void ResourceManifest::registerResource(const String& uuid, const WString& filePath)
 	void ResourceManifest::registerResource(const String& uuid, const WString& filePath)
 	{
 	{
 		auto iterFind = mUUIDToFilePath.find(uuid);
 		auto iterFind = mUUIDToFilePath.find(uuid);
@@ -39,17 +62,23 @@ namespace CamelotFramework
 		}
 		}
 	}
 	}
 
 
-	const WString& ResourceManifest::uuidToFilePath(const String& uuid) const
+	bool ResourceManifest::uuidToFilePath(const String& uuid, WString& filePath) const
 	{
 	{
 		auto iterFind = mUUIDToFilePath.find(uuid);
 		auto iterFind = mUUIDToFilePath.find(uuid);
 
 
 		if(iterFind != mUUIDToFilePath.end())
 		if(iterFind != mUUIDToFilePath.end())
-			return iterFind->second;
+		{
+			filePath = iterFind->second;
+			return true;
+		}
 		else
 		else
-			return StringUtil::WBLANK;
+		{
+			filePath = StringUtil::WBLANK;
+			return false;
+		}
 	}
 	}
 
 
-	const String& ResourceManifest::filePathToUUID(const WString& filePath) const
+	bool ResourceManifest::filePathToUUID(const WString& filePath, String& outUUID) const
 	{
 	{
 		WString standardizedFilePath = Path::standardizePath(filePath);
 		WString standardizedFilePath = Path::standardizePath(filePath);
 		StringUtil::toLowerCase(standardizedFilePath);
 		StringUtil::toLowerCase(standardizedFilePath);
@@ -57,9 +86,15 @@ namespace CamelotFramework
 		auto iterFind = mFilePathToUUID.find(standardizedFilePath);
 		auto iterFind = mFilePathToUUID.find(standardizedFilePath);
 
 
 		if(iterFind != mFilePathToUUID.end())
 		if(iterFind != mFilePathToUUID.end())
-			return iterFind->second;
+		{
+			outUUID = iterFind->second;
+			return true;
+		}
 		else
 		else
-			return StringUtil::BLANK;
+		{
+			outUUID = StringUtil::BLANK;
+			return false;
+		}
 	}
 	}
 
 
 	bool ResourceManifest::uuidExists(const String& uuid) const
 	bool ResourceManifest::uuidExists(const String& uuid) const
@@ -79,6 +114,68 @@ namespace CamelotFramework
 		return iterFind != mFilePathToUUID.end();
 		return iterFind != mFilePathToUUID.end();
 	}
 	}
 
 
+	void ResourceManifest::save(const ResourceManifestPtr& manifest, const WString& path, const WString& relativePath)
+	{
+		WString standRelativePath = Path::standardizePath(relativePath);
+		StringUtil::toLowerCase(standRelativePath);
+
+		ResourceManifestPtr copy = create(manifest->mName);
+
+		for(auto& elem : manifest->mFilePathToUUID)
+		{
+			if(!Path::includes(elem.first, standRelativePath))
+			{
+				CM_EXCEPT(InvalidStateException, "Path in resource manifest cannot be made relative to: \"" + 
+					toString(relativePath) + "\". Path: \"" + toString(elem.first) + "\"");
+			}
+
+			WString relativePath = Path::relative(standRelativePath, elem.first);
+
+			copy->mFilePathToUUID[relativePath] = elem.second;
+		}
+
+		for(auto& elem : manifest->mUUIDToFilePath)
+		{
+			if(!Path::includes(elem.second, standRelativePath))
+			{
+				CM_EXCEPT(InvalidStateException, "Path in resource manifest cannot be made relative to: \"" + 
+					toString(relativePath) + "\". Path: \"" + toString(elem.second) + "\"");
+			}
+
+			WString relativePath = Path::relative(standRelativePath, elem.second);
+
+			copy->mUUIDToFilePath[elem.first] = relativePath;
+		}
+
+		FileSerializer fs;
+		fs.encode(copy.get(), path);
+	}
+
+	ResourceManifestPtr ResourceManifest::load(const WString& path, const WString& relativePath)
+	{
+		WString standRelativePath = Path::standardizePath(relativePath);
+		StringUtil::toLowerCase(standRelativePath);
+
+		FileSerializer fs;
+		ResourceManifestPtr manifest = std::static_pointer_cast<ResourceManifest>(fs.decode(path));
+
+		ResourceManifestPtr copy = create(manifest->mName);
+
+		for(auto& elem : manifest->mFilePathToUUID)
+		{
+			WString absPath = Path::combine(standRelativePath, elem.first);
+			copy->mFilePathToUUID[absPath] = elem.second;
+		}
+
+		for(auto& elem : manifest->mUUIDToFilePath)
+		{
+			WString absPath = Path::combine(standRelativePath, elem.second);
+			copy->mUUIDToFilePath[elem.first] = absPath;
+		}
+
+		return copy;
+	}
+
 	RTTITypeBase* ResourceManifest::getRTTIStatic()
 	RTTITypeBase* ResourceManifest::getRTTIStatic()
 	{
 	{
 		return ResourceManifestRTTI::instance();
 		return ResourceManifestRTTI::instance();

+ 82 - 13
CamelotCore/Source/CmResources.cpp

@@ -58,7 +58,9 @@ namespace CamelotFramework
 	Resources::Resources()
 	Resources::Resources()
 		:mRequestHandler(nullptr), mResponseHandler(nullptr), mWorkQueue(nullptr)
 		:mRequestHandler(nullptr), mResponseHandler(nullptr), mWorkQueue(nullptr)
 	{
 	{
-		mResourceManifest = cm_shared_ptr<ResourceManifest>();
+		mDefaultResourceManifest = ResourceManifest::create("Default");
+		mResourceManifests.push_back(mDefaultResourceManifest);
+
 		mWorkQueue = cm_new<WorkQueue>();
 		mWorkQueue = cm_new<WorkQueue>();
 		mWorkQueueChannel = mWorkQueue->getChannel("Resources");
 		mWorkQueueChannel = mWorkQueue->getChannel("Resources");
 		mRequestHandler = cm_new<ResourceRequestHandler>();
 		mRequestHandler = cm_new<ResourceRequestHandler>();
@@ -108,35 +110,67 @@ namespace CamelotFramework
 
 
 	HResource Resources::loadFromUUID(const String& uuid)
 	HResource Resources::loadFromUUID(const String& uuid)
 	{
 	{
-		if(!mResourceManifest->uuidExists(uuid))
+		WString filePath;
+		bool foundPath = false;
+
+		// Default manifest is at 0th index but all other take priority since Default manifest could
+		// contain obsolete data. 
+		for(auto iter = mResourceManifests.rbegin(); iter != mResourceManifests.rend(); ++iter) 
+		{
+			if((*iter)->uuidToFilePath(uuid, filePath))
+			{
+				foundPath = true;
+				break;
+			}
+		}
+
+		if(!foundPath)
 		{
 		{
 			gDebug().logWarning("Cannot load resource. Resource with UUID '" + uuid + "' doesn't exist.");
 			gDebug().logWarning("Cannot load resource. Resource with UUID '" + uuid + "' doesn't exist.");
 			return HResource();
 			return HResource();
 		}
 		}
 
 
-		WString filePath = getPathFromUUID(uuid);
 		return load(filePath);
 		return load(filePath);
 	}
 	}
 
 
 	HResource Resources::loadFromUUIDAsync(const String& uuid)
 	HResource Resources::loadFromUUIDAsync(const String& uuid)
 	{
 	{
-		if(!mResourceManifest->uuidExists(uuid))
+		WString filePath;
+		bool foundPath = false;
+
+		for(auto iter = mResourceManifests.rbegin(); iter != mResourceManifests.rend(); ++iter) 
+		{
+			if((*iter)->uuidToFilePath(uuid, filePath))
+			{
+				foundPath = true;
+				break;
+			}
+		}
+
+		if(!foundPath)
 		{
 		{
 			gDebug().logWarning("Cannot load resource. Resource with UUID '" + uuid + "' doesn't exist.");
 			gDebug().logWarning("Cannot load resource. Resource with UUID '" + uuid + "' doesn't exist.");
 			return HResource();
 			return HResource();
 		}
 		}
 
 
-		WString filePath = getPathFromUUID(uuid);
 		return loadAsync(filePath);
 		return loadAsync(filePath);
 	}
 	}
 
 
 	HResource Resources::loadInternal(const WString& filePath, bool synchronous)
 	HResource Resources::loadInternal(const WString& filePath, bool synchronous)
 	{
 	{
 		String uuid;
 		String uuid;
-		if(!mResourceManifest->filePathExists(filePath))
+		bool foundUUID = false;
+		for(auto iter = mResourceManifests.rbegin(); iter != mResourceManifests.rend(); ++iter) 
+		{
+			if((*iter)->filePathToUUID(filePath, uuid))
+			{
+				foundUUID = true;
+				break;
+			}
+		}
+
+		if(!foundUUID)
 			uuid = UUIDGenerator::generateRandom();
 			uuid = UUIDGenerator::generateRandom();
-		else
-			uuid = getUUIDFromPath(filePath);
 
 
 		{
 		{
 			CM_LOCK_MUTEX(mLoadedResourceMutex);
 			CM_LOCK_MUTEX(mLoadedResourceMutex);
@@ -260,12 +294,35 @@ namespace CamelotFramework
 				CM_EXCEPT(InvalidParametersException, "Another file exists at the specified location.");
 				CM_EXCEPT(InvalidParametersException, "Another file exists at the specified location.");
 		}
 		}
 
 
-		mResourceManifest->registerResource(resource.getUUID(), filePath);
+		mDefaultResourceManifest->registerResource(resource.getUUID(), filePath);
 
 
 		FileSerializer fs;
 		FileSerializer fs;
 		fs.encode(resource.get(), filePath);
 		fs.encode(resource.get(), filePath);
 	}
 	}
 
 
+	void Resources::registerResourceManifest(const ResourceManifestPtr& manifest)
+	{
+		if(manifest->getName() == "Default")
+			return;
+
+		auto findIter = std::find(mResourceManifests.begin(), mResourceManifests.end(), manifest);
+		if(findIter == mResourceManifests.end())
+			mResourceManifests.push_back(manifest);
+		else
+			*findIter = manifest;
+	}
+
+	ResourceManifestPtr Resources::getResourceManifest(const String& name) const
+	{
+		for(auto iter = mResourceManifests.rbegin(); iter != mResourceManifests.rend(); ++iter) 
+		{
+			if(name == (*iter)->getName())
+				return (*iter);
+		}
+
+		return nullptr;
+	}
+
 	HResource Resources::createResourceHandle(const ResourcePtr& obj)
 	HResource Resources::createResourceHandle(const ResourcePtr& obj)
 	{
 	{
 		String uuid = UUIDGenerator::generateRandom();
 		String uuid = UUIDGenerator::generateRandom();
@@ -280,14 +337,26 @@ namespace CamelotFramework
 		return newHandle;
 		return newHandle;
 	}
 	}
 
 
-	const WString& Resources::getPathFromUUID(const String& uuid) const
+	bool Resources::getFilePathFromUUID(const String& uuid, WString& filePath) const
 	{
 	{
-		return mResourceManifest->uuidToFilePath(uuid);
+		for(auto iter = mResourceManifests.rbegin(); iter != mResourceManifests.rend(); ++iter) 
+		{
+			if((*iter)->uuidToFilePath(uuid, filePath))
+				return true;
+		}
+
+		return false;
 	}
 	}
 
 
-	const String& Resources::getUUIDFromPath(const WString& path) const
+	bool Resources::getUUIDFromFilePath(const WString& path, String& uuid) const
 	{
 	{
-		return mResourceManifest->filePathToUUID(path);
+		for(auto iter = mResourceManifests.rbegin(); iter != mResourceManifests.rend(); ++iter) 
+		{
+			if((*iter)->filePathToUUID(path, uuid))
+				return true;
+		}
+
+		return false;
 	}
 	}
 
 
 	void Resources::notifyResourceLoadingFinished(HResource& handle)
 	void Resources::notifyResourceLoadingFinished(HResource& handle)

+ 29 - 0
CamelotUtility/Include/CmPath.h

@@ -50,6 +50,9 @@ namespace CamelotFramework
 
 
 			for(; iterParent != parentPathElems.end(); ++iterChild, ++iterParent)
 			for(; iterParent != parentPathElems.end(); ++iterChild, ++iterParent)
 			{
 			{
+				if(iterChild == childPathElems.end())
+					return false;
+
 				if(!comparePathElements(*iterChild, *iterParent))
 				if(!comparePathElements(*iterChild, *iterParent))
 					return false;
 					return false;
 			}
 			}
@@ -57,6 +60,32 @@ namespace CamelotFramework
 			return true;
 			return true;
 		}
 		}
 
 
+		/**
+		 * @brief	Returns path relative to base.
+		 */
+		static WString relative(const WString& base, const WString& path)
+		{
+			Vector<WString>::type basePathElems = split(base);
+			Vector<WString>::type pathElems = split(path);
+
+			auto iterBase = basePathElems.begin();
+			auto iterPath = pathElems.begin();
+
+			for(; iterBase != basePathElems.end(); ++iterBase, ++iterPath)
+			{
+				if(!comparePathElements(*iterBase, *iterPath))
+					return L"";
+			}
+
+			WString relativePath;
+			for(; iterPath != pathElems.end(); ++iterPath)
+			{
+				relativePath = Path::combine(relativePath, *iterPath);
+			}
+
+			return relativePath;
+		}
+
 		static Vector<WString>::type split(const WString& path)
 		static Vector<WString>::type split(const WString& path)
 		{
 		{
 			Vector<WString>::type splitPath;
 			Vector<WString>::type splitPath;

+ 62 - 12
CamelotUtility/Include/CmRTTIPrerequisites.h

@@ -34,6 +34,26 @@ namespace CamelotFramework
 		return memory + rttiGetElemSize(data);
 		return memory + rttiGetElemSize(data);
 	}
 	}
 
 
+	/**
+	* @brief	Helper method when serializing known data types that have valid
+	* 			RTTIPlainType specializations.
+	* 			
+	*			Writes the specified data into memory, advances the memory pointer by the
+	*			bytes written and returns pointer to new memory. Also increases the size 
+	 *			value by the size of the written element.
+	 */
+	template<class ElemType>
+	char* rttiWriteElem(const ElemType& data, char* memory, UINT32& size)
+	{
+		RTTIPlainType<ElemType>::toMemory(data, memory);
+
+		UINT32 elemSize = rttiGetElemSize(data);
+		size += elemSize;
+
+		return memory + elemSize;
+	}
+
+
 	/**
 	/**
 	 * @brief	Helper method when serializing known data types that have valid
 	 * @brief	Helper method when serializing known data types that have valid
 	 * 			RTTIPlainType specializations.
 	 * 			RTTIPlainType specializations.
@@ -114,10 +134,14 @@ namespace CamelotFramework
 
 
 		static void toMemory(const std::vector<T, StdAlloc<T>>& data, char* memory)
 		static void toMemory(const std::vector<T, StdAlloc<T>>& data, char* memory)
 		{ 
 		{ 
-			UINT32 size = (UINT32)data.size();
+			UINT32 size = sizeof(UINT32);
+			char* memoryStart = memory;
+			memory += sizeof(UINT32);
 
 
-			memcpy(memory, &size, sizeof(UINT32));
+			UINT32 numElements = (UINT32)data.size();
+			memcpy(memory, &numElements, sizeof(UINT32));
 			memory += sizeof(UINT32);
 			memory += sizeof(UINT32);
+			size += sizeof(UINT32);
 
 
 			for(auto iter = data.begin(); iter != data.end(); ++iter)
 			for(auto iter = data.begin(); iter != data.end(); ++iter)
 			{
 			{
@@ -125,16 +149,21 @@ namespace CamelotFramework
 				RTTIPlainType<T>::toMemory(*iter, memory);
 				RTTIPlainType<T>::toMemory(*iter, memory);
 
 
 				memory += elementSize;
 				memory += elementSize;
+				size += elementSize;
 			}
 			}
+
+			memcpy(memoryStart, &size, sizeof(UINT32));
 		}
 		}
 
 
 		static UINT32 fromMemory(std::vector<T, StdAlloc<T>>& data, char* memory)
 		static UINT32 fromMemory(std::vector<T, StdAlloc<T>>& data, char* memory)
 		{ 
 		{ 
 			UINT32 size = 0;
 			UINT32 size = 0;
+			memcpy(&size, memory, sizeof(UINT32)); 
+			memory += sizeof(UINT32);
+
 			UINT32 numElements;
 			UINT32 numElements;
 			memcpy(&numElements, memory, sizeof(UINT32)); 
 			memcpy(&numElements, memory, sizeof(UINT32)); 
 			memory += sizeof(UINT32);
 			memory += sizeof(UINT32);
-			size += sizeof(UINT32);
 
 
 			for(UINT32 i = 0; i < numElements; i++)
 			for(UINT32 i = 0; i < numElements; i++)
 			{
 			{
@@ -143,7 +172,6 @@ namespace CamelotFramework
 				data.push_back(element);
 				data.push_back(element);
 
 
 				memory += elementSize;
 				memory += elementSize;
-				size += elementSize;
 			}
 			}
 
 
 			return size;
 			return size;
@@ -151,7 +179,7 @@ namespace CamelotFramework
 
 
 		static UINT32 getDynamicSize(const std::vector<T, StdAlloc<T>>& data)	
 		static UINT32 getDynamicSize(const std::vector<T, StdAlloc<T>>& data)	
 		{ 
 		{ 
-			UINT64 dataSize = sizeof(UINT32);
+			UINT64 dataSize = sizeof(UINT32) * 2;
 
 
 			for(auto iter = data.begin(); iter != data.end(); ++iter)
 			for(auto iter = data.begin(); iter != data.end(); ++iter)
 				dataSize += RTTIPlainType<T>::getDynamicSize(*iter);		
 				dataSize += RTTIPlainType<T>::getDynamicSize(*iter);		
@@ -168,10 +196,14 @@ namespace CamelotFramework
 
 
 		static void toMemory(const std::map<Key, Value, std::less<Key>, StdAlloc<std::pair<const Key, Value>>>& data, char* memory)
 		static void toMemory(const std::map<Key, Value, std::less<Key>, StdAlloc<std::pair<const Key, Value>>>& data, char* memory)
 		{ 
 		{ 
-			UINT32 size = (UINT32)data.size();
+			UINT32 size = sizeof(UINT32);
+			char* memoryStart = memory;
+			memory += sizeof(UINT32);
 
 
-			memcpy(memory, &size, sizeof(UINT32));
+			UINT32 numElements = (UINT32)data.size();
+			memcpy(memory, &numElements, sizeof(UINT32));
 			memory += sizeof(UINT32);
 			memory += sizeof(UINT32);
+			size += sizeof(UINT32);
 
 
 			for(auto iter = data.begin(); iter != data.end(); ++iter)
 			for(auto iter = data.begin(); iter != data.end(); ++iter)
 			{
 			{
@@ -179,21 +211,27 @@ namespace CamelotFramework
 				RTTIPlainType<Key>::toMemory(iter->first, memory);
 				RTTIPlainType<Key>::toMemory(iter->first, memory);
 
 
 				memory += keySize;
 				memory += keySize;
+				size += keySize;
 
 
 				UINT32 valueSize = RTTIPlainType<Value>::getDynamicSize(iter->second);
 				UINT32 valueSize = RTTIPlainType<Value>::getDynamicSize(iter->second);
 				RTTIPlainType<Value>::toMemory(iter->second, memory);
 				RTTIPlainType<Value>::toMemory(iter->second, memory);
 
 
 				memory += valueSize;
 				memory += valueSize;
+				size += valueSize;
 			}
 			}
+
+			memcpy(memoryStart, &size, sizeof(UINT32));
 		}
 		}
 
 
 		static UINT32 fromMemory(std::map<Key, Value, std::less<Key>, StdAlloc<std::pair<const Key, Value>>>& data, char* memory)
 		static UINT32 fromMemory(std::map<Key, Value, std::less<Key>, StdAlloc<std::pair<const Key, Value>>>& data, char* memory)
 		{ 
 		{ 
 			UINT32 size = 0;
 			UINT32 size = 0;
+			memcpy(&size, memory, sizeof(UINT32)); 
+			memory += sizeof(UINT32);
+
 			UINT32 numElements;
 			UINT32 numElements;
 			memcpy(&numElements, memory, sizeof(UINT32)); 
 			memcpy(&numElements, memory, sizeof(UINT32)); 
 			memory += sizeof(UINT32);
 			memory += sizeof(UINT32);
-			size += sizeof(UINT32);
 
 
 			for(UINT32 i = 0; i < numElements; i++)
 			for(UINT32 i = 0; i < numElements; i++)
 			{
 			{
@@ -206,7 +244,6 @@ namespace CamelotFramework
 				memory += valueSize;
 				memory += valueSize;
 
 
 				data[key] = value; 
 				data[key] = value; 
-				size += keySize + valueSize;
 			}
 			}
 
 
 			return size;
 			return size;
@@ -214,7 +251,7 @@ namespace CamelotFramework
 
 
 		static UINT32 getDynamicSize(const std::map<Key, Value, std::less<Key>, StdAlloc<std::pair<const Key, Value>>>& data)	
 		static UINT32 getDynamicSize(const std::map<Key, Value, std::less<Key>, StdAlloc<std::pair<const Key, Value>>>& data)	
 		{ 
 		{ 
-			UINT64 dataSize = sizeof(UINT32);
+			UINT64 dataSize = sizeof(UINT32) * 2;
 
 
 			for(auto iter = data.begin(); iter != data.end(); ++iter)
 			for(auto iter = data.begin(); iter != data.end(); ++iter)
 			{
 			{
@@ -234,31 +271,44 @@ namespace CamelotFramework
 
 
 		static void toMemory(const std::pair<A, B>& data, char* memory)
 		static void toMemory(const std::pair<A, B>& data, char* memory)
 		{ 
 		{ 
+			UINT32 size = sizeof(UINT32);
+			char* memoryStart = memory;
+			memory += sizeof(UINT32);
+
 			UINT32 firstSize = RTTIPlainType<A>::getDynamicSize(data.first);
 			UINT32 firstSize = RTTIPlainType<A>::getDynamicSize(data.first);
 			RTTIPlainType<A>::toMemory(data.first, memory);
 			RTTIPlainType<A>::toMemory(data.first, memory);
 
 
 			memory += firstSize;
 			memory += firstSize;
+			size += firstSize;
 
 
 			UINT32 secondSize = RTTIPlainType<B>::getDynamicSize(data.second);
 			UINT32 secondSize = RTTIPlainType<B>::getDynamicSize(data.second);
 			RTTIPlainType<B>::toMemory(data.second, memory);
 			RTTIPlainType<B>::toMemory(data.second, memory);
 
 
 			memory += secondSize;
 			memory += secondSize;
+			size += secondSize;
+
+			memcpy(memoryStart, &size, sizeof(UINT32));
 		}
 		}
 
 
 		static UINT32 fromMemory(std::pair<A, B>& data, char* memory)
 		static UINT32 fromMemory(std::pair<A, B>& data, char* memory)
 		{ 
 		{ 
+			UINT32 size = 0;
+			memcpy(&size, memory, sizeof(UINT32));
+			memory += sizeof(UINT32);
+
 			UINT32 firstSize = RTTIPlainType<A>::fromMemory(data.first, memory);
 			UINT32 firstSize = RTTIPlainType<A>::fromMemory(data.first, memory);
 			memory += firstSize;
 			memory += firstSize;
 
 
 			UINT32 secondSize = RTTIPlainType<B>::fromMemory(data.second, memory);
 			UINT32 secondSize = RTTIPlainType<B>::fromMemory(data.second, memory);
 			memory += secondSize;
 			memory += secondSize;
 
 
-			return firstSize + secondSize;
+			return size;
 		}
 		}
 
 
 		static UINT32 getDynamicSize(const std::pair<A, B>& data)	
 		static UINT32 getDynamicSize(const std::pair<A, B>& data)	
 		{ 
 		{ 
-			UINT64 dataSize = RTTIPlainType<A>::getDynamicSize(data.first);		
+			UINT64 dataSize = sizeof(UINT32);
+			dataSize += RTTIPlainType<A>::getDynamicSize(data.first);		
 			dataSize += RTTIPlainType<B>::getDynamicSize(data.second);
 			dataSize += RTTIPlainType<B>::getDynamicSize(data.second);
 
 
 			assert(dataSize <= std::numeric_limits<UINT32>::max());
 			assert(dataSize <= std::numeric_limits<UINT32>::max());

+ 5 - 2
CamelotUtility/Include/CmString.h

@@ -825,9 +825,12 @@ namespace CamelotFramework
 			memory += sizeof(UINT32);
 			memory += sizeof(UINT32);
 
 
 			UINT32 stringSize = size - sizeof(UINT32);
 			UINT32 stringSize = size - sizeof(UINT32);
-			WString::value_type* buffer = (WString::value_type*)cm_alloc<ScratchAlloc>(stringSize + 1);
+			WString::value_type* buffer = (WString::value_type*)cm_alloc<ScratchAlloc>(stringSize + sizeof(WString::value_type));
 			memcpy(buffer, memory, stringSize); 
 			memcpy(buffer, memory, stringSize); 
-			buffer[stringSize] = '\0';
+
+			UINT32 numChars =  stringSize / sizeof(WString::value_type);
+			buffer[numChars] = L'\0';
+
 			data = WString(buffer);
 			data = WString(buffer);
 
 
 			cm_free<ScratchAlloc>(buffer);
 			cm_free<ScratchAlloc>(buffer);

+ 1 - 10
ProjectLibrary.txt

@@ -1,17 +1,8 @@
 
 
-TODO
- - ProjectLibrary Save/Load - saves/loads the hierarchy of ResourceEntries
-    - ResourceManifest needs to store relative locations when I save it, so that user can move Project folder safely
  - Resource import queue (ability to display progress bar for resources that are importing)
  - Resource import queue (ability to display progress bar for resources that are importing)
+  - Plus modal windows?
  - Implement Delete command for ResourceTreeView
  - Implement Delete command for ResourceTreeView
 
 
---------------------
-
-Test:
- - Save/Load (After it is implemented)
-     - Ensure that leaving application and then reloading will still recognize previously saved assets and will not reimport them
-       - Also ensure that UUIDs will be properly read from .meta files
-
 -------------------
 -------------------
 
 
 LOW PRIORITY
 LOW PRIORITY