1 /*************************************************************************/
2 /*  editor_export.cpp                                                    */
3 /*************************************************************************/
4 /*                       This file is part of:                           */
5 /*                           GODOT ENGINE                                */
6 /*                      https://godotengine.org                          */
7 /*************************************************************************/
8 /* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur.                 */
9 /* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md).   */
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 #include "editor_export.h"
32 
33 #include "core/crypto/crypto_core.h"
34 #include "core/io/config_file.h"
35 #include "core/io/file_access_pack.h" // PACK_HEADER_MAGIC, PACK_FORMAT_VERSION
36 #include "core/io/resource_loader.h"
37 #include "core/io/resource_saver.h"
38 #include "core/io/zip_io.h"
39 #include "core/os/dir_access.h"
40 #include "core/os/file_access.h"
41 #include "core/project_settings.h"
42 #include "core/script_language.h"
43 #include "core/version.h"
44 #include "editor/editor_file_system.h"
45 #include "editor/plugins/script_editor_plugin.h"
46 #include "editor_node.h"
47 #include "editor_settings.h"
48 #include "scene/resources/resource_format_text.h"
49 
_get_pad(int p_alignment,int p_n)50 static int _get_pad(int p_alignment, int p_n) {
51 
52 	int rest = p_n % p_alignment;
53 	int pad = 0;
54 	if (rest > 0) {
55 		pad = p_alignment - rest;
56 	};
57 
58 	return pad;
59 }
60 
61 #define PCK_PADDING 16
62 
_set(const StringName & p_name,const Variant & p_value)63 bool EditorExportPreset::_set(const StringName &p_name, const Variant &p_value) {
64 
65 	if (values.has(p_name)) {
66 		values[p_name] = p_value;
67 		EditorExport::singleton->save_presets();
68 		return true;
69 	}
70 
71 	return false;
72 }
73 
_get(const StringName & p_name,Variant & r_ret) const74 bool EditorExportPreset::_get(const StringName &p_name, Variant &r_ret) const {
75 
76 	if (values.has(p_name)) {
77 		r_ret = values[p_name];
78 		return true;
79 	}
80 
81 	return false;
82 }
83 
_get_property_list(List<PropertyInfo> * p_list) const84 void EditorExportPreset::_get_property_list(List<PropertyInfo> *p_list) const {
85 
86 	for (const List<PropertyInfo>::Element *E = properties.front(); E; E = E->next()) {
87 
88 		if (platform->get_option_visibility(E->get().name, values)) {
89 			p_list->push_back(E->get());
90 		}
91 	}
92 }
93 
get_platform() const94 Ref<EditorExportPlatform> EditorExportPreset::get_platform() const {
95 
96 	return platform;
97 }
98 
update_files_to_export()99 void EditorExportPreset::update_files_to_export() {
100 	Vector<String> to_remove;
101 	for (Set<String>::Element *E = selected_files.front(); E; E = E->next()) {
102 		if (!FileAccess::exists(E->get())) {
103 			to_remove.push_back(E->get());
104 		}
105 	}
106 	for (int i = 0; i < to_remove.size(); ++i) {
107 		selected_files.erase(to_remove[i]);
108 	}
109 }
110 
get_files_to_export() const111 Vector<String> EditorExportPreset::get_files_to_export() const {
112 
113 	Vector<String> files;
114 	for (Set<String>::Element *E = selected_files.front(); E; E = E->next()) {
115 		files.push_back(E->get());
116 	}
117 	return files;
118 }
119 
set_name(const String & p_name)120 void EditorExportPreset::set_name(const String &p_name) {
121 	name = p_name;
122 	EditorExport::singleton->save_presets();
123 }
124 
get_name() const125 String EditorExportPreset::get_name() const {
126 	return name;
127 }
128 
set_runnable(bool p_enable)129 void EditorExportPreset::set_runnable(bool p_enable) {
130 
131 	runnable = p_enable;
132 	EditorExport::singleton->save_presets();
133 }
134 
is_runnable() const135 bool EditorExportPreset::is_runnable() const {
136 
137 	return runnable;
138 }
139 
set_export_filter(ExportFilter p_filter)140 void EditorExportPreset::set_export_filter(ExportFilter p_filter) {
141 
142 	export_filter = p_filter;
143 	EditorExport::singleton->save_presets();
144 }
145 
get_export_filter() const146 EditorExportPreset::ExportFilter EditorExportPreset::get_export_filter() const {
147 	return export_filter;
148 }
149 
set_include_filter(const String & p_include)150 void EditorExportPreset::set_include_filter(const String &p_include) {
151 
152 	include_filter = p_include;
153 	EditorExport::singleton->save_presets();
154 }
155 
get_include_filter() const156 String EditorExportPreset::get_include_filter() const {
157 
158 	return include_filter;
159 }
160 
set_export_path(const String & p_path)161 void EditorExportPreset::set_export_path(const String &p_path) {
162 
163 	export_path = p_path;
164 	/* NOTE(SonerSound): if there is a need to implement a PropertyHint that specifically indicates a relative path,
165 	 * this should be removed. */
166 	if (export_path.is_abs_path()) {
167 		String res_path = OS::get_singleton()->get_resource_dir();
168 		export_path = res_path.path_to_file(export_path);
169 	}
170 	EditorExport::singleton->save_presets();
171 }
172 
get_export_path() const173 String EditorExportPreset::get_export_path() const {
174 
175 	return export_path;
176 }
177 
set_exclude_filter(const String & p_exclude)178 void EditorExportPreset::set_exclude_filter(const String &p_exclude) {
179 
180 	exclude_filter = p_exclude;
181 	EditorExport::singleton->save_presets();
182 }
183 
get_exclude_filter() const184 String EditorExportPreset::get_exclude_filter() const {
185 
186 	return exclude_filter;
187 }
188 
add_export_file(const String & p_path)189 void EditorExportPreset::add_export_file(const String &p_path) {
190 
191 	selected_files.insert(p_path);
192 	EditorExport::singleton->save_presets();
193 }
194 
remove_export_file(const String & p_path)195 void EditorExportPreset::remove_export_file(const String &p_path) {
196 	selected_files.erase(p_path);
197 	EditorExport::singleton->save_presets();
198 }
199 
has_export_file(const String & p_path)200 bool EditorExportPreset::has_export_file(const String &p_path) {
201 
202 	return selected_files.has(p_path);
203 }
204 
add_patch(const String & p_path,int p_at_pos)205 void EditorExportPreset::add_patch(const String &p_path, int p_at_pos) {
206 
207 	if (p_at_pos < 0)
208 		patches.push_back(p_path);
209 	else
210 		patches.insert(p_at_pos, p_path);
211 	EditorExport::singleton->save_presets();
212 }
213 
remove_patch(int p_idx)214 void EditorExportPreset::remove_patch(int p_idx) {
215 	patches.remove(p_idx);
216 	EditorExport::singleton->save_presets();
217 }
218 
set_patch(int p_index,const String & p_path)219 void EditorExportPreset::set_patch(int p_index, const String &p_path) {
220 	ERR_FAIL_INDEX(p_index, patches.size());
221 	patches.write[p_index] = p_path;
222 	EditorExport::singleton->save_presets();
223 }
get_patch(int p_index)224 String EditorExportPreset::get_patch(int p_index) {
225 
226 	ERR_FAIL_INDEX_V(p_index, patches.size(), String());
227 	return patches[p_index];
228 }
229 
get_patches() const230 Vector<String> EditorExportPreset::get_patches() const {
231 	return patches;
232 }
233 
set_custom_features(const String & p_custom_features)234 void EditorExportPreset::set_custom_features(const String &p_custom_features) {
235 
236 	custom_features = p_custom_features;
237 	EditorExport::singleton->save_presets();
238 }
239 
get_custom_features() const240 String EditorExportPreset::get_custom_features() const {
241 
242 	return custom_features;
243 }
244 
set_script_export_mode(int p_mode)245 void EditorExportPreset::set_script_export_mode(int p_mode) {
246 
247 	script_mode = p_mode;
248 	EditorExport::singleton->save_presets();
249 }
250 
get_script_export_mode() const251 int EditorExportPreset::get_script_export_mode() const {
252 
253 	return script_mode;
254 }
255 
set_script_encryption_key(const String & p_key)256 void EditorExportPreset::set_script_encryption_key(const String &p_key) {
257 
258 	script_key = p_key;
259 	EditorExport::singleton->save_presets();
260 }
261 
get_script_encryption_key() const262 String EditorExportPreset::get_script_encryption_key() const {
263 
264 	return script_key;
265 }
266 
EditorExportPreset()267 EditorExportPreset::EditorExportPreset() :
268 		export_filter(EXPORT_ALL_RESOURCES),
269 		export_path(""),
270 		runnable(false),
271 		script_mode(MODE_SCRIPT_COMPILED) {
272 }
273 
274 ///////////////////////////////////
275 
gen_debug_flags(Vector<String> & r_flags,int p_flags)276 void EditorExportPlatform::gen_debug_flags(Vector<String> &r_flags, int p_flags) {
277 
278 	String host = EditorSettings::get_singleton()->get("network/debug/remote_host");
279 	int remote_port = (int)EditorSettings::get_singleton()->get("network/debug/remote_port");
280 
281 	if (p_flags & DEBUG_FLAG_REMOTE_DEBUG_LOCALHOST)
282 		host = "localhost";
283 
284 	if (p_flags & DEBUG_FLAG_DUMB_CLIENT) {
285 		int port = EditorSettings::get_singleton()->get("filesystem/file_server/port");
286 		String passwd = EditorSettings::get_singleton()->get("filesystem/file_server/password");
287 		r_flags.push_back("--remote-fs");
288 		r_flags.push_back(host + ":" + itos(port));
289 		if (passwd != "") {
290 			r_flags.push_back("--remote-fs-password");
291 			r_flags.push_back(passwd);
292 		}
293 	}
294 
295 	if (p_flags & DEBUG_FLAG_REMOTE_DEBUG) {
296 
297 		r_flags.push_back("--remote-debug");
298 
299 		r_flags.push_back(host + ":" + String::num(remote_port));
300 
301 		List<String> breakpoints;
302 		ScriptEditor::get_singleton()->get_breakpoints(&breakpoints);
303 
304 		if (breakpoints.size()) {
305 
306 			r_flags.push_back("--breakpoints");
307 			String bpoints;
308 			for (const List<String>::Element *E = breakpoints.front(); E; E = E->next()) {
309 
310 				bpoints += E->get().replace(" ", "%20");
311 				if (E->next())
312 					bpoints += ",";
313 			}
314 
315 			r_flags.push_back(bpoints);
316 		}
317 	}
318 
319 	if (p_flags & DEBUG_FLAG_VIEW_COLLISONS) {
320 
321 		r_flags.push_back("--debug-collisions");
322 	}
323 
324 	if (p_flags & DEBUG_FLAG_VIEW_NAVIGATION) {
325 
326 		r_flags.push_back("--debug-navigation");
327 	}
328 }
329 
_save_pack_file(void * p_userdata,const String & p_path,const Vector<uint8_t> & p_data,int p_file,int p_total)330 Error EditorExportPlatform::_save_pack_file(void *p_userdata, const String &p_path, const Vector<uint8_t> &p_data, int p_file, int p_total) {
331 
332 	PackData *pd = (PackData *)p_userdata;
333 
334 	SavedData sd;
335 	sd.path_utf8 = p_path.utf8();
336 	sd.ofs = pd->f->get_position();
337 	sd.size = p_data.size();
338 
339 	pd->f->store_buffer(p_data.ptr(), p_data.size());
340 	int pad = _get_pad(PCK_PADDING, sd.size);
341 	for (int i = 0; i < pad; i++) {
342 		pd->f->store_8(0);
343 	}
344 
345 	{
346 		unsigned char hash[16];
347 		CryptoCore::md5(p_data.ptr(), p_data.size(), hash);
348 		sd.md5.resize(16);
349 		for (int i = 0; i < 16; i++) {
350 			sd.md5.write[i] = hash[i];
351 		}
352 	}
353 
354 	pd->file_ofs.push_back(sd);
355 
356 	if (pd->ep->step(TTR("Storing File:") + " " + p_path, 2 + p_file * 100 / p_total, false)) {
357 		return ERR_SKIP;
358 	}
359 
360 	return OK;
361 }
362 
_save_zip_file(void * p_userdata,const String & p_path,const Vector<uint8_t> & p_data,int p_file,int p_total)363 Error EditorExportPlatform::_save_zip_file(void *p_userdata, const String &p_path, const Vector<uint8_t> &p_data, int p_file, int p_total) {
364 
365 	String path = p_path.replace_first("res://", "");
366 
367 	ZipData *zd = (ZipData *)p_userdata;
368 
369 	zipFile zip = (zipFile)zd->zip;
370 
371 	zipOpenNewFileInZip(zip,
372 			path.utf8().get_data(),
373 			NULL,
374 			NULL,
375 			0,
376 			NULL,
377 			0,
378 			NULL,
379 			Z_DEFLATED,
380 			Z_DEFAULT_COMPRESSION);
381 
382 	zipWriteInFileInZip(zip, p_data.ptr(), p_data.size());
383 	zipCloseFileInZip(zip);
384 
385 	if (zd->ep->step(TTR("Storing File:") + " " + p_path, 2 + p_file * 100 / p_total, false)) {
386 		return ERR_SKIP;
387 	}
388 
389 	return OK;
390 }
391 
get_option_icon(int p_index) const392 Ref<ImageTexture> EditorExportPlatform::get_option_icon(int p_index) const {
393 	Ref<Theme> theme = EditorNode::get_singleton()->get_editor_theme();
394 	ERR_FAIL_COND_V(theme.is_null(), Ref<ImageTexture>());
395 	return theme->get_icon("Play", "EditorIcons");
396 }
397 
find_export_template(String template_file_name,String * err) const398 String EditorExportPlatform::find_export_template(String template_file_name, String *err) const {
399 
400 	String current_version = VERSION_FULL_CONFIG;
401 	String template_path = EditorSettings::get_singleton()->get_templates_dir().plus_file(current_version).plus_file(template_file_name);
402 
403 	if (FileAccess::exists(template_path)) {
404 		return template_path;
405 	}
406 
407 	// Not found
408 	if (err) {
409 		*err += TTR("No export template found at the expected path:") + "\n" + template_path + "\n";
410 	}
411 	return String();
412 }
413 
exists_export_template(String template_file_name,String * err) const414 bool EditorExportPlatform::exists_export_template(String template_file_name, String *err) const {
415 	return find_export_template(template_file_name, err) != "";
416 }
417 
create_preset()418 Ref<EditorExportPreset> EditorExportPlatform::create_preset() {
419 
420 	Ref<EditorExportPreset> preset;
421 	preset.instance();
422 	preset->platform = Ref<EditorExportPlatform>(this);
423 
424 	List<ExportOption> options;
425 	get_export_options(&options);
426 
427 	for (List<ExportOption>::Element *E = options.front(); E; E = E->next()) {
428 
429 		preset->properties.push_back(E->get().option);
430 		preset->values[E->get().option.name] = E->get().default_value;
431 	}
432 
433 	return preset;
434 }
435 
_export_find_resources(EditorFileSystemDirectory * p_dir,Set<String> & p_paths)436 void EditorExportPlatform::_export_find_resources(EditorFileSystemDirectory *p_dir, Set<String> &p_paths) {
437 
438 	for (int i = 0; i < p_dir->get_subdir_count(); i++) {
439 		_export_find_resources(p_dir->get_subdir(i), p_paths);
440 	}
441 
442 	for (int i = 0; i < p_dir->get_file_count(); i++) {
443 		p_paths.insert(p_dir->get_file_path(i));
444 	}
445 }
446 
_export_find_dependencies(const String & p_path,Set<String> & p_paths)447 void EditorExportPlatform::_export_find_dependencies(const String &p_path, Set<String> &p_paths) {
448 
449 	if (p_paths.has(p_path))
450 		return;
451 
452 	p_paths.insert(p_path);
453 
454 	EditorFileSystemDirectory *dir;
455 	int file_idx;
456 	dir = EditorFileSystem::get_singleton()->find_file(p_path, &file_idx);
457 	if (!dir)
458 		return;
459 
460 	Vector<String> deps = dir->get_file_deps(file_idx);
461 
462 	for (int i = 0; i < deps.size(); i++) {
463 
464 		_export_find_dependencies(deps[i], p_paths);
465 	}
466 }
467 
_edit_files_with_filter(DirAccess * da,const Vector<String> & p_filters,Set<String> & r_list,bool exclude)468 void EditorExportPlatform::_edit_files_with_filter(DirAccess *da, const Vector<String> &p_filters, Set<String> &r_list, bool exclude) {
469 
470 	da->list_dir_begin();
471 	String cur_dir = da->get_current_dir().replace("\\", "/");
472 	if (!cur_dir.ends_with("/"))
473 		cur_dir += "/";
474 	String cur_dir_no_prefix = cur_dir.replace("res://", "");
475 
476 	Vector<String> dirs;
477 	String f;
478 	while ((f = da->get_next()) != "") {
479 		if (da->current_is_dir())
480 			dirs.push_back(f);
481 		else {
482 			String fullpath = cur_dir + f;
483 			// Test also against path without res:// so that filters like `file.txt` can work.
484 			String fullpath_no_prefix = cur_dir_no_prefix + f;
485 			for (int i = 0; i < p_filters.size(); ++i) {
486 				if (fullpath.matchn(p_filters[i]) || fullpath_no_prefix.matchn(p_filters[i])) {
487 					if (!exclude) {
488 						r_list.insert(fullpath);
489 					} else {
490 						r_list.erase(fullpath);
491 					}
492 				}
493 			}
494 		}
495 	}
496 
497 	da->list_dir_end();
498 
499 	for (int i = 0; i < dirs.size(); ++i) {
500 		String dir = dirs[i];
501 		if (dir.begins_with("."))
502 			continue;
503 		da->change_dir(dir);
504 		_edit_files_with_filter(da, p_filters, r_list, exclude);
505 		da->change_dir("..");
506 	}
507 }
508 
_edit_filter_list(Set<String> & r_list,const String & p_filter,bool exclude)509 void EditorExportPlatform::_edit_filter_list(Set<String> &r_list, const String &p_filter, bool exclude) {
510 
511 	if (p_filter == "")
512 		return;
513 	Vector<String> split = p_filter.split(",");
514 	Vector<String> filters;
515 	for (int i = 0; i < split.size(); i++) {
516 		String f = split[i].strip_edges();
517 		if (f.empty())
518 			continue;
519 		filters.push_back(f);
520 	}
521 
522 	DirAccess *da = DirAccess::open("res://");
523 	ERR_FAIL_NULL(da);
524 	_edit_files_with_filter(da, filters, r_list, exclude);
525 	memdelete(da);
526 }
527 
set_export_preset(const Ref<EditorExportPreset> & p_preset)528 void EditorExportPlugin::set_export_preset(const Ref<EditorExportPreset> &p_preset) {
529 
530 	if (p_preset.is_valid()) {
531 		export_preset = p_preset;
532 	}
533 }
534 
get_export_preset() const535 Ref<EditorExportPreset> EditorExportPlugin::get_export_preset() const {
536 
537 	return export_preset;
538 }
539 
add_file(const String & p_path,const Vector<uint8_t> & p_file,bool p_remap)540 void EditorExportPlugin::add_file(const String &p_path, const Vector<uint8_t> &p_file, bool p_remap) {
541 
542 	ExtraFile ef;
543 	ef.data = p_file;
544 	ef.path = p_path;
545 	ef.remap = p_remap;
546 	extra_files.push_back(ef);
547 }
548 
add_shared_object(const String & p_path,const Vector<String> & tags)549 void EditorExportPlugin::add_shared_object(const String &p_path, const Vector<String> &tags) {
550 
551 	shared_objects.push_back(SharedObject(p_path, tags));
552 }
553 
add_ios_framework(const String & p_path)554 void EditorExportPlugin::add_ios_framework(const String &p_path) {
555 	ios_frameworks.push_back(p_path);
556 }
557 
add_ios_embedded_framework(const String & p_path)558 void EditorExportPlugin::add_ios_embedded_framework(const String &p_path) {
559 	ios_embedded_frameworks.push_back(p_path);
560 }
561 
get_ios_frameworks() const562 Vector<String> EditorExportPlugin::get_ios_frameworks() const {
563 	return ios_frameworks;
564 }
565 
get_ios_embedded_frameworks() const566 Vector<String> EditorExportPlugin::get_ios_embedded_frameworks() const {
567 	return ios_embedded_frameworks;
568 }
569 
add_ios_plist_content(const String & p_plist_content)570 void EditorExportPlugin::add_ios_plist_content(const String &p_plist_content) {
571 	ios_plist_content += p_plist_content + "\n";
572 }
573 
get_ios_plist_content() const574 String EditorExportPlugin::get_ios_plist_content() const {
575 	return ios_plist_content;
576 }
577 
add_ios_linker_flags(const String & p_flags)578 void EditorExportPlugin::add_ios_linker_flags(const String &p_flags) {
579 	if (ios_linker_flags.length() > 0) {
580 		ios_linker_flags += ' ';
581 	}
582 	ios_linker_flags += p_flags;
583 }
584 
get_ios_linker_flags() const585 String EditorExportPlugin::get_ios_linker_flags() const {
586 	return ios_linker_flags;
587 }
588 
add_ios_bundle_file(const String & p_path)589 void EditorExportPlugin::add_ios_bundle_file(const String &p_path) {
590 	ios_bundle_files.push_back(p_path);
591 }
592 
get_ios_bundle_files() const593 Vector<String> EditorExportPlugin::get_ios_bundle_files() const {
594 	return ios_bundle_files;
595 }
596 
add_ios_cpp_code(const String & p_code)597 void EditorExportPlugin::add_ios_cpp_code(const String &p_code) {
598 	ios_cpp_code += p_code;
599 }
600 
get_ios_cpp_code() const601 String EditorExportPlugin::get_ios_cpp_code() const {
602 	return ios_cpp_code;
603 }
604 
add_ios_project_static_lib(const String & p_path)605 void EditorExportPlugin::add_ios_project_static_lib(const String &p_path) {
606 	ios_project_static_libs.push_back(p_path);
607 }
608 
get_ios_project_static_libs() const609 Vector<String> EditorExportPlugin::get_ios_project_static_libs() const {
610 	return ios_project_static_libs;
611 }
612 
_export_file_script(const String & p_path,const String & p_type,const PoolVector<String> & p_features)613 void EditorExportPlugin::_export_file_script(const String &p_path, const String &p_type, const PoolVector<String> &p_features) {
614 
615 	if (get_script_instance()) {
616 		get_script_instance()->call("_export_file", p_path, p_type, p_features);
617 	}
618 }
619 
_export_begin_script(const PoolVector<String> & p_features,bool p_debug,const String & p_path,int p_flags)620 void EditorExportPlugin::_export_begin_script(const PoolVector<String> &p_features, bool p_debug, const String &p_path, int p_flags) {
621 
622 	if (get_script_instance()) {
623 		get_script_instance()->call("_export_begin", p_features, p_debug, p_path, p_flags);
624 	}
625 }
626 
_export_end_script()627 void EditorExportPlugin::_export_end_script() {
628 
629 	if (get_script_instance()) {
630 		get_script_instance()->call("_export_end");
631 	}
632 }
633 
_export_file(const String & p_path,const String & p_type,const Set<String> & p_features)634 void EditorExportPlugin::_export_file(const String &p_path, const String &p_type, const Set<String> &p_features) {
635 }
636 
_export_begin(const Set<String> & p_features,bool p_debug,const String & p_path,int p_flags)637 void EditorExportPlugin::_export_begin(const Set<String> &p_features, bool p_debug, const String &p_path, int p_flags) {
638 }
639 
skip()640 void EditorExportPlugin::skip() {
641 
642 	skipped = true;
643 }
644 
_bind_methods()645 void EditorExportPlugin::_bind_methods() {
646 
647 	ClassDB::bind_method(D_METHOD("add_shared_object", "path", "tags"), &EditorExportPlugin::add_shared_object);
648 	ClassDB::bind_method(D_METHOD("add_ios_project_static_lib", "path"), &EditorExportPlugin::add_ios_project_static_lib);
649 	ClassDB::bind_method(D_METHOD("add_file", "path", "file", "remap"), &EditorExportPlugin::add_file);
650 	ClassDB::bind_method(D_METHOD("add_ios_framework", "path"), &EditorExportPlugin::add_ios_framework);
651 	ClassDB::bind_method(D_METHOD("add_ios_embedded_framework", "path"), &EditorExportPlugin::add_ios_embedded_framework);
652 	ClassDB::bind_method(D_METHOD("add_ios_plist_content", "plist_content"), &EditorExportPlugin::add_ios_plist_content);
653 	ClassDB::bind_method(D_METHOD("add_ios_linker_flags", "flags"), &EditorExportPlugin::add_ios_linker_flags);
654 	ClassDB::bind_method(D_METHOD("add_ios_bundle_file", "path"), &EditorExportPlugin::add_ios_bundle_file);
655 	ClassDB::bind_method(D_METHOD("add_ios_cpp_code", "code"), &EditorExportPlugin::add_ios_cpp_code);
656 	ClassDB::bind_method(D_METHOD("skip"), &EditorExportPlugin::skip);
657 
658 	BIND_VMETHOD(MethodInfo("_export_file", PropertyInfo(Variant::STRING, "path"), PropertyInfo(Variant::STRING, "type"), PropertyInfo(Variant::POOL_STRING_ARRAY, "features")));
659 	BIND_VMETHOD(MethodInfo("_export_begin", PropertyInfo(Variant::POOL_STRING_ARRAY, "features"), PropertyInfo(Variant::BOOL, "is_debug"), PropertyInfo(Variant::STRING, "path"), PropertyInfo(Variant::INT, "flags")));
660 	BIND_VMETHOD(MethodInfo("_export_end"));
661 }
662 
EditorExportPlugin()663 EditorExportPlugin::EditorExportPlugin() {
664 	skipped = false;
665 }
666 
get_feature_containers(const Ref<EditorExportPreset> & p_preset)667 EditorExportPlatform::FeatureContainers EditorExportPlatform::get_feature_containers(const Ref<EditorExportPreset> &p_preset) {
668 	Ref<EditorExportPlatform> platform = p_preset->get_platform();
669 	List<String> feature_list;
670 	platform->get_platform_features(&feature_list);
671 	platform->get_preset_features(p_preset, &feature_list);
672 
673 	FeatureContainers result;
674 	for (List<String>::Element *E = feature_list.front(); E; E = E->next()) {
675 		result.features.insert(E->get());
676 		result.features_pv.push_back(E->get());
677 	}
678 
679 	if (p_preset->get_custom_features() != String()) {
680 
681 		Vector<String> tmp_custom_list = p_preset->get_custom_features().split(",");
682 
683 		for (int i = 0; i < tmp_custom_list.size(); i++) {
684 			String f = tmp_custom_list[i].strip_edges();
685 			if (f != String()) {
686 				result.features.insert(f);
687 				result.features_pv.push_back(f);
688 			}
689 		}
690 	}
691 
692 	return result;
693 }
694 
ExportNotifier(EditorExportPlatform & p_platform,const Ref<EditorExportPreset> & p_preset,bool p_debug,const String & p_path,int p_flags)695 EditorExportPlatform::ExportNotifier::ExportNotifier(EditorExportPlatform &p_platform, const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags) {
696 	FeatureContainers features = p_platform.get_feature_containers(p_preset);
697 	Vector<Ref<EditorExportPlugin> > export_plugins = EditorExport::get_singleton()->get_export_plugins();
698 	//initial export plugin callback
699 	for (int i = 0; i < export_plugins.size(); i++) {
700 		if (export_plugins[i]->get_script_instance()) { //script based
701 			export_plugins.write[i]->_export_begin_script(features.features_pv, p_debug, p_path, p_flags);
702 		} else {
703 			export_plugins.write[i]->_export_begin(features.features, p_debug, p_path, p_flags);
704 		}
705 	}
706 }
707 
~ExportNotifier()708 EditorExportPlatform::ExportNotifier::~ExportNotifier() {
709 	Vector<Ref<EditorExportPlugin> > export_plugins = EditorExport::get_singleton()->get_export_plugins();
710 	for (int i = 0; i < export_plugins.size(); i++) {
711 		if (export_plugins[i]->get_script_instance()) {
712 			export_plugins.write[i]->_export_end_script();
713 		}
714 		export_plugins.write[i]->_export_end();
715 	}
716 }
717 
export_project_files(const Ref<EditorExportPreset> & p_preset,EditorExportSaveFunction p_func,void * p_udata,EditorExportSaveSharedObject p_so_func)718 Error EditorExportPlatform::export_project_files(const Ref<EditorExportPreset> &p_preset, EditorExportSaveFunction p_func, void *p_udata, EditorExportSaveSharedObject p_so_func) {
719 	//figure out paths of files that will be exported
720 	Set<String> paths;
721 	Vector<String> path_remaps;
722 
723 	if (p_preset->get_export_filter() == EditorExportPreset::EXPORT_ALL_RESOURCES) {
724 		//find stuff
725 		_export_find_resources(EditorFileSystem::get_singleton()->get_filesystem(), paths);
726 	} else {
727 		bool scenes_only = p_preset->get_export_filter() == EditorExportPreset::EXPORT_SELECTED_SCENES;
728 
729 		Vector<String> files = p_preset->get_files_to_export();
730 		for (int i = 0; i < files.size(); i++) {
731 			if (scenes_only && ResourceLoader::get_resource_type(files[i]) != "PackedScene")
732 				continue;
733 
734 			_export_find_dependencies(files[i], paths);
735 		}
736 	}
737 
738 	//add native icons to non-resource include list
739 	_edit_filter_list(paths, String("*.icns"), false);
740 	_edit_filter_list(paths, String("*.ico"), false);
741 
742 	_edit_filter_list(paths, p_preset->get_include_filter(), false);
743 	_edit_filter_list(paths, p_preset->get_exclude_filter(), true);
744 
745 	Vector<Ref<EditorExportPlugin> > export_plugins = EditorExport::get_singleton()->get_export_plugins();
746 	for (int i = 0; i < export_plugins.size(); i++) {
747 
748 		export_plugins.write[i]->set_export_preset(p_preset);
749 
750 		if (p_so_func) {
751 			for (int j = 0; j < export_plugins[i]->shared_objects.size(); j++) {
752 				p_so_func(p_udata, export_plugins[i]->shared_objects[j]);
753 			}
754 		}
755 		for (int j = 0; j < export_plugins[i]->extra_files.size(); j++) {
756 			p_func(p_udata, export_plugins[i]->extra_files[j].path, export_plugins[i]->extra_files[j].data, 0, paths.size());
757 		}
758 
759 		export_plugins.write[i]->_clear();
760 	}
761 
762 	FeatureContainers feature_containers = get_feature_containers(p_preset);
763 	Set<String> &features = feature_containers.features;
764 	PoolVector<String> &features_pv = feature_containers.features_pv;
765 
766 	//store everything in the export medium
767 	int idx = 0;
768 	int total = paths.size();
769 
770 	for (Set<String>::Element *E = paths.front(); E; E = E->next()) {
771 
772 		String path = E->get();
773 		String type = ResourceLoader::get_resource_type(path);
774 
775 		if (FileAccess::exists(path + ".import")) {
776 			//file is imported, replace by what it imports
777 			Ref<ConfigFile> config;
778 			config.instance();
779 			Error err = config->load(path + ".import");
780 			if (err != OK) {
781 				ERR_PRINTS("Could not parse: '" + path + "', not exported.");
782 				continue;
783 			}
784 
785 			List<String> remaps;
786 			config->get_section_keys("remap", &remaps);
787 
788 			Set<String> remap_features;
789 
790 			for (List<String>::Element *F = remaps.front(); F; F = F->next()) {
791 
792 				String remap = F->get();
793 				String feature = remap.get_slice(".", 1);
794 				if (features.has(feature)) {
795 					remap_features.insert(feature);
796 				}
797 			}
798 
799 			if (remap_features.size() > 1) {
800 				this->resolve_platform_feature_priorities(p_preset, remap_features);
801 			}
802 
803 			err = OK;
804 
805 			for (List<String>::Element *F = remaps.front(); F; F = F->next()) {
806 
807 				String remap = F->get();
808 				if (remap == "path") {
809 					String remapped_path = config->get_value("remap", remap);
810 					Vector<uint8_t> array = FileAccess::get_file_as_array(remapped_path);
811 					err = p_func(p_udata, remapped_path, array, idx, total);
812 				} else if (remap.begins_with("path.")) {
813 					String feature = remap.get_slice(".", 1);
814 
815 					if (remap_features.has(feature)) {
816 						String remapped_path = config->get_value("remap", remap);
817 						Vector<uint8_t> array = FileAccess::get_file_as_array(remapped_path);
818 						err = p_func(p_udata, remapped_path, array, idx, total);
819 					}
820 				}
821 			}
822 
823 			if (err != OK) {
824 				return err;
825 			}
826 
827 			//also save the .import file
828 			Vector<uint8_t> array = FileAccess::get_file_as_array(path + ".import");
829 			err = p_func(p_udata, path + ".import", array, idx, total);
830 
831 			if (err != OK) {
832 				return err;
833 			}
834 
835 		} else {
836 
837 			bool do_export = true;
838 			for (int i = 0; i < export_plugins.size(); i++) {
839 				if (export_plugins[i]->get_script_instance()) { //script based
840 					export_plugins.write[i]->_export_file_script(path, type, features_pv);
841 				} else {
842 					export_plugins.write[i]->_export_file(path, type, features);
843 				}
844 				if (p_so_func) {
845 					for (int j = 0; j < export_plugins[i]->shared_objects.size(); j++) {
846 						p_so_func(p_udata, export_plugins[i]->shared_objects[j]);
847 					}
848 				}
849 
850 				for (int j = 0; j < export_plugins[i]->extra_files.size(); j++) {
851 					p_func(p_udata, export_plugins[i]->extra_files[j].path, export_plugins[i]->extra_files[j].data, idx, total);
852 					if (export_plugins[i]->extra_files[j].remap) {
853 						do_export = false; //if remap, do not
854 						path_remaps.push_back(path);
855 						path_remaps.push_back(export_plugins[i]->extra_files[j].path);
856 					}
857 				}
858 
859 				if (export_plugins[i]->skipped) {
860 					do_export = false;
861 				}
862 				export_plugins.write[i]->_clear();
863 
864 				if (!do_export)
865 					break; //apologies, not exporting
866 			}
867 			//just store it as it comes
868 			if (do_export) {
869 				Vector<uint8_t> array = FileAccess::get_file_as_array(path);
870 				p_func(p_udata, path, array, idx, total);
871 			}
872 		}
873 
874 		idx++;
875 	}
876 
877 	//save config!
878 
879 	Vector<String> custom_list;
880 
881 	if (p_preset->get_custom_features() != String()) {
882 
883 		Vector<String> tmp_custom_list = p_preset->get_custom_features().split(",");
884 
885 		for (int i = 0; i < tmp_custom_list.size(); i++) {
886 			String f = tmp_custom_list[i].strip_edges();
887 			if (f != String()) {
888 				custom_list.push_back(f);
889 			}
890 		}
891 	}
892 
893 	ProjectSettings::CustomMap custom_map;
894 	if (path_remaps.size()) {
895 		if (1) { //new remap mode, use always as it's friendlier with multiple .pck exports
896 			for (int i = 0; i < path_remaps.size(); i += 2) {
897 				String from = path_remaps[i];
898 				String to = path_remaps[i + 1];
899 				String remap_file = "[remap]\n\npath=\"" + to.c_escape() + "\"\n";
900 				CharString utf8 = remap_file.utf8();
901 				Vector<uint8_t> new_file;
902 				new_file.resize(utf8.length());
903 				for (int j = 0; j < utf8.length(); j++) {
904 					new_file.write[j] = utf8[j];
905 				}
906 
907 				p_func(p_udata, from + ".remap", new_file, idx, total);
908 			}
909 		} else {
910 			//old remap mode, will still work, but it's unused because it's not multiple pck export friendly
911 			custom_map["path_remap/remapped_paths"] = path_remaps;
912 		}
913 	}
914 
915 	// Store icon and splash images directly, they need to bypass the import system and be loaded as images
916 	String icon = ProjectSettings::get_singleton()->get("application/config/icon");
917 	String splash = ProjectSettings::get_singleton()->get("application/boot_splash/image");
918 	if (icon != String() && FileAccess::exists(icon)) {
919 		Vector<uint8_t> array = FileAccess::get_file_as_array(icon);
920 		p_func(p_udata, icon, array, idx, total);
921 	}
922 	if (splash != String() && FileAccess::exists(splash) && icon != splash) {
923 		Vector<uint8_t> array = FileAccess::get_file_as_array(splash);
924 		p_func(p_udata, splash, array, idx, total);
925 	}
926 
927 	String config_file = "project.binary";
928 	String engine_cfb = EditorSettings::get_singleton()->get_cache_dir().plus_file("tmp" + config_file);
929 	ProjectSettings::get_singleton()->save_custom(engine_cfb, custom_map, custom_list);
930 	Vector<uint8_t> data = FileAccess::get_file_as_array(engine_cfb);
931 	DirAccess::remove_file_or_error(engine_cfb);
932 
933 	p_func(p_udata, "res://" + config_file, data, idx, total);
934 
935 	return OK;
936 }
937 
_add_shared_object(void * p_userdata,const SharedObject & p_so)938 Error EditorExportPlatform::_add_shared_object(void *p_userdata, const SharedObject &p_so) {
939 	PackData *pack_data = (PackData *)p_userdata;
940 	if (pack_data->so_files) {
941 		pack_data->so_files->push_back(p_so);
942 	}
943 
944 	return OK;
945 }
946 
save_pack(const Ref<EditorExportPreset> & p_preset,const String & p_path,Vector<SharedObject> * p_so_files,bool p_embed,int64_t * r_embedded_start,int64_t * r_embedded_size)947 Error EditorExportPlatform::save_pack(const Ref<EditorExportPreset> &p_preset, const String &p_path, Vector<SharedObject> *p_so_files, bool p_embed, int64_t *r_embedded_start, int64_t *r_embedded_size) {
948 
949 	EditorProgress ep("savepack", TTR("Packing"), 102, true);
950 
951 	String tmppath = EditorSettings::get_singleton()->get_cache_dir().plus_file("packtmp");
952 	FileAccess *ftmp = FileAccess::open(tmppath, FileAccess::WRITE);
953 	ERR_FAIL_COND_V_MSG(!ftmp, ERR_CANT_CREATE, "Cannot create file '" + tmppath + "'.");
954 
955 	PackData pd;
956 	pd.ep = &ep;
957 	pd.f = ftmp;
958 	pd.so_files = p_so_files;
959 
960 	Error err = export_project_files(p_preset, _save_pack_file, &pd, _add_shared_object);
961 
962 	memdelete(ftmp); //close tmp file
963 
964 	if (err != OK) {
965 		DirAccess::remove_file_or_error(tmppath);
966 		return err;
967 	}
968 
969 	pd.file_ofs.sort(); //do sort, so we can do binary search later
970 
971 	FileAccess *f;
972 	int64_t embed_pos = 0;
973 	if (!p_embed) {
974 		// Regular output to separate PCK file
975 		f = FileAccess::open(p_path, FileAccess::WRITE);
976 		if (!f) {
977 			DirAccess::remove_file_or_error(tmppath);
978 			ERR_FAIL_V(ERR_CANT_CREATE);
979 		}
980 	} else {
981 		// Append to executable
982 		f = FileAccess::open(p_path, FileAccess::READ_WRITE);
983 		if (!f) {
984 			DirAccess::remove_file_or_error(tmppath);
985 			ERR_FAIL_V(ERR_FILE_CANT_OPEN);
986 		}
987 
988 		f->seek_end();
989 		embed_pos = f->get_position();
990 
991 		if (r_embedded_start) {
992 			*r_embedded_start = embed_pos;
993 		}
994 
995 		// Ensure embedded PCK starts at a 64-bit multiple
996 		int pad = f->get_position() % 8;
997 		for (int i = 0; i < pad; i++) {
998 			f->store_8(0);
999 		}
1000 	}
1001 
1002 	int64_t pck_start_pos = f->get_position();
1003 
1004 	f->store_32(PACK_HEADER_MAGIC);
1005 	f->store_32(PACK_FORMAT_VERSION);
1006 	f->store_32(VERSION_MAJOR);
1007 	f->store_32(VERSION_MINOR);
1008 	f->store_32(VERSION_PATCH);
1009 
1010 	for (int i = 0; i < 16; i++) {
1011 		//reserved
1012 		f->store_32(0);
1013 	}
1014 
1015 	f->store_32(pd.file_ofs.size()); //amount of files
1016 
1017 	int64_t header_size = f->get_position();
1018 
1019 	//precalculate header size
1020 
1021 	for (int i = 0; i < pd.file_ofs.size(); i++) {
1022 		header_size += 4; // size of path string (32 bits is enough)
1023 		int string_len = pd.file_ofs[i].path_utf8.length();
1024 		header_size += string_len + _get_pad(4, string_len); ///size of path string
1025 		header_size += 8; // offset to file _with_ header size included
1026 		header_size += 8; // size of file
1027 		header_size += 16; // md5
1028 	}
1029 
1030 	int header_padding = _get_pad(PCK_PADDING, header_size);
1031 
1032 	for (int i = 0; i < pd.file_ofs.size(); i++) {
1033 
1034 		int string_len = pd.file_ofs[i].path_utf8.length();
1035 		int pad = _get_pad(4, string_len);
1036 
1037 		f->store_32(string_len + pad);
1038 		f->store_buffer((const uint8_t *)pd.file_ofs[i].path_utf8.get_data(), string_len);
1039 		for (int j = 0; j < pad; j++) {
1040 			f->store_8(0);
1041 		}
1042 
1043 		f->store_64(pd.file_ofs[i].ofs + header_padding + header_size);
1044 		f->store_64(pd.file_ofs[i].size); // pay attention here, this is where file is
1045 		f->store_buffer(pd.file_ofs[i].md5.ptr(), 16); //also save md5 for file
1046 	}
1047 
1048 	for (int i = 0; i < header_padding; i++) {
1049 		f->store_8(0);
1050 	}
1051 
1052 	// Save the rest of the data.
1053 
1054 	ftmp = FileAccess::open(tmppath, FileAccess::READ);
1055 	if (!ftmp) {
1056 		memdelete(f);
1057 		DirAccess::remove_file_or_error(tmppath);
1058 		ERR_FAIL_V_MSG(ERR_CANT_CREATE, "Can't open file to read from path '" + String(tmppath) + "'.");
1059 	}
1060 
1061 	const int bufsize = 16384;
1062 	uint8_t buf[bufsize];
1063 
1064 	while (true) {
1065 
1066 		int got = ftmp->get_buffer(buf, bufsize);
1067 		if (got <= 0)
1068 			break;
1069 		f->store_buffer(buf, got);
1070 	}
1071 
1072 	memdelete(ftmp);
1073 
1074 	if (p_embed) {
1075 		// Ensure embedded data ends at a 64-bit multiple
1076 		int64_t embed_end = f->get_position() - embed_pos + 12;
1077 		int pad = embed_end % 8;
1078 		for (int i = 0; i < pad; i++) {
1079 			f->store_8(0);
1080 		}
1081 
1082 		int64_t pck_size = f->get_position() - pck_start_pos;
1083 		f->store_64(pck_size);
1084 		f->store_32(PACK_HEADER_MAGIC);
1085 
1086 		if (r_embedded_size) {
1087 			*r_embedded_size = f->get_position() - embed_pos;
1088 		}
1089 	}
1090 
1091 	memdelete(f);
1092 	DirAccess::remove_file_or_error(tmppath);
1093 
1094 	return OK;
1095 }
1096 
save_zip(const Ref<EditorExportPreset> & p_preset,const String & p_path)1097 Error EditorExportPlatform::save_zip(const Ref<EditorExportPreset> &p_preset, const String &p_path) {
1098 
1099 	EditorProgress ep("savezip", TTR("Packing"), 102, true);
1100 
1101 	FileAccess *src_f;
1102 	zlib_filefunc_def io = zipio_create_io_from_file(&src_f);
1103 	zipFile zip = zipOpen2(p_path.utf8().get_data(), APPEND_STATUS_CREATE, NULL, &io);
1104 
1105 	ZipData zd;
1106 	zd.ep = &ep;
1107 	zd.zip = zip;
1108 
1109 	Error err = export_project_files(p_preset, _save_zip_file, &zd);
1110 	if (err != OK && err != ERR_SKIP)
1111 		ERR_PRINT("Failed to export project files");
1112 
1113 	zipClose(zip, NULL);
1114 
1115 	return OK;
1116 }
1117 
export_pack(const Ref<EditorExportPreset> & p_preset,bool p_debug,const String & p_path,int p_flags)1118 Error EditorExportPlatform::export_pack(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags) {
1119 	ExportNotifier notifier(*this, p_preset, p_debug, p_path, p_flags);
1120 	return save_pack(p_preset, p_path);
1121 }
1122 
export_zip(const Ref<EditorExportPreset> & p_preset,bool p_debug,const String & p_path,int p_flags)1123 Error EditorExportPlatform::export_zip(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags) {
1124 	ExportNotifier notifier(*this, p_preset, p_debug, p_path, p_flags);
1125 	return save_zip(p_preset, p_path);
1126 }
1127 
gen_export_flags(Vector<String> & r_flags,int p_flags)1128 void EditorExportPlatform::gen_export_flags(Vector<String> &r_flags, int p_flags) {
1129 
1130 	String host = EditorSettings::get_singleton()->get("network/debug/remote_host");
1131 	int remote_port = (int)EditorSettings::get_singleton()->get("network/debug/remote_port");
1132 
1133 	if (p_flags & DEBUG_FLAG_REMOTE_DEBUG_LOCALHOST)
1134 		host = "localhost";
1135 
1136 	if (p_flags & DEBUG_FLAG_DUMB_CLIENT) {
1137 		int port = EditorSettings::get_singleton()->get("filesystem/file_server/port");
1138 		String passwd = EditorSettings::get_singleton()->get("filesystem/file_server/password");
1139 		r_flags.push_back("--remote-fs");
1140 		r_flags.push_back(host + ":" + itos(port));
1141 		if (passwd != "") {
1142 			r_flags.push_back("--remote-fs-password");
1143 			r_flags.push_back(passwd);
1144 		}
1145 	}
1146 
1147 	if (p_flags & DEBUG_FLAG_REMOTE_DEBUG) {
1148 
1149 		r_flags.push_back("--remote-debug");
1150 
1151 		r_flags.push_back(host + ":" + String::num(remote_port));
1152 
1153 		List<String> breakpoints;
1154 		ScriptEditor::get_singleton()->get_breakpoints(&breakpoints);
1155 
1156 		if (breakpoints.size()) {
1157 
1158 			r_flags.push_back("--breakpoints");
1159 			String bpoints;
1160 			for (const List<String>::Element *E = breakpoints.front(); E; E = E->next()) {
1161 
1162 				bpoints += E->get().replace(" ", "%20");
1163 				if (E->next())
1164 					bpoints += ",";
1165 			}
1166 
1167 			r_flags.push_back(bpoints);
1168 		}
1169 	}
1170 
1171 	if (p_flags & DEBUG_FLAG_VIEW_COLLISONS) {
1172 
1173 		r_flags.push_back("--debug-collisions");
1174 	}
1175 
1176 	if (p_flags & DEBUG_FLAG_VIEW_NAVIGATION) {
1177 
1178 		r_flags.push_back("--debug-navigation");
1179 	}
1180 }
EditorExportPlatform()1181 EditorExportPlatform::EditorExportPlatform() {
1182 }
1183 
1184 ////
1185 
1186 EditorExport *EditorExport::singleton = NULL;
1187 
_save()1188 void EditorExport::_save() {
1189 
1190 	Ref<ConfigFile> config;
1191 	config.instance();
1192 	for (int i = 0; i < export_presets.size(); i++) {
1193 
1194 		Ref<EditorExportPreset> preset = export_presets[i];
1195 		String section = "preset." + itos(i);
1196 
1197 		config->set_value(section, "name", preset->get_name());
1198 		config->set_value(section, "platform", preset->get_platform()->get_name());
1199 		config->set_value(section, "runnable", preset->is_runnable());
1200 		config->set_value(section, "custom_features", preset->get_custom_features());
1201 
1202 		bool save_files = false;
1203 		switch (preset->get_export_filter()) {
1204 			case EditorExportPreset::EXPORT_ALL_RESOURCES: {
1205 				config->set_value(section, "export_filter", "all_resources");
1206 			} break;
1207 			case EditorExportPreset::EXPORT_SELECTED_SCENES: {
1208 				config->set_value(section, "export_filter", "scenes");
1209 				save_files = true;
1210 			} break;
1211 			case EditorExportPreset::EXPORT_SELECTED_RESOURCES: {
1212 				config->set_value(section, "export_filter", "resources");
1213 				save_files = true;
1214 			} break;
1215 		}
1216 
1217 		if (save_files) {
1218 			Vector<String> export_files = preset->get_files_to_export();
1219 			config->set_value(section, "export_files", export_files);
1220 		}
1221 		config->set_value(section, "include_filter", preset->get_include_filter());
1222 		config->set_value(section, "exclude_filter", preset->get_exclude_filter());
1223 		config->set_value(section, "export_path", preset->get_export_path());
1224 		config->set_value(section, "patch_list", preset->get_patches());
1225 		config->set_value(section, "script_export_mode", preset->get_script_export_mode());
1226 		config->set_value(section, "script_encryption_key", preset->get_script_encryption_key());
1227 
1228 		String option_section = "preset." + itos(i) + ".options";
1229 
1230 		for (const List<PropertyInfo>::Element *E = preset->get_properties().front(); E; E = E->next()) {
1231 			config->set_value(option_section, E->get().name, preset->get(E->get().name));
1232 		}
1233 	}
1234 
1235 	config->save("res://export_presets.cfg");
1236 }
1237 
save_presets()1238 void EditorExport::save_presets() {
1239 
1240 	if (block_save)
1241 		return;
1242 	save_timer->start();
1243 }
1244 
_bind_methods()1245 void EditorExport::_bind_methods() {
1246 
1247 	ClassDB::bind_method("_save", &EditorExport::_save);
1248 
1249 	ADD_SIGNAL(MethodInfo("export_presets_updated"));
1250 }
1251 
add_export_platform(const Ref<EditorExportPlatform> & p_platform)1252 void EditorExport::add_export_platform(const Ref<EditorExportPlatform> &p_platform) {
1253 
1254 	export_platforms.push_back(p_platform);
1255 }
1256 
get_export_platform_count()1257 int EditorExport::get_export_platform_count() {
1258 
1259 	return export_platforms.size();
1260 }
1261 
get_export_platform(int p_idx)1262 Ref<EditorExportPlatform> EditorExport::get_export_platform(int p_idx) {
1263 
1264 	ERR_FAIL_INDEX_V(p_idx, export_platforms.size(), Ref<EditorExportPlatform>());
1265 
1266 	return export_platforms[p_idx];
1267 }
1268 
add_export_preset(const Ref<EditorExportPreset> & p_preset,int p_at_pos)1269 void EditorExport::add_export_preset(const Ref<EditorExportPreset> &p_preset, int p_at_pos) {
1270 
1271 	if (p_at_pos < 0)
1272 		export_presets.push_back(p_preset);
1273 	else
1274 		export_presets.insert(p_at_pos, p_preset);
1275 }
1276 
test_etc2() const1277 String EditorExportPlatform::test_etc2() const {
1278 
1279 	String driver = ProjectSettings::get_singleton()->get("rendering/quality/driver/driver_name");
1280 	bool driver_fallback = ProjectSettings::get_singleton()->get("rendering/quality/driver/fallback_to_gles2");
1281 	bool etc_supported = ProjectSettings::get_singleton()->get("rendering/vram_compression/import_etc");
1282 	bool etc2_supported = ProjectSettings::get_singleton()->get("rendering/vram_compression/import_etc2");
1283 
1284 	if (driver == "GLES2" && !etc_supported) {
1285 		return TTR("Target platform requires 'ETC' texture compression for GLES2. Enable 'Import Etc' in Project Settings.");
1286 	} else if (driver == "GLES3") {
1287 		String err;
1288 		if (!etc2_supported) {
1289 			err += TTR("Target platform requires 'ETC2' texture compression for GLES3. Enable 'Import Etc 2' in Project Settings.");
1290 		}
1291 		if (driver_fallback && !etc_supported) {
1292 			if (err != String())
1293 				err += "\n";
1294 			err += TTR("Target platform requires 'ETC' texture compression for the driver fallback to GLES2.\nEnable 'Import Etc' in Project Settings, or disable 'Driver Fallback Enabled'.");
1295 		}
1296 		return err;
1297 	}
1298 	return String();
1299 }
1300 
get_export_preset_count() const1301 int EditorExport::get_export_preset_count() const {
1302 
1303 	return export_presets.size();
1304 }
1305 
get_export_preset(int p_idx)1306 Ref<EditorExportPreset> EditorExport::get_export_preset(int p_idx) {
1307 
1308 	ERR_FAIL_INDEX_V(p_idx, export_presets.size(), Ref<EditorExportPreset>());
1309 	return export_presets[p_idx];
1310 }
1311 
remove_export_preset(int p_idx)1312 void EditorExport::remove_export_preset(int p_idx) {
1313 
1314 	export_presets.remove(p_idx);
1315 	save_presets();
1316 }
1317 
add_export_plugin(const Ref<EditorExportPlugin> & p_plugin)1318 void EditorExport::add_export_plugin(const Ref<EditorExportPlugin> &p_plugin) {
1319 
1320 	if (export_plugins.find(p_plugin) == -1) {
1321 		export_plugins.push_back(p_plugin);
1322 	}
1323 }
1324 
remove_export_plugin(const Ref<EditorExportPlugin> & p_plugin)1325 void EditorExport::remove_export_plugin(const Ref<EditorExportPlugin> &p_plugin) {
1326 
1327 	export_plugins.erase(p_plugin);
1328 }
1329 
get_export_plugins()1330 Vector<Ref<EditorExportPlugin> > EditorExport::get_export_plugins() {
1331 
1332 	return export_plugins;
1333 }
1334 
_notification(int p_what)1335 void EditorExport::_notification(int p_what) {
1336 
1337 	switch (p_what) {
1338 		case NOTIFICATION_ENTER_TREE: {
1339 			load_config();
1340 		} break;
1341 		case NOTIFICATION_PROCESS: {
1342 			update_export_presets();
1343 		} break;
1344 	}
1345 }
1346 
load_config()1347 void EditorExport::load_config() {
1348 
1349 	Ref<ConfigFile> config;
1350 	config.instance();
1351 	Error err = config->load("res://export_presets.cfg");
1352 	if (err != OK)
1353 		return;
1354 
1355 	block_save = true;
1356 
1357 	int index = 0;
1358 	while (true) {
1359 
1360 		String section = "preset." + itos(index);
1361 		if (!config->has_section(section))
1362 			break;
1363 
1364 		String platform = config->get_value(section, "platform");
1365 
1366 		Ref<EditorExportPreset> preset;
1367 
1368 		for (int i = 0; i < export_platforms.size(); i++) {
1369 			if (export_platforms[i]->get_name() == platform) {
1370 				preset = export_platforms.write[i]->create_preset();
1371 				break;
1372 			}
1373 		}
1374 
1375 		if (!preset.is_valid()) {
1376 			index++;
1377 			ERR_CONTINUE(!preset.is_valid());
1378 		}
1379 
1380 		preset->set_name(config->get_value(section, "name"));
1381 		preset->set_runnable(config->get_value(section, "runnable"));
1382 
1383 		if (config->has_section_key(section, "custom_features")) {
1384 			preset->set_custom_features(config->get_value(section, "custom_features"));
1385 		}
1386 
1387 		String export_filter = config->get_value(section, "export_filter");
1388 
1389 		bool get_files = false;
1390 
1391 		if (export_filter == "all_resources") {
1392 			preset->set_export_filter(EditorExportPreset::EXPORT_ALL_RESOURCES);
1393 		} else if (export_filter == "scenes") {
1394 			preset->set_export_filter(EditorExportPreset::EXPORT_SELECTED_SCENES);
1395 			get_files = true;
1396 		} else if (export_filter == "resources") {
1397 			preset->set_export_filter(EditorExportPreset::EXPORT_SELECTED_RESOURCES);
1398 			get_files = true;
1399 		}
1400 
1401 		if (get_files) {
1402 
1403 			Vector<String> files = config->get_value(section, "export_files");
1404 
1405 			for (int i = 0; i < files.size(); i++) {
1406 				if (!FileAccess::exists(files[i])) {
1407 					preset->remove_export_file(files[i]);
1408 				} else {
1409 					preset->add_export_file(files[i]);
1410 				}
1411 			}
1412 		}
1413 
1414 		preset->set_include_filter(config->get_value(section, "include_filter"));
1415 		preset->set_exclude_filter(config->get_value(section, "exclude_filter"));
1416 		preset->set_export_path(config->get_value(section, "export_path", ""));
1417 
1418 		Vector<String> patch_list = config->get_value(section, "patch_list");
1419 
1420 		for (int i = 0; i < patch_list.size(); i++) {
1421 			preset->add_patch(patch_list[i]);
1422 		}
1423 
1424 		if (config->has_section_key(section, "script_export_mode")) {
1425 			preset->set_script_export_mode(config->get_value(section, "script_export_mode"));
1426 		}
1427 		if (config->has_section_key(section, "script_encryption_key")) {
1428 			preset->set_script_encryption_key(config->get_value(section, "script_encryption_key"));
1429 		}
1430 
1431 		String option_section = "preset." + itos(index) + ".options";
1432 
1433 		List<String> options;
1434 
1435 		config->get_section_keys(option_section, &options);
1436 
1437 		for (List<String>::Element *E = options.front(); E; E = E->next()) {
1438 
1439 			Variant value = config->get_value(option_section, E->get());
1440 
1441 			preset->set(E->get(), value);
1442 		}
1443 
1444 		add_export_preset(preset);
1445 		index++;
1446 	}
1447 
1448 	block_save = false;
1449 }
1450 
update_export_presets()1451 void EditorExport::update_export_presets() {
1452 	Map<StringName, List<EditorExportPlatform::ExportOption> > platform_options;
1453 
1454 	for (int i = 0; i < export_platforms.size(); i++) {
1455 		Ref<EditorExportPlatform> platform = export_platforms[i];
1456 
1457 		if (platform->should_update_export_options()) {
1458 			List<EditorExportPlatform::ExportOption> options;
1459 			platform->get_export_options(&options);
1460 
1461 			platform_options[platform->get_name()] = options;
1462 		}
1463 	}
1464 
1465 	bool export_presets_updated = false;
1466 	for (int i = 0; i < export_presets.size(); i++) {
1467 		Ref<EditorExportPreset> preset = export_presets[i];
1468 		if (platform_options.has(preset->get_platform()->get_name())) {
1469 			export_presets_updated = true;
1470 
1471 			List<EditorExportPlatform::ExportOption> options = platform_options[preset->get_platform()->get_name()];
1472 
1473 			// Copy the previous preset values
1474 			Map<StringName, Variant> previous_values = preset->values;
1475 
1476 			// Clear the preset properties and values prior to reloading
1477 			preset->properties.clear();
1478 			preset->values.clear();
1479 
1480 			for (List<EditorExportPlatform::ExportOption>::Element *E = options.front(); E; E = E->next()) {
1481 				preset->properties.push_back(E->get().option);
1482 
1483 				StringName option_name = E->get().option.name;
1484 				preset->values[option_name] = previous_values.has(option_name) ? previous_values[option_name] : E->get().default_value;
1485 			}
1486 		}
1487 	}
1488 
1489 	if (export_presets_updated) {
1490 		emit_signal(_export_presets_updated);
1491 	}
1492 }
1493 
poll_export_platforms()1494 bool EditorExport::poll_export_platforms() {
1495 
1496 	bool changed = false;
1497 	for (int i = 0; i < export_platforms.size(); i++) {
1498 		if (export_platforms.write[i]->poll_export()) {
1499 			changed = true;
1500 		}
1501 	}
1502 
1503 	return changed;
1504 }
1505 
EditorExport()1506 EditorExport::EditorExport() {
1507 
1508 	save_timer = memnew(Timer);
1509 	add_child(save_timer);
1510 	save_timer->set_wait_time(0.8);
1511 	save_timer->set_one_shot(true);
1512 	save_timer->connect("timeout", this, "_save");
1513 	block_save = false;
1514 
1515 	_export_presets_updated = "export_presets_updated";
1516 
1517 	singleton = this;
1518 	set_process(true);
1519 }
1520 
~EditorExport()1521 EditorExport::~EditorExport() {
1522 }
1523 
1524 //////////
1525 
get_preset_features(const Ref<EditorExportPreset> & p_preset,List<String> * r_features)1526 void EditorExportPlatformPC::get_preset_features(const Ref<EditorExportPreset> &p_preset, List<String> *r_features) {
1527 
1528 	if (p_preset->get("texture_format/s3tc")) {
1529 		r_features->push_back("s3tc");
1530 	}
1531 	if (p_preset->get("texture_format/etc")) {
1532 		r_features->push_back("etc");
1533 	}
1534 	if (p_preset->get("texture_format/etc2")) {
1535 		r_features->push_back("etc2");
1536 	}
1537 
1538 	if (p_preset->get("binary_format/64_bits")) {
1539 		r_features->push_back("64");
1540 	} else {
1541 		r_features->push_back("32");
1542 	}
1543 }
1544 
get_export_options(List<ExportOption> * r_options)1545 void EditorExportPlatformPC::get_export_options(List<ExportOption> *r_options) {
1546 
1547 	r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "texture_format/bptc"), false));
1548 	r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "texture_format/s3tc"), true));
1549 	r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "texture_format/etc"), false));
1550 	r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "texture_format/etc2"), false));
1551 	r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "texture_format/no_bptc_fallbacks"), true));
1552 	r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "binary_format/64_bits"), true));
1553 	r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "binary_format/embed_pck"), false));
1554 	r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "custom_template/release", PROPERTY_HINT_GLOBAL_FILE), ""));
1555 	r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "custom_template/debug", PROPERTY_HINT_GLOBAL_FILE), ""));
1556 }
1557 
get_name() const1558 String EditorExportPlatformPC::get_name() const {
1559 
1560 	return name;
1561 }
1562 
get_os_name() const1563 String EditorExportPlatformPC::get_os_name() const {
1564 
1565 	return os_name;
1566 }
get_logo() const1567 Ref<Texture> EditorExportPlatformPC::get_logo() const {
1568 
1569 	return logo;
1570 }
1571 
can_export(const Ref<EditorExportPreset> & p_preset,String & r_error,bool & r_missing_templates) const1572 bool EditorExportPlatformPC::can_export(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates) const {
1573 
1574 	String err;
1575 	bool valid = false;
1576 
1577 	// Look for export templates (first official, and if defined custom templates).
1578 
1579 	bool use64 = p_preset->get("binary_format/64_bits");
1580 	bool dvalid = exists_export_template(use64 ? debug_file_64 : debug_file_32, &err);
1581 	bool rvalid = exists_export_template(use64 ? release_file_64 : release_file_32, &err);
1582 
1583 	if (p_preset->get("custom_template/debug") != "") {
1584 		dvalid = FileAccess::exists(p_preset->get("custom_template/debug"));
1585 		if (!dvalid) {
1586 			err += TTR("Custom debug template not found.") + "\n";
1587 		}
1588 	}
1589 	if (p_preset->get("custom_template/release") != "") {
1590 		rvalid = FileAccess::exists(p_preset->get("custom_template/release"));
1591 		if (!rvalid) {
1592 			err += TTR("Custom release template not found.") + "\n";
1593 		}
1594 	}
1595 
1596 	valid = dvalid || rvalid;
1597 	r_missing_templates = !valid;
1598 
1599 	if (!err.empty())
1600 		r_error = err;
1601 	return valid;
1602 }
1603 
get_binary_extensions(const Ref<EditorExportPreset> & p_preset) const1604 List<String> EditorExportPlatformPC::get_binary_extensions(const Ref<EditorExportPreset> &p_preset) const {
1605 	List<String> list;
1606 	for (Map<String, String>::Element *E = extensions.front(); E; E = E->next()) {
1607 		if (p_preset->get(E->key())) {
1608 			list.push_back(extensions[E->key()]);
1609 			return list;
1610 		}
1611 	}
1612 
1613 	if (extensions.has("default")) {
1614 		list.push_back(extensions["default"]);
1615 		return list;
1616 	}
1617 
1618 	return list;
1619 }
1620 
export_project(const Ref<EditorExportPreset> & p_preset,bool p_debug,const String & p_path,int p_flags)1621 Error EditorExportPlatformPC::export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags) {
1622 	ExportNotifier notifier(*this, p_preset, p_debug, p_path, p_flags);
1623 
1624 	if (!DirAccess::exists(p_path.get_base_dir())) {
1625 		return ERR_FILE_BAD_PATH;
1626 	}
1627 
1628 	String custom_debug = p_preset->get("custom_template/debug");
1629 	String custom_release = p_preset->get("custom_template/release");
1630 
1631 	String template_path = p_debug ? custom_debug : custom_release;
1632 
1633 	template_path = template_path.strip_edges();
1634 
1635 	if (template_path == String()) {
1636 
1637 		if (p_preset->get("binary_format/64_bits")) {
1638 			if (p_debug) {
1639 				template_path = find_export_template(debug_file_64);
1640 			} else {
1641 				template_path = find_export_template(release_file_64);
1642 			}
1643 		} else {
1644 			if (p_debug) {
1645 				template_path = find_export_template(debug_file_32);
1646 			} else {
1647 				template_path = find_export_template(release_file_32);
1648 			}
1649 		}
1650 	}
1651 
1652 	if (template_path != String() && !FileAccess::exists(template_path)) {
1653 		EditorNode::get_singleton()->show_warning(TTR("Template file not found:") + "\n" + template_path);
1654 		return ERR_FILE_NOT_FOUND;
1655 	}
1656 
1657 	DirAccess *da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
1658 	Error err = da->copy(template_path, p_path, get_chmod_flags());
1659 	memdelete(da);
1660 
1661 	if (err == OK) {
1662 		String pck_path;
1663 		if (p_preset->get("binary_format/embed_pck")) {
1664 			pck_path = p_path;
1665 		} else {
1666 			pck_path = p_path.get_basename() + ".pck";
1667 		}
1668 
1669 		Vector<SharedObject> so_files;
1670 
1671 		int64_t embedded_pos;
1672 		int64_t embedded_size;
1673 		err = save_pack(p_preset, pck_path, &so_files, p_preset->get("binary_format/embed_pck"), &embedded_pos, &embedded_size);
1674 		if (err == OK && p_preset->get("binary_format/embed_pck")) {
1675 
1676 			if (embedded_size >= 0x100000000 && !p_preset->get("binary_format/64_bits")) {
1677 				EditorNode::get_singleton()->show_warning(TTR("On 32-bit exports the embedded PCK cannot be bigger than 4 GiB."));
1678 				return ERR_INVALID_PARAMETER;
1679 			}
1680 
1681 			FixUpEmbeddedPckFunc fixup_func = get_fixup_embedded_pck_func();
1682 			if (fixup_func) {
1683 				err = fixup_func(p_path, embedded_pos, embedded_size);
1684 			}
1685 		}
1686 
1687 		if (err == OK && !so_files.empty()) {
1688 			//if shared object files, copy them
1689 			da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
1690 			for (int i = 0; i < so_files.size() && err == OK; i++) {
1691 				err = da->copy(so_files[i].path, p_path.get_base_dir().plus_file(so_files[i].path.get_file()));
1692 				if (err == OK) {
1693 					err = sign_shared_object(p_preset, p_debug, p_path.get_base_dir().plus_file(so_files[i].path.get_file()));
1694 				}
1695 			}
1696 			memdelete(da);
1697 		}
1698 	}
1699 
1700 	return err;
1701 }
1702 
sign_shared_object(const Ref<EditorExportPreset> & p_preset,bool p_debug,const String & p_path)1703 Error EditorExportPlatformPC::sign_shared_object(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path) {
1704 	return OK;
1705 }
1706 
set_extension(const String & p_extension,const String & p_feature_key)1707 void EditorExportPlatformPC::set_extension(const String &p_extension, const String &p_feature_key) {
1708 	extensions[p_feature_key] = p_extension;
1709 }
1710 
set_name(const String & p_name)1711 void EditorExportPlatformPC::set_name(const String &p_name) {
1712 	name = p_name;
1713 }
1714 
set_os_name(const String & p_name)1715 void EditorExportPlatformPC::set_os_name(const String &p_name) {
1716 	os_name = p_name;
1717 }
1718 
set_logo(const Ref<Texture> & p_logo)1719 void EditorExportPlatformPC::set_logo(const Ref<Texture> &p_logo) {
1720 	logo = p_logo;
1721 }
1722 
set_release_64(const String & p_file)1723 void EditorExportPlatformPC::set_release_64(const String &p_file) {
1724 
1725 	release_file_64 = p_file;
1726 }
1727 
set_release_32(const String & p_file)1728 void EditorExportPlatformPC::set_release_32(const String &p_file) {
1729 
1730 	release_file_32 = p_file;
1731 }
set_debug_64(const String & p_file)1732 void EditorExportPlatformPC::set_debug_64(const String &p_file) {
1733 
1734 	debug_file_64 = p_file;
1735 }
set_debug_32(const String & p_file)1736 void EditorExportPlatformPC::set_debug_32(const String &p_file) {
1737 
1738 	debug_file_32 = p_file;
1739 }
1740 
add_platform_feature(const String & p_feature)1741 void EditorExportPlatformPC::add_platform_feature(const String &p_feature) {
1742 
1743 	extra_features.insert(p_feature);
1744 }
1745 
get_platform_features(List<String> * r_features)1746 void EditorExportPlatformPC::get_platform_features(List<String> *r_features) {
1747 	r_features->push_back("pc"); //all pcs support "pc"
1748 	r_features->push_back("s3tc"); //all pcs support "s3tc" compression
1749 	r_features->push_back(get_os_name()); //OS name is a feature
1750 	for (Set<String>::Element *E = extra_features.front(); E; E = E->next()) {
1751 		r_features->push_back(E->get());
1752 	}
1753 }
1754 
resolve_platform_feature_priorities(const Ref<EditorExportPreset> & p_preset,Set<String> & p_features)1755 void EditorExportPlatformPC::resolve_platform_feature_priorities(const Ref<EditorExportPreset> &p_preset, Set<String> &p_features) {
1756 
1757 	if (p_features.has("bptc")) {
1758 		if (p_preset->has("texture_format/no_bptc_fallbacks")) {
1759 			p_features.erase("s3tc");
1760 		}
1761 	}
1762 }
1763 
get_chmod_flags() const1764 int EditorExportPlatformPC::get_chmod_flags() const {
1765 
1766 	return chmod_flags;
1767 }
1768 
set_chmod_flags(int p_flags)1769 void EditorExportPlatformPC::set_chmod_flags(int p_flags) {
1770 
1771 	chmod_flags = p_flags;
1772 }
1773 
get_fixup_embedded_pck_func() const1774 EditorExportPlatformPC::FixUpEmbeddedPckFunc EditorExportPlatformPC::get_fixup_embedded_pck_func() const {
1775 
1776 	return fixup_embedded_pck_func;
1777 }
1778 
set_fixup_embedded_pck_func(FixUpEmbeddedPckFunc p_fixup_embedded_pck_func)1779 void EditorExportPlatformPC::set_fixup_embedded_pck_func(FixUpEmbeddedPckFunc p_fixup_embedded_pck_func) {
1780 
1781 	fixup_embedded_pck_func = p_fixup_embedded_pck_func;
1782 }
1783 
EditorExportPlatformPC()1784 EditorExportPlatformPC::EditorExportPlatformPC() {
1785 
1786 	chmod_flags = -1;
1787 	fixup_embedded_pck_func = NULL;
1788 }
1789 
1790 ///////////////////////
1791 
_export_file(const String & p_path,const String & p_type,const Set<String> & p_features)1792 void EditorExportTextSceneToBinaryPlugin::_export_file(const String &p_path, const String &p_type, const Set<String> &p_features) {
1793 
1794 	String extension = p_path.get_extension().to_lower();
1795 	if (extension != "tres" && extension != "tscn") {
1796 		return;
1797 	}
1798 
1799 	bool convert = GLOBAL_GET("editor/convert_text_resources_to_binary_on_export");
1800 	if (!convert)
1801 		return;
1802 	String tmp_path = EditorSettings::get_singleton()->get_cache_dir().plus_file("tmpfile.res");
1803 	Error err = ResourceFormatLoaderText::convert_file_to_binary(p_path, tmp_path);
1804 	if (err != OK) {
1805 		DirAccess::remove_file_or_error(tmp_path);
1806 		ERR_FAIL();
1807 	}
1808 	Vector<uint8_t> data = FileAccess::get_file_as_array(tmp_path);
1809 	if (data.size() == 0) {
1810 		DirAccess::remove_file_or_error(tmp_path);
1811 		ERR_FAIL();
1812 	}
1813 	DirAccess::remove_file_or_error(tmp_path);
1814 	add_file(p_path + ".converted.res", data, true);
1815 }
1816 
EditorExportTextSceneToBinaryPlugin()1817 EditorExportTextSceneToBinaryPlugin::EditorExportTextSceneToBinaryPlugin() {
1818 
1819 	GLOBAL_DEF("editor/convert_text_resources_to_binary_on_export", false);
1820 }
1821