BsProjectLibrary.cpp 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724
  1. #include "BsProjectLibrary.h"
  2. #include "CmPath.h"
  3. #include "CmFileSystem.h"
  4. #include "CmException.h"
  5. #include "CmResources.h"
  6. #include "CmResourceManifest.h"
  7. #include "CmImporter.h"
  8. #include "BsResourceMeta.h"
  9. #include "CmResources.h"
  10. #include "CmImporter.h"
  11. #include "CmImportOptions.h"
  12. #include "CmFileSerializer.h"
  13. #include "CmFolderMonitor.h"
  14. #include "CmDebug.h"
  15. #include "BsProjectLibraryEntries.h"
  16. using namespace CamelotFramework;
  17. using namespace BansheeEngine;
  18. namespace BansheeEditor
  19. {
  20. const WString ProjectLibrary::RESOURCES_DIR = L"Resources";
  21. const WString ProjectLibrary::INTERNAL_RESOURCES_DIR = L"Internal\\Resources";
  22. const WString ProjectLibrary::LIBRARY_ENTRIES_FILENAME = L"ProjectLibrary.asset";
  23. const WString ProjectLibrary::RESOURCE_MANIFEST_FILENAME = L"ResourceManifest.asset";
  24. ProjectLibrary::LibraryEntry::LibraryEntry(const CM::WString& path, const CM::WString& name, DirectoryEntry* parent, LibraryEntryType type)
  25. :path(path), parent(parent), type(type), elementName(name)
  26. { }
  27. ProjectLibrary::ResourceEntry::ResourceEntry(const CM::WString& path, const CM::WString& name, DirectoryEntry* parent)
  28. :LibraryEntry(path, name, parent, LibraryEntryType::File), lastUpdateTime(0)
  29. { }
  30. ProjectLibrary::DirectoryEntry::DirectoryEntry(const CM::WString& path, const CM::WString& name, DirectoryEntry* parent)
  31. :LibraryEntry(path, name, parent, LibraryEntryType::Directory)
  32. { }
  33. ProjectLibrary::ProjectLibrary(const WString& projectFolder)
  34. :mRootEntry(nullptr), mProjectFolder(projectFolder)
  35. {
  36. mResourcesFolder = Path::combine(mProjectFolder, RESOURCES_DIR);
  37. mMonitor = cm_new<FolderMonitor>();
  38. FolderChange folderChanges = (FolderChange)((UINT32)FolderChange::FileName | (UINT32)FolderChange::DirName |
  39. (UINT32)FolderChange::Creation | (UINT32)FolderChange::LastWrite);
  40. mMonitor->startMonitor(mResourcesFolder, true, folderChanges);
  41. mMonitor->onAdded.connect(boost::bind(&ProjectLibrary::onMonitorFileModified, this, _1));
  42. mMonitor->onRemoved.connect(boost::bind(&ProjectLibrary::onMonitorFileModified, this, _1));
  43. mMonitor->onModified.connect(boost::bind(&ProjectLibrary::onMonitorFileModified, this, _1));
  44. load();
  45. if(mResourceManifest == nullptr)
  46. mResourceManifest = ResourceManifest::create("ProjectLibrary");
  47. gResources().registerResourceManifest(mResourceManifest);
  48. checkForModifications(mResourcesFolder);
  49. }
  50. ProjectLibrary::~ProjectLibrary()
  51. {
  52. save();
  53. mMonitor->stopMonitorAll();
  54. cm_delete(mMonitor);
  55. if(mRootEntry != nullptr)
  56. deleteDirectoryInternal(mRootEntry);
  57. }
  58. void ProjectLibrary::update()
  59. {
  60. mMonitor->update();
  61. }
  62. void ProjectLibrary::checkForModifications(const CM::WString& fullPath)
  63. {
  64. if(!Path::includes(fullPath, mResourcesFolder))
  65. return; // Folder not part of our resources path, so no modifications
  66. if(mRootEntry == nullptr)
  67. {
  68. WString resPath = mResourcesFolder;
  69. mRootEntry = cm_new<DirectoryEntry>(resPath, Path::getFilename(resPath), nullptr);
  70. }
  71. WString pathToSearch = fullPath;
  72. LibraryEntry* entry = findEntry(pathToSearch);
  73. if(entry == nullptr) // File could be new, try to find parent directory entry
  74. {
  75. WString parentDirPath = Path::parentPath(pathToSearch);
  76. entry = findEntry(parentDirPath);
  77. // Cannot find parent directory. Create the needed hierarchy.
  78. DirectoryEntry* entryParent = nullptr;
  79. DirectoryEntry* newHierarchyParent = nullptr;
  80. if(entry == nullptr)
  81. createInternalParentHierarchy(pathToSearch, &newHierarchyParent, &entryParent);
  82. else
  83. entryParent = static_cast<DirectoryEntry*>(entry);
  84. if(FileSystem::isFile(pathToSearch))
  85. {
  86. addResourceInternal(entryParent, pathToSearch);
  87. }
  88. else if(FileSystem::isDirectory(pathToSearch))
  89. {
  90. addDirectoryInternal(entryParent, pathToSearch);
  91. if(newHierarchyParent == nullptr)
  92. checkForModifications(pathToSearch);
  93. }
  94. if(newHierarchyParent != nullptr)
  95. checkForModifications(newHierarchyParent->path);
  96. }
  97. else if(entry->type == LibraryEntryType::File)
  98. {
  99. if(FileSystem::isFile(entry->path))
  100. {
  101. reimportResourceInternal(static_cast<ResourceEntry*>(entry));
  102. }
  103. else
  104. {
  105. deleteResourceInternal(static_cast<ResourceEntry*>(entry));
  106. }
  107. }
  108. else if(entry->type == LibraryEntryType::Directory) // Check folder and all subfolders for modifications
  109. {
  110. if(!FileSystem::isDirectory(entry->path))
  111. {
  112. deleteDirectoryInternal(static_cast<DirectoryEntry*>(entry));
  113. }
  114. else
  115. {
  116. Stack<DirectoryEntry*>::type todo;
  117. todo.push(static_cast<DirectoryEntry*>(entry));
  118. Vector<WString>::type childFiles;
  119. Vector<WString>::type childDirectories;
  120. Vector<bool>::type existingEntries;
  121. Vector<LibraryEntry*>::type toDelete;
  122. while(!todo.empty())
  123. {
  124. DirectoryEntry* currentDir = todo.top();
  125. todo.pop();
  126. existingEntries.clear();
  127. existingEntries.resize(currentDir->mChildren.size());
  128. for(UINT32 i = 0; i < (UINT32)currentDir->mChildren.size(); i++)
  129. existingEntries[i] = false;
  130. childFiles.clear();
  131. childDirectories.clear();
  132. FileSystem::getChildren(currentDir->path, childFiles, childDirectories);
  133. for(auto& filePath : childFiles)
  134. {
  135. if(isMeta(filePath))
  136. {
  137. WString sourceFilePath = filePath;
  138. Path::replaceExtension(sourceFilePath, L"");
  139. if(!FileSystem::isFile(sourceFilePath))
  140. {
  141. LOGWRN("Found a .meta file without a corresponding resource. Deleting.");
  142. FileSystem::remove(filePath);
  143. }
  144. }
  145. else
  146. {
  147. ResourceEntry* existingEntry = nullptr;
  148. UINT32 idx = 0;
  149. for(auto& child : currentDir->mChildren)
  150. {
  151. if(child->type == LibraryEntryType::File && Path::equals(child->path, filePath))
  152. {
  153. existingEntries[idx] = true;
  154. existingEntry = static_cast<ResourceEntry*>(child);
  155. break;
  156. }
  157. idx++;
  158. }
  159. if(existingEntry != nullptr)
  160. {
  161. reimportResourceInternal(existingEntry);
  162. }
  163. else
  164. {
  165. addResourceInternal(currentDir, filePath);
  166. }
  167. }
  168. }
  169. for(auto& dirPath : childDirectories)
  170. {
  171. DirectoryEntry* existingEntry = nullptr;
  172. UINT32 idx = 0;
  173. for(auto& child : currentDir->mChildren)
  174. {
  175. if(child->type == LibraryEntryType::Directory && Path::equals(child->path, dirPath))
  176. {
  177. existingEntries[idx] = true;
  178. existingEntry = static_cast<DirectoryEntry*>(child);
  179. break;
  180. }
  181. idx++;
  182. }
  183. if(existingEntry == nullptr)
  184. addDirectoryInternal(currentDir, dirPath);
  185. }
  186. {
  187. for(UINT32 i = 0; i < (UINT32)existingEntries.size(); i++)
  188. {
  189. if(existingEntries[i])
  190. continue;
  191. toDelete.push_back(currentDir->mChildren[i]);
  192. }
  193. for(auto& child : toDelete)
  194. {
  195. if(child->type == LibraryEntryType::Directory)
  196. deleteDirectoryInternal(static_cast<DirectoryEntry*>(child));
  197. else if(child->type == LibraryEntryType::File)
  198. deleteResourceInternal(static_cast<ResourceEntry*>(child));
  199. }
  200. }
  201. for(auto& child : currentDir->mChildren)
  202. {
  203. if(child->type == LibraryEntryType::Directory)
  204. todo.push(static_cast<DirectoryEntry*>(child));
  205. }
  206. }
  207. }
  208. }
  209. }
  210. ProjectLibrary::ResourceEntry* ProjectLibrary::addResourceInternal(DirectoryEntry* parent, const CM::WString& filePath)
  211. {
  212. ResourceEntry* newResource = cm_new<ResourceEntry>(filePath, Path::getFilename(filePath), parent);
  213. parent->mChildren.push_back(newResource);
  214. reimportResourceInternal(newResource);
  215. if(!onEntryAdded.empty())
  216. onEntryAdded(filePath);
  217. return newResource;
  218. }
  219. ProjectLibrary::DirectoryEntry* ProjectLibrary::addDirectoryInternal(DirectoryEntry* parent, const CM::WString& dirPath)
  220. {
  221. DirectoryEntry* newEntry = cm_new<DirectoryEntry>(dirPath, Path::getFilename(dirPath), parent);
  222. parent->mChildren.push_back(newEntry);
  223. if(!onEntryAdded.empty())
  224. onEntryAdded(dirPath);
  225. return newEntry;
  226. }
  227. void ProjectLibrary::deleteResourceInternal(ResourceEntry* resource)
  228. {
  229. if(resource->meta != nullptr)
  230. {
  231. WString path;
  232. if(mResourceManifest->uuidToFilePath(resource->meta->getUUID(), path))
  233. {
  234. if(FileSystem::isFile(path))
  235. FileSystem::remove(path);
  236. mResourceManifest->unregisterResource(resource->meta->getUUID());
  237. }
  238. }
  239. DirectoryEntry* parent = resource->parent;
  240. auto findIter = std::find_if(parent->mChildren.begin(), parent->mChildren.end(),
  241. [&] (const LibraryEntry* entry) { return entry == resource; });
  242. parent->mChildren.erase(findIter);
  243. if(!onEntryRemoved.empty())
  244. onEntryRemoved(resource->path);
  245. cm_delete(resource);
  246. }
  247. void ProjectLibrary::deleteDirectoryInternal(DirectoryEntry* directory)
  248. {
  249. if(directory == mRootEntry)
  250. mRootEntry = nullptr;
  251. CM::Vector<LibraryEntry*>::type childrenToDestroy = directory->mChildren;
  252. for(auto& child : childrenToDestroy)
  253. {
  254. if(child->type == LibraryEntryType::Directory)
  255. deleteDirectoryInternal(static_cast<DirectoryEntry*>(child));
  256. else
  257. deleteResourceInternal(static_cast<ResourceEntry*>(child));
  258. }
  259. DirectoryEntry* parent = directory->parent;
  260. if(parent != nullptr)
  261. {
  262. auto findIter = std::find_if(parent->mChildren.begin(), parent->mChildren.end(),
  263. [&] (const LibraryEntry* entry) { return entry == directory; });
  264. parent->mChildren.erase(findIter);
  265. }
  266. if(!onEntryRemoved.empty())
  267. onEntryRemoved(directory->path);
  268. cm_delete(directory);
  269. }
  270. void ProjectLibrary::reimportResourceInternal(ResourceEntry* resource)
  271. {
  272. WString ext = Path::getExtension(resource->path);
  273. WString metaPath = resource->path + L".meta";
  274. ext = ext.substr(1, ext.size() - 1); // Remove the .
  275. if(!Importer::instance().supportsFileType(ext))
  276. return;
  277. if(resource->meta == nullptr)
  278. {
  279. FileSerializer fs;
  280. if(FileSystem::isFile(metaPath))
  281. {
  282. std::shared_ptr<IReflectable> loadedMeta = fs.decode(metaPath);
  283. if(loadedMeta != nullptr && loadedMeta->isDerivedFrom(ResourceMeta::getRTTIStatic()))
  284. {
  285. ResourceMetaPtr resourceMeta = std::static_pointer_cast<ResourceMeta>(loadedMeta);
  286. resource->meta = resourceMeta;
  287. }
  288. }
  289. }
  290. if(!isUpToDate(resource))
  291. {
  292. ImportOptionsPtr importOptions = nullptr;
  293. if(resource->meta != nullptr)
  294. importOptions = resource->meta->getImportOptions();
  295. else
  296. importOptions = Importer::instance().createImportOptions(resource->path);
  297. HResource importedResource;
  298. if(resource->meta == nullptr)
  299. {
  300. importedResource = Importer::instance().import(resource->path, importOptions);
  301. resource->meta = ResourceMeta::create(importedResource.getUUID(), importOptions);
  302. FileSerializer fs;
  303. fs.encode(resource->meta.get(), metaPath);
  304. }
  305. else
  306. {
  307. importedResource = HResource(resource->meta->getUUID());
  308. Importer::instance().reimport(importedResource, resource->path, importOptions);
  309. }
  310. WString internalResourcesPath = Path::combine(mProjectFolder, INTERNAL_RESOURCES_DIR);
  311. if(!FileSystem::isDirectory(internalResourcesPath))
  312. FileSystem::createDir(internalResourcesPath);
  313. internalResourcesPath = Path::combine(internalResourcesPath, toWString(importedResource.getUUID()) + L".asset");
  314. gResources().save(importedResource, internalResourcesPath, true);
  315. gResources().unload(importedResource);
  316. mResourceManifest->registerResource(importedResource.getUUID(), internalResourcesPath);
  317. resource->lastUpdateTime = std::time(nullptr);
  318. }
  319. }
  320. bool ProjectLibrary::isUpToDate(ResourceEntry* resource) const
  321. {
  322. if(resource->meta == nullptr)
  323. return false;
  324. WString path;
  325. if(!mResourceManifest->uuidToFilePath(resource->meta->getUUID(), path))
  326. return false;
  327. if(!FileSystem::isFile(path))
  328. return false;
  329. std::time_t lastModifiedTime = FileSystem::getLastModifiedTime(path);
  330. return lastModifiedTime <= resource->lastUpdateTime;
  331. }
  332. ProjectLibrary::LibraryEntry* ProjectLibrary::findEntry(const CM::WString& fullPath) const
  333. {
  334. Vector<WString>::type pathElems = Path::split(fullPath);
  335. Vector<WString>::type rootElems = Path::split(mRootEntry->path);
  336. auto pathIter = pathElems.begin();
  337. auto rootIter = rootElems.begin();
  338. while(pathIter != pathElems.end() && rootIter != rootElems.end() && Path::comparePathElements(*pathIter, *rootIter))
  339. {
  340. ++pathIter;
  341. ++rootIter;
  342. }
  343. if(pathIter == pathElems.begin()) // Not a single entry matches Resources path
  344. return nullptr;
  345. --pathIter;
  346. Stack<LibraryEntry*>::type todo;
  347. todo.push(mRootEntry);
  348. while(!todo.empty())
  349. {
  350. LibraryEntry* current = todo.top();
  351. todo.pop();
  352. if(Path::comparePathElements(*pathIter, current->elementName))
  353. {
  354. ++pathIter;
  355. if(pathIter == pathElems.end())
  356. return current;
  357. while(!todo.empty())
  358. todo.pop();
  359. if(current->type == LibraryEntryType::Directory)
  360. {
  361. DirectoryEntry* dirEntry = static_cast<DirectoryEntry*>(current);
  362. for(auto& child : dirEntry->mChildren)
  363. todo.push(child);
  364. }
  365. }
  366. }
  367. return nullptr;
  368. }
  369. void ProjectLibrary::moveEntry(const CM::WString& oldPath, const CM::WString& newPath)
  370. {
  371. if(FileSystem::isFile(oldPath) || FileSystem::isDirectory(oldPath))
  372. FileSystem::move(oldPath, newPath);
  373. WString oldMetaPath = getMetaPath(oldPath);
  374. WString newMetaPath = getMetaPath(newPath);
  375. LibraryEntry* oldEntry = findEntry(oldPath);
  376. if(oldEntry != nullptr) // Moving from the Resources folder
  377. {
  378. // Moved outside of Resources, delete entry & meta file
  379. if(!Path::includes(newPath, mResourcesFolder))
  380. {
  381. if(oldEntry->type == LibraryEntryType::File)
  382. {
  383. deleteResourceInternal(static_cast<ResourceEntry*>(oldEntry));
  384. if(FileSystem::isFile(oldMetaPath))
  385. FileSystem::remove(oldMetaPath);
  386. }
  387. else if(oldEntry->type == LibraryEntryType::Directory)
  388. deleteDirectoryInternal(static_cast<DirectoryEntry*>(oldEntry));
  389. }
  390. else // Just moving internally
  391. {
  392. if(FileSystem::isFile(oldMetaPath))
  393. FileSystem::move(oldMetaPath, newMetaPath);
  394. DirectoryEntry* parent = oldEntry->parent;
  395. auto findIter = std::find(parent->mChildren.begin(), parent->mChildren.end(), oldEntry);
  396. if(findIter != parent->mChildren.end())
  397. parent->mChildren.erase(findIter);
  398. WString parentPath = Path::parentPath(newPath);
  399. DirectoryEntry* newEntryParent = nullptr;
  400. LibraryEntry* newEntryParentLib = findEntry(parentPath);
  401. if(newEntryParentLib != nullptr)
  402. {
  403. assert(newEntryParentLib->type == LibraryEntryType::Directory);
  404. newEntryParent = static_cast<DirectoryEntry*>(newEntryParentLib);
  405. }
  406. DirectoryEntry* newHierarchyParent = nullptr;
  407. if(newEntryParent == nullptr) // New path parent doesn't exist, so we need to create the hierarchy
  408. createInternalParentHierarchy(newPath, &newHierarchyParent, &newEntryParent);
  409. newEntryParent->mChildren.push_back(oldEntry);
  410. oldEntry->parent = newEntryParent;
  411. oldEntry->path = newPath;
  412. oldEntry->elementName = Path::getFilename(newPath);
  413. if(oldEntry->type == LibraryEntryType::Directory) // Update child paths
  414. {
  415. Stack<LibraryEntry*>::type todo;
  416. todo.push(oldEntry);
  417. while(!todo.empty())
  418. {
  419. LibraryEntry* curEntry = todo.top();
  420. todo.pop();
  421. DirectoryEntry* curDirEntry = static_cast<DirectoryEntry*>(curEntry);
  422. for(auto& child : curDirEntry->mChildren)
  423. {
  424. child->path = Path::combine(child->parent->path, child->elementName);
  425. if(child->type == LibraryEntryType::Directory)
  426. todo.push(child);
  427. }
  428. }
  429. }
  430. if(!onEntryRemoved.empty())
  431. onEntryRemoved(oldPath);
  432. if(!onEntryAdded.empty())
  433. onEntryAdded(newPath);
  434. if(newHierarchyParent != nullptr)
  435. checkForModifications(newHierarchyParent->path);
  436. }
  437. }
  438. else // Moving from outside of the Resources folder (likely adding a new resource)
  439. {
  440. checkForModifications(newPath);
  441. }
  442. }
  443. void ProjectLibrary::deleteEntry(const CM::WString& path)
  444. {
  445. if(FileSystem::isFile(path))
  446. FileSystem::remove(path);
  447. LibraryEntry* entry = findEntry(path);
  448. if(entry != nullptr)
  449. {
  450. if(entry->type == LibraryEntryType::File)
  451. {
  452. deleteResourceInternal(static_cast<ResourceEntry*>(entry));
  453. WString metaPath = getMetaPath(path);
  454. if(FileSystem::isFile(metaPath))
  455. FileSystem::remove(metaPath);
  456. }
  457. else if(entry->type == LibraryEntryType::Directory)
  458. deleteDirectoryInternal(static_cast<DirectoryEntry*>(entry));
  459. }
  460. }
  461. void ProjectLibrary::createInternalParentHierarchy(const CM::WString& fullPath, DirectoryEntry** newHierarchyRoot, DirectoryEntry** newHierarchyLeaf)
  462. {
  463. WString parentPath = fullPath;
  464. DirectoryEntry* newEntryParent = nullptr;
  465. Stack<WString>::type parentPaths;
  466. do
  467. {
  468. WString newParentPath = Path::parentPath(parentPath);
  469. if(newParentPath == parentPath)
  470. break;
  471. LibraryEntry* newEntryParentLib = findEntry(newParentPath);
  472. if(newEntryParentLib != nullptr)
  473. {
  474. assert(newEntryParentLib->type == LibraryEntryType::Directory);
  475. newEntryParent = static_cast<DirectoryEntry*>(newEntryParentLib);
  476. break;
  477. }
  478. parentPaths.push(newParentPath);
  479. parentPath = newParentPath;
  480. } while (true);
  481. assert(newEntryParent != nullptr); // Must exist
  482. if(newHierarchyRoot != nullptr)
  483. *newHierarchyRoot = newEntryParent;
  484. while(!parentPaths.empty())
  485. {
  486. WString curPath = parentPaths.top();
  487. parentPaths.pop();
  488. newEntryParent = addDirectoryInternal(newEntryParent, curPath);
  489. }
  490. if(newHierarchyLeaf != nullptr)
  491. *newHierarchyLeaf = newEntryParent;
  492. }
  493. WString ProjectLibrary::getMetaPath(const CM::WString& path) const
  494. {
  495. WString metaPath = path + L".meta";
  496. return metaPath;
  497. }
  498. bool ProjectLibrary::isMeta(const WString& fullPath) const
  499. {
  500. return Path::getExtension(fullPath) == L".meta";
  501. }
  502. void ProjectLibrary::onMonitorFileModified(const WString& path)
  503. {
  504. if(!isMeta(path))
  505. checkForModifications(path);
  506. else
  507. {
  508. WString resourcePath = path;
  509. Path::replaceExtension(resourcePath, L"");
  510. checkForModifications(resourcePath);
  511. }
  512. }
  513. void ProjectLibrary::save()
  514. {
  515. std::shared_ptr<ProjectLibraryEntries> libEntries = ProjectLibraryEntries::create(*mRootEntry);
  516. WString libraryEntriesPath = Path::combine(mProjectFolder, INTERNAL_RESOURCES_DIR);
  517. libraryEntriesPath = Path::combine(libraryEntriesPath, LIBRARY_ENTRIES_FILENAME);
  518. FileSerializer fs;
  519. fs.encode(libEntries.get(), libraryEntriesPath);
  520. WString resourceManifestPath = Path::combine(mProjectFolder, INTERNAL_RESOURCES_DIR);
  521. resourceManifestPath = Path::combine(resourceManifestPath, RESOURCE_MANIFEST_FILENAME);
  522. ResourceManifest::save(mResourceManifest, resourceManifestPath, mProjectFolder);
  523. }
  524. void ProjectLibrary::load()
  525. {
  526. if(mRootEntry != nullptr)
  527. {
  528. deleteDirectoryInternal(mRootEntry);
  529. mRootEntry = nullptr;
  530. }
  531. WString resPath = mResourcesFolder;
  532. mRootEntry = cm_new<DirectoryEntry>(resPath, Path::getFilename(resPath), nullptr);
  533. WString libraryEntriesPath = Path::combine(mProjectFolder, INTERNAL_RESOURCES_DIR);
  534. libraryEntriesPath = Path::combine(libraryEntriesPath, LIBRARY_ENTRIES_FILENAME);
  535. if(FileSystem::exists(libraryEntriesPath))
  536. {
  537. FileSerializer fs;
  538. std::shared_ptr<ProjectLibraryEntries> libEntries = std::static_pointer_cast<ProjectLibraryEntries>(fs.decode(libraryEntriesPath));
  539. *mRootEntry = libEntries->getRootEntry();
  540. }
  541. // Load all meta files
  542. Stack<DirectoryEntry*>::type todo;
  543. todo.push(mRootEntry);
  544. while(!todo.empty())
  545. {
  546. DirectoryEntry* curDir = todo.top();
  547. todo.pop();
  548. for(auto& child : curDir->mChildren)
  549. {
  550. if(child->type == LibraryEntryType::File)
  551. {
  552. ResourceEntry* resEntry = static_cast<ResourceEntry*>(child);
  553. if(resEntry->meta == nullptr)
  554. {
  555. WString metaPath = resEntry->path + L".meta";
  556. FileSerializer fs;
  557. if(FileSystem::isFile(metaPath))
  558. {
  559. std::shared_ptr<IReflectable> loadedMeta = fs.decode(metaPath);
  560. if(loadedMeta != nullptr && loadedMeta->isDerivedFrom(ResourceMeta::getRTTIStatic()))
  561. {
  562. ResourceMetaPtr resourceMeta = std::static_pointer_cast<ResourceMeta>(loadedMeta);
  563. resEntry->meta = resourceMeta;
  564. }
  565. }
  566. }
  567. }
  568. else if(child->type == LibraryEntryType::Directory)
  569. {
  570. todo.push(static_cast<DirectoryEntry*>(child));
  571. }
  572. }
  573. }
  574. // Load resource manifest
  575. WString resourceManifestPath = Path::combine(mProjectFolder, INTERNAL_RESOURCES_DIR);
  576. resourceManifestPath = Path::combine(resourceManifestPath, RESOURCE_MANIFEST_FILENAME);
  577. if(FileSystem::exists(resourceManifestPath))
  578. {
  579. mResourceManifest = ResourceManifest::load(resourceManifestPath, mProjectFolder);
  580. }
  581. }
  582. }