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 | |
41 | class FileAccess; |
42 | |
43 | struct EditorProgressBG; |
44 | class 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 | |
85 | public: |
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 | |
114 | class EditorFileSystemImportFormatSupportQuery : public RefCounted { |
115 | GDCLASS(EditorFileSystemImportFormatSupportQuery, RefCounted); |
116 | |
117 | protected: |
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 | |
127 | public: |
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 | |
145 | class 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 | |
297 | protected: |
298 | void _notification(int p_what); |
299 | static void _bind_methods(); |
300 | |
301 | public: |
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 | |