1/**************************************************************************/
2/* editor_file_system.h */
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
31#ifndef EDITOR_FILE_SYSTEM_H
32#define EDITOR_FILE_SYSTEM_H
33
34#include "core/io/dir_access.h"
35#include "core/os/thread.h"
36#include "core/os/thread_safe.h"
37#include "core/templates/hash_set.h"
38#include "core/templates/safe_refcount.h"
39#include "scene/main/node.h"
40
41class FileAccess;
42
43struct EditorProgressBG;
44class EditorFileSystemDirectory : public Object {
45 GDCLASS(EditorFileSystemDirectory, Object);
46
47 String name;
48 uint64_t modified_time;
49 bool verified = false; //used for checking changes
50
51 EditorFileSystemDirectory *parent = nullptr;
52 Vector<EditorFileSystemDirectory *> subdirs;
53
54 struct FileInfo {
55 String file;
56 StringName type;
57 StringName resource_script_class; // If any resource has script with a global class name, its found here.
58 ResourceUID::ID uid = ResourceUID::INVALID_ID;
59 uint64_t modified_time = 0;
60 uint64_t import_modified_time = 0;
61 bool import_valid = false;
62 String import_group_file;
63 Vector<String> deps;
64 bool verified = false; //used for checking changes
65 // These are for script resources only.
66 String script_class_name;
67 String script_class_extends;
68 String script_class_icon_path;
69 };
70
71 struct FileInfoSort {
72 bool operator()(const FileInfo *p_a, const FileInfo *p_b) const {
73 return p_a->file < p_b->file;
74 }
75 };
76
77 void sort_files();
78
79 Vector<FileInfo *> files;
80
81 static void _bind_methods();
82
83 friend class EditorFileSystem;
84
85public:
86 String get_name();
87 String get_path() const;
88
89 int get_subdir_count() const;
90 EditorFileSystemDirectory *get_subdir(int p_idx);
91 int get_file_count() const;
92 String get_file(int p_idx) const;
93 String get_file_path(int p_idx) const;
94 StringName get_file_type(int p_idx) const;
95 StringName get_file_resource_script_class(int p_idx) const;
96 Vector<String> get_file_deps(int p_idx) const;
97 bool get_file_import_is_valid(int p_idx) const;
98 uint64_t get_file_modified_time(int p_idx) const;
99 String get_file_script_class_name(int p_idx) const; //used for scripts
100 String get_file_script_class_extends(int p_idx) const; //used for scripts
101 String get_file_script_class_icon_path(int p_idx) const; //used for scripts
102
103 EditorFileSystemDirectory *get_parent();
104
105 int find_file_index(const String &p_file) const;
106 int find_dir_index(const String &p_dir) const;
107
108 void force_update();
109
110 EditorFileSystemDirectory();
111 ~EditorFileSystemDirectory();
112};
113
114class EditorFileSystemImportFormatSupportQuery : public RefCounted {
115 GDCLASS(EditorFileSystemImportFormatSupportQuery, RefCounted);
116
117protected:
118 GDVIRTUAL0RC(bool, _is_active)
119 GDVIRTUAL0RC(Vector<String>, _get_file_extensions)
120 GDVIRTUAL0RC(bool, _query)
121 static void _bind_methods() {
122 GDVIRTUAL_BIND(_is_active);
123 GDVIRTUAL_BIND(_get_file_extensions);
124 GDVIRTUAL_BIND(_query);
125 }
126
127public:
128 virtual bool is_active() const {
129 bool ret = false;
130 GDVIRTUAL_REQUIRED_CALL(_is_active, ret);
131 return ret;
132 }
133 virtual Vector<String> get_file_extensions() const {
134 Vector<String> ret;
135 GDVIRTUAL_REQUIRED_CALL(_get_file_extensions, ret);
136 return ret;
137 }
138 virtual bool query() {
139 bool ret = false;
140 GDVIRTUAL_REQUIRED_CALL(_query, ret);
141 return ret;
142 }
143};
144
145class EditorFileSystem : public Node {
146 GDCLASS(EditorFileSystem, Node);
147
148 _THREAD_SAFE_CLASS_
149
150 struct ItemAction {
151 enum Action {
152 ACTION_NONE,
153 ACTION_DIR_ADD,
154 ACTION_DIR_REMOVE,
155 ACTION_FILE_ADD,
156 ACTION_FILE_REMOVE,
157 ACTION_FILE_TEST_REIMPORT,
158 ACTION_FILE_RELOAD
159 };
160
161 Action action = ACTION_NONE;
162 EditorFileSystemDirectory *dir = nullptr;
163 String file;
164 EditorFileSystemDirectory *new_dir = nullptr;
165 EditorFileSystemDirectory::FileInfo *new_file = nullptr;
166 };
167
168 bool use_threads = true;
169 Thread thread;
170 static void _thread_func(void *_userdata);
171
172 EditorFileSystemDirectory *new_filesystem = nullptr;
173
174 bool scanning = false;
175 bool importing = false;
176 bool first_scan = true;
177 bool scan_changes_pending = false;
178 float scan_total;
179 String filesystem_settings_version_for_import;
180 bool revalidate_import_files = false;
181
182 void _scan_filesystem();
183
184 HashSet<String> late_update_files;
185
186 void _save_late_updated_files();
187
188 EditorFileSystemDirectory *filesystem = nullptr;
189
190 static EditorFileSystem *singleton;
191
192 /* Used for reading the filesystem cache file */
193 struct FileCache {
194 String type;
195 String resource_script_class;
196 ResourceUID::ID uid = ResourceUID::INVALID_ID;
197 uint64_t modification_time = 0;
198 uint64_t import_modification_time = 0;
199 Vector<String> deps;
200 bool import_valid = false;
201 String import_group_file;
202 String script_class_name;
203 String script_class_extends;
204 String script_class_icon_path;
205 };
206
207 HashMap<String, FileCache> file_cache;
208
209 struct ScanProgress {
210 float low = 0;
211 float hi = 0;
212 mutable EditorProgressBG *progress = nullptr;
213 void update(int p_current, int p_total) const;
214 ScanProgress get_sub(int p_current, int p_total) const;
215 };
216
217 void _save_filesystem_cache();
218 void _save_filesystem_cache(EditorFileSystemDirectory *p_dir, Ref<FileAccess> p_file);
219
220 bool _find_file(const String &p_file, EditorFileSystemDirectory **r_d, int &r_file_pos) const;
221
222 void _scan_fs_changes(EditorFileSystemDirectory *p_dir, const ScanProgress &p_progress);
223
224 void _delete_internal_files(String p_file);
225
226 HashSet<String> textfile_extensions;
227 HashSet<String> valid_extensions;
228 HashSet<String> import_extensions;
229
230 void _scan_new_dir(EditorFileSystemDirectory *p_dir, Ref<DirAccess> &da, const ScanProgress &p_progress);
231
232 Thread thread_sources;
233 bool scanning_changes = false;
234 bool scanning_changes_done = false;
235
236 static void _thread_func_sources(void *_userdata);
237
238 List<String> sources_changed;
239 List<ItemAction> scan_actions;
240
241 bool _update_scan_actions();
242
243 void _update_extensions();
244
245 Error _reimport_file(const String &p_file, const HashMap<StringName, Variant> &p_custom_options = HashMap<StringName, Variant>(), const String &p_custom_importer = String(), Variant *generator_parameters = nullptr);
246 Error _reimport_group(const String &p_group_file, const Vector<String> &p_files);
247
248 bool _test_for_reimport(const String &p_path, bool p_only_imported_files);
249
250 bool reimport_on_missing_imported_files;
251
252 Vector<String> _get_dependencies(const String &p_path);
253
254 struct ImportFile {
255 String path;
256 String importer;
257 bool threaded = false;
258 int order = 0;
259 bool operator<(const ImportFile &p_if) const {
260 return order == p_if.order ? (importer < p_if.importer) : (order < p_if.order);
261 }
262 };
263
264 Mutex update_script_mutex;
265 HashSet<String> update_script_paths;
266 void _queue_update_script_class(const String &p_path);
267 void _update_script_classes();
268 void _update_pending_script_classes();
269
270 String _get_global_script_class(const String &p_type, const String &p_path, String *r_extends, String *r_icon_path) const;
271
272 static Error _resource_import(const String &p_path);
273
274 bool using_fat32_or_exfat; // Workaround for projects in FAT32 or exFAT filesystem (pendrives, most of the time)
275
276 void _find_group_files(EditorFileSystemDirectory *efd, HashMap<String, Vector<String>> &group_files, HashSet<String> &groups_to_reimport);
277
278 void _move_group_files(EditorFileSystemDirectory *efd, const String &p_group_file, const String &p_new_location);
279
280 HashSet<String> group_file_cache;
281
282 struct ImportThreadData {
283 const ImportFile *reimport_files;
284 int reimport_from;
285 int max_index = 0;
286 };
287
288 void _reimport_thread(uint32_t p_index, ImportThreadData *p_import_data);
289
290 static ResourceUID::ID _resource_saver_get_resource_id_for_path(const String &p_path, bool p_generate);
291
292 bool _scan_extensions();
293 bool _scan_import_support(Vector<String> reimports);
294
295 Vector<Ref<EditorFileSystemImportFormatSupportQuery>> import_support_queries;
296
297protected:
298 void _notification(int p_what);
299 static void _bind_methods();
300
301public:
302 static EditorFileSystem *get_singleton() { return singleton; }
303
304 EditorFileSystemDirectory *get_filesystem();
305 bool is_scanning() const;
306 bool is_importing() const { return importing; }
307 float get_scanning_progress() const;
308 void scan();
309 void scan_changes();
310 void update_file(const String &p_file);
311 HashSet<String> get_valid_extensions() const;
312
313 EditorFileSystemDirectory *get_filesystem_path(const String &p_path);
314 String get_file_type(const String &p_file) const;
315 EditorFileSystemDirectory *find_file(const String &p_file, int *r_index) const;
316
317 void reimport_files(const Vector<String> &p_files);
318 Error reimport_append(const String &p_file, const HashMap<StringName, Variant> &p_custom_options, const String &p_custom_importer, Variant p_generator_parameters);
319
320 void reimport_file_with_custom_parameters(const String &p_file, const String &p_importer, const HashMap<StringName, Variant> &p_custom_params);
321
322 bool is_group_file(const String &p_path) const;
323 void move_group_file(const String &p_path, const String &p_new_path);
324
325 static bool _should_skip_directory(const String &p_path);
326
327 void add_import_format_support_query(Ref<EditorFileSystemImportFormatSupportQuery> p_query);
328 void remove_import_format_support_query(Ref<EditorFileSystemImportFormatSupportQuery> p_query);
329 EditorFileSystem();
330 ~EditorFileSystem();
331};
332
333#endif // EDITOR_FILE_SYSTEM_H
334