BsProjectLibrary.cpp 56 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944
  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(); ++iterMeta)
  577. {
  578. const SPtr<ProjectResourceMeta>& metaEntry = *iterMeta;
  579. if (name == metaEntry->getUniqueName())
  580. {
  581. // Make sure the UUID we used for saving the resource matches the current one (should always
  582. // be true unless the meta-data somehow changes while the async import is happening)
  583. assert(entry.uuid == metaEntry->getUUID());
  584. HResource importedResource = gResources()._getResourceHandle(metaEntry->getUUID());
  585. gResources().update(importedResource, entry.resource);
  586. metaEntry->setPreviewIcons(icons);
  587. fileEntry->meta->add(metaEntry);
  588. iterMeta = existingMetas.erase(iterMeta);
  589. foundMeta = true;
  590. break;
  591. }
  592. }
  593. if (!foundMeta)
  594. {
  595. HResource importedResource;
  596. // Native resources are always expected to have a handle since Resources::load was called during
  597. // the 'import' step
  598. if (import.native)
  599. importedResource = entry.handle;
  600. else
  601. importedResource = gResources()._createResourceHandle(entry.resource, entry.uuid);
  602. SPtr<ResourceMetaData> subMeta = entry.resource->getMetaData();
  603. const UINT32 typeId = entry.resource->getTypeId();
  604. const UUID& UUID = importedResource.getUUID();
  605. SPtr<ProjectResourceMeta> resMeta = ProjectResourceMeta::create(name, UUID, typeId,
  606. icons, subMeta);
  607. fileEntry->meta->add(resMeta);
  608. }
  609. // Keep resource metas that we are not currently using, in case they get restored so their references
  610. // don't get broken
  611. if (!import.pruneMetas)
  612. {
  613. for (auto& metaEntry : existingMetas)
  614. fileEntry->meta->addInactive(metaEntry);
  615. }
  616. // Update UUID to path mapping
  617. if (isFirst)
  618. mUUIDToPath[entry.uuid] = fileEntry->path;
  619. else
  620. mUUIDToPath[entry.uuid] = fileEntry->path + name;
  621. isFirst = false;
  622. // Register path in manifest
  623. mResourceManifest->registerResource(entry.uuid, internalResourcesPath);
  624. }
  625. // Note: Ideally we replace this with a specialized BinaryCompare method
  626. bool metaModified = true;
  627. if(orgMetaData != nullptr)
  628. {
  629. SPtr<SerializedObject> newMetaData = SerializedObject::create(*fileEntry->meta);
  630. BinaryDiff diffHandler;
  631. SPtr<SerializedObject> diff = diffHandler.generateDiff(orgMetaData, newMetaData);
  632. metaModified = diff != nullptr;
  633. }
  634. if(metaModified)
  635. {
  636. // Save the meta file
  637. FileEncoder fs(metaPath);
  638. fs.encode(fileEntry->meta.get());
  639. }
  640. // Register any dependencies this resource depends on
  641. addDependencies(fileEntry);
  642. // Notify the outside world import is doen
  643. onEntryImported(fileEntry->path);
  644. // Queue any resources dependant on this one for import
  645. reimportDependants(fileEntry->path);
  646. return true;
  647. }
  648. void ProjectLibrary::_finishQueuedImports(bool wait)
  649. {
  650. for(auto iter = mQueuedImports.begin(); iter != mQueuedImports.end();)
  651. {
  652. if(finishQueuedImport(iter->first, *iter->second, wait))
  653. iter = mQueuedImports.erase(iter);
  654. else
  655. ++iter;
  656. }
  657. }
  658. bool ProjectLibrary::isUpToDate(FileEntry* resource) const
  659. {
  660. SPtr<QueuedImport> queuedImport;
  661. if(resource->meta == nullptr)
  662. {
  663. // Allow no meta if import in progress
  664. const auto iterFind = mQueuedImports.find(resource);
  665. if(iterFind == mQueuedImports.end())
  666. return false;
  667. queuedImport = iterFind->second;
  668. }
  669. else
  670. {
  671. auto& resourceMetas = resource->meta->getResourceMetaData();
  672. for (auto& resMeta : resourceMetas)
  673. {
  674. Path internalPath;
  675. if (!mResourceManifest->uuidToFilePath(resMeta->getUUID(), internalPath))
  676. return false;
  677. if (!FileSystem::isFile(internalPath))
  678. return false;
  679. }
  680. }
  681. // Note: We're keeping separate update times for queued imports. This allows the import to be cancelled (either by
  682. // user or by app crashing), without updating the actual update time. This way the systems knows to try to reimport
  683. // the resource on the next check. At the same time we don't want our checkForModifications function to keep
  684. // trying to reimport a resource if it's already been queued for import.
  685. const std::time_t lastUpdateTime = queuedImport ? queuedImport->timestamp : resource->lastUpdateTime;
  686. const std::time_t lastModifiedTime = FileSystem::getLastModifiedTime(resource->path);
  687. return lastModifiedTime <= lastUpdateTime;
  688. }
  689. Vector<USPtr<ProjectLibrary::LibraryEntry>> ProjectLibrary::search(const String& pattern)
  690. {
  691. return search(pattern, {});
  692. }
  693. Vector<USPtr<ProjectLibrary::LibraryEntry>> ProjectLibrary::search(const String& pattern, const Vector<UINT32>& typeIds)
  694. {
  695. Vector<USPtr<LibraryEntry>> foundEntries;
  696. std::regex escape("[.^$|()\\[\\]{}*+?\\\\]");
  697. String replace("\\\\&");
  698. String escapedPattern = std::regex_replace(pattern, escape, replace, std::regex_constants::match_default | std::regex_constants::format_sed);
  699. // For some reason MSVC stdlib implementation requires a different pattern than stdlib one
  700. #if BS_PLATFORM == BS_PLATFORM_WIN32
  701. std::regex wildcard("\\\\\\*");
  702. #else
  703. std::regex wildcard("\\\\\\\\\\*");
  704. #endif
  705. String wildcardReplace(".*");
  706. String searchPattern = std::regex_replace(escapedPattern, wildcard, ".*");
  707. std::regex searchRegex(searchPattern, std::regex_constants::ECMAScript | std::regex_constants::icase);
  708. Stack<DirectoryEntry*> todo;
  709. todo.push(mRootEntry.get());
  710. while (!todo.empty())
  711. {
  712. DirectoryEntry* dirEntry = todo.top();
  713. todo.pop();
  714. for (auto& child : dirEntry->mChildren)
  715. {
  716. if (std::regex_match(child->elementName, searchRegex))
  717. {
  718. if (typeIds.empty())
  719. foundEntries.push_back(child);
  720. else
  721. {
  722. if (child->type == LibraryEntryType::File)
  723. {
  724. FileEntry* childFileEntry = static_cast<FileEntry*>(child.get());
  725. if (childFileEntry->meta != nullptr)
  726. {
  727. auto& resourceMetas = childFileEntry->meta->getResourceMetaData();
  728. for (auto& typeId : typeIds)
  729. {
  730. bool found = false;
  731. for (auto& resMeta : resourceMetas)
  732. {
  733. if (resMeta->getTypeID() == typeId)
  734. {
  735. foundEntries.push_back(child);
  736. found = true;
  737. break;
  738. }
  739. }
  740. if (found)
  741. break;
  742. }
  743. }
  744. }
  745. }
  746. }
  747. if (child->type == LibraryEntryType::Directory)
  748. {
  749. DirectoryEntry* childDirEntry = static_cast<DirectoryEntry*>(child.get());
  750. todo.push(childDirEntry);
  751. }
  752. }
  753. }
  754. std::sort(foundEntries.begin(), foundEntries.end(),
  755. [&](const USPtr<LibraryEntry>& a, const USPtr<LibraryEntry>& b)
  756. {
  757. return a->elementName.compare(b->elementName) < 0;
  758. });
  759. return foundEntries;
  760. }
  761. USPtr<ProjectLibrary::LibraryEntry> ProjectLibrary::findEntry(const Path& path) const
  762. {
  763. Path relPath;
  764. const Path* searchPath;
  765. if (path.isAbsolute())
  766. {
  767. if (!mResourcesFolder.includes(path))
  768. return nullptr;
  769. relPath = path.getRelative(mRootEntry->path);
  770. searchPath = &relPath;
  771. }
  772. else
  773. searchPath = &path;
  774. BS_ASSERT(mRootEntry->path == mResourcesFolder);
  775. UINT32 numElems = searchPath->getNumDirectories() + (searchPath->isFile() ? 1 : 0);
  776. UINT32 idx = 0;
  777. USPtr<LibraryEntry> rootLibEntry = mRootEntry;
  778. USPtr<LibraryEntry>* current = &rootLibEntry;
  779. while (current != nullptr)
  780. {
  781. if (idx == numElems)
  782. return *current;
  783. const String& curElem =
  784. (searchPath->isFile() && idx == (numElems - 1)) ? searchPath->getFilename() : (*searchPath)[idx];
  785. if ((*current)->type == LibraryEntryType::Directory)
  786. {
  787. DirectoryEntry* dirEntry = static_cast<DirectoryEntry*>(current->get());
  788. size_t curElemHash = bs_hash(UTF8::toLower(curElem));
  789. current = nullptr;
  790. for (auto& child : dirEntry->mChildren)
  791. {
  792. if(curElemHash != child->elementNameHash)
  793. continue;
  794. if (Path::comparePathElem(curElem, child->elementName))
  795. {
  796. idx++;
  797. current = &child;
  798. break;
  799. }
  800. }
  801. }
  802. else // Found file
  803. {
  804. // If this is next to last element, next entry is assumed to be a sub-resource name, which we ignore
  805. if (idx == (numElems - 1))
  806. return *current;
  807. else
  808. break; // Not a valid path
  809. }
  810. }
  811. return nullptr;
  812. }
  813. bool ProjectLibrary::isSubresource(const Path& path) const
  814. {
  815. UINT32 numElems = path.getNumDirectories() + (path.isFile() ? 1 : 0);
  816. if (numElems <= 1)
  817. return false;
  818. Path filePath = path;
  819. filePath.makeParent();
  820. LibraryEntry* entry = findEntry(filePath).get();
  821. return entry != nullptr && entry->type == LibraryEntryType::File;
  822. }
  823. SPtr<ProjectResourceMeta> ProjectLibrary::findResourceMeta(const Path& path) const
  824. {
  825. UINT32 numElems = path.getNumDirectories() + (path.isFile() ? 1 : 0);
  826. // Check if it is a subresource path
  827. if(numElems > 1)
  828. {
  829. Path filePath = path;
  830. filePath.makeParent();
  831. LibraryEntry* entry = findEntry(filePath).get();
  832. if (entry == nullptr)
  833. return nullptr;
  834. // Entry is a subresource
  835. if (entry->type == LibraryEntryType::File)
  836. {
  837. FileEntry* fileEntry = static_cast<FileEntry*>(entry);
  838. if (fileEntry->meta == nullptr)
  839. return nullptr;
  840. auto& resourceMetas = fileEntry->meta->getResourceMetaData();
  841. for(auto& resMeta : resourceMetas)
  842. {
  843. if (resMeta->getUniqueName() == path.getTail())
  844. return resMeta;
  845. }
  846. // Found the file but no subresource or meta information
  847. return nullptr;
  848. }
  849. else // Entry not a subresource
  850. {
  851. DirectoryEntry* dirEntry = static_cast<DirectoryEntry*>(entry);
  852. for (auto& child : dirEntry->mChildren)
  853. {
  854. if (Path::comparePathElem(path.getTail(), child->elementName))
  855. {
  856. if (child->type == LibraryEntryType::File)
  857. {
  858. FileEntry* fileEntry = static_cast<FileEntry*>(child.get());
  859. if (fileEntry->meta == nullptr)
  860. return nullptr;
  861. return fileEntry->meta->getResourceMetaData()[0];
  862. }
  863. }
  864. }
  865. return nullptr;
  866. }
  867. }
  868. // Not a subresource path, load directly
  869. {
  870. LibraryEntry* entry = findEntry(path).get();
  871. if (entry == nullptr || entry->type == LibraryEntryType::Directory)
  872. return nullptr;
  873. FileEntry* fileEntry = static_cast<FileEntry*>(entry);
  874. if (fileEntry->meta == nullptr)
  875. return nullptr;
  876. return fileEntry->meta->getResourceMetaData()[0];
  877. }
  878. }
  879. Path ProjectLibrary::uuidToPath(const UUID& uuid) const
  880. {
  881. auto iterFind = mUUIDToPath.find(uuid);
  882. if (iterFind != mUUIDToPath.end())
  883. return iterFind->second;
  884. return Path::BLANK;
  885. }
  886. void ProjectLibrary::createEntry(const HResource& resource, const Path& path)
  887. {
  888. if (resource == nullptr)
  889. return;
  890. Path assetPath = path;
  891. if (path.isAbsolute())
  892. {
  893. if (!getResourcesFolder().includes(path))
  894. return;
  895. assetPath = path.getRelative(getResourcesFolder());
  896. }
  897. deleteEntry(assetPath);
  898. resource->setName(path.getFilename(false));
  899. Path absPath = assetPath.getAbsolute(getResourcesFolder());
  900. Resources::instance().save(resource, absPath, false);
  901. Path parentDirPath = absPath.getParent();
  902. USPtr<LibraryEntry> parentEntry = findEntry(parentDirPath);
  903. // Register parent hierarchy if not found
  904. DirectoryEntry* entryParent = nullptr;
  905. if (parentEntry == nullptr)
  906. createInternalParentHierarchy(absPath, nullptr, &entryParent);
  907. else
  908. entryParent = static_cast<DirectoryEntry*>(parentEntry.get());
  909. addResourceInternal(entryParent, absPath, nullptr, true, true);
  910. }
  911. void ProjectLibrary::saveEntry(const HResource& resource)
  912. {
  913. if (resource == nullptr)
  914. return;
  915. Path filePath = uuidToPath(resource.getUUID());
  916. if(filePath.isEmpty())
  917. {
  918. LOGWRN("Trying to save a resource that hasn't been registered with the project library. Call ProjectLibrary::create first.");
  919. return;
  920. }
  921. filePath.makeAbsolute(getResourcesFolder());
  922. Resources::instance().save(resource, filePath, true);
  923. LibraryEntry* fileEntry = findEntry(filePath).get();
  924. if(fileEntry)
  925. reimportResourceInternal(static_cast<FileEntry*>(fileEntry), nullptr, true, false, true);
  926. }
  927. void ProjectLibrary::createFolderEntry(const Path& path)
  928. {
  929. Path fullPath = path;
  930. if (fullPath.isAbsolute())
  931. {
  932. if (!mResourcesFolder.includes(fullPath))
  933. return;
  934. }
  935. else
  936. fullPath.makeAbsolute(mResourcesFolder);
  937. if (FileSystem::isDirectory(fullPath))
  938. return; // Already exists
  939. FileSystem::createDir(fullPath);
  940. Path parentPath = fullPath.getParent();
  941. DirectoryEntry* newEntryParent = nullptr;
  942. USPtr<LibraryEntry> newEntryParentLib = findEntry(parentPath);
  943. if (newEntryParentLib != nullptr)
  944. {
  945. assert(newEntryParentLib->type == LibraryEntryType::Directory);
  946. newEntryParent = static_cast<DirectoryEntry*>(newEntryParentLib.get());
  947. }
  948. DirectoryEntry* newHierarchyParent = nullptr;
  949. if (newEntryParent == nullptr) // New path parent doesn't exist, so we need to create the hierarchy
  950. createInternalParentHierarchy(fullPath, &newHierarchyParent, &newEntryParent);
  951. addDirectoryInternal(newEntryParent, fullPath);
  952. }
  953. void ProjectLibrary::moveEntry(const Path& oldPath, const Path& newPath, bool overwrite)
  954. {
  955. Path oldFullPath = oldPath;
  956. if (!oldFullPath.isAbsolute())
  957. oldFullPath.makeAbsolute(mResourcesFolder);
  958. Path newFullPath = newPath;
  959. if (!newFullPath.isAbsolute())
  960. newFullPath.makeAbsolute(mResourcesFolder);
  961. Path parentPath = newFullPath.getParent();
  962. if (!FileSystem::isDirectory(parentPath))
  963. {
  964. LOGWRN("Move operation failed. Destination directory \"" + parentPath.toString() + "\" doesn't exist.");
  965. return;
  966. }
  967. if(FileSystem::isFile(oldFullPath) || FileSystem::isDirectory(oldFullPath))
  968. FileSystem::move(oldFullPath, newFullPath, overwrite);
  969. Path oldMetaPath = getMetaPath(oldFullPath);
  970. Path newMetaPath = getMetaPath(newFullPath);
  971. USPtr<LibraryEntry> oldEntry = findEntry(oldFullPath);
  972. if(oldEntry != nullptr) // Moving from the Resources folder
  973. {
  974. // Moved outside of Resources, delete entry & meta file
  975. if (!mResourcesFolder.includes(newFullPath))
  976. {
  977. if(oldEntry->type == LibraryEntryType::File)
  978. deleteResourceInternal(static_pointer_cast<FileEntry>(oldEntry));
  979. else if(oldEntry->type == LibraryEntryType::Directory)
  980. deleteDirectoryInternal(static_pointer_cast<DirectoryEntry>(oldEntry));
  981. }
  982. else // Just moving internally
  983. {
  984. onEntryRemoved(oldEntry->path);
  985. USPtr<FileEntry> fileEntry = nullptr;
  986. if (oldEntry->type == LibraryEntryType::File)
  987. {
  988. fileEntry = static_pointer_cast<FileEntry>(oldEntry);
  989. removeDependencies(fileEntry.get());
  990. // Update uuid <-> path mapping
  991. if(fileEntry->meta != nullptr)
  992. {
  993. auto& resourceMetas = fileEntry->meta->getResourceMetaData();
  994. if (!resourceMetas.empty())
  995. {
  996. mUUIDToPath[resourceMetas[0]->getUUID()] = newFullPath;
  997. for (UINT32 i = 1; i < (UINT32)resourceMetas.size(); i++)
  998. {
  999. SPtr<ProjectResourceMeta> resMeta = resourceMetas[i];
  1000. const UUID& UUID = resMeta->getUUID();
  1001. mUUIDToPath[UUID] = newFullPath + resMeta->getUniqueName();
  1002. }
  1003. }
  1004. }
  1005. }
  1006. if(FileSystem::isFile(oldMetaPath))
  1007. FileSystem::move(oldMetaPath, newMetaPath);
  1008. DirectoryEntry* parent = oldEntry->parent;
  1009. auto findIter = std::find(parent->mChildren.begin(), parent->mChildren.end(), oldEntry);
  1010. if(findIter != parent->mChildren.end())
  1011. parent->mChildren.erase(findIter);
  1012. Path parentPath = newFullPath.getParent();
  1013. DirectoryEntry* newEntryParent = nullptr;
  1014. USPtr<LibraryEntry> newEntryParentLib = findEntry(parentPath);
  1015. if(newEntryParentLib != nullptr)
  1016. {
  1017. assert(newEntryParentLib->type == LibraryEntryType::Directory);
  1018. newEntryParent = static_cast<DirectoryEntry*>(newEntryParentLib.get());
  1019. }
  1020. DirectoryEntry* newHierarchyParent = nullptr;
  1021. if(newEntryParent == nullptr) // New path parent doesn't exist, so we need to create the hierarchy
  1022. createInternalParentHierarchy(newFullPath, &newHierarchyParent, &newEntryParent);
  1023. newEntryParent->mChildren.push_back(oldEntry);
  1024. oldEntry->parent = newEntryParent;
  1025. oldEntry->path = newFullPath;
  1026. oldEntry->elementName = newFullPath.getTail();
  1027. oldEntry->elementNameHash = bs_hash(UTF8::toLower(oldEntry->elementName));
  1028. if(oldEntry->type == LibraryEntryType::Directory) // Update child paths
  1029. {
  1030. Stack<LibraryEntry*> todo;
  1031. todo.push(oldEntry.get());
  1032. while(!todo.empty())
  1033. {
  1034. LibraryEntry* curEntry = todo.top();
  1035. todo.pop();
  1036. DirectoryEntry* curDirEntry = static_cast<DirectoryEntry*>(curEntry);
  1037. for(auto& child : curDirEntry->mChildren)
  1038. {
  1039. child->path = child->parent->path;
  1040. child->path.append(child->elementName);
  1041. if(child->type == LibraryEntryType::Directory)
  1042. todo.push(child.get());
  1043. }
  1044. }
  1045. }
  1046. onEntryAdded(oldEntry->path);
  1047. if (fileEntry != nullptr)
  1048. {
  1049. reimportDependants(oldFullPath);
  1050. reimportDependants(newFullPath);
  1051. }
  1052. }
  1053. }
  1054. else // Moving from outside of the Resources folder (likely adding a new resource)
  1055. {
  1056. checkForModifications(newFullPath);
  1057. }
  1058. }
  1059. void ProjectLibrary::copyEntry(const Path& oldPath, const Path& newPath, bool overwrite)
  1060. {
  1061. Path oldFullPath = oldPath;
  1062. if (!oldFullPath.isAbsolute())
  1063. oldFullPath.makeAbsolute(mResourcesFolder);
  1064. Path newFullPath = newPath;
  1065. if (!newFullPath.isAbsolute())
  1066. newFullPath.makeAbsolute(mResourcesFolder);
  1067. if (!FileSystem::exists(oldFullPath))
  1068. return;
  1069. FileSystem::copy(oldFullPath, newFullPath, overwrite);
  1070. // Copying a file/folder outside of the Resources folder, no special handling needed
  1071. if (!mResourcesFolder.includes(newFullPath))
  1072. return;
  1073. Path parentPath = newFullPath.getParent();
  1074. DirectoryEntry* newEntryParent = nullptr;
  1075. LibraryEntry* newEntryParentLib = findEntry(parentPath).get();
  1076. if (newEntryParentLib != nullptr)
  1077. {
  1078. assert(newEntryParentLib->type == LibraryEntryType::Directory);
  1079. newEntryParent = static_cast<DirectoryEntry*>(newEntryParentLib);
  1080. }
  1081. // If the source is outside of Resources folder, just plain import the copy
  1082. LibraryEntry* oldEntry = findEntry(oldFullPath).get();
  1083. if (oldEntry == nullptr)
  1084. {
  1085. checkForModifications(newFullPath);
  1086. return;
  1087. }
  1088. // Both source and destination are within Resources folder, need to preserve import options on the copies
  1089. if (FileSystem::isFile(newFullPath))
  1090. {
  1091. assert(oldEntry->type == LibraryEntryType::File);
  1092. FileEntry* oldResEntry = static_cast<FileEntry*>(oldEntry);
  1093. SPtr<ImportOptions> importOptions;
  1094. if (oldResEntry->meta != nullptr)
  1095. importOptions = oldResEntry->meta->getImportOptions();
  1096. addResourceInternal(newEntryParent, newFullPath, importOptions, true);
  1097. }
  1098. else
  1099. {
  1100. assert(oldEntry->type == LibraryEntryType::File);
  1101. DirectoryEntry* oldDirEntry = static_cast<DirectoryEntry*>(oldEntry);
  1102. DirectoryEntry* newDirEntry = addDirectoryInternal(newEntryParent, newFullPath).get();
  1103. Stack<std::tuple<DirectoryEntry*, DirectoryEntry*>> todo;
  1104. todo.push(std::make_tuple(oldDirEntry, newDirEntry));
  1105. while (!todo.empty())
  1106. {
  1107. auto current = todo.top();
  1108. todo.pop();
  1109. DirectoryEntry* sourceDir = std::get<0>(current);
  1110. DirectoryEntry* destDir = std::get<1>(current);
  1111. for (auto& child : sourceDir->mChildren)
  1112. {
  1113. Path childDestPath = destDir->path;
  1114. childDestPath.append(child->path.getTail());
  1115. if (child->type == LibraryEntryType::File)
  1116. {
  1117. FileEntry* childResEntry = static_cast<FileEntry*>(child.get());
  1118. SPtr<ImportOptions> importOptions;
  1119. if (childResEntry->meta != nullptr)
  1120. importOptions = childResEntry->meta->getImportOptions();
  1121. addResourceInternal(destDir, childDestPath, importOptions, true);
  1122. }
  1123. else // Directory
  1124. {
  1125. DirectoryEntry* childSourceDirEntry = static_cast<DirectoryEntry*>(child.get());
  1126. DirectoryEntry* childDestDirEntry = addDirectoryInternal(destDir, childDestPath).get();
  1127. todo.push(std::make_tuple(childSourceDirEntry, childDestDirEntry));
  1128. }
  1129. }
  1130. }
  1131. }
  1132. }
  1133. void ProjectLibrary::deleteEntry(const Path& path)
  1134. {
  1135. Path fullPath = path;
  1136. if (!fullPath.isAbsolute())
  1137. fullPath.makeAbsolute(mResourcesFolder);
  1138. if(FileSystem::exists(fullPath))
  1139. FileSystem::remove(fullPath);
  1140. USPtr<LibraryEntry> entry = findEntry(fullPath);
  1141. if(entry != nullptr)
  1142. {
  1143. if(entry->type == LibraryEntryType::File)
  1144. deleteResourceInternal(static_pointer_cast<FileEntry>(entry));
  1145. else if(entry->type == LibraryEntryType::Directory)
  1146. deleteDirectoryInternal(static_pointer_cast<DirectoryEntry>(entry));
  1147. }
  1148. }
  1149. void ProjectLibrary::reimport(const Path& path, const SPtr<ImportOptions>& importOptions, bool forceReimport,
  1150. bool synchronous)
  1151. {
  1152. LibraryEntry* entry = findEntry(path).get();
  1153. if (entry != nullptr)
  1154. {
  1155. if (entry->type == LibraryEntryType::File)
  1156. {
  1157. FileEntry* resEntry = static_cast<FileEntry*>(entry);
  1158. reimportResourceInternal(resEntry, importOptions, forceReimport, synchronous);
  1159. }
  1160. }
  1161. }
  1162. float ProjectLibrary::getImportProgress(const Path& path) const
  1163. {
  1164. LibraryEntry* entry = findEntry(path).get();
  1165. if (entry == nullptr)
  1166. return 0.0f;
  1167. if(entry->type == LibraryEntryType::Directory)
  1168. return 1.0f;
  1169. // Note: Only supporting binary progress reporting for now
  1170. const auto iterFind = mQueuedImports.find(static_cast<FileEntry*>(entry));
  1171. return iterFind != mQueuedImports.end() ? 0.0f : 1.0f;
  1172. }
  1173. void ProjectLibrary::cancelImport()
  1174. {
  1175. for(auto& entry : mQueuedImports)
  1176. entry.second->canceled = true;
  1177. }
  1178. void ProjectLibrary::waitForQueuedImport(FileEntry* fileEntry)
  1179. {
  1180. const auto iterFind = mQueuedImports.find(fileEntry);
  1181. if (iterFind != mQueuedImports.end())
  1182. {
  1183. if (finishQueuedImport(fileEntry, *iterFind->second, true))
  1184. mQueuedImports.erase(iterFind);
  1185. }
  1186. }
  1187. void ProjectLibrary::setIncludeInBuild(const Path& path, bool include)
  1188. {
  1189. LibraryEntry* entry = findEntry(path).get();
  1190. if (entry == nullptr || entry->type == LibraryEntryType::Directory)
  1191. return;
  1192. auto fileEntry = static_cast<FileEntry*>(entry);
  1193. // Any queued imports will overwrite the meta file, so make sure they finish first
  1194. waitForQueuedImport(fileEntry);
  1195. if (fileEntry->meta == nullptr)
  1196. return;
  1197. fileEntry->meta->setIncludeInBuild(include);
  1198. Path metaPath = fileEntry->path;
  1199. metaPath.setFilename(metaPath.getFilename() + ".meta");
  1200. FileEncoder fs(metaPath);
  1201. fs.encode(fileEntry->meta.get());
  1202. }
  1203. void ProjectLibrary::setUserData(const Path& path, const SPtr<IReflectable>& userData)
  1204. {
  1205. LibraryEntry* entry = findEntry(path).get();
  1206. if (entry == nullptr || entry->type == LibraryEntryType::Directory)
  1207. return;
  1208. auto fileEntry = static_cast<FileEntry*>(entry);
  1209. // Any queued imports will overwrite the meta file, so make sure they finish first
  1210. waitForQueuedImport(fileEntry);
  1211. SPtr<ProjectResourceMeta> resMeta = findResourceMeta(path);
  1212. if (resMeta == nullptr)
  1213. return;
  1214. resMeta->mUserData = userData;
  1215. Path metaPath = fileEntry->path;
  1216. metaPath.setFilename(metaPath.getFilename() + ".meta");
  1217. FileEncoder fs(metaPath);
  1218. fs.encode(fileEntry->meta.get());
  1219. }
  1220. Vector<USPtr<ProjectLibrary::FileEntry>> ProjectLibrary::getResourcesForBuild() const
  1221. {
  1222. Vector<USPtr<FileEntry>> output;
  1223. Stack<DirectoryEntry*> todo;
  1224. todo.push(mRootEntry.get());
  1225. while (!todo.empty())
  1226. {
  1227. DirectoryEntry* directory = todo.top();
  1228. todo.pop();
  1229. for (auto& child : directory->mChildren)
  1230. {
  1231. if (child->type == LibraryEntryType::File)
  1232. {
  1233. FileEntry* resEntry = static_cast<FileEntry*>(child.get());
  1234. if (resEntry->meta != nullptr && resEntry->meta->getIncludeInBuild())
  1235. output.push_back(static_pointer_cast<FileEntry>(child));
  1236. }
  1237. else if (child->type == LibraryEntryType::Directory)
  1238. {
  1239. todo.push(static_cast<DirectoryEntry*>(child.get()));
  1240. }
  1241. }
  1242. }
  1243. return output;
  1244. }
  1245. HResource ProjectLibrary::load(const Path& path)
  1246. {
  1247. SPtr<ProjectResourceMeta> meta = findResourceMeta(path);
  1248. if (meta == nullptr)
  1249. return HResource();
  1250. ResourceLoadFlags loadFlags = ResourceLoadFlag::Default | ResourceLoadFlag::KeepSourceData;
  1251. const UUID& resUUID = meta->getUUID();
  1252. return gResources().loadFromUUID(resUUID, false, loadFlags);
  1253. }
  1254. void ProjectLibrary::createInternalParentHierarchy(const Path& fullPath, DirectoryEntry** newHierarchyRoot,
  1255. DirectoryEntry** newHierarchyLeaf)
  1256. {
  1257. Path parentPath = fullPath;
  1258. DirectoryEntry* newEntryParent = nullptr;
  1259. Stack<Path> parentPaths;
  1260. do
  1261. {
  1262. Path newParentPath = parentPath.getParent();
  1263. if(newParentPath == parentPath)
  1264. break;
  1265. LibraryEntry* newEntryParentLib = findEntry(newParentPath).get();
  1266. if(newEntryParentLib != nullptr)
  1267. {
  1268. assert(newEntryParentLib->type == LibraryEntryType::Directory);
  1269. newEntryParent = static_cast<DirectoryEntry*>(newEntryParentLib);
  1270. break;
  1271. }
  1272. parentPaths.push(newParentPath);
  1273. parentPath = newParentPath;
  1274. } while (true);
  1275. assert(newEntryParent != nullptr); // Must exist
  1276. if(newHierarchyRoot != nullptr)
  1277. *newHierarchyRoot = newEntryParent;
  1278. while(!parentPaths.empty())
  1279. {
  1280. Path curPath = parentPaths.top();
  1281. parentPaths.pop();
  1282. newEntryParent = addDirectoryInternal(newEntryParent, curPath).get();
  1283. }
  1284. if(newHierarchyLeaf != nullptr)
  1285. *newHierarchyLeaf = newEntryParent;
  1286. }
  1287. Path ProjectLibrary::getMetaPath(const Path& path) const
  1288. {
  1289. Path metaPath = path;
  1290. metaPath.setFilename(metaPath.getFilename() + ".meta");
  1291. return metaPath;
  1292. }
  1293. bool ProjectLibrary::isMeta(const Path& fullPath) const
  1294. {
  1295. return fullPath.getExtension() == ".meta";
  1296. }
  1297. bool ProjectLibrary::isNative(const Path& path) const
  1298. {
  1299. String extension = path.getExtension();
  1300. return extension == ".asset" || extension == ".prefab";
  1301. }
  1302. void ProjectLibrary::unloadLibrary()
  1303. {
  1304. if (!mIsLoaded)
  1305. return;
  1306. _finishQueuedImports(true);
  1307. mProjectFolder = Path::BLANK;
  1308. mResourcesFolder = Path::BLANK;
  1309. clearEntries();
  1310. mRootEntry = bs_ushared_ptr_new<DirectoryEntry>(mResourcesFolder, mResourcesFolder.getTail(), nullptr);
  1311. mDependencies.clear();
  1312. gResources().unregisterResourceManifest(mResourceManifest);
  1313. mResourceManifest = nullptr;
  1314. mIsLoaded = false;
  1315. }
  1316. void ProjectLibrary::makeEntriesRelative()
  1317. {
  1318. // Make all paths relative before saving
  1319. std::function<void(LibraryEntry*, const Path&)> makeRelative =
  1320. [&](LibraryEntry* entry, const Path& root)
  1321. {
  1322. entry->path.makeRelative(root);
  1323. if (entry->type == LibraryEntryType::Directory)
  1324. {
  1325. DirectoryEntry* dirEntry = static_cast<DirectoryEntry*>(entry);
  1326. for (auto& child : dirEntry->mChildren)
  1327. makeRelative(child.get(), root);
  1328. }
  1329. };
  1330. Path root = getResourcesFolder();
  1331. makeRelative(mRootEntry.get(), root);
  1332. }
  1333. void ProjectLibrary::makeEntriesAbsolute()
  1334. {
  1335. std::function<void(LibraryEntry*, const Path&)> makeAbsolute =
  1336. [&](LibraryEntry* entry, const Path& root)
  1337. {
  1338. entry->path.makeAbsolute(root);
  1339. if (entry->type == LibraryEntryType::Directory)
  1340. {
  1341. DirectoryEntry* dirEntry = static_cast<DirectoryEntry*>(entry);
  1342. for (auto& child : dirEntry->mChildren)
  1343. makeAbsolute(child.get(), root);
  1344. }
  1345. };
  1346. Path root = getResourcesFolder();
  1347. makeAbsolute(mRootEntry.get(), root);
  1348. }
  1349. void ProjectLibrary::saveLibrary()
  1350. {
  1351. if (!mIsLoaded)
  1352. return;
  1353. // Make all paths relative before saving
  1354. makeEntriesRelative();
  1355. SPtr<ProjectLibraryEntries> libEntries = ProjectLibraryEntries::create(mRootEntry);
  1356. Path libraryEntriesPath = mProjectFolder;
  1357. libraryEntriesPath.append(PROJECT_INTERNAL_DIR);
  1358. libraryEntriesPath.append(LIBRARY_ENTRIES_FILENAME);
  1359. FileEncoder fs(libraryEntriesPath);
  1360. fs.encode(libEntries.get());
  1361. // Restore absolute entry paths
  1362. makeEntriesAbsolute();
  1363. Path resourceManifestPath = mProjectFolder;
  1364. resourceManifestPath.append(PROJECT_INTERNAL_DIR);
  1365. resourceManifestPath.append(RESOURCE_MANIFEST_FILENAME);
  1366. ResourceManifest::save(mResourceManifest, resourceManifestPath, mProjectFolder);
  1367. }
  1368. void ProjectLibrary::loadLibrary()
  1369. {
  1370. unloadLibrary();
  1371. mProjectFolder = gEditorApplication().getProjectPath();
  1372. mResourcesFolder = mProjectFolder;
  1373. mResourcesFolder.append(RESOURCES_DIR);
  1374. mRootEntry = bs_ushared_ptr_new<DirectoryEntry>(mResourcesFolder, mResourcesFolder.getTail(), nullptr);
  1375. Path libraryEntriesPath = mProjectFolder;
  1376. libraryEntriesPath.append(PROJECT_INTERNAL_DIR);
  1377. libraryEntriesPath.append(LIBRARY_ENTRIES_FILENAME);
  1378. if(FileSystem::exists(libraryEntriesPath))
  1379. {
  1380. FileDecoder fs(libraryEntriesPath);
  1381. SPtr<ProjectLibraryEntries> libEntries = std::static_pointer_cast<ProjectLibraryEntries>(fs.decode());
  1382. mRootEntry = libEntries->getRootEntry();
  1383. mRootEntry->parent = nullptr;
  1384. }
  1385. // Entries are stored relative to project folder, but we want their absolute paths now
  1386. makeEntriesAbsolute();
  1387. // Load resource manifest
  1388. Path resourceManifestPath = mProjectFolder;
  1389. resourceManifestPath.append(PROJECT_INTERNAL_DIR);
  1390. resourceManifestPath.append(RESOURCE_MANIFEST_FILENAME);
  1391. if (FileSystem::exists(resourceManifestPath))
  1392. mResourceManifest = ResourceManifest::load(resourceManifestPath, mProjectFolder);
  1393. else
  1394. mResourceManifest = ResourceManifest::create("ProjectLibrary");
  1395. gResources().registerResourceManifest(mResourceManifest);
  1396. // Load all meta files
  1397. Stack<DirectoryEntry*> todo;
  1398. todo.push(mRootEntry.get());
  1399. Vector<USPtr<LibraryEntry>> deletedEntries;
  1400. while(!todo.empty())
  1401. {
  1402. DirectoryEntry* curDir = todo.top();
  1403. todo.pop();
  1404. for(auto& child : curDir->mChildren)
  1405. {
  1406. if(child->type == LibraryEntryType::File)
  1407. {
  1408. USPtr<FileEntry> resEntry = static_pointer_cast<FileEntry>(child);
  1409. if (FileSystem::isFile(resEntry->path))
  1410. {
  1411. if (resEntry->meta == nullptr)
  1412. {
  1413. Path metaPath = resEntry->path;
  1414. metaPath.setFilename(metaPath.getFilename() + ".meta");
  1415. if (FileSystem::isFile(metaPath))
  1416. {
  1417. FileDecoder fs(metaPath);
  1418. SPtr<IReflectable> loadedMeta = fs.decode();
  1419. if (loadedMeta != nullptr && loadedMeta->isDerivedFrom(ProjectFileMeta::getRTTIStatic()))
  1420. {
  1421. SPtr<ProjectFileMeta> fileMeta = std::static_pointer_cast<ProjectFileMeta>(loadedMeta);
  1422. resEntry->meta = fileMeta;
  1423. }
  1424. }
  1425. }
  1426. if (resEntry->meta != nullptr)
  1427. {
  1428. auto& resourceMetas = resEntry->meta->getResourceMetaData();
  1429. if (!resourceMetas.empty())
  1430. {
  1431. mUUIDToPath[resourceMetas[0]->getUUID()] = resEntry->path;
  1432. for (UINT32 i = 1; i < (UINT32)resourceMetas.size(); i++)
  1433. {
  1434. SPtr<ProjectResourceMeta> entry = resourceMetas[i];
  1435. mUUIDToPath[entry->getUUID()] = resEntry->path + entry->getUniqueName();
  1436. }
  1437. }
  1438. }
  1439. addDependencies(resEntry.get());
  1440. }
  1441. else
  1442. deletedEntries.push_back(resEntry);
  1443. }
  1444. else if(child->type == LibraryEntryType::Directory)
  1445. {
  1446. if (FileSystem::isDirectory(child->path))
  1447. todo.push(static_cast<DirectoryEntry*>(child.get()));
  1448. else
  1449. deletedEntries.push_back(child);
  1450. }
  1451. }
  1452. }
  1453. // Remove entries that no longer have corresponding files
  1454. for (auto& deletedEntry : deletedEntries)
  1455. {
  1456. if (deletedEntry->type == LibraryEntryType::File)
  1457. deleteResourceInternal(static_pointer_cast<FileEntry>(deletedEntry));
  1458. else
  1459. deleteDirectoryInternal(static_pointer_cast<DirectoryEntry>(deletedEntry));
  1460. }
  1461. // Clean up internal library folder from obsolete files
  1462. Path internalResourcesFolder = mProjectFolder;
  1463. internalResourcesFolder.append(INTERNAL_RESOURCES_DIR);
  1464. if (FileSystem::exists(internalResourcesFolder))
  1465. {
  1466. Vector<Path> toDelete;
  1467. auto processFile = [&](const Path& file)
  1468. {
  1469. UUID uuid = UUID(file.getFilename(false));
  1470. if (mUUIDToPath.find(uuid) == mUUIDToPath.end())
  1471. {
  1472. mResourceManifest->unregisterResource(uuid);
  1473. toDelete.push_back(file);
  1474. }
  1475. return true;
  1476. };
  1477. FileSystem::iterate(internalResourcesFolder, processFile);
  1478. for (auto& entry : toDelete)
  1479. FileSystem::remove(entry);
  1480. }
  1481. mIsLoaded = true;
  1482. }
  1483. void ProjectLibrary::clearEntries()
  1484. {
  1485. if (mRootEntry == nullptr)
  1486. return;
  1487. std::function<void(LibraryEntry*)> invalidateRecursive =
  1488. [&](LibraryEntry* entry)
  1489. {
  1490. if (entry->type == LibraryEntryType::Directory)
  1491. {
  1492. DirectoryEntry* dirEntry = static_cast<DirectoryEntry*>(entry);
  1493. for (auto& child : dirEntry->mChildren)
  1494. invalidateRecursive(child.get());
  1495. *dirEntry = DirectoryEntry();
  1496. }
  1497. else
  1498. {
  1499. FileEntry* fileEntry = static_cast<FileEntry*>(entry);
  1500. *fileEntry = FileEntry();
  1501. }
  1502. };
  1503. assert(mQueuedImports.empty());
  1504. invalidateRecursive(mRootEntry.get());
  1505. mRootEntry = nullptr;
  1506. }
  1507. Vector<Path> ProjectLibrary::getImportDependencies(const FileEntry* entry)
  1508. {
  1509. Vector<Path> output;
  1510. if (entry->meta == nullptr)
  1511. return output;
  1512. auto& resourceMetas = entry->meta->getResourceMetaData();
  1513. for(auto& resMeta : resourceMetas)
  1514. {
  1515. if (resMeta->getTypeID() == TID_Shader)
  1516. {
  1517. SPtr<ShaderMetaData> metaData = std::static_pointer_cast<ShaderMetaData>(resMeta->getResourceMetaData());
  1518. for (auto& include : metaData->includes)
  1519. output.push_back(include);
  1520. }
  1521. }
  1522. return output;
  1523. }
  1524. void ProjectLibrary::addDependencies(const FileEntry* entry)
  1525. {
  1526. Vector<Path> dependencies = getImportDependencies(entry);
  1527. for (auto& dependency : dependencies)
  1528. mDependencies[dependency].push_back(entry->path);
  1529. }
  1530. void ProjectLibrary::removeDependencies(const FileEntry* entry)
  1531. {
  1532. Vector<Path> dependencies = getImportDependencies(entry);
  1533. for (auto& dependency : dependencies)
  1534. {
  1535. Vector<Path>& curDependencies = mDependencies[dependency];
  1536. auto iterRemove = std::remove_if(curDependencies.begin(), curDependencies.end(),
  1537. [&](const Path& x)
  1538. {
  1539. return x == entry->path;
  1540. });
  1541. curDependencies.erase(iterRemove, curDependencies.end());
  1542. }
  1543. }
  1544. void ProjectLibrary::reimportDependants(const Path& entryPath)
  1545. {
  1546. auto iterFind = mDependencies.find(entryPath);
  1547. if (iterFind == mDependencies.end())
  1548. return;
  1549. // Make a copy since we might modify this list during reimport
  1550. Vector<Path> dependencies = iterFind->second;
  1551. for (auto& dependency : dependencies)
  1552. {
  1553. LibraryEntry* entry = findEntry(dependency).get();
  1554. if (entry != nullptr && entry->type == LibraryEntryType::File)
  1555. {
  1556. FileEntry* resEntry = static_cast<FileEntry*>(entry);
  1557. SPtr<ImportOptions> importOptions;
  1558. if (resEntry->meta != nullptr)
  1559. importOptions = resEntry->meta->getImportOptions();
  1560. reimportResourceInternal(resEntry, importOptions, true);
  1561. }
  1562. }
  1563. }
  1564. BS_ED_EXPORT ProjectLibrary& gProjectLibrary()
  1565. {
  1566. return ProjectLibrary::instance();
  1567. }
  1568. }