AssetDatabase.cpp 9.3 KB

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