BsProjectLibrary.cpp 56 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948
  1. //********************************** Banshee Engine (www.banshee3d.com) **************************************************//
  2. //**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
  3. #include "Library/BsProjectLibrary.h"
  4. #include "FileSystem/BsFileSystem.h"
  5. #include "Error/BsException.h"
  6. #include "Resources/BsResources.h"
  7. #include "Resources/BsResourceManifest.h"
  8. #include "Importer/BsImporter.h"
  9. #include "Library/BsProjectResourceMeta.h"
  10. #include "Resources/BsResources.h"
  11. #include "Importer/BsImporter.h"
  12. #include "Importer/BsImportOptions.h"
  13. #include "Serialization/BsFileSerializer.h"
  14. #include "Serialization/BsSerializedObject.h"
  15. #include "Serialization/BsBinaryDiff.h"
  16. #include "Debug/BsDebug.h"
  17. #include "Library/BsProjectLibraryEntries.h"
  18. #include "Resources/BsResource.h"
  19. #include "BsEditorApplication.h"
  20. #include "Material/BsShader.h"
  21. #include "Image/BsTexture.h"
  22. #include "String/BsUnicode.h"
  23. #include "CoreThread/BsCoreThread.h"
  24. #include <regex>
  25. #include "Threading/BsTaskScheduler.h"
  26. using namespace std::placeholders;
  27. namespace bs
  28. {
  29. ProjectResourceIcons generatePreviewIcons(Resource& resource)
  30. {
  31. ProjectResourceIcons icons;
  32. const UINT32 typeId = resource.getTypeId();
  33. if(typeId == TID_Texture)
  34. {
  35. Texture& texture = static_cast<Texture&>(resource);
  36. const TextureProperties& props = texture.getProperties();
  37. const SPtr<PixelData> srcData = props.allocBuffer(0, 0);
  38. AsyncOp readOp = texture.readData(srcData);
  39. gCoreThread().submitAll(true);
  40. // 256
  41. const SPtr<PixelData> data256 = PixelData::create(256, 256, 1, props.getFormat());
  42. PixelUtil::scale(*srcData, *data256);
  43. // 192
  44. const SPtr<PixelData> data192 = PixelData::create(192, 192, 1, props.getFormat());
  45. PixelUtil::scale(*data256, *data192);
  46. // 128
  47. const SPtr<PixelData> data128 = PixelData::create(128, 128, 1, props.getFormat());
  48. PixelUtil::scale(*data192, *data128);
  49. // 96
  50. const SPtr<PixelData> data96 = PixelData::create(96, 96, 1, props.getFormat());
  51. PixelUtil::scale(*data128, *data96);
  52. // 64
  53. const SPtr<PixelData> data64 = PixelData::create(64, 64, 1, props.getFormat());
  54. PixelUtil::scale(*data96, *data64);
  55. // 48
  56. const SPtr<PixelData> data48 = PixelData::create(48, 48, 1, props.getFormat());
  57. PixelUtil::scale(*data64, *data48);
  58. // 32
  59. const SPtr<PixelData> data32 = PixelData::create(32, 32, 1, props.getFormat());
  60. PixelUtil::scale(*data48, *data32);
  61. // 16
  62. const SPtr<PixelData> data16 = PixelData::create(16, 16, 1, props.getFormat());
  63. PixelUtil::scale(*data32, *data16);
  64. icons.icon16 = Texture::create(data16);
  65. icons.icon32 = Texture::create(data32);
  66. icons.icon48 = Texture::create(data48);
  67. icons.icon64 = Texture::create(data64);
  68. icons.icon96 = Texture::create(data96);
  69. icons.icon128 = Texture::create(data128);
  70. icons.icon192 = Texture::create(data192);
  71. icons.icon256 = Texture::create(data256);
  72. }
  73. return icons;
  74. }
  75. const Path TEMP_DIR = "Temp/";
  76. const Path INTERNAL_TEMP_DIR = PROJECT_INTERNAL_DIR + TEMP_DIR;
  77. const Path ProjectLibrary::RESOURCES_DIR = "Resources/";
  78. const Path ProjectLibrary::INTERNAL_RESOURCES_DIR = PROJECT_INTERNAL_DIR + RESOURCES_DIR;
  79. const char* ProjectLibrary::LIBRARY_ENTRIES_FILENAME = "ProjectLibrary.asset";
  80. const char* ProjectLibrary::RESOURCE_MANIFEST_FILENAME = "ResourceManifest.asset";
  81. ProjectLibrary::LibraryEntry::LibraryEntry()
  82. :type(LibraryEntryType::Directory)
  83. { }
  84. ProjectLibrary::LibraryEntry::LibraryEntry(const Path& path, const String& name, DirectoryEntry* parent,
  85. LibraryEntryType type)
  86. :type(type), path(path), elementName(name), elementNameHash(bs_hash(UTF8::toLower(name))), parent(parent)
  87. { }
  88. ProjectLibrary::FileEntry::FileEntry(const Path& path, const String& name, DirectoryEntry* parent)
  89. :LibraryEntry(path, name, parent, LibraryEntryType::File)
  90. { }
  91. ProjectLibrary::DirectoryEntry::DirectoryEntry(const Path& path, const String& name, DirectoryEntry* parent)
  92. :LibraryEntry(path, name, parent, LibraryEntryType::Directory)
  93. { }
  94. ProjectLibrary::ProjectLibrary()
  95. : mRootEntry(nullptr), mIsLoaded(false)
  96. {
  97. mRootEntry = bs_ushared_ptr_new<DirectoryEntry>(mResourcesFolder, mResourcesFolder.getTail(), nullptr);
  98. }
  99. ProjectLibrary::~ProjectLibrary()
  100. {
  101. _finishQueuedImports(true);
  102. clearEntries();
  103. }
  104. UINT32 ProjectLibrary::checkForModifications(const Path& fullPath)
  105. {
  106. UINT32 resourcesToImport = 0;
  107. if (!mResourcesFolder.includes(fullPath))
  108. return resourcesToImport; // Folder not part of our resources path, so no modifications
  109. if(mRootEntry == nullptr)
  110. mRootEntry = bs_ushared_ptr_new<DirectoryEntry>(mResourcesFolder, mResourcesFolder.getTail(), nullptr);
  111. Path pathToSearch = fullPath;
  112. USPtr<LibraryEntry> entry = findEntry(pathToSearch);
  113. if (entry == nullptr) // File could be new, try to find parent directory entry
  114. {
  115. if (FileSystem::exists(pathToSearch))
  116. {
  117. if (isMeta(pathToSearch))
  118. {
  119. Path sourceFilePath = pathToSearch;
  120. sourceFilePath.setExtension("");
  121. if (!FileSystem::isFile(sourceFilePath))
  122. {
  123. LOGWRN("Found a .meta file without a corresponding resource. Deleting.");
  124. FileSystem::remove(pathToSearch);
  125. }
  126. }
  127. else
  128. {
  129. Path parentDirPath = pathToSearch.getParent();
  130. entry = findEntry(parentDirPath);
  131. // Cannot find parent directory. Create the needed hierarchy.
  132. DirectoryEntry* entryParent = nullptr;
  133. DirectoryEntry* newHierarchyParent = nullptr;
  134. if (entry == nullptr)
  135. createInternalParentHierarchy(pathToSearch, &newHierarchyParent, &entryParent);
  136. else
  137. entryParent = static_cast<DirectoryEntry*>(entry.get());
  138. if (FileSystem::isFile(pathToSearch))
  139. addResourceInternal(entryParent, pathToSearch);
  140. else if (FileSystem::isDirectory(pathToSearch))
  141. {
  142. addDirectoryInternal(entryParent, pathToSearch);
  143. resourcesToImport += checkForModifications(pathToSearch);
  144. }
  145. }
  146. }
  147. }
  148. else if(entry->type == LibraryEntryType::File)
  149. {
  150. if(FileSystem::isFile(entry->path))
  151. {
  152. FileEntry* resEntry = static_cast<FileEntry*>(entry.get());
  153. if(reimportResourceInternal(resEntry))
  154. resourcesToImport++;
  155. }
  156. else
  157. deleteResourceInternal(static_pointer_cast<FileEntry>(entry));
  158. }
  159. else if(entry->type == LibraryEntryType::Directory) // Check folder and all subfolders for modifications
  160. {
  161. if(!FileSystem::isDirectory(entry->path))
  162. {
  163. deleteDirectoryInternal(static_pointer_cast<DirectoryEntry>(entry));
  164. }
  165. else
  166. {
  167. Stack<DirectoryEntry*> todo;
  168. todo.push(static_cast<DirectoryEntry*>(entry.get()));
  169. Vector<Path> childFiles;
  170. Vector<Path> childDirectories;
  171. Vector<bool> existingEntries;
  172. Vector<USPtr<LibraryEntry>> toDelete;
  173. while(!todo.empty())
  174. {
  175. DirectoryEntry* currentDir = todo.top();
  176. todo.pop();
  177. existingEntries.clear();
  178. existingEntries.resize(currentDir->mChildren.size());
  179. for(UINT32 i = 0; i < (UINT32)currentDir->mChildren.size(); i++)
  180. existingEntries[i] = false;
  181. childFiles.clear();
  182. childDirectories.clear();
  183. FileSystem::getChildren(currentDir->path, childFiles, childDirectories);
  184. for(auto& filePath : childFiles)
  185. {
  186. if(isMeta(filePath))
  187. {
  188. Path sourceFilePath = filePath;
  189. sourceFilePath.setExtension("");
  190. if(!FileSystem::isFile(sourceFilePath))
  191. {
  192. LOGWRN("Found a .meta file without a corresponding resource. Deleting.");
  193. FileSystem::remove(filePath);
  194. }
  195. }
  196. else
  197. {
  198. FileEntry* existingEntry = nullptr;
  199. UINT32 idx = 0;
  200. for(auto& child : currentDir->mChildren)
  201. {
  202. if(child->type == LibraryEntryType::File && child->path == filePath)
  203. {
  204. existingEntries[idx] = true;
  205. existingEntry = static_cast<FileEntry*>(child.get());
  206. break;
  207. }
  208. idx++;
  209. }
  210. if(existingEntry != nullptr)
  211. {
  212. if(reimportResourceInternal(existingEntry))
  213. resourcesToImport++;
  214. }
  215. else
  216. {
  217. addResourceInternal(currentDir, filePath);
  218. resourcesToImport++;
  219. }
  220. }
  221. }
  222. for(auto& dirPath : childDirectories)
  223. {
  224. DirectoryEntry* existingEntry = nullptr;
  225. UINT32 idx = 0;
  226. for(auto& child : currentDir->mChildren)
  227. {
  228. if(child->type == LibraryEntryType::Directory && child->path == dirPath)
  229. {
  230. existingEntries[idx] = true;
  231. existingEntry = static_cast<DirectoryEntry*>(child.get());
  232. break;
  233. }
  234. idx++;
  235. }
  236. if(existingEntry == nullptr)
  237. addDirectoryInternal(currentDir, dirPath);
  238. }
  239. {
  240. for(UINT32 i = 0; i < (UINT32)existingEntries.size(); i++)
  241. {
  242. if(existingEntries[i])
  243. continue;
  244. toDelete.push_back(currentDir->mChildren[i]);
  245. }
  246. for(auto& child : toDelete)
  247. {
  248. if(child->type == LibraryEntryType::Directory)
  249. deleteDirectoryInternal(static_pointer_cast<DirectoryEntry>(child));
  250. else if(child->type == LibraryEntryType::File)
  251. deleteResourceInternal(static_pointer_cast<FileEntry>(child));
  252. }
  253. toDelete.clear();
  254. }
  255. for(auto& child : currentDir->mChildren)
  256. {
  257. if(child->type == LibraryEntryType::Directory)
  258. todo.push(static_cast<DirectoryEntry*>(child.get()));
  259. }
  260. }
  261. }
  262. }
  263. return resourcesToImport;
  264. }
  265. USPtr<ProjectLibrary::FileEntry> ProjectLibrary::addResourceInternal(DirectoryEntry* parent, const Path& filePath,
  266. const SPtr<ImportOptions>& importOptions, bool forceReimport, bool synchronous)
  267. {
  268. USPtr<FileEntry> newResource = bs_ushared_ptr_new<FileEntry>(filePath, filePath.getTail(), parent);
  269. parent->mChildren.push_back(newResource);
  270. reimportResourceInternal(newResource.get(), importOptions, forceReimport, false, synchronous);
  271. onEntryAdded(newResource->path);
  272. return newResource;
  273. }
  274. USPtr<ProjectLibrary::DirectoryEntry> ProjectLibrary::addDirectoryInternal(DirectoryEntry* parent, const Path& dirPath)
  275. {
  276. USPtr<DirectoryEntry> newEntry = bs_ushared_ptr_new<DirectoryEntry>(dirPath, dirPath.getTail(), parent);
  277. parent->mChildren.push_back(newEntry);
  278. onEntryAdded(newEntry->path);
  279. return newEntry;
  280. }
  281. void ProjectLibrary::deleteResourceInternal(USPtr<FileEntry> resource)
  282. {
  283. if(resource->meta != nullptr)
  284. {
  285. auto& resourceMetas = resource->meta->getResourceMetaData();
  286. for(auto& entry : resourceMetas)
  287. {
  288. const UUID& uuid = entry->getUUID();
  289. Path path;
  290. if (mResourceManifest->uuidToFilePath(uuid, path))
  291. {
  292. if (FileSystem::isFile(path))
  293. FileSystem::remove(path);
  294. mResourceManifest->unregisterResource(uuid);
  295. }
  296. mUUIDToPath.erase(uuid);
  297. }
  298. }
  299. Path metaPath = getMetaPath(resource->path);
  300. if (FileSystem::isFile(metaPath))
  301. FileSystem::remove(metaPath);
  302. DirectoryEntry* parent = resource->parent;
  303. auto findIter = std::find_if(parent->mChildren.begin(), parent->mChildren.end(),
  304. [&] (const USPtr<LibraryEntry>& entry) { return entry == resource; });
  305. parent->mChildren.erase(findIter);
  306. Path originalPath = resource->path;
  307. onEntryRemoved(originalPath);
  308. const auto iterQueuedImport = mQueuedImports.find(resource.get());
  309. if(iterQueuedImport != mQueuedImports.end())
  310. iterQueuedImport->second->canceled = true;
  311. removeDependencies(resource.get());
  312. *resource = FileEntry();
  313. reimportDependants(originalPath);
  314. }
  315. void ProjectLibrary::deleteDirectoryInternal(USPtr<DirectoryEntry> directory)
  316. {
  317. if(directory == mRootEntry)
  318. mRootEntry = nullptr;
  319. Vector<USPtr<LibraryEntry>> childrenToDestroy = directory->mChildren;
  320. for(auto& child : childrenToDestroy)
  321. {
  322. if(child->type == LibraryEntryType::Directory)
  323. deleteDirectoryInternal(static_pointer_cast<DirectoryEntry>(child));
  324. else
  325. deleteResourceInternal(static_pointer_cast<FileEntry>(child));
  326. }
  327. DirectoryEntry* parent = directory->parent;
  328. if(parent != nullptr)
  329. {
  330. auto findIter = std::find_if(parent->mChildren.begin(), parent->mChildren.end(),
  331. [&] (const USPtr<LibraryEntry>& entry) { return entry == directory; });
  332. parent->mChildren.erase(findIter);
  333. }
  334. onEntryRemoved(directory->path);
  335. *directory = DirectoryEntry();
  336. }
  337. bool ProjectLibrary::reimportResourceInternal(FileEntry* fileEntry, const SPtr<ImportOptions>& importOptions,
  338. bool forceReimport, bool pruneResourceMetas, bool synchronous)
  339. {
  340. Path metaPath = fileEntry->path;
  341. metaPath.setFilename(metaPath.getFilename() + ".meta");
  342. // If the file doesn't have meta-data, attempt to read it from a meta-file, if one exists. This can only happen
  343. // if library data is obsolete (e.g. when adding files from another copy of the project)
  344. if(fileEntry->meta == nullptr)
  345. {
  346. if(FileSystem::isFile(metaPath))
  347. {
  348. FileDecoder fs(metaPath);
  349. SPtr<IReflectable> loadedMeta = fs.decode();
  350. if(loadedMeta != nullptr && loadedMeta->isDerivedFrom(ProjectFileMeta::getRTTIStatic()))
  351. {
  352. const SPtr<ProjectFileMeta>& fileMeta = std::static_pointer_cast<ProjectFileMeta>(loadedMeta);
  353. fileEntry->meta = fileMeta;
  354. auto& resourceMetas = fileEntry->meta->getResourceMetaData();
  355. if (!resourceMetas.empty())
  356. {
  357. mUUIDToPath[resourceMetas[0]->getUUID()] = fileEntry->path;
  358. for (UINT32 i = 1; i < (UINT32)resourceMetas.size(); i++)
  359. {
  360. const SPtr<ProjectResourceMeta>& entry = resourceMetas[i];
  361. mUUIDToPath[entry->getUUID()] = fileEntry->path + entry->getUniqueName();
  362. }
  363. }
  364. }
  365. }
  366. }
  367. if (!isUpToDate(fileEntry) || forceReimport)
  368. {
  369. // Note: If resource is native we just copy it to the internal folder. We could avoid the copy and
  370. // load the resource directly from the Resources folder but that requires complicating library code.
  371. const bool isNativeResource = isNative(fileEntry->path);
  372. SPtr<ImportOptions> curImportOptions = nullptr;
  373. if (importOptions == nullptr && !isNativeResource)
  374. {
  375. if (fileEntry->meta != nullptr)
  376. curImportOptions = fileEntry->meta->getImportOptions();
  377. else
  378. curImportOptions = Importer::instance().createImportOptions(fileEntry->path);
  379. }
  380. else
  381. curImportOptions = importOptions;
  382. SPtr<QueuedImport> queuedImport = bs_shared_ptr_new<QueuedImport>();
  383. queuedImport->filePath = fileEntry->path;
  384. queuedImport->importOptions = curImportOptions;
  385. queuedImport->pruneMetas = pruneResourceMetas;
  386. queuedImport->native = isNativeResource;
  387. queuedImport->timestamp = std::time(nullptr);
  388. // If import is already queued for this file make the tasks dependant so they don't execute at the same time,
  389. // and so they execute in the proper order
  390. SPtr<Task> dependency;
  391. const auto iterFind = mQueuedImports.find(fileEntry);
  392. if (iterFind != mQueuedImports.end())
  393. {
  394. dependency = iterFind->second->importTask;
  395. // Need this reference just so the dependency is kept alive, otherwise it goes out of scope when we
  396. // remove or overwrite it from mQueuedImports map
  397. queuedImport->dependsOn = iterFind->second;
  398. // Note: We should cancel the task here so it doesn't run unnecessarily. But if the task is already
  399. // running it shouldn't be canceled as dependencies still need to wait on it (since cancelling a
  400. // running task doesn't actually stop it). Yet there is currently no good wait to check if task
  401. // is currently running.
  402. // Dependency being imported async but we want the current resource right away. Wait until dependency is
  403. // done otherwise when dependency finishes it will overwrite whatever we write now.
  404. if(synchronous && dependency)
  405. {
  406. if (finishQueuedImport(fileEntry, *iterFind->second, true))
  407. mQueuedImports.erase(iterFind);
  408. }
  409. }
  410. // Needs to be pass a weak pointer to worker methods since internally it holds a reference to the task itself,
  411. // and we can't have the task closure holding a reference back, otherwise it leaks
  412. std::weak_ptr<QueuedImport> queuedImportWeak = queuedImport;
  413. if(!isNativeResource)
  414. {
  415. // Find UUIDs for any existing sub-resources
  416. if (fileEntry->meta != nullptr)
  417. {
  418. const Vector<SPtr<ProjectResourceMeta>>& resourceMetas = fileEntry->meta->getAllResourceMetaData();
  419. for (auto& entry : resourceMetas)
  420. queuedImport->resources.emplace_back(entry->getUniqueName(), nullptr, entry->getUUID());
  421. }
  422. // Perform import, register the resources and their UUID in the QueuedImport structure and save the
  423. // resource on disk
  424. const auto importAsync = [queuedImportWeak, &projectFolder = mProjectFolder, &mutex = mQueuedImportMutex]()
  425. {
  426. SPtr<QueuedImport> queuedImport = queuedImportWeak.lock();
  427. Vector<SubResourceRaw> importedResources = gImporter()._importAll(queuedImport->filePath,
  428. queuedImport->importOptions);
  429. if (!importedResources.empty())
  430. {
  431. Path outputPath = projectFolder;
  432. outputPath.append(INTERNAL_TEMP_DIR);
  433. if (!FileSystem::isDirectory(outputPath))
  434. FileSystem::createDir(outputPath);
  435. for (auto& entry : importedResources)
  436. {
  437. String subresourceName = entry.name;
  438. Path::stripInvalid(subresourceName);
  439. UUID uuid;
  440. {
  441. // Any access to queuedImport->resources must be locked
  442. Lock lock(mutex);
  443. auto iterFind = std::find_if(queuedImport->resources.begin(), queuedImport->resources.end(),
  444. [&subresourceName](const QueuedImportResource& importResource)
  445. {
  446. return importResource.name == subresourceName;
  447. });
  448. if (iterFind != queuedImport->resources.end())
  449. iterFind->resource = entry.value;
  450. else
  451. {
  452. queuedImport->resources.push_back(QueuedImportResource(entry.name, entry.value,
  453. UUID::EMPTY));
  454. iterFind = queuedImport->resources.end() - 1;
  455. }
  456. if (iterFind->uuid.empty())
  457. iterFind->uuid = UUIDGenerator::generateRandom();
  458. uuid = iterFind->uuid;
  459. }
  460. const String uuidStr = uuid.toString();
  461. outputPath.setFilename(uuidStr + ".asset");
  462. gResources()._save(entry.value, outputPath, true);
  463. }
  464. }
  465. };
  466. if(!synchronous)
  467. {
  468. queuedImport->importTask = Task::create("ProjectLibraryImport", importAsync, TaskPriority::Normal,
  469. dependency);
  470. }
  471. else
  472. importAsync();
  473. }
  474. else
  475. {
  476. // If meta exists make sure it is registered in the manifest before load, otherwise it will get assigned a new UUID.
  477. // This can happen if library isn't properly saved before exiting the application.
  478. if (fileEntry->meta != nullptr)
  479. {
  480. auto& resourceMetas = fileEntry->meta->getResourceMetaData();
  481. mResourceManifest->registerResource(resourceMetas[0]->getUUID(), fileEntry->path);
  482. }
  483. const auto importAsync = [queuedImportWeak, &projectFolder = mProjectFolder, &mutex = mQueuedImportMutex]()
  484. {
  485. // Don't load dependencies because we don't need them, but also because they might not be in the
  486. // manifest which would screw up their UUIDs.
  487. SPtr<QueuedImport> queuedImport = queuedImportWeak.lock();
  488. HResource resource = gResources().load(queuedImport->filePath, ResourceLoadFlag::KeepSourceData);
  489. if (resource)
  490. {
  491. Path outputPath = projectFolder;
  492. outputPath.append(INTERNAL_TEMP_DIR);
  493. if (!FileSystem::isDirectory(outputPath))
  494. FileSystem::createDir(outputPath);
  495. {
  496. // Any access to queuedImport->resources must be locked
  497. Lock lock(mutex);
  498. queuedImport->resources.push_back(QueuedImportResource("primary", resource));
  499. }
  500. const String uuidStr = resource.getUUID().toString();
  501. outputPath.setFilename(uuidStr + ".asset");
  502. gResources()._save(resource.getInternalPtr(), outputPath, true);
  503. }
  504. };
  505. if(!synchronous)
  506. {
  507. queuedImport->importTask = Task::create("ProjectLibraryImport", importAsync, TaskPriority::Normal,
  508. dependency);
  509. }
  510. else
  511. importAsync();
  512. }
  513. if(!synchronous)
  514. {
  515. TaskScheduler::instance().addTask(queuedImport->importTask);
  516. mQueuedImports[fileEntry] = queuedImport;
  517. }
  518. if(synchronous)
  519. finishQueuedImport(fileEntry, *queuedImport, true);
  520. return true;
  521. }
  522. return false;
  523. }
  524. bool ProjectLibrary::finishQueuedImport(FileEntry* fileEntry, const QueuedImport& import, bool wait)
  525. {
  526. if (import.importTask != nullptr && !import.importTask->isComplete())
  527. {
  528. if (wait)
  529. import.importTask->wait();
  530. else
  531. return false;
  532. }
  533. // We wait on canceled task to finish and then just discard the results because any dependant tasks need to be
  534. // aware this tasks exists, so we can't just remove it straight away.
  535. if (import.canceled)
  536. return true;
  537. Path metaPath = fileEntry->path;
  538. metaPath.setFilename(metaPath.getFilename() + ".meta");
  539. SPtr<SerializedObject> orgMetaData;
  540. Vector<SPtr<ProjectResourceMeta>> existingMetas;
  541. if (fileEntry->meta == nullptr) // Build a brand new meta-file
  542. fileEntry->meta = ProjectFileMeta::create(import.importOptions);
  543. else // Existing meta-file, which needs to be updated
  544. {
  545. orgMetaData = SerializedObject::create(*fileEntry->meta);
  546. // Remove existing dependencies (they will be re-added later)
  547. removeDependencies(fileEntry);
  548. existingMetas = fileEntry->meta->getAllResourceMetaData();
  549. fileEntry->meta->clearResourceMetaData();
  550. fileEntry->meta->mImportOptions = import.importOptions;
  551. }
  552. fileEntry->lastUpdateTime = import.timestamp;
  553. Path internalResourcesPath = mProjectFolder;
  554. internalResourcesPath.append(INTERNAL_RESOURCES_DIR);
  555. if (!FileSystem::isDirectory(internalResourcesPath))
  556. FileSystem::createDir(internalResourcesPath);
  557. Path tempResourcesPath = mProjectFolder;
  558. tempResourcesPath.append(INTERNAL_TEMP_DIR);
  559. // See which sub-resource metas need to be updated, removed or added based on the new resource set
  560. bool isFirst = true;
  561. for (const auto& entry : import.resources)
  562. {
  563. // Entries with no resources are sub-resources that used to exist in this file, but haven't been imported
  564. // this time
  565. if (!entry.resource)
  566. continue;
  567. // Copy the resource file from the temporary directory
  568. const String uuidStr = entry.uuid.toString();
  569. tempResourcesPath.setFilename(uuidStr + ".asset");
  570. internalResourcesPath.setFilename(uuidStr + ".asset");
  571. FileSystem::move(tempResourcesPath, internalResourcesPath);
  572. String name = entry.name;
  573. Path::stripInvalid(name);
  574. const ProjectResourceIcons icons = generatePreviewIcons(*entry.resource);
  575. bool foundMeta = false;
  576. for (auto iterMeta = existingMetas.begin(); iterMeta != existingMetas.end();)
  577. {
  578. const SPtr<ProjectResourceMeta>& metaEntry = *iterMeta;
  579. if (name == metaEntry->getUniqueName())
  580. {
  581. if(!foundMeta)
  582. {
  583. // Make sure the UUID we used for saving the resource matches the current one (should always
  584. // be true unless the meta-data somehow changes while the async import is happening)
  585. assert(entry.uuid == metaEntry->getUUID());
  586. HResource importedResource = gResources()._getResourceHandle(metaEntry->getUUID());
  587. gResources().update(importedResource, entry.resource);
  588. metaEntry->setPreviewIcons(icons);
  589. fileEntry->meta->add(metaEntry);
  590. }
  591. foundMeta = true;
  592. iterMeta = existingMetas.erase(iterMeta);
  593. }
  594. else
  595. ++iterMeta;
  596. }
  597. if (!foundMeta)
  598. {
  599. HResource importedResource;
  600. // Native resources are always expected to have a handle since Resources::load was called during
  601. // the 'import' step
  602. if (import.native)
  603. importedResource = entry.handle;
  604. else
  605. importedResource = gResources()._createResourceHandle(entry.resource, entry.uuid);
  606. SPtr<ResourceMetaData> subMeta = entry.resource->getMetaData();
  607. const UINT32 typeId = entry.resource->getTypeId();
  608. const UUID& UUID = importedResource.getUUID();
  609. SPtr<ProjectResourceMeta> resMeta = ProjectResourceMeta::create(name, UUID, typeId,
  610. icons, subMeta);
  611. fileEntry->meta->add(resMeta);
  612. }
  613. // Update UUID to path mapping
  614. if (isFirst)
  615. mUUIDToPath[entry.uuid] = fileEntry->path;
  616. else
  617. mUUIDToPath[entry.uuid] = fileEntry->path + name;
  618. isFirst = false;
  619. // Register path in manifest
  620. mResourceManifest->registerResource(entry.uuid, internalResourcesPath);
  621. }
  622. // Keep resource metas that we are not currently using, in case they get restored so their references
  623. // don't get broken
  624. if (!import.pruneMetas)
  625. {
  626. for (auto& metaEntry : existingMetas)
  627. fileEntry->meta->addInactive(metaEntry);
  628. }
  629. // Note: Ideally we replace this with a specialized BinaryCompare method
  630. bool metaModified = true;
  631. if(orgMetaData != nullptr)
  632. {
  633. SPtr<SerializedObject> newMetaData = SerializedObject::create(*fileEntry->meta);
  634. BinaryDiff diffHandler;
  635. SPtr<SerializedObject> diff = diffHandler.generateDiff(orgMetaData, newMetaData);
  636. metaModified = diff != nullptr;
  637. }
  638. if(metaModified)
  639. {
  640. // Save the meta file
  641. FileEncoder fs(metaPath);
  642. fs.encode(fileEntry->meta.get());
  643. }
  644. // Register any dependencies this resource depends on
  645. addDependencies(fileEntry);
  646. // Notify the outside world import is doen
  647. onEntryImported(fileEntry->path);
  648. // Queue any resources dependant on this one for import
  649. reimportDependants(fileEntry->path);
  650. return true;
  651. }
  652. void ProjectLibrary::_finishQueuedImports(bool wait)
  653. {
  654. for(auto iter = mQueuedImports.begin(); iter != mQueuedImports.end();)
  655. {
  656. if(finishQueuedImport(iter->first, *iter->second, wait))
  657. iter = mQueuedImports.erase(iter);
  658. else
  659. ++iter;
  660. }
  661. }
  662. bool ProjectLibrary::isUpToDate(FileEntry* resource) const
  663. {
  664. SPtr<QueuedImport> queuedImport;
  665. if(resource->meta == nullptr)
  666. {
  667. // Allow no meta if import in progress
  668. const auto iterFind = mQueuedImports.find(resource);
  669. if(iterFind == mQueuedImports.end())
  670. return false;
  671. queuedImport = iterFind->second;
  672. }
  673. else
  674. {
  675. auto& resourceMetas = resource->meta->getResourceMetaData();
  676. for (auto& resMeta : resourceMetas)
  677. {
  678. Path internalPath;
  679. if (!mResourceManifest->uuidToFilePath(resMeta->getUUID(), internalPath))
  680. return false;
  681. if (!FileSystem::isFile(internalPath))
  682. return false;
  683. }
  684. }
  685. // Note: We're keeping separate update times for queued imports. This allows the import to be cancelled (either by
  686. // user or by app crashing), without updating the actual update time. This way the systems knows to try to reimport
  687. // the resource on the next check. At the same time we don't want our checkForModifications function to keep
  688. // trying to reimport a resource if it's already been queued for import.
  689. const std::time_t lastUpdateTime = queuedImport ? queuedImport->timestamp : resource->lastUpdateTime;
  690. const std::time_t lastModifiedTime = FileSystem::getLastModifiedTime(resource->path);
  691. return lastModifiedTime <= lastUpdateTime;
  692. }
  693. Vector<USPtr<ProjectLibrary::LibraryEntry>> ProjectLibrary::search(const String& pattern)
  694. {
  695. return search(pattern, {});
  696. }
  697. Vector<USPtr<ProjectLibrary::LibraryEntry>> ProjectLibrary::search(const String& pattern, const Vector<UINT32>& typeIds)
  698. {
  699. Vector<USPtr<LibraryEntry>> foundEntries;
  700. std::regex escape("[.^$|()\\[\\]{}*+?\\\\]");
  701. String replace("\\\\&");
  702. String escapedPattern = std::regex_replace(pattern, escape, replace, std::regex_constants::match_default | std::regex_constants::format_sed);
  703. // For some reason MSVC stdlib implementation requires a different pattern than stdlib one
  704. #if BS_PLATFORM == BS_PLATFORM_WIN32
  705. std::regex wildcard("\\\\\\*");
  706. #else
  707. std::regex wildcard("\\\\\\\\\\*");
  708. #endif
  709. String wildcardReplace(".*");
  710. String searchPattern = std::regex_replace(escapedPattern, wildcard, ".*");
  711. std::regex searchRegex(searchPattern, std::regex_constants::ECMAScript | std::regex_constants::icase);
  712. Stack<DirectoryEntry*> todo;
  713. todo.push(mRootEntry.get());
  714. while (!todo.empty())
  715. {
  716. DirectoryEntry* dirEntry = todo.top();
  717. todo.pop();
  718. for (auto& child : dirEntry->mChildren)
  719. {
  720. if (std::regex_match(child->elementName, searchRegex))
  721. {
  722. if (typeIds.empty())
  723. foundEntries.push_back(child);
  724. else
  725. {
  726. if (child->type == LibraryEntryType::File)
  727. {
  728. FileEntry* childFileEntry = static_cast<FileEntry*>(child.get());
  729. if (childFileEntry->meta != nullptr)
  730. {
  731. auto& resourceMetas = childFileEntry->meta->getResourceMetaData();
  732. for (auto& typeId : typeIds)
  733. {
  734. bool found = false;
  735. for (auto& resMeta : resourceMetas)
  736. {
  737. if (resMeta->getTypeID() == typeId)
  738. {
  739. foundEntries.push_back(child);
  740. found = true;
  741. break;
  742. }
  743. }
  744. if (found)
  745. break;
  746. }
  747. }
  748. }
  749. }
  750. }
  751. if (child->type == LibraryEntryType::Directory)
  752. {
  753. DirectoryEntry* childDirEntry = static_cast<DirectoryEntry*>(child.get());
  754. todo.push(childDirEntry);
  755. }
  756. }
  757. }
  758. std::sort(foundEntries.begin(), foundEntries.end(),
  759. [&](const USPtr<LibraryEntry>& a, const USPtr<LibraryEntry>& b)
  760. {
  761. return a->elementName.compare(b->elementName) < 0;
  762. });
  763. return foundEntries;
  764. }
  765. USPtr<ProjectLibrary::LibraryEntry> ProjectLibrary::findEntry(const Path& path) const
  766. {
  767. Path relPath;
  768. const Path* searchPath;
  769. if (path.isAbsolute())
  770. {
  771. if (!mResourcesFolder.includes(path))
  772. return nullptr;
  773. relPath = path.getRelative(mRootEntry->path);
  774. searchPath = &relPath;
  775. }
  776. else
  777. searchPath = &path;
  778. BS_ASSERT(mRootEntry->path == mResourcesFolder);
  779. UINT32 numElems = searchPath->getNumDirectories() + (searchPath->isFile() ? 1 : 0);
  780. UINT32 idx = 0;
  781. USPtr<LibraryEntry> rootLibEntry = mRootEntry;
  782. USPtr<LibraryEntry>* current = &rootLibEntry;
  783. while (current != nullptr)
  784. {
  785. if (idx == numElems)
  786. return *current;
  787. const String& curElem =
  788. (searchPath->isFile() && idx == (numElems - 1)) ? searchPath->getFilename() : (*searchPath)[idx];
  789. if ((*current)->type == LibraryEntryType::Directory)
  790. {
  791. DirectoryEntry* dirEntry = static_cast<DirectoryEntry*>(current->get());
  792. size_t curElemHash = bs_hash(UTF8::toLower(curElem));
  793. current = nullptr;
  794. for (auto& child : dirEntry->mChildren)
  795. {
  796. if(curElemHash != child->elementNameHash)
  797. continue;
  798. if (Path::comparePathElem(curElem, child->elementName))
  799. {
  800. idx++;
  801. current = &child;
  802. break;
  803. }
  804. }
  805. }
  806. else // Found file
  807. {
  808. // If this is next to last element, next entry is assumed to be a sub-resource name, which we ignore
  809. if (idx == (numElems - 1))
  810. return *current;
  811. else
  812. break; // Not a valid path
  813. }
  814. }
  815. return nullptr;
  816. }
  817. bool ProjectLibrary::isSubresource(const Path& path) const
  818. {
  819. UINT32 numElems = path.getNumDirectories() + (path.isFile() ? 1 : 0);
  820. if (numElems <= 1)
  821. return false;
  822. Path filePath = path;
  823. filePath.makeParent();
  824. LibraryEntry* entry = findEntry(filePath).get();
  825. return entry != nullptr && entry->type == LibraryEntryType::File;
  826. }
  827. SPtr<ProjectResourceMeta> ProjectLibrary::findResourceMeta(const Path& path) const
  828. {
  829. UINT32 numElems = path.getNumDirectories() + (path.isFile() ? 1 : 0);
  830. // Check if it is a subresource path
  831. if(numElems > 1)
  832. {
  833. Path filePath = path;
  834. filePath.makeParent();
  835. LibraryEntry* entry = findEntry(filePath).get();
  836. if (entry == nullptr)
  837. return nullptr;
  838. // Entry is a subresource
  839. if (entry->type == LibraryEntryType::File)
  840. {
  841. FileEntry* fileEntry = static_cast<FileEntry*>(entry);
  842. if (fileEntry->meta == nullptr)
  843. return nullptr;
  844. auto& resourceMetas = fileEntry->meta->getResourceMetaData();
  845. for(auto& resMeta : resourceMetas)
  846. {
  847. if (resMeta->getUniqueName() == path.getTail())
  848. return resMeta;
  849. }
  850. // Found the file but no subresource or meta information
  851. return nullptr;
  852. }
  853. else // Entry not a subresource
  854. {
  855. DirectoryEntry* dirEntry = static_cast<DirectoryEntry*>(entry);
  856. for (auto& child : dirEntry->mChildren)
  857. {
  858. if (Path::comparePathElem(path.getTail(), child->elementName))
  859. {
  860. if (child->type == LibraryEntryType::File)
  861. {
  862. FileEntry* fileEntry = static_cast<FileEntry*>(child.get());
  863. if (fileEntry->meta == nullptr)
  864. return nullptr;
  865. return fileEntry->meta->getResourceMetaData()[0];
  866. }
  867. }
  868. }
  869. return nullptr;
  870. }
  871. }
  872. // Not a subresource path, load directly
  873. {
  874. LibraryEntry* entry = findEntry(path).get();
  875. if (entry == nullptr || entry->type == LibraryEntryType::Directory)
  876. return nullptr;
  877. FileEntry* fileEntry = static_cast<FileEntry*>(entry);
  878. if (fileEntry->meta == nullptr)
  879. return nullptr;
  880. return fileEntry->meta->getResourceMetaData()[0];
  881. }
  882. }
  883. Path ProjectLibrary::uuidToPath(const UUID& uuid) const
  884. {
  885. auto iterFind = mUUIDToPath.find(uuid);
  886. if (iterFind != mUUIDToPath.end())
  887. return iterFind->second;
  888. return Path::BLANK;
  889. }
  890. void ProjectLibrary::createEntry(const HResource& resource, const Path& path)
  891. {
  892. if (resource == nullptr)
  893. return;
  894. Path assetPath = path;
  895. if (path.isAbsolute())
  896. {
  897. if (!getResourcesFolder().includes(path))
  898. return;
  899. assetPath = path.getRelative(getResourcesFolder());
  900. }
  901. deleteEntry(assetPath);
  902. resource->setName(path.getFilename(false));
  903. Path absPath = assetPath.getAbsolute(getResourcesFolder());
  904. Resources::instance().save(resource, absPath, false);
  905. Path parentDirPath = absPath.getParent();
  906. USPtr<LibraryEntry> parentEntry = findEntry(parentDirPath);
  907. // Register parent hierarchy if not found
  908. DirectoryEntry* entryParent = nullptr;
  909. if (parentEntry == nullptr)
  910. createInternalParentHierarchy(absPath, nullptr, &entryParent);
  911. else
  912. entryParent = static_cast<DirectoryEntry*>(parentEntry.get());
  913. addResourceInternal(entryParent, absPath, nullptr, true, true);
  914. }
  915. void ProjectLibrary::saveEntry(const HResource& resource)
  916. {
  917. if (resource == nullptr)
  918. return;
  919. Path filePath = uuidToPath(resource.getUUID());
  920. if(filePath.isEmpty())
  921. {
  922. LOGWRN("Trying to save a resource that hasn't been registered with the project library. Call ProjectLibrary::create first.");
  923. return;
  924. }
  925. filePath.makeAbsolute(getResourcesFolder());
  926. Resources::instance().save(resource, filePath, true);
  927. LibraryEntry* fileEntry = findEntry(filePath).get();
  928. if(fileEntry)
  929. reimportResourceInternal(static_cast<FileEntry*>(fileEntry), nullptr, true, false, true);
  930. }
  931. void ProjectLibrary::createFolderEntry(const Path& path)
  932. {
  933. Path fullPath = path;
  934. if (fullPath.isAbsolute())
  935. {
  936. if (!mResourcesFolder.includes(fullPath))
  937. return;
  938. }
  939. else
  940. fullPath.makeAbsolute(mResourcesFolder);
  941. if (FileSystem::isDirectory(fullPath))
  942. return; // Already exists
  943. FileSystem::createDir(fullPath);
  944. Path parentPath = fullPath.getParent();
  945. DirectoryEntry* newEntryParent = nullptr;
  946. USPtr<LibraryEntry> newEntryParentLib = findEntry(parentPath);
  947. if (newEntryParentLib != nullptr)
  948. {
  949. assert(newEntryParentLib->type == LibraryEntryType::Directory);
  950. newEntryParent = static_cast<DirectoryEntry*>(newEntryParentLib.get());
  951. }
  952. DirectoryEntry* newHierarchyParent = nullptr;
  953. if (newEntryParent == nullptr) // New path parent doesn't exist, so we need to create the hierarchy
  954. createInternalParentHierarchy(fullPath, &newHierarchyParent, &newEntryParent);
  955. addDirectoryInternal(newEntryParent, fullPath);
  956. }
  957. void ProjectLibrary::moveEntry(const Path& oldPath, const Path& newPath, bool overwrite)
  958. {
  959. Path oldFullPath = oldPath;
  960. if (!oldFullPath.isAbsolute())
  961. oldFullPath.makeAbsolute(mResourcesFolder);
  962. Path newFullPath = newPath;
  963. if (!newFullPath.isAbsolute())
  964. newFullPath.makeAbsolute(mResourcesFolder);
  965. Path parentPath = newFullPath.getParent();
  966. if (!FileSystem::isDirectory(parentPath))
  967. {
  968. LOGWRN("Move operation failed. Destination directory \"" + parentPath.toString() + "\" doesn't exist.");
  969. return;
  970. }
  971. if(FileSystem::isFile(oldFullPath) || FileSystem::isDirectory(oldFullPath))
  972. FileSystem::move(oldFullPath, newFullPath, overwrite);
  973. Path oldMetaPath = getMetaPath(oldFullPath);
  974. Path newMetaPath = getMetaPath(newFullPath);
  975. USPtr<LibraryEntry> oldEntry = findEntry(oldFullPath);
  976. if(oldEntry != nullptr) // Moving from the Resources folder
  977. {
  978. // Moved outside of Resources, delete entry & meta file
  979. if (!mResourcesFolder.includes(newFullPath))
  980. {
  981. if(oldEntry->type == LibraryEntryType::File)
  982. deleteResourceInternal(static_pointer_cast<FileEntry>(oldEntry));
  983. else if(oldEntry->type == LibraryEntryType::Directory)
  984. deleteDirectoryInternal(static_pointer_cast<DirectoryEntry>(oldEntry));
  985. }
  986. else // Just moving internally
  987. {
  988. onEntryRemoved(oldEntry->path);
  989. USPtr<FileEntry> fileEntry = nullptr;
  990. if (oldEntry->type == LibraryEntryType::File)
  991. {
  992. fileEntry = static_pointer_cast<FileEntry>(oldEntry);
  993. removeDependencies(fileEntry.get());
  994. // Update uuid <-> path mapping
  995. if(fileEntry->meta != nullptr)
  996. {
  997. auto& resourceMetas = fileEntry->meta->getResourceMetaData();
  998. if (!resourceMetas.empty())
  999. {
  1000. mUUIDToPath[resourceMetas[0]->getUUID()] = newFullPath;
  1001. for (UINT32 i = 1; i < (UINT32)resourceMetas.size(); i++)
  1002. {
  1003. SPtr<ProjectResourceMeta> resMeta = resourceMetas[i];
  1004. const UUID& UUID = resMeta->getUUID();
  1005. mUUIDToPath[UUID] = newFullPath + resMeta->getUniqueName();
  1006. }
  1007. }
  1008. }
  1009. }
  1010. if(FileSystem::isFile(oldMetaPath))
  1011. FileSystem::move(oldMetaPath, newMetaPath);
  1012. DirectoryEntry* parent = oldEntry->parent;
  1013. auto findIter = std::find(parent->mChildren.begin(), parent->mChildren.end(), oldEntry);
  1014. if(findIter != parent->mChildren.end())
  1015. parent->mChildren.erase(findIter);
  1016. Path parentPath = newFullPath.getParent();
  1017. DirectoryEntry* newEntryParent = nullptr;
  1018. USPtr<LibraryEntry> newEntryParentLib = findEntry(parentPath);
  1019. if(newEntryParentLib != nullptr)
  1020. {
  1021. assert(newEntryParentLib->type == LibraryEntryType::Directory);
  1022. newEntryParent = static_cast<DirectoryEntry*>(newEntryParentLib.get());
  1023. }
  1024. DirectoryEntry* newHierarchyParent = nullptr;
  1025. if(newEntryParent == nullptr) // New path parent doesn't exist, so we need to create the hierarchy
  1026. createInternalParentHierarchy(newFullPath, &newHierarchyParent, &newEntryParent);
  1027. newEntryParent->mChildren.push_back(oldEntry);
  1028. oldEntry->parent = newEntryParent;
  1029. oldEntry->path = newFullPath;
  1030. oldEntry->elementName = newFullPath.getTail();
  1031. oldEntry->elementNameHash = bs_hash(UTF8::toLower(oldEntry->elementName));
  1032. if(oldEntry->type == LibraryEntryType::Directory) // Update child paths
  1033. {
  1034. Stack<LibraryEntry*> todo;
  1035. todo.push(oldEntry.get());
  1036. while(!todo.empty())
  1037. {
  1038. LibraryEntry* curEntry = todo.top();
  1039. todo.pop();
  1040. DirectoryEntry* curDirEntry = static_cast<DirectoryEntry*>(curEntry);
  1041. for(auto& child : curDirEntry->mChildren)
  1042. {
  1043. child->path = child->parent->path;
  1044. child->path.append(child->elementName);
  1045. if(child->type == LibraryEntryType::Directory)
  1046. todo.push(child.get());
  1047. }
  1048. }
  1049. }
  1050. onEntryAdded(oldEntry->path);
  1051. if (fileEntry != nullptr)
  1052. {
  1053. reimportDependants(oldFullPath);
  1054. reimportDependants(newFullPath);
  1055. }
  1056. }
  1057. }
  1058. else // Moving from outside of the Resources folder (likely adding a new resource)
  1059. {
  1060. checkForModifications(newFullPath);
  1061. }
  1062. }
  1063. void ProjectLibrary::copyEntry(const Path& oldPath, const Path& newPath, bool overwrite)
  1064. {
  1065. Path oldFullPath = oldPath;
  1066. if (!oldFullPath.isAbsolute())
  1067. oldFullPath.makeAbsolute(mResourcesFolder);
  1068. Path newFullPath = newPath;
  1069. if (!newFullPath.isAbsolute())
  1070. newFullPath.makeAbsolute(mResourcesFolder);
  1071. if (!FileSystem::exists(oldFullPath))
  1072. return;
  1073. FileSystem::copy(oldFullPath, newFullPath, overwrite);
  1074. // Copying a file/folder outside of the Resources folder, no special handling needed
  1075. if (!mResourcesFolder.includes(newFullPath))
  1076. return;
  1077. Path parentPath = newFullPath.getParent();
  1078. DirectoryEntry* newEntryParent = nullptr;
  1079. LibraryEntry* newEntryParentLib = findEntry(parentPath).get();
  1080. if (newEntryParentLib != nullptr)
  1081. {
  1082. assert(newEntryParentLib->type == LibraryEntryType::Directory);
  1083. newEntryParent = static_cast<DirectoryEntry*>(newEntryParentLib);
  1084. }
  1085. // If the source is outside of Resources folder, just plain import the copy
  1086. LibraryEntry* oldEntry = findEntry(oldFullPath).get();
  1087. if (oldEntry == nullptr)
  1088. {
  1089. checkForModifications(newFullPath);
  1090. return;
  1091. }
  1092. // Both source and destination are within Resources folder, need to preserve import options on the copies
  1093. if (FileSystem::isFile(newFullPath))
  1094. {
  1095. assert(oldEntry->type == LibraryEntryType::File);
  1096. FileEntry* oldResEntry = static_cast<FileEntry*>(oldEntry);
  1097. SPtr<ImportOptions> importOptions;
  1098. if (oldResEntry->meta != nullptr)
  1099. importOptions = oldResEntry->meta->getImportOptions();
  1100. addResourceInternal(newEntryParent, newFullPath, importOptions, true);
  1101. }
  1102. else
  1103. {
  1104. assert(oldEntry->type == LibraryEntryType::File);
  1105. DirectoryEntry* oldDirEntry = static_cast<DirectoryEntry*>(oldEntry);
  1106. DirectoryEntry* newDirEntry = addDirectoryInternal(newEntryParent, newFullPath).get();
  1107. Stack<std::tuple<DirectoryEntry*, DirectoryEntry*>> todo;
  1108. todo.push(std::make_tuple(oldDirEntry, newDirEntry));
  1109. while (!todo.empty())
  1110. {
  1111. auto current = todo.top();
  1112. todo.pop();
  1113. DirectoryEntry* sourceDir = std::get<0>(current);
  1114. DirectoryEntry* destDir = std::get<1>(current);
  1115. for (auto& child : sourceDir->mChildren)
  1116. {
  1117. Path childDestPath = destDir->path;
  1118. childDestPath.append(child->path.getTail());
  1119. if (child->type == LibraryEntryType::File)
  1120. {
  1121. FileEntry* childResEntry = static_cast<FileEntry*>(child.get());
  1122. SPtr<ImportOptions> importOptions;
  1123. if (childResEntry->meta != nullptr)
  1124. importOptions = childResEntry->meta->getImportOptions();
  1125. addResourceInternal(destDir, childDestPath, importOptions, true);
  1126. }
  1127. else // Directory
  1128. {
  1129. DirectoryEntry* childSourceDirEntry = static_cast<DirectoryEntry*>(child.get());
  1130. DirectoryEntry* childDestDirEntry = addDirectoryInternal(destDir, childDestPath).get();
  1131. todo.push(std::make_tuple(childSourceDirEntry, childDestDirEntry));
  1132. }
  1133. }
  1134. }
  1135. }
  1136. }
  1137. void ProjectLibrary::deleteEntry(const Path& path)
  1138. {
  1139. Path fullPath = path;
  1140. if (!fullPath.isAbsolute())
  1141. fullPath.makeAbsolute(mResourcesFolder);
  1142. if(FileSystem::exists(fullPath))
  1143. FileSystem::remove(fullPath);
  1144. USPtr<LibraryEntry> entry = findEntry(fullPath);
  1145. if(entry != nullptr)
  1146. {
  1147. if(entry->type == LibraryEntryType::File)
  1148. deleteResourceInternal(static_pointer_cast<FileEntry>(entry));
  1149. else if(entry->type == LibraryEntryType::Directory)
  1150. deleteDirectoryInternal(static_pointer_cast<DirectoryEntry>(entry));
  1151. }
  1152. }
  1153. void ProjectLibrary::reimport(const Path& path, const SPtr<ImportOptions>& importOptions, bool forceReimport,
  1154. bool synchronous)
  1155. {
  1156. LibraryEntry* entry = findEntry(path).get();
  1157. if (entry != nullptr)
  1158. {
  1159. if (entry->type == LibraryEntryType::File)
  1160. {
  1161. FileEntry* resEntry = static_cast<FileEntry*>(entry);
  1162. reimportResourceInternal(resEntry, importOptions, forceReimport, synchronous);
  1163. }
  1164. }
  1165. }
  1166. float ProjectLibrary::getImportProgress(const Path& path) const
  1167. {
  1168. LibraryEntry* entry = findEntry(path).get();
  1169. if (entry == nullptr)
  1170. return 0.0f;
  1171. if(entry->type == LibraryEntryType::Directory)
  1172. return 1.0f;
  1173. // Note: Only supporting binary progress reporting for now
  1174. const auto iterFind = mQueuedImports.find(static_cast<FileEntry*>(entry));
  1175. return iterFind != mQueuedImports.end() ? 0.0f : 1.0f;
  1176. }
  1177. void ProjectLibrary::cancelImport()
  1178. {
  1179. for(auto& entry : mQueuedImports)
  1180. entry.second->canceled = true;
  1181. }
  1182. void ProjectLibrary::waitForQueuedImport(FileEntry* fileEntry)
  1183. {
  1184. const auto iterFind = mQueuedImports.find(fileEntry);
  1185. if (iterFind != mQueuedImports.end())
  1186. {
  1187. if (finishQueuedImport(fileEntry, *iterFind->second, true))
  1188. mQueuedImports.erase(iterFind);
  1189. }
  1190. }
  1191. void ProjectLibrary::setIncludeInBuild(const Path& path, bool include)
  1192. {
  1193. LibraryEntry* entry = findEntry(path).get();
  1194. if (entry == nullptr || entry->type == LibraryEntryType::Directory)
  1195. return;
  1196. auto fileEntry = static_cast<FileEntry*>(entry);
  1197. // Any queued imports will overwrite the meta file, so make sure they finish first
  1198. waitForQueuedImport(fileEntry);
  1199. if (fileEntry->meta == nullptr)
  1200. return;
  1201. fileEntry->meta->setIncludeInBuild(include);
  1202. Path metaPath = fileEntry->path;
  1203. metaPath.setFilename(metaPath.getFilename() + ".meta");
  1204. FileEncoder fs(metaPath);
  1205. fs.encode(fileEntry->meta.get());
  1206. }
  1207. void ProjectLibrary::setUserData(const Path& path, const SPtr<IReflectable>& userData)
  1208. {
  1209. LibraryEntry* entry = findEntry(path).get();
  1210. if (entry == nullptr || entry->type == LibraryEntryType::Directory)
  1211. return;
  1212. auto fileEntry = static_cast<FileEntry*>(entry);
  1213. // Any queued imports will overwrite the meta file, so make sure they finish first
  1214. waitForQueuedImport(fileEntry);
  1215. SPtr<ProjectResourceMeta> resMeta = findResourceMeta(path);
  1216. if (resMeta == nullptr)
  1217. return;
  1218. resMeta->mUserData = userData;
  1219. Path metaPath = fileEntry->path;
  1220. metaPath.setFilename(metaPath.getFilename() + ".meta");
  1221. FileEncoder fs(metaPath);
  1222. fs.encode(fileEntry->meta.get());
  1223. }
  1224. Vector<USPtr<ProjectLibrary::FileEntry>> ProjectLibrary::getResourcesForBuild() const
  1225. {
  1226. Vector<USPtr<FileEntry>> output;
  1227. Stack<DirectoryEntry*> todo;
  1228. todo.push(mRootEntry.get());
  1229. while (!todo.empty())
  1230. {
  1231. DirectoryEntry* directory = todo.top();
  1232. todo.pop();
  1233. for (auto& child : directory->mChildren)
  1234. {
  1235. if (child->type == LibraryEntryType::File)
  1236. {
  1237. FileEntry* resEntry = static_cast<FileEntry*>(child.get());
  1238. if (resEntry->meta != nullptr && resEntry->meta->getIncludeInBuild())
  1239. output.push_back(static_pointer_cast<FileEntry>(child));
  1240. }
  1241. else if (child->type == LibraryEntryType::Directory)
  1242. {
  1243. todo.push(static_cast<DirectoryEntry*>(child.get()));
  1244. }
  1245. }
  1246. }
  1247. return output;
  1248. }
  1249. HResource ProjectLibrary::load(const Path& path)
  1250. {
  1251. SPtr<ProjectResourceMeta> meta = findResourceMeta(path);
  1252. if (meta == nullptr)
  1253. return HResource();
  1254. ResourceLoadFlags loadFlags = ResourceLoadFlag::Default | ResourceLoadFlag::KeepSourceData;
  1255. const UUID& resUUID = meta->getUUID();
  1256. return gResources().loadFromUUID(resUUID, false, loadFlags);
  1257. }
  1258. void ProjectLibrary::createInternalParentHierarchy(const Path& fullPath, DirectoryEntry** newHierarchyRoot,
  1259. DirectoryEntry** newHierarchyLeaf)
  1260. {
  1261. Path parentPath = fullPath;
  1262. DirectoryEntry* newEntryParent = nullptr;
  1263. Stack<Path> parentPaths;
  1264. do
  1265. {
  1266. Path newParentPath = parentPath.getParent();
  1267. if(newParentPath == parentPath)
  1268. break;
  1269. LibraryEntry* newEntryParentLib = findEntry(newParentPath).get();
  1270. if(newEntryParentLib != nullptr)
  1271. {
  1272. assert(newEntryParentLib->type == LibraryEntryType::Directory);
  1273. newEntryParent = static_cast<DirectoryEntry*>(newEntryParentLib);
  1274. break;
  1275. }
  1276. parentPaths.push(newParentPath);
  1277. parentPath = newParentPath;
  1278. } while (true);
  1279. assert(newEntryParent != nullptr); // Must exist
  1280. if(newHierarchyRoot != nullptr)
  1281. *newHierarchyRoot = newEntryParent;
  1282. while(!parentPaths.empty())
  1283. {
  1284. Path curPath = parentPaths.top();
  1285. parentPaths.pop();
  1286. newEntryParent = addDirectoryInternal(newEntryParent, curPath).get();
  1287. }
  1288. if(newHierarchyLeaf != nullptr)
  1289. *newHierarchyLeaf = newEntryParent;
  1290. }
  1291. Path ProjectLibrary::getMetaPath(const Path& path) const
  1292. {
  1293. Path metaPath = path;
  1294. metaPath.setFilename(metaPath.getFilename() + ".meta");
  1295. return metaPath;
  1296. }
  1297. bool ProjectLibrary::isMeta(const Path& fullPath) const
  1298. {
  1299. return fullPath.getExtension() == ".meta";
  1300. }
  1301. bool ProjectLibrary::isNative(const Path& path) const
  1302. {
  1303. String extension = path.getExtension();
  1304. return extension == ".asset" || extension == ".prefab";
  1305. }
  1306. void ProjectLibrary::unloadLibrary()
  1307. {
  1308. if (!mIsLoaded)
  1309. return;
  1310. _finishQueuedImports(true);
  1311. mProjectFolder = Path::BLANK;
  1312. mResourcesFolder = Path::BLANK;
  1313. clearEntries();
  1314. mRootEntry = bs_ushared_ptr_new<DirectoryEntry>(mResourcesFolder, mResourcesFolder.getTail(), nullptr);
  1315. mDependencies.clear();
  1316. gResources().unregisterResourceManifest(mResourceManifest);
  1317. mResourceManifest = nullptr;
  1318. mIsLoaded = false;
  1319. }
  1320. void ProjectLibrary::makeEntriesRelative()
  1321. {
  1322. // Make all paths relative before saving
  1323. std::function<void(LibraryEntry*, const Path&)> makeRelative =
  1324. [&](LibraryEntry* entry, const Path& root)
  1325. {
  1326. entry->path.makeRelative(root);
  1327. if (entry->type == LibraryEntryType::Directory)
  1328. {
  1329. DirectoryEntry* dirEntry = static_cast<DirectoryEntry*>(entry);
  1330. for (auto& child : dirEntry->mChildren)
  1331. makeRelative(child.get(), root);
  1332. }
  1333. };
  1334. Path root = getResourcesFolder();
  1335. makeRelative(mRootEntry.get(), root);
  1336. }
  1337. void ProjectLibrary::makeEntriesAbsolute()
  1338. {
  1339. std::function<void(LibraryEntry*, const Path&)> makeAbsolute =
  1340. [&](LibraryEntry* entry, const Path& root)
  1341. {
  1342. entry->path.makeAbsolute(root);
  1343. if (entry->type == LibraryEntryType::Directory)
  1344. {
  1345. DirectoryEntry* dirEntry = static_cast<DirectoryEntry*>(entry);
  1346. for (auto& child : dirEntry->mChildren)
  1347. makeAbsolute(child.get(), root);
  1348. }
  1349. };
  1350. Path root = getResourcesFolder();
  1351. makeAbsolute(mRootEntry.get(), root);
  1352. }
  1353. void ProjectLibrary::saveLibrary()
  1354. {
  1355. if (!mIsLoaded)
  1356. return;
  1357. // Make all paths relative before saving
  1358. makeEntriesRelative();
  1359. SPtr<ProjectLibraryEntries> libEntries = ProjectLibraryEntries::create(mRootEntry);
  1360. Path libraryEntriesPath = mProjectFolder;
  1361. libraryEntriesPath.append(PROJECT_INTERNAL_DIR);
  1362. libraryEntriesPath.append(LIBRARY_ENTRIES_FILENAME);
  1363. FileEncoder fs(libraryEntriesPath);
  1364. fs.encode(libEntries.get());
  1365. // Restore absolute entry paths
  1366. makeEntriesAbsolute();
  1367. Path resourceManifestPath = mProjectFolder;
  1368. resourceManifestPath.append(PROJECT_INTERNAL_DIR);
  1369. resourceManifestPath.append(RESOURCE_MANIFEST_FILENAME);
  1370. ResourceManifest::save(mResourceManifest, resourceManifestPath, mProjectFolder);
  1371. }
  1372. void ProjectLibrary::loadLibrary()
  1373. {
  1374. unloadLibrary();
  1375. mProjectFolder = gEditorApplication().getProjectPath();
  1376. mResourcesFolder = mProjectFolder;
  1377. mResourcesFolder.append(RESOURCES_DIR);
  1378. mRootEntry = bs_ushared_ptr_new<DirectoryEntry>(mResourcesFolder, mResourcesFolder.getTail(), nullptr);
  1379. Path libraryEntriesPath = mProjectFolder;
  1380. libraryEntriesPath.append(PROJECT_INTERNAL_DIR);
  1381. libraryEntriesPath.append(LIBRARY_ENTRIES_FILENAME);
  1382. if(FileSystem::exists(libraryEntriesPath))
  1383. {
  1384. FileDecoder fs(libraryEntriesPath);
  1385. SPtr<ProjectLibraryEntries> libEntries = std::static_pointer_cast<ProjectLibraryEntries>(fs.decode());
  1386. mRootEntry = libEntries->getRootEntry();
  1387. mRootEntry->parent = nullptr;
  1388. }
  1389. // Entries are stored relative to project folder, but we want their absolute paths now
  1390. makeEntriesAbsolute();
  1391. // Load resource manifest
  1392. Path resourceManifestPath = mProjectFolder;
  1393. resourceManifestPath.append(PROJECT_INTERNAL_DIR);
  1394. resourceManifestPath.append(RESOURCE_MANIFEST_FILENAME);
  1395. if (FileSystem::exists(resourceManifestPath))
  1396. mResourceManifest = ResourceManifest::load(resourceManifestPath, mProjectFolder);
  1397. else
  1398. mResourceManifest = ResourceManifest::create("ProjectLibrary");
  1399. gResources().registerResourceManifest(mResourceManifest);
  1400. // Load all meta files
  1401. Stack<DirectoryEntry*> todo;
  1402. todo.push(mRootEntry.get());
  1403. Vector<USPtr<LibraryEntry>> deletedEntries;
  1404. while(!todo.empty())
  1405. {
  1406. DirectoryEntry* curDir = todo.top();
  1407. todo.pop();
  1408. for(auto& child : curDir->mChildren)
  1409. {
  1410. if(child->type == LibraryEntryType::File)
  1411. {
  1412. USPtr<FileEntry> resEntry = static_pointer_cast<FileEntry>(child);
  1413. if (FileSystem::isFile(resEntry->path))
  1414. {
  1415. if (resEntry->meta == nullptr)
  1416. {
  1417. Path metaPath = resEntry->path;
  1418. metaPath.setFilename(metaPath.getFilename() + ".meta");
  1419. if (FileSystem::isFile(metaPath))
  1420. {
  1421. FileDecoder fs(metaPath);
  1422. SPtr<IReflectable> loadedMeta = fs.decode();
  1423. if (loadedMeta != nullptr && loadedMeta->isDerivedFrom(ProjectFileMeta::getRTTIStatic()))
  1424. {
  1425. SPtr<ProjectFileMeta> fileMeta = std::static_pointer_cast<ProjectFileMeta>(loadedMeta);
  1426. resEntry->meta = fileMeta;
  1427. }
  1428. }
  1429. }
  1430. if (resEntry->meta != nullptr)
  1431. {
  1432. auto& resourceMetas = resEntry->meta->getResourceMetaData();
  1433. if (!resourceMetas.empty())
  1434. {
  1435. mUUIDToPath[resourceMetas[0]->getUUID()] = resEntry->path;
  1436. for (UINT32 i = 1; i < (UINT32)resourceMetas.size(); i++)
  1437. {
  1438. SPtr<ProjectResourceMeta> entry = resourceMetas[i];
  1439. mUUIDToPath[entry->getUUID()] = resEntry->path + entry->getUniqueName();
  1440. }
  1441. }
  1442. }
  1443. addDependencies(resEntry.get());
  1444. }
  1445. else
  1446. deletedEntries.push_back(resEntry);
  1447. }
  1448. else if(child->type == LibraryEntryType::Directory)
  1449. {
  1450. if (FileSystem::isDirectory(child->path))
  1451. todo.push(static_cast<DirectoryEntry*>(child.get()));
  1452. else
  1453. deletedEntries.push_back(child);
  1454. }
  1455. }
  1456. }
  1457. // Remove entries that no longer have corresponding files
  1458. for (auto& deletedEntry : deletedEntries)
  1459. {
  1460. if (deletedEntry->type == LibraryEntryType::File)
  1461. deleteResourceInternal(static_pointer_cast<FileEntry>(deletedEntry));
  1462. else
  1463. deleteDirectoryInternal(static_pointer_cast<DirectoryEntry>(deletedEntry));
  1464. }
  1465. // Clean up internal library folder from obsolete files
  1466. Path internalResourcesFolder = mProjectFolder;
  1467. internalResourcesFolder.append(INTERNAL_RESOURCES_DIR);
  1468. if (FileSystem::exists(internalResourcesFolder))
  1469. {
  1470. Vector<Path> toDelete;
  1471. auto processFile = [&](const Path& file)
  1472. {
  1473. UUID uuid = UUID(file.getFilename(false));
  1474. if (mUUIDToPath.find(uuid) == mUUIDToPath.end())
  1475. {
  1476. mResourceManifest->unregisterResource(uuid);
  1477. toDelete.push_back(file);
  1478. }
  1479. return true;
  1480. };
  1481. FileSystem::iterate(internalResourcesFolder, processFile);
  1482. for (auto& entry : toDelete)
  1483. FileSystem::remove(entry);
  1484. }
  1485. mIsLoaded = true;
  1486. }
  1487. void ProjectLibrary::clearEntries()
  1488. {
  1489. if (mRootEntry == nullptr)
  1490. return;
  1491. std::function<void(LibraryEntry*)> invalidateRecursive =
  1492. [&](LibraryEntry* entry)
  1493. {
  1494. if (entry->type == LibraryEntryType::Directory)
  1495. {
  1496. DirectoryEntry* dirEntry = static_cast<DirectoryEntry*>(entry);
  1497. for (auto& child : dirEntry->mChildren)
  1498. invalidateRecursive(child.get());
  1499. *dirEntry = DirectoryEntry();
  1500. }
  1501. else
  1502. {
  1503. FileEntry* fileEntry = static_cast<FileEntry*>(entry);
  1504. *fileEntry = FileEntry();
  1505. }
  1506. };
  1507. assert(mQueuedImports.empty());
  1508. invalidateRecursive(mRootEntry.get());
  1509. mRootEntry = nullptr;
  1510. }
  1511. Vector<Path> ProjectLibrary::getImportDependencies(const FileEntry* entry)
  1512. {
  1513. Vector<Path> output;
  1514. if (entry->meta == nullptr)
  1515. return output;
  1516. auto& resourceMetas = entry->meta->getResourceMetaData();
  1517. for(auto& resMeta : resourceMetas)
  1518. {
  1519. if (resMeta->getTypeID() == TID_Shader)
  1520. {
  1521. SPtr<ShaderMetaData> metaData = std::static_pointer_cast<ShaderMetaData>(resMeta->getResourceMetaData());
  1522. for (auto& include : metaData->includes)
  1523. output.push_back(include);
  1524. }
  1525. }
  1526. return output;
  1527. }
  1528. void ProjectLibrary::addDependencies(const FileEntry* entry)
  1529. {
  1530. Vector<Path> dependencies = getImportDependencies(entry);
  1531. for (auto& dependency : dependencies)
  1532. mDependencies[dependency].push_back(entry->path);
  1533. }
  1534. void ProjectLibrary::removeDependencies(const FileEntry* entry)
  1535. {
  1536. Vector<Path> dependencies = getImportDependencies(entry);
  1537. for (auto& dependency : dependencies)
  1538. {
  1539. Vector<Path>& curDependencies = mDependencies[dependency];
  1540. auto iterRemove = std::remove_if(curDependencies.begin(), curDependencies.end(),
  1541. [&](const Path& x)
  1542. {
  1543. return x == entry->path;
  1544. });
  1545. curDependencies.erase(iterRemove, curDependencies.end());
  1546. }
  1547. }
  1548. void ProjectLibrary::reimportDependants(const Path& entryPath)
  1549. {
  1550. auto iterFind = mDependencies.find(entryPath);
  1551. if (iterFind == mDependencies.end())
  1552. return;
  1553. // Make a copy since we might modify this list during reimport
  1554. Vector<Path> dependencies = iterFind->second;
  1555. for (auto& dependency : dependencies)
  1556. {
  1557. LibraryEntry* entry = findEntry(dependency).get();
  1558. if (entry != nullptr && entry->type == LibraryEntryType::File)
  1559. {
  1560. FileEntry* resEntry = static_cast<FileEntry*>(entry);
  1561. SPtr<ImportOptions> importOptions;
  1562. if (resEntry->meta != nullptr)
  1563. importOptions = resEntry->meta->getImportOptions();
  1564. reimportResourceInternal(resEntry, importOptions, true);
  1565. }
  1566. }
  1567. }
  1568. BS_ED_EXPORT ProjectLibrary& gProjectLibrary()
  1569. {
  1570. return ProjectLibrary::instance();
  1571. }
  1572. }