AssetDatabase.cpp 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498
  1. #include <Poco/MD5Engine.h>
  2. #include <Atomic/IO/Log.h>
  3. #include <Atomic/IO/File.h>
  4. #include <Atomic/IO/FileSystem.h>
  5. #include <Atomic/Math/Random.h>
  6. #include <Atomic/Resource/ResourceEvents.h>
  7. #include <Atomic/Resource/ResourceCache.h>
  8. #include "../ToolEvents.h"
  9. #include "../ToolSystem.h"
  10. #include "../Project/Project.h"
  11. #include "AssetEvents.h"
  12. #include "AssetDatabase.h"
  13. namespace ToolCore
  14. {
  15. AssetDatabase::AssetDatabase(Context* context) : Object(context)
  16. {
  17. SubscribeToEvent(E_PROJECTLOADED, HANDLER(AssetDatabase, HandleProjectLoaded));
  18. SubscribeToEvent(E_PROJECTUNLOADED, HANDLER(AssetDatabase, HandleProjectUnloaded));
  19. }
  20. AssetDatabase::~AssetDatabase()
  21. {
  22. }
  23. String AssetDatabase::GetCachePath()
  24. {
  25. if (project_.Null())
  26. return String::EMPTY;
  27. return project_->GetProjectPath() + "Cache/";
  28. }
  29. String AssetDatabase::GenerateAssetGUID()
  30. {
  31. Time* time = GetSubsystem<Time>();
  32. while (true)
  33. {
  34. Poco::MD5Engine md5;
  35. PODVector<unsigned> data;
  36. for (unsigned i = 0; i < 16; i++)
  37. {
  38. data.Push(time->GetTimeSinceEpoch() + Rand());
  39. }
  40. md5.update(&data[0], data.Size() * sizeof(unsigned));
  41. String guid = Poco::MD5Engine::digestToHex(md5.digest()).c_str();
  42. if (!usedGUID_.Contains(guid))
  43. {
  44. RegisterGUID(guid);
  45. return guid;
  46. }
  47. }
  48. assert(0);
  49. return "";
  50. }
  51. void AssetDatabase::RegisterGUID(const String& guid)
  52. {
  53. if (usedGUID_.Contains(guid))
  54. {
  55. assert(0);
  56. }
  57. usedGUID_.Push(guid);
  58. }
  59. void AssetDatabase::Import(const String& path)
  60. {
  61. FileSystem* fs = GetSubsystem<FileSystem>();
  62. // nothing for now
  63. if (fs->DirExists(path))
  64. return;
  65. }
  66. Asset* AssetDatabase::GetAssetByCachePath(const String& cachePath)
  67. {
  68. List<SharedPtr<Asset>>::ConstIterator itr = assets_.Begin();
  69. String cacheFilename = GetFileName(cachePath);
  70. while (itr != assets_.End())
  71. {
  72. if ((*itr)->GetCachePath().Contains(cacheFilename))
  73. return *itr;
  74. itr++;
  75. }
  76. return 0;
  77. }
  78. Asset* AssetDatabase::GetAssetByGUID(const String& guid)
  79. {
  80. List<SharedPtr<Asset>>::ConstIterator itr = assets_.Begin();
  81. while (itr != assets_.End())
  82. {
  83. if (guid == (*itr)->GetGUID())
  84. return *itr;
  85. itr++;
  86. }
  87. return 0;
  88. }
  89. Asset* AssetDatabase::GetAssetByPath(const String& path)
  90. {
  91. List<SharedPtr<Asset>>::ConstIterator itr = assets_.Begin();
  92. while (itr != assets_.End())
  93. {
  94. if (path == (*itr)->GetPath())
  95. return *itr;
  96. itr++;
  97. }
  98. return 0;
  99. }
  100. void AssetDatabase::PruneOrphanedDotAssetFiles()
  101. {
  102. if (project_.Null())
  103. {
  104. LOGDEBUG("AssetDatabase::PruneOrphanedDotAssetFiles - called without project loaded");
  105. return;
  106. }
  107. FileSystem* fs = GetSubsystem<FileSystem>();
  108. const String& resourcePath = project_->GetResourcePath();
  109. Vector<String> allResults;
  110. fs->ScanDir(allResults, resourcePath, "*.asset", SCAN_FILES, true);
  111. for (unsigned i = 0; i < allResults.Size(); i++)
  112. {
  113. String dotAssetFilename = resourcePath + allResults[i];
  114. String assetFilename = ReplaceExtension(dotAssetFilename, "");
  115. // remove orphaned asset files
  116. if (!fs->FileExists(assetFilename) && !fs->DirExists(assetFilename))
  117. {
  118. LOGINFOF("Removing orphaned asset file: %s", dotAssetFilename.CString());
  119. fs->Delete(dotAssetFilename);
  120. }
  121. }
  122. }
  123. String AssetDatabase::GetDotAssetFilename(const String& path)
  124. {
  125. FileSystem* fs = GetSubsystem<FileSystem>();
  126. String assetFilename = path + ".asset";
  127. if (fs->DirExists(path)) {
  128. assetFilename = RemoveTrailingSlash(path) + ".asset";
  129. }
  130. return assetFilename;
  131. }
  132. void AssetDatabase::AddAsset(SharedPtr<Asset>& asset)
  133. {
  134. assert(asset->GetGUID().Length());
  135. assert(!GetAssetByGUID(asset->GetGUID()));
  136. assets_.Push(asset);
  137. // set to the current timestamp
  138. asset->UpdateFileTimestamp();
  139. VariantMap eventData;
  140. eventData[ResourceAdded::P_GUID] = asset->GetGUID();
  141. SendEvent(E_RESOURCEADDED, eventData);
  142. }
  143. void AssetDatabase::DeleteAsset(Asset* asset)
  144. {
  145. SharedPtr<Asset> assetPtr(asset);
  146. List<SharedPtr<Asset>>::Iterator itr = assets_.Find(assetPtr);
  147. if (itr == assets_.End())
  148. return;
  149. assets_.Erase(itr);
  150. const String& resourcePath = asset->GetPath();
  151. FileSystem* fs = GetSubsystem<FileSystem>();
  152. if (fs->DirExists(resourcePath))
  153. {
  154. fs->RemoveDir(resourcePath, true);
  155. }
  156. else if (fs->FileExists(resourcePath))
  157. {
  158. fs->Delete(resourcePath);
  159. }
  160. String dotAsset = resourcePath + ".asset";
  161. if (fs->FileExists(dotAsset))
  162. {
  163. fs->Delete(dotAsset);
  164. }
  165. VariantMap eventData;
  166. eventData[ResourceRemoved::P_GUID] = asset->GetGUID();
  167. SendEvent(E_RESOURCEREMOVED, eventData);
  168. }
  169. bool AssetDatabase::ImportDirtyAssets()
  170. {
  171. PODVector<Asset*> assets;
  172. GetDirtyAssets(assets);
  173. for (unsigned i = 0; i < assets.Size(); i++)
  174. {
  175. assets[i]->Import();
  176. assets[i]->Save();
  177. assets[i]->dirty_ = false;
  178. assets[i]->UpdateFileTimestamp();
  179. }
  180. return assets.Size() != 0;
  181. }
  182. void AssetDatabase::PreloadAssets()
  183. {
  184. List<SharedPtr<Asset>>::ConstIterator itr = assets_.Begin();
  185. while (itr != assets_.End())
  186. {
  187. (*itr)->Preload();
  188. itr++;
  189. }
  190. }
  191. void AssetDatabase::Scan()
  192. {
  193. PruneOrphanedDotAssetFiles();
  194. FileSystem* fs = GetSubsystem<FileSystem>();
  195. const String& resourcePath = project_->GetResourcePath();
  196. Vector<String> allResults;
  197. fs->ScanDir(allResults, resourcePath, "", SCAN_FILES | SCAN_DIRS, true);
  198. Vector<String> filteredResults;
  199. filteredResults.Push(RemoveTrailingSlash(resourcePath));
  200. for (unsigned i = 0; i < allResults.Size(); i++)
  201. {
  202. allResults[i] = resourcePath + allResults[i];
  203. const String& path = allResults[i];
  204. if (path.StartsWith(".") || path.EndsWith("."))
  205. continue;
  206. String ext = GetExtension(path);
  207. if (ext == ".asset")
  208. continue;
  209. filteredResults.Push(path);
  210. }
  211. for (unsigned i = 0; i < filteredResults.Size(); i++)
  212. {
  213. const String& path = filteredResults[i];
  214. String dotAssetFilename = GetDotAssetFilename(path);
  215. if (!fs->FileExists(dotAssetFilename))
  216. {
  217. // new asset
  218. SharedPtr<Asset> asset(new Asset(context_));
  219. if (asset->SetPath(path))
  220. AddAsset(asset);
  221. }
  222. else
  223. {
  224. SharedPtr<File> file(new File(context_, dotAssetFilename));
  225. SharedPtr<JSONFile> json(new JSONFile(context_));
  226. json->Load(*file);
  227. file->Close();
  228. JSONValue root = json->GetRoot();
  229. assert(root.GetInt("version") == ASSET_VERSION);
  230. String guid = root.GetString("guid");
  231. if (!GetAssetByGUID(guid))
  232. {
  233. SharedPtr<Asset> asset(new Asset(context_));
  234. asset->SetPath(path);
  235. AddAsset(asset);
  236. }
  237. }
  238. }
  239. PreloadAssets();
  240. if (ImportDirtyAssets())
  241. Scan();
  242. }
  243. void AssetDatabase::GetFolderAssets(String folder, PODVector<Asset*>& assets) const
  244. {
  245. if (project_.Null())
  246. return;
  247. List<SharedPtr<Asset>>::ConstIterator itr = assets_.Begin();
  248. if (!folder.Length())
  249. {
  250. folder = project_->GetResourcePath();
  251. }
  252. folder = AddTrailingSlash(folder);
  253. while (itr != assets_.End())
  254. {
  255. String path = GetPath((*itr)->GetPath());
  256. if (path == folder)
  257. assets.Push(*itr);
  258. itr++;
  259. }
  260. }
  261. void AssetDatabase::GetAssetsByImporterType(StringHash type, PODVector<Asset*>& assets) const
  262. {
  263. assets.Clear();
  264. List<SharedPtr<Asset>>::ConstIterator itr = assets_.Begin();
  265. while (itr != assets_.End())
  266. {
  267. Asset* asset = *itr;
  268. if (asset->GetImporterType() == type)
  269. assets.Push(asset);
  270. itr++;
  271. }
  272. }
  273. void AssetDatabase::GetDirtyAssets(PODVector<Asset*>& assets)
  274. {
  275. assets.Clear();
  276. List<SharedPtr<Asset>>::ConstIterator itr = assets_.Begin();
  277. while (itr != assets_.End())
  278. {
  279. if ((*itr)->IsDirty())
  280. assets.Push(*itr);
  281. itr++;
  282. }
  283. }
  284. void AssetDatabase::HandleProjectLoaded(StringHash eventType, VariantMap& eventData)
  285. {
  286. project_ = GetSubsystem<ToolSystem>()->GetProject();
  287. FileSystem* fs = GetSubsystem<FileSystem>();
  288. if (!fs->DirExists(GetCachePath()))
  289. fs->CreateDir(GetCachePath());
  290. ResourceCache* cache = GetSubsystem<ResourceCache>();
  291. cache->AddResourceDir(GetCachePath());
  292. Scan();
  293. SubscribeToEvent(E_FILECHANGED, HANDLER(AssetDatabase, HandleFileChanged));
  294. }
  295. void AssetDatabase::HandleProjectUnloaded(StringHash eventType, VariantMap& eventData)
  296. {
  297. ResourceCache* cache = GetSubsystem<ResourceCache>();
  298. cache->RemoveResourceDir(GetCachePath());
  299. assets_.Clear();
  300. usedGUID_.Clear();
  301. project_ = 0;
  302. UnsubscribeFromEvent(E_FILECHANGED);
  303. }
  304. void AssetDatabase::HandleFileChanged(StringHash eventType, VariantMap& eventData)
  305. {
  306. using namespace FileChanged;
  307. const String& fullPath = eventData[P_FILENAME].GetString();
  308. FileSystem* fs = GetSubsystem<FileSystem>();
  309. String pathName, fileName, ext;
  310. SplitPath(fullPath, pathName, fileName, ext);
  311. // ignore changes in the Cache resource dir
  312. if (fullPath == GetCachePath() || pathName.StartsWith(GetCachePath()))
  313. return;
  314. // don't care about directories and asset file changes
  315. if (fs->DirExists(fullPath) || ext == ".asset")
  316. return;
  317. Asset* asset = GetAssetByPath(fullPath);
  318. if (!asset && fs->FileExists(fullPath))
  319. {
  320. Scan();
  321. return;
  322. }
  323. if (asset)
  324. {
  325. if(!fs->Exists(fullPath))
  326. {
  327. DeleteAsset(asset);
  328. }
  329. else
  330. {
  331. if (asset->GetFileTimestamp() != fs->GetLastModifiedTime(asset->GetPath()))
  332. {
  333. asset->SetDirty(true);
  334. Scan();
  335. }
  336. }
  337. }
  338. }
  339. String AssetDatabase::GetResourceImporterName(const String& resourceTypeName)
  340. {
  341. // TODO: have resource type register themselves
  342. if (resourceTypeToImporterType_.Empty())
  343. {
  344. resourceTypeToImporterType_["Sound"] = "AudioImporter";
  345. resourceTypeToImporterType_["Model"] = "ModelImporter";
  346. }
  347. if (!resourceTypeToImporterType_.Contains(resourceTypeName))
  348. return String::EMPTY;
  349. return resourceTypeToImporterType_[resourceTypeName];
  350. }
  351. }