BsProjectLibrary.cpp 56 KB

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