feat: godot-engine-source-4.3-stable
This commit is contained in:
parent
c59a7dcade
commit
7125d019b5
11149 changed files with 5070401 additions and 0 deletions
5
engine/editor/export/SCsub
Normal file
5
engine/editor/export/SCsub
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
Import("env")
|
||||
|
||||
env.add_source_files(env.editor_sources, "*.cpp")
|
||||
1573
engine/editor/export/codesign.cpp
Normal file
1573
engine/editor/export/codesign.cpp
Normal file
File diff suppressed because it is too large
Load diff
367
engine/editor/export/codesign.h
Normal file
367
engine/editor/export/codesign.h
Normal file
|
|
@ -0,0 +1,367 @@
|
|||
/**************************************************************************/
|
||||
/* 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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef CODESIGN_H
|
||||
#define CODESIGN_H
|
||||
|
||||
// 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/crypto/crypto_core.h"
|
||||
#include "core/io/dir_access.h"
|
||||
#include "core/io/file_access.h"
|
||||
#include "core/io/plist.h"
|
||||
#include "core/object/ref_counted.h"
|
||||
|
||||
#include "modules/modules_enabled.gen.h" // For regex.
|
||||
#ifdef MODULE_REGEX_ENABLED
|
||||
#include "modules/regex/regex.h"
|
||||
#endif
|
||||
|
||||
#ifdef MODULE_REGEX_ENABLED
|
||||
|
||||
/*************************************************************************/
|
||||
/* 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 {
|
||||
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 segmet.
|
||||
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);
|
||||
};
|
||||
|
||||
#endif // MODULE_REGEX_ENABLED
|
||||
|
||||
#endif // CODESIGN_H
|
||||
434
engine/editor/export/editor_export.cpp
Normal file
434
engine/editor/export/editor_export.cpp
Normal file
|
|
@ -0,0 +1,434 @@
|
|||
/**************************************************************************/
|
||||
/* 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/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, "encryption_include_filters", preset->get_enc_in_filter());
|
||||
config->set_value(section, "encryption_exclude_filters", preset->get_enc_ex_filter());
|
||||
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;
|
||||
}
|
||||
|
||||
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_valid()) {
|
||||
index++;
|
||||
ERR_CONTINUE(!preset.is_valid());
|
||||
}
|
||||
|
||||
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));
|
||||
|
||||
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";
|
||||
|
||||
List<String> options;
|
||||
config->get_section_keys(option_section, &options);
|
||||
|
||||
for (const String &E : options) {
|
||||
Variant value = config->get_value(option_section, E);
|
||||
preset->set(E, value);
|
||||
}
|
||||
|
||||
if (credentials->has_section(option_section)) {
|
||||
options.clear();
|
||||
credentials->get_section_keys(option_section, &options);
|
||||
|
||||
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;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
EditorExport::~EditorExport() {
|
||||
}
|
||||
88
engine/editor/export/editor_export.h
Normal file
88
engine/editor/export/editor_export.h
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
/**************************************************************************/
|
||||
/* 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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef EDITOR_EXPORT_H
|
||||
#define EDITOR_EXPORT_H
|
||||
|
||||
#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;
|
||||
|
||||
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 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();
|
||||
~EditorExport();
|
||||
};
|
||||
|
||||
#endif // EDITOR_EXPORT_H
|
||||
2045
engine/editor/export/editor_export_platform.cpp
Normal file
2045
engine/editor/export/editor_export_platform.cpp
Normal file
File diff suppressed because it is too large
Load diff
259
engine/editor/export/editor_export_platform.h
Normal file
259
engine/editor/export/editor_export_platform.h
Normal file
|
|
@ -0,0 +1,259 @@
|
|||
/**************************************************************************/
|
||||
/* 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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef EDITOR_EXPORT_PLATFORM_H
|
||||
#define EDITOR_EXPORT_PLATFORM_H
|
||||
|
||||
class EditorFileSystemDirectory;
|
||||
struct EditorProgress;
|
||||
|
||||
#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);
|
||||
typedef Error (*EditorExportSaveSharedObject)(void *p_userdata, const SharedObject &p_so);
|
||||
|
||||
enum ExportMessageType {
|
||||
EXPORT_MESSAGE_NONE,
|
||||
EXPORT_MESSAGE_INFO,
|
||||
EXPORT_MESSAGE_WARNING,
|
||||
EXPORT_MESSAGE_ERROR,
|
||||
};
|
||||
|
||||
struct ExportMessage {
|
||||
ExportMessageType msg_type;
|
||||
String category;
|
||||
String text;
|
||||
};
|
||||
|
||||
private:
|
||||
struct SavedData {
|
||||
uint64_t ofs = 0;
|
||||
uint64_t size = 0;
|
||||
bool encrypted = false;
|
||||
Vector<uint8_t> md5;
|
||||
CharString path_utf8;
|
||||
|
||||
bool operator<(const SavedData &p_data) const {
|
||||
return path_utf8 < p_data.path_utf8;
|
||||
}
|
||||
};
|
||||
|
||||
struct PackData {
|
||||
Ref<FileAccess> f;
|
||||
Vector<SavedData> file_ofs;
|
||||
EditorProgress *ep = nullptr;
|
||||
Vector<SharedObject> *so_files = nullptr;
|
||||
};
|
||||
|
||||
struct ZipData {
|
||||
void *zip = nullptr;
|
||||
EditorProgress *ep = nullptr;
|
||||
};
|
||||
|
||||
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 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);
|
||||
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);
|
||||
|
||||
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 Error _add_shared_object(void *p_userdata, const SharedObject &p_so);
|
||||
|
||||
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);
|
||||
String _get_script_encryption_key(const Ref<EditorExportPreset> &p_preset) const;
|
||||
|
||||
protected:
|
||||
struct ExportNotifier {
|
||||
ExportNotifier(EditorExportPlatform &p_platform, const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags);
|
||||
~ExportNotifier();
|
||||
};
|
||||
|
||||
HashSet<String> get_features(const Ref<EditorExportPreset> &p_preset, bool p_debug) const;
|
||||
|
||||
bool exists_export_template(const String &template_file_name, String *err) const;
|
||||
String find_export_template(const String &template_file_name, String *err = nullptr) const;
|
||||
void gen_export_flags(Vector<String> &r_flags, int p_flags);
|
||||
void gen_debug_flags(Vector<String> &r_flags, int 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, 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;
|
||||
|
||||
public:
|
||||
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_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;
|
||||
}
|
||||
|
||||
static Vector<String> get_forced_export_files();
|
||||
|
||||
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;
|
||||
|
||||
Error export_project_files(const Ref<EditorExportPreset> &p_preset, bool p_debug, EditorExportSaveFunction p_func, void *p_udata, EditorExportSaveSharedObject p_so_func = nullptr);
|
||||
|
||||
Error save_pack(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(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path);
|
||||
|
||||
virtual bool poll_export() { return false; }
|
||||
virtual int get_options_count() const { return 0; }
|
||||
virtual String get_options_tooltip() const { return ""; }
|
||||
virtual Ref<ImageTexture> 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 ""; }
|
||||
|
||||
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,
|
||||
};
|
||||
|
||||
virtual void cleanup() {}
|
||||
virtual Error run(const Ref<EditorExportPreset> &p_preset, int p_device, int p_debug_flags) { return OK; }
|
||||
virtual Ref<Texture2D> get_run_icon() const { return get_logo(); }
|
||||
|
||||
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, int p_flags = 0) = 0;
|
||||
virtual Error export_pack(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags = 0);
|
||||
virtual Error export_zip(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int 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) = 0;
|
||||
virtual String get_debug_protocol() const { return "tcp://"; }
|
||||
|
||||
EditorExportPlatform();
|
||||
};
|
||||
|
||||
#endif // EDITOR_EXPORT_PLATFORM_H
|
||||
268
engine/editor/export/editor_export_platform_pc.cpp
Normal file
268
engine/editor/export/editor_export_platform_pc.cpp
Normal file
|
|
@ -0,0 +1,268 @@
|
|||
/**************************************************************************/
|
||||
/* 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");
|
||||
}
|
||||
// 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));
|
||||
}
|
||||
|
||||
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, int 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, int 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, int 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, 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);
|
||||
}
|
||||
} else {
|
||||
err = da->copy(src_path, target_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;
|
||||
}
|
||||
82
engine/editor/export/editor_export_platform_pc.h
Normal file
82
engine/editor/export/editor_export_platform_pc.h
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
/**************************************************************************/
|
||||
/* 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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef EDITOR_EXPORT_PLATFORM_PC_H
|
||||
#define EDITOR_EXPORT_PLATFORM_PC_H
|
||||
|
||||
#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_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, int 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, int p_flags);
|
||||
virtual Error modify_template(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags) { return OK; };
|
||||
virtual Error export_project_data(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int 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;
|
||||
}
|
||||
};
|
||||
|
||||
#endif // EDITOR_EXPORT_PLATFORM_PC_H
|
||||
360
engine/editor/export/editor_export_plugin.cpp
Normal file
360
engine/editor/export/editor_export_plugin.cpp
Normal file
|
|
@ -0,0 +1,360 @@
|
|||
/**************************************************************************/
|
||||
/* 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 "core/io/dir_access.h"
|
||||
#include "core/io/file_access.h"
|
||||
#include "editor/editor_paths.h"
|
||||
#include "editor/editor_settings.h"
|
||||
#include "editor/export/editor_export_platform.h"
|
||||
#include "scene/resources/resource_format_text.h"
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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_ios_framework(const String &p_path) {
|
||||
ios_frameworks.push_back(p_path);
|
||||
}
|
||||
|
||||
void EditorExportPlugin::add_ios_embedded_framework(const String &p_path) {
|
||||
ios_embedded_frameworks.push_back(p_path);
|
||||
}
|
||||
|
||||
Vector<String> EditorExportPlugin::get_ios_frameworks() const {
|
||||
return ios_frameworks;
|
||||
}
|
||||
|
||||
Vector<String> EditorExportPlugin::get_ios_embedded_frameworks() const {
|
||||
return ios_embedded_frameworks;
|
||||
}
|
||||
|
||||
void EditorExportPlugin::add_ios_plist_content(const String &p_plist_content) {
|
||||
ios_plist_content += p_plist_content + "\n";
|
||||
}
|
||||
|
||||
String EditorExportPlugin::get_ios_plist_content() const {
|
||||
return ios_plist_content;
|
||||
}
|
||||
|
||||
void EditorExportPlugin::add_ios_linker_flags(const String &p_flags) {
|
||||
if (ios_linker_flags.length() > 0) {
|
||||
ios_linker_flags += ' ';
|
||||
}
|
||||
ios_linker_flags += p_flags;
|
||||
}
|
||||
|
||||
String EditorExportPlugin::get_ios_linker_flags() const {
|
||||
return ios_linker_flags;
|
||||
}
|
||||
|
||||
void EditorExportPlugin::add_ios_bundle_file(const String &p_path) {
|
||||
ios_bundle_files.push_back(p_path);
|
||||
}
|
||||
|
||||
Vector<String> EditorExportPlugin::get_ios_bundle_files() const {
|
||||
return ios_bundle_files;
|
||||
}
|
||||
|
||||
void EditorExportPlugin::add_ios_cpp_code(const String &p_code) {
|
||||
ios_cpp_code += p_code;
|
||||
}
|
||||
|
||||
String EditorExportPlugin::get_ios_cpp_code() const {
|
||||
return ios_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_ios_project_static_lib(const String &p_path) {
|
||||
ios_project_static_libs.push_back(p_path);
|
||||
}
|
||||
|
||||
Vector<String> EditorExportPlugin::get_ios_project_static_libs() const {
|
||||
return ios_project_static_libs;
|
||||
}
|
||||
|
||||
Variant EditorExportPlugin::get_option(const StringName &p_name) const {
|
||||
ERR_FAIL_NULL_V(export_preset, 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_REQUIRED_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_REQUIRED_CALL(_customize_scene, p_root, p_path, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
uint64_t EditorExportPlugin::_get_customization_configuration_hash() const {
|
||||
uint64_t ret = 0;
|
||||
GDVIRTUAL_REQUIRED_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_REQUIRED_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_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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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_ios_project_static_lib", "path"), &EditorExportPlugin::add_ios_project_static_lib);
|
||||
ClassDB::bind_method(D_METHOD("add_file", "path", "file", "remap"), &EditorExportPlugin::add_file);
|
||||
ClassDB::bind_method(D_METHOD("add_ios_framework", "path"), &EditorExportPlugin::add_ios_framework);
|
||||
ClassDB::bind_method(D_METHOD("add_ios_embedded_framework", "path"), &EditorExportPlugin::add_ios_embedded_framework);
|
||||
ClassDB::bind_method(D_METHOD("add_ios_plist_content", "plist_content"), &EditorExportPlugin::add_ios_plist_content);
|
||||
ClassDB::bind_method(D_METHOD("add_ios_linker_flags", "flags"), &EditorExportPlugin::add_ios_linker_flags);
|
||||
ClassDB::bind_method(D_METHOD("add_ios_bundle_file", "path"), &EditorExportPlugin::add_ios_bundle_file);
|
||||
ClassDB::bind_method(D_METHOD("add_ios_cpp_code", "code"), &EditorExportPlugin::add_ios_cpp_code);
|
||||
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);
|
||||
|
||||
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_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");
|
||||
}
|
||||
|
||||
EditorExportPlugin::EditorExportPlugin() {
|
||||
EDITOR_DEF("export/ssh/ssh", "");
|
||||
EDITOR_DEF("export/ssh/scp", "");
|
||||
}
|
||||
189
engine/editor/export/editor_export_plugin.h
Normal file
189
engine/editor/export/editor_export_plugin.h
Normal file
|
|
@ -0,0 +1,189 @@
|
|||
/**************************************************************************/
|
||||
/* 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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef EDITOR_EXPORT_PLUGIN_H
|
||||
#define EDITOR_EXPORT_PLUGIN_H
|
||||
|
||||
#include "core/extension/gdextension.h"
|
||||
#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;
|
||||
|
||||
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> ios_frameworks;
|
||||
Vector<String> ios_embedded_frameworks;
|
||||
Vector<String> ios_project_static_libs;
|
||||
String ios_plist_content;
|
||||
String ios_linker_flags;
|
||||
Vector<String> ios_bundle_files;
|
||||
String ios_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() {
|
||||
ios_frameworks.clear();
|
||||
ios_embedded_frameworks.clear();
|
||||
ios_bundle_files.clear();
|
||||
ios_plist_content = "";
|
||||
ios_linker_flags = "";
|
||||
ios_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_preset(const Ref<EditorExportPreset> &p_preset);
|
||||
Ref<EditorExportPreset> get_export_preset() 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_ios_framework(const String &p_path);
|
||||
void add_ios_embedded_framework(const String &p_path);
|
||||
void add_ios_project_static_lib(const String &p_path);
|
||||
void add_ios_plist_content(const String &p_plist_content);
|
||||
void add_ios_linker_flags(const String &p_flags);
|
||||
void add_ios_bundle_file(const String &p_path);
|
||||
void add_ios_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(Ref<Resource>, _customize_resource, const Ref<Resource> &, String)
|
||||
|
||||
GDVIRTUAL2RC(bool, _begin_customize_scenes, const Ref<EditorExportPlatform> &, const Vector<String> &)
|
||||
GDVIRTUAL2R(Node *, _customize_scene, Node *, String)
|
||||
GDVIRTUAL0RC(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(String, _get_export_option_warning, const Ref<EditorExportPlatform> &, String);
|
||||
|
||||
GDVIRTUAL0RC(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);
|
||||
|
||||
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 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;
|
||||
|
||||
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;
|
||||
|
||||
Vector<String> get_ios_frameworks() const;
|
||||
Vector<String> get_ios_embedded_frameworks() const;
|
||||
Vector<String> get_ios_project_static_libs() const;
|
||||
String get_ios_plist_content() const;
|
||||
String get_ios_linker_flags() const;
|
||||
Vector<String> get_ios_bundle_files() const;
|
||||
String get_ios_cpp_code() const;
|
||||
const Vector<String> &get_macos_plugin_files() const;
|
||||
Variant get_option(const StringName &p_name) const;
|
||||
|
||||
EditorExportPlugin();
|
||||
};
|
||||
|
||||
#endif // EDITOR_EXPORT_PLUGIN_H
|
||||
456
engine/editor/export/editor_export_preset.cpp
Normal file
456
engine/editor/export/editor_export_preset.cpp
Normal file
|
|
@ -0,0 +1,456 @@
|
|||
/**************************************************************************/
|
||||
/* 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;
|
||||
}
|
||||
|
||||
void EditorExportPreset::_bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("_get_property_warning", "name"), &EditorExportPreset::_get_property_warning);
|
||||
}
|
||||
|
||||
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) && platform->get_export_option_visibility(this, E.key)) {
|
||||
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()) {
|
||||
Array keys = plugin_overrides.keys();
|
||||
for (int x = 0; x < keys.size(); x++) {
|
||||
StringName key = keys[x];
|
||||
Variant value = plugin_overrides[key];
|
||||
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::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_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;
|
||||
}
|
||||
|
||||
EditorExportPreset::EditorExportPreset() {}
|
||||
186
engine/editor/export/editor_export_preset.h
Normal file
186
engine/editor/export/editor_export_preset.h
Normal file
|
|
@ -0,0 +1,186 @@
|
|||
/**************************************************************************/
|
||||
/* 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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef EDITOR_EXPORT_PRESET_H
|
||||
#define EDITOR_EXPORT_PRESET_H
|
||||
|
||||
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;
|
||||
|
||||
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;
|
||||
|
||||
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;
|
||||
|
||||
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 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_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, 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; }
|
||||
|
||||
EditorExportPreset();
|
||||
};
|
||||
|
||||
#endif // EDITOR_EXPORT_PRESET_H
|
||||
1069
engine/editor/export/export_template_manager.cpp
Normal file
1069
engine/editor/export/export_template_manager.cpp
Normal file
File diff suppressed because it is too large
Load diff
140
engine/editor/export/export_template_manager.h
Normal file
140
engine/editor/export/export_template_manager.h
Normal file
|
|
@ -0,0 +1,140 @@
|
|||
/**************************************************************************/
|
||||
/* 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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef EXPORT_TEMPLATE_MANAGER_H
|
||||
#define EXPORT_TEMPLATE_MANAGER_H
|
||||
|
||||
#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 downloads_available = true;
|
||||
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_open_button = 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 *download_progress_hb = nullptr;
|
||||
ProgressBar *download_progress_bar = nullptr;
|
||||
Label *download_progress_label = nullptr;
|
||||
HTTPRequest *download_templates = nullptr;
|
||||
Button *install_file_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);
|
||||
|
||||
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);
|
||||
static void _bind_methods();
|
||||
|
||||
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();
|
||||
};
|
||||
|
||||
#endif // EXPORT_TEMPLATE_MANAGER_H
|
||||
338
engine/editor/export/lipo.cpp
Normal file
338
engine/editor/export/lipo.cpp
Normal file
|
|
@ -0,0 +1,338 @@
|
|||
/**************************************************************************/
|
||||
/* 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"
|
||||
|
||||
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();
|
||||
}
|
||||
74
engine/editor/export/lipo.h
Normal file
74
engine/editor/export/lipo.h
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
/**************************************************************************/
|
||||
/* 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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef LIPO_H
|
||||
#define LIPO_H
|
||||
|
||||
// Universal / Universal 2 fat binary file creator and extractor.
|
||||
|
||||
#include "macho.h"
|
||||
|
||||
#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();
|
||||
};
|
||||
|
||||
#endif // LIPO_H
|
||||
562
engine/editor/export/macho.cpp
Normal file
562
engine/editor/export/macho.cpp
Normal file
|
|
@ -0,0 +1,562 @@
|
|||
/**************************************************************************/
|
||||
/* 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"
|
||||
|
||||
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;
|
||||
}
|
||||
228
engine/editor/export/macho.h
Normal file
228
engine/editor/export/macho.h
Normal file
|
|
@ -0,0 +1,228 @@
|
|||
/**************************************************************************/
|
||||
/* 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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef MACHO_H
|
||||
#define MACHO_H
|
||||
|
||||
// Mach-O binary object file format parser and editor.
|
||||
|
||||
#include "core/crypto/crypto.h"
|
||||
#include "core/crypto/crypto_core.h"
|
||||
#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,
|
||||
};
|
||||
|
||||
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);
|
||||
};
|
||||
|
||||
#endif // MACHO_H
|
||||
1598
engine/editor/export/project_export.cpp
Normal file
1598
engine/editor/export/project_export.cpp
Normal file
File diff suppressed because it is too large
Load diff
212
engine/editor/export/project_export.h
Normal file
212
engine/editor/export/project_export.h
Normal file
|
|
@ -0,0 +1,212 @@
|
|||
/**************************************************************************/
|
||||
/* 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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef PROJECT_EXPORT_H
|
||||
#define PROJECT_EXPORT_H
|
||||
|
||||
#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 RichTextLabel;
|
||||
class TabContainer;
|
||||
class Tree;
|
||||
class TreeItem;
|
||||
|
||||
class ProjectExportTextureFormatError : public HBoxContainer {
|
||||
GDCLASS(ProjectExportTextureFormatError, HBoxContainer);
|
||||
|
||||
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();
|
||||
};
|
||||
|
||||
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;
|
||||
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);
|
||||
|
||||
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;
|
||||
|
||||
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;
|
||||
void _enc_pck_changed(bool p_pressed);
|
||||
void _enc_directory_changed(bool p_pressed);
|
||||
void _enc_filters_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();
|
||||
~ProjectExportDialog();
|
||||
};
|
||||
|
||||
#endif // PROJECT_EXPORT_H
|
||||
Loading…
Add table
Add a link
Reference in a new issue