resource_importer.cpp 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602
  1. /**************************************************************************/
  2. /* resource_importer.cpp */
  3. /**************************************************************************/
  4. /* This file is part of: */
  5. /* GODOT ENGINE */
  6. /* https://godotengine.org */
  7. /**************************************************************************/
  8. /* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
  9. /* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
  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_importer.h"
  31. #include "core/config/project_settings.h"
  32. #include "core/io/config_file.h"
  33. #include "core/io/image.h"
  34. #include "core/os/os.h"
  35. #include "core/variant/variant_parser.h"
  36. bool ResourceFormatImporter::SortImporterByName::operator()(const Ref<ResourceImporter> &p_a, const Ref<ResourceImporter> &p_b) const {
  37. return p_a->get_importer_name() < p_b->get_importer_name();
  38. }
  39. Error ResourceFormatImporter::_get_path_and_type(const String &p_path, PathAndType &r_path_and_type, bool p_load, bool *r_valid) const {
  40. Error err;
  41. Ref<FileAccess> f = FileAccess::open(p_path + ".import", FileAccess::READ, &err);
  42. if (f.is_null()) {
  43. if (r_valid) {
  44. *r_valid = false;
  45. }
  46. return err;
  47. }
  48. VariantParser::StreamFile stream;
  49. stream.f = f;
  50. String assign;
  51. Variant value;
  52. VariantParser::Tag next_tag;
  53. if (r_valid) {
  54. *r_valid = true;
  55. }
  56. int lines = 0;
  57. String error_text;
  58. bool path_found = false; // First match must have priority.
  59. String decomp_path;
  60. bool decomp_path_found = false;
  61. while (true) {
  62. assign = Variant();
  63. next_tag.fields.clear();
  64. next_tag.name = String();
  65. err = VariantParser::parse_tag_assign_eof(&stream, lines, error_text, next_tag, assign, value, nullptr, true);
  66. if (err == ERR_FILE_EOF) {
  67. if (p_load && !path_found && decomp_path_found) {
  68. print_verbose(vformat("No natively supported texture format found for %s, using decompressable format %s.", p_path, decomp_path));
  69. r_path_and_type.path = decomp_path;
  70. }
  71. return OK;
  72. } else if (err != OK) {
  73. ERR_PRINT(vformat("ResourceFormatImporter::load - %s.import:%d error: %s.", p_path, lines, error_text));
  74. return err;
  75. }
  76. if (!assign.is_empty()) {
  77. if (!path_found && assign.begins_with("path.") && r_path_and_type.path.is_empty()) {
  78. String feature = assign.get_slicec('.', 1);
  79. if (OS::get_singleton()->has_feature(feature)) {
  80. r_path_and_type.path = value;
  81. path_found = true; // First match must have priority.
  82. } else if (p_load && Image::can_decompress(feature) && !decomp_path_found) { // When loading, check for decompressable formats and use first one found if nothing else is supported.
  83. decomp_path = value;
  84. decomp_path_found = true; // First match must have priority.
  85. }
  86. } else if (!path_found && assign == "path") {
  87. r_path_and_type.path = value;
  88. path_found = true; // First match must have priority.
  89. } else if (assign == "type") {
  90. r_path_and_type.type = ClassDB::get_compatibility_remapped_class(value);
  91. } else if (assign == "importer") {
  92. r_path_and_type.importer = value;
  93. } else if (assign == "uid") {
  94. r_path_and_type.uid = ResourceUID::get_singleton()->text_to_id(value);
  95. } else if (assign == "group_file") {
  96. r_path_and_type.group_file = value;
  97. } else if (assign == "metadata") {
  98. r_path_and_type.metadata = value;
  99. } else if (assign == "valid") {
  100. if (r_valid) {
  101. *r_valid = value;
  102. }
  103. }
  104. } else if (next_tag.name != "remap") {
  105. break;
  106. }
  107. }
  108. if (p_load && !path_found && decomp_path_found) {
  109. print_verbose(vformat("No natively supported texture format found for %s, using decompressable format %s.", p_path, decomp_path));
  110. r_path_and_type.path = decomp_path;
  111. return OK;
  112. }
  113. #ifdef TOOLS_ENABLED
  114. if (r_path_and_type.metadata && !r_path_and_type.path.is_empty()) {
  115. Dictionary meta = r_path_and_type.metadata;
  116. if (meta.has("has_editor_variant")) {
  117. r_path_and_type.path = r_path_and_type.path.get_basename() + ".editor." + r_path_and_type.path.get_extension();
  118. }
  119. }
  120. #endif
  121. if (r_path_and_type.type.is_empty()) {
  122. return ERR_FILE_CORRUPT;
  123. }
  124. if (r_path_and_type.path.is_empty()) {
  125. // Some importers may not write files to the .godot folder, so the path can be empty.
  126. if (r_path_and_type.importer.is_empty()) {
  127. return ERR_FILE_CORRUPT;
  128. }
  129. // It's only invalid if the extension for the importer is not empty.
  130. Ref<ResourceImporter> importer = get_importer_by_name(r_path_and_type.importer);
  131. if (importer.is_null() || !importer->get_save_extension().is_empty()) {
  132. return ERR_FILE_CORRUPT;
  133. }
  134. }
  135. return OK;
  136. }
  137. Ref<Resource> ResourceFormatImporter::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress, CacheMode p_cache_mode) {
  138. #ifdef TOOLS_ENABLED
  139. // When loading a resource on startup, we use the load_on_startup callback,
  140. // which executes the loading in the EditorFileSystem. It can reimport
  141. // the resource and retry the load, allowing the resource to be loaded
  142. // even if it is not yet imported.
  143. if (ResourceImporter::load_on_startup != nullptr) {
  144. return ResourceImporter::load_on_startup(this, p_path, r_error, p_use_sub_threads, r_progress, p_cache_mode);
  145. }
  146. #endif
  147. return load_internal(p_path, r_error, p_use_sub_threads, r_progress, p_cache_mode, false);
  148. }
  149. Ref<Resource> ResourceFormatImporter::load_internal(const String &p_path, Error *r_error, bool p_use_sub_threads, float *r_progress, CacheMode p_cache_mode, bool p_silence_errors) {
  150. PathAndType pat;
  151. Error err = _get_path_and_type(p_path, pat, true);
  152. if (err != OK) {
  153. if (r_error) {
  154. *r_error = err;
  155. }
  156. return Ref<Resource>();
  157. }
  158. if (p_silence_errors) {
  159. // Note: Some importers do not create files in the .godot folder, so we need to check if the path is empty.
  160. if (!pat.path.is_empty() && !FileAccess::exists(pat.path)) {
  161. return Ref<Resource>();
  162. }
  163. }
  164. Ref<Resource> res = ResourceLoader::_load(pat.path, p_path, pat.type, p_cache_mode, r_error, p_use_sub_threads, r_progress);
  165. #ifdef TOOLS_ENABLED
  166. if (res.is_valid()) {
  167. res->set_import_last_modified_time(res->get_last_modified_time()); //pass this, if used
  168. res->set_import_path(pat.path);
  169. }
  170. #endif
  171. return res;
  172. }
  173. void ResourceFormatImporter::get_recognized_extensions(List<String> *p_extensions) const {
  174. HashSet<String> found;
  175. for (int i = 0; i < importers.size(); i++) {
  176. List<String> local_exts;
  177. importers[i]->get_recognized_extensions(&local_exts);
  178. for (const String &F : local_exts) {
  179. if (!found.has(F)) {
  180. p_extensions->push_back(F);
  181. found.insert(F);
  182. }
  183. }
  184. }
  185. }
  186. void ResourceFormatImporter::get_recognized_extensions_for_type(const String &p_type, List<String> *p_extensions) const {
  187. if (p_type.is_empty()) {
  188. get_recognized_extensions(p_extensions);
  189. return;
  190. }
  191. HashSet<String> found;
  192. for (int i = 0; i < importers.size(); i++) {
  193. String res_type = importers[i]->get_resource_type();
  194. if (res_type.is_empty()) {
  195. continue;
  196. }
  197. if (!ClassDB::is_parent_class(res_type, p_type)) {
  198. continue;
  199. }
  200. List<String> local_exts;
  201. importers[i]->get_recognized_extensions(&local_exts);
  202. for (const String &F : local_exts) {
  203. if (!found.has(F)) {
  204. p_extensions->push_back(F);
  205. found.insert(F);
  206. }
  207. }
  208. }
  209. }
  210. bool ResourceFormatImporter::exists(const String &p_path) const {
  211. return FileAccess::exists(p_path + ".import");
  212. }
  213. bool ResourceFormatImporter::recognize_path(const String &p_path, const String &p_for_type) const {
  214. return FileAccess::exists(p_path + ".import");
  215. }
  216. Error ResourceFormatImporter::get_import_order_threads_and_importer(const String &p_path, int &r_order, bool &r_can_threads, String &r_importer) const {
  217. r_order = 0;
  218. r_importer = "";
  219. r_can_threads = false;
  220. Ref<ResourceImporter> importer;
  221. if (FileAccess::exists(p_path + ".import")) {
  222. PathAndType pat;
  223. Error err = _get_path_and_type(p_path, pat, false);
  224. if (err == OK) {
  225. importer = get_importer_by_name(pat.importer);
  226. }
  227. } else {
  228. importer = get_importer_by_file(p_path);
  229. }
  230. if (importer.is_valid()) {
  231. r_order = importer->get_import_order();
  232. r_importer = importer->get_importer_name();
  233. r_can_threads = importer->can_import_threaded();
  234. return OK;
  235. } else {
  236. return ERR_INVALID_PARAMETER;
  237. }
  238. }
  239. int ResourceFormatImporter::get_import_order(const String &p_path) const {
  240. Ref<ResourceImporter> importer;
  241. if (FileAccess::exists(p_path + ".import")) {
  242. PathAndType pat;
  243. Error err = _get_path_and_type(p_path, pat, false);
  244. if (err == OK) {
  245. importer = get_importer_by_name(pat.importer);
  246. }
  247. } else {
  248. importer = get_importer_by_file(p_path);
  249. }
  250. if (importer.is_valid()) {
  251. return importer->get_import_order();
  252. }
  253. return 0;
  254. }
  255. bool ResourceFormatImporter::handles_type(const String &p_type) const {
  256. for (int i = 0; i < importers.size(); i++) {
  257. String res_type = importers[i]->get_resource_type();
  258. if (res_type.is_empty()) {
  259. continue;
  260. }
  261. if (ClassDB::is_parent_class(res_type, p_type)) {
  262. return true;
  263. }
  264. }
  265. return true;
  266. }
  267. String ResourceFormatImporter::get_internal_resource_path(const String &p_path) const {
  268. PathAndType pat;
  269. Error err = _get_path_and_type(p_path, pat, false);
  270. if (err != OK) {
  271. return String();
  272. }
  273. return pat.path;
  274. }
  275. void ResourceFormatImporter::get_internal_resource_path_list(const String &p_path, List<String> *r_paths) {
  276. Error err;
  277. Ref<FileAccess> f = FileAccess::open(p_path + ".import", FileAccess::READ, &err);
  278. if (f.is_null()) {
  279. return;
  280. }
  281. VariantParser::StreamFile stream;
  282. stream.f = f;
  283. String assign;
  284. Variant value;
  285. VariantParser::Tag next_tag;
  286. int lines = 0;
  287. String error_text;
  288. while (true) {
  289. assign = Variant();
  290. next_tag.fields.clear();
  291. next_tag.name = String();
  292. err = VariantParser::parse_tag_assign_eof(&stream, lines, error_text, next_tag, assign, value, nullptr, true);
  293. if (err == ERR_FILE_EOF) {
  294. return;
  295. } else if (err != OK) {
  296. ERR_PRINT(vformat("ResourceFormatImporter::get_internal_resource_path_list - %s.import:%d error: %s.", p_path, lines, error_text));
  297. return;
  298. }
  299. if (!assign.is_empty()) {
  300. if (assign.begins_with("path.")) {
  301. r_paths->push_back(value);
  302. } else if (assign == "path") {
  303. r_paths->push_back(value);
  304. }
  305. } else if (next_tag.name != "remap") {
  306. break;
  307. }
  308. }
  309. }
  310. String ResourceFormatImporter::get_import_group_file(const String &p_path) const {
  311. bool valid = true;
  312. PathAndType pat;
  313. _get_path_and_type(p_path, pat, false, &valid);
  314. return valid ? pat.group_file : String();
  315. }
  316. bool ResourceFormatImporter::is_import_valid(const String &p_path) const {
  317. bool valid = true;
  318. PathAndType pat;
  319. _get_path_and_type(p_path, pat, false, &valid);
  320. return valid;
  321. }
  322. String ResourceFormatImporter::get_resource_type(const String &p_path) const {
  323. PathAndType pat;
  324. Error err = _get_path_and_type(p_path, pat, false);
  325. if (err != OK) {
  326. return "";
  327. }
  328. return pat.type;
  329. }
  330. ResourceUID::ID ResourceFormatImporter::get_resource_uid(const String &p_path) const {
  331. PathAndType pat;
  332. Error err = _get_path_and_type(p_path, pat, false);
  333. if (err != OK) {
  334. return ResourceUID::INVALID_ID;
  335. }
  336. return pat.uid;
  337. }
  338. bool ResourceFormatImporter::has_custom_uid_support() const {
  339. return true;
  340. }
  341. Error ResourceFormatImporter::get_resource_import_info(const String &p_path, StringName &r_type, ResourceUID::ID &r_uid, String &r_import_group_file) const {
  342. PathAndType pat;
  343. Error err = _get_path_and_type(p_path, pat, false);
  344. if (err == OK) {
  345. r_type = pat.type;
  346. r_uid = pat.uid;
  347. r_import_group_file = pat.group_file;
  348. } else {
  349. r_type = "";
  350. r_uid = ResourceUID::INVALID_ID;
  351. r_import_group_file = "";
  352. }
  353. return err;
  354. }
  355. Variant ResourceFormatImporter::get_resource_metadata(const String &p_path) const {
  356. PathAndType pat;
  357. Error err = _get_path_and_type(p_path, pat, false);
  358. if (err != OK) {
  359. return Variant();
  360. }
  361. return pat.metadata;
  362. }
  363. void ResourceFormatImporter::get_classes_used(const String &p_path, HashSet<StringName> *r_classes) {
  364. PathAndType pat;
  365. Error err = _get_path_and_type(p_path, pat, false);
  366. if (err != OK) {
  367. return;
  368. }
  369. ResourceLoader::get_classes_used(pat.path, r_classes);
  370. }
  371. void ResourceFormatImporter::get_dependencies(const String &p_path, List<String> *p_dependencies, bool p_add_types) {
  372. PathAndType pat;
  373. Error err = _get_path_and_type(p_path, pat, false);
  374. if (err != OK) {
  375. return;
  376. }
  377. ResourceLoader::get_dependencies(pat.path, p_dependencies, p_add_types);
  378. }
  379. void ResourceFormatImporter::get_build_dependencies(const String &p_path, HashSet<String> *r_dependencies) {
  380. if (!exists(p_path)) {
  381. return;
  382. }
  383. List<Ref<ResourceImporter>> valid_importers;
  384. get_importers_for_file(p_path, &valid_importers);
  385. for (Ref<ResourceImporter> importer : valid_importers) {
  386. importer->get_build_dependencies(p_path, r_dependencies);
  387. }
  388. }
  389. Ref<ResourceImporter> ResourceFormatImporter::get_importer_by_name(const String &p_name) const {
  390. for (int i = 0; i < importers.size(); i++) {
  391. if (importers[i]->get_importer_name() == p_name) {
  392. return importers[i];
  393. }
  394. }
  395. return Ref<ResourceImporter>();
  396. }
  397. void ResourceFormatImporter::add_importer(const Ref<ResourceImporter> &p_importer, bool p_first_priority) {
  398. ERR_FAIL_COND(p_importer.is_null());
  399. if (p_first_priority) {
  400. importers.insert(0, p_importer);
  401. } else {
  402. importers.push_back(p_importer);
  403. }
  404. }
  405. void ResourceFormatImporter::get_importers_for_file(const String &p_file, List<Ref<ResourceImporter>> *r_importers) {
  406. for (int i = 0; i < importers.size(); i++) {
  407. List<String> local_exts;
  408. importers[i]->get_recognized_extensions(&local_exts);
  409. for (const String &F : local_exts) {
  410. if (p_file.right(F.length()).nocasecmp_to(F) == 0) {
  411. r_importers->push_back(importers[i]);
  412. break;
  413. }
  414. }
  415. }
  416. }
  417. void ResourceFormatImporter::get_importers(List<Ref<ResourceImporter>> *r_importers) {
  418. for (int i = 0; i < importers.size(); i++) {
  419. r_importers->push_back(importers[i]);
  420. }
  421. }
  422. Ref<ResourceImporter> ResourceFormatImporter::get_importer_by_file(const String &p_file) const {
  423. Ref<ResourceImporter> importer;
  424. float priority = 0;
  425. for (int i = 0; i < importers.size(); i++) {
  426. List<String> local_exts;
  427. importers[i]->get_recognized_extensions(&local_exts);
  428. for (const String &F : local_exts) {
  429. if (p_file.right(F.length()).nocasecmp_to(F) == 0 && importers[i]->get_priority() > priority) {
  430. importer = importers[i];
  431. priority = importers[i]->get_priority();
  432. break;
  433. }
  434. }
  435. }
  436. return importer;
  437. }
  438. String ResourceFormatImporter::get_import_base_path(const String &p_for_file) const {
  439. return ProjectSettings::get_singleton()->get_imported_files_path().path_join(p_for_file.get_file() + "-" + p_for_file.md5_text());
  440. }
  441. bool ResourceFormatImporter::are_import_settings_valid(const String &p_path) const {
  442. bool valid = true;
  443. PathAndType pat;
  444. _get_path_and_type(p_path, pat, false, &valid);
  445. if (!valid) {
  446. return false;
  447. }
  448. for (int i = 0; i < importers.size(); i++) {
  449. if (importers[i]->get_importer_name() == pat.importer) {
  450. if (!importers[i]->are_import_settings_valid(p_path, pat.metadata)) { //importer thinks this is not valid
  451. return false;
  452. }
  453. }
  454. }
  455. return true;
  456. }
  457. String ResourceFormatImporter::get_import_settings_hash() const {
  458. Vector<Ref<ResourceImporter>> sorted_importers = importers;
  459. sorted_importers.sort_custom<SortImporterByName>();
  460. String hash;
  461. for (int i = 0; i < sorted_importers.size(); i++) {
  462. hash += ":" + sorted_importers[i]->get_importer_name() + ":" + sorted_importers[i]->get_import_settings_string();
  463. }
  464. return hash.md5_text();
  465. }
  466. ResourceFormatImporter::ResourceFormatImporter() {
  467. singleton = this;
  468. }
  469. //////////////
  470. void ResourceImporter::get_build_dependencies(const String &p_path, HashSet<String> *r_dependencies) {
  471. Vector<String> ret;
  472. if (GDVIRTUAL_CALL(_get_build_dependencies, p_path, ret)) {
  473. for (int i = 0; i < ret.size(); i++) {
  474. r_dependencies->insert(ret[i]);
  475. }
  476. return;
  477. }
  478. }
  479. void ResourceImporter::_bind_methods() {
  480. BIND_ENUM_CONSTANT(IMPORT_ORDER_DEFAULT);
  481. BIND_ENUM_CONSTANT(IMPORT_ORDER_SCENE);
  482. GDVIRTUAL_BIND(_get_build_dependencies, "path");
  483. }
  484. /////
  485. Error ResourceFormatImporterSaver::set_uid(const String &p_path, ResourceUID::ID p_uid) {
  486. Ref<ConfigFile> cf;
  487. cf.instantiate();
  488. Error err = cf->load(p_path + ".import");
  489. if (err != OK) {
  490. return err;
  491. }
  492. cf->set_value("remap", "uid", ResourceUID::get_singleton()->id_to_text(p_uid));
  493. cf->save(p_path + ".import");
  494. return OK;
  495. }