Browse Source

WIP C# ProjectLibrary

Marko Pintera 10 years ago
parent
commit
ac7355e56c

+ 7 - 3
BansheeEditor/Include/BsProjectLibrary.h

@@ -62,8 +62,12 @@ namespace BansheeEngine
 		Path uuidToPath(const String& uuid) const;
 
 		void createEntry(const HResource& resource, const Path& path);
-		void moveEntry(const Path& oldPath, const Path& newPath);
+		void createFolderEntry(const Path& path);
+		void saveEntry(const HResource& resource);
+		void moveEntry(const Path& oldPath, const Path& newPath, bool overwrite = true);
+		void copyEntry(const Path& oldPath, const Path& newPath, bool overwrite = true);
 		void deleteEntry(const Path& path);
+		void reimport(const Path& path, const ImportOptionsPtr& importOptions = nullptr, bool forceReimport = false);
 
 		const Path& getResourcesFolder() const { return mResourcesFolder; }
 
@@ -84,13 +88,13 @@ namespace BansheeEngine
 		void save();
 		void load();
 
-		ResourceEntry* addResourceInternal(DirectoryEntry* parent, const Path& filePath);
+		ResourceEntry* addResourceInternal(DirectoryEntry* parent, const Path& filePath, const ImportOptionsPtr& importOptions = nullptr, bool forceReimport = false);
 		DirectoryEntry* addDirectoryInternal(DirectoryEntry* parent, const Path& dirPath);
 
 		void deleteResourceInternal(ResourceEntry* resource);
 		void deleteDirectoryInternal(DirectoryEntry* directory);
 
-		void reimportResourceInternal(ResourceEntry* resource);
+		void reimportResourceInternal(ResourceEntry* resource, const ImportOptionsPtr& importOptions = nullptr, bool forceReimport = false);
 
 		void createInternalParentHierarchy(const Path& fullPath, DirectoryEntry** newHierarchyRoot, DirectoryEntry** newHierarchyLeaf);
 

+ 156 - 14
BansheeEditor/Source/BsProjectLibrary.cpp

@@ -260,12 +260,13 @@ namespace BansheeEngine
 		}
 	}
 
-	ProjectLibrary::ResourceEntry* ProjectLibrary::addResourceInternal(DirectoryEntry* parent, const Path& filePath)
+	ProjectLibrary::ResourceEntry* ProjectLibrary::addResourceInternal(DirectoryEntry* parent, const Path& filePath, 
+		const ImportOptionsPtr& importOptions, bool forceReimport)
 	{
 		ResourceEntry* newResource = bs_new<ResourceEntry>(filePath, filePath.getWTail(), parent);
 		parent->mChildren.push_back(newResource);
 
-		reimportResourceInternal(newResource);
+		reimportResourceInternal(newResource, importOptions, forceReimport);
 
 		if(!onEntryAdded.empty())
 			onEntryAdded(filePath);
@@ -339,7 +340,7 @@ namespace BansheeEngine
 		bs_delete(directory);
 	}
 
-	void ProjectLibrary::reimportResourceInternal(ResourceEntry* resource)
+	void ProjectLibrary::reimportResourceInternal(ResourceEntry* resource, const ImportOptionsPtr& importOptions, bool forceReimport)
 	{
 		WString ext = resource->path.getWExtension();
 		Path metaPath = resource->path;
@@ -364,32 +365,37 @@ namespace BansheeEngine
 			}
 		}
 
-		if(!isUpToDate(resource))
+		if (!isUpToDate(resource) || forceReimport)
 		{
-			ImportOptionsPtr importOptions = nullptr;
+			ImportOptionsPtr curImportOptions = nullptr;
 
-			if(resource->meta != nullptr)
-				importOptions = resource->meta->getImportOptions();
+			if (importOptions == nullptr)
+			{
+				if (resource->meta != nullptr)
+					curImportOptions = resource->meta->getImportOptions();
+				else
+					curImportOptions = Importer::instance().createImportOptions(resource->path);
+			}
 			else
-				importOptions = Importer::instance().createImportOptions(resource->path);
+				curImportOptions = importOptions;
 
 			HResource importedResource;
 
 			if(resource->meta == nullptr)
 			{
-				importedResource = Importer::instance().import(resource->path, importOptions);
+				importedResource = Importer::instance().import(resource->path, curImportOptions);
 
 				ResourceMetaDataPtr subMeta = importedResource->getMetaData();
 				UINT32 typeId = importedResource->getTypeId();
 
-				resource->meta = ProjectResourceMeta::create(importedResource.getUUID(), typeId, subMeta, importOptions);
+				resource->meta = ProjectResourceMeta::create(importedResource.getUUID(), typeId, subMeta, curImportOptions);
 				FileEncoder fs(metaPath);
 				fs.encode(resource->meta.get());
 			}
 			else
 			{
 				importedResource = HResource(resource->meta->getUUID());
-				Importer::instance().reimport(importedResource, resource->path, importOptions);
+				Importer::instance().reimport(importedResource, resource->path, curImportOptions);
 			}
 
 			Path internalResourcesPath = mProjectFolder;
@@ -508,16 +514,55 @@ namespace BansheeEngine
 
 		LibraryEntry* existingEntry = findEntry(assetPath);
 		if (existingEntry != nullptr)
-			BS_EXCEPT(InvalidParametersException, "Existing resource already exists at the specified path: " + assetPath.toString());
+			BS_EXCEPT(InvalidParametersException, "Resource already exists at the specified path: " + assetPath.toString());
 
 		Resources::instance().save(resource, assetPath, false);
 		checkForModifications(assetPath);
 	}
 
-	void ProjectLibrary::moveEntry(const Path& oldPath, const Path& newPath)
+	void ProjectLibrary::saveEntry(const HResource& resource)
+	{
+		if (resource == nullptr)
+			return;
+
+		Path filePath;
+		if (!mResourceManifest->uuidToFilePath(resource.getUUID(), filePath))
+			return;
+
+		Resources::instance().save(resource, filePath, false);
+	}
+
+	void ProjectLibrary::createFolderEntry(const Path& path)
+	{
+		if (FileSystem::isDirectory(path))
+			return; // Already exists
+
+		FileSystem::createDir(path);
+
+		if (!mResourcesFolder.includes(path))
+			return;
+
+		Path parentPath = path.getParent();
+
+		DirectoryEntry* newEntryParent = nullptr;
+		LibraryEntry* newEntryParentLib = findEntry(parentPath);
+		if (newEntryParentLib != nullptr)
+		{
+			assert(newEntryParentLib->type == LibraryEntryType::Directory);
+			newEntryParent = static_cast<DirectoryEntry*>(newEntryParentLib);
+		}
+
+		DirectoryEntry* newHierarchyParent = nullptr;
+		if (newEntryParent == nullptr) // New path parent doesn't exist, so we need to create the hierarchy
+			createInternalParentHierarchy(path, &newHierarchyParent, &newEntryParent);
+
+		addDirectoryInternal(newEntryParent, path);
+	}
+
+	void ProjectLibrary::moveEntry(const Path& oldPath, const Path& newPath, bool overwrite)
 	{
 		if(FileSystem::isFile(oldPath) || FileSystem::isDirectory(oldPath))
-			FileSystem::move(oldPath, newPath);
+			FileSystem::move(oldPath, newPath, overwrite);
 
 		Path oldMetaPath = getMetaPath(oldPath);
 		Path newMetaPath = getMetaPath(newPath);
@@ -605,6 +650,90 @@ namespace BansheeEngine
 		}
 	}
 
+	void ProjectLibrary::copyEntry(const Path& oldPath, const Path& newPath, bool overwrite)
+	{
+		if (!FileSystem::exists(oldPath))
+			return;
+
+		FileSystem::copy(oldPath, newPath, overwrite);
+
+		// Copying a file/folder outside of the Resources folder, no special handling needed
+		if (!mResourcesFolder.includes(newPath))
+			return;
+
+		Path parentPath = newPath.getParent();
+
+		DirectoryEntry* newEntryParent = nullptr;
+		LibraryEntry* newEntryParentLib = findEntry(parentPath);
+		if (newEntryParentLib != nullptr)
+		{
+			assert(newEntryParentLib->type == LibraryEntryType::Directory);
+			newEntryParent = static_cast<DirectoryEntry*>(newEntryParentLib);
+		}
+
+		DirectoryEntry* newHierarchyParent = nullptr;
+		if (newEntryParent == nullptr) // New path parent doesn't exist, so we need to create the hierarchy
+			createInternalParentHierarchy(newPath, &newHierarchyParent, &newEntryParent);
+
+		// If the source is outside of Resources folder, just plain import the copy
+		LibraryEntry* oldEntry = findEntry(oldPath);
+		if (oldEntry == nullptr)
+		{
+			checkForModifications(newHierarchyParent->path);
+			return;
+		}
+
+		// Both source and destination are within Resources folder, need to preserve import options on the copies
+		LibraryEntry* newEntry = nullptr;
+		if (FileSystem::isFile(newPath))
+		{
+			assert(oldEntry->type == LibraryEntryType::File);
+			ResourceEntry* oldResEntry = static_cast<ResourceEntry*>(oldEntry);
+
+			newEntry = addResourceInternal(newEntryParent, newPath, oldResEntry->meta->getImportOptions(), true);
+		}
+		else
+		{
+			assert(oldEntry->type == LibraryEntryType::File);
+			DirectoryEntry* oldDirEntry = static_cast<DirectoryEntry*>(oldEntry);
+
+			DirectoryEntry* newDirEntry = addDirectoryInternal(newEntryParent, newPath);
+			newEntry = newDirEntry;
+
+			Stack<std::tuple<DirectoryEntry*, DirectoryEntry*>> todo;
+			todo.push(std::make_tuple(oldDirEntry, newDirEntry));
+
+			while (!todo.empty())
+			{
+				auto current = todo.top();
+				todo.pop();
+
+				DirectoryEntry* sourceDir = std::get<0>(current);
+				DirectoryEntry* destDir = std::get<1>(current);
+
+				for (auto& child : sourceDir->mChildren)
+				{
+					Path childDestPath = destDir->path;
+					childDestPath.append(child->path.getWTail());
+
+					if (child->type == LibraryEntryType::File)
+					{
+						ResourceEntry* childResEntry = static_cast<ResourceEntry*>(child);
+
+						addResourceInternal(destDir, childDestPath, childResEntry->meta->getImportOptions(), true);
+					}
+					else // Directory
+					{
+						DirectoryEntry* childSourceDirEntry = static_cast<DirectoryEntry*>(child);
+						DirectoryEntry* childDestDirEntry = addDirectoryInternal(destDir, childDestPath);
+
+						todo.push(std::make_tuple(childSourceDirEntry, childSourceDirEntry));
+					}
+				}
+			}
+		}
+	}
+
 	void ProjectLibrary::deleteEntry(const Path& path)
 	{
 		if(FileSystem::exists(path))
@@ -626,6 +755,19 @@ namespace BansheeEngine
 		}
 	}
 
+	void ProjectLibrary::reimport(const Path& path, const ImportOptionsPtr& importOptions = nullptr, bool forceReimport = false)
+	{
+		LibraryEntry* entry = findEntry(path);
+		if (entry != nullptr)
+		{
+			if (entry->type == LibraryEntryType::File)
+			{
+				ResourceEntry* resEntry = static_cast<ResourceEntry*>(entry);
+				reimportResourceInternal(resEntry, importOptions, forceReimport);
+			}
+		}
+	}
+
 	void ProjectLibrary::createInternalParentHierarchy(const Path& fullPath, DirectoryEntry** newHierarchyRoot, DirectoryEntry** newHierarchyLeaf)
 	{
 		Path parentPath = fullPath;

+ 10 - 0
BansheeUtility/Include/BsFileSystem.h

@@ -54,6 +54,16 @@ namespace BansheeEngine
 		 */
 		static void move(const Path& oldPath, const Path& newPath, bool overwriteExisting = true);
 
+		/**
+		 * @brief	Makes a copy of a file or a folder in the specified path. 
+		 *
+		 * @param	oldPath			 	Full path to the old file/folder.
+		 * @param	newPath			 	Full path to the new file/folder.
+		 * @param	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);
+
 		/**
 		 * @brief	Creates a folder at the specified path.
 		 *

+ 80 - 2
BansheeUtility/Source/Win32/BsFileSystem.cpp

@@ -184,13 +184,12 @@ namespace BansheeEngine
 		}
 	}
 
-	void win32_copy(const WString& from, const WString& to)
+	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)
@@ -330,6 +329,85 @@ namespace BansheeEngine
 		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
+					{
+						BS_EXCEPT(InvalidStateException, "Copy operation failed because another file already exists at the new path: \"" + toString(destPathStr) + "\"");
+					}
+				}
+			}
+
+			bool destIsFile = !destinationPath.getWExtension().empty();
+
+			if (!srcIsFile && destIsFile)
+			{
+				BS_EXCEPT(InvalidStateException, "Cannot copy a source folder to a destination file.");
+			}
+			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());

+ 176 - 0
MBansheeEditor/ImportOptions.cs

@@ -0,0 +1,176 @@
+using System;
+using System.Runtime.CompilerServices;
+using BansheeEngine;
+
+namespace BansheeEditor
+{
+    public class ImportOptions : ScriptObject
+    {
+
+    }
+
+    public class TextureImportOptions : ImportOptions
+    {
+        public TextureImportOptions()
+        {
+            Internal_CreateInstance(this);
+        }
+
+        public PixelFormat Format
+        {
+            get { return Internal_GetPixelFormat(mCachedPtr); }
+            set { Internal_SetPixelFormat(mCachedPtr, value); }
+        }
+
+        public bool GenerateMipmaps
+        {
+            get { return Internal_GetGenerateMipmaps(mCachedPtr); }
+            set { Internal_SetGenerateMipmaps(mCachedPtr, value); }
+        }
+
+        public UInt32 MaxMipmapLevel
+        {
+            get { return Internal_GetMaxMipmapLevel(mCachedPtr); }
+            set { Internal_SetMaxMipmapLevel(mCachedPtr, value); }
+        }
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern void Internal_CreateInstance(TextureImportOptions instance);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern PixelFormat Internal_GetPixelFormat(IntPtr thisPtr);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern void Internal_SetPixelFormat(IntPtr thisPtr, PixelFormat value);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern bool Internal_GetGenerateMipmaps(IntPtr thisPtr);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern void Internal_SetGenerateMipmaps(IntPtr thisPtr, bool value);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern UInt32 Internal_GetMaxMipmapLevel(IntPtr thisPtr);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern void Internal_SetMaxMipmapLevel(IntPtr thisPtr, UInt32 value);
+    }
+
+    public class GpuProgramImportOptions : ImportOptions
+    {
+        public GpuProgramImportOptions()
+        {
+            Internal_CreateInstance(this);
+        }
+
+        public string EntryPoint
+        {
+            get { return Internal_GetEntryPoint(mCachedPtr); }
+            set { Internal_SetEntryPoint(mCachedPtr, value); }
+        }
+
+        public GpuLanguage Language
+        {
+            get { return Internal_GetLanguage(mCachedPtr); }
+            set { Internal_SetLanguage(mCachedPtr, value); }
+        }
+
+        public GpuProgramProfile Profile
+        {
+            get { return Internal_GetProfile(mCachedPtr); }
+            set { Internal_SetProfile(mCachedPtr, value); }
+        }
+
+        public GpuProgramType Type
+        {
+            get { return Internal_GetType(mCachedPtr); }
+            set { Internal_SetType(mCachedPtr, value); }
+        }
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern void Internal_CreateInstance(GpuProgramImportOptions instance);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern string Internal_GetEntryPoint(IntPtr thisPtr);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern void Internal_SetEntryPoint(IntPtr thisPtr, string value);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern GpuLanguage Internal_GetLanguage(IntPtr thisPtr);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern void Internal_SetLanguage(IntPtr thisPtr, GpuLanguage value);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern GpuProgramProfile Internal_GetProfile(IntPtr thisPtr);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern void Internal_SetProfile(IntPtr thisPtr, GpuProgramProfile value);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern GpuProgramType Internal_GetType(IntPtr thisPtr);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern void Internal_SetType(IntPtr thisPtr, GpuProgramType value);
+    }
+
+    public class FontImportOptions : ImportOptions
+    {
+        public FontImportOptions()
+        {
+            Internal_CreateInstance(this);
+        }
+
+        public UInt32[] FontSizes
+        {
+            get { return Internal_GetFontSizes(mCachedPtr); }
+            set { Internal_SetFontSizes(mCachedPtr, value); }
+        }
+
+        public UInt32 DPI
+        {
+            get { return Internal_GetDPI(mCachedPtr); }
+            set { Internal_SetDPI(mCachedPtr, value); }
+        }
+
+        public bool Antialiasing
+        {
+            get { return Internal_GetAntialiasing(mCachedPtr); }
+            set { Internal_SetAntialiasing(mCachedPtr, value); }
+        }
+
+        public CharRange[] CharRanges
+        {
+            get { return Internal_GetCharRanges(mCachedPtr); }
+            set { Internal_SetCharRanges(mCachedPtr, value); }
+        }
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern void Internal_CreateInstance(FontImportOptions instance);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern UInt32[] Internal_GetFontSizes(IntPtr thisPtr);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern void Internal_SetFontSizes(IntPtr thisPtr, UInt32[] value);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern UInt32 Internal_GetDPI(IntPtr thisPtr);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern void Internal_SetDPI(IntPtr thisPtr, UInt32 value);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern bool Internal_GetAntialiasing(IntPtr thisPtr);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern void Internal_SetAntialiasing(IntPtr thisPtr, bool value);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern CharRange[] Internal_GetCharRanges(IntPtr thisPtr);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern void Internal_SetCharRanges(IntPtr thisPtr, CharRange[] value);
+    }
+}

+ 1 - 0
MBansheeEditor/MBansheeEditor.csproj

@@ -70,6 +70,7 @@
     <Compile Include="GUI\GUIToggleField.cs" />
     <Compile Include="GUI\GUIVector2Field.cs" />
     <Compile Include="GUI\GUIVector4Field.cs" />
+    <Compile Include="ImportOptions.cs" />
     <Compile Include="Inspector\CustomInspector.cs" />
     <Compile Include="Inspector\GenericInspector.cs" />
     <Compile Include="Inspector\InspectableArray.cs" />

+ 182 - 10
MBansheeEditor/ProjectLibrary.cs

@@ -5,8 +5,10 @@ using BansheeEngine;
 
 namespace BansheeEditor
 {
-    public sealed class ProjectLibrary
+    public sealed class ProjectLibrary : ScriptObject
     {
+        public static DirectoryEntry Root { get { return Internal_GetRoot(); } }
+
         public static void Create(Resource resource, string path)
         {
             if (Path.IsPathRooted(path))
@@ -15,6 +17,11 @@ namespace BansheeEditor
             Internal_Create(resource, path);
         }
 
+        public static void Save(Resource resource)
+        {
+            Internal_Save(resource);
+        }
+
         public static T Load<T>(string path) where T : Resource
         {
             if (Path.IsPathRooted(path))
@@ -23,20 +30,185 @@ namespace BansheeEditor
             return (T) Internal_Load(path);
         }
 
-        // TODO - Will also need (at least):
-        // - GetPath
-        // - Reimport
-        // - Load
-        // - Move
-        // - Rename
-        // - Delete
-        // - Copy
-        // - CreateFolder
+        public static void Reimport(string path, ImportOptions options = null, bool force = false)
+        {
+            Internal_Reimport(path, options, force);
+        }
+
+        public static bool Exists(string path)
+        {
+            return GetEntry(path) != null;
+        }
+
+        public static LibraryEntry GetEntry(string path)
+        {
+            return Internal_GetEntry(path);
+        }
+
+        public static string GetPath(Resource resource)
+        {
+            return Internal_GetPath(resource);
+        }
+
+        public static void Delete(string path)
+        {
+            Internal_Delete(path);
+        }
+
+        public static void CreateFolder(string path)
+        {
+            Internal_CreateFolder(path);
+        }
+
+        public static void Rename(string path, string name)
+        {
+            Internal_Rename(path, name);
+        }
+
+        public static void Move(string oldPath, string newPath, bool overwrite = false)
+        {
+            Internal_Move(oldPath, newPath, overwrite);
+        }
+
+        public static void Copy(string source, string destination, bool overwrite = false)
+        {
+            Internal_Copy(source, destination, overwrite);
+        }
 
         [MethodImpl(MethodImplOptions.InternalCall)]
         private static extern void Internal_Create(Resource resource, string path);
 
         [MethodImpl(MethodImplOptions.InternalCall)]
         private static extern Resource Internal_Load(string path);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern void Internal_Save(Resource resource);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern DirectoryEntry Internal_GetRoot();
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern void Internal_Reimport(string path, ImportOptions options, bool force);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern LibraryEntry Internal_GetEntry(string path);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern string Internal_GetPath(Resource resource);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern void Internal_Delete(string path);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern void Internal_CreateFolder(string path);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern void Internal_Rename(string path, string name);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern void Internal_Move(string oldPath, string newPath, bool overwrite);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern void Internal_Copy(string source, string destination, bool overwrite);
+    }
+
+    public enum LibraryEntryType
+    {
+        File, Directory
+    }
+
+    public enum ResourceType
+    {
+        Texture, SpriteTexture, Mesh, Font, GUISkin
+    }
+
+    public class LibraryEntry : ScriptObject
+    {
+        public String Path
+        {
+            get
+            {
+                // TODO
+                return "";
+            }
+        }
+
+        public String Name
+        {
+            get
+            {
+                // TODO
+                return "";
+            }
+        }
+
+        public LibraryEntryType Type
+        {
+            get
+            {
+                // TODO
+                return LibraryEntryType.File;
+            }
+        }
+
+        public DirectoryEntry Parent
+        {
+            get
+            {
+                // TODO
+                return null;
+            }
+        }
+    }
+
+    public class DirectoryEntry : LibraryEntry
+    {
+        public LibraryEntry[] Children
+        {
+            get
+            {
+                // TODO
+                return null;
+            }
+        }
+    }
+
+    public class FileEntry : LibraryEntry
+    {
+        public ImportOptions Options
+        {
+            get
+            {
+                // TODO
+                return null;
+            }
+        }
+
+        public string UUID
+        {
+            get
+            {
+                // TODO
+                return "";
+            }
+        }
+
+        public Texture2D Icon
+        {
+            get
+            {
+                // TODO
+                return null;
+            }
+        }
+
+        public ResourceType ResType
+        {
+            get
+            {
+                // TODO
+                return ResourceType.Texture;
+            }
+        }
     }
 }

+ 10 - 1
MBansheeEngine/Font.cs

@@ -1,7 +1,16 @@
-using System.Runtime.CompilerServices;
+using System;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
 
 namespace BansheeEngine
 {
+    [StructLayout(LayoutKind.Sequential)]
+    public struct CharRange
+    {
+        public UInt32 start;
+        public UInt32 end;
+    }
+
     public sealed class Font : Resource // TODO - Dummy class
     {
         internal Font(bool constructNative)

+ 58 - 0
MBansheeEngine/GpuProgram.cs

@@ -0,0 +1,58 @@
+namespace BansheeEngine
+{
+    public sealed class GpuProgram : Resource
+    {
+        // TODO - Dummy class
+    }
+
+    public enum GpuLanguage
+    {
+        HLSL9,
+        HLSL11,
+        GLSL
+    }
+
+    // Note: Must be equal to C++ enum GpuProgramType
+    public enum GpuProgramType
+    {
+        Vertex,
+        Fragment,
+        Geometry,
+        Domain,
+        Hull,
+        Compute
+    };
+
+    // Note: Must be equal to C++ enum GpuProgramProfile
+    public enum GpuProgramProfile
+    {
+        None,
+        Fragment_1_1,
+        Fragment_1_2,
+        Fragment_1_3,
+        Fragment_1_4,
+        Fragment_2_0,
+        Fragment_2_x,
+        Fragment_2_a,
+        Fragment_2_b,
+        Fragment_3_0,
+        Fragment_3_x,
+        Fragment_4_0,
+        Fragment_4_1,
+        Fragment_5_0,
+        Vertex_1_1,
+        Vertex_2_0,
+        Vertex_2_x,
+        Vertex_2_a,
+        Vertex_3_0,
+        Vertex_4_0,
+        Vertex_4_1,
+        Vertex_5_0,
+        Geometry_4_0,
+        Geometry_4_1,
+        Geometry_5_0,
+        Hull_5_0,
+        Domain_5_0,
+        Compute_5_0
+    };
+}

+ 1 - 0
MBansheeEngine/MBansheeEngine.csproj

@@ -57,6 +57,7 @@
     <Compile Include="DontSerializeField.cs" />
     <Compile Include="Font.cs" />
     <Compile Include="GameObject.cs" />
+    <Compile Include="GpuProgram.cs" />
     <Compile Include="GUI\GUIArea.cs" />
     <Compile Include="GUI\GUILayoutUtility.cs" />
     <Compile Include="GUI\GUIPanel.cs" />

+ 0 - 3
MBansheeEngine/Texture.cs

@@ -1,8 +1,5 @@
 using System;
-using System.Collections.Generic;
-using System.Linq;
 using System.Runtime.CompilerServices;
-using System.Text;
 
 namespace BansheeEngine
 {

+ 10 - 0
SBansheeEditor/Include/BsScriptProjectLibrary.h

@@ -14,6 +14,16 @@ namespace BansheeEngine
 	private:
 		static void internal_Create(MonoObject* resource, MonoString* path);
 		static MonoObject* internal_Load(MonoString* path);
+		static void internal_Save(MonoObject* resource);
+		static MonoObject* internal_GetRoot();
+		static void internal_Reimport(MonoString* path, MonoObject* options, bool force);
+		static MonoObject* internal_GetEntry(MonoString* path);
+		static MonoString* internal_GetPath(MonoObject* resource);
+		static void internal_Delete(MonoString* path);
+		static void internal_CreateFolder(MonoString* path);
+		static void internal_Rename(MonoString* path, MonoString* name);
+		static void internal_Move(MonoString* oldPath, MonoString* newPath, bool overwrite);
+		static void internal_Copy(MonoString* source, MonoString* destination, bool overwrite);
 
 		ScriptProjectLibrary(MonoObject* instance);
 	};

+ 71 - 0
SBansheeEditor/Source/BsScriptProjectLibrary.cpp

@@ -24,6 +24,16 @@ namespace BansheeEngine
 	{
 		metaData.scriptClass->addInternalCall("Internal_Create", &ScriptProjectLibrary::internal_Create);
 		metaData.scriptClass->addInternalCall("Internal_Load", &ScriptProjectLibrary::internal_Load);
+		metaData.scriptClass->addInternalCall("Internal_Save", &ScriptProjectLibrary::internal_Save);
+		metaData.scriptClass->addInternalCall("Internal_GetRoot", &ScriptProjectLibrary::internal_GetRoot);
+		metaData.scriptClass->addInternalCall("Internal_Reimport", &ScriptProjectLibrary::internal_Reimport);
+		metaData.scriptClass->addInternalCall("Internal_GetEntry", &ScriptProjectLibrary::internal_GetEntry);
+		metaData.scriptClass->addInternalCall("Internal_GetPath", &ScriptProjectLibrary::internal_GetPath);
+		metaData.scriptClass->addInternalCall("Internal_Delete", &ScriptProjectLibrary::internal_Delete);
+		metaData.scriptClass->addInternalCall("Internal_CreateFolder", &ScriptProjectLibrary::internal_CreateFolder);
+		metaData.scriptClass->addInternalCall("Internal_Rename", &ScriptProjectLibrary::internal_Rename);
+		metaData.scriptClass->addInternalCall("Internal_Move", &ScriptProjectLibrary::internal_Move);
+		metaData.scriptClass->addInternalCall("Internal_Copy", &ScriptProjectLibrary::internal_Copy);
 	}
 
 	void ScriptProjectLibrary::internal_Create(MonoObject* resource, MonoString* path)
@@ -59,4 +69,65 @@ namespace BansheeEngine
 
 		return scriptResource->getManagedInstance();
 	}
+
+	void ScriptProjectLibrary::internal_Save(MonoObject* resource)
+	{
+
+	}
+
+	MonoObject* ScriptProjectLibrary::internal_GetRoot()
+	{
+		// TODO
+	}
+
+	void ScriptProjectLibrary::internal_Reimport(MonoString* path, MonoObject* options, bool force)
+	{
+		// TODO
+	}
+
+	MonoObject* ScriptProjectLibrary::internal_GetEntry(MonoString* path)
+	{
+		// TODO
+	}
+
+	MonoString* ScriptProjectLibrary::internal_GetPath(MonoObject* resource)
+	{
+
+	}
+
+	void ScriptProjectLibrary::internal_Delete(MonoString* path)
+	{
+		Path pathToDelete = MonoUtil::monoToWString(path);
+		ProjectLibrary::instance().deleteEntry(pathToDelete);
+	}
+
+	void ScriptProjectLibrary::internal_CreateFolder(MonoString* path)
+	{
+		Path folderToCreate = MonoUtil::monoToWString(path);
+		ProjectLibrary::instance().createFolderEntry(folderToCreate);
+	}
+
+	void ScriptProjectLibrary::internal_Rename(MonoString* path, MonoString* name)
+	{
+		Path oldPath = MonoUtil::monoToWString(path);
+		Path newPath = oldPath.getParent().append(MonoUtil::monoToWString(name));
+
+		ProjectLibrary::instance().moveEntry(oldPath, newPath, false);
+	}
+
+	void ScriptProjectLibrary::internal_Move(MonoString* oldPath, MonoString* newPath, bool overwrite)
+	{
+		Path oldPathNative = MonoUtil::monoToWString(oldPath);
+		Path newPathNative = MonoUtil::monoToWString(newPath);
+
+		ProjectLibrary::instance().moveEntry(oldPathNative, newPathNative, overwrite);
+	}
+
+	void ScriptProjectLibrary::internal_Copy(MonoString* source, MonoString* destination, bool overwrite)
+	{
+		Path oldPathNative = MonoUtil::monoToWString(source);
+		Path newPathNative = MonoUtil::monoToWString(destination);
+
+		ProjectLibrary::instance().copyEntry(oldPathNative, newPathNative, overwrite);
+	}
 }

+ 17 - 0
TODO.txt

@@ -6,6 +6,23 @@ Assembly refresh
 When serializing Camera I cannot save the reference to RenderTexture. Make it a Resource?
 Possibly set up automatic refresh in debug mode after initialization? As an ad-hoc unit test
 
+----------------------------------------------------------------------
+Project library
+
+Finish ProjectLibrary script interface (this includes Library and DirectoryEntry classes)
+Create ImportOptions script interface
+Figure out how to deal with C# LibraryEntries (if I just make them c++ wrappers I feel it 
+ would be too slow (they'd need to store a path, and then I'd have to lookup that path in ProjectLibrary whenever any property
+ - Keep a separate C# version of the entries? It gets upated when ProjectLibrary callbacks are triggered.
+ - OR integrate C# entries directly in ProjectLibrary? (i.e. some kind of special field that holds a C# version of it)
+ - Performance implications shouldn't matter. It is not something that is called every frame and we're unlikely to iterate over the entire hierarchy. 
+   If such case is needed then I can introduce a more performant specialized wrapper. Therefore C# wrapper should just store a path and then 
+   lookup the entry in ProjectLibrary
+Make sure C# ProjectLibrary triggers same callbacks as its c++ counterpart
+Figure out how to create a C# BuiltinResources class. It should be able to load arbitrary resources from a non-project folder
+ - I should move all the GUI element style creation out of code and make it just pure data (once I have an editor for it)
+ - Will also need a debug button to reimport all the built-in resources
+
 ----------------------------------------------------------------------
 Simple stuff