From e05d5b8813c8e9ac873b68653a0dadb234050aed Mon Sep 17 00:00:00 2001 From: Juan Date: Thu, 5 Sep 2024 08:52:37 +0200 Subject: [PATCH] Provide a reliable way to see original resources in a directory When exporting a project, resources are often moved, converted or remapped (source import files are gone, text scenes are converted to binary, etc). This new function allows to list a directory and obtain the actual original resource files. Example ```GDScript var resources = ResourceLoader.list_directory("res://images") print(resources) ``` Result will be something like: ``` ["image1.png","image2.png","image3.png"] ``` instead of ``` ["image1.png.import","image2.png.import","image3.png.import"] ``` --- core/core_bind.cpp | 5 +++ core/core_bind.h | 2 ++ core/io/resource_loader.cpp | 56 ++++++++++++++++++++++++++++++++++ core/io/resource_loader.h | 2 ++ doc/classes/DirAccess.xml | 1 + doc/classes/ResourceLoader.xml | 7 +++++ 6 files changed, 73 insertions(+) diff --git a/core/core_bind.cpp b/core/core_bind.cpp index 891e3a28c9..0fe0952a69 100644 --- a/core/core_bind.cpp +++ b/core/core_bind.cpp @@ -132,6 +132,10 @@ ResourceUID::ID ResourceLoader::get_resource_uid(const String &p_path) { return ::ResourceLoader::get_resource_uid(p_path); } +Vector ResourceLoader::list_directory(const String &p_directory) { + return ::ResourceLoader::list_directory(p_directory); +} + void ResourceLoader::_bind_methods() { ClassDB::bind_method(D_METHOD("load_threaded_request", "path", "type_hint", "use_sub_threads", "cache_mode"), &ResourceLoader::load_threaded_request, DEFVAL(""), DEFVAL(false), DEFVAL(CACHE_MODE_REUSE)); ClassDB::bind_method(D_METHOD("load_threaded_get_status", "path", "progress"), &ResourceLoader::load_threaded_get_status, DEFVAL_ARRAY); @@ -147,6 +151,7 @@ void ResourceLoader::_bind_methods() { ClassDB::bind_method(D_METHOD("get_cached_ref", "path"), &ResourceLoader::get_cached_ref); ClassDB::bind_method(D_METHOD("exists", "path", "type_hint"), &ResourceLoader::exists, DEFVAL("")); ClassDB::bind_method(D_METHOD("get_resource_uid", "path"), &ResourceLoader::get_resource_uid); + ClassDB::bind_method(D_METHOD("list_directory", "directory_path"), &ResourceLoader::list_directory); BIND_ENUM_CONSTANT(THREAD_LOAD_INVALID_RESOURCE); BIND_ENUM_CONSTANT(THREAD_LOAD_IN_PROGRESS); diff --git a/core/core_bind.h b/core/core_bind.h index ce0bde3c05..7c70506a30 100644 --- a/core/core_bind.h +++ b/core/core_bind.h @@ -87,6 +87,8 @@ public: bool exists(const String &p_path, const String &p_type_hint = ""); ResourceUID::ID get_resource_uid(const String &p_path); + Vector list_directory(const String &p_directory); + ResourceLoader() { singleton = this; } }; diff --git a/core/io/resource_loader.cpp b/core/io/resource_loader.cpp index f026d5416c..e0354c3158 100644 --- a/core/io/resource_loader.cpp +++ b/core/io/resource_loader.cpp @@ -32,6 +32,7 @@ #include "core/config/project_settings.h" #include "core/core_bind.h" +#include "core/io/dir_access.h" #include "core/io/file_access.h" #include "core/io/resource_importer.h" #include "core/object/script_language.h" @@ -40,6 +41,7 @@ #include "core/os/safe_binary_mutex.h" #include "core/string/print_string.h" #include "core/string/translation_server.h" +#include "core/templates/rb_set.h" #include "core/variant/variant_parser.h" #include "servers/rendering_server.h" @@ -1439,6 +1441,60 @@ bool ResourceLoader::is_cleaning_tasks() { return cleaning_tasks; } +Vector ResourceLoader::list_directory(const String &p_directory) { + RBSet files_found; + Ref dir = DirAccess::open(p_directory); + if (dir.is_null()) { + return Vector(); + } + + Error err = dir->list_dir_begin(); + if (err != OK) { + return Vector(); + } + + String d = dir->get_next(); + while (!d.is_empty()) { + bool recognized = false; + if (dir->current_is_dir()) { + if (d != "." && d != "..") { + d += "/"; + recognized = true; + } + } else { + if (d.ends_with(".import") || d.ends_with(".remap") || d.ends_with(".uid")) { + d = d.substr(0, d.rfind(".")); + } + + if (d.ends_with(".gdc")) { + d = d.substr(0, d.rfind(".")); + d += ".gd"; + } + + const String full_path = p_directory.path_join(d); + // Try all loaders and pick the first match for the type hint. + for (int i = 0; i < loader_count; i++) { + if (loader[i]->recognize_path(full_path)) { + recognized = true; + break; + } + } + } + + if (recognized) { + files_found.insert(d); + } + d = dir->get_next(); + } + + Vector ret; + for (const String &f : files_found) { + ret.push_back(f); + } + + return ret; +} + void ResourceLoader::initialize() {} void ResourceLoader::finalize() {} diff --git a/core/io/resource_loader.h b/core/io/resource_loader.h index caaf9f8f45..c7a0261699 100644 --- a/core/io/resource_loader.h +++ b/core/io/resource_loader.h @@ -302,6 +302,8 @@ public: static bool is_cleaning_tasks(); + static Vector list_directory(const String &p_directory); + static void initialize(); static void finalize(); }; diff --git a/doc/classes/DirAccess.xml b/doc/classes/DirAccess.xml index 9c71addf0c..dcd2d527e2 100644 --- a/doc/classes/DirAccess.xml +++ b/doc/classes/DirAccess.xml @@ -60,6 +60,7 @@ } [/csharp] [/codeblocks] + Keep in mind that file names may change or be remapped after export. If you want to see the actual resource file list as it appears in the editor, use [method ResourceLoader.list_directory] instead. $DOCS_URL/tutorials/scripting/filesystem.html diff --git a/doc/classes/ResourceLoader.xml b/doc/classes/ResourceLoader.xml index 56c3208fc3..1e2c0efafa 100644 --- a/doc/classes/ResourceLoader.xml +++ b/doc/classes/ResourceLoader.xml @@ -74,6 +74,13 @@ Once a resource has been loaded by the engine, it is cached in memory for faster access, and future calls to the [method load] method will use the cached version. The cached resource can be overridden by using [method Resource.take_over_path] on a new resource for that same path. + + + + + Lists a directory (as example: "res://assets/enemies"), returning all resources contained within. The resource files are the original file names as visible in the editor before exporting. + +