BsProjectLibrary.cpp 31 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088
  1. #include "BsProjectLibrary.h"
  2. #include "BsFileSystem.h"
  3. #include "BsException.h"
  4. #include "BsResources.h"
  5. #include "BsResourceManifest.h"
  6. #include "BsImporter.h"
  7. #include "BsProjectResourceMeta.h"
  8. #include "BsResources.h"
  9. #include "BsImporter.h"
  10. #include "BsImportOptions.h"
  11. #include "BsFileSerializer.h"
  12. #include "BsFolderMonitor.h"
  13. #include "BsDebug.h"
  14. #include "BsProjectLibraryEntries.h"
  15. #include "BsResource.h"
  16. #include "BsResourceImporter.h"
  17. #include "BsShader.h"
  18. #include <regex>
  19. using namespace std::placeholders;
  20. namespace BansheeEngine
  21. {
  22. const Path ProjectLibrary::RESOURCES_DIR = L"Resources\\";
  23. const Path ProjectLibrary::INTERNAL_RESOURCES_DIR = L"Internal\\Resources\\";
  24. const WString ProjectLibrary::LIBRARY_ENTRIES_FILENAME = L"ProjectLibrary.asset";
  25. const WString ProjectLibrary::RESOURCE_MANIFEST_FILENAME = L"ResourceManifest.asset";
  26. ProjectLibrary::LibraryEntry::LibraryEntry()
  27. :parent(nullptr), type(LibraryEntryType::Directory)
  28. { }
  29. ProjectLibrary::LibraryEntry::LibraryEntry(const Path& path, const WString& name, DirectoryEntry* parent, LibraryEntryType type)
  30. :path(path), parent(parent), type(type), elementName(name)
  31. { }
  32. ProjectLibrary::ResourceEntry::ResourceEntry()
  33. :lastUpdateTime(0)
  34. { }
  35. ProjectLibrary::ResourceEntry::ResourceEntry(const Path& path, const WString& name, DirectoryEntry* parent)
  36. :LibraryEntry(path, name, parent, LibraryEntryType::File), lastUpdateTime(0)
  37. { }
  38. ProjectLibrary::DirectoryEntry::DirectoryEntry()
  39. { }
  40. ProjectLibrary::DirectoryEntry::DirectoryEntry(const Path& path, const WString& name, DirectoryEntry* parent)
  41. :LibraryEntry(path, name, parent, LibraryEntryType::Directory)
  42. { }
  43. ProjectLibrary::ProjectLibrary(const Path& projectFolder)
  44. :mRootEntry(nullptr), mProjectFolder(projectFolder)
  45. {
  46. mResourcesFolder = mProjectFolder;
  47. mResourcesFolder.append(RESOURCES_DIR);
  48. mMonitor = bs_new<FolderMonitor>();
  49. FolderChange folderChanges = (FolderChange)((UINT32)FolderChange::FileName | (UINT32)FolderChange::DirName |
  50. (UINT32)FolderChange::Creation | (UINT32)FolderChange::LastWrite);
  51. mMonitor->startMonitor(mResourcesFolder, true, folderChanges);
  52. mMonitor->onAdded.connect(std::bind(&ProjectLibrary::onMonitorFileModified, this, _1));
  53. mMonitor->onRemoved.connect(std::bind(&ProjectLibrary::onMonitorFileModified, this, _1));
  54. mMonitor->onModified.connect(std::bind(&ProjectLibrary::onMonitorFileModified, this, _1));
  55. load();
  56. if(mResourceManifest == nullptr)
  57. mResourceManifest = ResourceManifest::create("ProjectLibrary");
  58. gResources().registerResourceManifest(mResourceManifest);
  59. checkForModifications(mResourcesFolder);
  60. }
  61. ProjectLibrary::~ProjectLibrary()
  62. {
  63. save();
  64. mMonitor->stopMonitorAll();
  65. bs_delete(mMonitor);
  66. if(mRootEntry != nullptr)
  67. deleteDirectoryInternal(mRootEntry);
  68. }
  69. void ProjectLibrary::update()
  70. {
  71. mMonitor->_update();
  72. while (!mReimportQueue.empty())
  73. {
  74. Path toReimport = *mReimportQueue.begin();
  75. LibraryEntry* entry = findEntry(toReimport);
  76. if (entry != nullptr && entry->type == LibraryEntryType::File)
  77. {
  78. ResourceEntry* resEntry = static_cast<ResourceEntry*>(entry);
  79. reimportResourceInternal(resEntry, resEntry->meta->getImportOptions(), true);
  80. queueDependantForReimport(resEntry);
  81. }
  82. mReimportQueue.erase(mReimportQueue.begin());
  83. }
  84. }
  85. void ProjectLibrary::checkForModifications(const Path& fullPath)
  86. {
  87. if (!mResourcesFolder.includes(fullPath))
  88. return; // Folder not part of our resources path, so no modifications
  89. if(mRootEntry == nullptr)
  90. {
  91. mRootEntry = bs_new<DirectoryEntry>(mResourcesFolder, mResourcesFolder.getWTail(), nullptr);
  92. }
  93. Path pathToSearch = fullPath;
  94. LibraryEntry* entry = findEntry(pathToSearch);
  95. if(entry == nullptr) // File could be new, try to find parent directory entry
  96. {
  97. Path parentDirPath = pathToSearch.getParent();
  98. entry = findEntry(parentDirPath);
  99. // Cannot find parent directory. Create the needed hierarchy.
  100. DirectoryEntry* entryParent = nullptr;
  101. DirectoryEntry* newHierarchyParent = nullptr;
  102. if(entry == nullptr)
  103. createInternalParentHierarchy(pathToSearch, &newHierarchyParent, &entryParent);
  104. else
  105. entryParent = static_cast<DirectoryEntry*>(entry);
  106. if(FileSystem::isFile(pathToSearch))
  107. {
  108. addResourceInternal(entryParent, pathToSearch);
  109. }
  110. else if(FileSystem::isDirectory(pathToSearch))
  111. {
  112. addDirectoryInternal(entryParent, pathToSearch);
  113. if(newHierarchyParent == nullptr)
  114. checkForModifications(pathToSearch);
  115. }
  116. if(newHierarchyParent != nullptr)
  117. checkForModifications(newHierarchyParent->path);
  118. }
  119. else if(entry->type == LibraryEntryType::File)
  120. {
  121. if(FileSystem::isFile(entry->path))
  122. {
  123. ResourceEntry* resEntry = static_cast<ResourceEntry*>(entry);
  124. reimportResourceInternal(resEntry);
  125. queueDependantForReimport(resEntry);
  126. }
  127. else
  128. {
  129. deleteResourceInternal(static_cast<ResourceEntry*>(entry));
  130. }
  131. }
  132. else if(entry->type == LibraryEntryType::Directory) // Check folder and all subfolders for modifications
  133. {
  134. if(!FileSystem::isDirectory(entry->path))
  135. {
  136. deleteDirectoryInternal(static_cast<DirectoryEntry*>(entry));
  137. }
  138. else
  139. {
  140. Stack<DirectoryEntry*> todo;
  141. todo.push(static_cast<DirectoryEntry*>(entry));
  142. Vector<Path> childFiles;
  143. Vector<Path> childDirectories;
  144. Vector<bool> existingEntries;
  145. Vector<LibraryEntry*> toDelete;
  146. while(!todo.empty())
  147. {
  148. DirectoryEntry* currentDir = todo.top();
  149. todo.pop();
  150. existingEntries.clear();
  151. existingEntries.resize(currentDir->mChildren.size());
  152. for(UINT32 i = 0; i < (UINT32)currentDir->mChildren.size(); i++)
  153. existingEntries[i] = false;
  154. childFiles.clear();
  155. childDirectories.clear();
  156. FileSystem::getChildren(currentDir->path, childFiles, childDirectories);
  157. for(auto& filePath : childFiles)
  158. {
  159. if(isMeta(filePath))
  160. {
  161. Path sourceFilePath = filePath;
  162. sourceFilePath.setExtension(L"");
  163. if(!FileSystem::isFile(sourceFilePath))
  164. {
  165. LOGWRN("Found a .meta file without a corresponding resource. Deleting.");
  166. FileSystem::remove(filePath);
  167. }
  168. }
  169. else
  170. {
  171. ResourceEntry* existingEntry = nullptr;
  172. UINT32 idx = 0;
  173. for(auto& child : currentDir->mChildren)
  174. {
  175. if(child->type == LibraryEntryType::File && child->path == filePath)
  176. {
  177. existingEntries[idx] = true;
  178. existingEntry = static_cast<ResourceEntry*>(child);
  179. break;
  180. }
  181. idx++;
  182. }
  183. if(existingEntry != nullptr)
  184. {
  185. reimportResourceInternal(existingEntry);
  186. queueDependantForReimport(existingEntry);
  187. }
  188. else
  189. {
  190. addResourceInternal(currentDir, filePath);
  191. }
  192. }
  193. }
  194. for(auto& dirPath : childDirectories)
  195. {
  196. DirectoryEntry* existingEntry = nullptr;
  197. UINT32 idx = 0;
  198. for(auto& child : currentDir->mChildren)
  199. {
  200. if(child->type == LibraryEntryType::Directory && child->path == dirPath)
  201. {
  202. existingEntries[idx] = true;
  203. existingEntry = static_cast<DirectoryEntry*>(child);
  204. break;
  205. }
  206. idx++;
  207. }
  208. if(existingEntry == nullptr)
  209. addDirectoryInternal(currentDir, dirPath);
  210. }
  211. {
  212. for(UINT32 i = 0; i < (UINT32)existingEntries.size(); i++)
  213. {
  214. if(existingEntries[i])
  215. continue;
  216. toDelete.push_back(currentDir->mChildren[i]);
  217. }
  218. for(auto& child : toDelete)
  219. {
  220. if(child->type == LibraryEntryType::Directory)
  221. deleteDirectoryInternal(static_cast<DirectoryEntry*>(child));
  222. else if(child->type == LibraryEntryType::File)
  223. deleteResourceInternal(static_cast<ResourceEntry*>(child));
  224. }
  225. }
  226. for(auto& child : currentDir->mChildren)
  227. {
  228. if(child->type == LibraryEntryType::Directory)
  229. todo.push(static_cast<DirectoryEntry*>(child));
  230. }
  231. }
  232. }
  233. }
  234. }
  235. ProjectLibrary::ResourceEntry* ProjectLibrary::addResourceInternal(DirectoryEntry* parent, const Path& filePath,
  236. const ImportOptionsPtr& importOptions, bool forceReimport)
  237. {
  238. ResourceEntry* newResource = bs_new<ResourceEntry>(filePath, filePath.getWTail(), parent);
  239. parent->mChildren.push_back(newResource);
  240. reimportResourceInternal(newResource, importOptions, forceReimport);
  241. doOnEntryAdded(newResource);
  242. return newResource;
  243. }
  244. ProjectLibrary::DirectoryEntry* ProjectLibrary::addDirectoryInternal(DirectoryEntry* parent, const Path& dirPath)
  245. {
  246. DirectoryEntry* newEntry = bs_new<DirectoryEntry>(dirPath, dirPath.getWTail(), parent);
  247. parent->mChildren.push_back(newEntry);
  248. doOnEntryAdded(newEntry);
  249. return newEntry;
  250. }
  251. void ProjectLibrary::deleteResourceInternal(ResourceEntry* resource)
  252. {
  253. if(resource->meta != nullptr)
  254. {
  255. Path path;
  256. if(mResourceManifest->uuidToFilePath(resource->meta->getUUID(), path))
  257. {
  258. if(FileSystem::isFile(path))
  259. FileSystem::remove(path);
  260. mResourceManifest->unregisterResource(resource->meta->getUUID());
  261. }
  262. }
  263. DirectoryEntry* parent = resource->parent;
  264. auto findIter = std::find_if(parent->mChildren.begin(), parent->mChildren.end(),
  265. [&] (const LibraryEntry* entry) { return entry == resource; });
  266. parent->mChildren.erase(findIter);
  267. doOnEntryRemoved(resource);
  268. bs_delete(resource);
  269. }
  270. void ProjectLibrary::deleteDirectoryInternal(DirectoryEntry* directory)
  271. {
  272. if(directory == mRootEntry)
  273. mRootEntry = nullptr;
  274. Vector<LibraryEntry*> childrenToDestroy = directory->mChildren;
  275. for(auto& child : childrenToDestroy)
  276. {
  277. if(child->type == LibraryEntryType::Directory)
  278. deleteDirectoryInternal(static_cast<DirectoryEntry*>(child));
  279. else
  280. deleteResourceInternal(static_cast<ResourceEntry*>(child));
  281. }
  282. DirectoryEntry* parent = directory->parent;
  283. if(parent != nullptr)
  284. {
  285. auto findIter = std::find_if(parent->mChildren.begin(), parent->mChildren.end(),
  286. [&] (const LibraryEntry* entry) { return entry == directory; });
  287. parent->mChildren.erase(findIter);
  288. }
  289. doOnEntryRemoved(directory);
  290. bs_delete(directory);
  291. }
  292. void ProjectLibrary::reimportResourceInternal(ResourceEntry* resource, const ImportOptionsPtr& importOptions, bool forceReimport)
  293. {
  294. WString ext = resource->path.getWExtension();
  295. Path metaPath = resource->path;
  296. metaPath.setFilename(metaPath.getWFilename() + L".meta");
  297. ext = ext.substr(1, ext.size() - 1); // Remove the .
  298. if (!Importer::instance().supportsFileType(ext))
  299. return;
  300. if(resource->meta == nullptr)
  301. {
  302. if(FileSystem::isFile(metaPath))
  303. {
  304. FileDecoder fs(metaPath);
  305. std::shared_ptr<IReflectable> loadedMeta = fs.decode();
  306. if(loadedMeta != nullptr && loadedMeta->isDerivedFrom(ProjectResourceMeta::getRTTIStatic()))
  307. {
  308. ProjectResourceMetaPtr resourceMeta = std::static_pointer_cast<ProjectResourceMeta>(loadedMeta);
  309. resource->meta = resourceMeta;
  310. }
  311. }
  312. }
  313. if (!isUpToDate(resource) || forceReimport)
  314. {
  315. ImportOptionsPtr curImportOptions = nullptr;
  316. if (importOptions == nullptr)
  317. {
  318. if (resource->meta != nullptr)
  319. curImportOptions = resource->meta->getImportOptions();
  320. else
  321. curImportOptions = Importer::instance().createImportOptions(resource->path);
  322. }
  323. else
  324. curImportOptions = importOptions;
  325. HResource importedResource;
  326. if(resource->meta == nullptr)
  327. {
  328. importedResource = Importer::instance().import(resource->path, curImportOptions);
  329. ResourceMetaDataPtr subMeta = importedResource->getMetaData();
  330. UINT32 typeId = importedResource->getTypeId();
  331. resource->meta = ProjectResourceMeta::create(importedResource.getUUID(), typeId, subMeta, curImportOptions);
  332. FileEncoder fs(metaPath);
  333. fs.encode(resource->meta.get());
  334. }
  335. else
  336. {
  337. removeDependencies(resource);
  338. importedResource = HResource(resource->meta->getUUID());
  339. Importer::instance().reimport(importedResource, resource->path, curImportOptions);
  340. }
  341. addDependencies(resource);
  342. Path internalResourcesPath = mProjectFolder;
  343. internalResourcesPath.append(INTERNAL_RESOURCES_DIR);
  344. if(!FileSystem::isDirectory(internalResourcesPath))
  345. FileSystem::createDir(internalResourcesPath);
  346. internalResourcesPath.setFilename(toWString(importedResource.getUUID()) + L".asset");
  347. gResources().save(importedResource, internalResourcesPath, true);
  348. gResources().unload(importedResource);
  349. mResourceManifest->registerResource(importedResource.getUUID(), internalResourcesPath);
  350. resource->lastUpdateTime = std::time(nullptr);
  351. }
  352. }
  353. bool ProjectLibrary::isUpToDate(ResourceEntry* resource) const
  354. {
  355. if(resource->meta == nullptr)
  356. return false;
  357. Path path;
  358. if(!mResourceManifest->uuidToFilePath(resource->meta->getUUID(), path))
  359. return false;
  360. if(!FileSystem::isFile(path))
  361. return false;
  362. std::time_t lastModifiedTime = FileSystem::getLastModifiedTime(path);
  363. return lastModifiedTime <= resource->lastUpdateTime;
  364. }
  365. Vector<ProjectLibrary::LibraryEntry*> ProjectLibrary::search(const WString& pattern)
  366. {
  367. return search(pattern, {});
  368. }
  369. Vector<ProjectLibrary::LibraryEntry*> ProjectLibrary::search(const WString& pattern, const Vector<UINT32>& typeIds)
  370. {
  371. Vector<LibraryEntry*> foundEntries;
  372. std::wregex escape(L"[.^$|()\\[\\]{}*+?\\\\]");
  373. WString replace(L"\\\\&");
  374. WString escapedPattern = std::regex_replace(pattern, escape, replace, std::regex_constants::match_default | std::regex_constants::format_sed);
  375. std::wregex wildcard(L"\\\\\\*");
  376. WString wildcardReplace(L".*");
  377. WString searchPattern = std::regex_replace(escapedPattern, wildcard, L".*");
  378. std::wregex searchRegex(searchPattern);
  379. Stack<DirectoryEntry*> todo;
  380. todo.push(mRootEntry);
  381. while (!todo.empty())
  382. {
  383. DirectoryEntry* dirEntry = todo.top();
  384. todo.pop();
  385. for (auto& child : dirEntry->mChildren)
  386. {
  387. if (std::regex_match(child->elementName, searchRegex))
  388. {
  389. if (typeIds.size() == 0)
  390. foundEntries.push_back(child);
  391. else
  392. {
  393. if (child->type == LibraryEntryType::File)
  394. {
  395. ResourceEntry* childResEntry = static_cast<ResourceEntry*>(child);
  396. for (auto& typeId : typeIds)
  397. {
  398. if (childResEntry->meta->getTypeID() == typeId)
  399. {
  400. foundEntries.push_back(child);
  401. break;
  402. }
  403. }
  404. }
  405. }
  406. }
  407. if (child->type == LibraryEntryType::Directory)
  408. {
  409. DirectoryEntry* childDirEntry = static_cast<DirectoryEntry*>(child);
  410. todo.push(childDirEntry);
  411. }
  412. }
  413. }
  414. std::sort(foundEntries.begin(), foundEntries.end(),
  415. [&](const LibraryEntry* a, const LibraryEntry* b)
  416. {
  417. return a->elementName.compare(b->elementName);
  418. });
  419. return foundEntries;
  420. }
  421. ProjectLibrary::LibraryEntry* ProjectLibrary::findEntry(const Path& fullPath) const
  422. {
  423. if (!mRootEntry->path.includes(fullPath))
  424. return nullptr;
  425. Path relPath = fullPath.getRelative(mRootEntry->path);
  426. UINT32 numElems = relPath.getNumDirectories() + (relPath.isFile() ? 1 : 0);
  427. UINT32 idx = 0;
  428. LibraryEntry* current = mRootEntry;
  429. while (current != nullptr)
  430. {
  431. if (idx == numElems)
  432. return current;
  433. WString curElem;
  434. if (relPath.isFile() && idx == (numElems - 1))
  435. curElem = relPath.getWFilename();
  436. else
  437. curElem = relPath[idx];
  438. if (current->type == LibraryEntryType::Directory)
  439. {
  440. DirectoryEntry* dirEntry = static_cast<DirectoryEntry*>(current);
  441. for (auto& child : dirEntry->mChildren)
  442. {
  443. current = nullptr;
  444. if (Path::comparePathElem(curElem, child->elementName))
  445. {
  446. idx++;
  447. current = child;
  448. break;
  449. }
  450. }
  451. }
  452. else
  453. break;
  454. }
  455. return nullptr;
  456. }
  457. ProjectResourceMetaPtr ProjectLibrary::findResourceMeta(const String& uuid) const
  458. {
  459. if (mResourceManifest == nullptr)
  460. return nullptr;
  461. Path filePath;
  462. if (!mResourceManifest->uuidToFilePath(uuid, filePath))
  463. return nullptr;
  464. LibraryEntry* libEntry = findEntry(filePath);
  465. if (libEntry == nullptr || libEntry->type != LibraryEntryType::File)
  466. return nullptr;
  467. ResourceEntry* resEntry = static_cast<ResourceEntry*>(libEntry);
  468. return resEntry->meta;
  469. }
  470. Path ProjectLibrary::uuidToPath(const String& uuid) const
  471. {
  472. if (mResourceManifest == nullptr)
  473. return Path();
  474. Path filePath;
  475. if (!mResourceManifest->uuidToFilePath(uuid, filePath))
  476. return Path();
  477. return filePath;
  478. }
  479. void ProjectLibrary::createEntry(const HResource& resource, const Path& path)
  480. {
  481. if (resource == nullptr)
  482. return;
  483. Path assetPath = mResourcesFolder;
  484. assetPath.append(path);
  485. assetPath.setExtension(assetPath.getWExtension() + L"." + ResourceImporter::DEFAULT_EXTENSION);
  486. LibraryEntry* existingEntry = findEntry(assetPath);
  487. if (existingEntry != nullptr)
  488. BS_EXCEPT(InvalidParametersException, "Resource already exists at the specified path: " + assetPath.toString());
  489. Resources::instance().save(resource, assetPath, false);
  490. checkForModifications(assetPath);
  491. }
  492. void ProjectLibrary::saveEntry(const HResource& resource)
  493. {
  494. if (resource == nullptr)
  495. return;
  496. Path filePath;
  497. if (!mResourceManifest->uuidToFilePath(resource.getUUID(), filePath))
  498. return;
  499. Resources::instance().save(resource, filePath, false);
  500. }
  501. void ProjectLibrary::createFolderEntry(const Path& path)
  502. {
  503. if (FileSystem::isDirectory(path))
  504. return; // Already exists
  505. FileSystem::createDir(path);
  506. if (!mResourcesFolder.includes(path))
  507. return;
  508. Path parentPath = path.getParent();
  509. DirectoryEntry* newEntryParent = nullptr;
  510. LibraryEntry* newEntryParentLib = findEntry(parentPath);
  511. if (newEntryParentLib != nullptr)
  512. {
  513. assert(newEntryParentLib->type == LibraryEntryType::Directory);
  514. newEntryParent = static_cast<DirectoryEntry*>(newEntryParentLib);
  515. }
  516. DirectoryEntry* newHierarchyParent = nullptr;
  517. if (newEntryParent == nullptr) // New path parent doesn't exist, so we need to create the hierarchy
  518. createInternalParentHierarchy(path, &newHierarchyParent, &newEntryParent);
  519. addDirectoryInternal(newEntryParent, path);
  520. }
  521. void ProjectLibrary::moveEntry(const Path& oldPath, const Path& newPath, bool overwrite)
  522. {
  523. if(FileSystem::isFile(oldPath) || FileSystem::isDirectory(oldPath))
  524. FileSystem::move(oldPath, newPath, overwrite);
  525. Path oldMetaPath = getMetaPath(oldPath);
  526. Path newMetaPath = getMetaPath(newPath);
  527. LibraryEntry* oldEntry = findEntry(oldPath);
  528. if(oldEntry != nullptr) // Moving from the Resources folder
  529. {
  530. // Moved outside of Resources, delete entry & meta file
  531. if (!mResourcesFolder.includes(newPath))
  532. {
  533. if(oldEntry->type == LibraryEntryType::File)
  534. {
  535. deleteResourceInternal(static_cast<ResourceEntry*>(oldEntry));
  536. if(FileSystem::isFile(oldMetaPath))
  537. FileSystem::remove(oldMetaPath);
  538. }
  539. else if(oldEntry->type == LibraryEntryType::Directory)
  540. deleteDirectoryInternal(static_cast<DirectoryEntry*>(oldEntry));
  541. }
  542. else // Just moving internally
  543. {
  544. doOnEntryRemoved(oldEntry);
  545. if(FileSystem::isFile(oldMetaPath))
  546. FileSystem::move(oldMetaPath, newMetaPath);
  547. DirectoryEntry* parent = oldEntry->parent;
  548. auto findIter = std::find(parent->mChildren.begin(), parent->mChildren.end(), oldEntry);
  549. if(findIter != parent->mChildren.end())
  550. parent->mChildren.erase(findIter);
  551. Path parentPath = newPath.getParent();
  552. DirectoryEntry* newEntryParent = nullptr;
  553. LibraryEntry* newEntryParentLib = findEntry(parentPath);
  554. if(newEntryParentLib != nullptr)
  555. {
  556. assert(newEntryParentLib->type == LibraryEntryType::Directory);
  557. newEntryParent = static_cast<DirectoryEntry*>(newEntryParentLib);
  558. }
  559. DirectoryEntry* newHierarchyParent = nullptr;
  560. if(newEntryParent == nullptr) // New path parent doesn't exist, so we need to create the hierarchy
  561. createInternalParentHierarchy(newPath, &newHierarchyParent, &newEntryParent);
  562. newEntryParent->mChildren.push_back(oldEntry);
  563. oldEntry->parent = newEntryParent;
  564. oldEntry->path = newPath;
  565. oldEntry->elementName = newPath.getWTail();
  566. if(oldEntry->type == LibraryEntryType::Directory) // Update child paths
  567. {
  568. Stack<LibraryEntry*> todo;
  569. todo.push(oldEntry);
  570. while(!todo.empty())
  571. {
  572. LibraryEntry* curEntry = todo.top();
  573. todo.pop();
  574. DirectoryEntry* curDirEntry = static_cast<DirectoryEntry*>(curEntry);
  575. for(auto& child : curDirEntry->mChildren)
  576. {
  577. child->path = child->parent->path;
  578. child->path.append(child->elementName);
  579. if(child->type == LibraryEntryType::Directory)
  580. todo.push(child);
  581. }
  582. }
  583. }
  584. doOnEntryAdded(oldEntry);
  585. if(newHierarchyParent != nullptr)
  586. checkForModifications(newHierarchyParent->path);
  587. }
  588. }
  589. else // Moving from outside of the Resources folder (likely adding a new resource)
  590. {
  591. checkForModifications(newPath);
  592. }
  593. }
  594. void ProjectLibrary::copyEntry(const Path& oldPath, const Path& newPath, bool overwrite)
  595. {
  596. if (!FileSystem::exists(oldPath))
  597. return;
  598. FileSystem::copy(oldPath, newPath, overwrite);
  599. // Copying a file/folder outside of the Resources folder, no special handling needed
  600. if (!mResourcesFolder.includes(newPath))
  601. return;
  602. Path parentPath = newPath.getParent();
  603. DirectoryEntry* newEntryParent = nullptr;
  604. LibraryEntry* newEntryParentLib = findEntry(parentPath);
  605. if (newEntryParentLib != nullptr)
  606. {
  607. assert(newEntryParentLib->type == LibraryEntryType::Directory);
  608. newEntryParent = static_cast<DirectoryEntry*>(newEntryParentLib);
  609. }
  610. DirectoryEntry* newHierarchyParent = nullptr;
  611. if (newEntryParent == nullptr) // New path parent doesn't exist, so we need to create the hierarchy
  612. createInternalParentHierarchy(newPath, &newHierarchyParent, &newEntryParent);
  613. // If the source is outside of Resources folder, just plain import the copy
  614. LibraryEntry* oldEntry = findEntry(oldPath);
  615. if (oldEntry == nullptr)
  616. {
  617. checkForModifications(newHierarchyParent->path);
  618. return;
  619. }
  620. // Both source and destination are within Resources folder, need to preserve import options on the copies
  621. LibraryEntry* newEntry = nullptr;
  622. if (FileSystem::isFile(newPath))
  623. {
  624. assert(oldEntry->type == LibraryEntryType::File);
  625. ResourceEntry* oldResEntry = static_cast<ResourceEntry*>(oldEntry);
  626. newEntry = addResourceInternal(newEntryParent, newPath, oldResEntry->meta->getImportOptions(), true);
  627. }
  628. else
  629. {
  630. assert(oldEntry->type == LibraryEntryType::File);
  631. DirectoryEntry* oldDirEntry = static_cast<DirectoryEntry*>(oldEntry);
  632. DirectoryEntry* newDirEntry = addDirectoryInternal(newEntryParent, newPath);
  633. newEntry = newDirEntry;
  634. Stack<std::tuple<DirectoryEntry*, DirectoryEntry*>> todo;
  635. todo.push(std::make_tuple(oldDirEntry, newDirEntry));
  636. while (!todo.empty())
  637. {
  638. auto current = todo.top();
  639. todo.pop();
  640. DirectoryEntry* sourceDir = std::get<0>(current);
  641. DirectoryEntry* destDir = std::get<1>(current);
  642. for (auto& child : sourceDir->mChildren)
  643. {
  644. Path childDestPath = destDir->path;
  645. childDestPath.append(child->path.getWTail());
  646. if (child->type == LibraryEntryType::File)
  647. {
  648. ResourceEntry* childResEntry = static_cast<ResourceEntry*>(child);
  649. addResourceInternal(destDir, childDestPath, childResEntry->meta->getImportOptions(), true);
  650. }
  651. else // Directory
  652. {
  653. DirectoryEntry* childSourceDirEntry = static_cast<DirectoryEntry*>(child);
  654. DirectoryEntry* childDestDirEntry = addDirectoryInternal(destDir, childDestPath);
  655. todo.push(std::make_tuple(childSourceDirEntry, childSourceDirEntry));
  656. }
  657. }
  658. }
  659. }
  660. }
  661. void ProjectLibrary::deleteEntry(const Path& path)
  662. {
  663. if(FileSystem::exists(path))
  664. FileSystem::remove(path);
  665. LibraryEntry* entry = findEntry(path);
  666. if(entry != nullptr)
  667. {
  668. if(entry->type == LibraryEntryType::File)
  669. {
  670. deleteResourceInternal(static_cast<ResourceEntry*>(entry));
  671. Path metaPath = getMetaPath(path);
  672. if(FileSystem::isFile(metaPath))
  673. FileSystem::remove(metaPath);
  674. }
  675. else if(entry->type == LibraryEntryType::Directory)
  676. deleteDirectoryInternal(static_cast<DirectoryEntry*>(entry));
  677. }
  678. }
  679. void ProjectLibrary::reimport(const Path& path, const ImportOptionsPtr& importOptions, bool forceReimport)
  680. {
  681. LibraryEntry* entry = findEntry(path);
  682. if (entry != nullptr)
  683. {
  684. if (entry->type == LibraryEntryType::File)
  685. {
  686. ResourceEntry* resEntry = static_cast<ResourceEntry*>(entry);
  687. reimportResourceInternal(resEntry, importOptions, forceReimport);
  688. queueDependantForReimport(resEntry);
  689. }
  690. }
  691. }
  692. void ProjectLibrary::createInternalParentHierarchy(const Path& fullPath, DirectoryEntry** newHierarchyRoot, DirectoryEntry** newHierarchyLeaf)
  693. {
  694. Path parentPath = fullPath;
  695. DirectoryEntry* newEntryParent = nullptr;
  696. Stack<Path> parentPaths;
  697. do
  698. {
  699. Path newParentPath = parentPath.getParent();
  700. if(newParentPath == parentPath)
  701. break;
  702. LibraryEntry* newEntryParentLib = findEntry(newParentPath);
  703. if(newEntryParentLib != nullptr)
  704. {
  705. assert(newEntryParentLib->type == LibraryEntryType::Directory);
  706. newEntryParent = static_cast<DirectoryEntry*>(newEntryParentLib);
  707. break;
  708. }
  709. parentPaths.push(newParentPath);
  710. parentPath = newParentPath;
  711. } while (true);
  712. assert(newEntryParent != nullptr); // Must exist
  713. if(newHierarchyRoot != nullptr)
  714. *newHierarchyRoot = newEntryParent;
  715. while(!parentPaths.empty())
  716. {
  717. Path curPath = parentPaths.top();
  718. parentPaths.pop();
  719. newEntryParent = addDirectoryInternal(newEntryParent, curPath);
  720. }
  721. if(newHierarchyLeaf != nullptr)
  722. *newHierarchyLeaf = newEntryParent;
  723. }
  724. Path ProjectLibrary::getMetaPath(const Path& path) const
  725. {
  726. Path metaPath = path;
  727. metaPath.setFilename(metaPath.getWFilename() + L".meta");
  728. return metaPath;
  729. }
  730. bool ProjectLibrary::isMeta(const Path& fullPath) const
  731. {
  732. return fullPath.getWExtension() == L".meta";
  733. }
  734. void ProjectLibrary::onMonitorFileModified(const Path& path)
  735. {
  736. if(!isMeta(path))
  737. checkForModifications(path);
  738. else
  739. {
  740. Path resourcePath = path;
  741. resourcePath.setExtension(L"");
  742. checkForModifications(resourcePath);
  743. }
  744. }
  745. void ProjectLibrary::save()
  746. {
  747. std::shared_ptr<ProjectLibraryEntries> libEntries = ProjectLibraryEntries::create(*mRootEntry);
  748. Path libraryEntriesPath = mProjectFolder;
  749. libraryEntriesPath.append(INTERNAL_RESOURCES_DIR);
  750. libraryEntriesPath.append(LIBRARY_ENTRIES_FILENAME);
  751. FileEncoder fs(libraryEntriesPath);
  752. fs.encode(libEntries.get());
  753. Path resourceManifestPath = mProjectFolder;
  754. resourceManifestPath.append(INTERNAL_RESOURCES_DIR);
  755. resourceManifestPath.append(RESOURCE_MANIFEST_FILENAME);
  756. ResourceManifest::save(mResourceManifest, resourceManifestPath, mProjectFolder);
  757. }
  758. void ProjectLibrary::load()
  759. {
  760. if(mRootEntry != nullptr)
  761. {
  762. deleteDirectoryInternal(mRootEntry);
  763. mRootEntry = nullptr;
  764. }
  765. mRootEntry = bs_new<DirectoryEntry>(mResourcesFolder, mResourcesFolder.getWTail(), nullptr);
  766. Path libraryEntriesPath = mProjectFolder;
  767. libraryEntriesPath.append(INTERNAL_RESOURCES_DIR);
  768. libraryEntriesPath.append(LIBRARY_ENTRIES_FILENAME);
  769. if(FileSystem::exists(libraryEntriesPath))
  770. {
  771. FileDecoder fs(libraryEntriesPath);
  772. std::shared_ptr<ProjectLibraryEntries> libEntries = std::static_pointer_cast<ProjectLibraryEntries>(fs.decode());
  773. *mRootEntry = libEntries->getRootEntry();
  774. for(auto& child : mRootEntry->mChildren)
  775. child->parent = mRootEntry;
  776. mRootEntry->parent = nullptr;
  777. }
  778. // Load all meta files
  779. Stack<DirectoryEntry*> todo;
  780. todo.push(mRootEntry);
  781. while(!todo.empty())
  782. {
  783. DirectoryEntry* curDir = todo.top();
  784. todo.pop();
  785. for(auto& child : curDir->mChildren)
  786. {
  787. if(child->type == LibraryEntryType::File)
  788. {
  789. ResourceEntry* resEntry = static_cast<ResourceEntry*>(child);
  790. if(resEntry->meta == nullptr)
  791. {
  792. Path metaPath = resEntry->path;
  793. metaPath.setFilename(metaPath.getWFilename() + L".meta");
  794. if(FileSystem::isFile(metaPath))
  795. {
  796. FileDecoder fs(metaPath);
  797. std::shared_ptr<IReflectable> loadedMeta = fs.decode();
  798. if(loadedMeta != nullptr && loadedMeta->isDerivedFrom(ProjectResourceMeta::getRTTIStatic()))
  799. {
  800. ProjectResourceMetaPtr resourceMeta = std::static_pointer_cast<ProjectResourceMeta>(loadedMeta);
  801. resEntry->meta = resourceMeta;
  802. }
  803. }
  804. }
  805. addDependencies(resEntry);
  806. }
  807. else if(child->type == LibraryEntryType::Directory)
  808. {
  809. todo.push(static_cast<DirectoryEntry*>(child));
  810. }
  811. }
  812. }
  813. // Load resource manifest
  814. Path resourceManifestPath = mProjectFolder;
  815. resourceManifestPath.append(INTERNAL_RESOURCES_DIR);
  816. resourceManifestPath.append(RESOURCE_MANIFEST_FILENAME);
  817. if(FileSystem::exists(resourceManifestPath))
  818. {
  819. mResourceManifest = ResourceManifest::load(resourceManifestPath, mProjectFolder);
  820. }
  821. }
  822. void ProjectLibrary::doOnEntryRemoved(const LibraryEntry* entry)
  823. {
  824. if (!onEntryRemoved.empty())
  825. onEntryRemoved(entry->path);
  826. if (entry->type == LibraryEntryType::File)
  827. {
  828. const ResourceEntry* resEntry = static_cast<const ResourceEntry*>(entry);
  829. queueDependantForReimport(resEntry);
  830. removeDependencies(resEntry);
  831. }
  832. }
  833. void ProjectLibrary::doOnEntryAdded(const LibraryEntry* entry)
  834. {
  835. if (!onEntryAdded.empty())
  836. onEntryAdded(entry->path);
  837. if (entry->type == LibraryEntryType::File)
  838. {
  839. const ResourceEntry* resEntry = static_cast<const ResourceEntry*>(entry);
  840. queueDependantForReimport(resEntry);
  841. }
  842. }
  843. Vector<Path> ProjectLibrary::getImportDependencies(const ResourceEntry* entry)
  844. {
  845. Vector<Path> output;
  846. if (entry->meta->getTypeID() == TID_Shader)
  847. {
  848. SPtr<ShaderMetaData> metaData = std::static_pointer_cast<ShaderMetaData>(entry->meta->getResourceMetaData());
  849. for (auto& include : metaData->includes)
  850. output.push_back(include);
  851. }
  852. return output;
  853. }
  854. void ProjectLibrary::addDependencies(const ResourceEntry* entry)
  855. {
  856. Vector<Path> dependencies = getImportDependencies(entry);
  857. for (auto& dependency : dependencies)
  858. mDependencies[dependency].push_back(entry->path);
  859. }
  860. void ProjectLibrary::removeDependencies(const ResourceEntry* entry)
  861. {
  862. Vector<Path> dependencies = getImportDependencies(entry);
  863. for (auto& dependency : dependencies)
  864. {
  865. Vector<Path>& curDependencies = mDependencies[dependency];
  866. auto iterRemove = std::remove_if(curDependencies.begin(), curDependencies.end(),
  867. [&](const Path& x)
  868. {
  869. return x == entry->path;
  870. });
  871. curDependencies.erase(iterRemove, curDependencies.end());
  872. }
  873. }
  874. void ProjectLibrary::queueDependantForReimport(const ResourceEntry* entry)
  875. {
  876. auto iterFind = mDependencies.find(entry->path);
  877. if (iterFind == mDependencies.end())
  878. return;
  879. for (auto& dependency : iterFind->second)
  880. mReimportQueue.insert(dependency);
  881. }
  882. }