Repository: godotengine/godot (language: cpp)

Issue:
UID error in Project Manager when trying to load icon
### Tested versions

4.4 dev5
Another regression from #97912

### System information

W10

### Issue description

Similar to #99221
When you set project's icon, it will be stored as UID. While it works fine in the editor, Project Manager is not able to load UIDs of projects, so it throws
```
ERROR: Condition "!unique_ids.has(p_id)" is true. Returning: String()
   at: ResourceUID::get_id_path (C:\godot_source\core/io/resource_uid.cpp:133)
ERROR: Error opening file ''.
   at: (C:\godot_source\core/io/image_loader.cpp:91)
```
and falls back to default icon.

It *should* be possible to read the UID from cache, but if it's not cached, there is no feasible way to load the icon.

### Steps to reproduce

1. Open a project in Project Manager
2. In 4.4 dev5, set its `application/config/icon`
3. Go back to Project Manager

### Minimal reproduction project (MRP)

N/A


Discussion / hints:

Fix patch:
diff --git a/core/io/resource_uid.cpp b/core/io/resource_uid.cpp
index 135d5559443b..3a0e2aa090d3 100644
--- a/core/io/resource_uid.cpp
+++ b/core/io/resource_uid.cpp
@@ -229,7 +229,7 @@ Error ResourceUID::load_from_cache(bool p_reset) {
 		int32_t len = f->get_32();
 		Cache c;
 		c.cs.resize(len + 1);
-		ERR_FAIL_COND_V(c.cs.size() != len + 1, ERR_FILE_CORRUPT); // out of memory
+		ERR_FAIL_COND_V(c.cs.size() != len + 1, ERR_FILE_CORRUPT); // Out of memory.
 		c.cs[len] = 0;
 		int32_t rl = f->get_buffer((uint8_t *)c.cs.ptrw(), len);
 		ERR_FAIL_COND_V(rl != len, ERR_FILE_CORRUPT);
@@ -257,7 +257,7 @@ Error ResourceUID::update_cache() {
 	for (KeyValue<ID, Cache> &E : unique_ids) {
 		if (!E.value.saved_to_cache) {
 			if (f.is_null()) {
-				f = FileAccess::open(get_cache_file(), FileAccess::READ_WRITE); //append
+				f = FileAccess::open(get_cache_file(), FileAccess::READ_WRITE); // Append.
 				if (f.is_null()) {
 					return ERR_CANT_OPEN;
 				}
@@ -282,6 +282,25 @@ Error ResourceUID::update_cache() {
 	return OK;
 }
 
+String ResourceUID::get_path_from_cache(Ref<FileAccess> &p_cache_file, const String &p_uid_string) {
+	const uint32_t entry_count = p_cache_file->get_32();
+	CharString cs;
+	for (uint32_t i = 0; i < entry_count; i++) {
+		int64_t id = p_cache_file->get_64();
+		int32_t len = p_cache_file->get_32();
+		cs.resize(len + 1);
+		ERR_FAIL_COND_V(cs.size() != len + 1, String());
+		cs[len] = 0;
+		int32_t rl = p_cache_file->get_buffer((uint8_t *)cs.ptrw(), len);
+		ERR_FAIL_COND_V(rl != len, String());
+
+		if (singleton->id_to_text(id) == p_uid_string) {
+			return String(cs);
+		}
+	}
+	return String();
+}
+
 void ResourceUID::clear() {
 	cache_entries = 0;
 	unique_ids.clear();
diff --git a/core/io/resource_uid.h b/core/io/resource_uid.h
index bdbd87dcad53..6d96953fcc1d 100644
--- a/core/io/resource_uid.h
+++ b/core/io/resource_uid.h
@@ -35,6 +35,8 @@
 #include "core/string/string_name.h"
 #include "core/templates/hash_map.h"
 
+class FileAccess;
+
 class ResourceUID : public Object {
 	GDCLASS(ResourceUID, Object)
 public:
@@ -78,6 +80,7 @@ class ResourceUID : public Object {
 	Error load_from_cache(bool p_reset);
 	Error save_to_cache();
 	Error update_cache();
+	static String get_path_from_cache(Ref<FileAccess> &p_cache_file, const String &p_uid_string);
 
 	void clear();
 
diff --git a/editor/project_manager/project_list.cpp b/editor/project_manager/project_list.cpp
index f530c107273b..5b7c307a3f78 100644
--- a/editor/project_manager/project_list.cpp
+++ b/editor/project_manager/project_list.cpp
@@ -489,9 +489,20 @@ ProjectList::Item ProjectList::load_project_data(const String &p_path, bool p_fa
 
 	const String description = cf->get_value("application", "config/description", "");
 	const PackedStringArray tags = cf->get_value("application", "config/tags", PackedStringArray());
-	const String icon = cf->get_value("application", "config/icon", "");
 	const String main_scene = cf->get_value("application", "run/main_scene", "");
 
+	String icon = cf->get_value("application", "config/icon", "");
+	if (icon.begins_with("uid://")) {
+		Error err;
+		Ref<FileAccess> file = FileAccess::open(p_path.path_join(".godot/uid_cache.bin"), FileAccess::READ, &err);
+		if (err == OK) {
+			icon = ResourceUID::get_path_from_cache(file, icon);
+			if (icon.is_empty()) {
+				WARN_PRINT(vformat("Could not load icon from UID for project at path \"%s\". Make sure UID cache exists.", p_path));
+			}
+		}
+	}
+
 	PackedStringArray project_features = cf->get_value("application", "config/features", PackedStringArray());
 	PackedStringArray unsupported_features = ProjectSettings::get_unsupported_features(project_features);
 


Test patch: