BsProjectLibrary.cpp 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345
  1. #include "BsProjectLibrary.h"
  2. #include "BsEditorApplication.h"
  3. #include "CmPath.h"
  4. #include "CmFileSystem.h"
  5. #include "CmException.h"
  6. #include "CmResources.h"
  7. #include "CmResourceManifest.h"
  8. #include "CmImporter.h"
  9. #include "BsResourceMeta.h"
  10. #include "CmResources.h"
  11. #include "CmImporter.h"
  12. #include "CmImportOptions.h"
  13. #include "CmFileSerializer.h"
  14. using namespace CamelotFramework;
  15. using namespace BansheeEngine;
  16. namespace BansheeEditor
  17. {
  18. const WString ProjectLibrary::INTERNAL_RESOURCES_DIR = L"Internal/Resources";
  19. enum class LibraryEntryType
  20. {
  21. File,
  22. Directory
  23. };
  24. struct ProjectLibrary::LibraryEntry
  25. {
  26. LibraryEntryType type;
  27. WPath path;
  28. DirectoryEntry* parent;
  29. };
  30. struct ProjectLibrary::ResourceEntry : public ProjectLibrary::LibraryEntry
  31. {
  32. ResourceMetaPtr meta;
  33. std::time_t lastUpdateTime;
  34. };
  35. struct ProjectLibrary::DirectoryEntry : public ProjectLibrary::LibraryEntry
  36. {
  37. Vector<LibraryEntry*>::type mChildren;
  38. };
  39. ProjectLibrary::ProjectLibrary()
  40. :mRootEntry(nullptr)
  41. {
  42. }
  43. ProjectLibrary::~ProjectLibrary()
  44. {
  45. if(mRootEntry != nullptr)
  46. deleteDirectory(mRootEntry);
  47. }
  48. void ProjectLibrary::checkForModifications(const CM::WString& folder)
  49. {
  50. if(!PathUtil::includes(folder, EditorApplication::instance().getResourcesFolderPath()))
  51. return; // Folder not part of our resources path, so no modifications
  52. if(mRootEntry == nullptr)
  53. {
  54. mRootEntry = cm_new<DirectoryEntry>();
  55. mRootEntry->path = toPath(EditorApplication::instance().getResourcesFolderPath());
  56. }
  57. DirectoryEntry* directoryEntry = findDirectoryEntry(folder);
  58. if(directoryEntry == nullptr)
  59. {
  60. CM_EXCEPT(InternalErrorException, "Attempting to check for modifications on a folder that is not yet part " \
  61. "of the project library. Maybe check for modifications on the parent folder first? Folder: \"" + toString(folder) + "\"");
  62. }
  63. Stack<DirectoryEntry*>::type todo;
  64. todo.push(directoryEntry);
  65. Vector<WPath>::type childFiles;
  66. Vector<WPath>::type childDirectories;
  67. Vector<bool>::type existingEntries;
  68. Vector<LibraryEntry*>::type toDelete;
  69. while(!todo.empty())
  70. {
  71. DirectoryEntry* currentDir = todo.top();
  72. todo.pop();
  73. existingEntries.clear();
  74. existingEntries.resize(currentDir->mChildren.size());
  75. for(UINT32 i = 0; i < (UINT32)currentDir->mChildren.size(); i++)
  76. existingEntries[i] = false;
  77. childFiles.clear();
  78. childDirectories.clear();
  79. FileSystem::getChildren(currentDir->path, childFiles, childDirectories);
  80. for(auto& filePath : childFiles)
  81. {
  82. ResourceEntry* existingEntry = nullptr;
  83. UINT32 idx = 0;
  84. for(auto& child : currentDir->mChildren)
  85. {
  86. if(child->type == LibraryEntryType::File && child->path == filePath)
  87. {
  88. existingEntries[idx] = true;
  89. existingEntry = static_cast<ResourceEntry*>(child);
  90. break;
  91. }
  92. idx++;
  93. }
  94. if(existingEntry != nullptr)
  95. {
  96. reimportResource(existingEntry);
  97. }
  98. else
  99. {
  100. addResource(currentDir, filePath);
  101. }
  102. }
  103. for(auto& dirPath : childDirectories)
  104. {
  105. DirectoryEntry* existingEntry = nullptr;
  106. UINT32 idx = 0;
  107. for(auto& child : currentDir->mChildren)
  108. {
  109. if(child->type == LibraryEntryType::Directory && child->path == dirPath)
  110. {
  111. existingEntries[idx] = true;
  112. existingEntry = static_cast<DirectoryEntry*>(child);
  113. break;
  114. }
  115. idx++;
  116. }
  117. if(existingEntry == nullptr)
  118. {
  119. DirectoryEntry* newEntry = cm_new<DirectoryEntry>();
  120. newEntry->path = dirPath;
  121. newEntry->type = LibraryEntryType::Directory;
  122. newEntry->parent = currentDir;
  123. currentDir->mChildren.push_back(newEntry);
  124. }
  125. }
  126. {
  127. UINT32 idx = 0;
  128. toDelete.clear();
  129. for(auto& child : currentDir->mChildren)
  130. {
  131. if(existingEntries[idx])
  132. continue;
  133. toDelete.push_back(child);
  134. idx++;
  135. }
  136. for(auto& child : toDelete)
  137. {
  138. if(child->type == LibraryEntryType::Directory)
  139. deleteDirectory(static_cast<DirectoryEntry*>(child));
  140. else if(child->type == LibraryEntryType::File)
  141. deleteResource(static_cast<ResourceEntry*>(child));
  142. }
  143. }
  144. for(auto& child : currentDir->mChildren)
  145. {
  146. if(child->type == LibraryEntryType::Directory)
  147. todo.push(static_cast<DirectoryEntry*>(child));
  148. }
  149. }
  150. }
  151. void ProjectLibrary::addResource(DirectoryEntry* parent, const CM::WPath& filePath)
  152. {
  153. ResourceEntry* newResource = cm_new<ResourceEntry>();
  154. newResource->type = LibraryEntryType::File;
  155. newResource->path = filePath;
  156. newResource->parent = parent;
  157. parent->mChildren.push_back(newResource);
  158. reimportResource(newResource);
  159. }
  160. void ProjectLibrary::reimportResource(ResourceEntry* resource)
  161. {
  162. WString ext = toWString(PathUtil::getExtension(resource->path));
  163. WPath metaPath = resource->path;
  164. PathUtil::replaceExtension(metaPath, ext + L".meta");
  165. ext = ext.substr(1, ext.size() - 1); // Remove the .
  166. if(!Importer::instance().supportsFileType(ext))
  167. return;
  168. if(resource->meta == nullptr)
  169. {
  170. FileSerializer fs;
  171. if(FileSystem::isFile(metaPath))
  172. {
  173. std::shared_ptr<IReflectable> loadedMeta = fs.decode(toWString(metaPath));
  174. if(loadedMeta != nullptr && loadedMeta->isDerivedFrom(ResourceMeta::getRTTIStatic()))
  175. {
  176. ResourceMetaPtr resourceMeta = std::static_pointer_cast<ResourceMeta>(loadedMeta);
  177. resource->meta = resourceMeta;
  178. }
  179. }
  180. }
  181. if(!isUpToDate(resource))
  182. {
  183. ImportOptionsPtr importOptions = nullptr;
  184. if(resource->meta != nullptr)
  185. importOptions = resource->meta->getImportOptions();
  186. else
  187. importOptions = Importer::instance().createImportOptions(toWString(resource->path));
  188. HResource importedResource = Importer::instance().import(toWString(resource->path), importOptions);
  189. if(resource->meta == nullptr)
  190. {
  191. resource->meta = ResourceMeta::create(importedResource.getUUID(), importOptions);
  192. FileSerializer fs;
  193. fs.encode(resource->meta.get(), toWString(metaPath)); // TODO - Make it accept WPath
  194. }
  195. WPath internalResourcesPath = toPath(EditorApplication::instance().getActiveProjectPath()) / toPath(INTERNAL_RESOURCES_DIR);
  196. if(!FileSystem::isDirectory(internalResourcesPath))
  197. FileSystem::createDir(internalResourcesPath);
  198. internalResourcesPath /= toPath(toWString(importedResource.getUUID()) + L".asset");
  199. gResources().save(importedResource, toWString(internalResourcesPath), true);
  200. resource->lastUpdateTime = std::time(nullptr);
  201. }
  202. }
  203. void ProjectLibrary::deleteResource(ResourceEntry* resource)
  204. {
  205. ResourceManifestPtr manifest = gResources().getResourceManifest();
  206. if(resource->meta != nullptr)
  207. {
  208. if(manifest->uuidExists(resource->meta->getUUID()))
  209. {
  210. const WString& path = manifest->uuidToFilePath(resource->meta->getUUID());
  211. if(FileSystem::isFile(path))
  212. FileSystem::remove(path);
  213. }
  214. WString ext = toWString(PathUtil::getExtension(resource->path));
  215. WPath metaPath = resource->path;
  216. PathUtil::replaceExtension(metaPath, ext + L".meta");
  217. if(FileSystem::isFile(metaPath))
  218. FileSystem::remove(metaPath);
  219. }
  220. DirectoryEntry* parent = resource->parent;
  221. auto findIter = std::find_if(parent->mChildren.begin(), parent->mChildren.end(),
  222. [&] (const LibraryEntry* entry) { return entry == resource; });
  223. parent->mChildren.erase(findIter);
  224. cm_delete(resource);
  225. }
  226. void ProjectLibrary::deleteDirectory(DirectoryEntry* directory)
  227. {
  228. if(directory == mRootEntry)
  229. mRootEntry = nullptr;
  230. for(auto& child : directory->mChildren)
  231. {
  232. if(child->type == LibraryEntryType::Directory)
  233. deleteDirectory(static_cast<DirectoryEntry*>(child));
  234. else
  235. deleteResource(static_cast<ResourceEntry*>(child));
  236. }
  237. DirectoryEntry* parent = directory->parent;
  238. auto findIter = std::find_if(parent->mChildren.begin(), parent->mChildren.end(),
  239. [&] (const LibraryEntry* entry) { return entry == directory; });
  240. parent->mChildren.erase(findIter);
  241. cm_delete(directory);
  242. }
  243. bool ProjectLibrary::isUpToDate(ResourceEntry* resource) const
  244. {
  245. if(resource->meta == nullptr)
  246. return false;
  247. ResourceManifestPtr manifest = gResources().getResourceManifest();
  248. if(!manifest->uuidExists(resource->meta->getUUID()))
  249. return false;
  250. const WString& path = manifest->uuidToFilePath(resource->meta->getUUID());
  251. if(!FileSystem::isFile(path))
  252. return false;
  253. std::time_t lastModifiedTime = FileSystem::getLastModifiedTime(path);
  254. return lastModifiedTime <= resource->lastUpdateTime;
  255. }
  256. ProjectLibrary::DirectoryEntry* ProjectLibrary::findDirectoryEntry(const CM::WString& folder) const
  257. {
  258. WPath pathToSearch = toPath(folder);
  259. Stack<DirectoryEntry*>::type todo;
  260. todo.push(mRootEntry);
  261. while(!todo.empty())
  262. {
  263. DirectoryEntry* currentDir = todo.top();
  264. todo.pop();
  265. for(auto& child : currentDir->mChildren)
  266. {
  267. if(child->type == LibraryEntryType::Directory)
  268. {
  269. if(pathToSearch == child->path)
  270. return static_cast<DirectoryEntry*>(child);
  271. todo.push(static_cast<DirectoryEntry*>(child));
  272. }
  273. }
  274. }
  275. return nullptr;
  276. }
  277. }