initial commit, 4.5 stable
Some checks failed
🔗 GHA / 📊 Static checks (push) Has been cancelled
🔗 GHA / 🤖 Android (push) Has been cancelled
🔗 GHA / 🍏 iOS (push) Has been cancelled
🔗 GHA / 🐧 Linux (push) Has been cancelled
🔗 GHA / 🍎 macOS (push) Has been cancelled
🔗 GHA / 🏁 Windows (push) Has been cancelled
🔗 GHA / 🌐 Web (push) Has been cancelled
Some checks failed
🔗 GHA / 📊 Static checks (push) Has been cancelled
🔗 GHA / 🤖 Android (push) Has been cancelled
🔗 GHA / 🍏 iOS (push) Has been cancelled
🔗 GHA / 🐧 Linux (push) Has been cancelled
🔗 GHA / 🍎 macOS (push) Has been cancelled
🔗 GHA / 🏁 Windows (push) Has been cancelled
🔗 GHA / 🌐 Web (push) Has been cancelled
This commit is contained in:
6
editor/export/SCsub
Normal file
6
editor/export/SCsub
Normal file
@@ -0,0 +1,6 @@
|
||||
#!/usr/bin/env python
|
||||
from misc.utility.scons_hints import *
|
||||
|
||||
Import("env")
|
||||
|
||||
env.add_source_files(env.editor_sources, "*.cpp")
|
1559
editor/export/codesign.cpp
Normal file
1559
editor/export/codesign.cpp
Normal file
File diff suppressed because it is too large
Load Diff
354
editor/export/codesign.h
Normal file
354
editor/export/codesign.h
Normal file
@@ -0,0 +1,354 @@
|
||||
/**************************************************************************/
|
||||
/* codesign.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
// macOS code signature creation utility.
|
||||
//
|
||||
// Current implementation has the following limitation:
|
||||
// - Only version 11.3.0 signatures are supported.
|
||||
// - Only "framework" and "app" bundle types are supported.
|
||||
// - Page hash array scattering is not supported.
|
||||
// - Reading and writing binary property lists i snot supported (third-party frameworks with binary Info.plist will not work unless .plist is converted to text format).
|
||||
// - Requirements code generator is not implemented (only hard-coded requirements for the ad-hoc signing is supported).
|
||||
// - RFC5652/CMS blob generation is not implemented, supports ad-hoc signing only.
|
||||
|
||||
#include "core/io/file_access.h"
|
||||
#include "core/object/ref_counted.h"
|
||||
|
||||
/*************************************************************************/
|
||||
/* CodeSignCodeResources */
|
||||
/*************************************************************************/
|
||||
|
||||
class CodeSignCodeResources {
|
||||
public:
|
||||
enum class CRMatch {
|
||||
CR_MATCH_NO,
|
||||
CR_MATCH_YES,
|
||||
CR_MATCH_NESTED,
|
||||
CR_MATCH_OPTIONAL,
|
||||
};
|
||||
|
||||
private:
|
||||
struct CRFile {
|
||||
String name;
|
||||
String hash;
|
||||
String hash2;
|
||||
bool optional;
|
||||
bool nested;
|
||||
String requirements;
|
||||
};
|
||||
|
||||
struct CRRule {
|
||||
String file_pattern;
|
||||
String key;
|
||||
int weight;
|
||||
bool store;
|
||||
CRRule() {
|
||||
weight = 1;
|
||||
store = true;
|
||||
}
|
||||
CRRule(const String &p_file_pattern, const String &p_key, int p_weight, bool p_store) {
|
||||
file_pattern = p_file_pattern;
|
||||
key = p_key;
|
||||
weight = p_weight;
|
||||
store = p_store;
|
||||
}
|
||||
};
|
||||
|
||||
Vector<CRRule> rules1;
|
||||
Vector<CRRule> rules2;
|
||||
|
||||
Vector<CRFile> files1;
|
||||
Vector<CRFile> files2;
|
||||
|
||||
String hash_sha1_base64(const String &p_path);
|
||||
String hash_sha256_base64(const String &p_path);
|
||||
|
||||
public:
|
||||
void add_rule1(const String &p_rule, const String &p_key = "", int p_weight = 0, bool p_store = true);
|
||||
void add_rule2(const String &p_rule, const String &p_key = "", int p_weight = 0, bool p_store = true);
|
||||
|
||||
CRMatch match_rules1(const String &p_path) const;
|
||||
CRMatch match_rules2(const String &p_path) const;
|
||||
|
||||
bool add_file1(const String &p_root, const String &p_path);
|
||||
bool add_file2(const String &p_root, const String &p_path);
|
||||
bool add_nested_file(const String &p_root, const String &p_path, const String &p_exepath);
|
||||
|
||||
bool add_folder_recursive(const String &p_root, const String &p_path = "", const String &p_main_exe_path = "");
|
||||
|
||||
bool save_to_file(const String &p_path);
|
||||
};
|
||||
|
||||
/*************************************************************************/
|
||||
/* CodeSignBlob */
|
||||
/*************************************************************************/
|
||||
|
||||
class CodeSignBlob : public RefCounted {
|
||||
GDSOFTCLASS(CodeSignBlob, RefCounted);
|
||||
|
||||
public:
|
||||
virtual PackedByteArray get_hash_sha1() const = 0;
|
||||
virtual PackedByteArray get_hash_sha256() const = 0;
|
||||
|
||||
virtual int get_size() const = 0;
|
||||
virtual uint32_t get_index_type() const = 0;
|
||||
|
||||
virtual void write_to_file(Ref<FileAccess> p_file) const = 0;
|
||||
};
|
||||
|
||||
/*************************************************************************/
|
||||
/* CodeSignRequirements */
|
||||
/*************************************************************************/
|
||||
|
||||
// Note: Proper code generator is not implemented (any we probably won't ever need it), just a hardcoded bytecode for the limited set of cases.
|
||||
|
||||
class CodeSignRequirements : public CodeSignBlob {
|
||||
PackedByteArray blob;
|
||||
|
||||
static inline size_t PAD(size_t s, size_t a) {
|
||||
return (s % a == 0) ? 0 : (a - s % a);
|
||||
}
|
||||
|
||||
_FORCE_INLINE_ void _parse_certificate_slot(uint32_t &r_pos, String &r_out, uint32_t p_rq_size) const;
|
||||
_FORCE_INLINE_ void _parse_key(uint32_t &r_pos, String &r_out, uint32_t p_rq_size) const;
|
||||
_FORCE_INLINE_ void _parse_oid_key(uint32_t &r_pos, String &r_out, uint32_t p_rq_size) const;
|
||||
_FORCE_INLINE_ void _parse_hash_string(uint32_t &r_pos, String &r_out, uint32_t p_rq_size) const;
|
||||
_FORCE_INLINE_ void _parse_value(uint32_t &r_pos, String &r_out, uint32_t p_rq_size) const;
|
||||
_FORCE_INLINE_ void _parse_date(uint32_t &r_pos, String &r_out, uint32_t p_rq_size) const;
|
||||
_FORCE_INLINE_ bool _parse_match(uint32_t &r_pos, String &r_out, uint32_t p_rq_size) const;
|
||||
|
||||
public:
|
||||
CodeSignRequirements();
|
||||
CodeSignRequirements(const PackedByteArray &p_data);
|
||||
|
||||
Vector<String> parse_requirements() const;
|
||||
|
||||
virtual PackedByteArray get_hash_sha1() const override;
|
||||
virtual PackedByteArray get_hash_sha256() const override;
|
||||
|
||||
virtual int get_size() const override;
|
||||
|
||||
virtual uint32_t get_index_type() const override { return 0x00000002; }
|
||||
virtual void write_to_file(Ref<FileAccess> p_file) const override;
|
||||
};
|
||||
|
||||
/*************************************************************************/
|
||||
/* CodeSignEntitlementsText */
|
||||
/*************************************************************************/
|
||||
|
||||
// PList formatted entitlements.
|
||||
|
||||
class CodeSignEntitlementsText : public CodeSignBlob {
|
||||
PackedByteArray blob;
|
||||
|
||||
public:
|
||||
CodeSignEntitlementsText();
|
||||
CodeSignEntitlementsText(const String &p_string);
|
||||
|
||||
virtual PackedByteArray get_hash_sha1() const override;
|
||||
virtual PackedByteArray get_hash_sha256() const override;
|
||||
|
||||
virtual int get_size() const override;
|
||||
|
||||
virtual uint32_t get_index_type() const override { return 0x00000005; }
|
||||
virtual void write_to_file(Ref<FileAccess> p_file) const override;
|
||||
};
|
||||
|
||||
/*************************************************************************/
|
||||
/* CodeSignEntitlementsBinary */
|
||||
/*************************************************************************/
|
||||
|
||||
// ASN.1 serialized entitlements.
|
||||
|
||||
class CodeSignEntitlementsBinary : public CodeSignBlob {
|
||||
PackedByteArray blob;
|
||||
|
||||
public:
|
||||
CodeSignEntitlementsBinary();
|
||||
CodeSignEntitlementsBinary(const String &p_string);
|
||||
|
||||
virtual PackedByteArray get_hash_sha1() const override;
|
||||
virtual PackedByteArray get_hash_sha256() const override;
|
||||
|
||||
virtual int get_size() const override;
|
||||
|
||||
virtual uint32_t get_index_type() const override { return 0x00000007; }
|
||||
virtual void write_to_file(Ref<FileAccess> p_file) const override;
|
||||
};
|
||||
|
||||
/*************************************************************************/
|
||||
/* CodeSignCodeDirectory */
|
||||
/*************************************************************************/
|
||||
|
||||
// Code Directory, runtime options, code segment and special structure hashes.
|
||||
|
||||
class CodeSignCodeDirectory : public CodeSignBlob {
|
||||
public:
|
||||
enum Slot {
|
||||
SLOT_INFO_PLIST = -1,
|
||||
SLOT_REQUIREMENTS = -2,
|
||||
SLOT_RESOURCES = -3,
|
||||
SLOT_APP_SPECIFIC = -4, // Unused.
|
||||
SLOT_ENTITLEMENTS = -5,
|
||||
SLOT_RESERVER1 = -6, // Unused.
|
||||
SLOT_DER_ENTITLEMENTS = -7,
|
||||
};
|
||||
|
||||
enum CodeSignExecSegFlags {
|
||||
EXECSEG_MAIN_BINARY = 0x1,
|
||||
EXECSEG_ALLOW_UNSIGNED = 0x10,
|
||||
EXECSEG_DEBUGGER = 0x20,
|
||||
EXECSEG_JIT = 0x40,
|
||||
EXECSEG_SKIP_LV = 0x80,
|
||||
EXECSEG_CAN_LOAD_CDHASH = 0x100,
|
||||
EXECSEG_CAN_EXEC_CDHASH = 0x200,
|
||||
};
|
||||
|
||||
enum CodeSignatureFlags {
|
||||
SIGNATURE_HOST = 0x0001,
|
||||
SIGNATURE_ADHOC = 0x0002,
|
||||
SIGNATURE_TASK_ALLOW = 0x0004,
|
||||
SIGNATURE_INSTALLER = 0x0008,
|
||||
SIGNATURE_FORCED_LV = 0x0010,
|
||||
SIGNATURE_INVALID_ALLOWED = 0x0020,
|
||||
SIGNATURE_FORCE_HARD = 0x0100,
|
||||
SIGNATURE_FORCE_KILL = 0x0200,
|
||||
SIGNATURE_FORCE_EXPIRATION = 0x0400,
|
||||
SIGNATURE_RESTRICT = 0x0800,
|
||||
SIGNATURE_ENFORCEMENT = 0x1000,
|
||||
SIGNATURE_LIBRARY_VALIDATION = 0x2000,
|
||||
SIGNATURE_ENTITLEMENTS_VALIDATED = 0x4000,
|
||||
SIGNATURE_NVRAM_UNRESTRICTED = 0x8000,
|
||||
SIGNATURE_RUNTIME = 0x10000,
|
||||
SIGNATURE_LINKER_SIGNED = 0x20000,
|
||||
};
|
||||
|
||||
private:
|
||||
PackedByteArray blob;
|
||||
|
||||
struct CodeDirectoryHeader {
|
||||
uint32_t version; // Using version 0x0020500.
|
||||
uint32_t flags; // // Option flags.
|
||||
uint32_t hash_offset; // Slot zero offset.
|
||||
uint32_t ident_offset; // Identifier string offset.
|
||||
uint32_t special_slots; // Nr. of slots with negative index.
|
||||
uint32_t code_slots; // Nr. of slots with index >= 0, (code_limit / page_size).
|
||||
uint32_t code_limit; // Everything before code signature load command offset.
|
||||
uint8_t hash_size; // 20 (SHA-1) or 32 (SHA-256).
|
||||
uint8_t hash_type; // 1 (SHA-1) or 2 (SHA-256).
|
||||
uint8_t platform; // Not used.
|
||||
uint8_t page_size; // Page size, power of two, 2^12 (4096).
|
||||
uint32_t spare2; // Not used.
|
||||
// Version 0x20100
|
||||
uint32_t scatter_vector_offset; // Set to 0 and ignore.
|
||||
// Version 0x20200
|
||||
uint32_t team_offset; // Team id string offset.
|
||||
// Version 0x20300
|
||||
uint32_t spare3; // Not used.
|
||||
uint64_t code_limit_64; // Set to 0 and ignore.
|
||||
// Version 0x20400
|
||||
uint64_t exec_seg_base; // Start of the signed code segment.
|
||||
uint64_t exec_seg_limit; // Code segment (__TEXT) vmsize.
|
||||
uint64_t exec_seg_flags; // Executable segment flags.
|
||||
// Version 0x20500
|
||||
uint32_t runtime; // Runtime version.
|
||||
uint32_t pre_encrypt_offset; // Set to 0 and ignore.
|
||||
};
|
||||
|
||||
int32_t pages = 0;
|
||||
int32_t remain = 0;
|
||||
int32_t code_slots = 0;
|
||||
int32_t special_slots = 0;
|
||||
|
||||
public:
|
||||
CodeSignCodeDirectory();
|
||||
CodeSignCodeDirectory(uint8_t p_hash_size, uint8_t p_hash_type, bool p_main, const CharString &p_id, const CharString &p_team_id, uint32_t p_page_size, uint64_t p_exe_limit, uint64_t p_code_limit);
|
||||
|
||||
int32_t get_page_count();
|
||||
int32_t get_page_remainder();
|
||||
|
||||
bool set_hash_in_slot(const PackedByteArray &p_hash, int p_slot);
|
||||
|
||||
virtual PackedByteArray get_hash_sha1() const override;
|
||||
virtual PackedByteArray get_hash_sha256() const override;
|
||||
|
||||
virtual int get_size() const override;
|
||||
virtual uint32_t get_index_type() const override { return 0x00000000; }
|
||||
|
||||
virtual void write_to_file(Ref<FileAccess> p_file) const override;
|
||||
};
|
||||
|
||||
/*************************************************************************/
|
||||
/* CodeSignSignature */
|
||||
/*************************************************************************/
|
||||
|
||||
class CodeSignSignature : public CodeSignBlob {
|
||||
PackedByteArray blob;
|
||||
|
||||
public:
|
||||
CodeSignSignature();
|
||||
|
||||
virtual PackedByteArray get_hash_sha1() const override;
|
||||
virtual PackedByteArray get_hash_sha256() const override;
|
||||
|
||||
virtual int get_size() const override;
|
||||
virtual uint32_t get_index_type() const override { return 0x00010000; }
|
||||
|
||||
virtual void write_to_file(Ref<FileAccess> p_file) const override;
|
||||
};
|
||||
|
||||
/*************************************************************************/
|
||||
/* CodeSignSuperBlob */
|
||||
/*************************************************************************/
|
||||
|
||||
class CodeSignSuperBlob {
|
||||
Vector<Ref<CodeSignBlob>> blobs;
|
||||
|
||||
public:
|
||||
bool add_blob(const Ref<CodeSignBlob> &p_blob);
|
||||
|
||||
int get_size() const;
|
||||
void write_to_file(Ref<FileAccess> p_file) const;
|
||||
};
|
||||
|
||||
/*************************************************************************/
|
||||
/* CodeSign */
|
||||
/*************************************************************************/
|
||||
|
||||
class CodeSign {
|
||||
static PackedByteArray file_hash_sha1(const String &p_path);
|
||||
static PackedByteArray file_hash_sha256(const String &p_path);
|
||||
static Error _codesign_file(bool p_use_hardened_runtime, bool p_force, const String &p_info, const String &p_exe_path, const String &p_bundle_path, const String &p_ent_path, bool p_ios_bundle, String &r_error_msg);
|
||||
|
||||
public:
|
||||
static Error codesign(bool p_use_hardened_runtime, bool p_force, const String &p_path, const String &p_ent_path, String &r_error_msg);
|
||||
};
|
139
editor/export/dedicated_server_export_plugin.cpp
Normal file
139
editor/export/dedicated_server_export_plugin.cpp
Normal file
@@ -0,0 +1,139 @@
|
||||
/**************************************************************************/
|
||||
/* dedicated_server_export_plugin.cpp */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#include "dedicated_server_export_plugin.h"
|
||||
|
||||
EditorExportPreset::FileExportMode DedicatedServerExportPlugin::_get_export_mode_for_path(const String &p_path) {
|
||||
Ref<EditorExportPreset> preset = get_export_preset();
|
||||
ERR_FAIL_COND_V(preset.is_null(), EditorExportPreset::MODE_FILE_NOT_CUSTOMIZED);
|
||||
|
||||
EditorExportPreset::FileExportMode mode = preset->get_file_export_mode(p_path);
|
||||
if (mode != EditorExportPreset::MODE_FILE_NOT_CUSTOMIZED) {
|
||||
return mode;
|
||||
}
|
||||
|
||||
String path = p_path;
|
||||
if (path.begins_with("res://")) {
|
||||
path = path.substr(6);
|
||||
}
|
||||
|
||||
Vector<String> parts = path.split("/");
|
||||
|
||||
while (parts.size() > 0) {
|
||||
parts.resize(parts.size() - 1);
|
||||
|
||||
String test_path = "res://";
|
||||
if (parts.size() > 0) {
|
||||
test_path += String("/").join(parts) + "/";
|
||||
}
|
||||
|
||||
mode = preset->get_file_export_mode(test_path);
|
||||
if (mode != EditorExportPreset::MODE_FILE_NOT_CUSTOMIZED) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return mode;
|
||||
}
|
||||
|
||||
PackedStringArray DedicatedServerExportPlugin::_get_export_features(const Ref<EditorExportPlatform> &p_platform, bool p_debug) const {
|
||||
PackedStringArray ret;
|
||||
|
||||
Ref<EditorExportPreset> preset = get_export_preset();
|
||||
ERR_FAIL_COND_V(preset.is_null(), ret);
|
||||
|
||||
if (preset->is_dedicated_server()) {
|
||||
ret.append("dedicated_server");
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
uint64_t DedicatedServerExportPlugin::_get_customization_configuration_hash() const {
|
||||
Ref<EditorExportPreset> preset = get_export_preset();
|
||||
ERR_FAIL_COND_V(preset.is_null(), 0);
|
||||
|
||||
if (preset->get_export_filter() != EditorExportPreset::EXPORT_CUSTOMIZED) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return preset->get_customized_files().hash();
|
||||
}
|
||||
|
||||
bool DedicatedServerExportPlugin::_begin_customize_scenes(const Ref<EditorExportPlatform> &p_platform, const Vector<String> &p_features) {
|
||||
Ref<EditorExportPreset> preset = get_export_preset();
|
||||
ERR_FAIL_COND_V(preset.is_null(), false);
|
||||
|
||||
current_export_mode = EditorExportPreset::MODE_FILE_NOT_CUSTOMIZED;
|
||||
|
||||
return preset->get_export_filter() == EditorExportPreset::EXPORT_CUSTOMIZED;
|
||||
}
|
||||
|
||||
bool DedicatedServerExportPlugin::_begin_customize_resources(const Ref<EditorExportPlatform> &p_platform, const Vector<String> &p_features) {
|
||||
Ref<EditorExportPreset> preset = get_export_preset();
|
||||
ERR_FAIL_COND_V(preset.is_null(), false);
|
||||
|
||||
current_export_mode = EditorExportPreset::MODE_FILE_NOT_CUSTOMIZED;
|
||||
|
||||
return preset->get_export_filter() == EditorExportPreset::EXPORT_CUSTOMIZED;
|
||||
}
|
||||
|
||||
Node *DedicatedServerExportPlugin::_customize_scene(Node *p_root, const String &p_path) {
|
||||
// Simply set the export mode based on the scene path. All the real
|
||||
// customization happens in _customize_resource().
|
||||
current_export_mode = _get_export_mode_for_path(p_path);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Ref<Resource> DedicatedServerExportPlugin::_customize_resource(const Ref<Resource> &p_resource, const String &p_path) {
|
||||
// If the resource has a path, we use that to get our export mode. But if it
|
||||
// doesn't, we assume that this resource is embedded in the last resource with
|
||||
// a path.
|
||||
if (p_path != "") {
|
||||
current_export_mode = _get_export_mode_for_path(p_path);
|
||||
}
|
||||
|
||||
if (p_resource.is_valid() && current_export_mode == EditorExportPreset::MODE_FILE_STRIP && p_resource->has_method("create_placeholder")) {
|
||||
Callable::CallError err;
|
||||
Ref<Resource> result = p_resource->callp("create_placeholder", nullptr, 0, err);
|
||||
if (err.error == Callable::CallError::CALL_OK) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
return Ref<Resource>();
|
||||
}
|
||||
|
||||
void DedicatedServerExportPlugin::_end_customize_scenes() {
|
||||
current_export_mode = EditorExportPreset::MODE_FILE_NOT_CUSTOMIZED;
|
||||
}
|
||||
|
||||
void DedicatedServerExportPlugin::_end_customize_resources() {
|
||||
current_export_mode = EditorExportPreset::MODE_FILE_NOT_CUSTOMIZED;
|
||||
}
|
55
editor/export/dedicated_server_export_plugin.h
Normal file
55
editor/export/dedicated_server_export_plugin.h
Normal file
@@ -0,0 +1,55 @@
|
||||
/**************************************************************************/
|
||||
/* dedicated_server_export_plugin.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "editor/export/editor_export_plugin.h"
|
||||
|
||||
class DedicatedServerExportPlugin : public EditorExportPlugin {
|
||||
private:
|
||||
EditorExportPreset::FileExportMode current_export_mode;
|
||||
|
||||
EditorExportPreset::FileExportMode _get_export_mode_for_path(const String &p_path);
|
||||
|
||||
protected:
|
||||
String get_name() const override { return "DedicatedServer"; }
|
||||
|
||||
PackedStringArray _get_export_features(const Ref<EditorExportPlatform> &p_platform, bool p_debug) const override;
|
||||
uint64_t _get_customization_configuration_hash() const override;
|
||||
|
||||
bool _begin_customize_scenes(const Ref<EditorExportPlatform> &p_platform, const Vector<String> &p_features) override;
|
||||
bool _begin_customize_resources(const Ref<EditorExportPlatform> &p_platform, const Vector<String> &p_features) override;
|
||||
|
||||
Node *_customize_scene(Node *p_root, const String &p_path) override;
|
||||
Ref<Resource> _customize_resource(const Ref<Resource> &p_resource, const String &p_path) override;
|
||||
|
||||
void _end_customize_scenes() override;
|
||||
void _end_customize_resources() override;
|
||||
};
|
453
editor/export/editor_export.cpp
Normal file
453
editor/export/editor_export.cpp
Normal file
@@ -0,0 +1,453 @@
|
||||
/**************************************************************************/
|
||||
/* editor_export.cpp */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#include "editor_export.h"
|
||||
|
||||
#include "core/config/project_settings.h"
|
||||
#include "core/io/config_file.h"
|
||||
#include "editor/settings/editor_settings.h"
|
||||
|
||||
EditorExport *EditorExport::singleton = nullptr;
|
||||
|
||||
void EditorExport::_save() {
|
||||
Ref<ConfigFile> config;
|
||||
Ref<ConfigFile> credentials;
|
||||
config.instantiate();
|
||||
credentials.instantiate();
|
||||
for (int i = 0; i < export_presets.size(); i++) {
|
||||
Ref<EditorExportPreset> preset = export_presets[i];
|
||||
String section = "preset." + itos(i);
|
||||
|
||||
config->set_value(section, "name", preset->get_name());
|
||||
config->set_value(section, "platform", preset->get_platform()->get_name());
|
||||
config->set_value(section, "runnable", preset->is_runnable());
|
||||
config->set_value(section, "advanced_options", preset->are_advanced_options_enabled());
|
||||
config->set_value(section, "dedicated_server", preset->is_dedicated_server());
|
||||
config->set_value(section, "custom_features", preset->get_custom_features());
|
||||
|
||||
bool save_files = false;
|
||||
switch (preset->get_export_filter()) {
|
||||
case EditorExportPreset::EXPORT_ALL_RESOURCES: {
|
||||
config->set_value(section, "export_filter", "all_resources");
|
||||
} break;
|
||||
case EditorExportPreset::EXPORT_SELECTED_SCENES: {
|
||||
config->set_value(section, "export_filter", "scenes");
|
||||
save_files = true;
|
||||
} break;
|
||||
case EditorExportPreset::EXPORT_SELECTED_RESOURCES: {
|
||||
config->set_value(section, "export_filter", "resources");
|
||||
save_files = true;
|
||||
} break;
|
||||
case EditorExportPreset::EXCLUDE_SELECTED_RESOURCES: {
|
||||
config->set_value(section, "export_filter", "exclude");
|
||||
save_files = true;
|
||||
} break;
|
||||
case EditorExportPreset::EXPORT_CUSTOMIZED: {
|
||||
config->set_value(section, "export_filter", "customized");
|
||||
config->set_value(section, "customized_files", preset->get_customized_files());
|
||||
save_files = false;
|
||||
};
|
||||
}
|
||||
|
||||
if (save_files) {
|
||||
Vector<String> export_files = preset->get_files_to_export();
|
||||
config->set_value(section, "export_files", export_files);
|
||||
}
|
||||
config->set_value(section, "include_filter", preset->get_include_filter());
|
||||
config->set_value(section, "exclude_filter", preset->get_exclude_filter());
|
||||
config->set_value(section, "export_path", preset->get_export_path());
|
||||
config->set_value(section, "patches", preset->get_patches());
|
||||
|
||||
config->set_value(section, "encryption_include_filters", preset->get_enc_in_filter());
|
||||
config->set_value(section, "encryption_exclude_filters", preset->get_enc_ex_filter());
|
||||
config->set_value(section, "seed", preset->get_seed());
|
||||
|
||||
config->set_value(section, "encrypt_pck", preset->get_enc_pck());
|
||||
config->set_value(section, "encrypt_directory", preset->get_enc_directory());
|
||||
config->set_value(section, "script_export_mode", preset->get_script_export_mode());
|
||||
credentials->set_value(section, "script_encryption_key", preset->get_script_encryption_key());
|
||||
|
||||
String option_section = "preset." + itos(i) + ".options";
|
||||
|
||||
for (const KeyValue<StringName, Variant> &E : preset->values) {
|
||||
PropertyInfo *prop = preset->properties.getptr(E.key);
|
||||
if (prop && prop->usage & PROPERTY_USAGE_SECRET) {
|
||||
credentials->set_value(option_section, E.key, E.value);
|
||||
} else {
|
||||
config->set_value(option_section, E.key, E.value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
config->save("res://export_presets.cfg");
|
||||
credentials->save("res://.godot/export_credentials.cfg");
|
||||
}
|
||||
|
||||
void EditorExport::save_presets() {
|
||||
if (block_save) {
|
||||
return;
|
||||
}
|
||||
save_timer->start();
|
||||
}
|
||||
|
||||
void EditorExport::emit_presets_runnable_changed() {
|
||||
emit_signal(_export_presets_runnable_updated);
|
||||
}
|
||||
|
||||
void EditorExport::_bind_methods() {
|
||||
ADD_SIGNAL(MethodInfo(_export_presets_updated));
|
||||
ADD_SIGNAL(MethodInfo(_export_presets_runnable_updated));
|
||||
}
|
||||
|
||||
void EditorExport::add_export_platform(const Ref<EditorExportPlatform> &p_platform) {
|
||||
export_platforms.push_back(p_platform);
|
||||
|
||||
should_update_presets = true;
|
||||
should_reload_presets = true;
|
||||
}
|
||||
|
||||
void EditorExport::remove_export_platform(const Ref<EditorExportPlatform> &p_platform) {
|
||||
export_platforms.erase(p_platform);
|
||||
p_platform->cleanup();
|
||||
|
||||
should_update_presets = true;
|
||||
should_reload_presets = true;
|
||||
}
|
||||
|
||||
int EditorExport::get_export_platform_count() {
|
||||
return export_platforms.size();
|
||||
}
|
||||
|
||||
Ref<EditorExportPlatform> EditorExport::get_export_platform(int p_idx) {
|
||||
ERR_FAIL_INDEX_V(p_idx, export_platforms.size(), Ref<EditorExportPlatform>());
|
||||
|
||||
return export_platforms[p_idx];
|
||||
}
|
||||
|
||||
void EditorExport::add_export_preset(const Ref<EditorExportPreset> &p_preset, int p_at_pos) {
|
||||
if (p_at_pos < 0) {
|
||||
export_presets.push_back(p_preset);
|
||||
} else {
|
||||
export_presets.insert(p_at_pos, p_preset);
|
||||
}
|
||||
emit_presets_runnable_changed();
|
||||
}
|
||||
|
||||
int EditorExport::get_export_preset_count() const {
|
||||
return export_presets.size();
|
||||
}
|
||||
|
||||
Ref<EditorExportPreset> EditorExport::get_export_preset(int p_idx) {
|
||||
ERR_FAIL_INDEX_V(p_idx, export_presets.size(), Ref<EditorExportPreset>());
|
||||
return export_presets[p_idx];
|
||||
}
|
||||
|
||||
void EditorExport::remove_export_preset(int p_idx) {
|
||||
export_presets.remove_at(p_idx);
|
||||
save_presets();
|
||||
emit_presets_runnable_changed();
|
||||
}
|
||||
|
||||
void EditorExport::add_export_plugin(const Ref<EditorExportPlugin> &p_plugin) {
|
||||
if (!export_plugins.has(p_plugin)) {
|
||||
export_plugins.push_back(p_plugin);
|
||||
should_update_presets = true;
|
||||
}
|
||||
}
|
||||
|
||||
void EditorExport::remove_export_plugin(const Ref<EditorExportPlugin> &p_plugin) {
|
||||
export_plugins.erase(p_plugin);
|
||||
should_update_presets = true;
|
||||
}
|
||||
|
||||
Vector<Ref<EditorExportPlugin>> EditorExport::get_export_plugins() {
|
||||
return export_plugins;
|
||||
}
|
||||
|
||||
void EditorExport::_notification(int p_what) {
|
||||
switch (p_what) {
|
||||
case NOTIFICATION_ENTER_TREE: {
|
||||
load_config();
|
||||
} break;
|
||||
|
||||
case NOTIFICATION_PROCESS: {
|
||||
update_export_presets();
|
||||
} break;
|
||||
|
||||
case NOTIFICATION_EXIT_TREE: {
|
||||
for (int i = 0; i < export_platforms.size(); i++) {
|
||||
export_platforms.write[i]->cleanup();
|
||||
}
|
||||
} break;
|
||||
|
||||
case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: {
|
||||
for (int i = 0; i < export_platforms.size(); i++) {
|
||||
export_platforms.write[i]->notification(p_what);
|
||||
}
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
void EditorExport::load_config() {
|
||||
Ref<ConfigFile> config;
|
||||
config.instantiate();
|
||||
Error err = config->load("res://export_presets.cfg");
|
||||
if (err != OK) {
|
||||
return;
|
||||
}
|
||||
|
||||
Ref<ConfigFile> credentials;
|
||||
credentials.instantiate();
|
||||
err = credentials->load("res://.godot/export_credentials.cfg");
|
||||
if (!(err == OK || err == ERR_FILE_NOT_FOUND)) {
|
||||
return;
|
||||
}
|
||||
|
||||
block_save = true;
|
||||
|
||||
int index = 0;
|
||||
while (true) {
|
||||
String section = "preset." + itos(index);
|
||||
if (!config->has_section(section)) {
|
||||
break;
|
||||
}
|
||||
|
||||
String platform = config->get_value(section, "platform");
|
||||
#ifndef DISABLE_DEPRECATED
|
||||
// Compatibility with Linux platform before 4.3.
|
||||
if (platform == "Linux/X11") {
|
||||
platform = "Linux";
|
||||
}
|
||||
#endif
|
||||
|
||||
Ref<EditorExportPreset> preset;
|
||||
|
||||
for (int i = 0; i < export_platforms.size(); i++) {
|
||||
if (export_platforms[i]->get_name() == platform) {
|
||||
preset = export_platforms.write[i]->create_preset();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (preset.is_null()) {
|
||||
index++;
|
||||
continue; // Unknown platform, skip without error (platform might be loaded later).
|
||||
}
|
||||
|
||||
preset->set_name(config->get_value(section, "name"));
|
||||
preset->set_advanced_options_enabled(config->get_value(section, "advanced_options", false));
|
||||
preset->set_runnable(config->get_value(section, "runnable"));
|
||||
preset->set_dedicated_server(config->get_value(section, "dedicated_server", false));
|
||||
|
||||
if (config->has_section_key(section, "custom_features")) {
|
||||
preset->set_custom_features(config->get_value(section, "custom_features"));
|
||||
}
|
||||
|
||||
String export_filter = config->get_value(section, "export_filter");
|
||||
|
||||
bool get_files = false;
|
||||
|
||||
if (export_filter == "all_resources") {
|
||||
preset->set_export_filter(EditorExportPreset::EXPORT_ALL_RESOURCES);
|
||||
} else if (export_filter == "scenes") {
|
||||
preset->set_export_filter(EditorExportPreset::EXPORT_SELECTED_SCENES);
|
||||
get_files = true;
|
||||
} else if (export_filter == "resources") {
|
||||
preset->set_export_filter(EditorExportPreset::EXPORT_SELECTED_RESOURCES);
|
||||
get_files = true;
|
||||
} else if (export_filter == "exclude") {
|
||||
preset->set_export_filter(EditorExportPreset::EXCLUDE_SELECTED_RESOURCES);
|
||||
get_files = true;
|
||||
} else if (export_filter == "customized") {
|
||||
preset->set_export_filter(EditorExportPreset::EXPORT_CUSTOMIZED);
|
||||
preset->set_customized_files(config->get_value(section, "customized_files", Dictionary()));
|
||||
get_files = false;
|
||||
}
|
||||
|
||||
if (get_files) {
|
||||
Vector<String> files = config->get_value(section, "export_files");
|
||||
|
||||
for (int i = 0; i < files.size(); i++) {
|
||||
if (!FileAccess::exists(files[i])) {
|
||||
preset->remove_export_file(files[i]);
|
||||
} else {
|
||||
preset->add_export_file(files[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
preset->set_include_filter(config->get_value(section, "include_filter"));
|
||||
preset->set_exclude_filter(config->get_value(section, "exclude_filter"));
|
||||
preset->set_export_path(config->get_value(section, "export_path", ""));
|
||||
preset->set_script_export_mode(config->get_value(section, "script_export_mode", EditorExportPreset::MODE_SCRIPT_BINARY_TOKENS_COMPRESSED));
|
||||
preset->set_patches(config->get_value(section, "patches", Vector<String>()));
|
||||
|
||||
if (config->has_section_key(section, "seed")) {
|
||||
preset->set_seed(config->get_value(section, "seed"));
|
||||
}
|
||||
if (config->has_section_key(section, "encrypt_pck")) {
|
||||
preset->set_enc_pck(config->get_value(section, "encrypt_pck"));
|
||||
}
|
||||
if (config->has_section_key(section, "encrypt_directory")) {
|
||||
preset->set_enc_directory(config->get_value(section, "encrypt_directory"));
|
||||
}
|
||||
if (config->has_section_key(section, "encryption_include_filters")) {
|
||||
preset->set_enc_in_filter(config->get_value(section, "encryption_include_filters"));
|
||||
}
|
||||
if (config->has_section_key(section, "encryption_exclude_filters")) {
|
||||
preset->set_enc_ex_filter(config->get_value(section, "encryption_exclude_filters"));
|
||||
}
|
||||
if (credentials->has_section_key(section, "script_encryption_key")) {
|
||||
preset->set_script_encryption_key(credentials->get_value(section, "script_encryption_key"));
|
||||
}
|
||||
|
||||
String option_section = "preset." + itos(index) + ".options";
|
||||
|
||||
Vector<String> options = config->get_section_keys(option_section);
|
||||
|
||||
for (const String &E : options) {
|
||||
Variant value = config->get_value(option_section, E);
|
||||
preset->set(E, value);
|
||||
}
|
||||
|
||||
if (credentials->has_section(option_section)) {
|
||||
options = credentials->get_section_keys(option_section);
|
||||
|
||||
for (const String &E : options) {
|
||||
// Drop values for secret properties that no longer exist, or during the next save they would end up in the regular config file.
|
||||
if (preset->get_properties().has(E)) {
|
||||
Variant value = credentials->get_value(option_section, E);
|
||||
preset->set(E, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
add_export_preset(preset);
|
||||
index++;
|
||||
}
|
||||
|
||||
block_save = false;
|
||||
}
|
||||
|
||||
void EditorExport::update_export_presets() {
|
||||
HashMap<StringName, List<EditorExportPlatform::ExportOption>> platform_options;
|
||||
|
||||
if (should_reload_presets) {
|
||||
should_reload_presets = false;
|
||||
export_presets.clear();
|
||||
load_config();
|
||||
}
|
||||
|
||||
for (int i = 0; i < export_platforms.size(); i++) {
|
||||
Ref<EditorExportPlatform> platform = export_platforms[i];
|
||||
|
||||
bool should_update = should_update_presets;
|
||||
should_update |= platform->should_update_export_options();
|
||||
for (int j = 0; j < export_plugins.size(); j++) {
|
||||
should_update |= export_plugins.write[j]->_should_update_export_options(platform);
|
||||
}
|
||||
|
||||
if (should_update) {
|
||||
List<EditorExportPlatform::ExportOption> options;
|
||||
platform->get_export_options(&options);
|
||||
|
||||
for (int j = 0; j < export_plugins.size(); j++) {
|
||||
export_plugins[j]->_get_export_options(platform, &options);
|
||||
}
|
||||
|
||||
platform_options[platform->get_name()] = options;
|
||||
}
|
||||
}
|
||||
should_update_presets = false;
|
||||
|
||||
bool export_presets_updated = false;
|
||||
for (int i = 0; i < export_presets.size(); i++) {
|
||||
Ref<EditorExportPreset> preset = export_presets[i];
|
||||
if (platform_options.has(preset->get_platform()->get_name())) {
|
||||
export_presets_updated = true;
|
||||
|
||||
bool update_value_overrides = false;
|
||||
List<EditorExportPlatform::ExportOption> options = platform_options[preset->get_platform()->get_name()];
|
||||
|
||||
// Clear the preset properties prior to reloading, keep the values to preserve options from plugins that may be currently disabled.
|
||||
preset->properties.clear();
|
||||
preset->update_visibility.clear();
|
||||
|
||||
for (const EditorExportPlatform::ExportOption &E : options) {
|
||||
StringName option_name = E.option.name;
|
||||
preset->properties[option_name] = E.option;
|
||||
if (!preset->has(option_name)) {
|
||||
preset->values[option_name] = E.default_value;
|
||||
}
|
||||
preset->update_visibility[option_name] = E.update_visibility;
|
||||
if (E.update_visibility) {
|
||||
update_value_overrides = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (update_value_overrides) {
|
||||
preset->update_value_overrides();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (export_presets_updated) {
|
||||
emit_signal(_export_presets_updated);
|
||||
}
|
||||
}
|
||||
|
||||
bool EditorExport::poll_export_platforms() {
|
||||
bool changed = false;
|
||||
for (int i = 0; i < export_platforms.size(); i++) {
|
||||
if (export_platforms.write[i]->poll_export()) {
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
return changed;
|
||||
}
|
||||
|
||||
void EditorExport::connect_presets_runnable_updated(const Callable &p_target) {
|
||||
connect(_export_presets_runnable_updated, p_target);
|
||||
}
|
||||
|
||||
EditorExport::EditorExport() {
|
||||
save_timer = memnew(Timer);
|
||||
add_child(save_timer);
|
||||
save_timer->set_wait_time(0.8);
|
||||
save_timer->set_one_shot(true);
|
||||
save_timer->connect("timeout", callable_mp(this, &EditorExport::_save));
|
||||
|
||||
_export_presets_updated = StringName("export_presets_updated", true);
|
||||
_export_presets_runnable_updated = StringName("export_presets_runnable_updated", true);
|
||||
|
||||
singleton = this;
|
||||
set_process(true);
|
||||
}
|
86
editor/export/editor_export.h
Normal file
86
editor/export/editor_export.h
Normal file
@@ -0,0 +1,86 @@
|
||||
/**************************************************************************/
|
||||
/* editor_export.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "editor_export_platform.h"
|
||||
#include "editor_export_plugin.h"
|
||||
|
||||
class EditorExport : public Node {
|
||||
GDCLASS(EditorExport, Node);
|
||||
|
||||
Vector<Ref<EditorExportPlatform>> export_platforms;
|
||||
Vector<Ref<EditorExportPreset>> export_presets;
|
||||
Vector<Ref<EditorExportPlugin>> export_plugins;
|
||||
|
||||
static inline StringName _export_presets_updated;
|
||||
static inline StringName _export_presets_runnable_updated;
|
||||
|
||||
Timer *save_timer = nullptr;
|
||||
bool block_save = false;
|
||||
bool should_update_presets = false;
|
||||
bool should_reload_presets = false;
|
||||
|
||||
static EditorExport *singleton;
|
||||
|
||||
void _save();
|
||||
|
||||
protected:
|
||||
friend class EditorExportPreset;
|
||||
void save_presets();
|
||||
void emit_presets_runnable_changed();
|
||||
|
||||
void _notification(int p_what);
|
||||
static void _bind_methods();
|
||||
|
||||
public:
|
||||
static EditorExport *get_singleton() { return singleton; }
|
||||
|
||||
void add_export_platform(const Ref<EditorExportPlatform> &p_platform);
|
||||
int get_export_platform_count();
|
||||
Ref<EditorExportPlatform> get_export_platform(int p_idx);
|
||||
void remove_export_platform(const Ref<EditorExportPlatform> &p_platform);
|
||||
|
||||
void add_export_preset(const Ref<EditorExportPreset> &p_preset, int p_at_pos = -1);
|
||||
int get_export_preset_count() const;
|
||||
Ref<EditorExportPreset> get_export_preset(int p_idx);
|
||||
void remove_export_preset(int p_idx);
|
||||
|
||||
void add_export_plugin(const Ref<EditorExportPlugin> &p_plugin);
|
||||
void remove_export_plugin(const Ref<EditorExportPlugin> &p_plugin);
|
||||
Vector<Ref<EditorExportPlugin>> get_export_plugins();
|
||||
|
||||
void load_config();
|
||||
void update_export_presets();
|
||||
bool poll_export_platforms();
|
||||
void connect_presets_runnable_updated(const Callable &p_target);
|
||||
|
||||
EditorExport();
|
||||
};
|
41
editor/export/editor_export_platform.compat.inc
Normal file
41
editor/export/editor_export_platform.compat.inc
Normal file
@@ -0,0 +1,41 @@
|
||||
/**************************************************************************/
|
||||
/* editor_export_platform.compat.inc */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef DISABLE_DEPRECATED
|
||||
|
||||
Vector<String> EditorExportPlatform::_get_forced_export_files_bind_compat_71542() {
|
||||
return get_forced_export_files(Ref<EditorExportPreset>());
|
||||
}
|
||||
|
||||
void EditorExportPlatform::_bind_compatibility_methods() {
|
||||
ClassDB::bind_compatibility_static_method("EditorExportPlatform", D_METHOD("get_forced_export_files"), &EditorExportPlatform::_get_forced_export_files_bind_compat_71542);
|
||||
}
|
||||
|
||||
#endif
|
2549
editor/export/editor_export_platform.cpp
Normal file
2549
editor/export/editor_export_platform.cpp
Normal file
File diff suppressed because it is too large
Load Diff
355
editor/export/editor_export_platform.h
Normal file
355
editor/export/editor_export_platform.h
Normal file
@@ -0,0 +1,355 @@
|
||||
/**************************************************************************/
|
||||
/* editor_export_platform.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
class EditorFileSystemDirectory;
|
||||
struct EditorProgress;
|
||||
|
||||
#include "core/config/project_settings.h"
|
||||
#include "core/io/dir_access.h"
|
||||
#include "core/io/zip_io.h"
|
||||
#include "core/os/shared_object.h"
|
||||
#include "editor_export_preset.h"
|
||||
#include "scene/gui/rich_text_label.h"
|
||||
#include "scene/main/node.h"
|
||||
#include "scene/resources/image_texture.h"
|
||||
|
||||
class EditorExportPlugin;
|
||||
|
||||
const String ENV_SCRIPT_ENCRYPTION_KEY = "GODOT_SCRIPT_ENCRYPTION_KEY";
|
||||
|
||||
class EditorExportPlatform : public RefCounted {
|
||||
GDCLASS(EditorExportPlatform, RefCounted);
|
||||
|
||||
protected:
|
||||
static void _bind_methods();
|
||||
|
||||
public:
|
||||
typedef Error (*EditorExportSaveFunction)(void *p_userdata, const String &p_path, const Vector<uint8_t> &p_data, int p_file, int p_total, const Vector<String> &p_enc_in_filters, const Vector<String> &p_enc_ex_filters, const Vector<uint8_t> &p_key, uint64_t p_seed);
|
||||
typedef Error (*EditorExportRemoveFunction)(void *p_userdata, const String &p_path);
|
||||
typedef Error (*EditorExportSaveSharedObject)(void *p_userdata, const SharedObject &p_so);
|
||||
|
||||
enum DebugFlags {
|
||||
DEBUG_FLAG_DUMB_CLIENT = 1,
|
||||
DEBUG_FLAG_REMOTE_DEBUG = 2,
|
||||
DEBUG_FLAG_REMOTE_DEBUG_LOCALHOST = 4,
|
||||
DEBUG_FLAG_VIEW_COLLISIONS = 8,
|
||||
DEBUG_FLAG_VIEW_NAVIGATION = 16,
|
||||
};
|
||||
|
||||
enum ExportMessageType {
|
||||
EXPORT_MESSAGE_NONE,
|
||||
EXPORT_MESSAGE_INFO,
|
||||
EXPORT_MESSAGE_WARNING,
|
||||
EXPORT_MESSAGE_ERROR,
|
||||
};
|
||||
|
||||
struct ExportMessage {
|
||||
ExportMessageType msg_type;
|
||||
String category;
|
||||
String text;
|
||||
};
|
||||
|
||||
struct SavedData {
|
||||
uint64_t ofs = 0;
|
||||
uint64_t size = 0;
|
||||
bool encrypted = false;
|
||||
bool removal = false;
|
||||
Vector<uint8_t> md5;
|
||||
CharString path_utf8;
|
||||
|
||||
bool operator<(const SavedData &p_data) const {
|
||||
return path_utf8 < p_data.path_utf8;
|
||||
}
|
||||
};
|
||||
|
||||
struct PackData {
|
||||
String path;
|
||||
Ref<FileAccess> f;
|
||||
Vector<SavedData> file_ofs;
|
||||
EditorProgress *ep = nullptr;
|
||||
Vector<SharedObject> *so_files = nullptr;
|
||||
bool use_sparse_pck = false;
|
||||
};
|
||||
|
||||
static bool _store_header(Ref<FileAccess> p_fd, bool p_enc, bool p_sparse, uint64_t &r_file_base_ofs, uint64_t &r_dir_base_ofs);
|
||||
static bool _encrypt_and_store_directory(Ref<FileAccess> p_fd, PackData &p_pack_data, const Vector<uint8_t> &p_key, uint64_t p_seed, uint64_t p_file_base);
|
||||
static Error _encrypt_and_store_data(Ref<FileAccess> p_fd, const String &p_path, const Vector<uint8_t> &p_data, const Vector<String> &p_enc_in_filters, const Vector<String> &p_enc_ex_filters, const Vector<uint8_t> &p_key, uint64_t p_seed, bool &r_encrypt);
|
||||
String _get_script_encryption_key(const Ref<EditorExportPreset> &p_preset) const;
|
||||
|
||||
private:
|
||||
struct ZipData {
|
||||
void *zip = nullptr;
|
||||
EditorProgress *ep = nullptr;
|
||||
Vector<SharedObject> *so_files = nullptr;
|
||||
int file_count = 0;
|
||||
};
|
||||
|
||||
Vector<ExportMessage> messages;
|
||||
|
||||
void _export_find_resources(EditorFileSystemDirectory *p_dir, HashSet<String> &p_paths);
|
||||
void _export_find_customized_resources(const Ref<EditorExportPreset> &p_preset, EditorFileSystemDirectory *p_dir, EditorExportPreset::FileExportMode p_mode, HashSet<String> &p_paths);
|
||||
void _export_find_dependencies(const String &p_path, HashSet<String> &p_paths);
|
||||
|
||||
static bool _check_hash(const uint8_t *p_hash, const Vector<uint8_t> &p_data);
|
||||
|
||||
static Error _save_pack_file(void *p_userdata, const String &p_path, const Vector<uint8_t> &p_data, int p_file, int p_total, const Vector<String> &p_enc_in_filters, const Vector<String> &p_enc_ex_filters, const Vector<uint8_t> &p_key, uint64_t p_seed);
|
||||
static Error _save_pack_patch_file(void *p_userdata, const String &p_path, const Vector<uint8_t> &p_data, int p_file, int p_total, const Vector<String> &p_enc_in_filters, const Vector<String> &p_enc_ex_filters, const Vector<uint8_t> &p_key, uint64_t p_seed);
|
||||
static Error _pack_add_shared_object(void *p_userdata, const SharedObject &p_so);
|
||||
|
||||
static Error _remove_pack_file(void *p_userdata, const String &p_path);
|
||||
|
||||
static Error _save_zip_file(void *p_userdata, const String &p_path, const Vector<uint8_t> &p_data, int p_file, int p_total, const Vector<String> &p_enc_in_filters, const Vector<String> &p_enc_ex_filters, const Vector<uint8_t> &p_key, uint64_t p_seed);
|
||||
static Error _save_zip_patch_file(void *p_userdata, const String &p_path, const Vector<uint8_t> &p_data, int p_file, int p_total, const Vector<String> &p_enc_in_filters, const Vector<String> &p_enc_ex_filters, const Vector<uint8_t> &p_key, uint64_t p_seed);
|
||||
static Error _zip_add_shared_object(void *p_userdata, const SharedObject &p_so);
|
||||
|
||||
struct ScriptCallbackData {
|
||||
Callable file_cb;
|
||||
Callable so_cb;
|
||||
};
|
||||
|
||||
static Error _script_save_file(void *p_userdata, const String &p_path, const Vector<uint8_t> &p_data, int p_file, int p_total, const Vector<String> &p_enc_in_filters, const Vector<String> &p_enc_ex_filters, const Vector<uint8_t> &p_key, uint64_t p_seed);
|
||||
static Error _script_add_shared_object(void *p_userdata, const SharedObject &p_so);
|
||||
|
||||
void _edit_files_with_filter(Ref<DirAccess> &da, const Vector<String> &p_filters, HashSet<String> &r_list, bool exclude);
|
||||
void _edit_filter_list(HashSet<String> &r_list, const String &p_filter, bool exclude);
|
||||
|
||||
static Vector<uint8_t> _filter_extension_list_config_file(const String &p_config_path, const HashSet<String> &p_paths);
|
||||
|
||||
struct FileExportCache {
|
||||
uint64_t source_modified_time = 0;
|
||||
String source_md5;
|
||||
String saved_path;
|
||||
bool used = false;
|
||||
};
|
||||
|
||||
bool _export_customize_dictionary(Dictionary &dict, LocalVector<Ref<EditorExportPlugin>> &customize_resources_plugins);
|
||||
bool _export_customize_array(Array &array, LocalVector<Ref<EditorExportPlugin>> &customize_resources_plugins);
|
||||
bool _export_customize_object(Object *p_object, LocalVector<Ref<EditorExportPlugin>> &customize_resources_plugins);
|
||||
bool _export_customize_scene_resources(Node *p_root, Node *p_node, LocalVector<Ref<EditorExportPlugin>> &customize_resources_plugins);
|
||||
bool _is_editable_ancestor(Node *p_root, Node *p_node);
|
||||
|
||||
String _export_customize(const String &p_path, LocalVector<Ref<EditorExportPlugin>> &customize_resources_plugins, LocalVector<Ref<EditorExportPlugin>> &customize_scenes_plugins, HashMap<String, FileExportCache> &export_cache, const String &export_base_path, bool p_force_save);
|
||||
|
||||
protected:
|
||||
struct ExportNotifier {
|
||||
ExportNotifier(EditorExportPlatform &p_platform, const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, BitField<EditorExportPlatform::DebugFlags> p_flags);
|
||||
~ExportNotifier();
|
||||
};
|
||||
|
||||
HashSet<String> get_features(const Ref<EditorExportPreset> &p_preset, bool p_debug) const;
|
||||
|
||||
Dictionary _find_export_template(const String &p_template_file_name) const {
|
||||
Dictionary ret;
|
||||
String err;
|
||||
|
||||
String path = find_export_template(p_template_file_name, &err);
|
||||
ret["result"] = (err.is_empty() && !path.is_empty()) ? OK : FAILED;
|
||||
ret["path"] = path;
|
||||
ret["error_string"] = err;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool exists_export_template(const String &p_template_file_name, String *r_err) const;
|
||||
String find_export_template(const String &p_template_file_name, String *r_err = nullptr) const;
|
||||
Vector<String> gen_export_flags(BitField<EditorExportPlatform::DebugFlags> p_flags);
|
||||
|
||||
virtual void zip_folder_recursive(zipFile &p_zip, const String &p_root_path, const String &p_folder, const String &p_pkg_name);
|
||||
|
||||
Error _ssh_run_on_remote(const String &p_host, const String &p_port, const Vector<String> &p_ssh_args, const String &p_cmd_args, Array r_output = Array(), int p_port_fwd = -1) const {
|
||||
String pipe;
|
||||
Error err = ssh_run_on_remote(p_host, p_port, p_ssh_args, p_cmd_args, &pipe, p_port_fwd);
|
||||
r_output.push_back(pipe);
|
||||
return err;
|
||||
}
|
||||
OS::ProcessID _ssh_run_on_remote_no_wait(const String &p_host, const String &p_port, const Vector<String> &p_ssh_args, const String &p_cmd_args, int p_port_fwd = -1) const {
|
||||
OS::ProcessID pid = 0;
|
||||
Error err = ssh_run_on_remote_no_wait(p_host, p_port, p_ssh_args, p_cmd_args, &pid, p_port_fwd);
|
||||
if (err != OK) {
|
||||
return -1;
|
||||
} else {
|
||||
return pid;
|
||||
}
|
||||
}
|
||||
|
||||
Error ssh_run_on_remote(const String &p_host, const String &p_port, const Vector<String> &p_ssh_args, const String &p_cmd_args, String *r_out = nullptr, int p_port_fwd = -1) const;
|
||||
Error ssh_run_on_remote_no_wait(const String &p_host, const String &p_port, const Vector<String> &p_ssh_args, const String &p_cmd_args, OS::ProcessID *r_pid = nullptr, int p_port_fwd = -1) const;
|
||||
Error ssh_push_to_remote(const String &p_host, const String &p_port, const Vector<String> &p_scp_args, const String &p_src_file, const String &p_dst_file) const;
|
||||
|
||||
Error _load_patches(const Vector<String> &p_patches);
|
||||
void _unload_patches();
|
||||
|
||||
Ref<Image> _load_icon_or_splash_image(const String &p_path, Error *r_error) const;
|
||||
|
||||
#ifndef DISABLE_DEPRECATED
|
||||
static Vector<String> _get_forced_export_files_bind_compat_71542();
|
||||
static void _bind_compatibility_methods();
|
||||
#endif
|
||||
|
||||
public:
|
||||
static Variant get_project_setting(const Ref<EditorExportPreset> &p_preset, const StringName &p_name);
|
||||
virtual void get_preset_features(const Ref<EditorExportPreset> &p_preset, List<String> *r_features) const = 0;
|
||||
|
||||
struct ExportOption {
|
||||
PropertyInfo option;
|
||||
Variant default_value;
|
||||
bool update_visibility = false;
|
||||
bool required = false;
|
||||
|
||||
ExportOption(const PropertyInfo &p_info, const Variant &p_default, bool p_update_visibility = false, bool p_required = false) :
|
||||
option(p_info),
|
||||
default_value(p_default),
|
||||
update_visibility(p_update_visibility),
|
||||
required(p_required) {
|
||||
}
|
||||
ExportOption() {}
|
||||
};
|
||||
|
||||
virtual Ref<EditorExportPreset> create_preset();
|
||||
virtual bool is_executable(const String &p_path) const { return false; }
|
||||
|
||||
virtual void clear_messages() { messages.clear(); }
|
||||
virtual void add_message(ExportMessageType p_type, const String &p_category, const String &p_message) {
|
||||
ExportMessage msg;
|
||||
msg.category = p_category;
|
||||
msg.text = p_message;
|
||||
msg.msg_type = p_type;
|
||||
messages.push_back(msg);
|
||||
switch (p_type) {
|
||||
case EXPORT_MESSAGE_INFO: {
|
||||
print_line(vformat("%s: %s", msg.category, msg.text));
|
||||
} break;
|
||||
case EXPORT_MESSAGE_WARNING: {
|
||||
WARN_PRINT(vformat("%s: %s", msg.category, msg.text));
|
||||
} break;
|
||||
case EXPORT_MESSAGE_ERROR: {
|
||||
ERR_PRINT(vformat("%s: %s", msg.category, msg.text));
|
||||
} break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
virtual int get_message_count() const {
|
||||
return messages.size();
|
||||
}
|
||||
|
||||
virtual ExportMessage get_message(int p_index) const {
|
||||
ERR_FAIL_INDEX_V(p_index, messages.size(), ExportMessage());
|
||||
return messages[p_index];
|
||||
}
|
||||
|
||||
virtual ExportMessageType _get_message_type(int p_index) const {
|
||||
ERR_FAIL_INDEX_V(p_index, messages.size(), EXPORT_MESSAGE_NONE);
|
||||
return messages[p_index].msg_type;
|
||||
}
|
||||
|
||||
virtual String _get_message_category(int p_index) const {
|
||||
ERR_FAIL_INDEX_V(p_index, messages.size(), String());
|
||||
return messages[p_index].category;
|
||||
}
|
||||
|
||||
virtual String _get_message_text(int p_index) const {
|
||||
ERR_FAIL_INDEX_V(p_index, messages.size(), String());
|
||||
return messages[p_index].text;
|
||||
}
|
||||
|
||||
virtual ExportMessageType get_worst_message_type() const {
|
||||
ExportMessageType worst_type = EXPORT_MESSAGE_NONE;
|
||||
for (int i = 0; i < messages.size(); i++) {
|
||||
worst_type = MAX(worst_type, messages[i].msg_type);
|
||||
}
|
||||
return worst_type;
|
||||
}
|
||||
|
||||
Dictionary get_internal_export_files(const Ref<EditorExportPreset> &p_preset, bool p_debug);
|
||||
|
||||
static Vector<String> get_forced_export_files(const Ref<EditorExportPreset> &p_preset);
|
||||
|
||||
virtual bool fill_log_messages(RichTextLabel *p_log, Error p_err);
|
||||
|
||||
virtual void get_export_options(List<ExportOption> *r_options) const = 0;
|
||||
virtual bool should_update_export_options() { return false; }
|
||||
virtual bool get_export_option_visibility(const EditorExportPreset *p_preset, const String &p_option) const { return true; }
|
||||
virtual String get_export_option_warning(const EditorExportPreset *p_preset, const StringName &p_name) const { return String(); }
|
||||
|
||||
virtual String get_os_name() const = 0;
|
||||
virtual String get_name() const = 0;
|
||||
virtual Ref<Texture2D> get_logo() const = 0;
|
||||
|
||||
Array get_current_presets() const;
|
||||
|
||||
Error _export_project_files(const Ref<EditorExportPreset> &p_preset, bool p_debug, const Callable &p_save_func, const Callable &p_so_func);
|
||||
Error export_project_files(const Ref<EditorExportPreset> &p_preset, bool p_debug, EditorExportSaveFunction p_save_func, EditorExportRemoveFunction p_remove_func, void *p_udata, EditorExportSaveSharedObject p_so_func = nullptr);
|
||||
|
||||
Dictionary _save_pack(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, bool p_embed = false);
|
||||
Dictionary _save_zip(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path);
|
||||
|
||||
Dictionary _save_pack_patch(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path);
|
||||
Dictionary _save_zip_patch(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path);
|
||||
|
||||
Error save_pack(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, Vector<SharedObject> *p_so_files = nullptr, EditorExportSaveFunction p_save_func = nullptr, EditorExportRemoveFunction p_remove_func = nullptr, bool p_embed = false, int64_t *r_embedded_start = nullptr, int64_t *r_embedded_size = nullptr);
|
||||
Error save_zip(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, Vector<SharedObject> *p_so_files = nullptr, EditorExportSaveFunction p_save_func = nullptr);
|
||||
|
||||
Error save_pack_patch(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, Vector<SharedObject> *p_so_files = nullptr, bool p_embed = false, int64_t *r_embedded_start = nullptr, int64_t *r_embedded_size = nullptr);
|
||||
Error save_zip_patch(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, Vector<SharedObject> *p_so_files = nullptr);
|
||||
|
||||
virtual bool poll_export() { return false; }
|
||||
virtual int get_options_count() const { return 0; }
|
||||
virtual String get_options_tooltip() const { return ""; }
|
||||
virtual Ref<Texture2D> get_option_icon(int p_index) const;
|
||||
virtual String get_option_label(int p_device) const { return ""; }
|
||||
virtual String get_option_tooltip(int p_device) const { return ""; }
|
||||
virtual String get_device_architecture(int p_device) const { return ""; }
|
||||
|
||||
virtual void cleanup() {}
|
||||
virtual Error run(const Ref<EditorExportPreset> &p_preset, int p_device, BitField<EditorExportPlatform::DebugFlags> p_debug_flags) { return OK; }
|
||||
virtual Ref<Texture2D> get_run_icon() const { return get_logo(); }
|
||||
|
||||
virtual bool can_export(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates, bool p_debug = false) const;
|
||||
virtual bool has_valid_export_configuration(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates, bool p_debug = false) const = 0;
|
||||
virtual bool has_valid_project_configuration(const Ref<EditorExportPreset> &p_preset, String &r_error) const = 0;
|
||||
|
||||
virtual List<String> get_binary_extensions(const Ref<EditorExportPreset> &p_preset) const = 0;
|
||||
virtual Error export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, BitField<EditorExportPlatform::DebugFlags> p_flags = 0) = 0;
|
||||
virtual Error export_pack(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, BitField<EditorExportPlatform::DebugFlags> p_flags = 0);
|
||||
virtual Error export_zip(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, BitField<EditorExportPlatform::DebugFlags> p_flags = 0);
|
||||
virtual Error export_pack_patch(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, const Vector<String> &p_patches = Vector<String>(), BitField<EditorExportPlatform::DebugFlags> p_flags = 0);
|
||||
virtual Error export_zip_patch(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, const Vector<String> &p_patches = Vector<String>(), BitField<EditorExportPlatform::DebugFlags> p_flags = 0);
|
||||
virtual void get_platform_features(List<String> *r_features) const = 0;
|
||||
virtual void resolve_platform_feature_priorities(const Ref<EditorExportPreset> &p_preset, HashSet<String> &p_features) {}
|
||||
virtual String get_debug_protocol() const { return "tcp://"; }
|
||||
virtual HashMap<String, Variant> get_custom_project_settings(const Ref<EditorExportPreset> &p_preset) const { return HashMap<String, Variant>(); }
|
||||
};
|
||||
|
||||
VARIANT_ENUM_CAST(EditorExportPlatform::ExportMessageType)
|
||||
VARIANT_BITFIELD_CAST(EditorExportPlatform::DebugFlags);
|
2902
editor/export/editor_export_platform_apple_embedded.cpp
Normal file
2902
editor/export/editor_export_platform_apple_embedded.cpp
Normal file
File diff suppressed because it is too large
Load Diff
331
editor/export/editor_export_platform_apple_embedded.h
Normal file
331
editor/export/editor_export_platform_apple_embedded.h
Normal file
@@ -0,0 +1,331 @@
|
||||
/**************************************************************************/
|
||||
/* editor_export_platform_apple_embedded.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "plugin_config_apple_embedded.h"
|
||||
|
||||
#include "core/config/project_settings.h"
|
||||
#include "core/io/file_access.h"
|
||||
#include "core/io/image_loader.h"
|
||||
#include "core/io/marshalls.h"
|
||||
#include "core/io/resource_saver.h"
|
||||
#include "core/io/zip_io.h"
|
||||
#include "core/os/os.h"
|
||||
#include "core/templates/safe_refcount.h"
|
||||
#include "editor/export/editor_export_platform.h"
|
||||
#include "editor/settings/editor_settings.h"
|
||||
#include "main/splash.gen.h"
|
||||
#include "scene/resources/image_texture.h"
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include <functional>
|
||||
|
||||
// Optional environment variables for defining confidential information. If any
|
||||
// of these is set, they will override the values set in the credentials file.
|
||||
const String ENV_APPLE_PLATFORM_PROFILE_UUID_DEBUG = "GODOT_APPLE_PLATFORM_PROVISIONING_PROFILE_UUID_DEBUG";
|
||||
const String ENV_APPLE_PLATFORM_PROFILE_UUID_RELEASE = "GODOT_APPLE_PLATFORM_PROVISIONING_PROFILE_UUID_RELEASE";
|
||||
const String ENV_APPLE_PLATFORM_PROFILE_SPECIFIER_DEBUG = "GODOT_APPLE_PLATFORM_PROFILE_SPECIFIER_DEBUG";
|
||||
const String ENV_APPLE_PLATFORM_PROFILE_SPECIFIER_RELEASE = "GODOT_APPLE_PLATFORM_PROFILE_SPECIFIER_RELEASE";
|
||||
|
||||
static const String storyboard_image_scale_mode[] = {
|
||||
"center",
|
||||
"scaleAspectFit",
|
||||
"scaleAspectFill",
|
||||
"scaleToFill",
|
||||
};
|
||||
|
||||
class EditorExportPlatformAppleEmbedded : public EditorExportPlatform {
|
||||
GDCLASS(EditorExportPlatformAppleEmbedded, EditorExportPlatform);
|
||||
|
||||
Ref<ImageTexture> logo;
|
||||
Ref<ImageTexture> run_icon;
|
||||
|
||||
// Plugins
|
||||
mutable SafeFlag plugins_changed;
|
||||
SafeFlag devices_changed;
|
||||
|
||||
struct Device {
|
||||
String id;
|
||||
String name;
|
||||
bool wifi = false;
|
||||
bool use_ios_deploy = false;
|
||||
};
|
||||
|
||||
Vector<Device> devices;
|
||||
Mutex device_lock;
|
||||
|
||||
Mutex plugins_lock;
|
||||
mutable Vector<PluginConfigAppleEmbedded> plugins;
|
||||
#ifdef MACOS_ENABLED
|
||||
Thread check_for_changes_thread;
|
||||
SafeFlag quit_request;
|
||||
SafeFlag has_runnable_preset;
|
||||
|
||||
static bool _check_xcode_install();
|
||||
static void _check_for_changes_poll_thread(void *ud);
|
||||
void _update_preset_status();
|
||||
|
||||
protected:
|
||||
void _start_remote_device_poller_thread() {
|
||||
check_for_changes_thread.start(_check_for_changes_poll_thread, this);
|
||||
}
|
||||
|
||||
int _execute(const String &p_path, const List<String> &p_arguments, std::function<void(const String &)> p_on_data);
|
||||
|
||||
private:
|
||||
#endif
|
||||
|
||||
typedef Error (*FileHandler)(String p_file, void *p_userdata);
|
||||
static Error _walk_dir_recursive(Ref<DirAccess> &p_da, FileHandler p_handler, void *p_userdata);
|
||||
static Error _codesign(String p_file, void *p_userdata);
|
||||
|
||||
struct AppleEmbeddedConfigData {
|
||||
String pkg_name;
|
||||
String binary_name;
|
||||
String plist_content;
|
||||
String architectures;
|
||||
String linker_flags;
|
||||
String cpp_code;
|
||||
String modules_buildfile;
|
||||
String modules_fileref;
|
||||
String modules_buildphase;
|
||||
String modules_buildgrp;
|
||||
Vector<String> capabilities;
|
||||
bool use_swift_runtime;
|
||||
};
|
||||
|
||||
struct ExportArchitecture {
|
||||
String name;
|
||||
bool is_default = false;
|
||||
|
||||
ExportArchitecture() {}
|
||||
|
||||
ExportArchitecture(String p_name, bool p_is_default) {
|
||||
name = p_name;
|
||||
is_default = p_is_default;
|
||||
}
|
||||
};
|
||||
|
||||
struct AppleEmbeddedExportAsset {
|
||||
String exported_path;
|
||||
bool is_framework = false; // framework is anything linked to the binary, otherwise it's a resource
|
||||
bool should_embed = false;
|
||||
};
|
||||
|
||||
String _get_additional_plist_content();
|
||||
String _get_linker_flags();
|
||||
String _get_cpp_code();
|
||||
void _fix_config_file(const Ref<EditorExportPreset> &p_preset, Vector<uint8_t> &pfile, const AppleEmbeddedConfigData &p_config, bool p_debug);
|
||||
|
||||
Vector<ExportArchitecture> _get_supported_architectures() const;
|
||||
Vector<String> _get_preset_architectures(const Ref<EditorExportPreset> &p_preset) const;
|
||||
|
||||
void _check_xcframework_content(const String &p_path, int &r_total_libs, int &r_static_libs, int &r_dylibs, int &r_frameworks) const;
|
||||
Error _convert_to_framework(const String &p_source, const String &p_destination, const String &p_id) const;
|
||||
|
||||
void _add_assets_to_project(const String &p_out_dir, const Ref<EditorExportPreset> &p_preset, Vector<uint8_t> &p_project_data, const Vector<AppleEmbeddedExportAsset> &p_additional_assets);
|
||||
Error _export_additional_assets(const Ref<EditorExportPreset> &p_preset, const String &p_out_dir, const Vector<String> &p_assets, bool p_is_framework, bool p_should_embed, Vector<AppleEmbeddedExportAsset> &r_exported_assets);
|
||||
Error _copy_asset(const Ref<EditorExportPreset> &p_preset, const String &p_out_dir, const String &p_asset, const String *p_custom_file_name, bool p_is_framework, bool p_should_embed, Vector<AppleEmbeddedExportAsset> &r_exported_assets);
|
||||
Error _export_additional_assets(const Ref<EditorExportPreset> &p_preset, const String &p_out_dir, const Vector<SharedObject> &p_libraries, Vector<AppleEmbeddedExportAsset> &r_exported_assets);
|
||||
Error _export_apple_embedded_plugins(const Ref<EditorExportPreset> &p_preset, AppleEmbeddedConfigData &p_config_data, const String &dest_dir, Vector<AppleEmbeddedExportAsset> &r_exported_assets, bool p_debug);
|
||||
|
||||
Error _export_project_helper(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, BitField<EditorExportPlatform::DebugFlags> p_flags, bool p_oneclick);
|
||||
|
||||
bool is_package_name_valid(const String &p_package, String *r_error = nullptr) const;
|
||||
|
||||
protected:
|
||||
struct IconInfo {
|
||||
const char *preset_key;
|
||||
const char *idiom;
|
||||
const char *export_name;
|
||||
const char *actual_size_side;
|
||||
const char *scale;
|
||||
const char *unscaled_size;
|
||||
bool force_opaque;
|
||||
};
|
||||
|
||||
void _blend_and_rotate(Ref<Image> &p_dst, Ref<Image> &p_src, bool p_rot);
|
||||
|
||||
virtual Error _export_loading_screen_file(const Ref<EditorExportPreset> &p_preset, const String &p_dest_dir) { return OK; }
|
||||
virtual Error _export_icons(const Ref<EditorExportPreset> &p_preset, const String &p_iconset_dir) { return OK; }
|
||||
|
||||
virtual String get_platform_name() const = 0;
|
||||
virtual String get_sdk_name() const = 0;
|
||||
virtual const Vector<String> get_device_types() const = 0;
|
||||
virtual String get_minimum_deployment_target() const = 0;
|
||||
|
||||
virtual void get_preset_features(const Ref<EditorExportPreset> &p_preset, List<String> *r_features) const override;
|
||||
virtual void get_export_options(List<ExportOption> *r_options) const override;
|
||||
virtual bool get_export_option_visibility(const EditorExportPreset *p_preset, const String &p_option) const override;
|
||||
virtual String get_export_option_warning(const EditorExportPreset *p_preset, const StringName &p_name) const override;
|
||||
|
||||
virtual Vector<IconInfo> get_icon_infos() const = 0;
|
||||
|
||||
void _notification(int p_what);
|
||||
|
||||
virtual void get_platform_features(List<String> *r_features) const override {
|
||||
r_features->push_back("mobile");
|
||||
r_features->push_back("apple_embedded");
|
||||
}
|
||||
|
||||
public:
|
||||
virtual Ref<Texture2D> get_logo() const override { return logo; }
|
||||
virtual Ref<Texture2D> get_run_icon() const override { return run_icon; }
|
||||
|
||||
virtual int get_options_count() const override;
|
||||
virtual String get_options_tooltip() const override;
|
||||
virtual Ref<Texture2D> get_option_icon(int p_index) const override;
|
||||
virtual String get_option_label(int p_index) const override;
|
||||
virtual String get_option_tooltip(int p_index) const override;
|
||||
virtual Error run(const Ref<EditorExportPreset> &p_preset, int p_device, BitField<EditorExportPlatform::DebugFlags> p_debug_flags) override;
|
||||
|
||||
virtual bool poll_export() override {
|
||||
bool dc = devices_changed.is_set();
|
||||
if (dc) {
|
||||
// don't clear unless we're reporting true, to avoid race
|
||||
devices_changed.clear();
|
||||
}
|
||||
return dc;
|
||||
}
|
||||
|
||||
virtual bool should_update_export_options() override {
|
||||
bool export_options_changed = plugins_changed.is_set();
|
||||
if (export_options_changed) {
|
||||
// don't clear unless we're reporting true, to avoid race
|
||||
plugins_changed.clear();
|
||||
}
|
||||
return export_options_changed;
|
||||
}
|
||||
|
||||
virtual List<String> get_binary_extensions(const Ref<EditorExportPreset> &p_preset) const override {
|
||||
List<String> list;
|
||||
if (p_preset.is_valid()) {
|
||||
bool project_only = p_preset->get("application/export_project_only");
|
||||
if (project_only) {
|
||||
list.push_back("xcodeproj");
|
||||
} else {
|
||||
list.push_back("ipa");
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
virtual Error export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, BitField<EditorExportPlatform::DebugFlags> p_flags = 0) override;
|
||||
|
||||
virtual bool has_valid_export_configuration(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates, bool p_debug = false) const override;
|
||||
virtual bool has_valid_project_configuration(const Ref<EditorExportPreset> &p_preset, String &r_error) const override;
|
||||
|
||||
virtual void resolve_platform_feature_priorities(const Ref<EditorExportPreset> &p_preset, HashSet<String> &p_features) override {
|
||||
}
|
||||
|
||||
EditorExportPlatformAppleEmbedded(const char *p_platform_logo_svg, const char *p_run_icon_svg);
|
||||
~EditorExportPlatformAppleEmbedded();
|
||||
|
||||
/// List the gdip files in the directory specified by the p_path parameter.
|
||||
static Vector<String> list_plugin_config_files(const String &p_path, bool p_check_directories) {
|
||||
Vector<String> dir_files;
|
||||
Ref<DirAccess> da = DirAccess::open(p_path);
|
||||
if (da.is_valid()) {
|
||||
da->list_dir_begin();
|
||||
while (true) {
|
||||
String file = da->get_next();
|
||||
if (file.is_empty()) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (file == "." || file == "..") {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (da->current_is_hidden()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (da->current_is_dir()) {
|
||||
if (p_check_directories) {
|
||||
Vector<String> directory_files = list_plugin_config_files(p_path.path_join(file), false);
|
||||
for (int i = 0; i < directory_files.size(); ++i) {
|
||||
dir_files.push_back(file.path_join(directory_files[i]));
|
||||
}
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (file.ends_with(PluginConfigAppleEmbedded::PLUGIN_CONFIG_EXT)) {
|
||||
dir_files.push_back(file);
|
||||
}
|
||||
}
|
||||
da->list_dir_end();
|
||||
}
|
||||
|
||||
return dir_files;
|
||||
}
|
||||
|
||||
static Vector<PluginConfigAppleEmbedded> get_plugins(const String &p_platform_name) {
|
||||
Vector<PluginConfigAppleEmbedded> loaded_plugins;
|
||||
|
||||
String plugins_dir = ProjectSettings::get_singleton()->get_resource_path().path_join(p_platform_name + "/plugins");
|
||||
|
||||
if (DirAccess::exists(plugins_dir)) {
|
||||
Vector<String> plugins_filenames = list_plugin_config_files(plugins_dir, true);
|
||||
|
||||
if (!plugins_filenames.is_empty()) {
|
||||
Ref<ConfigFile> config_file;
|
||||
config_file.instantiate();
|
||||
for (int i = 0; i < plugins_filenames.size(); i++) {
|
||||
PluginConfigAppleEmbedded config = PluginConfigAppleEmbedded::load_plugin_config(config_file, plugins_dir.path_join(plugins_filenames[i]));
|
||||
if (config.valid_config) {
|
||||
loaded_plugins.push_back(config);
|
||||
} else {
|
||||
print_error("Invalid plugin config file " + plugins_filenames[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return loaded_plugins;
|
||||
}
|
||||
|
||||
static Vector<PluginConfigAppleEmbedded> get_enabled_plugins(const String &p_platform_name, const Ref<EditorExportPreset> &p_presets) {
|
||||
Vector<PluginConfigAppleEmbedded> enabled_plugins;
|
||||
Vector<PluginConfigAppleEmbedded> all_plugins = get_plugins(p_platform_name);
|
||||
for (int i = 0; i < all_plugins.size(); i++) {
|
||||
PluginConfigAppleEmbedded plugin = all_plugins[i];
|
||||
bool enabled = p_presets->get("plugins/" + plugin.name);
|
||||
if (enabled) {
|
||||
enabled_plugins.push_back(plugin);
|
||||
}
|
||||
}
|
||||
|
||||
return enabled_plugins;
|
||||
}
|
||||
};
|
367
editor/export/editor_export_platform_extension.cpp
Normal file
367
editor/export/editor_export_platform_extension.cpp
Normal file
@@ -0,0 +1,367 @@
|
||||
/**************************************************************************/
|
||||
/* editor_export_platform_extension.cpp */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#include "editor_export_platform_extension.h"
|
||||
|
||||
void EditorExportPlatformExtension::_bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("set_config_error", "error_text"), &EditorExportPlatformExtension::set_config_error);
|
||||
ClassDB::bind_method(D_METHOD("get_config_error"), &EditorExportPlatformExtension::get_config_error);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_config_missing_templates", "missing_templates"), &EditorExportPlatformExtension::set_config_missing_templates);
|
||||
ClassDB::bind_method(D_METHOD("get_config_missing_templates"), &EditorExportPlatformExtension::get_config_missing_templates);
|
||||
|
||||
GDVIRTUAL_BIND(_get_preset_features, "preset");
|
||||
GDVIRTUAL_BIND(_is_executable, "path");
|
||||
GDVIRTUAL_BIND(_get_export_options);
|
||||
GDVIRTUAL_BIND(_should_update_export_options);
|
||||
GDVIRTUAL_BIND(_get_export_option_visibility, "preset", "option");
|
||||
GDVIRTUAL_BIND(_get_export_option_warning, "preset", "option");
|
||||
|
||||
GDVIRTUAL_BIND(_get_os_name);
|
||||
GDVIRTUAL_BIND(_get_name);
|
||||
GDVIRTUAL_BIND(_get_logo);
|
||||
|
||||
GDVIRTUAL_BIND(_poll_export);
|
||||
GDVIRTUAL_BIND(_get_options_count);
|
||||
GDVIRTUAL_BIND(_get_options_tooltip);
|
||||
|
||||
GDVIRTUAL_BIND(_get_option_icon, "device");
|
||||
#ifndef DISABLE_DEPRECATED
|
||||
GDVIRTUAL_BIND_COMPAT(_get_option_icon_bind_compat_108825, "device");
|
||||
#endif
|
||||
|
||||
GDVIRTUAL_BIND(_get_option_label, "device");
|
||||
GDVIRTUAL_BIND(_get_option_tooltip, "device");
|
||||
GDVIRTUAL_BIND(_get_device_architecture, "device");
|
||||
|
||||
GDVIRTUAL_BIND(_cleanup);
|
||||
|
||||
GDVIRTUAL_BIND(_run, "preset", "device", "debug_flags");
|
||||
GDVIRTUAL_BIND(_get_run_icon);
|
||||
|
||||
GDVIRTUAL_BIND(_can_export, "preset", "debug");
|
||||
GDVIRTUAL_BIND(_has_valid_export_configuration, "preset", "debug");
|
||||
GDVIRTUAL_BIND(_has_valid_project_configuration, "preset");
|
||||
|
||||
GDVIRTUAL_BIND(_get_binary_extensions, "preset");
|
||||
|
||||
GDVIRTUAL_BIND(_export_project, "preset", "debug", "path", "flags");
|
||||
GDVIRTUAL_BIND(_export_pack, "preset", "debug", "path", "flags");
|
||||
GDVIRTUAL_BIND(_export_zip, "preset", "debug", "path", "flags");
|
||||
GDVIRTUAL_BIND(_export_pack_patch, "preset", "debug", "path", "patches", "flags");
|
||||
GDVIRTUAL_BIND(_export_zip_patch, "preset", "debug", "path", "patches", "flags");
|
||||
|
||||
GDVIRTUAL_BIND(_get_platform_features);
|
||||
|
||||
GDVIRTUAL_BIND(_get_debug_protocol);
|
||||
}
|
||||
|
||||
void EditorExportPlatformExtension::get_preset_features(const Ref<EditorExportPreset> &p_preset, List<String> *r_features) const {
|
||||
Vector<String> ret;
|
||||
if (GDVIRTUAL_CALL(_get_preset_features, p_preset, ret) && r_features) {
|
||||
for (const String &E : ret) {
|
||||
r_features->push_back(E);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool EditorExportPlatformExtension::is_executable(const String &p_path) const {
|
||||
bool ret = false;
|
||||
GDVIRTUAL_CALL(_is_executable, p_path, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void EditorExportPlatformExtension::get_export_options(List<ExportOption> *r_options) const {
|
||||
TypedArray<Dictionary> ret;
|
||||
if (GDVIRTUAL_CALL(_get_export_options, ret) && r_options) {
|
||||
for (const Variant &var : ret) {
|
||||
const Dictionary &d = var;
|
||||
ERR_CONTINUE(!d.has("name"));
|
||||
ERR_CONTINUE(!d.has("type"));
|
||||
|
||||
PropertyInfo pinfo = PropertyInfo::from_dict(d);
|
||||
ERR_CONTINUE(pinfo.name.is_empty() && (pinfo.usage & PROPERTY_USAGE_STORAGE));
|
||||
ERR_CONTINUE(pinfo.type < 0 || pinfo.type >= Variant::VARIANT_MAX);
|
||||
|
||||
Variant default_value;
|
||||
if (d.has("default_value")) {
|
||||
default_value = d["default_value"];
|
||||
}
|
||||
bool update_visibility = false;
|
||||
if (d.has("update_visibility")) {
|
||||
update_visibility = d["update_visibility"];
|
||||
}
|
||||
bool required = false;
|
||||
if (d.has("required")) {
|
||||
required = d["required"];
|
||||
}
|
||||
|
||||
r_options->push_back(ExportOption(pinfo, default_value, update_visibility, required));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool EditorExportPlatformExtension::should_update_export_options() {
|
||||
bool ret = false;
|
||||
GDVIRTUAL_CALL(_should_update_export_options, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool EditorExportPlatformExtension::get_export_option_visibility(const EditorExportPreset *p_preset, const String &p_option) const {
|
||||
bool ret = true;
|
||||
GDVIRTUAL_CALL(_get_export_option_visibility, Ref<EditorExportPreset>(p_preset), p_option, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
String EditorExportPlatformExtension::get_export_option_warning(const EditorExportPreset *p_preset, const StringName &p_name) const {
|
||||
String ret;
|
||||
GDVIRTUAL_CALL(_get_export_option_warning, Ref<EditorExportPreset>(p_preset), p_name, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
String EditorExportPlatformExtension::get_os_name() const {
|
||||
String ret;
|
||||
GDVIRTUAL_CALL(_get_os_name, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
String EditorExportPlatformExtension::get_name() const {
|
||||
String ret;
|
||||
GDVIRTUAL_CALL(_get_name, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
Ref<Texture2D> EditorExportPlatformExtension::get_logo() const {
|
||||
Ref<Texture2D> ret;
|
||||
GDVIRTUAL_CALL(_get_logo, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool EditorExportPlatformExtension::poll_export() {
|
||||
bool ret = false;
|
||||
GDVIRTUAL_CALL(_poll_export, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int EditorExportPlatformExtension::get_options_count() const {
|
||||
int ret = 0;
|
||||
GDVIRTUAL_CALL(_get_options_count, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
String EditorExportPlatformExtension::get_options_tooltip() const {
|
||||
String ret;
|
||||
GDVIRTUAL_CALL(_get_options_tooltip, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
Ref<Texture2D> EditorExportPlatformExtension::get_option_icon(int p_index) const {
|
||||
Ref<Texture2D> ret;
|
||||
if (GDVIRTUAL_CALL(_get_option_icon, p_index, ret)) {
|
||||
return ret;
|
||||
}
|
||||
#ifndef DISABLE_DEPRECATED
|
||||
Ref<ImageTexture> comp_ret;
|
||||
if (GDVIRTUAL_CALL(_get_option_icon_bind_compat_108825, p_index, comp_ret)) {
|
||||
return comp_ret;
|
||||
}
|
||||
#endif
|
||||
return EditorExportPlatform::get_option_icon(p_index);
|
||||
}
|
||||
|
||||
String EditorExportPlatformExtension::get_option_label(int p_device) const {
|
||||
String ret;
|
||||
GDVIRTUAL_CALL(_get_option_label, p_device, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
String EditorExportPlatformExtension::get_option_tooltip(int p_device) const {
|
||||
String ret;
|
||||
GDVIRTUAL_CALL(_get_option_tooltip, p_device, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
String EditorExportPlatformExtension::get_device_architecture(int p_device) const {
|
||||
String ret;
|
||||
GDVIRTUAL_CALL(_get_device_architecture, p_device, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void EditorExportPlatformExtension::cleanup() {
|
||||
GDVIRTUAL_CALL(_cleanup);
|
||||
}
|
||||
|
||||
Error EditorExportPlatformExtension::run(const Ref<EditorExportPreset> &p_preset, int p_device, BitField<EditorExportPlatform::DebugFlags> p_debug_flags) {
|
||||
Error ret = OK;
|
||||
GDVIRTUAL_CALL(_run, p_preset, p_device, p_debug_flags, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
Ref<Texture2D> EditorExportPlatformExtension::get_run_icon() const {
|
||||
Ref<Texture2D> ret;
|
||||
if (GDVIRTUAL_CALL(_get_run_icon, ret)) {
|
||||
return ret;
|
||||
}
|
||||
return EditorExportPlatform::get_run_icon();
|
||||
}
|
||||
|
||||
bool EditorExportPlatformExtension::can_export(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates, bool p_debug) const {
|
||||
bool ret = false;
|
||||
config_error = r_error;
|
||||
config_missing_templates = r_missing_templates;
|
||||
if (GDVIRTUAL_CALL(_can_export, p_preset, p_debug, ret)) {
|
||||
r_error = config_error;
|
||||
r_missing_templates = config_missing_templates;
|
||||
return ret;
|
||||
}
|
||||
return EditorExportPlatform::can_export(p_preset, r_error, r_missing_templates, p_debug);
|
||||
}
|
||||
|
||||
bool EditorExportPlatformExtension::has_valid_export_configuration(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates, bool p_debug) const {
|
||||
bool ret = false;
|
||||
config_error = r_error;
|
||||
config_missing_templates = r_missing_templates;
|
||||
if (GDVIRTUAL_CALL(_has_valid_export_configuration, p_preset, p_debug, ret)) {
|
||||
r_error = config_error;
|
||||
r_missing_templates = config_missing_templates;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool EditorExportPlatformExtension::has_valid_project_configuration(const Ref<EditorExportPreset> &p_preset, String &r_error) const {
|
||||
bool ret = false;
|
||||
config_error = r_error;
|
||||
if (GDVIRTUAL_CALL(_has_valid_project_configuration, p_preset, ret)) {
|
||||
r_error = config_error;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
List<String> EditorExportPlatformExtension::get_binary_extensions(const Ref<EditorExportPreset> &p_preset) const {
|
||||
List<String> ret_list;
|
||||
Vector<String> ret;
|
||||
if (GDVIRTUAL_CALL(_get_binary_extensions, p_preset, ret)) {
|
||||
for (const String &E : ret) {
|
||||
ret_list.push_back(E);
|
||||
}
|
||||
}
|
||||
return ret_list;
|
||||
}
|
||||
|
||||
Error EditorExportPlatformExtension::export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, BitField<EditorExportPlatform::DebugFlags> p_flags) {
|
||||
ExportNotifier notifier(*this, p_preset, p_debug, p_path, p_flags);
|
||||
|
||||
Error ret = FAILED;
|
||||
GDVIRTUAL_CALL(_export_project, p_preset, p_debug, p_path, p_flags, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
Error EditorExportPlatformExtension::export_pack(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, BitField<EditorExportPlatform::DebugFlags> p_flags) {
|
||||
ExportNotifier notifier(*this, p_preset, p_debug, p_path, p_flags);
|
||||
|
||||
Error ret = FAILED;
|
||||
if (GDVIRTUAL_CALL(_export_pack, p_preset, p_debug, p_path, p_flags, ret)) {
|
||||
return ret;
|
||||
}
|
||||
return save_pack(p_preset, p_debug, p_path);
|
||||
}
|
||||
|
||||
Error EditorExportPlatformExtension::export_zip(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, BitField<EditorExportPlatform::DebugFlags> p_flags) {
|
||||
ExportNotifier notifier(*this, p_preset, p_debug, p_path, p_flags);
|
||||
|
||||
Error ret = FAILED;
|
||||
if (GDVIRTUAL_CALL(_export_zip, p_preset, p_debug, p_path, p_flags, ret)) {
|
||||
return ret;
|
||||
}
|
||||
return save_zip(p_preset, p_debug, p_path);
|
||||
}
|
||||
|
||||
Error EditorExportPlatformExtension::export_pack_patch(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, const Vector<String> &p_patches, BitField<EditorExportPlatform::DebugFlags> p_flags) {
|
||||
ExportNotifier notifier(*this, p_preset, p_debug, p_path, p_flags);
|
||||
|
||||
Error err = _load_patches(p_patches.is_empty() ? p_preset->get_patches() : p_patches);
|
||||
if (err != OK) {
|
||||
return err;
|
||||
}
|
||||
|
||||
Error ret = FAILED;
|
||||
if (GDVIRTUAL_CALL(_export_pack_patch, p_preset, p_debug, p_path, p_patches, p_flags, ret)) {
|
||||
_unload_patches();
|
||||
return ret;
|
||||
}
|
||||
|
||||
err = save_pack_patch(p_preset, p_debug, p_path);
|
||||
_unload_patches();
|
||||
return err;
|
||||
}
|
||||
|
||||
Error EditorExportPlatformExtension::export_zip_patch(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, const Vector<String> &p_patches, BitField<EditorExportPlatform::DebugFlags> p_flags) {
|
||||
ExportNotifier notifier(*this, p_preset, p_debug, p_path, p_flags);
|
||||
|
||||
Error err = _load_patches(p_patches.is_empty() ? p_preset->get_patches() : p_patches);
|
||||
if (err != OK) {
|
||||
return err;
|
||||
}
|
||||
|
||||
Error ret = FAILED;
|
||||
if (GDVIRTUAL_CALL(_export_zip_patch, p_preset, p_debug, p_path, p_patches, p_flags, ret)) {
|
||||
_unload_patches();
|
||||
return ret;
|
||||
}
|
||||
|
||||
err = save_zip_patch(p_preset, p_debug, p_path);
|
||||
_unload_patches();
|
||||
return err;
|
||||
}
|
||||
|
||||
void EditorExportPlatformExtension::get_platform_features(List<String> *r_features) const {
|
||||
Vector<String> ret;
|
||||
if (GDVIRTUAL_CALL(_get_platform_features, ret) && r_features) {
|
||||
for (const String &E : ret) {
|
||||
r_features->push_back(E);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
String EditorExportPlatformExtension::get_debug_protocol() const {
|
||||
String ret;
|
||||
if (GDVIRTUAL_CALL(_get_debug_protocol, ret)) {
|
||||
return ret;
|
||||
}
|
||||
return EditorExportPlatform::get_debug_protocol();
|
||||
}
|
||||
|
||||
EditorExportPlatformExtension::EditorExportPlatformExtension() {
|
||||
//NOP
|
||||
}
|
||||
|
||||
EditorExportPlatformExtension::~EditorExportPlatformExtension() {
|
||||
//NOP
|
||||
}
|
156
editor/export/editor_export_platform_extension.h
Normal file
156
editor/export/editor_export_platform_extension.h
Normal file
@@ -0,0 +1,156 @@
|
||||
/**************************************************************************/
|
||||
/* editor_export_platform_extension.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "editor_export_platform.h"
|
||||
#include "editor_export_preset.h"
|
||||
|
||||
class EditorExportPlatformExtension : public EditorExportPlatform {
|
||||
GDCLASS(EditorExportPlatformExtension, EditorExportPlatform);
|
||||
|
||||
mutable String config_error;
|
||||
mutable bool config_missing_templates = false;
|
||||
|
||||
protected:
|
||||
static void _bind_methods();
|
||||
|
||||
public:
|
||||
virtual void get_preset_features(const Ref<EditorExportPreset> &p_preset, List<String> *r_features) const override;
|
||||
GDVIRTUAL1RC_REQUIRED(Vector<String>, _get_preset_features, Ref<EditorExportPreset>);
|
||||
|
||||
virtual bool is_executable(const String &p_path) const override;
|
||||
GDVIRTUAL1RC(bool, _is_executable, const String &);
|
||||
|
||||
virtual void get_export_options(List<ExportOption> *r_options) const override;
|
||||
GDVIRTUAL0RC(TypedArray<Dictionary>, _get_export_options);
|
||||
|
||||
virtual bool should_update_export_options() override;
|
||||
GDVIRTUAL0R(bool, _should_update_export_options);
|
||||
|
||||
virtual bool get_export_option_visibility(const EditorExportPreset *p_preset, const String &p_option) const override;
|
||||
GDVIRTUAL2RC(bool, _get_export_option_visibility, Ref<EditorExportPreset>, const String &);
|
||||
|
||||
virtual String get_export_option_warning(const EditorExportPreset *p_preset, const StringName &p_name) const override;
|
||||
GDVIRTUAL2RC(String, _get_export_option_warning, Ref<EditorExportPreset>, const StringName &);
|
||||
|
||||
virtual String get_os_name() const override;
|
||||
GDVIRTUAL0RC_REQUIRED(String, _get_os_name);
|
||||
|
||||
virtual String get_name() const override;
|
||||
GDVIRTUAL0RC_REQUIRED(String, _get_name);
|
||||
|
||||
virtual Ref<Texture2D> get_logo() const override;
|
||||
GDVIRTUAL0RC_REQUIRED(Ref<Texture2D>, _get_logo);
|
||||
|
||||
virtual bool poll_export() override;
|
||||
GDVIRTUAL0R(bool, _poll_export);
|
||||
|
||||
virtual int get_options_count() const override;
|
||||
GDVIRTUAL0RC(int, _get_options_count);
|
||||
|
||||
virtual String get_options_tooltip() const override;
|
||||
GDVIRTUAL0RC(String, _get_options_tooltip);
|
||||
|
||||
virtual Ref<Texture2D> get_option_icon(int p_index) const override;
|
||||
GDVIRTUAL1RC(Ref<Texture2D>, _get_option_icon, int);
|
||||
|
||||
#ifndef DISABLE_DEPRECATED
|
||||
GDVIRTUAL1RC_COMPAT(_get_option_icon_bind_compat_108825, Ref<ImageTexture>, _get_option_icon, int)
|
||||
#endif
|
||||
|
||||
virtual String get_option_label(int p_device) const override;
|
||||
GDVIRTUAL1RC(String, _get_option_label, int);
|
||||
|
||||
virtual String get_option_tooltip(int p_device) const override;
|
||||
GDVIRTUAL1RC(String, _get_option_tooltip, int);
|
||||
|
||||
virtual String get_device_architecture(int p_device) const override;
|
||||
GDVIRTUAL1RC(String, _get_device_architecture, int);
|
||||
|
||||
virtual void cleanup() override;
|
||||
GDVIRTUAL0(_cleanup);
|
||||
|
||||
virtual Error run(const Ref<EditorExportPreset> &p_preset, int p_device, BitField<EditorExportPlatform::DebugFlags> p_debug_flags) override;
|
||||
GDVIRTUAL3R(Error, _run, Ref<EditorExportPreset>, int, BitField<EditorExportPlatform::DebugFlags>);
|
||||
|
||||
virtual Ref<Texture2D> get_run_icon() const override;
|
||||
GDVIRTUAL0RC(Ref<Texture2D>, _get_run_icon);
|
||||
|
||||
void set_config_error(const String &p_error) const {
|
||||
config_error = p_error;
|
||||
}
|
||||
String get_config_error() const {
|
||||
return config_error;
|
||||
}
|
||||
|
||||
void set_config_missing_templates(bool p_missing_templates) const {
|
||||
config_missing_templates = p_missing_templates;
|
||||
}
|
||||
bool get_config_missing_templates() const {
|
||||
return config_missing_templates;
|
||||
}
|
||||
|
||||
virtual bool can_export(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates, bool p_debug = false) const override;
|
||||
GDVIRTUAL2RC(bool, _can_export, Ref<EditorExportPreset>, bool);
|
||||
|
||||
virtual bool has_valid_export_configuration(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates, bool p_debug = false) const override;
|
||||
GDVIRTUAL2RC_REQUIRED(bool, _has_valid_export_configuration, Ref<EditorExportPreset>, bool);
|
||||
|
||||
virtual bool has_valid_project_configuration(const Ref<EditorExportPreset> &p_preset, String &r_error) const override;
|
||||
GDVIRTUAL1RC_REQUIRED(bool, _has_valid_project_configuration, Ref<EditorExportPreset>);
|
||||
|
||||
virtual List<String> get_binary_extensions(const Ref<EditorExportPreset> &p_preset) const override;
|
||||
GDVIRTUAL1RC_REQUIRED(Vector<String>, _get_binary_extensions, Ref<EditorExportPreset>);
|
||||
|
||||
virtual Error export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, BitField<EditorExportPlatform::DebugFlags> p_flags = 0) override;
|
||||
GDVIRTUAL4R_REQUIRED(Error, _export_project, Ref<EditorExportPreset>, bool, const String &, BitField<EditorExportPlatform::DebugFlags>);
|
||||
|
||||
virtual Error export_pack(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, BitField<EditorExportPlatform::DebugFlags> p_flags = 0) override;
|
||||
GDVIRTUAL4R(Error, _export_pack, Ref<EditorExportPreset>, bool, const String &, BitField<EditorExportPlatform::DebugFlags>);
|
||||
|
||||
virtual Error export_zip(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, BitField<EditorExportPlatform::DebugFlags> p_flags = 0) override;
|
||||
GDVIRTUAL4R(Error, _export_zip, Ref<EditorExportPreset>, bool, const String &, BitField<EditorExportPlatform::DebugFlags>);
|
||||
|
||||
virtual Error export_pack_patch(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, const Vector<String> &p_patches = Vector<String>(), BitField<EditorExportPlatform::DebugFlags> p_flags = 0) override;
|
||||
GDVIRTUAL5R(Error, _export_pack_patch, Ref<EditorExportPreset>, bool, const String &, const Vector<String> &, BitField<EditorExportPlatform::DebugFlags>);
|
||||
|
||||
virtual Error export_zip_patch(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, const Vector<String> &p_patches = Vector<String>(), BitField<EditorExportPlatform::DebugFlags> p_flags = 0) override;
|
||||
GDVIRTUAL5R(Error, _export_zip_patch, Ref<EditorExportPreset>, bool, const String &, const Vector<String> &, BitField<EditorExportPlatform::DebugFlags>);
|
||||
|
||||
virtual void get_platform_features(List<String> *r_features) const override;
|
||||
GDVIRTUAL0RC_REQUIRED(Vector<String>, _get_platform_features);
|
||||
|
||||
virtual String get_debug_protocol() const override;
|
||||
GDVIRTUAL0RC(String, _get_debug_protocol);
|
||||
|
||||
EditorExportPlatformExtension();
|
||||
~EditorExportPlatformExtension();
|
||||
};
|
292
editor/export/editor_export_platform_pc.cpp
Normal file
292
editor/export/editor_export_platform_pc.cpp
Normal file
@@ -0,0 +1,292 @@
|
||||
/**************************************************************************/
|
||||
/* editor_export_platform_pc.cpp */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#include "editor_export_platform_pc.h"
|
||||
|
||||
#include "core/config/project_settings.h"
|
||||
#include "scene/resources/image_texture.h"
|
||||
|
||||
void EditorExportPlatformPC::get_preset_features(const Ref<EditorExportPreset> &p_preset, List<String> *r_features) const {
|
||||
if (p_preset->get("texture_format/s3tc_bptc")) {
|
||||
r_features->push_back("s3tc");
|
||||
r_features->push_back("bptc");
|
||||
}
|
||||
if (p_preset->get("texture_format/etc2_astc")) {
|
||||
r_features->push_back("etc2");
|
||||
r_features->push_back("astc");
|
||||
}
|
||||
if (p_preset->get("shader_baker/enabled")) {
|
||||
r_features->push_back("shader_baker");
|
||||
}
|
||||
// PC platforms only have one architecture per export, since
|
||||
// we export a single executable instead of a bundle.
|
||||
r_features->push_back(p_preset->get("binary_format/architecture"));
|
||||
}
|
||||
|
||||
void EditorExportPlatformPC::get_export_options(List<ExportOption> *r_options) const {
|
||||
String ext_filter = (get_os_name() == "Windows") ? "*.exe" : "";
|
||||
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "custom_template/debug", PROPERTY_HINT_GLOBAL_FILE, ext_filter), ""));
|
||||
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "custom_template/release", PROPERTY_HINT_GLOBAL_FILE, ext_filter), ""));
|
||||
|
||||
r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "debug/export_console_wrapper", PROPERTY_HINT_ENUM, "No,Debug Only,Debug and Release"), 1));
|
||||
|
||||
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "binary_format/embed_pck"), false));
|
||||
|
||||
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "texture_format/s3tc_bptc"), true));
|
||||
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "texture_format/etc2_astc"), false));
|
||||
|
||||
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "shader_baker/enabled"), false));
|
||||
}
|
||||
|
||||
String EditorExportPlatformPC::get_export_option_warning(const EditorExportPreset *p_preset, const StringName &p_name) const {
|
||||
if (p_name == "shader_baker/enabled" && bool(p_preset->get("shader_baker/enabled"))) {
|
||||
String export_renderer = GLOBAL_GET("rendering/renderer/rendering_method");
|
||||
if (OS::get_singleton()->get_current_rendering_method() == "gl_compatibility") {
|
||||
return TTR("\"Shader Baker\" is not supported when using the Compatibility renderer.");
|
||||
} else if (OS::get_singleton()->get_current_rendering_method() != export_renderer) {
|
||||
return vformat(TTR("The editor is currently using a different renderer than what the target platform will use. \"Shader Baker\" won't be able to include core shaders. Switch to the \"%s\" renderer temporarily to fix this."), export_renderer);
|
||||
}
|
||||
}
|
||||
|
||||
return String();
|
||||
}
|
||||
|
||||
String EditorExportPlatformPC::get_name() const {
|
||||
return name;
|
||||
}
|
||||
|
||||
String EditorExportPlatformPC::get_os_name() const {
|
||||
return os_name;
|
||||
}
|
||||
|
||||
Ref<Texture2D> EditorExportPlatformPC::get_logo() const {
|
||||
return logo;
|
||||
}
|
||||
|
||||
bool EditorExportPlatformPC::has_valid_export_configuration(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates, bool p_debug) const {
|
||||
String err;
|
||||
bool valid = false;
|
||||
|
||||
// Look for export templates (first official, and if defined custom templates).
|
||||
String arch = p_preset->get("binary_format/architecture");
|
||||
bool dvalid = exists_export_template(get_template_file_name("debug", arch), &err);
|
||||
bool rvalid = exists_export_template(get_template_file_name("release", arch), &err);
|
||||
|
||||
if (p_preset->get("custom_template/debug") != "") {
|
||||
dvalid = FileAccess::exists(p_preset->get("custom_template/debug"));
|
||||
if (!dvalid) {
|
||||
err += TTR("Custom debug template not found.") + "\n";
|
||||
}
|
||||
}
|
||||
if (p_preset->get("custom_template/release") != "") {
|
||||
rvalid = FileAccess::exists(p_preset->get("custom_template/release"));
|
||||
if (!rvalid) {
|
||||
err += TTR("Custom release template not found.") + "\n";
|
||||
}
|
||||
}
|
||||
|
||||
valid = dvalid || rvalid;
|
||||
r_missing_templates = !valid;
|
||||
|
||||
bool uses_s3tc_bptc = p_preset->get("texture_format/s3tc_bptc");
|
||||
bool uses_etc2_astc = p_preset->get("texture_format/etc2_astc");
|
||||
|
||||
if (!uses_s3tc_bptc && !uses_etc2_astc) {
|
||||
valid = false;
|
||||
err += TTR("A texture format must be selected to export the project. Please select at least one texture format.");
|
||||
}
|
||||
|
||||
if (!err.is_empty()) {
|
||||
r_error = err;
|
||||
}
|
||||
return valid;
|
||||
}
|
||||
|
||||
bool EditorExportPlatformPC::has_valid_project_configuration(const Ref<EditorExportPreset> &p_preset, String &r_error) const {
|
||||
return true;
|
||||
}
|
||||
|
||||
Error EditorExportPlatformPC::export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, BitField<EditorExportPlatform::DebugFlags> p_flags) {
|
||||
ExportNotifier notifier(*this, p_preset, p_debug, p_path, p_flags);
|
||||
|
||||
Error err = prepare_template(p_preset, p_debug, p_path, p_flags);
|
||||
if (err == OK) {
|
||||
err = modify_template(p_preset, p_debug, p_path, p_flags);
|
||||
}
|
||||
if (err == OK) {
|
||||
err = export_project_data(p_preset, p_debug, p_path, p_flags);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
Error EditorExportPlatformPC::prepare_template(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, BitField<EditorExportPlatform::DebugFlags> p_flags) {
|
||||
if (!DirAccess::exists(p_path.get_base_dir())) {
|
||||
add_message(EXPORT_MESSAGE_ERROR, TTR("Prepare Template"), TTR("The given export path doesn't exist."));
|
||||
return ERR_FILE_BAD_PATH;
|
||||
}
|
||||
|
||||
String custom_debug = p_preset->get("custom_template/debug");
|
||||
String custom_release = p_preset->get("custom_template/release");
|
||||
|
||||
String template_path = p_debug ? custom_debug : custom_release;
|
||||
|
||||
template_path = template_path.strip_edges();
|
||||
|
||||
if (template_path.is_empty()) {
|
||||
template_path = find_export_template(get_template_file_name(p_debug ? "debug" : "release", p_preset->get("binary_format/architecture")));
|
||||
}
|
||||
|
||||
if (!template_path.is_empty() && !FileAccess::exists(template_path)) {
|
||||
add_message(EXPORT_MESSAGE_ERROR, TTR("Prepare Template"), vformat(TTR("Template file not found: \"%s\"."), template_path));
|
||||
return ERR_FILE_NOT_FOUND;
|
||||
}
|
||||
|
||||
// Matching the extensions in platform/windows/console_wrapper_windows.cpp
|
||||
static const char *const wrapper_extensions[] = {
|
||||
".console.exe",
|
||||
"_console.exe",
|
||||
" console.exe",
|
||||
"console.exe",
|
||||
nullptr,
|
||||
};
|
||||
int con_wrapper_mode = p_preset->get("debug/export_console_wrapper");
|
||||
bool copy_wrapper = (con_wrapper_mode == 1 && p_debug) || (con_wrapper_mode == 2);
|
||||
|
||||
Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
|
||||
da->make_dir_recursive(p_path.get_base_dir());
|
||||
Error err = da->copy(template_path, p_path, get_chmod_flags());
|
||||
if (err == OK && copy_wrapper) {
|
||||
for (int i = 0; wrapper_extensions[i]; ++i) {
|
||||
const String wrapper_path = template_path.get_basename() + wrapper_extensions[i];
|
||||
if (FileAccess::exists(wrapper_path)) {
|
||||
err = da->copy(wrapper_path, p_path.get_basename() + ".console.exe", get_chmod_flags());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (err != OK) {
|
||||
add_message(EXPORT_MESSAGE_ERROR, TTR("Prepare Template"), TTR("Failed to copy export template."));
|
||||
return err;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
Error EditorExportPlatformPC::export_project_data(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, BitField<EditorExportPlatform::DebugFlags> p_flags) {
|
||||
String pck_path;
|
||||
if (p_preset->get("binary_format/embed_pck")) {
|
||||
pck_path = p_path;
|
||||
} else {
|
||||
pck_path = p_path.get_basename() + ".pck";
|
||||
}
|
||||
|
||||
Vector<SharedObject> so_files;
|
||||
|
||||
int64_t embedded_pos;
|
||||
int64_t embedded_size;
|
||||
Error err = save_pack(p_preset, p_debug, pck_path, &so_files, nullptr, nullptr, p_preset->get("binary_format/embed_pck"), &embedded_pos, &embedded_size);
|
||||
if (err == OK && p_preset->get("binary_format/embed_pck")) {
|
||||
if (embedded_size >= 0x100000000 && String(p_preset->get("binary_format/architecture")).contains("32")) {
|
||||
add_message(EXPORT_MESSAGE_ERROR, TTR("PCK Embedding"), TTR("On 32-bit exports the embedded PCK cannot be bigger than 4 GiB."));
|
||||
return ERR_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
err = fixup_embedded_pck(p_path, embedded_pos, embedded_size);
|
||||
}
|
||||
|
||||
if (err == OK && !so_files.is_empty()) {
|
||||
// If shared object files, copy them.
|
||||
Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
|
||||
for (int i = 0; i < so_files.size() && err == OK; i++) {
|
||||
String src_path = ProjectSettings::get_singleton()->globalize_path(so_files[i].path);
|
||||
String target_path;
|
||||
if (so_files[i].target.is_empty()) {
|
||||
target_path = p_path.get_base_dir();
|
||||
} else {
|
||||
target_path = p_path.get_base_dir().path_join(so_files[i].target);
|
||||
da->make_dir_recursive(target_path);
|
||||
}
|
||||
target_path = target_path.path_join(src_path.get_file());
|
||||
|
||||
if (da->dir_exists(src_path)) {
|
||||
err = da->make_dir_recursive(target_path);
|
||||
if (err == OK) {
|
||||
err = da->copy_dir(src_path, target_path, -1, true);
|
||||
if (err != OK) {
|
||||
add_message(EXPORT_MESSAGE_ERROR, TTR("GDExtension"), vformat(TTR("Failed to copy shared object \"%s\"."), src_path));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
err = da->copy(src_path, target_path);
|
||||
if (err != OK) {
|
||||
add_message(EXPORT_MESSAGE_ERROR, TTR("GDExtension"), vformat(TTR("Failed to copy shared object \"%s\"."), src_path));
|
||||
}
|
||||
if (err == OK) {
|
||||
err = sign_shared_object(p_preset, p_debug, target_path);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
Error EditorExportPlatformPC::sign_shared_object(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path) {
|
||||
return OK;
|
||||
}
|
||||
|
||||
void EditorExportPlatformPC::set_name(const String &p_name) {
|
||||
name = p_name;
|
||||
}
|
||||
|
||||
void EditorExportPlatformPC::set_os_name(const String &p_name) {
|
||||
os_name = p_name;
|
||||
}
|
||||
|
||||
void EditorExportPlatformPC::set_logo(const Ref<Texture2D> &p_logo) {
|
||||
logo = p_logo;
|
||||
}
|
||||
|
||||
void EditorExportPlatformPC::get_platform_features(List<String> *r_features) const {
|
||||
r_features->push_back("pc"); // Identify PC platforms as such.
|
||||
r_features->push_back(get_os_name().to_lower()); // OS name is a feature.
|
||||
}
|
||||
|
||||
void EditorExportPlatformPC::resolve_platform_feature_priorities(const Ref<EditorExportPreset> &p_preset, HashSet<String> &p_features) {
|
||||
}
|
||||
|
||||
int EditorExportPlatformPC::get_chmod_flags() const {
|
||||
return chmod_flags;
|
||||
}
|
||||
|
||||
void EditorExportPlatformPC::set_chmod_flags(int p_flags) {
|
||||
chmod_flags = p_flags;
|
||||
}
|
79
editor/export/editor_export_platform_pc.h
Normal file
79
editor/export/editor_export_platform_pc.h
Normal file
@@ -0,0 +1,79 @@
|
||||
/**************************************************************************/
|
||||
/* editor_export_platform_pc.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "editor_export_platform.h"
|
||||
|
||||
class EditorExportPlatformPC : public EditorExportPlatform {
|
||||
GDCLASS(EditorExportPlatformPC, EditorExportPlatform);
|
||||
|
||||
private:
|
||||
Ref<ImageTexture> logo;
|
||||
String name;
|
||||
String os_name;
|
||||
|
||||
int chmod_flags = -1;
|
||||
|
||||
public:
|
||||
virtual void get_preset_features(const Ref<EditorExportPreset> &p_preset, List<String> *r_features) const override;
|
||||
virtual void get_export_options(List<ExportOption> *r_options) const override;
|
||||
virtual String get_export_option_warning(const EditorExportPreset *p_preset, const StringName &p_name) const override;
|
||||
|
||||
virtual String get_name() const override;
|
||||
virtual String get_os_name() const override;
|
||||
virtual Ref<Texture2D> get_logo() const override;
|
||||
|
||||
virtual bool has_valid_export_configuration(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates, bool p_debug = false) const override;
|
||||
virtual bool has_valid_project_configuration(const Ref<EditorExportPreset> &p_preset, String &r_error) const override;
|
||||
virtual Error export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, BitField<EditorExportPlatform::DebugFlags> p_flags = 0) override;
|
||||
virtual Error sign_shared_object(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path);
|
||||
virtual String get_template_file_name(const String &p_target, const String &p_arch) const = 0;
|
||||
|
||||
virtual Error prepare_template(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, BitField<EditorExportPlatform::DebugFlags> p_flags);
|
||||
virtual Error modify_template(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, BitField<EditorExportPlatform::DebugFlags> p_flags) { return OK; }
|
||||
virtual Error export_project_data(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, BitField<EditorExportPlatform::DebugFlags> p_flags);
|
||||
|
||||
void set_name(const String &p_name);
|
||||
void set_os_name(const String &p_name);
|
||||
|
||||
void set_logo(const Ref<Texture2D> &p_logo);
|
||||
|
||||
void add_platform_feature(const String &p_feature);
|
||||
virtual void get_platform_features(List<String> *r_features) const override;
|
||||
virtual void resolve_platform_feature_priorities(const Ref<EditorExportPreset> &p_preset, HashSet<String> &p_features) override;
|
||||
|
||||
int get_chmod_flags() const;
|
||||
void set_chmod_flags(int p_flags);
|
||||
|
||||
virtual Error fixup_embedded_pck(const String &p_path, int64_t p_embedded_start, int64_t p_embedded_size) {
|
||||
return Error::OK;
|
||||
}
|
||||
};
|
399
editor/export/editor_export_plugin.cpp
Normal file
399
editor/export/editor_export_plugin.cpp
Normal file
@@ -0,0 +1,399 @@
|
||||
/**************************************************************************/
|
||||
/* editor_export_plugin.cpp */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#include "editor_export_plugin.h"
|
||||
|
||||
#include "core/config/project_settings.h"
|
||||
#include "editor/export/editor_export_platform.h"
|
||||
|
||||
void EditorExportPlugin::set_export_base_path(const String &p_export_base_path) {
|
||||
export_base_path = p_export_base_path;
|
||||
}
|
||||
|
||||
const String &EditorExportPlugin::get_export_base_path() const {
|
||||
return export_base_path;
|
||||
}
|
||||
|
||||
void EditorExportPlugin::set_export_preset(const Ref<EditorExportPreset> &p_preset) {
|
||||
if (p_preset.is_valid()) {
|
||||
export_preset = p_preset;
|
||||
}
|
||||
}
|
||||
|
||||
Ref<EditorExportPreset> EditorExportPlugin::get_export_preset() const {
|
||||
return export_preset;
|
||||
}
|
||||
|
||||
Ref<EditorExportPlatform> EditorExportPlugin::get_export_platform() const {
|
||||
if (export_preset.is_valid()) {
|
||||
return export_preset->get_platform();
|
||||
} else {
|
||||
return Ref<EditorExportPlatform>();
|
||||
}
|
||||
}
|
||||
|
||||
void EditorExportPlugin::add_file(const String &p_path, const Vector<uint8_t> &p_file, bool p_remap) {
|
||||
ExtraFile ef;
|
||||
ef.data = p_file;
|
||||
ef.path = p_path;
|
||||
ef.remap = p_remap;
|
||||
extra_files.push_back(ef);
|
||||
}
|
||||
|
||||
void EditorExportPlugin::add_shared_object(const String &p_path, const Vector<String> &p_tags, const String &p_target) {
|
||||
shared_objects.push_back(SharedObject(p_path, p_tags, p_target));
|
||||
}
|
||||
|
||||
void EditorExportPlugin::_add_shared_object(const SharedObject &p_shared_object) {
|
||||
shared_objects.push_back(p_shared_object);
|
||||
}
|
||||
|
||||
void EditorExportPlugin::add_apple_embedded_platform_framework(const String &p_path) {
|
||||
apple_embedded_platform_frameworks.push_back(p_path);
|
||||
}
|
||||
|
||||
void EditorExportPlugin::add_apple_embedded_platform_embedded_framework(const String &p_path) {
|
||||
apple_embedded_platform_embedded_frameworks.push_back(p_path);
|
||||
}
|
||||
|
||||
Vector<String> EditorExportPlugin::get_apple_embedded_platform_frameworks() const {
|
||||
return apple_embedded_platform_frameworks;
|
||||
}
|
||||
|
||||
Vector<String> EditorExportPlugin::get_apple_embedded_platform_embedded_frameworks() const {
|
||||
return apple_embedded_platform_embedded_frameworks;
|
||||
}
|
||||
|
||||
void EditorExportPlugin::add_apple_embedded_platform_plist_content(const String &p_plist_content) {
|
||||
apple_embedded_platform_plist_content += p_plist_content + "\n";
|
||||
}
|
||||
|
||||
String EditorExportPlugin::get_apple_embedded_platform_plist_content() const {
|
||||
return apple_embedded_platform_plist_content;
|
||||
}
|
||||
|
||||
void EditorExportPlugin::add_apple_embedded_platform_linker_flags(const String &p_flags) {
|
||||
if (apple_embedded_platform_linker_flags.length() > 0) {
|
||||
apple_embedded_platform_linker_flags += ' ';
|
||||
}
|
||||
apple_embedded_platform_linker_flags += p_flags;
|
||||
}
|
||||
|
||||
String EditorExportPlugin::get_apple_embedded_platform_linker_flags() const {
|
||||
return apple_embedded_platform_linker_flags;
|
||||
}
|
||||
|
||||
void EditorExportPlugin::add_apple_embedded_platform_bundle_file(const String &p_path) {
|
||||
apple_embedded_platform_bundle_files.push_back(p_path);
|
||||
}
|
||||
|
||||
Vector<String> EditorExportPlugin::get_apple_embedded_platform_bundle_files() const {
|
||||
return apple_embedded_platform_bundle_files;
|
||||
}
|
||||
|
||||
void EditorExportPlugin::add_apple_embedded_platform_cpp_code(const String &p_code) {
|
||||
apple_embedded_platform_cpp_code += p_code;
|
||||
}
|
||||
|
||||
String EditorExportPlugin::get_apple_embedded_platform_cpp_code() const {
|
||||
return apple_embedded_platform_cpp_code;
|
||||
}
|
||||
|
||||
void EditorExportPlugin::add_macos_plugin_file(const String &p_path) {
|
||||
macos_plugin_files.push_back(p_path);
|
||||
}
|
||||
|
||||
const Vector<String> &EditorExportPlugin::get_macos_plugin_files() const {
|
||||
return macos_plugin_files;
|
||||
}
|
||||
|
||||
void EditorExportPlugin::add_apple_embedded_platform_project_static_lib(const String &p_path) {
|
||||
apple_embedded_platform_project_static_libs.push_back(p_path);
|
||||
}
|
||||
|
||||
Vector<String> EditorExportPlugin::get_apple_embedded_platform_project_static_libs() const {
|
||||
return apple_embedded_platform_project_static_libs;
|
||||
}
|
||||
|
||||
Variant EditorExportPlugin::get_option(const StringName &p_name) const {
|
||||
ERR_FAIL_COND_V(export_preset.is_null(), Variant());
|
||||
return export_preset->get(p_name);
|
||||
}
|
||||
|
||||
String EditorExportPlugin::_has_valid_export_configuration(const Ref<EditorExportPlatform> &p_export_platform, const Ref<EditorExportPreset> &p_preset) {
|
||||
String warning;
|
||||
if (!supports_platform(p_export_platform)) {
|
||||
warning += vformat(TTR("Plugin \"%s\" is not supported on \"%s\""), get_name(), p_export_platform->get_name());
|
||||
warning += "\n";
|
||||
return warning;
|
||||
}
|
||||
|
||||
set_export_preset(p_preset);
|
||||
List<EditorExportPlatform::ExportOption> options;
|
||||
_get_export_options(p_export_platform, &options);
|
||||
for (const EditorExportPlatform::ExportOption &E : options) {
|
||||
String option_warning = _get_export_option_warning(p_export_platform, E.option.name);
|
||||
if (!option_warning.is_empty()) {
|
||||
warning += option_warning + "\n";
|
||||
}
|
||||
}
|
||||
|
||||
return warning;
|
||||
}
|
||||
|
||||
void EditorExportPlugin::_export_file_script(const String &p_path, const String &p_type, const Vector<String> &p_features) {
|
||||
GDVIRTUAL_CALL(_export_file, p_path, p_type, p_features);
|
||||
}
|
||||
|
||||
void EditorExportPlugin::_export_begin_script(const Vector<String> &p_features, bool p_debug, const String &p_path, int p_flags) {
|
||||
GDVIRTUAL_CALL(_export_begin, p_features, p_debug, p_path, p_flags);
|
||||
}
|
||||
|
||||
void EditorExportPlugin::_export_end_script() {
|
||||
GDVIRTUAL_CALL(_export_end);
|
||||
}
|
||||
|
||||
// Customization
|
||||
|
||||
bool EditorExportPlugin::_begin_customize_resources(const Ref<EditorExportPlatform> &p_platform, const Vector<String> &p_features) {
|
||||
bool ret = false;
|
||||
GDVIRTUAL_CALL(_begin_customize_resources, p_platform, p_features, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
Ref<Resource> EditorExportPlugin::_customize_resource(const Ref<Resource> &p_resource, const String &p_path) {
|
||||
Ref<Resource> ret;
|
||||
GDVIRTUAL_CALL(_customize_resource, p_resource, p_path, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool EditorExportPlugin::_begin_customize_scenes(const Ref<EditorExportPlatform> &p_platform, const Vector<String> &p_features) {
|
||||
bool ret = false;
|
||||
GDVIRTUAL_CALL(_begin_customize_scenes, p_platform, p_features, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
Node *EditorExportPlugin::_customize_scene(Node *p_root, const String &p_path) {
|
||||
Node *ret = nullptr;
|
||||
GDVIRTUAL_CALL(_customize_scene, p_root, p_path, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
uint64_t EditorExportPlugin::_get_customization_configuration_hash() const {
|
||||
uint64_t ret = 0;
|
||||
GDVIRTUAL_CALL(_get_customization_configuration_hash, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void EditorExportPlugin::_end_customize_scenes() {
|
||||
GDVIRTUAL_CALL(_end_customize_scenes);
|
||||
}
|
||||
|
||||
void EditorExportPlugin::_end_customize_resources() {
|
||||
GDVIRTUAL_CALL(_end_customize_resources);
|
||||
}
|
||||
|
||||
String EditorExportPlugin::get_name() const {
|
||||
String ret;
|
||||
GDVIRTUAL_CALL(_get_name, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool EditorExportPlugin::supports_platform(const Ref<EditorExportPlatform> &p_export_platform) const {
|
||||
bool ret = false;
|
||||
GDVIRTUAL_CALL(_supports_platform, p_export_platform, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
PackedStringArray EditorExportPlugin::get_export_features(const Ref<EditorExportPlatform> &p_export_platform, bool p_debug) const {
|
||||
return _get_export_features(p_export_platform, p_debug);
|
||||
}
|
||||
|
||||
PackedStringArray EditorExportPlugin::get_android_dependencies(const Ref<EditorExportPlatform> &p_export_platform, bool p_debug) const {
|
||||
PackedStringArray ret;
|
||||
GDVIRTUAL_CALL(_get_android_dependencies, p_export_platform, p_debug, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
PackedStringArray EditorExportPlugin::get_android_dependencies_maven_repos(const Ref<EditorExportPlatform> &p_export_platform, bool p_debug) const {
|
||||
PackedStringArray ret;
|
||||
GDVIRTUAL_CALL(_get_android_dependencies_maven_repos, p_export_platform, p_debug, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
PackedStringArray EditorExportPlugin::get_android_libraries(const Ref<EditorExportPlatform> &p_export_platform, bool p_debug) const {
|
||||
PackedStringArray ret;
|
||||
GDVIRTUAL_CALL(_get_android_libraries, p_export_platform, p_debug, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
String EditorExportPlugin::get_android_manifest_activity_element_contents(const Ref<EditorExportPlatform> &p_export_platform, bool p_debug) const {
|
||||
String ret;
|
||||
GDVIRTUAL_CALL(_get_android_manifest_activity_element_contents, p_export_platform, p_debug, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
String EditorExportPlugin::get_android_manifest_application_element_contents(const Ref<EditorExportPlatform> &p_export_platform, bool p_debug) const {
|
||||
String ret;
|
||||
GDVIRTUAL_CALL(_get_android_manifest_application_element_contents, p_export_platform, p_debug, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
String EditorExportPlugin::get_android_manifest_element_contents(const Ref<EditorExportPlatform> &p_export_platform, bool p_debug) const {
|
||||
String ret;
|
||||
GDVIRTUAL_CALL(_get_android_manifest_element_contents, p_export_platform, p_debug, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
PackedByteArray EditorExportPlugin::update_android_prebuilt_manifest(const Ref<EditorExportPlatform> &p_export_platform, const PackedByteArray &p_manifest_data) const {
|
||||
PackedByteArray ret;
|
||||
GDVIRTUAL_CALL(_update_android_prebuilt_manifest, p_export_platform, p_manifest_data, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
PackedStringArray EditorExportPlugin::_get_export_features(const Ref<EditorExportPlatform> &p_platform, bool p_debug) const {
|
||||
PackedStringArray ret;
|
||||
GDVIRTUAL_CALL(_get_export_features, p_platform, p_debug, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void EditorExportPlugin::_get_export_options(const Ref<EditorExportPlatform> &p_platform, List<EditorExportPlatform::ExportOption> *r_options) const {
|
||||
TypedArray<Dictionary> ret;
|
||||
GDVIRTUAL_CALL(_get_export_options, p_platform, ret);
|
||||
for (int i = 0; i < ret.size(); i++) {
|
||||
Dictionary option = ret[i];
|
||||
ERR_CONTINUE_MSG(!option.has("option"), "Missing required element 'option'");
|
||||
ERR_CONTINUE_MSG(!option.has("default_value"), "Missing required element 'default_value'");
|
||||
PropertyInfo property_info = PropertyInfo::from_dict(option["option"]);
|
||||
Variant default_value = option["default_value"];
|
||||
bool update_visibility = option.has("update_visibility") && option["update_visibility"];
|
||||
r_options->push_back(EditorExportPlatform::ExportOption(property_info, default_value, update_visibility));
|
||||
}
|
||||
}
|
||||
|
||||
bool EditorExportPlugin::_should_update_export_options(const Ref<EditorExportPlatform> &p_platform) const {
|
||||
bool ret = false;
|
||||
GDVIRTUAL_CALL(_should_update_export_options, p_platform, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool EditorExportPlugin::_get_export_option_visibility(const Ref<EditorExportPlatform> &p_export_platform, const String &p_option_name) const {
|
||||
bool ret = true;
|
||||
GDVIRTUAL_CALL(_get_export_option_visibility, p_export_platform, p_option_name, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
String EditorExportPlugin::_get_export_option_warning(const Ref<EditorExportPlatform> &p_export_platform, const String &p_option_name) const {
|
||||
String ret;
|
||||
GDVIRTUAL_CALL(_get_export_option_warning, p_export_platform, p_option_name, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
Dictionary EditorExportPlugin::_get_export_options_overrides(const Ref<EditorExportPlatform> &p_platform) const {
|
||||
Dictionary ret;
|
||||
GDVIRTUAL_CALL(_get_export_options_overrides, p_platform, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void EditorExportPlugin::_export_file(const String &p_path, const String &p_type, const HashSet<String> &p_features) {
|
||||
}
|
||||
|
||||
void EditorExportPlugin::_export_begin(const HashSet<String> &p_features, bool p_debug, const String &p_path, int p_flags) {
|
||||
}
|
||||
|
||||
void EditorExportPlugin::_export_end() {}
|
||||
|
||||
void EditorExportPlugin::skip() {
|
||||
skipped = true;
|
||||
}
|
||||
|
||||
void EditorExportPlugin::_bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("add_shared_object", "path", "tags", "target"), &EditorExportPlugin::add_shared_object);
|
||||
ClassDB::bind_method(D_METHOD("add_file", "path", "file", "remap"), &EditorExportPlugin::add_file);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("add_apple_embedded_platform_project_static_lib", "path"), &EditorExportPlugin::add_apple_embedded_platform_project_static_lib);
|
||||
ClassDB::bind_method(D_METHOD("add_apple_embedded_platform_framework", "path"), &EditorExportPlugin::add_apple_embedded_platform_framework);
|
||||
ClassDB::bind_method(D_METHOD("add_apple_embedded_platform_embedded_framework", "path"), &EditorExportPlugin::add_apple_embedded_platform_embedded_framework);
|
||||
ClassDB::bind_method(D_METHOD("add_apple_embedded_platform_plist_content", "plist_content"), &EditorExportPlugin::add_apple_embedded_platform_plist_content);
|
||||
ClassDB::bind_method(D_METHOD("add_apple_embedded_platform_linker_flags", "flags"), &EditorExportPlugin::add_apple_embedded_platform_linker_flags);
|
||||
ClassDB::bind_method(D_METHOD("add_apple_embedded_platform_bundle_file", "path"), &EditorExportPlugin::add_apple_embedded_platform_bundle_file);
|
||||
ClassDB::bind_method(D_METHOD("add_apple_embedded_platform_cpp_code", "code"), &EditorExportPlugin::add_apple_embedded_platform_cpp_code);
|
||||
|
||||
#ifndef DISABLE_DEPRECATED
|
||||
ClassDB::bind_method(D_METHOD("add_ios_project_static_lib", "path"), &EditorExportPlugin::add_apple_embedded_platform_project_static_lib);
|
||||
ClassDB::bind_method(D_METHOD("add_ios_framework", "path"), &EditorExportPlugin::add_apple_embedded_platform_framework);
|
||||
ClassDB::bind_method(D_METHOD("add_ios_embedded_framework", "path"), &EditorExportPlugin::add_apple_embedded_platform_embedded_framework);
|
||||
ClassDB::bind_method(D_METHOD("add_ios_plist_content", "plist_content"), &EditorExportPlugin::add_apple_embedded_platform_plist_content);
|
||||
ClassDB::bind_method(D_METHOD("add_ios_linker_flags", "flags"), &EditorExportPlugin::add_apple_embedded_platform_linker_flags);
|
||||
ClassDB::bind_method(D_METHOD("add_ios_bundle_file", "path"), &EditorExportPlugin::add_apple_embedded_platform_bundle_file);
|
||||
ClassDB::bind_method(D_METHOD("add_ios_cpp_code", "code"), &EditorExportPlugin::add_apple_embedded_platform_cpp_code);
|
||||
#endif
|
||||
|
||||
ClassDB::bind_method(D_METHOD("add_macos_plugin_file", "path"), &EditorExportPlugin::add_macos_plugin_file);
|
||||
ClassDB::bind_method(D_METHOD("skip"), &EditorExportPlugin::skip);
|
||||
ClassDB::bind_method(D_METHOD("get_option", "name"), &EditorExportPlugin::get_option);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("get_export_preset"), &EditorExportPlugin::get_export_preset);
|
||||
ClassDB::bind_method(D_METHOD("get_export_platform"), &EditorExportPlugin::get_export_platform);
|
||||
|
||||
GDVIRTUAL_BIND(_export_file, "path", "type", "features");
|
||||
GDVIRTUAL_BIND(_export_begin, "features", "is_debug", "path", "flags");
|
||||
GDVIRTUAL_BIND(_export_end);
|
||||
|
||||
GDVIRTUAL_BIND(_begin_customize_resources, "platform", "features");
|
||||
GDVIRTUAL_BIND(_customize_resource, "resource", "path");
|
||||
|
||||
GDVIRTUAL_BIND(_begin_customize_scenes, "platform", "features");
|
||||
GDVIRTUAL_BIND(_customize_scene, "scene", "path");
|
||||
|
||||
GDVIRTUAL_BIND(_get_customization_configuration_hash);
|
||||
|
||||
GDVIRTUAL_BIND(_end_customize_scenes);
|
||||
GDVIRTUAL_BIND(_end_customize_resources);
|
||||
|
||||
GDVIRTUAL_BIND(_get_export_options, "platform");
|
||||
GDVIRTUAL_BIND(_get_export_options_overrides, "platform");
|
||||
GDVIRTUAL_BIND(_should_update_export_options, "platform");
|
||||
GDVIRTUAL_BIND(_get_export_option_visibility, "platform", "option");
|
||||
GDVIRTUAL_BIND(_get_export_option_warning, "platform", "option");
|
||||
|
||||
GDVIRTUAL_BIND(_get_export_features, "platform", "debug");
|
||||
GDVIRTUAL_BIND(_get_name);
|
||||
|
||||
GDVIRTUAL_BIND(_supports_platform, "platform");
|
||||
|
||||
GDVIRTUAL_BIND(_get_android_dependencies, "platform", "debug");
|
||||
GDVIRTUAL_BIND(_get_android_dependencies_maven_repos, "platform", "debug");
|
||||
GDVIRTUAL_BIND(_get_android_libraries, "platform", "debug");
|
||||
GDVIRTUAL_BIND(_get_android_manifest_activity_element_contents, "platform", "debug");
|
||||
GDVIRTUAL_BIND(_get_android_manifest_application_element_contents, "platform", "debug");
|
||||
GDVIRTUAL_BIND(_get_android_manifest_element_contents, "platform", "debug");
|
||||
GDVIRTUAL_BIND(_update_android_prebuilt_manifest, "platform", "manifest_data");
|
||||
}
|
192
editor/export/editor_export_plugin.h
Normal file
192
editor/export/editor_export_plugin.h
Normal file
@@ -0,0 +1,192 @@
|
||||
/**************************************************************************/
|
||||
/* editor_export_plugin.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/os/shared_object.h"
|
||||
#include "editor_export_platform.h"
|
||||
#include "editor_export_preset.h"
|
||||
#include "scene/main/node.h"
|
||||
|
||||
class EditorExportPlugin : public RefCounted {
|
||||
GDCLASS(EditorExportPlugin, RefCounted);
|
||||
|
||||
friend class EditorExport;
|
||||
friend class EditorExportPlatform;
|
||||
friend class EditorExportPreset;
|
||||
|
||||
String export_base_path;
|
||||
Ref<EditorExportPreset> export_preset;
|
||||
|
||||
Vector<SharedObject> shared_objects;
|
||||
struct ExtraFile {
|
||||
String path;
|
||||
Vector<uint8_t> data;
|
||||
bool remap = false;
|
||||
};
|
||||
Vector<ExtraFile> extra_files;
|
||||
bool skipped = false;
|
||||
|
||||
Vector<String> apple_embedded_platform_frameworks;
|
||||
Vector<String> apple_embedded_platform_embedded_frameworks;
|
||||
Vector<String> apple_embedded_platform_project_static_libs;
|
||||
String apple_embedded_platform_plist_content;
|
||||
String apple_embedded_platform_linker_flags;
|
||||
Vector<String> apple_embedded_platform_bundle_files;
|
||||
String apple_embedded_platform_cpp_code;
|
||||
|
||||
Vector<String> macos_plugin_files;
|
||||
|
||||
_FORCE_INLINE_ void _clear() {
|
||||
shared_objects.clear();
|
||||
extra_files.clear();
|
||||
skipped = false;
|
||||
}
|
||||
|
||||
_FORCE_INLINE_ void _export_end_clear() {
|
||||
apple_embedded_platform_frameworks.clear();
|
||||
apple_embedded_platform_embedded_frameworks.clear();
|
||||
apple_embedded_platform_bundle_files.clear();
|
||||
apple_embedded_platform_plist_content = "";
|
||||
apple_embedded_platform_linker_flags = "";
|
||||
apple_embedded_platform_cpp_code = "";
|
||||
macos_plugin_files.clear();
|
||||
}
|
||||
|
||||
// Export
|
||||
void _export_file_script(const String &p_path, const String &p_type, const Vector<String> &p_features);
|
||||
void _export_begin_script(const Vector<String> &p_features, bool p_debug, const String &p_path, int p_flags);
|
||||
void _export_end_script();
|
||||
|
||||
String _has_valid_export_configuration(const Ref<EditorExportPlatform> &p_export_platform, const Ref<EditorExportPreset> &p_preset);
|
||||
|
||||
protected:
|
||||
void set_export_base_path(const String &p_export_base_path);
|
||||
const String &get_export_base_path() const;
|
||||
void set_export_preset(const Ref<EditorExportPreset> &p_preset);
|
||||
Ref<EditorExportPreset> get_export_preset() const;
|
||||
Ref<EditorExportPlatform> get_export_platform() const;
|
||||
|
||||
void add_file(const String &p_path, const Vector<uint8_t> &p_file, bool p_remap);
|
||||
void add_shared_object(const String &p_path, const Vector<String> &tags, const String &p_target = String());
|
||||
void _add_shared_object(const SharedObject &p_shared_object);
|
||||
|
||||
void add_apple_embedded_platform_framework(const String &p_path);
|
||||
void add_apple_embedded_platform_embedded_framework(const String &p_path);
|
||||
void add_apple_embedded_platform_project_static_lib(const String &p_path);
|
||||
void add_apple_embedded_platform_plist_content(const String &p_plist_content);
|
||||
void add_apple_embedded_platform_linker_flags(const String &p_flags);
|
||||
void add_apple_embedded_platform_bundle_file(const String &p_path);
|
||||
void add_apple_embedded_platform_cpp_code(const String &p_code);
|
||||
void add_macos_plugin_file(const String &p_path);
|
||||
|
||||
void skip();
|
||||
|
||||
virtual void _export_file(const String &p_path, const String &p_type, const HashSet<String> &p_features);
|
||||
virtual void _export_begin(const HashSet<String> &p_features, bool p_debug, const String &p_path, int p_flags);
|
||||
virtual void _export_end();
|
||||
|
||||
static void _bind_methods();
|
||||
|
||||
GDVIRTUAL3(_export_file, String, String, Vector<String>)
|
||||
GDVIRTUAL4(_export_begin, Vector<String>, bool, String, uint32_t)
|
||||
GDVIRTUAL0(_export_end)
|
||||
|
||||
GDVIRTUAL2RC(bool, _begin_customize_resources, const Ref<EditorExportPlatform> &, const Vector<String> &)
|
||||
GDVIRTUAL2R_REQUIRED(Ref<Resource>, _customize_resource, const Ref<Resource> &, String)
|
||||
|
||||
GDVIRTUAL2RC(bool, _begin_customize_scenes, const Ref<EditorExportPlatform> &, const Vector<String> &)
|
||||
GDVIRTUAL2R_REQUIRED(Node *, _customize_scene, Node *, String)
|
||||
GDVIRTUAL0RC_REQUIRED(uint64_t, _get_customization_configuration_hash)
|
||||
|
||||
GDVIRTUAL0(_end_customize_scenes)
|
||||
GDVIRTUAL0(_end_customize_resources)
|
||||
|
||||
GDVIRTUAL2RC(PackedStringArray, _get_export_features, const Ref<EditorExportPlatform> &, bool);
|
||||
GDVIRTUAL1RC(TypedArray<Dictionary>, _get_export_options, const Ref<EditorExportPlatform> &);
|
||||
GDVIRTUAL1RC(Dictionary, _get_export_options_overrides, const Ref<EditorExportPlatform> &);
|
||||
GDVIRTUAL1RC(bool, _should_update_export_options, const Ref<EditorExportPlatform> &);
|
||||
GDVIRTUAL2RC(bool, _get_export_option_visibility, const Ref<EditorExportPlatform> &, String);
|
||||
GDVIRTUAL2RC(String, _get_export_option_warning, const Ref<EditorExportPlatform> &, String);
|
||||
|
||||
GDVIRTUAL0RC_REQUIRED(String, _get_name)
|
||||
|
||||
GDVIRTUAL1RC(bool, _supports_platform, const Ref<EditorExportPlatform> &);
|
||||
|
||||
GDVIRTUAL2RC(PackedStringArray, _get_android_dependencies, const Ref<EditorExportPlatform> &, bool);
|
||||
GDVIRTUAL2RC(PackedStringArray, _get_android_dependencies_maven_repos, const Ref<EditorExportPlatform> &, bool);
|
||||
GDVIRTUAL2RC(PackedStringArray, _get_android_libraries, const Ref<EditorExportPlatform> &, bool);
|
||||
GDVIRTUAL2RC(String, _get_android_manifest_activity_element_contents, const Ref<EditorExportPlatform> &, bool);
|
||||
GDVIRTUAL2RC(String, _get_android_manifest_application_element_contents, const Ref<EditorExportPlatform> &, bool);
|
||||
GDVIRTUAL2RC(String, _get_android_manifest_element_contents, const Ref<EditorExportPlatform> &, bool);
|
||||
GDVIRTUAL2RC(PackedByteArray, _update_android_prebuilt_manifest, const Ref<EditorExportPlatform> &, const PackedByteArray &);
|
||||
|
||||
virtual bool _begin_customize_resources(const Ref<EditorExportPlatform> &p_platform, const Vector<String> &p_features); // Return true if this plugin does property export customization
|
||||
virtual Ref<Resource> _customize_resource(const Ref<Resource> &p_resource, const String &p_path); // If nothing is returned, it means do not touch (nothing changed). If something is returned (either the same or a different resource) it means changes are made.
|
||||
|
||||
virtual bool _begin_customize_scenes(const Ref<EditorExportPlatform> &p_platform, const Vector<String> &p_features); // Return true if this plugin does property export customization
|
||||
virtual Node *_customize_scene(Node *p_root, const String &p_path); // Return true if a change was made
|
||||
|
||||
virtual uint64_t _get_customization_configuration_hash() const; // Hash used for caching customized resources and scenes.
|
||||
|
||||
virtual void _end_customize_scenes();
|
||||
virtual void _end_customize_resources();
|
||||
|
||||
virtual PackedStringArray _get_export_features(const Ref<EditorExportPlatform> &p_export_platform, bool p_debug) const;
|
||||
virtual void _get_export_options(const Ref<EditorExportPlatform> &p_export_platform, List<EditorExportPlatform::ExportOption> *r_options) const;
|
||||
virtual Dictionary _get_export_options_overrides(const Ref<EditorExportPlatform> &p_export_platform) const;
|
||||
virtual bool _should_update_export_options(const Ref<EditorExportPlatform> &p_export_platform) const;
|
||||
virtual bool _get_export_option_visibility(const Ref<EditorExportPlatform> &p_export_platform, const String &p_option_name) const;
|
||||
virtual String _get_export_option_warning(const Ref<EditorExportPlatform> &p_export_platform, const String &p_option_name) const;
|
||||
|
||||
public:
|
||||
virtual String get_name() const;
|
||||
|
||||
virtual bool supports_platform(const Ref<EditorExportPlatform> &p_export_platform) const;
|
||||
PackedStringArray get_export_features(const Ref<EditorExportPlatform> &p_export_platform, bool p_debug) const;
|
||||
|
||||
virtual PackedStringArray get_android_dependencies(const Ref<EditorExportPlatform> &p_export_platform, bool p_debug) const;
|
||||
virtual PackedStringArray get_android_dependencies_maven_repos(const Ref<EditorExportPlatform> &p_export_platform, bool p_debug) const;
|
||||
virtual PackedStringArray get_android_libraries(const Ref<EditorExportPlatform> &p_export_platform, bool p_debug) const;
|
||||
virtual String get_android_manifest_activity_element_contents(const Ref<EditorExportPlatform> &p_export_platform, bool p_debug) const;
|
||||
virtual String get_android_manifest_application_element_contents(const Ref<EditorExportPlatform> &p_export_platform, bool p_debug) const;
|
||||
virtual String get_android_manifest_element_contents(const Ref<EditorExportPlatform> &p_export_platform, bool p_debug) const;
|
||||
virtual PackedByteArray update_android_prebuilt_manifest(const Ref<EditorExportPlatform> &p_export_platform, const PackedByteArray &p_manifest_data) const;
|
||||
|
||||
Vector<String> get_apple_embedded_platform_frameworks() const;
|
||||
Vector<String> get_apple_embedded_platform_embedded_frameworks() const;
|
||||
Vector<String> get_apple_embedded_platform_project_static_libs() const;
|
||||
String get_apple_embedded_platform_plist_content() const;
|
||||
String get_apple_embedded_platform_linker_flags() const;
|
||||
Vector<String> get_apple_embedded_platform_bundle_files() const;
|
||||
String get_apple_embedded_platform_cpp_code() const;
|
||||
const Vector<String> &get_macos_plugin_files() const;
|
||||
Variant get_option(const StringName &p_name) const;
|
||||
};
|
586
editor/export/editor_export_preset.cpp
Normal file
586
editor/export/editor_export_preset.cpp
Normal file
@@ -0,0 +1,586 @@
|
||||
/**************************************************************************/
|
||||
/* editor_export_preset.cpp */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#include "editor_export.h"
|
||||
|
||||
#include "core/config/project_settings.h"
|
||||
|
||||
bool EditorExportPreset::_set(const StringName &p_name, const Variant &p_value) {
|
||||
values[p_name] = p_value;
|
||||
EditorExport::singleton->save_presets();
|
||||
if (update_visibility.has(p_name)) {
|
||||
if (update_visibility[p_name]) {
|
||||
update_value_overrides();
|
||||
notify_property_list_changed();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool EditorExportPreset::_get(const StringName &p_name, Variant &r_ret) const {
|
||||
if (value_overrides.has(p_name)) {
|
||||
r_ret = value_overrides[p_name];
|
||||
return true;
|
||||
}
|
||||
|
||||
if (values.has(p_name)) {
|
||||
r_ret = values[p_name];
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
Variant EditorExportPreset::get_project_setting(const StringName &p_name) {
|
||||
List<String> ftr_list;
|
||||
platform->get_platform_features(&ftr_list);
|
||||
platform->get_preset_features(this, &ftr_list);
|
||||
|
||||
Vector<String> features;
|
||||
for (const String &E : ftr_list) {
|
||||
features.push_back(E);
|
||||
}
|
||||
|
||||
if (!get_custom_features().is_empty()) {
|
||||
Vector<String> tmp_custom_list = get_custom_features().split(",");
|
||||
|
||||
for (int i = 0; i < tmp_custom_list.size(); i++) {
|
||||
String f = tmp_custom_list[i].strip_edges();
|
||||
if (!f.is_empty()) {
|
||||
features.push_back(f);
|
||||
}
|
||||
}
|
||||
}
|
||||
return ProjectSettings::get_singleton()->get_setting_with_override_and_custom_features(p_name, features);
|
||||
}
|
||||
|
||||
void EditorExportPreset::_bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("_get_property_warning", "name"), &EditorExportPreset::_get_property_warning);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("has", "property"), &EditorExportPreset::has);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("get_files_to_export"), &EditorExportPreset::get_files_to_export);
|
||||
ClassDB::bind_method(D_METHOD("get_customized_files"), &EditorExportPreset::get_customized_files);
|
||||
ClassDB::bind_method(D_METHOD("get_customized_files_count"), &EditorExportPreset::get_customized_files_count);
|
||||
ClassDB::bind_method(D_METHOD("has_export_file", "path"), &EditorExportPreset::has_export_file);
|
||||
ClassDB::bind_method(D_METHOD("get_file_export_mode", "path", "default"), &EditorExportPreset::get_file_export_mode, DEFVAL(MODE_FILE_NOT_CUSTOMIZED));
|
||||
ClassDB::bind_method(D_METHOD("get_project_setting", "name"), &EditorExportPreset::get_project_setting);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("get_preset_name"), &EditorExportPreset::get_name);
|
||||
ClassDB::bind_method(D_METHOD("is_runnable"), &EditorExportPreset::is_runnable);
|
||||
ClassDB::bind_method(D_METHOD("are_advanced_options_enabled"), &EditorExportPreset::are_advanced_options_enabled);
|
||||
ClassDB::bind_method(D_METHOD("is_dedicated_server"), &EditorExportPreset::is_dedicated_server);
|
||||
ClassDB::bind_method(D_METHOD("get_export_filter"), &EditorExportPreset::get_export_filter);
|
||||
ClassDB::bind_method(D_METHOD("get_include_filter"), &EditorExportPreset::get_include_filter);
|
||||
ClassDB::bind_method(D_METHOD("get_exclude_filter"), &EditorExportPreset::get_exclude_filter);
|
||||
ClassDB::bind_method(D_METHOD("get_custom_features"), &EditorExportPreset::get_custom_features);
|
||||
ClassDB::bind_method(D_METHOD("get_patches"), &EditorExportPreset::get_patches);
|
||||
ClassDB::bind_method(D_METHOD("get_export_path"), &EditorExportPreset::get_export_path);
|
||||
ClassDB::bind_method(D_METHOD("get_encryption_in_filter"), &EditorExportPreset::get_enc_in_filter);
|
||||
ClassDB::bind_method(D_METHOD("get_encryption_ex_filter"), &EditorExportPreset::get_enc_ex_filter);
|
||||
ClassDB::bind_method(D_METHOD("get_encrypt_pck"), &EditorExportPreset::get_enc_pck);
|
||||
ClassDB::bind_method(D_METHOD("get_encrypt_directory"), &EditorExportPreset::get_enc_directory);
|
||||
ClassDB::bind_method(D_METHOD("get_encryption_key"), &EditorExportPreset::get_script_encryption_key);
|
||||
ClassDB::bind_method(D_METHOD("get_script_export_mode"), &EditorExportPreset::get_script_export_mode);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("get_or_env", "name", "env_var"), &EditorExportPreset::_get_or_env);
|
||||
ClassDB::bind_method(D_METHOD("get_version", "name", "windows_version"), &EditorExportPreset::get_version);
|
||||
|
||||
BIND_ENUM_CONSTANT(EXPORT_ALL_RESOURCES);
|
||||
BIND_ENUM_CONSTANT(EXPORT_SELECTED_SCENES);
|
||||
BIND_ENUM_CONSTANT(EXPORT_SELECTED_RESOURCES);
|
||||
BIND_ENUM_CONSTANT(EXCLUDE_SELECTED_RESOURCES);
|
||||
BIND_ENUM_CONSTANT(EXPORT_CUSTOMIZED);
|
||||
|
||||
BIND_ENUM_CONSTANT(MODE_FILE_NOT_CUSTOMIZED);
|
||||
BIND_ENUM_CONSTANT(MODE_FILE_STRIP);
|
||||
BIND_ENUM_CONSTANT(MODE_FILE_KEEP);
|
||||
BIND_ENUM_CONSTANT(MODE_FILE_REMOVE);
|
||||
|
||||
BIND_ENUM_CONSTANT(MODE_SCRIPT_TEXT);
|
||||
BIND_ENUM_CONSTANT(MODE_SCRIPT_BINARY_TOKENS);
|
||||
BIND_ENUM_CONSTANT(MODE_SCRIPT_BINARY_TOKENS_COMPRESSED);
|
||||
}
|
||||
|
||||
String EditorExportPreset::_get_property_warning(const StringName &p_name) const {
|
||||
if (value_overrides.has(p_name)) {
|
||||
return String();
|
||||
}
|
||||
|
||||
String warning = platform->get_export_option_warning(this, p_name);
|
||||
if (!warning.is_empty()) {
|
||||
warning += "\n";
|
||||
}
|
||||
|
||||
// Get property warning from editor export plugins.
|
||||
Vector<Ref<EditorExportPlugin>> export_plugins = EditorExport::get_singleton()->get_export_plugins();
|
||||
for (int i = 0; i < export_plugins.size(); i++) {
|
||||
if (!export_plugins[i]->supports_platform(platform)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
export_plugins.write[i]->set_export_preset(Ref<EditorExportPreset>(this));
|
||||
String plugin_warning = export_plugins[i]->_get_export_option_warning(platform, p_name);
|
||||
if (!plugin_warning.is_empty()) {
|
||||
warning += plugin_warning + "\n";
|
||||
}
|
||||
}
|
||||
|
||||
return warning;
|
||||
}
|
||||
|
||||
void EditorExportPreset::_get_property_list(List<PropertyInfo> *p_list) const {
|
||||
for (const KeyValue<StringName, PropertyInfo> &E : properties) {
|
||||
if (!value_overrides.has(E.key)) {
|
||||
bool property_visible = platform->get_export_option_visibility(this, E.key);
|
||||
if (!property_visible) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Get option visibility from editor export plugins.
|
||||
Vector<Ref<EditorExportPlugin>> export_plugins = EditorExport::get_singleton()->get_export_plugins();
|
||||
for (int i = 0; i < export_plugins.size(); i++) {
|
||||
if (!export_plugins[i]->supports_platform(platform)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
export_plugins.write[i]->set_export_preset(Ref<EditorExportPreset>(this));
|
||||
property_visible = export_plugins[i]->_get_export_option_visibility(platform, E.key);
|
||||
if (!property_visible) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (property_visible) {
|
||||
p_list->push_back(E.value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ref<EditorExportPlatform> EditorExportPreset::get_platform() const {
|
||||
return platform;
|
||||
}
|
||||
|
||||
void EditorExportPreset::update_files() {
|
||||
{
|
||||
Vector<String> to_remove;
|
||||
for (const String &E : selected_files) {
|
||||
if (!FileAccess::exists(E)) {
|
||||
to_remove.push_back(E);
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < to_remove.size(); ++i) {
|
||||
selected_files.erase(to_remove[i]);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
Vector<String> to_remove;
|
||||
for (const KeyValue<String, FileExportMode> &E : customized_files) {
|
||||
if (!FileAccess::exists(E.key) && !DirAccess::exists(E.key)) {
|
||||
to_remove.push_back(E.key);
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < to_remove.size(); ++i) {
|
||||
customized_files.erase(to_remove[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void EditorExportPreset::update_value_overrides() {
|
||||
Vector<Ref<EditorExportPlugin>> export_plugins = EditorExport::get_singleton()->get_export_plugins();
|
||||
HashMap<StringName, Variant> new_value_overrides;
|
||||
|
||||
value_overrides.clear();
|
||||
|
||||
for (int i = 0; i < export_plugins.size(); i++) {
|
||||
if (!export_plugins[i]->supports_platform(platform)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
export_plugins.write[i]->set_export_preset(Ref<EditorExportPreset>(this));
|
||||
|
||||
Dictionary plugin_overrides = export_plugins[i]->_get_export_options_overrides(platform);
|
||||
if (!plugin_overrides.is_empty()) {
|
||||
for (const KeyValue<Variant, Variant> &kv : plugin_overrides) {
|
||||
const StringName &key = kv.key;
|
||||
const Variant &value = kv.value;
|
||||
if (new_value_overrides.has(key) && new_value_overrides[key] != value) {
|
||||
WARN_PRINT_ED(vformat("Editor export plugin '%s' overrides pre-existing export option override '%s' with new value.", export_plugins[i]->get_name(), key));
|
||||
}
|
||||
new_value_overrides[key] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
value_overrides = new_value_overrides;
|
||||
notify_property_list_changed();
|
||||
}
|
||||
|
||||
Vector<String> EditorExportPreset::get_files_to_export() const {
|
||||
Vector<String> files;
|
||||
for (const String &E : selected_files) {
|
||||
files.push_back(E);
|
||||
}
|
||||
return files;
|
||||
}
|
||||
|
||||
Dictionary EditorExportPreset::get_customized_files() const {
|
||||
Dictionary files;
|
||||
for (const KeyValue<String, FileExportMode> &E : customized_files) {
|
||||
String mode;
|
||||
switch (E.value) {
|
||||
case MODE_FILE_NOT_CUSTOMIZED: {
|
||||
continue;
|
||||
} break;
|
||||
case MODE_FILE_STRIP: {
|
||||
mode = "strip";
|
||||
} break;
|
||||
case MODE_FILE_KEEP: {
|
||||
mode = "keep";
|
||||
} break;
|
||||
case MODE_FILE_REMOVE: {
|
||||
mode = "remove";
|
||||
}
|
||||
}
|
||||
files[E.key] = mode;
|
||||
}
|
||||
return files;
|
||||
}
|
||||
|
||||
int EditorExportPreset::get_customized_files_count() const {
|
||||
return customized_files.size();
|
||||
}
|
||||
|
||||
void EditorExportPreset::set_customized_files(const Dictionary &p_files) {
|
||||
for (const Variant *key = p_files.next(nullptr); key; key = p_files.next(key)) {
|
||||
EditorExportPreset::FileExportMode mode = EditorExportPreset::MODE_FILE_NOT_CUSTOMIZED;
|
||||
String value = p_files[*key];
|
||||
if (value == "strip") {
|
||||
mode = EditorExportPreset::MODE_FILE_STRIP;
|
||||
} else if (value == "keep") {
|
||||
mode = EditorExportPreset::MODE_FILE_KEEP;
|
||||
} else if (value == "remove") {
|
||||
mode = EditorExportPreset::MODE_FILE_REMOVE;
|
||||
}
|
||||
set_file_export_mode(*key, mode);
|
||||
}
|
||||
}
|
||||
|
||||
void EditorExportPreset::set_name(const String &p_name) {
|
||||
name = p_name;
|
||||
EditorExport::singleton->save_presets();
|
||||
}
|
||||
|
||||
String EditorExportPreset::get_name() const {
|
||||
return name;
|
||||
}
|
||||
|
||||
void EditorExportPreset::set_runnable(bool p_enable) {
|
||||
runnable = p_enable;
|
||||
EditorExport::singleton->emit_presets_runnable_changed();
|
||||
EditorExport::singleton->save_presets();
|
||||
}
|
||||
|
||||
bool EditorExportPreset::is_runnable() const {
|
||||
return runnable;
|
||||
}
|
||||
|
||||
void EditorExportPreset::set_advanced_options_enabled(bool p_enabled) {
|
||||
if (advanced_options_enabled == p_enabled) {
|
||||
return;
|
||||
}
|
||||
advanced_options_enabled = p_enabled;
|
||||
EditorExport::singleton->save_presets();
|
||||
notify_property_list_changed();
|
||||
}
|
||||
|
||||
bool EditorExportPreset::are_advanced_options_enabled() const {
|
||||
return advanced_options_enabled;
|
||||
}
|
||||
|
||||
void EditorExportPreset::set_dedicated_server(bool p_enable) {
|
||||
dedicated_server = p_enable;
|
||||
EditorExport::singleton->save_presets();
|
||||
}
|
||||
|
||||
bool EditorExportPreset::is_dedicated_server() const {
|
||||
return dedicated_server;
|
||||
}
|
||||
|
||||
void EditorExportPreset::set_export_filter(ExportFilter p_filter) {
|
||||
export_filter = p_filter;
|
||||
EditorExport::singleton->save_presets();
|
||||
}
|
||||
|
||||
EditorExportPreset::ExportFilter EditorExportPreset::get_export_filter() const {
|
||||
return export_filter;
|
||||
}
|
||||
|
||||
void EditorExportPreset::set_include_filter(const String &p_include) {
|
||||
include_filter = p_include;
|
||||
EditorExport::singleton->save_presets();
|
||||
}
|
||||
|
||||
String EditorExportPreset::get_include_filter() const {
|
||||
return include_filter;
|
||||
}
|
||||
|
||||
void EditorExportPreset::set_export_path(const String &p_path) {
|
||||
export_path = p_path;
|
||||
/* NOTE(SonerSound): if there is a need to implement a PropertyHint that specifically indicates a relative path,
|
||||
* this should be removed. */
|
||||
if (export_path.is_absolute_path()) {
|
||||
String res_path = OS::get_singleton()->get_resource_dir();
|
||||
export_path = res_path.path_to_file(export_path);
|
||||
}
|
||||
EditorExport::singleton->save_presets();
|
||||
}
|
||||
|
||||
String EditorExportPreset::get_export_path() const {
|
||||
return export_path;
|
||||
}
|
||||
|
||||
void EditorExportPreset::set_exclude_filter(const String &p_exclude) {
|
||||
exclude_filter = p_exclude;
|
||||
EditorExport::singleton->save_presets();
|
||||
}
|
||||
|
||||
String EditorExportPreset::get_exclude_filter() const {
|
||||
return exclude_filter;
|
||||
}
|
||||
|
||||
void EditorExportPreset::add_export_file(const String &p_path) {
|
||||
selected_files.insert(p_path);
|
||||
EditorExport::singleton->save_presets();
|
||||
}
|
||||
|
||||
void EditorExportPreset::remove_export_file(const String &p_path) {
|
||||
selected_files.erase(p_path);
|
||||
EditorExport::singleton->save_presets();
|
||||
}
|
||||
|
||||
bool EditorExportPreset::has_export_file(const String &p_path) {
|
||||
return selected_files.has(p_path);
|
||||
}
|
||||
|
||||
void EditorExportPreset::set_file_export_mode(const String &p_path, EditorExportPreset::FileExportMode p_mode) {
|
||||
if (p_mode == FileExportMode::MODE_FILE_NOT_CUSTOMIZED) {
|
||||
customized_files.erase(p_path);
|
||||
} else {
|
||||
customized_files.insert(p_path, p_mode);
|
||||
}
|
||||
EditorExport::singleton->save_presets();
|
||||
}
|
||||
|
||||
EditorExportPreset::FileExportMode EditorExportPreset::get_file_export_mode(const String &p_path, EditorExportPreset::FileExportMode p_default) const {
|
||||
HashMap<String, FileExportMode>::ConstIterator i = customized_files.find(p_path);
|
||||
if (i) {
|
||||
return i->value;
|
||||
}
|
||||
return p_default;
|
||||
}
|
||||
|
||||
void EditorExportPreset::add_patch(const String &p_path, int p_at_pos) {
|
||||
ERR_FAIL_COND_EDMSG(patches.has(p_path), vformat("Failed to add patch \"%s\". Patches must be unique.", p_path));
|
||||
|
||||
if (p_at_pos < 0) {
|
||||
patches.push_back(p_path);
|
||||
} else {
|
||||
patches.insert(p_at_pos, p_path);
|
||||
}
|
||||
|
||||
EditorExport::singleton->save_presets();
|
||||
}
|
||||
|
||||
void EditorExportPreset::set_patch(int p_index, const String &p_path) {
|
||||
remove_patch(p_index);
|
||||
add_patch(p_path, p_index);
|
||||
}
|
||||
|
||||
String EditorExportPreset::get_patch(int p_index) {
|
||||
ERR_FAIL_INDEX_V(p_index, patches.size(), String());
|
||||
return patches[p_index];
|
||||
}
|
||||
|
||||
void EditorExportPreset::remove_patch(int p_index) {
|
||||
ERR_FAIL_INDEX(p_index, patches.size());
|
||||
patches.remove_at(p_index);
|
||||
EditorExport::singleton->save_presets();
|
||||
}
|
||||
|
||||
void EditorExportPreset::set_patches(const Vector<String> &p_patches) {
|
||||
patches = p_patches;
|
||||
}
|
||||
|
||||
Vector<String> EditorExportPreset::get_patches() const {
|
||||
return patches;
|
||||
}
|
||||
|
||||
void EditorExportPreset::set_custom_features(const String &p_custom_features) {
|
||||
custom_features = p_custom_features;
|
||||
EditorExport::singleton->save_presets();
|
||||
}
|
||||
|
||||
String EditorExportPreset::get_custom_features() const {
|
||||
return custom_features;
|
||||
}
|
||||
|
||||
void EditorExportPreset::set_enc_in_filter(const String &p_filter) {
|
||||
enc_in_filters = p_filter;
|
||||
EditorExport::singleton->save_presets();
|
||||
}
|
||||
|
||||
String EditorExportPreset::get_enc_in_filter() const {
|
||||
return enc_in_filters;
|
||||
}
|
||||
|
||||
void EditorExportPreset::set_enc_ex_filter(const String &p_filter) {
|
||||
enc_ex_filters = p_filter;
|
||||
EditorExport::singleton->save_presets();
|
||||
}
|
||||
|
||||
String EditorExportPreset::get_enc_ex_filter() const {
|
||||
return enc_ex_filters;
|
||||
}
|
||||
|
||||
void EditorExportPreset::set_seed(uint64_t p_seed) {
|
||||
seed = p_seed;
|
||||
EditorExport::singleton->save_presets();
|
||||
}
|
||||
|
||||
uint64_t EditorExportPreset::get_seed() const {
|
||||
return seed;
|
||||
}
|
||||
|
||||
void EditorExportPreset::set_enc_pck(bool p_enabled) {
|
||||
enc_pck = p_enabled;
|
||||
EditorExport::singleton->save_presets();
|
||||
}
|
||||
|
||||
bool EditorExportPreset::get_enc_pck() const {
|
||||
return enc_pck;
|
||||
}
|
||||
|
||||
void EditorExportPreset::set_enc_directory(bool p_enabled) {
|
||||
enc_directory = p_enabled;
|
||||
EditorExport::singleton->save_presets();
|
||||
}
|
||||
|
||||
bool EditorExportPreset::get_enc_directory() const {
|
||||
return enc_directory;
|
||||
}
|
||||
|
||||
void EditorExportPreset::set_script_encryption_key(const String &p_key) {
|
||||
script_key = p_key;
|
||||
EditorExport::singleton->save_presets();
|
||||
}
|
||||
|
||||
String EditorExportPreset::get_script_encryption_key() const {
|
||||
return script_key;
|
||||
}
|
||||
|
||||
void EditorExportPreset::set_script_export_mode(int p_mode) {
|
||||
script_mode = p_mode;
|
||||
EditorExport::singleton->save_presets();
|
||||
}
|
||||
|
||||
int EditorExportPreset::get_script_export_mode() const {
|
||||
return script_mode;
|
||||
}
|
||||
|
||||
Variant EditorExportPreset::get_or_env(const StringName &p_name, const String &p_env_var, bool *r_valid) const {
|
||||
const String from_env = OS::get_singleton()->get_environment(p_env_var);
|
||||
if (!from_env.is_empty()) {
|
||||
if (r_valid) {
|
||||
*r_valid = true;
|
||||
}
|
||||
return from_env;
|
||||
}
|
||||
return get(p_name, r_valid);
|
||||
}
|
||||
|
||||
_FORCE_INLINE_ bool _check_digits(const String &p_str) {
|
||||
for (int i = 0; i < p_str.length(); i++) {
|
||||
char32_t c = p_str.operator[](i);
|
||||
if (!is_digit(c)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
String EditorExportPreset::get_version(const StringName &p_preset_string, bool p_windows_version) const {
|
||||
String result = get(p_preset_string);
|
||||
if (result.is_empty()) {
|
||||
result = GLOBAL_GET("application/config/version");
|
||||
|
||||
// Split and validate version number components.
|
||||
const PackedStringArray result_split = result.split(".", false);
|
||||
bool valid_version = !result_split.is_empty();
|
||||
for (const String &E : result_split) {
|
||||
if (!_check_digits(E)) {
|
||||
valid_version = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (valid_version) {
|
||||
if (p_windows_version) {
|
||||
// Modify version number to match Windows constraints (version numbers must have 4 components).
|
||||
if (result_split.size() == 1) {
|
||||
result = result + ".0.0.0";
|
||||
} else if (result_split.size() == 2) {
|
||||
result = result + ".0.0";
|
||||
} else if (result_split.size() == 3) {
|
||||
result = result + ".0";
|
||||
} else {
|
||||
result = vformat("%s.%s.%s.%s", result_split[0], result_split[1], result_split[2], result_split[3]);
|
||||
}
|
||||
} else {
|
||||
result = String(".").join(result_split);
|
||||
}
|
||||
} else {
|
||||
if (!result.is_empty()) {
|
||||
WARN_PRINT(vformat("Invalid version number \"%s\". The version number can only contain numeric characters (0-9) and non-consecutive periods (.).", result));
|
||||
}
|
||||
if (p_windows_version) {
|
||||
result = "1.0.0.0";
|
||||
} else {
|
||||
result = "1.0.0";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
203
editor/export/editor_export_preset.h
Normal file
203
editor/export/editor_export_preset.h
Normal file
@@ -0,0 +1,203 @@
|
||||
/**************************************************************************/
|
||||
/* editor_export_preset.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
class EditorExportPlatform;
|
||||
|
||||
#include "core/object/ref_counted.h"
|
||||
|
||||
class EditorExportPreset : public RefCounted {
|
||||
GDCLASS(EditorExportPreset, RefCounted);
|
||||
|
||||
public:
|
||||
enum ExportFilter {
|
||||
EXPORT_ALL_RESOURCES,
|
||||
EXPORT_SELECTED_SCENES,
|
||||
EXPORT_SELECTED_RESOURCES,
|
||||
EXCLUDE_SELECTED_RESOURCES,
|
||||
EXPORT_CUSTOMIZED,
|
||||
};
|
||||
|
||||
enum FileExportMode {
|
||||
MODE_FILE_NOT_CUSTOMIZED,
|
||||
MODE_FILE_STRIP,
|
||||
MODE_FILE_KEEP,
|
||||
MODE_FILE_REMOVE,
|
||||
};
|
||||
|
||||
enum ScriptExportMode {
|
||||
MODE_SCRIPT_TEXT,
|
||||
MODE_SCRIPT_BINARY_TOKENS,
|
||||
MODE_SCRIPT_BINARY_TOKENS_COMPRESSED,
|
||||
};
|
||||
|
||||
private:
|
||||
Ref<EditorExportPlatform> platform;
|
||||
ExportFilter export_filter = EXPORT_ALL_RESOURCES;
|
||||
String include_filter;
|
||||
String exclude_filter;
|
||||
String export_path;
|
||||
|
||||
String exporter;
|
||||
HashSet<String> selected_files;
|
||||
HashMap<String, FileExportMode> customized_files;
|
||||
bool runnable = false;
|
||||
bool advanced_options_enabled = false;
|
||||
bool dedicated_server = false;
|
||||
|
||||
Vector<String> patches;
|
||||
|
||||
friend class EditorExport;
|
||||
friend class EditorExportPlatform;
|
||||
|
||||
HashMap<StringName, PropertyInfo> properties;
|
||||
HashMap<StringName, Variant> values;
|
||||
HashMap<StringName, Variant> value_overrides;
|
||||
HashMap<StringName, bool> update_visibility;
|
||||
|
||||
String name;
|
||||
|
||||
String custom_features;
|
||||
|
||||
String enc_in_filters;
|
||||
String enc_ex_filters;
|
||||
bool enc_pck = false;
|
||||
bool enc_directory = false;
|
||||
uint64_t seed = 0;
|
||||
|
||||
String script_key;
|
||||
int script_mode = MODE_SCRIPT_BINARY_TOKENS_COMPRESSED;
|
||||
|
||||
protected:
|
||||
bool _set(const StringName &p_name, const Variant &p_value);
|
||||
bool _get(const StringName &p_name, Variant &r_ret) const;
|
||||
void _get_property_list(List<PropertyInfo> *p_list) const;
|
||||
|
||||
String _get_property_warning(const StringName &p_name) const;
|
||||
|
||||
static void _bind_methods();
|
||||
|
||||
public:
|
||||
Ref<EditorExportPlatform> get_platform() const;
|
||||
|
||||
bool has(const StringName &p_property) const { return values.has(p_property); }
|
||||
|
||||
void update_files();
|
||||
void update_value_overrides();
|
||||
|
||||
Vector<String> get_files_to_export() const;
|
||||
Dictionary get_customized_files() const;
|
||||
int get_customized_files_count() const;
|
||||
void set_customized_files(const Dictionary &p_files);
|
||||
|
||||
void add_export_file(const String &p_path);
|
||||
void remove_export_file(const String &p_path);
|
||||
bool has_export_file(const String &p_path);
|
||||
|
||||
void set_file_export_mode(const String &p_path, FileExportMode p_mode);
|
||||
FileExportMode get_file_export_mode(const String &p_path, FileExportMode p_default = MODE_FILE_NOT_CUSTOMIZED) const;
|
||||
|
||||
Variant get_project_setting(const StringName &p_name);
|
||||
|
||||
void set_name(const String &p_name);
|
||||
String get_name() const;
|
||||
|
||||
void set_runnable(bool p_enable);
|
||||
bool is_runnable() const;
|
||||
|
||||
void set_advanced_options_enabled(bool p_enabled);
|
||||
bool are_advanced_options_enabled() const;
|
||||
|
||||
void set_dedicated_server(bool p_enable);
|
||||
bool is_dedicated_server() const;
|
||||
|
||||
void set_export_filter(ExportFilter p_filter);
|
||||
ExportFilter get_export_filter() const;
|
||||
|
||||
void set_include_filter(const String &p_include);
|
||||
String get_include_filter() const;
|
||||
|
||||
void set_exclude_filter(const String &p_exclude);
|
||||
String get_exclude_filter() const;
|
||||
|
||||
void add_patch(const String &p_path, int p_at_pos = -1);
|
||||
void set_patch(int p_index, const String &p_path);
|
||||
String get_patch(int p_index);
|
||||
void remove_patch(int p_index);
|
||||
void set_patches(const Vector<String> &p_patches);
|
||||
Vector<String> get_patches() const;
|
||||
|
||||
void set_custom_features(const String &p_custom_features);
|
||||
String get_custom_features() const;
|
||||
|
||||
void set_export_path(const String &p_path);
|
||||
String get_export_path() const;
|
||||
|
||||
void set_enc_in_filter(const String &p_filter);
|
||||
String get_enc_in_filter() const;
|
||||
|
||||
void set_enc_ex_filter(const String &p_filter);
|
||||
String get_enc_ex_filter() const;
|
||||
|
||||
void set_seed(uint64_t p_seed);
|
||||
uint64_t get_seed() const;
|
||||
|
||||
void set_enc_pck(bool p_enabled);
|
||||
bool get_enc_pck() const;
|
||||
|
||||
void set_enc_directory(bool p_enabled);
|
||||
bool get_enc_directory() const;
|
||||
|
||||
void set_script_encryption_key(const String &p_key);
|
||||
String get_script_encryption_key() const;
|
||||
|
||||
void set_script_export_mode(int p_mode);
|
||||
int get_script_export_mode() const;
|
||||
|
||||
Variant _get_or_env(const StringName &p_name, const String &p_env_var) const {
|
||||
return get_or_env(p_name, p_env_var);
|
||||
}
|
||||
Variant get_or_env(const StringName &p_name, const String &p_env_var, bool *r_valid = nullptr) const;
|
||||
|
||||
// Return the preset's version number, or fall back to the
|
||||
// `application/config/version` project setting if set to an empty string.
|
||||
// If `p_windows_version` is `true`, formats the returned version number to
|
||||
// be compatible with Windows executable metadata (which requires a
|
||||
// 4-component format).
|
||||
String get_version(const StringName &p_name, bool p_windows_version = false) const;
|
||||
|
||||
const HashMap<StringName, PropertyInfo> &get_properties() const { return properties; }
|
||||
const HashMap<StringName, Variant> &get_values() const { return values; }
|
||||
};
|
||||
|
||||
VARIANT_ENUM_CAST(EditorExportPreset::ExportFilter);
|
||||
VARIANT_ENUM_CAST(EditorExportPreset::FileExportMode);
|
||||
VARIANT_ENUM_CAST(EditorExportPreset::ScriptExportMode);
|
1171
editor/export/export_template_manager.cpp
Normal file
1171
editor/export/export_template_manager.cpp
Normal file
File diff suppressed because it is too large
Load Diff
137
editor/export/export_template_manager.h
Normal file
137
editor/export/export_template_manager.h
Normal file
@@ -0,0 +1,137 @@
|
||||
/**************************************************************************/
|
||||
/* export_template_manager.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "scene/gui/dialogs.h"
|
||||
|
||||
class EditorExportPreset;
|
||||
class ExportTemplateVersion;
|
||||
class FileDialog;
|
||||
class HTTPRequest;
|
||||
class MenuButton;
|
||||
class OptionButton;
|
||||
class ProgressBar;
|
||||
class Tree;
|
||||
|
||||
class ExportTemplateManager : public AcceptDialog {
|
||||
GDCLASS(ExportTemplateManager, AcceptDialog);
|
||||
|
||||
bool current_version_exists = false;
|
||||
bool mirrors_available = false;
|
||||
bool is_refreshing_mirrors = false;
|
||||
bool is_downloading_templates = false;
|
||||
float update_countdown = 0;
|
||||
|
||||
Label *current_value = nullptr;
|
||||
Label *current_missing_label = nullptr;
|
||||
Label *current_installed_label = nullptr;
|
||||
|
||||
HBoxContainer *current_installed_hb = nullptr;
|
||||
LineEdit *current_installed_path = nullptr;
|
||||
Button *current_uninstall_button = nullptr;
|
||||
|
||||
VBoxContainer *install_options_vb = nullptr;
|
||||
OptionButton *mirrors_list = nullptr;
|
||||
|
||||
enum MirrorAction {
|
||||
VISIT_WEB_MIRROR,
|
||||
COPY_MIRROR_URL,
|
||||
};
|
||||
|
||||
MenuButton *mirror_options_button = nullptr;
|
||||
HBoxContainer *enable_online_hb = nullptr;
|
||||
HBoxContainer *download_progress_hb = nullptr;
|
||||
ProgressBar *download_progress_bar = nullptr;
|
||||
Label *download_progress_label = nullptr;
|
||||
HTTPRequest *download_templates = nullptr;
|
||||
Button *install_file_button = nullptr;
|
||||
Button *download_current_button = nullptr;
|
||||
HTTPRequest *request_mirrors = nullptr;
|
||||
|
||||
enum TemplatesAction {
|
||||
OPEN_TEMPLATE_FOLDER,
|
||||
UNINSTALL_TEMPLATE,
|
||||
};
|
||||
|
||||
Tree *installed_table = nullptr;
|
||||
|
||||
ConfirmationDialog *uninstall_confirm = nullptr;
|
||||
String uninstall_version;
|
||||
FileDialog *install_file_dialog = nullptr;
|
||||
AcceptDialog *hide_dialog_accept = nullptr;
|
||||
|
||||
void _update_template_status();
|
||||
|
||||
void _download_current();
|
||||
void _download_template(const String &p_url, bool p_skip_check = false);
|
||||
void _download_template_completed(int p_status, int p_code, const PackedStringArray &headers, const PackedByteArray &p_data);
|
||||
void _cancel_template_download();
|
||||
void _refresh_mirrors();
|
||||
void _refresh_mirrors_completed(int p_status, int p_code, const PackedStringArray &headers, const PackedByteArray &p_data);
|
||||
void _force_online_mode();
|
||||
|
||||
bool _humanize_http_status(HTTPRequest *p_request, String *r_status, int *r_downloaded_bytes, int *r_total_bytes);
|
||||
void _set_current_progress_status(const String &p_status, bool p_error = false);
|
||||
void _set_current_progress_value(float p_value, const String &p_status);
|
||||
|
||||
void _install_file();
|
||||
bool _install_file_selected(const String &p_file, bool p_skip_progress = false);
|
||||
|
||||
void _uninstall_template(const String &p_version);
|
||||
void _uninstall_template_confirmed();
|
||||
|
||||
String _get_selected_mirror() const;
|
||||
void _mirror_options_button_cbk(int p_id);
|
||||
void _installed_table_button_cbk(Object *p_item, int p_column, int p_id, MouseButton p_button);
|
||||
|
||||
void _open_template_folder(const String &p_version);
|
||||
|
||||
virtual void ok_pressed() override;
|
||||
void _hide_dialog();
|
||||
|
||||
protected:
|
||||
void _notification(int p_what);
|
||||
|
||||
public:
|
||||
static String get_android_build_directory(const Ref<EditorExportPreset> &p_preset);
|
||||
static String get_android_source_zip(const Ref<EditorExportPreset> &p_preset);
|
||||
static String get_android_template_identifier(const Ref<EditorExportPreset> &p_preset);
|
||||
|
||||
bool is_android_template_installed(const Ref<EditorExportPreset> &p_preset);
|
||||
bool can_install_android_template(const Ref<EditorExportPreset> &p_preset);
|
||||
Error install_android_template(const Ref<EditorExportPreset> &p_preset);
|
||||
|
||||
Error install_android_template_from_file(const String &p_file, const Ref<EditorExportPreset> &p_preset);
|
||||
|
||||
void popup_manager();
|
||||
|
||||
ExportTemplateManager();
|
||||
};
|
169
editor/export/gdextension_export_plugin.h
Normal file
169
editor/export/gdextension_export_plugin.h
Normal file
@@ -0,0 +1,169 @@
|
||||
/**************************************************************************/
|
||||
/* gdextension_export_plugin.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/extension/gdextension_library_loader.h"
|
||||
#include "editor/export/editor_export.h"
|
||||
|
||||
class GDExtensionExportPlugin : public EditorExportPlugin {
|
||||
protected:
|
||||
virtual void _export_file(const String &p_path, const String &p_type, const HashSet<String> &p_features);
|
||||
virtual String get_name() const { return "GDExtension"; }
|
||||
};
|
||||
|
||||
void GDExtensionExportPlugin::_export_file(const String &p_path, const String &p_type, const HashSet<String> &p_features) {
|
||||
if (p_type != "GDExtension") {
|
||||
return;
|
||||
}
|
||||
|
||||
Ref<ConfigFile> config;
|
||||
config.instantiate();
|
||||
|
||||
Error err = config->load(p_path);
|
||||
ERR_FAIL_COND_MSG(err, "Failed to load GDExtension file: " + p_path);
|
||||
|
||||
// Check whether this GDExtension should be exported.
|
||||
bool android_aar_plugin = config->get_value("configuration", "android_aar_plugin", false);
|
||||
if (android_aar_plugin && p_features.has("android")) {
|
||||
// The gdextension configuration and Android .so files will be provided by the Android aar
|
||||
// plugin it's part of, so we abort here.
|
||||
skip();
|
||||
return;
|
||||
}
|
||||
|
||||
ERR_FAIL_COND_MSG(!config->has_section_key("configuration", "entry_symbol"), "Failed to export GDExtension file, missing entry symbol: " + p_path);
|
||||
|
||||
String entry_symbol = config->get_value("configuration", "entry_symbol");
|
||||
|
||||
HashSet<String> all_archs;
|
||||
all_archs.insert("x86_32");
|
||||
all_archs.insert("x86_64");
|
||||
all_archs.insert("arm32");
|
||||
all_archs.insert("arm64");
|
||||
all_archs.insert("rv64");
|
||||
all_archs.insert("ppc64");
|
||||
all_archs.insert("wasm32");
|
||||
all_archs.insert("loongarch64");
|
||||
all_archs.insert("universal");
|
||||
|
||||
HashSet<String> archs;
|
||||
HashSet<String> features_wo_arch;
|
||||
Vector<String> features_vector;
|
||||
for (const String &tag : p_features) {
|
||||
if (all_archs.has(tag)) {
|
||||
archs.insert(tag);
|
||||
} else {
|
||||
features_wo_arch.insert(tag);
|
||||
}
|
||||
features_vector.append(tag);
|
||||
}
|
||||
|
||||
if (archs.is_empty()) {
|
||||
archs.insert("unknown_arch"); // Not archs specified, still try to match.
|
||||
}
|
||||
|
||||
HashSet<String> libs_added;
|
||||
struct FoundLibInfo {
|
||||
int count = 0;
|
||||
Vector<String> libs;
|
||||
};
|
||||
HashMap<String, FoundLibInfo> libs_found;
|
||||
for (const String &arch_tag : archs) {
|
||||
if (arch_tag != "universal") {
|
||||
libs_found[arch_tag] = FoundLibInfo();
|
||||
}
|
||||
}
|
||||
|
||||
for (const String &arch_tag : archs) {
|
||||
PackedStringArray tags;
|
||||
String library_path = GDExtensionLibraryLoader::find_extension_library(
|
||||
p_path, config, [features_wo_arch, arch_tag](const String &p_feature) { return features_wo_arch.has(p_feature) || (p_feature == arch_tag); }, &tags);
|
||||
|
||||
if (libs_added.has(library_path)) {
|
||||
continue; // Universal library, already added for another arch, do not duplicate.
|
||||
}
|
||||
if (!library_path.is_empty()) {
|
||||
libs_added.insert(library_path);
|
||||
add_shared_object(library_path, tags);
|
||||
|
||||
if (p_features.has("apple_embedded") && (library_path.ends_with(".a") || library_path.ends_with(".xcframework"))) {
|
||||
String additional_code = "extern void register_dynamic_symbol(char *name, void *address);\n"
|
||||
"extern void add_apple_embedded_platform_init_callback(void (*cb)());\n"
|
||||
"\n"
|
||||
"extern \"C\" void $ENTRY();\n"
|
||||
"void $ENTRY_init() {\n"
|
||||
" if (&$ENTRY) register_dynamic_symbol((char *)\"$ENTRY\", (void *)$ENTRY);\n"
|
||||
"}\n"
|
||||
"struct $ENTRY_struct {\n"
|
||||
" $ENTRY_struct() {\n"
|
||||
" add_apple_embedded_platform_init_callback($ENTRY_init);\n"
|
||||
" }\n"
|
||||
"};\n"
|
||||
"$ENTRY_struct $ENTRY_struct_instance;\n\n";
|
||||
additional_code = additional_code.replace("$ENTRY", entry_symbol);
|
||||
add_apple_embedded_platform_cpp_code(additional_code);
|
||||
|
||||
String linker_flags = "-Wl,-U,_" + entry_symbol;
|
||||
add_apple_embedded_platform_linker_flags(linker_flags);
|
||||
}
|
||||
|
||||
// Update found library info.
|
||||
if (arch_tag == "universal") {
|
||||
for (const String &sub_arch_tag : archs) {
|
||||
if (sub_arch_tag != "universal") {
|
||||
libs_found[sub_arch_tag].count++;
|
||||
libs_found[sub_arch_tag].libs.push_back(library_path);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
libs_found[arch_tag].count++;
|
||||
libs_found[arch_tag].libs.push_back(library_path);
|
||||
}
|
||||
}
|
||||
|
||||
Vector<SharedObject> dependencies_shared_objects = GDExtensionLibraryLoader::find_extension_dependencies(p_path, config, [p_features](String p_feature) { return p_features.has(p_feature); });
|
||||
for (const SharedObject &shared_object : dependencies_shared_objects) {
|
||||
_add_shared_object(shared_object);
|
||||
}
|
||||
}
|
||||
|
||||
for (const KeyValue<String, FoundLibInfo> &E : libs_found) {
|
||||
if (E.value.count == 0) {
|
||||
if (get_export_platform().is_valid()) {
|
||||
get_export_platform()->add_message(EditorExportPlatform::EXPORT_MESSAGE_WARNING, TTR("GDExtension"), vformat(TTR("No \"%s\" library found for GDExtension: \"%s\". Possible feature flags for your platform: %s"), E.key, p_path, String(", ").join(features_vector)));
|
||||
}
|
||||
} else if (E.value.count > 1) {
|
||||
if (get_export_platform().is_valid()) {
|
||||
get_export_platform()->add_message(EditorExportPlatform::EXPORT_MESSAGE_WARNING, TTR("GDExtension"), vformat(TTR("Multiple \"%s\" libraries found for GDExtension: \"%s\": \"%s\"."), E.key, p_path, String(", ").join(E.value.libs)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
340
editor/export/lipo.cpp
Normal file
340
editor/export/lipo.cpp
Normal file
@@ -0,0 +1,340 @@
|
||||
/**************************************************************************/
|
||||
/* lipo.cpp */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#include "lipo.h"
|
||||
|
||||
#include "macho.h"
|
||||
|
||||
bool LipO::is_lipo(const String &p_path) {
|
||||
Ref<FileAccess> fb = FileAccess::open(p_path, FileAccess::READ);
|
||||
ERR_FAIL_COND_V_MSG(fb.is_null(), false, vformat("LipO: Can't open file: \"%s\".", p_path));
|
||||
uint32_t magic = fb->get_32();
|
||||
return (magic == 0xbebafeca || magic == 0xcafebabe || magic == 0xbfbafeca || magic == 0xcafebabf);
|
||||
}
|
||||
|
||||
bool LipO::create_file(const String &p_output_path, const Vector<String> &p_files) {
|
||||
close();
|
||||
|
||||
fa = FileAccess::open(p_output_path, FileAccess::WRITE);
|
||||
ERR_FAIL_COND_V_MSG(fa.is_null(), false, vformat("LipO: Can't open file: \"%s\".", p_output_path));
|
||||
|
||||
uint64_t max_size = 0;
|
||||
for (int i = 0; i < p_files.size(); i++) {
|
||||
{
|
||||
MachO mh;
|
||||
if (!mh.open_file(p_files[i])) {
|
||||
ERR_FAIL_V_MSG(false, vformat("LipO: Invalid MachO file: \"%s\".", p_files[i]));
|
||||
}
|
||||
|
||||
FatArch arch;
|
||||
arch.cputype = mh.get_cputype();
|
||||
arch.cpusubtype = mh.get_cpusubtype();
|
||||
arch.offset = 0;
|
||||
arch.size = mh.get_size();
|
||||
arch.align = mh.get_align();
|
||||
max_size += arch.size;
|
||||
|
||||
archs.push_back(arch);
|
||||
}
|
||||
|
||||
Ref<FileAccess> fb = FileAccess::open(p_files[i], FileAccess::READ);
|
||||
if (fb.is_null()) {
|
||||
close();
|
||||
ERR_FAIL_V_MSG(false, vformat("LipO: Can't open file: \"%s\".", p_files[i]));
|
||||
}
|
||||
}
|
||||
|
||||
// Write header.
|
||||
bool is_64 = (max_size >= std::numeric_limits<uint32_t>::max());
|
||||
if (is_64) {
|
||||
fa->store_32(0xbfbafeca);
|
||||
} else {
|
||||
fa->store_32(0xbebafeca);
|
||||
}
|
||||
fa->store_32(BSWAP32(archs.size()));
|
||||
uint64_t offset = archs.size() * (is_64 ? 32 : 20) + 8;
|
||||
for (int i = 0; i < archs.size(); i++) {
|
||||
archs.write[i].offset = offset + PAD(offset, uint64_t(1) << archs[i].align);
|
||||
if (is_64) {
|
||||
fa->store_32(BSWAP32(archs[i].cputype));
|
||||
fa->store_32(BSWAP32(archs[i].cpusubtype));
|
||||
fa->store_64(BSWAP64(archs[i].offset));
|
||||
fa->store_64(BSWAP64(archs[i].size));
|
||||
fa->store_32(BSWAP32(archs[i].align));
|
||||
fa->store_32(0);
|
||||
} else {
|
||||
fa->store_32(BSWAP32(archs[i].cputype));
|
||||
fa->store_32(BSWAP32(archs[i].cpusubtype));
|
||||
fa->store_32(BSWAP32(archs[i].offset));
|
||||
fa->store_32(BSWAP32(archs[i].size));
|
||||
fa->store_32(BSWAP32(archs[i].align));
|
||||
}
|
||||
offset = archs[i].offset + archs[i].size;
|
||||
}
|
||||
|
||||
// Write files and padding.
|
||||
for (int i = 0; i < archs.size(); i++) {
|
||||
Ref<FileAccess> fb = FileAccess::open(p_files[i], FileAccess::READ);
|
||||
if (fb.is_null()) {
|
||||
close();
|
||||
ERR_FAIL_V_MSG(false, vformat("LipO: Can't open file: \"%s\".", p_files[i]));
|
||||
}
|
||||
uint64_t cur = fa->get_position();
|
||||
for (uint64_t j = cur; j < archs[i].offset; j++) {
|
||||
fa->store_8(0);
|
||||
}
|
||||
int pages = archs[i].size / 4096;
|
||||
int remain = archs[i].size % 4096;
|
||||
unsigned char step[4096];
|
||||
for (int j = 0; j < pages; j++) {
|
||||
uint64_t br = fb->get_buffer(step, 4096);
|
||||
if (br > 0) {
|
||||
fa->store_buffer(step, br);
|
||||
}
|
||||
}
|
||||
uint64_t br = fb->get_buffer(step, remain);
|
||||
if (br > 0) {
|
||||
fa->store_buffer(step, br);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LipO::create_file(const String &p_output_path, const Vector<String> &p_files, const Vector<Vector2i> &p_cputypes) {
|
||||
close();
|
||||
|
||||
fa = FileAccess::open(p_output_path, FileAccess::WRITE);
|
||||
ERR_FAIL_COND_V_MSG(fa.is_null(), false, vformat("LipO: Can't open file: \"%s\".", p_output_path));
|
||||
ERR_FAIL_COND_V(p_files.size() != p_cputypes.size(), false);
|
||||
|
||||
uint64_t max_size = 0;
|
||||
for (int i = 0; i < p_files.size(); i++) {
|
||||
Ref<FileAccess> fb = FileAccess::open(p_files[i], FileAccess::READ);
|
||||
if (fb.is_null()) {
|
||||
close();
|
||||
ERR_FAIL_V_MSG(false, vformat("LipO: Can't open file: \"%s\".", p_files[i]));
|
||||
}
|
||||
|
||||
{
|
||||
FatArch arch;
|
||||
MachO mh;
|
||||
if (MachO::is_macho(p_files[i]) && mh.open_file(p_files[i])) {
|
||||
arch.cputype = mh.get_cputype();
|
||||
arch.cpusubtype = mh.get_cpusubtype();
|
||||
arch.offset = 0;
|
||||
arch.size = mh.get_size();
|
||||
arch.align = mh.get_align();
|
||||
ERR_FAIL_V_MSG(arch.cputype != (uint32_t)p_cputypes[i].x || arch.cpusubtype != (uint32_t)p_cputypes[i].y, vformat("Mismatching MachO architecture: \"%s\".", p_files[i]));
|
||||
} else {
|
||||
arch.cputype = (uint32_t)p_cputypes[i].x;
|
||||
arch.cpusubtype = (uint32_t)p_cputypes[i].y;
|
||||
arch.offset = 0;
|
||||
arch.size = fb->get_length();
|
||||
arch.align = 0x03;
|
||||
}
|
||||
max_size += arch.size;
|
||||
|
||||
archs.push_back(arch);
|
||||
}
|
||||
}
|
||||
|
||||
// Write header.
|
||||
bool is_64 = (max_size >= std::numeric_limits<uint32_t>::max());
|
||||
if (is_64) {
|
||||
fa->store_32(0xbfbafeca);
|
||||
} else {
|
||||
fa->store_32(0xbebafeca);
|
||||
}
|
||||
fa->store_32(BSWAP32(archs.size()));
|
||||
uint64_t offset = archs.size() * (is_64 ? 32 : 20) + 8;
|
||||
for (int i = 0; i < archs.size(); i++) {
|
||||
archs.write[i].offset = offset + PAD(offset, uint64_t(1) << archs[i].align);
|
||||
if (is_64) {
|
||||
fa->store_32(BSWAP32(archs[i].cputype));
|
||||
fa->store_32(BSWAP32(archs[i].cpusubtype));
|
||||
fa->store_64(BSWAP64(archs[i].offset));
|
||||
fa->store_64(BSWAP64(archs[i].size));
|
||||
fa->store_32(BSWAP32(archs[i].align));
|
||||
fa->store_32(0);
|
||||
} else {
|
||||
fa->store_32(BSWAP32(archs[i].cputype));
|
||||
fa->store_32(BSWAP32(archs[i].cpusubtype));
|
||||
fa->store_32(BSWAP32(archs[i].offset));
|
||||
fa->store_32(BSWAP32(archs[i].size));
|
||||
fa->store_32(BSWAP32(archs[i].align));
|
||||
}
|
||||
offset = archs[i].offset + archs[i].size;
|
||||
}
|
||||
|
||||
// Write files and padding.
|
||||
for (int i = 0; i < archs.size(); i++) {
|
||||
Ref<FileAccess> fb = FileAccess::open(p_files[i], FileAccess::READ);
|
||||
if (fb.is_null()) {
|
||||
close();
|
||||
ERR_FAIL_V_MSG(false, vformat("LipO: Can't open file: \"%s\".", p_files[i]));
|
||||
}
|
||||
uint64_t cur = fa->get_position();
|
||||
for (uint64_t j = cur; j < archs[i].offset; j++) {
|
||||
fa->store_8(0);
|
||||
}
|
||||
int pages = archs[i].size / 4096;
|
||||
int remain = archs[i].size % 4096;
|
||||
unsigned char step[4096];
|
||||
for (int j = 0; j < pages; j++) {
|
||||
uint64_t br = fb->get_buffer(step, 4096);
|
||||
if (br > 0) {
|
||||
fa->store_buffer(step, br);
|
||||
}
|
||||
}
|
||||
uint64_t br = fb->get_buffer(step, remain);
|
||||
if (br > 0) {
|
||||
fa->store_buffer(step, br);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LipO::open_file(const String &p_path) {
|
||||
close();
|
||||
|
||||
fa = FileAccess::open(p_path, FileAccess::READ);
|
||||
ERR_FAIL_COND_V_MSG(fa.is_null(), false, vformat("LipO: Can't open file: \"%s\".", p_path));
|
||||
|
||||
uint32_t magic = fa->get_32();
|
||||
if (magic == 0xbebafeca) {
|
||||
// 32-bit fat binary, bswap.
|
||||
uint32_t nfat_arch = BSWAP32(fa->get_32());
|
||||
for (uint32_t i = 0; i < nfat_arch; i++) {
|
||||
FatArch arch;
|
||||
arch.cputype = BSWAP32(fa->get_32());
|
||||
arch.cpusubtype = BSWAP32(fa->get_32());
|
||||
arch.offset = BSWAP32(fa->get_32());
|
||||
arch.size = BSWAP32(fa->get_32());
|
||||
arch.align = BSWAP32(fa->get_32());
|
||||
|
||||
archs.push_back(arch);
|
||||
}
|
||||
} else if (magic == 0xcafebabe) {
|
||||
// 32-bit fat binary.
|
||||
uint32_t nfat_arch = fa->get_32();
|
||||
for (uint32_t i = 0; i < nfat_arch; i++) {
|
||||
FatArch arch;
|
||||
arch.cputype = fa->get_32();
|
||||
arch.cpusubtype = fa->get_32();
|
||||
arch.offset = fa->get_32();
|
||||
arch.size = fa->get_32();
|
||||
arch.align = fa->get_32();
|
||||
|
||||
archs.push_back(arch);
|
||||
}
|
||||
} else if (magic == 0xbfbafeca) {
|
||||
// 64-bit fat binary, bswap.
|
||||
uint32_t nfat_arch = BSWAP32(fa->get_32());
|
||||
for (uint32_t i = 0; i < nfat_arch; i++) {
|
||||
FatArch arch;
|
||||
arch.cputype = BSWAP32(fa->get_32());
|
||||
arch.cpusubtype = BSWAP32(fa->get_32());
|
||||
arch.offset = BSWAP64(fa->get_64());
|
||||
arch.size = BSWAP64(fa->get_64());
|
||||
arch.align = BSWAP32(fa->get_32());
|
||||
fa->get_32(); // Skip, reserved.
|
||||
|
||||
archs.push_back(arch);
|
||||
}
|
||||
} else if (magic == 0xcafebabf) {
|
||||
// 64-bit fat binary.
|
||||
uint32_t nfat_arch = fa->get_32();
|
||||
for (uint32_t i = 0; i < nfat_arch; i++) {
|
||||
FatArch arch;
|
||||
arch.cputype = fa->get_32();
|
||||
arch.cpusubtype = fa->get_32();
|
||||
arch.offset = fa->get_64();
|
||||
arch.size = fa->get_64();
|
||||
arch.align = fa->get_32();
|
||||
fa->get_32(); // Skip, reserved.
|
||||
|
||||
archs.push_back(arch);
|
||||
}
|
||||
} else {
|
||||
close();
|
||||
ERR_FAIL_V_MSG(false, vformat("LipO: Invalid fat binary: \"%s\".", p_path));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
int LipO::get_arch_count() const {
|
||||
ERR_FAIL_COND_V_MSG(fa.is_null(), 0, "LipO: File not opened.");
|
||||
return archs.size();
|
||||
}
|
||||
|
||||
uint32_t LipO::get_arch_cputype(int p_index) const {
|
||||
ERR_FAIL_COND_V_MSG(fa.is_null(), 0, "LipO: File not opened.");
|
||||
ERR_FAIL_INDEX_V(p_index, archs.size(), 0);
|
||||
return archs[p_index].cputype;
|
||||
}
|
||||
|
||||
uint32_t LipO::get_arch_cpusubtype(int p_index) const {
|
||||
ERR_FAIL_COND_V_MSG(fa.is_null(), 0, "LipO: File not opened.");
|
||||
ERR_FAIL_INDEX_V(p_index, archs.size(), 0);
|
||||
return archs[p_index].cpusubtype;
|
||||
}
|
||||
|
||||
bool LipO::extract_arch(int p_index, const String &p_path) {
|
||||
ERR_FAIL_COND_V_MSG(fa.is_null(), false, "LipO: File not opened.");
|
||||
ERR_FAIL_INDEX_V(p_index, archs.size(), false);
|
||||
|
||||
Ref<FileAccess> fb = FileAccess::open(p_path, FileAccess::WRITE);
|
||||
ERR_FAIL_COND_V_MSG(fb.is_null(), false, vformat("LipO: Can't open file: \"%s\".", p_path));
|
||||
|
||||
fa->seek(archs[p_index].offset);
|
||||
|
||||
int pages = archs[p_index].size / 4096;
|
||||
int remain = archs[p_index].size % 4096;
|
||||
unsigned char step[4096];
|
||||
for (int i = 0; i < pages; i++) {
|
||||
uint64_t br = fa->get_buffer(step, 4096);
|
||||
if (br > 0) {
|
||||
fb->store_buffer(step, br);
|
||||
}
|
||||
}
|
||||
uint64_t br = fa->get_buffer(step, remain);
|
||||
if (br > 0) {
|
||||
fb->store_buffer(step, br);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void LipO::close() {
|
||||
archs.clear();
|
||||
}
|
||||
|
||||
LipO::~LipO() {
|
||||
close();
|
||||
}
|
69
editor/export/lipo.h
Normal file
69
editor/export/lipo.h
Normal file
@@ -0,0 +1,69 @@
|
||||
/**************************************************************************/
|
||||
/* lipo.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
// Universal / Universal 2 fat binary file creator and extractor.
|
||||
|
||||
#include "core/io/file_access.h"
|
||||
#include "core/object/ref_counted.h"
|
||||
|
||||
class LipO : public RefCounted {
|
||||
struct FatArch {
|
||||
uint32_t cputype;
|
||||
uint32_t cpusubtype;
|
||||
uint64_t offset;
|
||||
uint64_t size;
|
||||
uint32_t align;
|
||||
};
|
||||
|
||||
Ref<FileAccess> fa;
|
||||
Vector<FatArch> archs;
|
||||
|
||||
static inline size_t PAD(size_t s, size_t a) {
|
||||
return (a - s % a);
|
||||
}
|
||||
|
||||
public:
|
||||
static bool is_lipo(const String &p_path);
|
||||
|
||||
bool create_file(const String &p_output_path, const Vector<String> &p_files);
|
||||
bool create_file(const String &p_output_path, const Vector<String> &p_files, const Vector<Vector2i> &p_cputypes);
|
||||
|
||||
bool open_file(const String &p_path);
|
||||
int get_arch_count() const;
|
||||
uint32_t get_arch_cputype(int p_index) const;
|
||||
uint32_t get_arch_cpusubtype(int p_index) const;
|
||||
bool extract_arch(int p_index, const String &p_path);
|
||||
|
||||
void close();
|
||||
|
||||
~LipO();
|
||||
};
|
564
editor/export/macho.cpp
Normal file
564
editor/export/macho.cpp
Normal file
@@ -0,0 +1,564 @@
|
||||
/**************************************************************************/
|
||||
/* macho.cpp */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#include "macho.h"
|
||||
|
||||
#include "core/crypto/crypto_core.h"
|
||||
|
||||
uint32_t MachO::seg_align(uint64_t p_vmaddr, uint32_t p_min, uint32_t p_max) {
|
||||
uint32_t salign = p_max;
|
||||
if (p_vmaddr != 0) {
|
||||
uint64_t seg_align = 1;
|
||||
salign = 0;
|
||||
while ((seg_align & p_vmaddr) == 0) {
|
||||
seg_align = seg_align << 1;
|
||||
salign++;
|
||||
}
|
||||
salign = CLAMP(salign, p_min, p_max);
|
||||
}
|
||||
return salign;
|
||||
}
|
||||
|
||||
bool MachO::alloc_signature(uint64_t p_size) {
|
||||
ERR_FAIL_COND_V_MSG(fa.is_null(), false, "MachO: File not opened.");
|
||||
if (signature_offset != 0) {
|
||||
// Nothing to do, already have signature load command.
|
||||
return true;
|
||||
}
|
||||
if (lc_limit == 0 || lc_limit + 16 > exe_base) {
|
||||
ERR_FAIL_V_MSG(false, "MachO: Can't allocate signature load command, please use \"codesign_allocate\" utility first.");
|
||||
} else {
|
||||
// Add signature load command.
|
||||
signature_offset = lc_limit;
|
||||
|
||||
fa->seek(lc_limit);
|
||||
LoadCommandHeader lc;
|
||||
lc.cmd = LC_CODE_SIGNATURE;
|
||||
lc.cmdsize = 16;
|
||||
if (swap) {
|
||||
lc.cmdsize = BSWAP32(lc.cmdsize);
|
||||
}
|
||||
fa->store_buffer((const uint8_t *)&lc, sizeof(LoadCommandHeader));
|
||||
|
||||
uint32_t lc_offset = fa->get_length() + PAD(fa->get_length(), 16);
|
||||
uint32_t lc_size = 0;
|
||||
if (swap) {
|
||||
lc_offset = BSWAP32(lc_offset);
|
||||
lc_size = BSWAP32(lc_size);
|
||||
}
|
||||
fa->store_32(lc_offset);
|
||||
fa->store_32(lc_size);
|
||||
|
||||
// Write new command number.
|
||||
fa->seek(0x10);
|
||||
uint32_t ncmds = fa->get_32();
|
||||
uint32_t cmdssize = fa->get_32();
|
||||
if (swap) {
|
||||
ncmds = BSWAP32(ncmds);
|
||||
cmdssize = BSWAP32(cmdssize);
|
||||
}
|
||||
ncmds += 1;
|
||||
cmdssize += 16;
|
||||
if (swap) {
|
||||
ncmds = BSWAP32(ncmds);
|
||||
cmdssize = BSWAP32(cmdssize);
|
||||
}
|
||||
fa->seek(0x10);
|
||||
fa->store_32(ncmds);
|
||||
fa->store_32(cmdssize);
|
||||
|
||||
lc_limit = lc_limit + sizeof(LoadCommandHeader) + 8;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
bool MachO::is_macho(const String &p_path) {
|
||||
Ref<FileAccess> fb = FileAccess::open(p_path, FileAccess::READ);
|
||||
ERR_FAIL_COND_V_MSG(fb.is_null(), false, vformat("MachO: Can't open file: \"%s\".", p_path));
|
||||
uint32_t magic = fb->get_32();
|
||||
return (magic == 0xcefaedfe || magic == 0xfeedface || magic == 0xcffaedfe || magic == 0xfeedfacf);
|
||||
}
|
||||
|
||||
uint32_t MachO::get_filetype(const String &p_path) {
|
||||
Ref<FileAccess> fa = FileAccess::open(p_path, FileAccess::READ);
|
||||
ERR_FAIL_COND_V_MSG(fa.is_null(), 0, vformat("MachO: Can't open file: \"%s\".", p_path));
|
||||
uint32_t magic = fa->get_32();
|
||||
MachHeader mach_header;
|
||||
|
||||
// Read MachO header.
|
||||
if (magic == 0xcefaedfe || magic == 0xfeedface) {
|
||||
// Thin 32-bit binary.
|
||||
fa->get_buffer((uint8_t *)&mach_header, sizeof(MachHeader));
|
||||
} else if (magic == 0xcffaedfe || magic == 0xfeedfacf) {
|
||||
// Thin 64-bit binary.
|
||||
fa->get_buffer((uint8_t *)&mach_header, sizeof(MachHeader));
|
||||
fa->get_32(); // Skip extra reserved field.
|
||||
} else {
|
||||
ERR_FAIL_V_MSG(0, vformat("MachO: File is not a valid MachO binary: \"%s\".", p_path));
|
||||
}
|
||||
return mach_header.filetype;
|
||||
}
|
||||
|
||||
bool MachO::open_file(const String &p_path) {
|
||||
fa = FileAccess::open(p_path, FileAccess::READ_WRITE);
|
||||
ERR_FAIL_COND_V_MSG(fa.is_null(), false, vformat("MachO: Can't open file: \"%s\".", p_path));
|
||||
uint32_t magic = fa->get_32();
|
||||
MachHeader mach_header;
|
||||
|
||||
// Read MachO header.
|
||||
swap = (magic == 0xcffaedfe || magic == 0xcefaedfe);
|
||||
if (magic == 0xcefaedfe || magic == 0xfeedface) {
|
||||
// Thin 32-bit binary.
|
||||
fa->get_buffer((uint8_t *)&mach_header, sizeof(MachHeader));
|
||||
} else if (magic == 0xcffaedfe || magic == 0xfeedfacf) {
|
||||
// Thin 64-bit binary.
|
||||
fa->get_buffer((uint8_t *)&mach_header, sizeof(MachHeader));
|
||||
fa->get_32(); // Skip extra reserved field.
|
||||
} else {
|
||||
ERR_FAIL_V_MSG(false, vformat("MachO: File is not a valid MachO binary: \"%s\".", p_path));
|
||||
}
|
||||
|
||||
if (swap) {
|
||||
mach_header.ncmds = BSWAP32(mach_header.ncmds);
|
||||
mach_header.cpusubtype = BSWAP32(mach_header.cpusubtype);
|
||||
mach_header.cputype = BSWAP32(mach_header.cputype);
|
||||
}
|
||||
cpusubtype = mach_header.cpusubtype;
|
||||
cputype = mach_header.cputype;
|
||||
align = 0;
|
||||
exe_base = std::numeric_limits<uint64_t>::max();
|
||||
exe_limit = 0;
|
||||
lc_limit = 0;
|
||||
link_edit_offset = 0;
|
||||
signature_offset = 0;
|
||||
|
||||
// Read load commands.
|
||||
for (uint32_t i = 0; i < mach_header.ncmds; i++) {
|
||||
LoadCommandHeader lc;
|
||||
fa->get_buffer((uint8_t *)&lc, sizeof(LoadCommandHeader));
|
||||
if (swap) {
|
||||
lc.cmd = BSWAP32(lc.cmd);
|
||||
lc.cmdsize = BSWAP32(lc.cmdsize);
|
||||
}
|
||||
uint64_t ps = fa->get_position();
|
||||
switch (lc.cmd) {
|
||||
case LC_SEGMENT: {
|
||||
LoadCommandSegment lc_seg;
|
||||
fa->get_buffer((uint8_t *)&lc_seg, sizeof(LoadCommandSegment));
|
||||
if (swap) {
|
||||
lc_seg.nsects = BSWAP32(lc_seg.nsects);
|
||||
lc_seg.vmaddr = BSWAP32(lc_seg.vmaddr);
|
||||
lc_seg.vmsize = BSWAP32(lc_seg.vmsize);
|
||||
}
|
||||
align = MAX(align, seg_align(lc_seg.vmaddr, 2, 15));
|
||||
if (String(lc_seg.segname) == "__TEXT") {
|
||||
exe_limit = MAX(exe_limit, lc_seg.vmsize);
|
||||
for (uint32_t j = 0; j < lc_seg.nsects; j++) {
|
||||
Section lc_sect;
|
||||
fa->get_buffer((uint8_t *)&lc_sect, sizeof(Section));
|
||||
if (String(lc_sect.sectname) == "__text") {
|
||||
if (swap) {
|
||||
exe_base = MIN(exe_base, BSWAP32(lc_sect.offset));
|
||||
} else {
|
||||
exe_base = MIN(exe_base, lc_sect.offset);
|
||||
}
|
||||
}
|
||||
if (swap) {
|
||||
align = MAX(align, BSWAP32(lc_sect.align));
|
||||
} else {
|
||||
align = MAX(align, lc_sect.align);
|
||||
}
|
||||
}
|
||||
} else if (String(lc_seg.segname) == "__LINKEDIT") {
|
||||
link_edit_offset = ps - 8;
|
||||
}
|
||||
} break;
|
||||
case LC_SEGMENT_64: {
|
||||
LoadCommandSegment64 lc_seg;
|
||||
fa->get_buffer((uint8_t *)&lc_seg, sizeof(LoadCommandSegment64));
|
||||
if (swap) {
|
||||
lc_seg.nsects = BSWAP32(lc_seg.nsects);
|
||||
lc_seg.vmaddr = BSWAP64(lc_seg.vmaddr);
|
||||
lc_seg.vmsize = BSWAP64(lc_seg.vmsize);
|
||||
}
|
||||
align = MAX(align, seg_align(lc_seg.vmaddr, 3, 15));
|
||||
if (String(lc_seg.segname) == "__TEXT") {
|
||||
exe_limit = MAX(exe_limit, lc_seg.vmsize);
|
||||
for (uint32_t j = 0; j < lc_seg.nsects; j++) {
|
||||
Section64 lc_sect;
|
||||
fa->get_buffer((uint8_t *)&lc_sect, sizeof(Section64));
|
||||
if (String(lc_sect.sectname) == "__text") {
|
||||
if (swap) {
|
||||
exe_base = MIN(exe_base, BSWAP32(lc_sect.offset));
|
||||
} else {
|
||||
exe_base = MIN(exe_base, lc_sect.offset);
|
||||
}
|
||||
if (swap) {
|
||||
align = MAX(align, BSWAP32(lc_sect.align));
|
||||
} else {
|
||||
align = MAX(align, lc_sect.align);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (String(lc_seg.segname) == "__LINKEDIT") {
|
||||
link_edit_offset = ps - 8;
|
||||
}
|
||||
} break;
|
||||
case LC_CODE_SIGNATURE: {
|
||||
signature_offset = ps - 8;
|
||||
} break;
|
||||
default: {
|
||||
} break;
|
||||
}
|
||||
fa->seek(ps + lc.cmdsize - 8);
|
||||
lc_limit = ps + lc.cmdsize - 8;
|
||||
}
|
||||
|
||||
if (exe_limit == 0 || lc_limit == 0) {
|
||||
ERR_FAIL_V_MSG(false, vformat("MachO: No load commands or executable code found: \"%s\".", p_path));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
uint64_t MachO::get_exe_base() {
|
||||
ERR_FAIL_COND_V_MSG(fa.is_null(), 0, "MachO: File not opened.");
|
||||
return exe_base;
|
||||
}
|
||||
|
||||
uint64_t MachO::get_exe_limit() {
|
||||
ERR_FAIL_COND_V_MSG(fa.is_null(), 0, "MachO: File not opened.");
|
||||
return exe_limit;
|
||||
}
|
||||
|
||||
int32_t MachO::get_align() {
|
||||
ERR_FAIL_COND_V_MSG(fa.is_null(), 0, "MachO: File not opened.");
|
||||
return align;
|
||||
}
|
||||
|
||||
uint32_t MachO::get_cputype() {
|
||||
ERR_FAIL_COND_V_MSG(fa.is_null(), 0, "MachO: File not opened.");
|
||||
return cputype;
|
||||
}
|
||||
|
||||
uint32_t MachO::get_cpusubtype() {
|
||||
ERR_FAIL_COND_V_MSG(fa.is_null(), 0, "MachO: File not opened.");
|
||||
return cpusubtype;
|
||||
}
|
||||
|
||||
uint64_t MachO::get_size() {
|
||||
ERR_FAIL_COND_V_MSG(fa.is_null(), 0, "MachO: File not opened.");
|
||||
return fa->get_length();
|
||||
}
|
||||
|
||||
uint64_t MachO::get_signature_offset() {
|
||||
ERR_FAIL_COND_V_MSG(fa.is_null(), 0, "MachO: File not opened.");
|
||||
ERR_FAIL_COND_V_MSG(signature_offset == 0, 0, "MachO: No signature load command.");
|
||||
|
||||
fa->seek(signature_offset + 8);
|
||||
if (swap) {
|
||||
return BSWAP32(fa->get_32());
|
||||
} else {
|
||||
return fa->get_32();
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t MachO::get_code_limit() {
|
||||
ERR_FAIL_COND_V_MSG(fa.is_null(), 0, "MachO: File not opened.");
|
||||
|
||||
if (signature_offset == 0) {
|
||||
return fa->get_length() + PAD(fa->get_length(), 16);
|
||||
} else {
|
||||
return get_signature_offset();
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t MachO::get_signature_size() {
|
||||
ERR_FAIL_COND_V_MSG(fa.is_null(), 0, "MachO: File not opened.");
|
||||
ERR_FAIL_COND_V_MSG(signature_offset == 0, 0, "MachO: No signature load command.");
|
||||
|
||||
fa->seek(signature_offset + 12);
|
||||
if (swap) {
|
||||
return BSWAP32(fa->get_32());
|
||||
} else {
|
||||
return fa->get_32();
|
||||
}
|
||||
}
|
||||
|
||||
bool MachO::is_signed() {
|
||||
ERR_FAIL_COND_V_MSG(fa.is_null(), false, "MachO: File not opened.");
|
||||
if (signature_offset == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
fa->seek(get_signature_offset());
|
||||
uint32_t magic = BSWAP32(fa->get_32());
|
||||
if (magic != 0xfade0cc0) {
|
||||
return false; // No SuperBlob found.
|
||||
}
|
||||
fa->get_32(); // Skip size field, unused.
|
||||
uint32_t count = BSWAP32(fa->get_32());
|
||||
for (uint32_t i = 0; i < count; i++) {
|
||||
uint32_t index_type = BSWAP32(fa->get_32());
|
||||
uint32_t offset = BSWAP32(fa->get_32());
|
||||
if (index_type == 0x00000000) { // CodeDirectory index type.
|
||||
fa->seek(get_signature_offset() + offset + 12);
|
||||
uint32_t flags = BSWAP32(fa->get_32());
|
||||
if (flags & 0x20000) {
|
||||
return false; // Found CD, linker-signed.
|
||||
} else {
|
||||
return true; // Found CD, not linker-signed.
|
||||
}
|
||||
}
|
||||
}
|
||||
return false; // No CD found.
|
||||
}
|
||||
|
||||
PackedByteArray MachO::get_cdhash_sha1() {
|
||||
ERR_FAIL_COND_V_MSG(fa.is_null(), PackedByteArray(), "MachO: File not opened.");
|
||||
if (signature_offset == 0) {
|
||||
return PackedByteArray();
|
||||
}
|
||||
|
||||
fa->seek(get_signature_offset());
|
||||
uint32_t magic = BSWAP32(fa->get_32());
|
||||
if (magic != 0xfade0cc0) {
|
||||
return PackedByteArray(); // No SuperBlob found.
|
||||
}
|
||||
fa->get_32(); // Skip size field, unused.
|
||||
uint32_t count = BSWAP32(fa->get_32());
|
||||
for (uint32_t i = 0; i < count; i++) {
|
||||
fa->get_32(); // Index type, skip.
|
||||
uint32_t offset = BSWAP32(fa->get_32());
|
||||
uint64_t pos = fa->get_position();
|
||||
|
||||
fa->seek(get_signature_offset() + offset);
|
||||
uint32_t cdmagic = BSWAP32(fa->get_32());
|
||||
uint32_t cdsize = BSWAP32(fa->get_32());
|
||||
if (cdmagic == 0xfade0c02) { // CodeDirectory.
|
||||
fa->seek(get_signature_offset() + offset + 36);
|
||||
uint8_t hash_size = fa->get_8();
|
||||
uint8_t hash_type = fa->get_8();
|
||||
if (hash_size == 0x14 && hash_type == 0x01) { /* SHA-1 */
|
||||
PackedByteArray hash;
|
||||
hash.resize(0x14);
|
||||
|
||||
fa->seek(get_signature_offset() + offset);
|
||||
PackedByteArray blob;
|
||||
blob.resize(cdsize);
|
||||
fa->get_buffer(blob.ptrw(), cdsize);
|
||||
|
||||
CryptoCore::SHA1Context ctx;
|
||||
ctx.start();
|
||||
ctx.update(blob.ptr(), blob.size());
|
||||
ctx.finish(hash.ptrw());
|
||||
|
||||
return hash;
|
||||
}
|
||||
}
|
||||
fa->seek(pos);
|
||||
}
|
||||
return PackedByteArray();
|
||||
}
|
||||
|
||||
PackedByteArray MachO::get_cdhash_sha256() {
|
||||
ERR_FAIL_COND_V_MSG(fa.is_null(), PackedByteArray(), "MachO: File not opened.");
|
||||
if (signature_offset == 0) {
|
||||
return PackedByteArray();
|
||||
}
|
||||
|
||||
fa->seek(get_signature_offset());
|
||||
uint32_t magic = BSWAP32(fa->get_32());
|
||||
if (magic != 0xfade0cc0) {
|
||||
return PackedByteArray(); // No SuperBlob found.
|
||||
}
|
||||
fa->get_32(); // Skip size field, unused.
|
||||
uint32_t count = BSWAP32(fa->get_32());
|
||||
for (uint32_t i = 0; i < count; i++) {
|
||||
fa->get_32(); // Index type, skip.
|
||||
uint32_t offset = BSWAP32(fa->get_32());
|
||||
uint64_t pos = fa->get_position();
|
||||
|
||||
fa->seek(get_signature_offset() + offset);
|
||||
uint32_t cdmagic = BSWAP32(fa->get_32());
|
||||
uint32_t cdsize = BSWAP32(fa->get_32());
|
||||
if (cdmagic == 0xfade0c02) { // CodeDirectory.
|
||||
fa->seek(get_signature_offset() + offset + 36);
|
||||
uint8_t hash_size = fa->get_8();
|
||||
uint8_t hash_type = fa->get_8();
|
||||
if (hash_size == 0x20 && hash_type == 0x02) { /* SHA-256 */
|
||||
PackedByteArray hash;
|
||||
hash.resize(0x20);
|
||||
|
||||
fa->seek(get_signature_offset() + offset);
|
||||
PackedByteArray blob;
|
||||
blob.resize(cdsize);
|
||||
fa->get_buffer(blob.ptrw(), cdsize);
|
||||
|
||||
CryptoCore::SHA256Context ctx;
|
||||
ctx.start();
|
||||
ctx.update(blob.ptr(), blob.size());
|
||||
ctx.finish(hash.ptrw());
|
||||
|
||||
return hash;
|
||||
}
|
||||
}
|
||||
fa->seek(pos);
|
||||
}
|
||||
return PackedByteArray();
|
||||
}
|
||||
|
||||
PackedByteArray MachO::get_requirements() {
|
||||
ERR_FAIL_COND_V_MSG(fa.is_null(), PackedByteArray(), "MachO: File not opened.");
|
||||
if (signature_offset == 0) {
|
||||
return PackedByteArray();
|
||||
}
|
||||
|
||||
fa->seek(get_signature_offset());
|
||||
uint32_t magic = BSWAP32(fa->get_32());
|
||||
if (magic != 0xfade0cc0) {
|
||||
return PackedByteArray(); // No SuperBlob found.
|
||||
}
|
||||
fa->get_32(); // Skip size field, unused.
|
||||
uint32_t count = BSWAP32(fa->get_32());
|
||||
for (uint32_t i = 0; i < count; i++) {
|
||||
fa->get_32(); // Index type, skip.
|
||||
uint32_t offset = BSWAP32(fa->get_32());
|
||||
uint64_t pos = fa->get_position();
|
||||
|
||||
fa->seek(get_signature_offset() + offset);
|
||||
uint32_t rqmagic = BSWAP32(fa->get_32());
|
||||
uint32_t rqsize = BSWAP32(fa->get_32());
|
||||
if (rqmagic == 0xfade0c01) { // Requirements.
|
||||
PackedByteArray blob;
|
||||
fa->seek(get_signature_offset() + offset);
|
||||
blob.resize(rqsize);
|
||||
fa->get_buffer(blob.ptrw(), rqsize);
|
||||
return blob;
|
||||
}
|
||||
fa->seek(pos);
|
||||
}
|
||||
return PackedByteArray();
|
||||
}
|
||||
|
||||
const Ref<FileAccess> MachO::get_file() const {
|
||||
return fa;
|
||||
}
|
||||
|
||||
Ref<FileAccess> MachO::get_file() {
|
||||
return fa;
|
||||
}
|
||||
|
||||
bool MachO::set_signature_size(uint64_t p_size) {
|
||||
ERR_FAIL_COND_V_MSG(fa.is_null(), false, "MachO: File not opened.");
|
||||
|
||||
// Ensure signature load command exists.
|
||||
ERR_FAIL_COND_V_MSG(link_edit_offset == 0, false, "MachO: No __LINKEDIT segment found.");
|
||||
ERR_FAIL_COND_V_MSG(!alloc_signature(p_size), false, "MachO: Can't allocate signature load command.");
|
||||
|
||||
// Update signature load command.
|
||||
uint64_t old_size = get_signature_size();
|
||||
uint64_t new_size = p_size + PAD(p_size, 16384);
|
||||
|
||||
if (new_size <= old_size) {
|
||||
fa->seek(get_signature_offset());
|
||||
for (uint64_t i = 0; i < old_size; i++) {
|
||||
fa->store_8(0x00);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
fa->seek(signature_offset + 12);
|
||||
if (swap) {
|
||||
fa->store_32(BSWAP32(new_size));
|
||||
} else {
|
||||
fa->store_32(new_size);
|
||||
}
|
||||
|
||||
uint64_t end = get_signature_offset() + new_size;
|
||||
|
||||
// Update "__LINKEDIT" segment.
|
||||
LoadCommandHeader lc;
|
||||
fa->seek(link_edit_offset);
|
||||
fa->get_buffer((uint8_t *)&lc, sizeof(LoadCommandHeader));
|
||||
if (swap) {
|
||||
lc.cmd = BSWAP32(lc.cmd);
|
||||
lc.cmdsize = BSWAP32(lc.cmdsize);
|
||||
}
|
||||
switch (lc.cmd) {
|
||||
case LC_SEGMENT: {
|
||||
LoadCommandSegment lc_seg;
|
||||
fa->get_buffer((uint8_t *)&lc_seg, sizeof(LoadCommandSegment));
|
||||
if (swap) {
|
||||
lc_seg.vmsize = BSWAP32(lc_seg.vmsize);
|
||||
lc_seg.filesize = BSWAP32(lc_seg.filesize);
|
||||
lc_seg.fileoff = BSWAP32(lc_seg.fileoff);
|
||||
}
|
||||
|
||||
lc_seg.vmsize = end - lc_seg.fileoff;
|
||||
lc_seg.vmsize += PAD(lc_seg.vmsize, 4096);
|
||||
lc_seg.filesize = end - lc_seg.fileoff;
|
||||
|
||||
if (swap) {
|
||||
lc_seg.vmsize = BSWAP32(lc_seg.vmsize);
|
||||
lc_seg.filesize = BSWAP32(lc_seg.filesize);
|
||||
}
|
||||
fa->seek(link_edit_offset + 8);
|
||||
fa->store_buffer((const uint8_t *)&lc_seg, sizeof(LoadCommandSegment));
|
||||
} break;
|
||||
case LC_SEGMENT_64: {
|
||||
LoadCommandSegment64 lc_seg;
|
||||
fa->get_buffer((uint8_t *)&lc_seg, sizeof(LoadCommandSegment64));
|
||||
if (swap) {
|
||||
lc_seg.vmsize = BSWAP64(lc_seg.vmsize);
|
||||
lc_seg.filesize = BSWAP64(lc_seg.filesize);
|
||||
lc_seg.fileoff = BSWAP64(lc_seg.fileoff);
|
||||
}
|
||||
lc_seg.vmsize = end - lc_seg.fileoff;
|
||||
lc_seg.vmsize += PAD(lc_seg.vmsize, 4096);
|
||||
lc_seg.filesize = end - lc_seg.fileoff;
|
||||
if (swap) {
|
||||
lc_seg.vmsize = BSWAP64(lc_seg.vmsize);
|
||||
lc_seg.filesize = BSWAP64(lc_seg.filesize);
|
||||
}
|
||||
fa->seek(link_edit_offset + 8);
|
||||
fa->store_buffer((const uint8_t *)&lc_seg, sizeof(LoadCommandSegment64));
|
||||
} break;
|
||||
default: {
|
||||
ERR_FAIL_V_MSG(false, "MachO: Invalid __LINKEDIT segment type.");
|
||||
} break;
|
||||
}
|
||||
fa->seek(get_signature_offset());
|
||||
for (uint64_t i = 0; i < new_size; i++) {
|
||||
fa->store_8(0x00);
|
||||
}
|
||||
return true;
|
||||
}
|
225
editor/export/macho.h
Normal file
225
editor/export/macho.h
Normal file
@@ -0,0 +1,225 @@
|
||||
/**************************************************************************/
|
||||
/* macho.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
// Mach-O binary object file format parser and editor.
|
||||
|
||||
#include "core/io/file_access.h"
|
||||
#include "core/object/ref_counted.h"
|
||||
|
||||
class MachO : public RefCounted {
|
||||
public:
|
||||
struct MachHeader {
|
||||
uint32_t cputype;
|
||||
uint32_t cpusubtype;
|
||||
uint32_t filetype;
|
||||
uint32_t ncmds;
|
||||
uint32_t sizeofcmds;
|
||||
uint32_t flags;
|
||||
};
|
||||
|
||||
enum LoadCommandID {
|
||||
LC_SEGMENT = 0x00000001,
|
||||
LC_SYMTAB = 0x00000002,
|
||||
LC_SYMSEG = 0x00000003,
|
||||
LC_THREAD = 0x00000004,
|
||||
LC_UNIXTHREAD = 0x00000005,
|
||||
LC_LOADFVMLIB = 0x00000006,
|
||||
LC_IDFVMLIB = 0x00000007,
|
||||
LC_IDENT = 0x00000008,
|
||||
LC_FVMFILE = 0x00000009,
|
||||
LC_PREPAGE = 0x0000000a,
|
||||
LC_DYSYMTAB = 0x0000000b,
|
||||
LC_LOAD_DYLIB = 0x0000000c,
|
||||
LC_ID_DYLIB = 0x0000000d,
|
||||
LC_LOAD_DYLINKER = 0x0000000e,
|
||||
LC_ID_DYLINKER = 0x0000000f,
|
||||
LC_PREBOUND_DYLIB = 0x00000010,
|
||||
LC_ROUTINES = 0x00000011,
|
||||
LC_SUB_FRAMEWORK = 0x00000012,
|
||||
LC_SUB_UMBRELLA = 0x00000013,
|
||||
LC_SUB_CLIENT = 0x00000014,
|
||||
LC_SUB_LIBRARY = 0x00000015,
|
||||
LC_TWOLEVEL_HINTS = 0x00000016,
|
||||
LC_PREBIND_CKSUM = 0x00000017,
|
||||
LC_LOAD_WEAK_DYLIB = 0x80000018,
|
||||
LC_SEGMENT_64 = 0x00000019,
|
||||
LC_ROUTINES_64 = 0x0000001a,
|
||||
LC_UUID = 0x0000001b,
|
||||
LC_RPATH = 0x8000001c,
|
||||
LC_CODE_SIGNATURE = 0x0000001d,
|
||||
LC_SEGMENT_SPLIT_INFO = 0x0000001e,
|
||||
LC_REEXPORT_DYLIB = 0x8000001f,
|
||||
LC_LAZY_LOAD_DYLIB = 0x00000020,
|
||||
LC_ENCRYPTION_INFO = 0x00000021,
|
||||
LC_DYLD_INFO = 0x00000022,
|
||||
LC_DYLD_INFO_ONLY = 0x80000022,
|
||||
LC_LOAD_UPWARD_DYLIB = 0x80000023,
|
||||
LC_VERSION_MIN_MACOSX = 0x00000024,
|
||||
LC_VERSION_MIN_IPHONEOS = 0x00000025,
|
||||
LC_FUNCTION_STARTS = 0x00000026,
|
||||
LC_DYLD_ENVIRONMENT = 0x00000027,
|
||||
LC_MAIN = 0x80000028,
|
||||
LC_DATA_IN_CODE = 0x00000029,
|
||||
LC_SOURCE_VERSION = 0x0000002a,
|
||||
LC_DYLIB_CODE_SIGN_DRS = 0x0000002b,
|
||||
LC_ENCRYPTION_INFO_64 = 0x0000002c,
|
||||
LC_LINKER_OPTION = 0x0000002d,
|
||||
LC_LINKER_OPTIMIZATION_HINT = 0x0000002e,
|
||||
LC_VERSION_MIN_TVOS = 0x0000002f,
|
||||
LC_VERSION_MIN_WATCHOS = 0x00000030,
|
||||
LC_BUILD_VERSION = 0x00000032,
|
||||
};
|
||||
|
||||
enum PlatformID {
|
||||
PLATFORM_UNKNOWN = 0,
|
||||
PLATFORM_MACOS = 1,
|
||||
PLATFORM_IOS = 2,
|
||||
PLATFORM_TVOS = 3,
|
||||
PLATFORM_WATCHOS = 4,
|
||||
PLATFORM_BRIDGEOS = 5,
|
||||
PLATFORM_MACCATALYST = 6,
|
||||
PLATFORM_IOSSIMULATOR = 7,
|
||||
PLATFORM_TVOSSIMULATOR = 8,
|
||||
PLATFORM_WATCHOSSIMULATOR = 9,
|
||||
PLATFORM_DRIVERKIT = 10,
|
||||
PLATFORM_VISIONOS = 11,
|
||||
PLATFORM_VISIONOSSIMULATOR = 12,
|
||||
};
|
||||
|
||||
struct LoadCommandHeader {
|
||||
uint32_t cmd;
|
||||
uint32_t cmdsize;
|
||||
};
|
||||
|
||||
struct LoadCommandSegment {
|
||||
char segname[16];
|
||||
uint32_t vmaddr;
|
||||
uint32_t vmsize;
|
||||
uint32_t fileoff;
|
||||
uint32_t filesize;
|
||||
uint32_t maxprot;
|
||||
uint32_t initprot;
|
||||
uint32_t nsects;
|
||||
uint32_t flags;
|
||||
};
|
||||
|
||||
struct LoadCommandSegment64 {
|
||||
char segname[16];
|
||||
uint64_t vmaddr;
|
||||
uint64_t vmsize;
|
||||
uint64_t fileoff;
|
||||
uint64_t filesize;
|
||||
uint32_t maxprot;
|
||||
uint32_t initprot;
|
||||
uint32_t nsects;
|
||||
uint32_t flags;
|
||||
};
|
||||
|
||||
struct Section {
|
||||
char sectname[16];
|
||||
char segname[16];
|
||||
uint32_t addr;
|
||||
uint32_t size;
|
||||
uint32_t offset;
|
||||
uint32_t align;
|
||||
uint32_t reloff;
|
||||
uint32_t nreloc;
|
||||
uint32_t flags;
|
||||
uint32_t reserved1;
|
||||
uint32_t reserved2;
|
||||
};
|
||||
|
||||
struct Section64 {
|
||||
char sectname[16];
|
||||
char segname[16];
|
||||
uint64_t addr;
|
||||
uint64_t size;
|
||||
uint32_t offset;
|
||||
uint32_t align;
|
||||
uint32_t reloff;
|
||||
uint32_t nreloc;
|
||||
uint32_t flags;
|
||||
uint32_t reserved1;
|
||||
uint32_t reserved2;
|
||||
uint32_t reserved3;
|
||||
};
|
||||
|
||||
private:
|
||||
Ref<FileAccess> fa;
|
||||
bool swap = false;
|
||||
|
||||
uint64_t lc_limit = 0;
|
||||
|
||||
uint64_t exe_limit = 0;
|
||||
uint64_t exe_base = std::numeric_limits<uint64_t>::max(); // Start of first __text section.
|
||||
uint32_t align = 0;
|
||||
uint32_t cputype = 0;
|
||||
uint32_t cpusubtype = 0;
|
||||
|
||||
uint64_t link_edit_offset = 0; // __LINKEDIT segment offset.
|
||||
uint64_t signature_offset = 0; // Load command offset.
|
||||
|
||||
uint32_t seg_align(uint64_t p_vmaddr, uint32_t p_min, uint32_t p_max);
|
||||
bool alloc_signature(uint64_t p_size);
|
||||
|
||||
static inline size_t PAD(size_t s, size_t a) {
|
||||
return (a - s % a);
|
||||
}
|
||||
|
||||
public:
|
||||
static bool is_macho(const String &p_path);
|
||||
static uint32_t get_filetype(const String &p_path);
|
||||
|
||||
bool open_file(const String &p_path);
|
||||
|
||||
uint64_t get_exe_base();
|
||||
uint64_t get_exe_limit();
|
||||
int32_t get_align();
|
||||
uint32_t get_cputype();
|
||||
uint32_t get_cpusubtype();
|
||||
uint64_t get_size();
|
||||
uint64_t get_code_limit();
|
||||
|
||||
uint64_t get_signature_offset();
|
||||
bool is_signed();
|
||||
|
||||
PackedByteArray get_cdhash_sha1();
|
||||
PackedByteArray get_cdhash_sha256();
|
||||
|
||||
PackedByteArray get_requirements();
|
||||
|
||||
const Ref<FileAccess> get_file() const;
|
||||
Ref<FileAccess> get_file();
|
||||
|
||||
uint64_t get_signature_size();
|
||||
bool set_signature_size(uint64_t p_size);
|
||||
};
|
285
editor/export/plugin_config_apple_embedded.cpp
Normal file
285
editor/export/plugin_config_apple_embedded.cpp
Normal file
@@ -0,0 +1,285 @@
|
||||
/**************************************************************************/
|
||||
/* plugin_config_apple_embedded.cpp */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#include "plugin_config_apple_embedded.h"
|
||||
|
||||
#include "core/config/project_settings.h"
|
||||
#include "core/io/dir_access.h"
|
||||
#include "core/io/file_access.h"
|
||||
|
||||
String PluginConfigAppleEmbedded::resolve_local_dependency_path(String plugin_config_dir, String dependency_path) {
|
||||
String absolute_path;
|
||||
|
||||
if (dependency_path.is_empty()) {
|
||||
return absolute_path;
|
||||
}
|
||||
|
||||
if (dependency_path.is_absolute_path()) {
|
||||
return dependency_path;
|
||||
}
|
||||
|
||||
String res_path = ProjectSettings::get_singleton()->globalize_path("res://");
|
||||
absolute_path = plugin_config_dir.path_join(dependency_path);
|
||||
|
||||
return absolute_path.replace(res_path, "res://");
|
||||
}
|
||||
|
||||
String PluginConfigAppleEmbedded::resolve_system_dependency_path(String dependency_path) {
|
||||
String absolute_path;
|
||||
|
||||
if (dependency_path.is_empty()) {
|
||||
return absolute_path;
|
||||
}
|
||||
|
||||
if (dependency_path.is_absolute_path()) {
|
||||
return dependency_path;
|
||||
}
|
||||
|
||||
String system_path = "/System/Library/Frameworks";
|
||||
|
||||
return system_path.path_join(dependency_path);
|
||||
}
|
||||
|
||||
Vector<String> PluginConfigAppleEmbedded::resolve_local_dependencies(String plugin_config_dir, Vector<String> p_paths) {
|
||||
Vector<String> paths;
|
||||
|
||||
for (int i = 0; i < p_paths.size(); i++) {
|
||||
String path = resolve_local_dependency_path(plugin_config_dir, p_paths[i]);
|
||||
|
||||
if (path.is_empty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
paths.push_back(path);
|
||||
}
|
||||
|
||||
return paths;
|
||||
}
|
||||
|
||||
Vector<String> PluginConfigAppleEmbedded::resolve_system_dependencies(Vector<String> p_paths) {
|
||||
Vector<String> paths;
|
||||
|
||||
for (int i = 0; i < p_paths.size(); i++) {
|
||||
String path = resolve_system_dependency_path(p_paths[i]);
|
||||
|
||||
if (path.is_empty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
paths.push_back(path);
|
||||
}
|
||||
|
||||
return paths;
|
||||
}
|
||||
|
||||
bool PluginConfigAppleEmbedded::validate_plugin(PluginConfigAppleEmbedded &plugin_config) {
|
||||
bool valid_name = !plugin_config.name.is_empty();
|
||||
bool valid_binary_name = !plugin_config.binary.is_empty();
|
||||
bool valid_initialize = !plugin_config.initialization_method.is_empty();
|
||||
bool valid_deinitialize = !plugin_config.deinitialization_method.is_empty();
|
||||
|
||||
bool fields_value = valid_name && valid_binary_name && valid_initialize && valid_deinitialize;
|
||||
|
||||
if (!fields_value) {
|
||||
return false;
|
||||
}
|
||||
|
||||
String plugin_extension = plugin_config.binary.get_extension().to_lower();
|
||||
|
||||
if ((plugin_extension == "a" && FileAccess::exists(plugin_config.binary)) ||
|
||||
(plugin_extension == "xcframework" && DirAccess::exists(plugin_config.binary))) {
|
||||
plugin_config.valid_config = true;
|
||||
plugin_config.supports_targets = false;
|
||||
} else {
|
||||
String file_path = plugin_config.binary.get_base_dir();
|
||||
String file_name = plugin_config.binary.get_basename().get_file();
|
||||
String file_extension = plugin_config.binary.get_extension();
|
||||
String release_file_name = file_path.path_join(file_name + ".release." + file_extension);
|
||||
String debug_file_name = file_path.path_join(file_name + ".debug." + file_extension);
|
||||
|
||||
if ((plugin_extension == "a" && FileAccess::exists(release_file_name) && FileAccess::exists(debug_file_name)) ||
|
||||
(plugin_extension == "xcframework" && DirAccess::exists(release_file_name) && DirAccess::exists(debug_file_name))) {
|
||||
plugin_config.valid_config = true;
|
||||
plugin_config.supports_targets = true;
|
||||
}
|
||||
}
|
||||
|
||||
return plugin_config.valid_config;
|
||||
}
|
||||
|
||||
String PluginConfigAppleEmbedded::get_plugin_main_binary(PluginConfigAppleEmbedded &plugin_config, bool p_debug) {
|
||||
if (!plugin_config.supports_targets) {
|
||||
return plugin_config.binary;
|
||||
}
|
||||
|
||||
String plugin_binary_dir = plugin_config.binary.get_base_dir();
|
||||
String plugin_name_prefix = plugin_config.binary.get_basename().get_file();
|
||||
String plugin_extension = plugin_config.binary.get_extension();
|
||||
String plugin_file = plugin_name_prefix + "." + (p_debug ? "debug" : "release") + "." + plugin_extension;
|
||||
|
||||
return plugin_binary_dir.path_join(plugin_file);
|
||||
}
|
||||
|
||||
uint64_t PluginConfigAppleEmbedded::get_plugin_modification_time(const PluginConfigAppleEmbedded &plugin_config, const String &config_path) {
|
||||
uint64_t last_updated = FileAccess::get_modified_time(config_path);
|
||||
|
||||
if (!plugin_config.supports_targets) {
|
||||
last_updated = MAX(last_updated, FileAccess::get_modified_time(plugin_config.binary));
|
||||
} else {
|
||||
String file_path = plugin_config.binary.get_base_dir();
|
||||
String file_name = plugin_config.binary.get_basename().get_file();
|
||||
String plugin_extension = plugin_config.binary.get_extension();
|
||||
String release_file_name = file_path.path_join(file_name + ".release." + plugin_extension);
|
||||
String debug_file_name = file_path.path_join(file_name + ".debug." + plugin_extension);
|
||||
|
||||
last_updated = MAX(last_updated, FileAccess::get_modified_time(release_file_name));
|
||||
last_updated = MAX(last_updated, FileAccess::get_modified_time(debug_file_name));
|
||||
}
|
||||
|
||||
return last_updated;
|
||||
}
|
||||
|
||||
PluginConfigAppleEmbedded PluginConfigAppleEmbedded::load_plugin_config(Ref<ConfigFile> config_file, const String &path) {
|
||||
PluginConfigAppleEmbedded plugin_config = {};
|
||||
|
||||
if (config_file.is_null()) {
|
||||
return plugin_config;
|
||||
}
|
||||
|
||||
config_file->clear();
|
||||
|
||||
Error err = config_file->load(path);
|
||||
|
||||
if (err != OK) {
|
||||
return plugin_config;
|
||||
}
|
||||
|
||||
String config_base_dir = path.get_base_dir();
|
||||
|
||||
plugin_config.name = config_file->get_value(PluginConfigAppleEmbedded::CONFIG_SECTION, PluginConfigAppleEmbedded::CONFIG_NAME_KEY, String());
|
||||
plugin_config.use_swift_runtime = config_file->get_value(PluginConfigAppleEmbedded::CONFIG_SECTION, PluginConfigAppleEmbedded::CONFIG_USE_SWIFT_KEY, false);
|
||||
plugin_config.initialization_method = config_file->get_value(PluginConfigAppleEmbedded::CONFIG_SECTION, PluginConfigAppleEmbedded::CONFIG_INITIALIZE_KEY, String());
|
||||
plugin_config.deinitialization_method = config_file->get_value(PluginConfigAppleEmbedded::CONFIG_SECTION, PluginConfigAppleEmbedded::CONFIG_DEINITIALIZE_KEY, String());
|
||||
|
||||
String binary_path = config_file->get_value(PluginConfigAppleEmbedded::CONFIG_SECTION, PluginConfigAppleEmbedded::CONFIG_BINARY_KEY, String());
|
||||
plugin_config.binary = resolve_local_dependency_path(config_base_dir, binary_path);
|
||||
|
||||
if (config_file->has_section(PluginConfigAppleEmbedded::DEPENDENCIES_SECTION)) {
|
||||
Vector<String> linked_dependencies = config_file->get_value(PluginConfigAppleEmbedded::DEPENDENCIES_SECTION, PluginConfigAppleEmbedded::DEPENDENCIES_LINKED_KEY, Vector<String>());
|
||||
Vector<String> embedded_dependencies = config_file->get_value(PluginConfigAppleEmbedded::DEPENDENCIES_SECTION, PluginConfigAppleEmbedded::DEPENDENCIES_EMBEDDED_KEY, Vector<String>());
|
||||
Vector<String> system_dependencies = config_file->get_value(PluginConfigAppleEmbedded::DEPENDENCIES_SECTION, PluginConfigAppleEmbedded::DEPENDENCIES_SYSTEM_KEY, Vector<String>());
|
||||
Vector<String> files = config_file->get_value(PluginConfigAppleEmbedded::DEPENDENCIES_SECTION, PluginConfigAppleEmbedded::DEPENDENCIES_FILES_KEY, Vector<String>());
|
||||
|
||||
plugin_config.linked_dependencies = resolve_local_dependencies(config_base_dir, linked_dependencies);
|
||||
plugin_config.embedded_dependencies = resolve_local_dependencies(config_base_dir, embedded_dependencies);
|
||||
plugin_config.system_dependencies = resolve_system_dependencies(system_dependencies);
|
||||
|
||||
plugin_config.files_to_copy = resolve_local_dependencies(config_base_dir, files);
|
||||
|
||||
plugin_config.capabilities = config_file->get_value(PluginConfigAppleEmbedded::DEPENDENCIES_SECTION, PluginConfigAppleEmbedded::DEPENDENCIES_CAPABILITIES_KEY, Vector<String>());
|
||||
|
||||
plugin_config.linker_flags = config_file->get_value(PluginConfigAppleEmbedded::DEPENDENCIES_SECTION, PluginConfigAppleEmbedded::DEPENDENCIES_LINKER_FLAGS, Vector<String>());
|
||||
}
|
||||
|
||||
if (config_file->has_section(PluginConfigAppleEmbedded::PLIST_SECTION)) {
|
||||
Vector<String> keys = config_file->get_section_keys(PluginConfigAppleEmbedded::PLIST_SECTION);
|
||||
|
||||
for (const String &key : keys) {
|
||||
Vector<String> key_components = key.split(":");
|
||||
|
||||
String key_value = "";
|
||||
PluginConfigAppleEmbedded::PlistItemType key_type = PluginConfigAppleEmbedded::PlistItemType::UNKNOWN;
|
||||
|
||||
if (key_components.size() == 1) {
|
||||
key_value = key_components[0];
|
||||
key_type = PluginConfigAppleEmbedded::PlistItemType::STRING;
|
||||
} else if (key_components.size() == 2) {
|
||||
key_value = key_components[0];
|
||||
|
||||
if (key_components[1].to_lower() == "string") {
|
||||
key_type = PluginConfigAppleEmbedded::PlistItemType::STRING;
|
||||
} else if (key_components[1].to_lower() == "integer") {
|
||||
key_type = PluginConfigAppleEmbedded::PlistItemType::INTEGER;
|
||||
} else if (key_components[1].to_lower() == "boolean") {
|
||||
key_type = PluginConfigAppleEmbedded::PlistItemType::BOOLEAN;
|
||||
} else if (key_components[1].to_lower() == "raw") {
|
||||
key_type = PluginConfigAppleEmbedded::PlistItemType::RAW;
|
||||
} else if (key_components[1].to_lower() == "string_input") {
|
||||
key_type = PluginConfigAppleEmbedded::PlistItemType::STRING_INPUT;
|
||||
}
|
||||
}
|
||||
|
||||
if (key_value.is_empty() || key_type == PluginConfigAppleEmbedded::PlistItemType::UNKNOWN) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String value;
|
||||
|
||||
switch (key_type) {
|
||||
case PluginConfigAppleEmbedded::PlistItemType::STRING: {
|
||||
String raw_value = config_file->get_value(PluginConfigAppleEmbedded::PLIST_SECTION, key, String());
|
||||
value = "<string>" + raw_value + "</string>";
|
||||
} break;
|
||||
case PluginConfigAppleEmbedded::PlistItemType::INTEGER: {
|
||||
int raw_value = config_file->get_value(PluginConfigAppleEmbedded::PLIST_SECTION, key, 0);
|
||||
Dictionary value_dictionary;
|
||||
String value_format = "<integer>$value</integer>";
|
||||
value_dictionary["value"] = raw_value;
|
||||
value = value_format.format(value_dictionary, "$_");
|
||||
} break;
|
||||
case PluginConfigAppleEmbedded::PlistItemType::BOOLEAN:
|
||||
if (config_file->get_value(PluginConfigAppleEmbedded::PLIST_SECTION, key, false)) {
|
||||
value = "<true/>";
|
||||
} else {
|
||||
value = "<false/>";
|
||||
}
|
||||
break;
|
||||
case PluginConfigAppleEmbedded::PlistItemType::RAW: {
|
||||
String raw_value = config_file->get_value(PluginConfigAppleEmbedded::PLIST_SECTION, key, String());
|
||||
value = raw_value;
|
||||
} break;
|
||||
case PluginConfigAppleEmbedded::PlistItemType::STRING_INPUT: {
|
||||
String raw_value = config_file->get_value(PluginConfigAppleEmbedded::PLIST_SECTION, key, String());
|
||||
value = raw_value;
|
||||
} break;
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
|
||||
plugin_config.plist[key_value] = PluginConfigAppleEmbedded::PlistItem{ key_type, value };
|
||||
}
|
||||
}
|
||||
|
||||
if (validate_plugin(plugin_config)) {
|
||||
plugin_config.last_updated = get_plugin_modification_time(plugin_config, path);
|
||||
}
|
||||
|
||||
return plugin_config;
|
||||
}
|
131
editor/export/plugin_config_apple_embedded.h
Normal file
131
editor/export/plugin_config_apple_embedded.h
Normal file
@@ -0,0 +1,131 @@
|
||||
/**************************************************************************/
|
||||
/* plugin_config_apple_embedded.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/io/config_file.h"
|
||||
#include "core/string/ustring.h"
|
||||
|
||||
/*
|
||||
The `config` section and fields are required and defined as follow:
|
||||
- **name**: name of the plugin
|
||||
- **binary**: path to static `.a` library
|
||||
- **use_swift_runtime**: optional boolean field used to determine if Swift runtime is used
|
||||
|
||||
The `dependencies` and fields are optional.
|
||||
- **linked**: dependencies that should only be linked.
|
||||
- **embedded**: dependencies that should be linked and embedded into application.
|
||||
- **system**: system dependencies that should be linked.
|
||||
- **capabilities**: capabilities that would be used for `UIRequiredDeviceCapabilities` options in Info.plist file.
|
||||
- **files**: files that would be copied into application
|
||||
|
||||
The `plist` section are optional.
|
||||
- **key**: key and value that would be added in Info.plist file.
|
||||
*/
|
||||
|
||||
struct PluginConfigAppleEmbedded {
|
||||
inline static const char *PLUGIN_CONFIG_EXT = ".gdip";
|
||||
|
||||
inline static const char *CONFIG_SECTION = "config";
|
||||
inline static const char *CONFIG_NAME_KEY = "name";
|
||||
inline static const char *CONFIG_BINARY_KEY = "binary";
|
||||
inline static const char *CONFIG_USE_SWIFT_KEY = "use_swift_runtime";
|
||||
inline static const char *CONFIG_INITIALIZE_KEY = "initialization";
|
||||
inline static const char *CONFIG_DEINITIALIZE_KEY = "deinitialization";
|
||||
|
||||
inline static const char *DEPENDENCIES_SECTION = "dependencies";
|
||||
inline static const char *DEPENDENCIES_LINKED_KEY = "linked";
|
||||
inline static const char *DEPENDENCIES_EMBEDDED_KEY = "embedded";
|
||||
inline static const char *DEPENDENCIES_SYSTEM_KEY = "system";
|
||||
inline static const char *DEPENDENCIES_CAPABILITIES_KEY = "capabilities";
|
||||
inline static const char *DEPENDENCIES_FILES_KEY = "files";
|
||||
inline static const char *DEPENDENCIES_LINKER_FLAGS = "linker_flags";
|
||||
|
||||
inline static const char *PLIST_SECTION = "plist";
|
||||
|
||||
enum PlistItemType {
|
||||
UNKNOWN,
|
||||
STRING,
|
||||
INTEGER,
|
||||
BOOLEAN,
|
||||
RAW,
|
||||
STRING_INPUT,
|
||||
};
|
||||
|
||||
struct PlistItem {
|
||||
PlistItemType type;
|
||||
String value;
|
||||
};
|
||||
|
||||
// Set to true when the config file is properly loaded.
|
||||
bool valid_config = false;
|
||||
bool supports_targets = false;
|
||||
// Unix timestamp of last change to this plugin.
|
||||
uint64_t last_updated = 0;
|
||||
|
||||
// Required config section
|
||||
String name;
|
||||
String binary;
|
||||
bool use_swift_runtime;
|
||||
String initialization_method;
|
||||
String deinitialization_method;
|
||||
|
||||
// Optional dependencies section
|
||||
Vector<String> linked_dependencies;
|
||||
Vector<String> embedded_dependencies;
|
||||
Vector<String> system_dependencies;
|
||||
|
||||
Vector<String> files_to_copy;
|
||||
Vector<String> capabilities;
|
||||
|
||||
Vector<String> linker_flags;
|
||||
|
||||
// Optional plist section
|
||||
// String value is default value.
|
||||
// Currently supports `string`, `boolean`, `integer`, `raw`, `string_input` types
|
||||
// <name>:<type> = <value>
|
||||
HashMap<String, PlistItem> plist;
|
||||
|
||||
static String resolve_local_dependency_path(String plugin_config_dir, String dependency_path);
|
||||
|
||||
static String resolve_system_dependency_path(String dependency_path);
|
||||
|
||||
static Vector<String> resolve_local_dependencies(String plugin_config_dir, Vector<String> p_paths);
|
||||
|
||||
static Vector<String> resolve_system_dependencies(Vector<String> p_paths);
|
||||
|
||||
static bool validate_plugin(PluginConfigAppleEmbedded &plugin_config);
|
||||
|
||||
static String get_plugin_main_binary(PluginConfigAppleEmbedded &plugin_config, bool p_debug);
|
||||
|
||||
static uint64_t get_plugin_modification_time(const PluginConfigAppleEmbedded &plugin_config, const String &config_path);
|
||||
|
||||
static PluginConfigAppleEmbedded load_plugin_config(Ref<ConfigFile> config_file, const String &path);
|
||||
};
|
1865
editor/export/project_export.cpp
Normal file
1865
editor/export/project_export.cpp
Normal file
File diff suppressed because it is too large
Load Diff
226
editor/export/project_export.h
Normal file
226
editor/export/project_export.h
Normal file
@@ -0,0 +1,226 @@
|
||||
/**************************************************************************/
|
||||
/* project_export.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "editor/export/editor_export_preset.h"
|
||||
#include "scene/gui/dialogs.h"
|
||||
|
||||
class CheckBox;
|
||||
class CheckButton;
|
||||
class EditorFileDialog;
|
||||
class EditorFileSystemDirectory;
|
||||
class EditorInspector;
|
||||
class EditorPropertyPath;
|
||||
class ItemList;
|
||||
class LinkButton;
|
||||
class MenuButton;
|
||||
class OptionButton;
|
||||
class PopupMenu;
|
||||
class ProjectExportDialog;
|
||||
class RichTextLabel;
|
||||
class TabContainer;
|
||||
class Tree;
|
||||
class TreeItem;
|
||||
|
||||
class ProjectExportTextureFormatError : public HBoxContainer {
|
||||
GDCLASS(ProjectExportTextureFormatError, HBoxContainer);
|
||||
|
||||
ProjectExportDialog *export_dialog = nullptr;
|
||||
Label *texture_format_error_label = nullptr;
|
||||
LinkButton *fix_texture_format_button = nullptr;
|
||||
String setting_identifier;
|
||||
void _on_fix_texture_format_pressed();
|
||||
|
||||
protected:
|
||||
static void _bind_methods();
|
||||
void _notification(int p_what);
|
||||
|
||||
public:
|
||||
void show_for_texture_format(const String &p_friendly_name, const String &p_setting_identifier);
|
||||
ProjectExportTextureFormatError(ProjectExportDialog *p_export_dialog);
|
||||
};
|
||||
|
||||
class ProjectExportDialog : public ConfirmationDialog {
|
||||
GDCLASS(ProjectExportDialog, ConfirmationDialog);
|
||||
|
||||
TabContainer *sections = nullptr;
|
||||
|
||||
MenuButton *add_preset = nullptr;
|
||||
Button *duplicate_preset = nullptr;
|
||||
Button *delete_preset = nullptr;
|
||||
ItemList *presets = nullptr;
|
||||
|
||||
LineEdit *name = nullptr;
|
||||
EditorPropertyPath *export_path = nullptr;
|
||||
EditorInspector *parameters = nullptr;
|
||||
CheckButton *runnable = nullptr;
|
||||
CheckButton *advanced_options = nullptr;
|
||||
|
||||
Button *button_export = nullptr;
|
||||
bool updating = false;
|
||||
|
||||
RichTextLabel *result_dialog_log = nullptr;
|
||||
AcceptDialog *result_dialog = nullptr;
|
||||
ConfirmationDialog *delete_confirm = nullptr;
|
||||
|
||||
OptionButton *export_filter = nullptr;
|
||||
LineEdit *include_filters = nullptr;
|
||||
LineEdit *exclude_filters = nullptr;
|
||||
Tree *include_files = nullptr;
|
||||
Label *server_strip_message = nullptr;
|
||||
PopupMenu *file_mode_popup = nullptr;
|
||||
|
||||
Label *include_label = nullptr;
|
||||
MarginContainer *include_margin = nullptr;
|
||||
|
||||
Button *export_button = nullptr;
|
||||
Button *export_all_button = nullptr;
|
||||
AcceptDialog *export_all_dialog = nullptr;
|
||||
|
||||
RBSet<String> feature_set;
|
||||
|
||||
Tree *patches = nullptr;
|
||||
int patch_index = -1;
|
||||
EditorFileDialog *patch_dialog = nullptr;
|
||||
ConfirmationDialog *patch_erase = nullptr;
|
||||
Button *patch_add_btn = nullptr;
|
||||
|
||||
LineEdit *custom_features = nullptr;
|
||||
RichTextLabel *custom_feature_display = nullptr;
|
||||
|
||||
LineEdit *script_key = nullptr;
|
||||
Label *script_key_error = nullptr;
|
||||
|
||||
ProjectExportTextureFormatError *export_texture_format_error = nullptr;
|
||||
Label *export_error = nullptr;
|
||||
Label *export_warning = nullptr;
|
||||
HBoxContainer *export_templates_error = nullptr;
|
||||
|
||||
String default_filename;
|
||||
|
||||
bool exporting = false;
|
||||
|
||||
void _advanced_options_pressed();
|
||||
void _runnable_pressed();
|
||||
void _update_parameters(const String &p_edited_property);
|
||||
void _name_changed(const String &p_string);
|
||||
void _export_path_changed(const StringName &p_property, const Variant &p_value, const String &p_field, bool p_changing);
|
||||
void _add_preset(int p_platform);
|
||||
void _edit_preset(int p_index);
|
||||
void _duplicate_preset();
|
||||
void _delete_preset();
|
||||
void _delete_preset_confirm();
|
||||
void _update_export_all();
|
||||
|
||||
void _force_update_current_preset_parameters();
|
||||
void _update_current_preset();
|
||||
void _update_presets();
|
||||
|
||||
void _export_type_changed(int p_which);
|
||||
void _filter_changed(const String &p_filter);
|
||||
String _get_resource_export_header(EditorExportPreset::ExportFilter p_filter) const;
|
||||
void _fill_resource_tree();
|
||||
void _setup_item_for_file_mode(TreeItem *p_item, EditorExportPreset::FileExportMode p_mode);
|
||||
bool _fill_tree(EditorFileSystemDirectory *p_dir, TreeItem *p_item, Ref<EditorExportPreset> ¤t, EditorExportPreset::ExportFilter p_export_filter);
|
||||
void _propagate_file_export_mode(TreeItem *p_item, EditorExportPreset::FileExportMode p_inherited_export_mode);
|
||||
void _tree_changed();
|
||||
void _check_propagated_to_item(Object *p_obj, int column);
|
||||
void _tree_popup_edited(bool p_arrow_clicked);
|
||||
void _set_file_export_mode(int p_id);
|
||||
|
||||
void _patch_tree_button_clicked(Object *p_item, int p_column, int p_id, int p_mouse_button_index);
|
||||
void _patch_tree_item_edited();
|
||||
void _patch_file_selected(const String &p_path);
|
||||
void _patch_delete_confirmed();
|
||||
void _patch_add_pack_pressed();
|
||||
|
||||
Variant get_drag_data_fw(const Point2 &p_point, Control *p_from);
|
||||
bool can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) const;
|
||||
void drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from);
|
||||
|
||||
EditorFileDialog *export_pck_zip = nullptr;
|
||||
EditorFileDialog *export_project = nullptr;
|
||||
|
||||
CheckButton *enc_pck = nullptr;
|
||||
CheckButton *enc_directory = nullptr;
|
||||
LineEdit *enc_in_filters = nullptr;
|
||||
LineEdit *enc_ex_filters = nullptr;
|
||||
LineEdit *seed_input = nullptr;
|
||||
|
||||
OptionButton *script_mode = nullptr;
|
||||
|
||||
void _open_export_template_manager();
|
||||
|
||||
void _export_pck_zip();
|
||||
void _export_pck_zip_selected(const String &p_path);
|
||||
|
||||
void _validate_export_path(const String &p_path);
|
||||
void _export_project();
|
||||
void _export_project_to_path(const String &p_path);
|
||||
void _export_all_dialog();
|
||||
void _export_all_dialog_action(const String &p_str);
|
||||
void _export_all(bool p_debug);
|
||||
|
||||
void _update_feature_list();
|
||||
void _custom_features_changed(const String &p_text);
|
||||
|
||||
bool updating_script_key = false;
|
||||
bool updating_enc_filters = false;
|
||||
bool updating_seed = false;
|
||||
void _enc_pck_changed(bool p_pressed);
|
||||
void _enc_directory_changed(bool p_pressed);
|
||||
void _enc_filters_changed(const String &p_text);
|
||||
void _seed_input_changed(const String &p_text);
|
||||
void _script_encryption_key_changed(const String &p_key);
|
||||
bool _validate_script_encryption_key(const String &p_key);
|
||||
|
||||
void _script_export_mode_changed(int p_mode);
|
||||
|
||||
void _open_key_help_link();
|
||||
|
||||
void _tab_changed(int);
|
||||
|
||||
protected:
|
||||
void _notification(int p_what);
|
||||
static void _bind_methods();
|
||||
|
||||
public:
|
||||
void popup_export();
|
||||
|
||||
void set_export_path(const String &p_value);
|
||||
String get_export_path();
|
||||
|
||||
Ref<EditorExportPreset> get_current_preset() const;
|
||||
|
||||
bool is_exporting() const { return exporting; }
|
||||
|
||||
ProjectExportDialog();
|
||||
};
|
122
editor/export/project_zip_packer.cpp
Normal file
122
editor/export/project_zip_packer.cpp
Normal file
@@ -0,0 +1,122 @@
|
||||
/**************************************************************************/
|
||||
/* project_zip_packer.cpp */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#include "project_zip_packer.h"
|
||||
|
||||
#include "core/config/project_settings.h"
|
||||
#include "core/io/dir_access.h"
|
||||
#include "core/io/file_access.h"
|
||||
#include "core/os/os.h"
|
||||
#include "core/os/time.h"
|
||||
|
||||
String ProjectZIPPacker::get_project_zip_safe_name() {
|
||||
// Name the downloaded ZIP file to contain the project name and download date for easier organization.
|
||||
// Replace characters not allowed (or risky) in Windows file names with safe characters.
|
||||
// In the project name, all invalid characters become an empty string so that a name
|
||||
// like "Platformer 2: Godette's Revenge" becomes "platformer_2-_godette-s_revenge".
|
||||
const String project_name = GLOBAL_GET("application/config/name");
|
||||
const String project_name_safe = project_name.to_lower().replace_char(' ', '_');
|
||||
const String datetime_safe =
|
||||
Time::get_singleton()->get_datetime_string_from_system(false, true).replace_char(' ', '_');
|
||||
const String output_name = OS::get_singleton()->get_safe_dir_name(vformat("%s_%s.zip", project_name_safe, datetime_safe));
|
||||
return output_name;
|
||||
}
|
||||
|
||||
void ProjectZIPPacker::pack_project_zip(const String &p_path) {
|
||||
Ref<FileAccess> io_fa;
|
||||
zlib_filefunc_def io = zipio_create_io(&io_fa);
|
||||
|
||||
String resource_path = ProjectSettings::get_singleton()->get_resource_path();
|
||||
const String base_path = resource_path.substr(0, resource_path.rfind_char('/')) + "/";
|
||||
|
||||
zipFile zip = zipOpen2(p_path.utf8().get_data(), APPEND_STATUS_CREATE, nullptr, &io);
|
||||
_zip_recursive(resource_path, base_path, zip);
|
||||
zipClose(zip, nullptr);
|
||||
}
|
||||
|
||||
void ProjectZIPPacker::_zip_file(const String &p_path, const String &p_base_path, zipFile p_zip) {
|
||||
Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::READ);
|
||||
if (f.is_null()) {
|
||||
WARN_PRINT("Unable to open file for zipping: " + p_path);
|
||||
return;
|
||||
}
|
||||
Vector<uint8_t> data;
|
||||
uint64_t len = f->get_length();
|
||||
data.resize(len);
|
||||
f->get_buffer(data.ptrw(), len);
|
||||
|
||||
String path = p_path.replace_first(p_base_path, "");
|
||||
zipOpenNewFileInZip(p_zip,
|
||||
path.utf8().get_data(),
|
||||
nullptr,
|
||||
nullptr,
|
||||
0,
|
||||
nullptr,
|
||||
0,
|
||||
nullptr,
|
||||
Z_DEFLATED,
|
||||
Z_DEFAULT_COMPRESSION);
|
||||
zipWriteInFileInZip(p_zip, data.ptr(), data.size());
|
||||
zipCloseFileInZip(p_zip);
|
||||
}
|
||||
|
||||
void ProjectZIPPacker::_zip_recursive(const String &p_path, const String &p_base_path, zipFile p_zip) {
|
||||
Ref<DirAccess> dir = DirAccess::open(p_path);
|
||||
if (dir.is_null()) {
|
||||
WARN_PRINT("Unable to open directory for zipping: " + p_path);
|
||||
return;
|
||||
}
|
||||
dir->list_dir_begin();
|
||||
String cur = dir->get_next();
|
||||
String project_data_dir_name = ProjectSettings::get_singleton()->get_project_data_dir_name();
|
||||
while (!cur.is_empty()) {
|
||||
String cs = p_path.path_join(cur);
|
||||
if (cur == "." || cur == ".." || cur == project_data_dir_name) {
|
||||
// Skip
|
||||
} else if (dir->current_is_dir()) {
|
||||
String path = cs.replace_first(p_base_path, "") + "/";
|
||||
zipOpenNewFileInZip(p_zip,
|
||||
path.utf8().get_data(),
|
||||
nullptr,
|
||||
nullptr,
|
||||
0,
|
||||
nullptr,
|
||||
0,
|
||||
nullptr,
|
||||
Z_DEFLATED,
|
||||
Z_DEFAULT_COMPRESSION);
|
||||
zipCloseFileInZip(p_zip);
|
||||
_zip_recursive(cs, p_base_path, p_zip);
|
||||
} else {
|
||||
_zip_file(cs, p_base_path, p_zip);
|
||||
}
|
||||
cur = dir->get_next();
|
||||
}
|
||||
}
|
43
editor/export/project_zip_packer.h
Normal file
43
editor/export/project_zip_packer.h
Normal file
@@ -0,0 +1,43 @@
|
||||
/**************************************************************************/
|
||||
/* project_zip_packer.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/io/zip_io.h"
|
||||
#include "core/variant/variant.h"
|
||||
|
||||
class ProjectZIPPacker {
|
||||
static void _zip_file(const String &p_path, const String &p_base_path, zipFile p_zip);
|
||||
static void _zip_recursive(const String &p_path, const String &p_base_path, zipFile p_zip);
|
||||
|
||||
public:
|
||||
static String get_project_zip_safe_name();
|
||||
static void pack_project_zip(const String &p_path);
|
||||
};
|
34
editor/export/register_exporters.h
Normal file
34
editor/export/register_exporters.h
Normal file
@@ -0,0 +1,34 @@
|
||||
/**************************************************************************/
|
||||
/* register_exporters.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
void register_exporter_types();
|
||||
void register_exporters();
|
471
editor/export/shader_baker_export_plugin.cpp
Normal file
471
editor/export/shader_baker_export_plugin.cpp
Normal file
@@ -0,0 +1,471 @@
|
||||
/**************************************************************************/
|
||||
/* shader_baker_export_plugin.cpp */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#include "shader_baker_export_plugin.h"
|
||||
|
||||
#include "core/config/project_settings.h"
|
||||
#include "core/version.h"
|
||||
#include "editor/editor_node.h"
|
||||
#include "scene/3d/label_3d.h"
|
||||
#include "scene/3d/sprite_3d.h"
|
||||
#include "servers/rendering/renderer_rd/renderer_scene_render_rd.h"
|
||||
#include "servers/rendering/renderer_rd/storage_rd/material_storage.h"
|
||||
|
||||
// Ensure that AlphaCut is the same between the two classes so we can share the code to detect transparency.
|
||||
static_assert(ENUM_MEMBERS_EQUAL(SpriteBase3D::ALPHA_CUT_DISABLED, Label3D::ALPHA_CUT_DISABLED));
|
||||
static_assert(ENUM_MEMBERS_EQUAL(SpriteBase3D::ALPHA_CUT_DISCARD, Label3D::ALPHA_CUT_DISCARD));
|
||||
static_assert(ENUM_MEMBERS_EQUAL(SpriteBase3D::ALPHA_CUT_OPAQUE_PREPASS, Label3D::ALPHA_CUT_OPAQUE_PREPASS));
|
||||
static_assert(ENUM_MEMBERS_EQUAL(SpriteBase3D::ALPHA_CUT_HASH, Label3D::ALPHA_CUT_HASH));
|
||||
static_assert(ENUM_MEMBERS_EQUAL(SpriteBase3D::ALPHA_CUT_MAX, Label3D::ALPHA_CUT_MAX));
|
||||
|
||||
String ShaderBakerExportPlugin::get_name() const {
|
||||
return "ShaderBaker";
|
||||
}
|
||||
|
||||
bool ShaderBakerExportPlugin::_is_active(const Vector<String> &p_features) const {
|
||||
// Shader baker should only work when a RendererRD driver is active, as the embedded shaders won't be found otherwise.
|
||||
return RendererSceneRenderRD::get_singleton() != nullptr && RendererRD::MaterialStorage::get_singleton() != nullptr && p_features.has("shader_baker");
|
||||
}
|
||||
|
||||
bool ShaderBakerExportPlugin::_initialize_container_format(const Ref<EditorExportPlatform> &p_platform, const Vector<String> &p_features, const Ref<EditorExportPreset> &p_preset) {
|
||||
Variant driver_variant = GLOBAL_GET("rendering/rendering_device/driver." + p_platform->get_os_name().to_lower());
|
||||
if (!driver_variant.is_string()) {
|
||||
driver_variant = GLOBAL_GET("rendering/rendering_device/driver");
|
||||
if (!driver_variant.is_string()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
shader_container_driver = driver_variant;
|
||||
|
||||
for (Ref<ShaderBakerExportPluginPlatform> platform : platforms) {
|
||||
if (platform->matches_driver(shader_container_driver)) {
|
||||
shader_container_format = platform->create_shader_container_format(p_platform, get_export_preset());
|
||||
ERR_FAIL_NULL_V_MSG(shader_container_format, false, "Unable to create shader container format for the export platform.");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void ShaderBakerExportPlugin::_cleanup_container_format() {
|
||||
if (shader_container_format != nullptr) {
|
||||
memdelete(shader_container_format);
|
||||
shader_container_format = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
bool ShaderBakerExportPlugin::_initialize_cache_directory() {
|
||||
shader_cache_export_path = get_export_base_path().path_join("shader_baker").path_join(shader_cache_platform_name).path_join(shader_container_driver);
|
||||
|
||||
if (!DirAccess::dir_exists_absolute(shader_cache_export_path)) {
|
||||
Error err = DirAccess::make_dir_recursive_absolute(shader_cache_export_path);
|
||||
ERR_FAIL_COND_V_MSG(err != OK, false, "Can't create shader cache folder for exporting.");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ShaderBakerExportPlugin::_begin_customize_resources(const Ref<EditorExportPlatform> &p_platform, const Vector<String> &p_features) {
|
||||
if (!_is_active(p_features)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!_initialize_container_format(p_platform, p_features, get_export_preset())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Engine::get_singleton()->is_generate_spirv_debug_info_enabled()) {
|
||||
WARN_PRINT("Shader baker can't generate a compatible shader when run with --generate-spirv-debug-info. Restart the editor without this argument if you want to bake shaders.");
|
||||
return false;
|
||||
}
|
||||
|
||||
shader_cache_platform_name = p_platform->get_os_name();
|
||||
shader_cache_renderer_name = RendererSceneRenderRD::get_singleton()->get_name();
|
||||
tasks_processed = 0;
|
||||
tasks_total = 0;
|
||||
tasks_cancelled = false;
|
||||
|
||||
StringBuilder to_hash;
|
||||
to_hash.append("[GodotVersionNumber]");
|
||||
to_hash.append(GODOT_VERSION_NUMBER);
|
||||
to_hash.append("[GodotVersionHash]");
|
||||
to_hash.append(GODOT_VERSION_HASH);
|
||||
to_hash.append("[Renderer]");
|
||||
to_hash.append(shader_cache_renderer_name);
|
||||
customization_configuration_hash = to_hash.as_string().hash64();
|
||||
|
||||
BitField<RenderingShaderLibrary::FeatureBits> renderer_features = {};
|
||||
bool xr_enabled = GLOBAL_GET("xr/shaders/enabled");
|
||||
renderer_features.set_flag(RenderingShaderLibrary::FEATURE_ADVANCED_BIT);
|
||||
if (xr_enabled) {
|
||||
renderer_features.set_flag(RenderingShaderLibrary::FEATURE_MULTIVIEW_BIT);
|
||||
}
|
||||
|
||||
int vrs_mode = GLOBAL_GET("rendering/vrs/mode");
|
||||
if (vrs_mode != 0) {
|
||||
renderer_features.set_flag(RenderingShaderLibrary::FEATURE_VRS_BIT);
|
||||
}
|
||||
|
||||
// Both FP16 and FP32 variants should be included.
|
||||
renderer_features.set_flag(RenderingShaderLibrary::FEATURE_FP16_BIT);
|
||||
renderer_features.set_flag(RenderingShaderLibrary::FEATURE_FP32_BIT);
|
||||
|
||||
RendererSceneRenderRD::get_singleton()->enable_features(renderer_features);
|
||||
|
||||
// Included all shaders created by renderers and effects.
|
||||
ShaderRD::shaders_embedded_set_lock();
|
||||
const ShaderRD::ShaderVersionPairSet &pair_set = ShaderRD::shaders_embedded_set_get();
|
||||
for (Pair<ShaderRD *, RID> pair : pair_set) {
|
||||
_customize_shader_version(pair.first, pair.second);
|
||||
}
|
||||
|
||||
ShaderRD::shaders_embedded_set_unlock();
|
||||
|
||||
// Include all shaders created by embedded materials.
|
||||
RendererRD::MaterialStorage *material_storage = RendererRD::MaterialStorage::get_singleton();
|
||||
material_storage->shader_embedded_set_lock();
|
||||
const HashSet<RID> &rid_set = material_storage->shader_embedded_set_get();
|
||||
for (RID rid : rid_set) {
|
||||
RendererRD::MaterialStorage::ShaderData *shader_data = material_storage->shader_get_data(rid);
|
||||
if (shader_data != nullptr) {
|
||||
Pair<ShaderRD *, RID> shader_version_pair = shader_data->get_native_shader_and_version();
|
||||
if (shader_version_pair.first != nullptr) {
|
||||
_customize_shader_version(shader_version_pair.first, shader_version_pair.second);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
material_storage->shader_embedded_set_unlock();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ShaderBakerExportPlugin::_begin_customize_scenes(const Ref<EditorExportPlatform> &p_platform, const Vector<String> &p_features) {
|
||||
if (!_is_active(p_features)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (shader_container_format == nullptr) {
|
||||
// Resource customization failed to initialize.
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ShaderBakerExportPlugin::_end_customize_resources() {
|
||||
if (!_initialize_cache_directory()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Run a progress bar that waits for all shader baking tasks to finish.
|
||||
bool progress_active = true;
|
||||
EditorProgress editor_progress("baking_shaders", TTR("Baking shaders"), tasks_total);
|
||||
editor_progress.step("Baking...", 0);
|
||||
while (progress_active) {
|
||||
uint32_t tasks_for_progress = 0;
|
||||
{
|
||||
MutexLock lock(tasks_mutex);
|
||||
if (tasks_processed >= tasks_total) {
|
||||
progress_active = false;
|
||||
} else {
|
||||
tasks_condition.wait(lock);
|
||||
tasks_for_progress = tasks_processed;
|
||||
}
|
||||
}
|
||||
|
||||
if (progress_active && editor_progress.step("Baking...", tasks_for_progress)) {
|
||||
// User skipped the shader baker, we just don't pack the shaders in the project.
|
||||
tasks_cancelled = true;
|
||||
progress_active = false;
|
||||
}
|
||||
}
|
||||
|
||||
String shader_cache_user_dir = ShaderRD::get_shader_cache_user_dir();
|
||||
for (const ShaderGroupItem &group_item : shader_group_items) {
|
||||
// Wait for all shader compilation tasks of the group to be finished.
|
||||
for (WorkerThreadPool::TaskID task_id : group_item.variant_tasks) {
|
||||
WorkerThreadPool::get_singleton()->wait_for_task_completion(task_id);
|
||||
}
|
||||
|
||||
if (!tasks_cancelled) {
|
||||
WorkResult work_result;
|
||||
{
|
||||
MutexLock lock(shader_work_results_mutex);
|
||||
work_result = shader_work_results[group_item.cache_path];
|
||||
}
|
||||
|
||||
PackedByteArray cache_file_bytes = ShaderRD::save_shader_cache_bytes(group_item.variants, work_result.variant_data);
|
||||
add_file(shader_cache_user_dir.path_join(group_item.cache_path), cache_file_bytes, false);
|
||||
|
||||
String cache_file_path = shader_cache_export_path.path_join(group_item.cache_path);
|
||||
if (!DirAccess::exists(cache_file_path)) {
|
||||
DirAccess::make_dir_recursive_absolute(cache_file_path.get_base_dir());
|
||||
}
|
||||
|
||||
Ref<FileAccess> cache_file_access = FileAccess::open(cache_file_path, FileAccess::WRITE);
|
||||
if (cache_file_access.is_valid()) {
|
||||
cache_file_access->store_buffer(cache_file_bytes);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!tasks_cancelled) {
|
||||
String file_cache_path = shader_cache_export_path.path_join("file_cache");
|
||||
Ref<FileAccess> cache_list_access = FileAccess::open(file_cache_path, FileAccess::READ_WRITE);
|
||||
if (cache_list_access.is_null()) {
|
||||
cache_list_access = FileAccess::open(file_cache_path, FileAccess::WRITE);
|
||||
}
|
||||
|
||||
if (cache_list_access.is_valid()) {
|
||||
String cache_list_line;
|
||||
while (cache_list_line = cache_list_access->get_line(), !cache_list_line.is_empty()) {
|
||||
// Only add if it wasn't already added.
|
||||
if (!shader_paths_processed.has(cache_list_line)) {
|
||||
PackedByteArray cache_file_bytes = FileAccess::get_file_as_bytes(shader_cache_export_path.path_join(cache_list_line));
|
||||
if (!cache_file_bytes.is_empty()) {
|
||||
add_file(shader_cache_user_dir.path_join(cache_list_line), cache_file_bytes, false);
|
||||
}
|
||||
}
|
||||
|
||||
shader_paths_processed.erase(cache_list_line);
|
||||
}
|
||||
|
||||
for (const String &shader_path : shader_paths_processed) {
|
||||
cache_list_access->store_line(shader_path);
|
||||
}
|
||||
|
||||
cache_list_access->close();
|
||||
}
|
||||
}
|
||||
|
||||
shader_paths_processed.clear();
|
||||
shader_work_results.clear();
|
||||
shader_group_items.clear();
|
||||
|
||||
_cleanup_container_format();
|
||||
}
|
||||
|
||||
Ref<Resource> ShaderBakerExportPlugin::_customize_resource(const Ref<Resource> &p_resource, const String &p_path) {
|
||||
RendererRD::MaterialStorage *singleton = RendererRD::MaterialStorage::get_singleton();
|
||||
DEV_ASSERT(singleton != nullptr);
|
||||
|
||||
Ref<Material> material = p_resource;
|
||||
if (material.is_valid()) {
|
||||
RID material_rid = material->get_rid();
|
||||
if (material_rid.is_valid()) {
|
||||
RendererRD::MaterialStorage::ShaderData *shader_data = singleton->material_get_shader_data(material_rid);
|
||||
if (shader_data != nullptr) {
|
||||
Pair<ShaderRD *, RID> shader_version_pair = shader_data->get_native_shader_and_version();
|
||||
if (shader_version_pair.first != nullptr) {
|
||||
_customize_shader_version(shader_version_pair.first, shader_version_pair.second);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Ref<Resource>();
|
||||
}
|
||||
|
||||
Node *ShaderBakerExportPlugin::_customize_scene(Node *p_root, const String &p_path) {
|
||||
LocalVector<Node *> nodes_to_visit;
|
||||
nodes_to_visit.push_back(p_root);
|
||||
while (!nodes_to_visit.is_empty()) {
|
||||
// Visit all nodes recursively in the scene to find the Label3Ds and Sprite3Ds.
|
||||
Node *node = nodes_to_visit[nodes_to_visit.size() - 1];
|
||||
nodes_to_visit.remove_at(nodes_to_visit.size() - 1);
|
||||
|
||||
Label3D *label_3d = Object::cast_to<Label3D>(node);
|
||||
Sprite3D *sprite_3d = Object::cast_to<Sprite3D>(node);
|
||||
if (label_3d != nullptr || sprite_3d != nullptr) {
|
||||
// Create materials for Label3D and Sprite3D, which are normally generated at runtime on demand.
|
||||
HashMap<StringName, Variant> properties;
|
||||
|
||||
// These must match the defaults set by Sprite3D/Label3D.
|
||||
properties["transparent"] = true; // Label3D doesn't have this property, but it is always true anyway.
|
||||
properties["shaded"] = false;
|
||||
properties["double_sided"] = true;
|
||||
properties["no_depth_test"] = false;
|
||||
properties["fixed_size"] = false;
|
||||
properties["billboard"] = StandardMaterial3D::BILLBOARD_DISABLED;
|
||||
properties["texture_filter"] = StandardMaterial3D::TEXTURE_FILTER_LINEAR_WITH_MIPMAPS;
|
||||
properties["alpha_antialiasing_mode"] = StandardMaterial3D::ALPHA_ANTIALIASING_OFF;
|
||||
properties["alpha_cut"] = SpriteBase3D::ALPHA_CUT_DISABLED;
|
||||
|
||||
List<PropertyInfo> property_list;
|
||||
node->get_property_list(&property_list);
|
||||
for (const PropertyInfo &info : property_list) {
|
||||
bool valid = false;
|
||||
Variant property = node->get(info.name, &valid);
|
||||
if (valid) {
|
||||
properties[info.name] = property;
|
||||
}
|
||||
}
|
||||
|
||||
// This must follow the logic in Sprite3D::draw_texture_rect().
|
||||
BaseMaterial3D::Transparency mat_transparency = BaseMaterial3D::Transparency::TRANSPARENCY_DISABLED;
|
||||
if (properties["transparent"]) {
|
||||
SpriteBase3D::AlphaCutMode acm = SpriteBase3D::AlphaCutMode(int(properties["alpha_cut"]));
|
||||
if (acm == SpriteBase3D::ALPHA_CUT_DISCARD) {
|
||||
mat_transparency = BaseMaterial3D::Transparency::TRANSPARENCY_ALPHA_SCISSOR;
|
||||
} else if (acm == SpriteBase3D::ALPHA_CUT_OPAQUE_PREPASS) {
|
||||
mat_transparency = BaseMaterial3D::Transparency::TRANSPARENCY_ALPHA_DEPTH_PRE_PASS;
|
||||
} else if (acm == SpriteBase3D::ALPHA_CUT_HASH) {
|
||||
mat_transparency = BaseMaterial3D::Transparency::TRANSPARENCY_ALPHA_HASH;
|
||||
} else {
|
||||
mat_transparency = BaseMaterial3D::Transparency::TRANSPARENCY_ALPHA;
|
||||
}
|
||||
}
|
||||
|
||||
StandardMaterial3D::BillboardMode billboard_mode = StandardMaterial3D::BillboardMode(int(properties["billboard"]));
|
||||
Ref<Material> sprite_3d_material = StandardMaterial3D::get_material_for_2d(bool(properties["shaded"]), mat_transparency, bool(properties["double_sided"]), billboard_mode == StandardMaterial3D::BILLBOARD_ENABLED, billboard_mode == StandardMaterial3D::BILLBOARD_FIXED_Y, false, bool(properties["no_depth_test"]), bool(properties["fixed_size"]), BaseMaterial3D::TextureFilter(int(properties["texture_filter"])), BaseMaterial3D::AlphaAntiAliasing(int(properties["alpha_antialiasing_mode"])));
|
||||
_customize_resource(sprite_3d_material, String());
|
||||
|
||||
if (label_3d != nullptr) {
|
||||
// Generate variants with and without MSDF support since we don't have access to the font here.
|
||||
Ref<Material> label_3d_material = StandardMaterial3D::get_material_for_2d(bool(properties["shaded"]), mat_transparency, bool(properties["double_sided"]), billboard_mode == StandardMaterial3D::BILLBOARD_ENABLED, billboard_mode == StandardMaterial3D::BILLBOARD_FIXED_Y, true, bool(properties["no_depth_test"]), bool(properties["fixed_size"]), BaseMaterial3D::TextureFilter(int(properties["texture_filter"])), BaseMaterial3D::AlphaAntiAliasing(int(properties["alpha_antialiasing_mode"])));
|
||||
_customize_resource(label_3d_material, String());
|
||||
}
|
||||
}
|
||||
|
||||
// Visit children.
|
||||
int child_count = node->get_child_count();
|
||||
for (int i = 0; i < child_count; i++) {
|
||||
nodes_to_visit.push_back(node->get_child(i));
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
uint64_t ShaderBakerExportPlugin::_get_customization_configuration_hash() const {
|
||||
return customization_configuration_hash;
|
||||
}
|
||||
|
||||
void ShaderBakerExportPlugin::_customize_shader_version(ShaderRD *p_shader, RID p_version) {
|
||||
const int64_t variant_count = p_shader->get_variant_count();
|
||||
const int64_t group_count = p_shader->get_group_count();
|
||||
LocalVector<ShaderGroupItem> group_items;
|
||||
group_items.resize(group_count);
|
||||
|
||||
RBSet<uint32_t> groups_to_compile;
|
||||
for (int64_t i = 0; i < group_count; i++) {
|
||||
if (!p_shader->is_group_enabled(i)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String cache_path = p_shader->version_get_cache_file_relative_path(p_version, i, shader_container_driver);
|
||||
if (shader_paths_processed.has(cache_path)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
shader_paths_processed.insert(cache_path);
|
||||
groups_to_compile.insert(i);
|
||||
|
||||
group_items[i].cache_path = cache_path;
|
||||
group_items[i].variants = p_shader->get_group_to_variants(i);
|
||||
|
||||
{
|
||||
MutexLock lock(shader_work_results_mutex);
|
||||
shader_work_results[cache_path].variant_data.resize(variant_count);
|
||||
}
|
||||
}
|
||||
|
||||
for (int64_t i = 0; i < variant_count; i++) {
|
||||
int group = p_shader->get_variant_to_group(i);
|
||||
if (!p_shader->is_variant_enabled(i) || !groups_to_compile.has(group)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
WorkItem work_item;
|
||||
work_item.cache_path = group_items[group].cache_path;
|
||||
work_item.shader_name = p_shader->get_name();
|
||||
work_item.stage_sources = p_shader->version_build_variant_stage_sources(p_version, i);
|
||||
work_item.variant = i;
|
||||
|
||||
WorkerThreadPool::TaskID task_id = WorkerThreadPool::get_singleton()->add_template_task(this, &ShaderBakerExportPlugin::_process_work_item, work_item);
|
||||
group_items[group].variant_tasks.push_back(task_id);
|
||||
tasks_total++;
|
||||
}
|
||||
|
||||
for (uint32_t i : groups_to_compile) {
|
||||
shader_group_items.push_back(group_items[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void ShaderBakerExportPlugin::_process_work_item(WorkItem p_work_item) {
|
||||
if (!tasks_cancelled) {
|
||||
// Only process the item if the tasks haven't been cancelled by the user yet.
|
||||
Vector<RD::ShaderStageSPIRVData> spirv_data = ShaderRD::compile_stages(p_work_item.stage_sources);
|
||||
ERR_FAIL_COND_MSG(spirv_data.is_empty(), "Unable to retrieve SPIR-V data for shader");
|
||||
|
||||
RD::ShaderReflection shader_refl;
|
||||
Error err = RenderingDeviceCommons::reflect_spirv(spirv_data, shader_refl);
|
||||
ERR_FAIL_COND_MSG(err != OK, "Unable to reflect SPIR-V data that was compiled");
|
||||
|
||||
Ref<RenderingShaderContainer> shader_container = shader_container_format->create_container();
|
||||
shader_container->set_from_shader_reflection(p_work_item.shader_name, shader_refl);
|
||||
|
||||
// Compile shader binary from SPIR-V.
|
||||
bool code_compiled = shader_container->set_code_from_spirv(spirv_data);
|
||||
ERR_FAIL_COND_MSG(!code_compiled, vformat("Failed to compile code to native for SPIR-V."));
|
||||
|
||||
PackedByteArray shader_bytes = shader_container->to_bytes();
|
||||
{
|
||||
MutexLock lock(shader_work_results_mutex);
|
||||
shader_work_results[p_work_item.cache_path].variant_data.ptrw()[p_work_item.variant] = shader_bytes;
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
MutexLock lock(tasks_mutex);
|
||||
tasks_processed++;
|
||||
}
|
||||
|
||||
tasks_condition.notify_one();
|
||||
}
|
||||
|
||||
ShaderBakerExportPlugin::ShaderBakerExportPlugin() {
|
||||
// Do nothing.
|
||||
}
|
||||
|
||||
ShaderBakerExportPlugin::~ShaderBakerExportPlugin() {
|
||||
// Do nothing.
|
||||
}
|
||||
|
||||
void ShaderBakerExportPlugin::add_platform(Ref<ShaderBakerExportPluginPlatform> p_platform) {
|
||||
platforms.push_back(p_platform);
|
||||
}
|
||||
|
||||
void ShaderBakerExportPlugin::remove_platform(Ref<ShaderBakerExportPluginPlatform> p_platform) {
|
||||
platforms.erase(p_platform);
|
||||
}
|
102
editor/export/shader_baker_export_plugin.h
Normal file
102
editor/export/shader_baker_export_plugin.h
Normal file
@@ -0,0 +1,102 @@
|
||||
/**************************************************************************/
|
||||
/* shader_baker_export_plugin.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "editor/export/editor_export_plugin.h"
|
||||
#include "servers/rendering/renderer_rd/shader_rd.h"
|
||||
#include "servers/rendering/rendering_shader_container.h"
|
||||
|
||||
class ShaderBakerExportPluginPlatform : public RefCounted {
|
||||
GDCLASS(ShaderBakerExportPluginPlatform, RefCounted);
|
||||
|
||||
public:
|
||||
virtual RenderingShaderContainerFormat *create_shader_container_format(const Ref<EditorExportPlatform> &p_platform, const Ref<EditorExportPreset> &p_preset) = 0;
|
||||
virtual bool matches_driver(const String &p_driver) = 0;
|
||||
virtual ~ShaderBakerExportPluginPlatform() {}
|
||||
};
|
||||
|
||||
class ShaderBakerExportPlugin : public EditorExportPlugin {
|
||||
protected:
|
||||
struct WorkItem {
|
||||
String cache_path;
|
||||
String shader_name;
|
||||
Vector<String> stage_sources;
|
||||
int64_t variant = 0;
|
||||
};
|
||||
|
||||
struct WorkResult {
|
||||
// Since this result is per group, this vector will have gaps in the data it covers as the indices must stay relative to all variants.
|
||||
Vector<PackedByteArray> variant_data;
|
||||
};
|
||||
|
||||
struct ShaderGroupItem {
|
||||
String cache_path;
|
||||
LocalVector<int> variants;
|
||||
LocalVector<WorkerThreadPool::TaskID> variant_tasks;
|
||||
};
|
||||
|
||||
String shader_cache_platform_name;
|
||||
String shader_cache_renderer_name;
|
||||
String shader_cache_export_path;
|
||||
RBSet<String> shader_paths_processed;
|
||||
HashMap<String, WorkResult> shader_work_results;
|
||||
Mutex shader_work_results_mutex;
|
||||
LocalVector<ShaderGroupItem> shader_group_items;
|
||||
RenderingShaderContainerFormat *shader_container_format = nullptr;
|
||||
String shader_container_driver;
|
||||
Vector<Ref<ShaderBakerExportPluginPlatform>> platforms;
|
||||
uint64_t customization_configuration_hash = 0;
|
||||
uint32_t tasks_processed = 0;
|
||||
uint32_t tasks_total = 0;
|
||||
std::atomic<bool> tasks_cancelled;
|
||||
BinaryMutex tasks_mutex;
|
||||
ConditionVariable tasks_condition;
|
||||
|
||||
virtual String get_name() const override;
|
||||
virtual bool _is_active(const Vector<String> &p_features) const;
|
||||
virtual bool _initialize_container_format(const Ref<EditorExportPlatform> &p_platform, const Vector<String> &p_features, const Ref<EditorExportPreset> &p_preset);
|
||||
virtual void _cleanup_container_format();
|
||||
virtual bool _initialize_cache_directory();
|
||||
virtual bool _begin_customize_resources(const Ref<EditorExportPlatform> &p_platform, const Vector<String> &p_features) override;
|
||||
virtual bool _begin_customize_scenes(const Ref<EditorExportPlatform> &p_platform, const Vector<String> &p_features) override;
|
||||
virtual void _end_customize_resources() override;
|
||||
virtual Ref<Resource> _customize_resource(const Ref<Resource> &p_resource, const String &p_path) override;
|
||||
virtual Node *_customize_scene(Node *p_root, const String &p_path) override;
|
||||
virtual uint64_t _get_customization_configuration_hash() const override;
|
||||
virtual void _customize_shader_version(ShaderRD *p_shader, RID p_version);
|
||||
void _process_work_item(WorkItem p_work_item);
|
||||
|
||||
public:
|
||||
ShaderBakerExportPlugin();
|
||||
virtual ~ShaderBakerExportPlugin() override;
|
||||
void add_platform(Ref<ShaderBakerExportPluginPlatform> p_platform);
|
||||
void remove_platform(Ref<ShaderBakerExportPluginPlatform> p_platform);
|
||||
};
|
Reference in New Issue
Block a user