resource_uid.cpp 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266
  1. /*************************************************************************/
  2. /* resource_uid.cpp */
  3. /*************************************************************************/
  4. /* This file is part of: */
  5. /* GODOT ENGINE */
  6. /* https://godotengine.org */
  7. /*************************************************************************/
  8. /* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
  9. /* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
  10. /* */
  11. /* Permission is hereby granted, free of charge, to any person obtaining */
  12. /* a copy of this software and associated documentation files (the */
  13. /* "Software"), to deal in the Software without restriction, including */
  14. /* without limitation the rights to use, copy, modify, merge, publish, */
  15. /* distribute, sublicense, and/or sell copies of the Software, and to */
  16. /* permit persons to whom the Software is furnished to do so, subject to */
  17. /* the following conditions: */
  18. /* */
  19. /* The above copyright notice and this permission notice shall be */
  20. /* included in all copies or substantial portions of the Software. */
  21. /* */
  22. /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
  23. /* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
  24. /* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
  25. /* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
  26. /* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
  27. /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
  28. /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
  29. /*************************************************************************/
  30. #include "resource_uid.h"
  31. #include "core/config/project_settings.h"
  32. #include "core/crypto/crypto.h"
  33. #include "core/io/dir_access.h"
  34. #include "core/io/file_access.h"
  35. static constexpr uint32_t char_count = ('z' - 'a');
  36. static constexpr uint32_t base = char_count + ('9' - '0');
  37. String ResourceUID::get_cache_file() {
  38. return ProjectSettings::get_singleton()->get_project_data_path().plus_file("uid_cache.bin");
  39. }
  40. String ResourceUID::id_to_text(ID p_id) const {
  41. if (p_id < 0) {
  42. return "uid://<invalid>";
  43. }
  44. String txt;
  45. while (p_id) {
  46. uint32_t c = p_id % base;
  47. if (c < char_count) {
  48. txt = String::chr('a' + c) + txt;
  49. } else {
  50. txt = String::chr('0' + (c - char_count)) + txt;
  51. }
  52. p_id /= base;
  53. }
  54. return "uid://" + txt;
  55. }
  56. ResourceUID::ID ResourceUID::text_to_id(const String &p_text) const {
  57. if (!p_text.begins_with("uid://") || p_text == "uid://<invalid>") {
  58. return INVALID_ID;
  59. }
  60. uint32_t l = p_text.length();
  61. uint64_t uid = 0;
  62. for (uint32_t i = 6; i < l; i++) {
  63. uid *= base;
  64. uint32_t c = p_text[i];
  65. if (c >= 'a' && c <= 'z') {
  66. uid += c - 'a';
  67. } else if (c >= '0' && c <= '9') {
  68. uid += c - '0' + char_count;
  69. } else {
  70. return INVALID_ID;
  71. }
  72. }
  73. return ID(uid & 0x7FFFFFFFFFFFFFFF);
  74. }
  75. ResourceUID::ID ResourceUID::create_id() const {
  76. mutex.lock();
  77. if (crypto.is_null()) {
  78. crypto = Ref<Crypto>(Crypto::create());
  79. }
  80. mutex.unlock();
  81. while (true) {
  82. PackedByteArray bytes = crypto->generate_random_bytes(8);
  83. ERR_FAIL_COND_V(bytes.size() != 8, INVALID_ID);
  84. const uint64_t *ptr64 = (const uint64_t *)bytes.ptr();
  85. ID id = int64_t((*ptr64) & 0x7FFFFFFFFFFFFFFF);
  86. mutex.lock();
  87. bool exists = unique_ids.has(id);
  88. mutex.unlock();
  89. if (!exists) {
  90. return id;
  91. }
  92. }
  93. }
  94. bool ResourceUID::has_id(ID p_id) const {
  95. MutexLock l(mutex);
  96. return unique_ids.has(p_id);
  97. }
  98. void ResourceUID::add_id(ID p_id, const String &p_path) {
  99. MutexLock l(mutex);
  100. ERR_FAIL_COND(unique_ids.has(p_id));
  101. Cache c;
  102. c.cs = p_path.utf8();
  103. unique_ids[p_id] = c;
  104. changed = true;
  105. }
  106. void ResourceUID::set_id(ID p_id, const String &p_path) {
  107. MutexLock l(mutex);
  108. ERR_FAIL_COND(!unique_ids.has(p_id));
  109. CharString cs = p_path.utf8();
  110. if (strcmp(cs.ptr(), unique_ids[p_id].cs.ptr()) != 0) {
  111. unique_ids[p_id].cs = cs;
  112. unique_ids[p_id].saved_to_cache = false; //changed
  113. changed = true;
  114. }
  115. }
  116. String ResourceUID::get_id_path(ID p_id) const {
  117. MutexLock l(mutex);
  118. ERR_FAIL_COND_V(!unique_ids.has(p_id), String());
  119. const CharString &cs = unique_ids[p_id].cs;
  120. return String::utf8(cs.ptr());
  121. }
  122. void ResourceUID::remove_id(ID p_id) {
  123. MutexLock l(mutex);
  124. ERR_FAIL_COND(!unique_ids.has(p_id));
  125. unique_ids.erase(p_id);
  126. }
  127. Error ResourceUID::save_to_cache() {
  128. String cache_file = get_cache_file();
  129. if (!FileAccess::exists(cache_file)) {
  130. DirAccessRef d = DirAccess::create(DirAccess::ACCESS_RESOURCES);
  131. d->make_dir_recursive(String(cache_file).get_base_dir()); //ensure base dir exists
  132. }
  133. FileAccessRef f = FileAccess::open(cache_file, FileAccess::WRITE);
  134. if (!f) {
  135. return ERR_CANT_OPEN;
  136. }
  137. MutexLock l(mutex);
  138. f->store_32(unique_ids.size());
  139. cache_entries = 0;
  140. for (OrderedHashMap<ID, Cache>::Element E = unique_ids.front(); E; E = E.next()) {
  141. f->store_64(E.key());
  142. uint32_t s = E.get().cs.length();
  143. f->store_32(s);
  144. f->store_buffer((const uint8_t *)E.get().cs.ptr(), s);
  145. E.get().saved_to_cache = true;
  146. cache_entries++;
  147. }
  148. changed = false;
  149. return OK;
  150. }
  151. Error ResourceUID::load_from_cache() {
  152. FileAccessRef f = FileAccess::open(get_cache_file(), FileAccess::READ);
  153. if (!f) {
  154. return ERR_CANT_OPEN;
  155. }
  156. MutexLock l(mutex);
  157. unique_ids.clear();
  158. uint32_t entry_count = f->get_32();
  159. for (uint32_t i = 0; i < entry_count; i++) {
  160. int64_t id = f->get_64();
  161. int32_t len = f->get_32();
  162. Cache c;
  163. c.cs.resize(len + 1);
  164. ERR_FAIL_COND_V(c.cs.size() != len + 1, ERR_FILE_CORRUPT); // out of memory
  165. c.cs[len] = 0;
  166. int32_t rl = f->get_buffer((uint8_t *)c.cs.ptrw(), len);
  167. ERR_FAIL_COND_V(rl != len, ERR_FILE_CORRUPT);
  168. c.saved_to_cache = true;
  169. unique_ids[id] = c;
  170. }
  171. cache_entries = entry_count;
  172. changed = false;
  173. return OK;
  174. }
  175. Error ResourceUID::update_cache() {
  176. if (!changed) {
  177. return OK;
  178. }
  179. if (cache_entries == 0) {
  180. return save_to_cache();
  181. }
  182. MutexLock l(mutex);
  183. FileAccess *f = nullptr;
  184. for (OrderedHashMap<ID, Cache>::Element E = unique_ids.front(); E; E = E.next()) {
  185. if (!E.get().saved_to_cache) {
  186. if (f == nullptr) {
  187. f = FileAccess::open(get_cache_file(), FileAccess::READ_WRITE); //append
  188. if (!f) {
  189. return ERR_CANT_OPEN;
  190. }
  191. f->seek_end();
  192. }
  193. f->store_64(E.key());
  194. uint32_t s = E.get().cs.length();
  195. f->store_32(s);
  196. f->store_buffer((const uint8_t *)E.get().cs.ptr(), s);
  197. E.get().saved_to_cache = true;
  198. cache_entries++;
  199. }
  200. }
  201. if (f != nullptr) {
  202. f->seek(0);
  203. f->store_32(cache_entries); //update amount of entries
  204. f->close();
  205. memdelete(f);
  206. }
  207. changed = false;
  208. return OK;
  209. }
  210. void ResourceUID::clear() {
  211. cache_entries = 0;
  212. unique_ids.clear();
  213. changed = false;
  214. }
  215. void ResourceUID::_bind_methods() {
  216. ClassDB::bind_method(D_METHOD("id_to_text", "id"), &ResourceUID::id_to_text);
  217. ClassDB::bind_method(D_METHOD("text_to_id", "text_id"), &ResourceUID::text_to_id);
  218. ClassDB::bind_method(D_METHOD("create_id"), &ResourceUID::create_id);
  219. ClassDB::bind_method(D_METHOD("has_id", "id"), &ResourceUID::has_id);
  220. ClassDB::bind_method(D_METHOD("add_id", "id", "path"), &ResourceUID::add_id);
  221. ClassDB::bind_method(D_METHOD("set_id", "id", "path"), &ResourceUID::set_id);
  222. ClassDB::bind_method(D_METHOD("get_id_path", "id"), &ResourceUID::get_id_path);
  223. ClassDB::bind_method(D_METHOD("remove_id", "id"), &ResourceUID::remove_id);
  224. BIND_CONSTANT(INVALID_ID)
  225. }
  226. ResourceUID *ResourceUID::singleton = nullptr;
  227. ResourceUID::ResourceUID() {
  228. ERR_FAIL_COND(singleton != nullptr);
  229. singleton = this;
  230. }
  231. ResourceUID::~ResourceUID() {
  232. }