mirror of
https://github.com/Relintai/pandemonium_engine_minimal.git
synced 2024-11-03 18:05:39 +01:00
Removed the platform exporters.
This commit is contained in:
parent
94c7f0a442
commit
6016061c54
@ -38,8 +38,6 @@ for x in sorted(glob.glob("platform/*")):
|
||||
sys.path.insert(0, tmppath)
|
||||
import detect
|
||||
|
||||
if os.path.exists(x + "/export/export.cpp"):
|
||||
platform_exporters.append(x[9:])
|
||||
if os.path.exists(x + "/api/api.cpp"):
|
||||
platform_apis.append(x[9:])
|
||||
if detect.is_active():
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,368 +0,0 @@
|
||||
/*************************************************************************/
|
||||
/* codesign.h */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
|
||||
/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
|
||||
/* */
|
||||
/* 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. */
|
||||
/*************************************************************************/
|
||||
|
||||
// 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.
|
||||
|
||||
#ifndef CODESIGN_H
|
||||
#define CODESIGN_H
|
||||
|
||||
#include "core/crypto/crypto.h"
|
||||
#include "core/crypto/crypto_core.h"
|
||||
#include "core/os/dir_access.h"
|
||||
#include "core/os/file_access.h"
|
||||
#include "core/object/reference.h"
|
||||
#include "modules/modules_enabled.gen.h" // For regex.
|
||||
#ifdef MODULE_REGEX_ENABLED
|
||||
#include "modules/regex/regex.h"
|
||||
#endif
|
||||
|
||||
#include "plist.h"
|
||||
|
||||
#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 Reference {
|
||||
public:
|
||||
virtual PoolByteArray get_hash_sha1() const = 0;
|
||||
virtual PoolByteArray get_hash_sha256() const = 0;
|
||||
|
||||
virtual int get_size() const = 0;
|
||||
virtual uint32_t get_index_type() const = 0;
|
||||
|
||||
virtual void write_to_file(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 {
|
||||
PoolByteArray 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 PoolByteArray &p_data);
|
||||
|
||||
Vector<String> parse_requirements() const;
|
||||
|
||||
virtual PoolByteArray get_hash_sha1() const;
|
||||
virtual PoolByteArray get_hash_sha256() const;
|
||||
|
||||
virtual int get_size() const;
|
||||
|
||||
virtual uint32_t get_index_type() const { return 0x00000002; };
|
||||
virtual void write_to_file(FileAccess *p_file) const;
|
||||
};
|
||||
|
||||
/*************************************************************************/
|
||||
/* CodeSignEntitlementsText */
|
||||
/*************************************************************************/
|
||||
|
||||
// PList formatted entitlements.
|
||||
|
||||
class CodeSignEntitlementsText : public CodeSignBlob {
|
||||
PoolByteArray blob;
|
||||
|
||||
public:
|
||||
CodeSignEntitlementsText();
|
||||
CodeSignEntitlementsText(const String &p_string);
|
||||
|
||||
virtual PoolByteArray get_hash_sha1() const;
|
||||
virtual PoolByteArray get_hash_sha256() const;
|
||||
|
||||
virtual int get_size() const;
|
||||
|
||||
virtual uint32_t get_index_type() const { return 0x00000005; };
|
||||
virtual void write_to_file(FileAccess *p_file) const;
|
||||
};
|
||||
|
||||
/*************************************************************************/
|
||||
/* CodeSignEntitlementsBinary */
|
||||
/*************************************************************************/
|
||||
|
||||
// ASN.1 serialized entitlements.
|
||||
|
||||
class CodeSignEntitlementsBinary : public CodeSignBlob {
|
||||
PoolByteArray blob;
|
||||
|
||||
public:
|
||||
CodeSignEntitlementsBinary();
|
||||
CodeSignEntitlementsBinary(const String &p_string);
|
||||
|
||||
virtual PoolByteArray get_hash_sha1() const;
|
||||
virtual PoolByteArray get_hash_sha256() const;
|
||||
|
||||
virtual int get_size() const;
|
||||
|
||||
virtual uint32_t get_index_type() const { return 0x00000007; };
|
||||
virtual void write_to_file(FileAccess *p_file) const;
|
||||
};
|
||||
|
||||
/*************************************************************************/
|
||||
/* 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:
|
||||
PoolByteArray 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 PoolByteArray &p_hash, int p_slot);
|
||||
|
||||
virtual PoolByteArray get_hash_sha1() const;
|
||||
virtual PoolByteArray get_hash_sha256() const;
|
||||
|
||||
virtual int get_size() const;
|
||||
virtual uint32_t get_index_type() const { return 0x00000000; };
|
||||
|
||||
virtual void write_to_file(FileAccess *p_file) const;
|
||||
};
|
||||
|
||||
/*************************************************************************/
|
||||
/* CodeSignSignature */
|
||||
/*************************************************************************/
|
||||
|
||||
class CodeSignSignature : public CodeSignBlob {
|
||||
PoolByteArray blob;
|
||||
|
||||
public:
|
||||
CodeSignSignature();
|
||||
|
||||
virtual PoolByteArray get_hash_sha1() const;
|
||||
virtual PoolByteArray get_hash_sha256() const;
|
||||
|
||||
virtual int get_size() const;
|
||||
virtual uint32_t get_index_type() const { return 0x00010000; };
|
||||
|
||||
virtual void write_to_file(FileAccess *p_file) const;
|
||||
};
|
||||
|
||||
/*************************************************************************/
|
||||
/* CodeSignSuperBlob */
|
||||
/*************************************************************************/
|
||||
|
||||
class CodeSignSuperBlob {
|
||||
Vector<Ref<CodeSignBlob>> blobs;
|
||||
|
||||
public:
|
||||
bool add_blob(const Ref<CodeSignBlob> &p_blob);
|
||||
|
||||
int get_size() const;
|
||||
void write_to_file(FileAccess *p_file) const;
|
||||
};
|
||||
|
||||
/*************************************************************************/
|
||||
/* CodeSign */
|
||||
/*************************************************************************/
|
||||
|
||||
class CodeSign {
|
||||
static PoolByteArray file_hash_sha1(const String &p_path);
|
||||
static PoolByteArray 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
|
File diff suppressed because it is too large
Load Diff
@ -1,36 +0,0 @@
|
||||
/*************************************************************************/
|
||||
/* export.h */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
|
||||
/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
|
||||
/* */
|
||||
/* 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 OSX_EXPORT_H
|
||||
#define OSX_EXPORT_H
|
||||
|
||||
void register_osx_exporter();
|
||||
|
||||
#endif // OSX_EXPORT_H
|
@ -1,243 +0,0 @@
|
||||
/*************************************************************************/
|
||||
/* lipo.cpp */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
|
||||
/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/*************************************************************************/
|
||||
|
||||
#include "lipo.h"
|
||||
|
||||
#include "modules/modules_enabled.gen.h" // For regex.
|
||||
|
||||
#ifdef MODULE_REGEX_ENABLED
|
||||
|
||||
bool LipO::is_lipo(const String &p_path) {
|
||||
FileAccessRef fb = FileAccess::open(p_path, FileAccess::READ);
|
||||
ERR_FAIL_COND_V_MSG(!fb, 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 PoolStringArray &p_files) {
|
||||
close();
|
||||
|
||||
fa = FileAccess::open(p_output_path, FileAccess::WRITE);
|
||||
ERR_FAIL_COND_V_MSG(!fa, 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: Can't open 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);
|
||||
|
||||
FileAccessRef fb = FileAccess::open(p_files[i], FileAccess::READ);
|
||||
if (!fb) {
|
||||
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++) {
|
||||
FileAccessRef fb = FileAccess::open(p_files[i], FileAccess::READ);
|
||||
if (!fb) {
|
||||
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);
|
||||
}
|
||||
fb->close();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LipO::open_file(const String &p_path) {
|
||||
close();
|
||||
|
||||
fa = FileAccess::open(p_path, FileAccess::READ);
|
||||
ERR_FAIL_COND_V_MSG(!fa, 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, 0, "LipO: File not opened.");
|
||||
return archs.size();
|
||||
}
|
||||
|
||||
bool LipO::extract_arch(int p_index, const String &p_path) {
|
||||
ERR_FAIL_COND_V_MSG(!fa, false, "LipO: File not opened.");
|
||||
ERR_FAIL_INDEX_V(p_index, archs.size(), false);
|
||||
|
||||
FileAccessRef fb = FileAccess::open(p_path, FileAccess::WRITE);
|
||||
ERR_FAIL_COND_V_MSG(!fb, 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);
|
||||
}
|
||||
fb->close();
|
||||
return true;
|
||||
}
|
||||
|
||||
void LipO::close() {
|
||||
if (fa) {
|
||||
fa->close();
|
||||
memdelete(fa);
|
||||
fa = nullptr;
|
||||
}
|
||||
archs.clear();
|
||||
}
|
||||
|
||||
LipO::~LipO() {
|
||||
close();
|
||||
}
|
||||
|
||||
#endif // MODULE_REGEX_ENABLED
|
@ -1,76 +0,0 @@
|
||||
/*************************************************************************/
|
||||
/* lipo.h */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
|
||||
/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
|
||||
/* */
|
||||
/* 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. */
|
||||
/*************************************************************************/
|
||||
|
||||
// Universal / Universal 2 fat binary file creator and extractor.
|
||||
|
||||
#ifndef LIPO_H
|
||||
#define LIPO_H
|
||||
|
||||
#include "core/os/file_access.h"
|
||||
#include "core/object/reference.h"
|
||||
#include "modules/modules_enabled.gen.h" // For regex.
|
||||
|
||||
#include "macho.h"
|
||||
|
||||
#ifdef MODULE_REGEX_ENABLED
|
||||
|
||||
class LipO : public Reference {
|
||||
struct FatArch {
|
||||
uint32_t cputype;
|
||||
uint32_t cpusubtype;
|
||||
uint64_t offset;
|
||||
uint64_t size;
|
||||
uint32_t align;
|
||||
};
|
||||
|
||||
FileAccess *fa = nullptr;
|
||||
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 PoolStringArray &p_files);
|
||||
|
||||
bool open_file(const String &p_path);
|
||||
int get_arch_count() const;
|
||||
bool extract_arch(int p_index, const String &p_path);
|
||||
|
||||
void close();
|
||||
|
||||
~LipO();
|
||||
};
|
||||
|
||||
#endif // MODULE_REGEX_ENABLED
|
||||
|
||||
#endif // LIPO_H
|
@ -1,556 +0,0 @@
|
||||
/*************************************************************************/
|
||||
/* macho.cpp */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
|
||||
/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/*************************************************************************/
|
||||
|
||||
#include "macho.h"
|
||||
|
||||
#include "modules/modules_enabled.gen.h" // For regex.
|
||||
|
||||
#ifdef MODULE_REGEX_ENABLED
|
||||
|
||||
uint32_t MachO::seg_align(uint64_t p_vmaddr, uint32_t p_min, uint32_t p_max) {
|
||||
uint32_t align = p_max;
|
||||
if (p_vmaddr != 0) {
|
||||
uint64_t seg_align = 1;
|
||||
align = 0;
|
||||
while ((seg_align & p_vmaddr) == 0) {
|
||||
seg_align = seg_align << 1;
|
||||
align++;
|
||||
}
|
||||
align = CLAMP(align, p_min, p_max);
|
||||
}
|
||||
return align;
|
||||
}
|
||||
|
||||
bool MachO::alloc_signature(uint64_t p_size) {
|
||||
ERR_FAIL_COND_V_MSG(!fa, 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_len() + PAD(fa->get_len(), 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) {
|
||||
FileAccessRef fb = FileAccess::open(p_path, FileAccess::READ);
|
||||
ERR_FAIL_COND_V_MSG(!fb, 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);
|
||||
}
|
||||
|
||||
bool MachO::open_file(const String &p_path) {
|
||||
fa = FileAccess::open(p_path, FileAccess::READ_WRITE);
|
||||
ERR_FAIL_COND_V_MSG(!fa, 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, 0, "MachO: File not opened.");
|
||||
return exe_base;
|
||||
}
|
||||
|
||||
uint64_t MachO::get_exe_limit() {
|
||||
ERR_FAIL_COND_V_MSG(!fa, 0, "MachO: File not opened.");
|
||||
return exe_limit;
|
||||
}
|
||||
|
||||
int32_t MachO::get_align() {
|
||||
ERR_FAIL_COND_V_MSG(!fa, 0, "MachO: File not opened.");
|
||||
return align;
|
||||
}
|
||||
|
||||
uint32_t MachO::get_cputype() {
|
||||
ERR_FAIL_COND_V_MSG(!fa, 0, "MachO: File not opened.");
|
||||
return cputype;
|
||||
}
|
||||
|
||||
uint32_t MachO::get_cpusubtype() {
|
||||
ERR_FAIL_COND_V_MSG(!fa, 0, "MachO: File not opened.");
|
||||
return cpusubtype;
|
||||
}
|
||||
|
||||
uint64_t MachO::get_size() {
|
||||
ERR_FAIL_COND_V_MSG(!fa, 0, "MachO: File not opened.");
|
||||
return fa->get_len();
|
||||
}
|
||||
|
||||
uint64_t MachO::get_signature_offset() {
|
||||
ERR_FAIL_COND_V_MSG(!fa, 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, 0, "MachO: File not opened.");
|
||||
|
||||
if (signature_offset == 0) {
|
||||
return fa->get_len() + PAD(fa->get_len(), 16);
|
||||
} else {
|
||||
return get_signature_offset();
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t MachO::get_signature_size() {
|
||||
ERR_FAIL_COND_V_MSG(!fa, 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, 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.
|
||||
}
|
||||
|
||||
PoolByteArray MachO::get_cdhash_sha1() {
|
||||
ERR_FAIL_COND_V_MSG(!fa, PoolByteArray(), "MachO: File not opened.");
|
||||
if (signature_offset == 0) {
|
||||
return PoolByteArray();
|
||||
}
|
||||
|
||||
fa->seek(get_signature_offset());
|
||||
uint32_t magic = BSWAP32(fa->get_32());
|
||||
if (magic != 0xfade0cc0) {
|
||||
return PoolByteArray(); // 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 */
|
||||
PoolByteArray hash;
|
||||
hash.resize(0x14);
|
||||
|
||||
fa->seek(get_signature_offset() + offset);
|
||||
PoolByteArray blob;
|
||||
blob.resize(cdsize);
|
||||
fa->get_buffer(blob.write().ptr(), cdsize);
|
||||
|
||||
CryptoCore::SHA1Context ctx;
|
||||
ctx.start();
|
||||
ctx.update(blob.read().ptr(), blob.size());
|
||||
ctx.finish(hash.write().ptr());
|
||||
|
||||
return hash;
|
||||
}
|
||||
}
|
||||
fa->seek(pos);
|
||||
}
|
||||
return PoolByteArray();
|
||||
}
|
||||
|
||||
PoolByteArray MachO::get_cdhash_sha256() {
|
||||
ERR_FAIL_COND_V_MSG(!fa, PoolByteArray(), "MachO: File not opened.");
|
||||
if (signature_offset == 0) {
|
||||
return PoolByteArray();
|
||||
}
|
||||
|
||||
fa->seek(get_signature_offset());
|
||||
uint32_t magic = BSWAP32(fa->get_32());
|
||||
if (magic != 0xfade0cc0) {
|
||||
return PoolByteArray(); // 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 */
|
||||
PoolByteArray hash;
|
||||
hash.resize(0x20);
|
||||
|
||||
fa->seek(get_signature_offset() + offset);
|
||||
PoolByteArray blob;
|
||||
blob.resize(cdsize);
|
||||
fa->get_buffer(blob.write().ptr(), cdsize);
|
||||
|
||||
CryptoCore::SHA256Context ctx;
|
||||
ctx.start();
|
||||
ctx.update(blob.read().ptr(), blob.size());
|
||||
ctx.finish(hash.write().ptr());
|
||||
|
||||
return hash;
|
||||
}
|
||||
}
|
||||
fa->seek(pos);
|
||||
}
|
||||
return PoolByteArray();
|
||||
}
|
||||
|
||||
PoolByteArray MachO::get_requirements() {
|
||||
ERR_FAIL_COND_V_MSG(!fa, PoolByteArray(), "MachO: File not opened.");
|
||||
if (signature_offset == 0) {
|
||||
return PoolByteArray();
|
||||
}
|
||||
|
||||
fa->seek(get_signature_offset());
|
||||
uint32_t magic = BSWAP32(fa->get_32());
|
||||
if (magic != 0xfade0cc0) {
|
||||
return PoolByteArray(); // 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.
|
||||
PoolByteArray blob;
|
||||
fa->seek(get_signature_offset() + offset);
|
||||
blob.resize(rqsize);
|
||||
fa->get_buffer(blob.write().ptr(), rqsize);
|
||||
return blob;
|
||||
}
|
||||
fa->seek(pos);
|
||||
}
|
||||
return PoolByteArray();
|
||||
}
|
||||
|
||||
const FileAccess *MachO::get_file() const {
|
||||
return fa;
|
||||
}
|
||||
|
||||
FileAccess *MachO::get_file() {
|
||||
return fa;
|
||||
}
|
||||
|
||||
bool MachO::set_signature_size(uint64_t p_size) {
|
||||
ERR_FAIL_COND_V_MSG(!fa, 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;
|
||||
}
|
||||
|
||||
MachO::~MachO() {
|
||||
if (fa) {
|
||||
fa->close();
|
||||
memdelete(fa);
|
||||
fa = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
#endif // MODULE_REGEX_ENABLED
|
@ -1,217 +0,0 @@
|
||||
/*************************************************************************/
|
||||
/* macho.h */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
|
||||
/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
|
||||
/* */
|
||||
/* 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. */
|
||||
/*************************************************************************/
|
||||
|
||||
// Mach-O binary object file format parser and editor.
|
||||
|
||||
#ifndef MACHO_H
|
||||
#define MACHO_H
|
||||
|
||||
#include "core/crypto/crypto.h"
|
||||
#include "core/crypto/crypto_core.h"
|
||||
#include "core/os/file_access.h"
|
||||
#include "core/object/reference.h"
|
||||
#include "modules/modules_enabled.gen.h" // For regex.
|
||||
|
||||
#ifdef MODULE_REGEX_ENABLED
|
||||
|
||||
class MachO : public Reference {
|
||||
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,
|
||||
};
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
FileAccess *fa = nullptr;
|
||||
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);
|
||||
|
||||
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();
|
||||
|
||||
PoolByteArray get_cdhash_sha1();
|
||||
PoolByteArray get_cdhash_sha256();
|
||||
|
||||
PoolByteArray get_requirements();
|
||||
|
||||
const FileAccess *get_file() const;
|
||||
FileAccess *get_file();
|
||||
|
||||
uint64_t get_signature_size();
|
||||
bool set_signature_size(uint64_t p_size);
|
||||
|
||||
~MachO();
|
||||
};
|
||||
|
||||
#endif // MODULE_REGEX_ENABLED
|
||||
|
||||
#endif // MACHO_H
|
@ -1,570 +0,0 @@
|
||||
/*************************************************************************/
|
||||
/* plist.cpp */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
|
||||
/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
|
||||
/* */
|
||||
/* 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 "plist.h"
|
||||
|
||||
#include "modules/modules_enabled.gen.h" // For regex.
|
||||
|
||||
#ifdef MODULE_REGEX_ENABLED
|
||||
|
||||
Ref<PListNode> PListNode::new_array() {
|
||||
Ref<PListNode> node = memnew(PListNode());
|
||||
ERR_FAIL_COND_V(node.is_null(), Ref<PListNode>());
|
||||
node->data_type = PList::PLNodeType::PL_NODE_TYPE_ARRAY;
|
||||
return node;
|
||||
}
|
||||
|
||||
Ref<PListNode> PListNode::new_dict() {
|
||||
Ref<PListNode> node = memnew(PListNode());
|
||||
ERR_FAIL_COND_V(node.is_null(), Ref<PListNode>());
|
||||
node->data_type = PList::PLNodeType::PL_NODE_TYPE_DICT;
|
||||
return node;
|
||||
}
|
||||
|
||||
Ref<PListNode> PListNode::new_string(const String &p_string) {
|
||||
Ref<PListNode> node = memnew(PListNode());
|
||||
ERR_FAIL_COND_V(node.is_null(), Ref<PListNode>());
|
||||
node->data_type = PList::PLNodeType::PL_NODE_TYPE_STRING;
|
||||
node->data_string = p_string.utf8();
|
||||
return node;
|
||||
}
|
||||
|
||||
Ref<PListNode> PListNode::new_data(const String &p_string) {
|
||||
Ref<PListNode> node = memnew(PListNode());
|
||||
ERR_FAIL_COND_V(node.is_null(), Ref<PListNode>());
|
||||
node->data_type = PList::PLNodeType::PL_NODE_TYPE_DATA;
|
||||
node->data_string = p_string.utf8();
|
||||
return node;
|
||||
}
|
||||
|
||||
Ref<PListNode> PListNode::new_date(const String &p_string) {
|
||||
Ref<PListNode> node = memnew(PListNode());
|
||||
ERR_FAIL_COND_V(node.is_null(), Ref<PListNode>());
|
||||
node->data_type = PList::PLNodeType::PL_NODE_TYPE_DATE;
|
||||
node->data_string = p_string.utf8();
|
||||
return node;
|
||||
}
|
||||
|
||||
Ref<PListNode> PListNode::new_bool(bool p_bool) {
|
||||
Ref<PListNode> node = memnew(PListNode());
|
||||
ERR_FAIL_COND_V(node.is_null(), Ref<PListNode>());
|
||||
node->data_type = PList::PLNodeType::PL_NODE_TYPE_BOOLEAN;
|
||||
node->data_bool = p_bool;
|
||||
return node;
|
||||
}
|
||||
|
||||
Ref<PListNode> PListNode::new_int(int32_t p_int) {
|
||||
Ref<PListNode> node = memnew(PListNode());
|
||||
ERR_FAIL_COND_V(node.is_null(), Ref<PListNode>());
|
||||
node->data_type = PList::PLNodeType::PL_NODE_TYPE_INTEGER;
|
||||
node->data_int = p_int;
|
||||
return node;
|
||||
}
|
||||
|
||||
Ref<PListNode> PListNode::new_real(float p_real) {
|
||||
Ref<PListNode> node = memnew(PListNode());
|
||||
ERR_FAIL_COND_V(node.is_null(), Ref<PListNode>());
|
||||
node->data_type = PList::PLNodeType::PL_NODE_TYPE_REAL;
|
||||
node->data_real = p_real;
|
||||
return node;
|
||||
}
|
||||
|
||||
bool PListNode::push_subnode(const Ref<PListNode> &p_node, const String &p_key) {
|
||||
ERR_FAIL_COND_V(p_node.is_null(), false);
|
||||
if (data_type == PList::PLNodeType::PL_NODE_TYPE_DICT) {
|
||||
ERR_FAIL_COND_V(p_key.empty(), false);
|
||||
ERR_FAIL_COND_V(data_dict.has(p_key), false);
|
||||
data_dict[p_key] = p_node;
|
||||
return true;
|
||||
} else if (data_type == PList::PLNodeType::PL_NODE_TYPE_ARRAY) {
|
||||
data_array.push_back(p_node);
|
||||
return true;
|
||||
} else {
|
||||
ERR_FAIL_V_MSG(false, "PList: Invalid parent node type, should be DICT or ARRAY.");
|
||||
}
|
||||
}
|
||||
|
||||
size_t PListNode::get_asn1_size(uint8_t p_len_octets) const {
|
||||
// Get size of all data, excluding type and size information.
|
||||
switch (data_type) {
|
||||
case PList::PLNodeType::PL_NODE_TYPE_NIL: {
|
||||
return 0;
|
||||
} break;
|
||||
case PList::PLNodeType::PL_NODE_TYPE_DATA:
|
||||
case PList::PLNodeType::PL_NODE_TYPE_DATE: {
|
||||
ERR_FAIL_V_MSG(0, "PList: DATE and DATA nodes are not supported by ASN.1 serialization.");
|
||||
} break;
|
||||
case PList::PLNodeType::PL_NODE_TYPE_STRING: {
|
||||
return data_string.length();
|
||||
} break;
|
||||
case PList::PLNodeType::PL_NODE_TYPE_BOOLEAN: {
|
||||
return 1;
|
||||
} break;
|
||||
case PList::PLNodeType::PL_NODE_TYPE_INTEGER:
|
||||
case PList::PLNodeType::PL_NODE_TYPE_REAL: {
|
||||
return 4;
|
||||
} break;
|
||||
case PList::PLNodeType::PL_NODE_TYPE_ARRAY: {
|
||||
size_t size = 0;
|
||||
for (int i = 0; i < data_array.size(); i++) {
|
||||
size += 1 + _asn1_size_len(p_len_octets) + data_array[i]->get_asn1_size(p_len_octets);
|
||||
}
|
||||
return size;
|
||||
} break;
|
||||
case PList::PLNodeType::PL_NODE_TYPE_DICT: {
|
||||
size_t size = 0;
|
||||
for (const RBMap<String, Ref<PListNode>>::Element *it = data_dict.front(); it; it = it->next()) {
|
||||
size += 1 + _asn1_size_len(p_len_octets); // Sequence.
|
||||
size += 1 + _asn1_size_len(p_len_octets) + it->key().utf8().length(); //Key.
|
||||
size += 1 + _asn1_size_len(p_len_octets) + it->value()->get_asn1_size(p_len_octets); // Value.
|
||||
}
|
||||
return size;
|
||||
} break;
|
||||
default: {
|
||||
return 0;
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
int PListNode::_asn1_size_len(uint8_t p_len_octets) {
|
||||
if (p_len_octets > 1) {
|
||||
return p_len_octets + 1;
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
void PListNode::store_asn1_size(PoolByteArray &p_stream, uint8_t p_len_octets) const {
|
||||
uint32_t size = get_asn1_size(p_len_octets);
|
||||
if (p_len_octets > 1) {
|
||||
p_stream.push_back(0x80 + p_len_octets);
|
||||
}
|
||||
for (int i = p_len_octets - 1; i >= 0; i--) {
|
||||
uint8_t x = (size >> i * 8) & 0xFF;
|
||||
p_stream.push_back(x);
|
||||
}
|
||||
}
|
||||
|
||||
bool PListNode::store_asn1(PoolByteArray &p_stream, uint8_t p_len_octets) const {
|
||||
// Convert to binary ASN1 stream.
|
||||
bool valid = true;
|
||||
switch (data_type) {
|
||||
case PList::PLNodeType::PL_NODE_TYPE_NIL: {
|
||||
// Nothing to store.
|
||||
} break;
|
||||
case PList::PLNodeType::PL_NODE_TYPE_DATE:
|
||||
case PList::PLNodeType::PL_NODE_TYPE_DATA: {
|
||||
ERR_FAIL_V_MSG(false, "PList: DATE and DATA nodes are not supported by ASN.1 serialization.");
|
||||
} break;
|
||||
case PList::PLNodeType::PL_NODE_TYPE_STRING: {
|
||||
p_stream.push_back(0x0C);
|
||||
store_asn1_size(p_stream, p_len_octets);
|
||||
for (int i = 0; i < data_string.size(); i++) {
|
||||
p_stream.push_back(data_string[i]);
|
||||
}
|
||||
} break;
|
||||
case PList::PLNodeType::PL_NODE_TYPE_BOOLEAN: {
|
||||
p_stream.push_back(0x01);
|
||||
store_asn1_size(p_stream, p_len_octets);
|
||||
if (data_bool) {
|
||||
p_stream.push_back(0x01);
|
||||
} else {
|
||||
p_stream.push_back(0x00);
|
||||
}
|
||||
} break;
|
||||
case PList::PLNodeType::PL_NODE_TYPE_INTEGER: {
|
||||
p_stream.push_back(0x02);
|
||||
store_asn1_size(p_stream, p_len_octets);
|
||||
for (int i = 4; i >= 0; i--) {
|
||||
uint8_t x = (data_int >> i * 8) & 0xFF;
|
||||
p_stream.push_back(x);
|
||||
}
|
||||
} break;
|
||||
case PList::PLNodeType::PL_NODE_TYPE_REAL: {
|
||||
p_stream.push_back(0x03);
|
||||
store_asn1_size(p_stream, p_len_octets);
|
||||
for (int i = 4; i >= 0; i--) {
|
||||
uint8_t x = (data_int >> i * 8) & 0xFF;
|
||||
p_stream.push_back(x);
|
||||
}
|
||||
} break;
|
||||
case PList::PLNodeType::PL_NODE_TYPE_ARRAY: {
|
||||
p_stream.push_back(0x30); // Sequence.
|
||||
store_asn1_size(p_stream, p_len_octets);
|
||||
for (int i = 0; i < data_array.size(); i++) {
|
||||
valid = valid && data_array[i]->store_asn1(p_stream, p_len_octets);
|
||||
}
|
||||
} break;
|
||||
case PList::PLNodeType::PL_NODE_TYPE_DICT: {
|
||||
p_stream.push_back(0x31); // Set.
|
||||
store_asn1_size(p_stream, p_len_octets);
|
||||
for (const RBMap<String, Ref<PListNode>>::Element *it = data_dict.front(); it; it = it->next()) {
|
||||
CharString cs = it->key().utf8();
|
||||
uint32_t size = cs.length();
|
||||
|
||||
// Sequence.
|
||||
p_stream.push_back(0x30);
|
||||
uint32_t seq_size = 2 * (1 + _asn1_size_len(p_len_octets)) + size + it->value()->get_asn1_size(p_len_octets);
|
||||
if (p_len_octets > 1) {
|
||||
p_stream.push_back(0x80 + p_len_octets);
|
||||
}
|
||||
for (int i = p_len_octets - 1; i >= 0; i--) {
|
||||
uint8_t x = (seq_size >> i * 8) & 0xFF;
|
||||
p_stream.push_back(x);
|
||||
}
|
||||
// Key.
|
||||
p_stream.push_back(0x0C);
|
||||
if (p_len_octets > 1) {
|
||||
p_stream.push_back(0x80 + p_len_octets);
|
||||
}
|
||||
for (int i = p_len_octets - 1; i >= 0; i--) {
|
||||
uint8_t x = (size >> i * 8) & 0xFF;
|
||||
p_stream.push_back(x);
|
||||
}
|
||||
for (uint32_t i = 0; i < size; i++) {
|
||||
p_stream.push_back(cs[i]);
|
||||
}
|
||||
// Value.
|
||||
valid = valid && it->value()->store_asn1(p_stream, p_len_octets);
|
||||
}
|
||||
} break;
|
||||
}
|
||||
return valid;
|
||||
}
|
||||
|
||||
void PListNode::store_text(String &p_stream, uint8_t p_indent) const {
|
||||
// Convert to text XML stream.
|
||||
switch (data_type) {
|
||||
case PList::PLNodeType::PL_NODE_TYPE_NIL: {
|
||||
// Nothing to store.
|
||||
} break;
|
||||
case PList::PLNodeType::PL_NODE_TYPE_DATA: {
|
||||
p_stream += String("\t").repeat(p_indent);
|
||||
p_stream += "<data>\n";
|
||||
p_stream += String("\t").repeat(p_indent);
|
||||
p_stream += data_string + "\n";
|
||||
p_stream += String("\t").repeat(p_indent);
|
||||
p_stream += "</data>\n";
|
||||
} break;
|
||||
case PList::PLNodeType::PL_NODE_TYPE_DATE: {
|
||||
p_stream += String("\t").repeat(p_indent);
|
||||
p_stream += "<date>";
|
||||
p_stream += data_string;
|
||||
p_stream += "</date>\n";
|
||||
} break;
|
||||
case PList::PLNodeType::PL_NODE_TYPE_STRING: {
|
||||
p_stream += String("\t").repeat(p_indent);
|
||||
p_stream += "<string>";
|
||||
p_stream += String::utf8(data_string);
|
||||
p_stream += "</string>\n";
|
||||
} break;
|
||||
case PList::PLNodeType::PL_NODE_TYPE_BOOLEAN: {
|
||||
p_stream += String("\t").repeat(p_indent);
|
||||
if (data_bool) {
|
||||
p_stream += "<true/>\n";
|
||||
} else {
|
||||
p_stream += "<false/>\n";
|
||||
}
|
||||
} break;
|
||||
case PList::PLNodeType::PL_NODE_TYPE_INTEGER: {
|
||||
p_stream += String("\t").repeat(p_indent);
|
||||
p_stream += "<integer>";
|
||||
p_stream += itos(data_int);
|
||||
p_stream += "</integer>\n";
|
||||
} break;
|
||||
case PList::PLNodeType::PL_NODE_TYPE_REAL: {
|
||||
p_stream += String("\t").repeat(p_indent);
|
||||
p_stream += "<real>";
|
||||
p_stream += rtos(data_real);
|
||||
p_stream += "</real>\n";
|
||||
} break;
|
||||
case PList::PLNodeType::PL_NODE_TYPE_ARRAY: {
|
||||
p_stream += String("\t").repeat(p_indent);
|
||||
p_stream += "<array>\n";
|
||||
for (int i = 0; i < data_array.size(); i++) {
|
||||
data_array[i]->store_text(p_stream, p_indent + 1);
|
||||
}
|
||||
p_stream += String("\t").repeat(p_indent);
|
||||
p_stream += "</array>\n";
|
||||
} break;
|
||||
case PList::PLNodeType::PL_NODE_TYPE_DICT: {
|
||||
p_stream += String("\t").repeat(p_indent);
|
||||
p_stream += "<dict>\n";
|
||||
for (const RBMap<String, Ref<PListNode>>::Element *it = data_dict.front(); it; it = it->next()) {
|
||||
p_stream += String("\t").repeat(p_indent + 1);
|
||||
p_stream += "<key>";
|
||||
p_stream += it->key();
|
||||
p_stream += "</key>\n";
|
||||
it->value()->store_text(p_stream, p_indent + 1);
|
||||
}
|
||||
p_stream += String("\t").repeat(p_indent);
|
||||
p_stream += "</dict>\n";
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************/
|
||||
|
||||
PList::PList() {
|
||||
root = PListNode::new_dict();
|
||||
}
|
||||
|
||||
PList::PList(const String &p_string) {
|
||||
load_string(p_string);
|
||||
}
|
||||
|
||||
bool PList::load_file(const String &p_filename) {
|
||||
root = Ref<PListNode>();
|
||||
|
||||
FileAccessRef fb = FileAccess::open(p_filename, FileAccess::READ);
|
||||
if (!fb) {
|
||||
return false;
|
||||
}
|
||||
|
||||
unsigned char magic[8];
|
||||
fb->get_buffer(magic, 8);
|
||||
|
||||
if (String::utf8((const char *)magic, 8) == "bplist00") {
|
||||
ERR_FAIL_V_MSG(false, "PList: Binary property lists are not supported.");
|
||||
} else {
|
||||
// Load text plist.
|
||||
Error err;
|
||||
Vector<uint8_t> array = FileAccess::get_file_as_array(p_filename, &err);
|
||||
ERR_FAIL_COND_V(err != OK, false);
|
||||
|
||||
String ret;
|
||||
ret.parse_utf8((const char *)array.ptr(), array.size());
|
||||
return load_string(ret);
|
||||
}
|
||||
}
|
||||
|
||||
bool PList::load_string(const String &p_string) {
|
||||
root = Ref<PListNode>();
|
||||
|
||||
int pos = 0;
|
||||
bool in_plist = false;
|
||||
bool done_plist = false;
|
||||
List<Ref<PListNode>> stack;
|
||||
String key;
|
||||
while (pos >= 0) {
|
||||
int open_token_s = p_string.find("<", pos);
|
||||
if (open_token_s == -1) {
|
||||
ERR_FAIL_V_MSG(false, "PList: Unexpected end of data. No tags found.");
|
||||
}
|
||||
int open_token_e = p_string.find(">", open_token_s);
|
||||
pos = open_token_e;
|
||||
|
||||
String token = p_string.substr(open_token_s + 1, open_token_e - open_token_s - 1);
|
||||
if (token.empty()) {
|
||||
ERR_FAIL_V_MSG(false, "PList: Invalid token name.");
|
||||
}
|
||||
String value;
|
||||
if (token[0] == '?' || token[0] == '!') { // Skip <?xml ... ?> and <!DOCTYPE ... >
|
||||
int end_token_e = p_string.find(">", open_token_s);
|
||||
pos = end_token_e;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (token.find("plist", 0) == 0) {
|
||||
in_plist = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (token == "/plist") {
|
||||
in_plist = false;
|
||||
done_plist = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!in_plist) {
|
||||
ERR_FAIL_V_MSG(false, "PList: Node outside of <plist> tag.");
|
||||
}
|
||||
|
||||
if (token == "dict") {
|
||||
if (!stack.empty()) {
|
||||
// Add subnode end enter it.
|
||||
Ref<PListNode> dict = PListNode::new_dict();
|
||||
dict->data_type = PList::PLNodeType::PL_NODE_TYPE_DICT;
|
||||
if (!stack.back()->get()->push_subnode(dict, key)) {
|
||||
ERR_FAIL_V_MSG(false, "PList: Can't push subnode, invalid parent type.");
|
||||
}
|
||||
stack.push_back(dict);
|
||||
} else {
|
||||
// Add root node.
|
||||
if (!root.is_null()) {
|
||||
ERR_FAIL_V_MSG(false, "PList: Root node already set.");
|
||||
}
|
||||
Ref<PListNode> dict = PListNode::new_dict();
|
||||
stack.push_back(dict);
|
||||
root = dict;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (token == "/dict") {
|
||||
// Exit current dict.
|
||||
if (stack.empty() || stack.back()->get()->data_type != PList::PLNodeType::PL_NODE_TYPE_DICT) {
|
||||
ERR_FAIL_V_MSG(false, "PList: Mismatched </dict> tag.");
|
||||
}
|
||||
stack.pop_back();
|
||||
continue;
|
||||
}
|
||||
|
||||
if (token == "array") {
|
||||
if (!stack.empty()) {
|
||||
// Add subnode end enter it.
|
||||
Ref<PListNode> arr = PListNode::new_array();
|
||||
if (!stack.back()->get()->push_subnode(arr, key)) {
|
||||
ERR_FAIL_V_MSG(false, "PList: Can't push subnode, invalid parent type.");
|
||||
}
|
||||
stack.push_back(arr);
|
||||
} else {
|
||||
// Add root node.
|
||||
if (!root.is_null()) {
|
||||
ERR_FAIL_V_MSG(false, "PList: Root node already set.");
|
||||
}
|
||||
Ref<PListNode> arr = PListNode::new_array();
|
||||
stack.push_back(arr);
|
||||
root = arr;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (token == "/array") {
|
||||
// Exit current array.
|
||||
if (stack.empty() || stack.back()->get()->data_type != PList::PLNodeType::PL_NODE_TYPE_ARRAY) {
|
||||
ERR_FAIL_V_MSG(false, "PList: Mismatched </array> tag.");
|
||||
}
|
||||
stack.pop_back();
|
||||
continue;
|
||||
}
|
||||
|
||||
if (token[token.length() - 1] == '/') {
|
||||
token = token.substr(0, token.length() - 1);
|
||||
} else {
|
||||
int end_token_s = p_string.find("</", pos);
|
||||
if (end_token_s == -1) {
|
||||
ERR_FAIL_V_MSG(false, vformat("PList: Mismatched <%s> tag.", token));
|
||||
}
|
||||
int end_token_e = p_string.find(">", end_token_s);
|
||||
pos = end_token_e;
|
||||
String end_token = p_string.substr(end_token_s + 2, end_token_e - end_token_s - 2);
|
||||
if (end_token != token) {
|
||||
ERR_FAIL_V_MSG(false, vformat("PList: Mismatched <%s> and <%s> tag pair.", token, end_token));
|
||||
}
|
||||
value = p_string.substr(open_token_e + 1, end_token_s - open_token_e - 1);
|
||||
}
|
||||
if (token == "key") {
|
||||
key = value;
|
||||
} else {
|
||||
Ref<PListNode> var = nullptr;
|
||||
if (token == "true") {
|
||||
var = PListNode::new_bool(true);
|
||||
} else if (token == "false") {
|
||||
var = PListNode::new_bool(false);
|
||||
} else if (token == "integer") {
|
||||
var = PListNode::new_int(value.to_int());
|
||||
} else if (token == "real") {
|
||||
var = PListNode::new_real(value.to_float());
|
||||
} else if (token == "string") {
|
||||
var = PListNode::new_string(value);
|
||||
} else if (token == "data") {
|
||||
var = PListNode::new_data(value);
|
||||
} else if (token == "date") {
|
||||
var = PListNode::new_date(value);
|
||||
} else {
|
||||
ERR_FAIL_V_MSG(false, "PList: Invalid value type.");
|
||||
}
|
||||
if (stack.empty() || !stack.back()->get()->push_subnode(var, key)) {
|
||||
ERR_FAIL_V_MSG(false, "PList: Can't push subnode, invalid parent type.");
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!stack.empty() || !done_plist) {
|
||||
ERR_FAIL_V_MSG(false, "PList: Unexpected end of data. Root node is not closed.");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
PoolByteArray PList::save_asn1() const {
|
||||
if (root == nullptr) {
|
||||
ERR_FAIL_V_MSG(PoolByteArray(), "PList: Invalid PList, no root node.");
|
||||
}
|
||||
size_t size = root->get_asn1_size(1);
|
||||
uint8_t len_octets = 0;
|
||||
if (size < 0x80) {
|
||||
len_octets = 1;
|
||||
} else {
|
||||
size = root->get_asn1_size(2);
|
||||
if (size < 0xFFFF) {
|
||||
len_octets = 2;
|
||||
} else {
|
||||
size = root->get_asn1_size(3);
|
||||
if (size < 0xFFFFFF) {
|
||||
len_octets = 3;
|
||||
} else {
|
||||
size = root->get_asn1_size(4);
|
||||
if (size < 0xFFFFFFFF) {
|
||||
len_octets = 4;
|
||||
} else {
|
||||
ERR_FAIL_V_MSG(PoolByteArray(), "PList: Data is too big for ASN.1 serializer, should be < 4 GiB.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PoolByteArray ret;
|
||||
if (!root->store_asn1(ret, len_octets)) {
|
||||
ERR_FAIL_V_MSG(PoolByteArray(), "PList: ASN.1 serializer error.");
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
String PList::save_text() const {
|
||||
if (root == nullptr) {
|
||||
ERR_FAIL_V_MSG(String(), "PList: Invalid PList, no root node.");
|
||||
}
|
||||
|
||||
String ret;
|
||||
ret += "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
|
||||
ret += "<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n";
|
||||
ret += "<plist version=\"1.0\">\n";
|
||||
|
||||
root->store_text(ret, 0);
|
||||
|
||||
ret += "</plist>\n\n";
|
||||
return ret;
|
||||
}
|
||||
|
||||
Ref<PListNode> PList::get_root() {
|
||||
return root;
|
||||
}
|
||||
|
||||
#endif // MODULE_REGEX_ENABLED
|
@ -1,116 +0,0 @@
|
||||
/*************************************************************************/
|
||||
/* plist.h */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
|
||||
/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
|
||||
/* */
|
||||
/* 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. */
|
||||
/*************************************************************************/
|
||||
|
||||
// Property list file format (application/x-plist) parser, property list ASN-1 serialization.
|
||||
|
||||
#ifndef PLIST_H
|
||||
#define PLIST_H
|
||||
|
||||
#include "core/crypto/crypto_core.h"
|
||||
#include "core/os/file_access.h"
|
||||
#include "modules/modules_enabled.gen.h" // For regex.
|
||||
|
||||
#ifdef MODULE_REGEX_ENABLED
|
||||
|
||||
class PListNode;
|
||||
|
||||
class PList : public Reference {
|
||||
friend class PListNode;
|
||||
|
||||
public:
|
||||
enum PLNodeType {
|
||||
PL_NODE_TYPE_NIL,
|
||||
PL_NODE_TYPE_STRING,
|
||||
PL_NODE_TYPE_ARRAY,
|
||||
PL_NODE_TYPE_DICT,
|
||||
PL_NODE_TYPE_BOOLEAN,
|
||||
PL_NODE_TYPE_INTEGER,
|
||||
PL_NODE_TYPE_REAL,
|
||||
PL_NODE_TYPE_DATA,
|
||||
PL_NODE_TYPE_DATE,
|
||||
};
|
||||
|
||||
private:
|
||||
Ref<PListNode> root;
|
||||
|
||||
public:
|
||||
PList();
|
||||
PList(const String &p_string);
|
||||
|
||||
bool load_file(const String &p_filename);
|
||||
bool load_string(const String &p_string);
|
||||
|
||||
PoolByteArray save_asn1() const;
|
||||
String save_text() const;
|
||||
|
||||
Ref<PListNode> get_root();
|
||||
};
|
||||
|
||||
/*************************************************************************/
|
||||
|
||||
class PListNode : public Reference {
|
||||
static int _asn1_size_len(uint8_t p_len_octets);
|
||||
|
||||
public:
|
||||
PList::PLNodeType data_type = PList::PLNodeType::PL_NODE_TYPE_NIL;
|
||||
|
||||
CharString data_string;
|
||||
Vector<Ref<PListNode>> data_array;
|
||||
RBMap<String, Ref<PListNode>> data_dict;
|
||||
union {
|
||||
int32_t data_int;
|
||||
bool data_bool;
|
||||
float data_real;
|
||||
};
|
||||
|
||||
static Ref<PListNode> new_array();
|
||||
static Ref<PListNode> new_dict();
|
||||
static Ref<PListNode> new_string(const String &p_string);
|
||||
static Ref<PListNode> new_data(const String &p_string);
|
||||
static Ref<PListNode> new_date(const String &p_string);
|
||||
static Ref<PListNode> new_bool(bool p_bool);
|
||||
static Ref<PListNode> new_int(int32_t p_int);
|
||||
static Ref<PListNode> new_real(float p_real);
|
||||
|
||||
bool push_subnode(const Ref<PListNode> &p_node, const String &p_key = "");
|
||||
|
||||
size_t get_asn1_size(uint8_t p_len_octets) const;
|
||||
|
||||
void store_asn1_size(PoolByteArray &p_stream, uint8_t p_len_octets) const;
|
||||
bool store_asn1(PoolByteArray &p_stream, uint8_t p_len_octets) const;
|
||||
void store_text(String &p_stream, uint8_t p_indent) const;
|
||||
|
||||
PListNode() {}
|
||||
~PListNode() {}
|
||||
};
|
||||
|
||||
#endif // MODULE_REGEX_ENABLED
|
||||
|
||||
#endif // PLIST_H
|
@ -1,707 +0,0 @@
|
||||
/*************************************************************************/
|
||||
/* export.cpp */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
|
||||
/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
|
||||
/* */
|
||||
/* 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 "export.h"
|
||||
|
||||
#include "core/io/image_loader.h"
|
||||
#include "core/os/file_access.h"
|
||||
#include "core/os/os.h"
|
||||
#include "editor/editor_export.h"
|
||||
#include "editor/editor_node.h"
|
||||
#include "editor/editor_settings.h"
|
||||
#include "platform/windows/logo.gen.h"
|
||||
|
||||
#include "core/config/project_settings.h"
|
||||
|
||||
class EditorExportPlatformWindows : public EditorExportPlatformPC {
|
||||
Error _process_icon(const Ref<EditorExportPreset> &p_preset, const String &p_src_path, const String &p_dst_path);
|
||||
Error _rcedit_add_data(const Ref<EditorExportPreset> &p_preset, const String &p_path);
|
||||
Error _code_sign(const Ref<EditorExportPreset> &p_preset, const String &p_path);
|
||||
|
||||
public:
|
||||
virtual Error export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags = 0);
|
||||
virtual Error sign_shared_object(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path);
|
||||
virtual Error modify_template(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags);
|
||||
virtual Error fixup_embedded_pck(const String &p_path, int64_t p_embedded_start, int64_t p_embedded_size);
|
||||
virtual void get_export_options(List<ExportOption> *r_options);
|
||||
virtual bool get_option_visibility(const EditorExportPreset *p_preset, const String &p_option, const RBMap<StringName, Variant> &p_options) const;
|
||||
virtual bool has_valid_export_configuration(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates) const;
|
||||
virtual bool has_valid_project_configuration(const Ref<EditorExportPreset> &p_preset, String &r_error) const;
|
||||
};
|
||||
|
||||
Error EditorExportPlatformWindows::_process_icon(const Ref<EditorExportPreset> &p_preset, const String &p_src_path, const String &p_dst_path) {
|
||||
static const uint8_t icon_size[] = { 16, 32, 48, 64, 128, 0 /*256*/ };
|
||||
|
||||
struct IconData {
|
||||
PoolVector<uint8_t> data;
|
||||
uint8_t pal_colors = 0;
|
||||
uint16_t planes = 0;
|
||||
uint16_t bpp = 32;
|
||||
};
|
||||
|
||||
HashMap<uint8_t, IconData> images;
|
||||
Error err;
|
||||
|
||||
if (p_src_path.get_extension() == "ico") {
|
||||
FileAccess *f = FileAccess::open(p_src_path, FileAccess::READ, &err);
|
||||
if (err != OK) {
|
||||
return err;
|
||||
}
|
||||
|
||||
// Read ICONDIR.
|
||||
f->get_16(); // Reserved.
|
||||
uint16_t icon_type = f->get_16(); // Image type: 1 - ICO.
|
||||
uint16_t icon_count = f->get_16(); // Number of images.
|
||||
if (icon_type != 1) {
|
||||
f->close();
|
||||
memdelete(f);
|
||||
|
||||
ERR_FAIL_V(ERR_CANT_OPEN);
|
||||
}
|
||||
|
||||
for (uint16_t i = 0; i < icon_count; i++) {
|
||||
// Read ICONDIRENTRY.
|
||||
uint16_t w = f->get_8(); // Width in pixels.
|
||||
uint16_t h = f->get_8(); // Height in pixels.
|
||||
uint8_t pal_colors = f->get_8(); // Number of colors in the palette (0 - no palette).
|
||||
f->get_8(); // Reserved.
|
||||
uint16_t planes = f->get_16(); // Number of color planes.
|
||||
uint16_t bpp = f->get_16(); // Bits per pixel.
|
||||
uint32_t img_size = f->get_32(); // Image data size in bytes.
|
||||
uint32_t img_offset = f->get_32(); // Image data offset.
|
||||
if (w != h) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Read image data.
|
||||
uint64_t prev_offset = f->get_position();
|
||||
images[w].pal_colors = pal_colors;
|
||||
images[w].planes = planes;
|
||||
images[w].bpp = bpp;
|
||||
images[w].data.resize(img_size);
|
||||
PoolVector<uint8_t>::Write wr = images[w].data.write();
|
||||
f->seek(img_offset);
|
||||
f->get_buffer(wr.ptr(), img_size);
|
||||
f->seek(prev_offset);
|
||||
}
|
||||
f->close();
|
||||
memdelete(f);
|
||||
} else {
|
||||
Ref<Image> src_image;
|
||||
src_image.instance();
|
||||
err = ImageLoader::load_image(p_src_path, src_image.ptr());
|
||||
ERR_FAIL_COND_V(err != OK || src_image->empty(), ERR_CANT_OPEN);
|
||||
for (size_t i = 0; i < sizeof(icon_size) / sizeof(icon_size[0]); ++i) {
|
||||
int size = (icon_size[i] == 0) ? 256 : icon_size[i];
|
||||
|
||||
Ref<Image> res_image = src_image->duplicate();
|
||||
ERR_FAIL_COND_V(res_image.is_null() || res_image->empty(), ERR_CANT_OPEN);
|
||||
res_image->resize(size, size, (Image::Interpolation)(p_preset->get("application/icon_interpolation").operator int()));
|
||||
images[icon_size[i]].data = res_image->save_png_to_buffer();
|
||||
}
|
||||
}
|
||||
|
||||
uint16_t valid_icon_count = 0;
|
||||
for (size_t i = 0; i < sizeof(icon_size) / sizeof(icon_size[0]); ++i) {
|
||||
if (images.has(icon_size[i])) {
|
||||
valid_icon_count++;
|
||||
} else {
|
||||
int size = (icon_size[i] == 0) ? 256 : icon_size[i];
|
||||
add_message(EXPORT_MESSAGE_WARNING, TTR("Resources Modification"), vformat(TTR("Icon size \"%d\" is missing."), size));
|
||||
}
|
||||
}
|
||||
ERR_FAIL_COND_V(valid_icon_count == 0, ERR_CANT_OPEN);
|
||||
|
||||
FileAccess *fw = FileAccess::open(p_dst_path, FileAccess::WRITE, &err);
|
||||
if (err != OK) {
|
||||
return err;
|
||||
}
|
||||
|
||||
// Write ICONDIR.
|
||||
fw->store_16(0); // Reserved.
|
||||
fw->store_16(1); // Image type: 1 - ICO.
|
||||
fw->store_16(valid_icon_count); // Number of images.
|
||||
|
||||
// Write ICONDIRENTRY.
|
||||
uint32_t img_offset = 6 + 16 * valid_icon_count;
|
||||
for (size_t i = 0; i < sizeof(icon_size) / sizeof(icon_size[0]); ++i) {
|
||||
if (images.has(icon_size[i])) {
|
||||
const IconData &di = images[icon_size[i]];
|
||||
fw->store_8(icon_size[i]); // Width in pixels.
|
||||
fw->store_8(icon_size[i]); // Height in pixels.
|
||||
fw->store_8(di.pal_colors); // Number of colors in the palette (0 - no palette).
|
||||
fw->store_8(0); // Reserved.
|
||||
fw->store_16(di.planes); // Number of color planes.
|
||||
fw->store_16(di.bpp); // Bits per pixel.
|
||||
fw->store_32(di.data.size()); // Image data size in bytes.
|
||||
fw->store_32(img_offset); // Image data offset.
|
||||
|
||||
img_offset += di.data.size();
|
||||
}
|
||||
}
|
||||
|
||||
// Write image data.
|
||||
for (size_t i = 0; i < sizeof(icon_size) / sizeof(icon_size[0]); ++i) {
|
||||
if (images.has(icon_size[i])) {
|
||||
const IconData &di = images[icon_size[i]];
|
||||
PoolVector<uint8_t>::Read r = di.data.read();
|
||||
fw->store_buffer(r.ptr(), di.data.size());
|
||||
}
|
||||
}
|
||||
fw->close();
|
||||
memdelete(fw);
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
Error EditorExportPlatformWindows::sign_shared_object(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path) {
|
||||
if (p_preset->get("codesign/enable")) {
|
||||
return _code_sign(p_preset, p_path);
|
||||
} else {
|
||||
return OK;
|
||||
}
|
||||
}
|
||||
|
||||
Error EditorExportPlatformWindows::modify_template(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags) {
|
||||
if (p_preset->get("application/modify_resources")) {
|
||||
_rcedit_add_data(p_preset, p_path);
|
||||
}
|
||||
return OK;
|
||||
}
|
||||
|
||||
Error EditorExportPlatformWindows::export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags) {
|
||||
String pck_path = p_path;
|
||||
if (p_preset->get("binary_format/embed_pck")) {
|
||||
pck_path = p_path.get_basename() + ".tmp";
|
||||
}
|
||||
|
||||
Error err = EditorExportPlatformPC::export_project(p_preset, p_debug, pck_path, p_flags);
|
||||
if (p_preset->get("codesign/enable") && err == OK) {
|
||||
_code_sign(p_preset, pck_path);
|
||||
}
|
||||
|
||||
if (p_preset->get("binary_format/embed_pck") && err == OK) {
|
||||
DirAccessRef tmp_dir = DirAccess::create_for_path(p_path.get_base_dir());
|
||||
err = tmp_dir->rename(pck_path, p_path);
|
||||
if (err != OK) {
|
||||
add_message(EXPORT_MESSAGE_ERROR, TTR("PCK Embedding"), vformat(TTR("Failed to rename temporary file \"%s\"."), pck_path));
|
||||
}
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
bool EditorExportPlatformWindows::get_option_visibility(const EditorExportPreset *p_preset, const String &p_option, const RBMap<StringName, Variant> &p_options) const {
|
||||
// This option is not supported by "osslsigncode", used on non-Windows host.
|
||||
if (!OS::get_singleton()->has_feature("Windows") && p_option == "codesign/identity_type") {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void EditorExportPlatformWindows::get_export_options(List<ExportOption> *r_options) {
|
||||
EditorExportPlatformPC::get_export_options(r_options);
|
||||
|
||||
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/enable"), false));
|
||||
r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "codesign/identity_type", PROPERTY_HINT_ENUM, "Select automatically,Use PKCS12 file (specify *.PFX/*.P12 file),Use certificate store (specify SHA1 hash)"), 0));
|
||||
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "codesign/identity", PROPERTY_HINT_GLOBAL_FILE, "*.pfx,*.p12"), ""));
|
||||
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "codesign/password"), ""));
|
||||
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/timestamp"), true));
|
||||
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "codesign/timestamp_server_url"), ""));
|
||||
r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "codesign/digest_algorithm", PROPERTY_HINT_ENUM, "SHA1,SHA256"), 1));
|
||||
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "codesign/description"), ""));
|
||||
r_options->push_back(ExportOption(PropertyInfo(Variant::POOL_STRING_ARRAY, "codesign/custom_options"), PoolStringArray()));
|
||||
|
||||
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "application/modify_resources"), true));
|
||||
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/icon", PROPERTY_HINT_FILE, "*.ico,*.png,*.webp,*.svg"), ""));
|
||||
r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "application/icon_interpolation", PROPERTY_HINT_ENUM, "Nearest neighbor,Bilinear,Cubic,Trilinear,Lanczos"), 4));
|
||||
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/file_version", PROPERTY_HINT_PLACEHOLDER_TEXT, "1.0.0.0"), ""));
|
||||
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/product_version", PROPERTY_HINT_PLACEHOLDER_TEXT, "1.0.0.0"), ""));
|
||||
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/company_name", PROPERTY_HINT_PLACEHOLDER_TEXT, "Company Name"), ""));
|
||||
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/product_name", PROPERTY_HINT_PLACEHOLDER_TEXT, "Game Name"), ""));
|
||||
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/file_description"), ""));
|
||||
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/copyright"), ""));
|
||||
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/trademarks"), ""));
|
||||
}
|
||||
|
||||
Error EditorExportPlatformWindows::_rcedit_add_data(const Ref<EditorExportPreset> &p_preset, const String &p_path) {
|
||||
String rcedit_path = EditorSettings::get_singleton()->get("export/windows/rcedit");
|
||||
|
||||
if (rcedit_path != String() && !FileAccess::exists(rcedit_path)) {
|
||||
add_message(EXPORT_MESSAGE_WARNING, TTR("Resources Modification"), vformat(TTR("Could not find rcedit executable at \"%s\"."), rcedit_path));
|
||||
return ERR_FILE_NOT_FOUND;
|
||||
}
|
||||
|
||||
if (rcedit_path == String()) {
|
||||
rcedit_path = "rcedit"; // try to run rcedit from PATH
|
||||
}
|
||||
|
||||
#ifndef WINDOWS_ENABLED
|
||||
// On non-Windows we need WINE to run rcedit
|
||||
String wine_path = EditorSettings::get_singleton()->get("export/windows/wine");
|
||||
|
||||
if (wine_path != String() && !FileAccess::exists(wine_path)) {
|
||||
add_message(EXPORT_MESSAGE_WARNING, TTR("Resources Modification"), vformat(TTR("Could not find wine executable at \"%s\"."), wine_path));
|
||||
return ERR_FILE_NOT_FOUND;
|
||||
}
|
||||
|
||||
if (wine_path == String()) {
|
||||
wine_path = "wine"; // try to run wine from PATH
|
||||
}
|
||||
#endif
|
||||
|
||||
String icon_path = ProjectSettings::get_singleton()->globalize_path(p_preset->get("application/icon"));
|
||||
String tmp_icon_path = EditorSettings::get_singleton()->get_cache_dir().plus_file("_rcedit.ico");
|
||||
if (!icon_path.empty()) {
|
||||
if (_process_icon(p_preset, icon_path, tmp_icon_path) != OK) {
|
||||
add_message(EXPORT_MESSAGE_WARNING, TTR("Resources Modification"), vformat(TTR("Invalid icon file \"%s\"."), icon_path));
|
||||
icon_path = String();
|
||||
}
|
||||
}
|
||||
|
||||
String file_verion = p_preset->get("application/file_version");
|
||||
String product_version = p_preset->get("application/product_version");
|
||||
String company_name = p_preset->get("application/company_name");
|
||||
String product_name = p_preset->get("application/product_name");
|
||||
String file_description = p_preset->get("application/file_description");
|
||||
String copyright = p_preset->get("application/copyright");
|
||||
String trademarks = p_preset->get("application/trademarks");
|
||||
String comments = p_preset->get("application/comments");
|
||||
|
||||
List<String> args;
|
||||
args.push_back(p_path);
|
||||
if (icon_path != String()) {
|
||||
args.push_back("--set-icon");
|
||||
args.push_back(tmp_icon_path);
|
||||
}
|
||||
if (file_verion != String()) {
|
||||
args.push_back("--set-file-version");
|
||||
args.push_back(file_verion);
|
||||
}
|
||||
if (product_version != String()) {
|
||||
args.push_back("--set-product-version");
|
||||
args.push_back(product_version);
|
||||
}
|
||||
if (company_name != String()) {
|
||||
args.push_back("--set-version-string");
|
||||
args.push_back("CompanyName");
|
||||
args.push_back(company_name);
|
||||
}
|
||||
if (product_name != String()) {
|
||||
args.push_back("--set-version-string");
|
||||
args.push_back("ProductName");
|
||||
args.push_back(product_name);
|
||||
}
|
||||
if (file_description != String()) {
|
||||
args.push_back("--set-version-string");
|
||||
args.push_back("FileDescription");
|
||||
args.push_back(file_description);
|
||||
}
|
||||
if (copyright != String()) {
|
||||
args.push_back("--set-version-string");
|
||||
args.push_back("LegalCopyright");
|
||||
args.push_back(copyright);
|
||||
}
|
||||
if (trademarks != String()) {
|
||||
args.push_back("--set-version-string");
|
||||
args.push_back("LegalTrademarks");
|
||||
args.push_back(trademarks);
|
||||
}
|
||||
|
||||
#ifndef WINDOWS_ENABLED
|
||||
// On non-Windows we need WINE to run rcedit
|
||||
args.push_front(rcedit_path);
|
||||
rcedit_path = wine_path;
|
||||
#endif
|
||||
|
||||
String str;
|
||||
Error err = OS::get_singleton()->execute(rcedit_path, args, true, nullptr, &str, nullptr, true);
|
||||
|
||||
if (FileAccess::exists(tmp_icon_path)) {
|
||||
DirAccess::remove_file_or_error(tmp_icon_path);
|
||||
}
|
||||
|
||||
if (err != OK || (str.find("not found") != -1) || (str.find("not recognized") != -1)) {
|
||||
add_message(EXPORT_MESSAGE_WARNING, TTR("Resources Modification"), TTR("Could not start rcedit executable. Configure rcedit path in the Editor Settings (Export > Windows > rcedit), or disable \"Application > Modify Resources\" in the export preset."));
|
||||
return err;
|
||||
}
|
||||
|
||||
print_line("rcedit (" + p_path + "): " + str);
|
||||
|
||||
if (str.find("Fatal error") != -1) {
|
||||
add_message(EXPORT_MESSAGE_WARNING, TTR("Resources Modification"), vformat(TTR("rcedit failed to modify executable: %s."), str));
|
||||
return FAILED;
|
||||
}
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
Error EditorExportPlatformWindows::_code_sign(const Ref<EditorExportPreset> &p_preset, const String &p_path) {
|
||||
List<String> args;
|
||||
|
||||
#ifdef WINDOWS_ENABLED
|
||||
String signtool_path = EditorSettings::get_singleton()->get("export/windows/signtool");
|
||||
if (signtool_path != String() && !FileAccess::exists(signtool_path)) {
|
||||
add_message(EXPORT_MESSAGE_WARNING, TTR("Code Signing"), TTR("Could not start signtool executable. Configure signtool path in the Editor Settings (Export > Windows > Signtool), or disable \"Codesign\" in the export preset."));
|
||||
return ERR_FILE_NOT_FOUND;
|
||||
}
|
||||
if (signtool_path == String()) {
|
||||
signtool_path = "signtool"; // try to run signtool from PATH
|
||||
}
|
||||
#else
|
||||
String signtool_path = EditorSettings::get_singleton()->get("export/windows/osslsigncode");
|
||||
if (signtool_path != String() && !FileAccess::exists(signtool_path)) {
|
||||
add_message(EXPORT_MESSAGE_WARNING, TTR("Code Signing"), vformat(TTR("Could not find osslsigncode executable at \"%s\"."), signtool_path));
|
||||
return ERR_FILE_NOT_FOUND;
|
||||
}
|
||||
if (signtool_path == String()) {
|
||||
signtool_path = "osslsigncode"; // try to run signtool from PATH
|
||||
}
|
||||
#endif
|
||||
|
||||
args.push_back("sign");
|
||||
|
||||
//identity
|
||||
#ifdef WINDOWS_ENABLED
|
||||
int id_type = p_preset->get("codesign/identity_type");
|
||||
if (id_type == 0) { //auto select
|
||||
args.push_back("/a");
|
||||
} else if (id_type == 1) { //pkcs12
|
||||
if (p_preset->get("codesign/identity") != "") {
|
||||
args.push_back("/f");
|
||||
args.push_back(p_preset->get("codesign/identity"));
|
||||
} else {
|
||||
add_message(EXPORT_MESSAGE_WARNING, TTR("Code Signing"), TTR("No identity found."));
|
||||
return FAILED;
|
||||
}
|
||||
} else if (id_type == 2) { //Windows certificate store
|
||||
if (p_preset->get("codesign/identity") != "") {
|
||||
args.push_back("/sha1");
|
||||
args.push_back(p_preset->get("codesign/identity"));
|
||||
} else {
|
||||
add_message(EXPORT_MESSAGE_WARNING, TTR("Code Signing"), TTR("No identity found."));
|
||||
return FAILED;
|
||||
}
|
||||
} else {
|
||||
add_message(EXPORT_MESSAGE_WARNING, TTR("Code Signing"), TTR("Invalid identity type."));
|
||||
return FAILED;
|
||||
}
|
||||
#else
|
||||
if (p_preset->get("codesign/identity") != "") {
|
||||
args.push_back("-pkcs12");
|
||||
args.push_back(p_preset->get("codesign/identity"));
|
||||
} else {
|
||||
add_message(EXPORT_MESSAGE_WARNING, TTR("Code Signing"), TTR("No identity found."));
|
||||
return FAILED;
|
||||
}
|
||||
#endif
|
||||
|
||||
//password
|
||||
if (p_preset->get("codesign/password") != "") {
|
||||
#ifdef WINDOWS_ENABLED
|
||||
args.push_back("/p");
|
||||
#else
|
||||
args.push_back("-pass");
|
||||
#endif
|
||||
args.push_back(p_preset->get("codesign/password"));
|
||||
}
|
||||
|
||||
//timestamp
|
||||
if (p_preset->get("codesign/timestamp")) {
|
||||
if (p_preset->get("codesign/timestamp_server") != "") {
|
||||
#ifdef WINDOWS_ENABLED
|
||||
args.push_back("/tr");
|
||||
args.push_back(p_preset->get("codesign/timestamp_server_url"));
|
||||
args.push_back("/td");
|
||||
if ((int)p_preset->get("codesign/digest_algorithm") == 0) {
|
||||
args.push_back("sha1");
|
||||
} else {
|
||||
args.push_back("sha256");
|
||||
}
|
||||
#else
|
||||
args.push_back("-ts");
|
||||
args.push_back(p_preset->get("codesign/timestamp_server_url"));
|
||||
#endif
|
||||
} else {
|
||||
add_message(EXPORT_MESSAGE_WARNING, TTR("Code Signing"), TTR("Invalid timestamp server."));
|
||||
return FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
//digest
|
||||
#ifdef WINDOWS_ENABLED
|
||||
args.push_back("/fd");
|
||||
#else
|
||||
args.push_back("-h");
|
||||
#endif
|
||||
if ((int)p_preset->get("codesign/digest_algorithm") == 0) {
|
||||
args.push_back("sha1");
|
||||
} else {
|
||||
args.push_back("sha256");
|
||||
}
|
||||
|
||||
//description
|
||||
if (p_preset->get("codesign/description") != "") {
|
||||
#ifdef WINDOWS_ENABLED
|
||||
args.push_back("/d");
|
||||
#else
|
||||
args.push_back("-n");
|
||||
#endif
|
||||
args.push_back(p_preset->get("codesign/description"));
|
||||
}
|
||||
|
||||
//user options
|
||||
PoolStringArray user_args = p_preset->get("codesign/custom_options");
|
||||
for (int i = 0; i < user_args.size(); i++) {
|
||||
String user_arg = user_args[i].strip_edges();
|
||||
if (!user_arg.empty()) {
|
||||
args.push_back(user_arg);
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef WINDOWS_ENABLED
|
||||
args.push_back("-in");
|
||||
#endif
|
||||
args.push_back(p_path);
|
||||
#ifndef WINDOWS_ENABLED
|
||||
args.push_back("-out");
|
||||
args.push_back(p_path + "_signed");
|
||||
#endif
|
||||
|
||||
String str;
|
||||
Error err = OS::get_singleton()->execute(signtool_path, args, true, nullptr, &str, nullptr, true);
|
||||
if (err != OK || (str.find("not found") != -1) || (str.find("not recognized") != -1)) {
|
||||
#ifndef WINDOWS_ENABLED
|
||||
add_message(EXPORT_MESSAGE_WARNING, TTR("Code Signing"), TTR("Could not start signtool executable. Configure signtool path in the Editor Settings (Export > Windows > signtool), or disable \"Codesign\" in the export preset."));
|
||||
#else
|
||||
add_message(EXPORT_MESSAGE_WARNING, TTR("Code Signing"), TTR("Could not start osslsigncode executable. Configure signtool path in the Editor Settings (Export > Windows > osslsigncode), or disable \"Codesign\" in the export preset."));
|
||||
#endif
|
||||
return err;
|
||||
}
|
||||
|
||||
print_line("codesign (" + p_path + "): " + str);
|
||||
#ifndef WINDOWS_ENABLED
|
||||
if (str.find("SignTool Error") != -1) {
|
||||
#else
|
||||
if (str.find("Failed") != -1) {
|
||||
#endif
|
||||
add_message(EXPORT_MESSAGE_WARNING, TTR("Code Signing"), vformat(TTR("Signtool failed to sign executable: %s."), str));
|
||||
return FAILED;
|
||||
}
|
||||
|
||||
#ifndef WINDOWS_ENABLED
|
||||
DirAccessRef tmp_dir = DirAccess::create_for_path(p_path.get_base_dir());
|
||||
|
||||
err = tmp_dir->remove(p_path);
|
||||
if (err != OK) {
|
||||
add_message(EXPORT_MESSAGE_WARNING, TTR("Code Signing"), vformat(TTR("Failed to remove temporary file \"%s\"."), p_path));
|
||||
return err;
|
||||
}
|
||||
|
||||
err = tmp_dir->rename(p_path + "_signed", p_path);
|
||||
if (err != OK) {
|
||||
add_message(EXPORT_MESSAGE_WARNING, TTR("Code Signing"), vformat(TTR("Failed to rename temporary file \"%s\"."), p_path + "_signed"));
|
||||
return err;
|
||||
}
|
||||
#endif
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
bool EditorExportPlatformWindows::has_valid_export_configuration(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates) const {
|
||||
String err = "";
|
||||
bool valid = EditorExportPlatformPC::has_valid_export_configuration(p_preset, err, r_missing_templates);
|
||||
|
||||
String rcedit_path = EditorSettings::get_singleton()->get("export/windows/rcedit");
|
||||
if (p_preset->get("application/modify_resources") && rcedit_path.empty()) {
|
||||
err += TTR("The rcedit tool must be configured in the Editor Settings (Export > Windows > rcedit) to change the icon or app information data.") + "\n";
|
||||
}
|
||||
|
||||
if (!err.empty()) {
|
||||
r_error = err;
|
||||
}
|
||||
|
||||
return valid;
|
||||
}
|
||||
|
||||
bool EditorExportPlatformWindows::has_valid_project_configuration(const Ref<EditorExportPreset> &p_preset, String &r_error) const {
|
||||
String err = "";
|
||||
bool valid = true;
|
||||
|
||||
String icon_path = ProjectSettings::get_singleton()->globalize_path(p_preset->get("application/icon"));
|
||||
if (!icon_path.empty() && !FileAccess::exists(icon_path)) {
|
||||
err += TTR("Invalid icon path:") + " " + icon_path + "\n";
|
||||
}
|
||||
|
||||
// Only non-negative integers can exist in the version string.
|
||||
|
||||
String file_version = p_preset->get("application/file_version");
|
||||
if (!file_version.empty()) {
|
||||
Vector<String> version_array = file_version.split(".", false);
|
||||
if (version_array.size() != 4 || !version_array[0].is_valid_integer() ||
|
||||
!version_array[1].is_valid_integer() || !version_array[2].is_valid_integer() ||
|
||||
!version_array[3].is_valid_integer() || file_version.find("-") > -1) {
|
||||
err += TTR("Invalid file version:") + " " + file_version + "\n";
|
||||
}
|
||||
}
|
||||
|
||||
String product_version = p_preset->get("application/product_version");
|
||||
if (!product_version.empty()) {
|
||||
Vector<String> version_array = product_version.split(".", false);
|
||||
if (version_array.size() != 4 || !version_array[0].is_valid_integer() ||
|
||||
!version_array[1].is_valid_integer() || !version_array[2].is_valid_integer() ||
|
||||
!version_array[3].is_valid_integer() || product_version.find("-") > -1) {
|
||||
err += TTR("Invalid product version:") + " " + product_version + "\n";
|
||||
}
|
||||
}
|
||||
|
||||
if (!err.empty()) {
|
||||
r_error = err;
|
||||
}
|
||||
|
||||
return valid;
|
||||
}
|
||||
|
||||
Error EditorExportPlatformWindows::fixup_embedded_pck(const String &p_path, int64_t p_embedded_start, int64_t p_embedded_size) {
|
||||
// Patch the header of the "pck" section in the PE file so that it corresponds to the embedded data
|
||||
|
||||
if (p_embedded_size + p_embedded_start >= 0x100000000) { // Check for total executable size
|
||||
add_message(EXPORT_MESSAGE_ERROR, TTR("PCK Embedding"), TTR("Windows executables cannot be >= 4 GiB."));
|
||||
return ERR_INVALID_DATA;
|
||||
}
|
||||
|
||||
FileAccess *f = FileAccess::open(p_path, FileAccess::READ_WRITE);
|
||||
if (!f) {
|
||||
add_message(EXPORT_MESSAGE_ERROR, TTR("PCK Embedding"), vformat(TTR("Failed to open executable file \"%s\"."), p_path));
|
||||
return ERR_CANT_OPEN;
|
||||
}
|
||||
|
||||
// Jump to the PE header and check the magic number
|
||||
{
|
||||
f->seek(0x3c);
|
||||
uint32_t pe_pos = f->get_32();
|
||||
|
||||
f->seek(pe_pos);
|
||||
uint32_t magic = f->get_32();
|
||||
if (magic != 0x00004550) {
|
||||
f->close();
|
||||
add_message(EXPORT_MESSAGE_ERROR, TTR("PCK Embedding"), TTR("Executable file header corrupted."));
|
||||
return ERR_FILE_CORRUPT;
|
||||
}
|
||||
}
|
||||
|
||||
// Process header
|
||||
|
||||
int num_sections;
|
||||
{
|
||||
int64_t header_pos = f->get_position();
|
||||
|
||||
f->seek(header_pos + 2);
|
||||
num_sections = f->get_16();
|
||||
f->seek(header_pos + 16);
|
||||
uint16_t opt_header_size = f->get_16();
|
||||
|
||||
// Skip rest of header + optional header to go to the section headers
|
||||
f->seek(f->get_position() + 2 + opt_header_size);
|
||||
}
|
||||
|
||||
// Search for the "pck" section
|
||||
|
||||
int64_t section_table_pos = f->get_position();
|
||||
|
||||
bool found = false;
|
||||
for (int i = 0; i < num_sections; ++i) {
|
||||
int64_t section_header_pos = section_table_pos + i * 40;
|
||||
f->seek(section_header_pos);
|
||||
|
||||
uint8_t section_name[9];
|
||||
f->get_buffer(section_name, 8);
|
||||
section_name[8] = '\0';
|
||||
|
||||
if (strcmp((char *)section_name, "pck") == 0) {
|
||||
// "pck" section found, let's patch!
|
||||
|
||||
// Set virtual size to a little to avoid it taking memory (zero would give issues)
|
||||
f->seek(section_header_pos + 8);
|
||||
f->store_32(8);
|
||||
|
||||
f->seek(section_header_pos + 16);
|
||||
f->store_32(p_embedded_size);
|
||||
f->seek(section_header_pos + 20);
|
||||
f->store_32(p_embedded_start);
|
||||
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
f->close();
|
||||
|
||||
if (!found) {
|
||||
add_message(EXPORT_MESSAGE_ERROR, TTR("PCK Embedding"), TTR("Executable \"pck\" section not found."));
|
||||
return ERR_FILE_CORRUPT;
|
||||
}
|
||||
return OK;
|
||||
}
|
||||
|
||||
void register_windows_exporter() {
|
||||
#ifndef ANDROID_ENABLED
|
||||
EDITOR_DEF("export/windows/rcedit", "");
|
||||
EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::STRING, "export/windows/rcedit", PROPERTY_HINT_GLOBAL_FILE, "*.exe"));
|
||||
#ifdef WINDOWS_ENABLED
|
||||
EDITOR_DEF("export/windows/signtool", "");
|
||||
EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::STRING, "export/windows/signtool", PROPERTY_HINT_GLOBAL_FILE, "*.exe"));
|
||||
#else
|
||||
EDITOR_DEF("export/windows/osslsigncode", "");
|
||||
EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::STRING, "export/windows/osslsigncode", PROPERTY_HINT_GLOBAL_FILE));
|
||||
// On non-Windows we need WINE to run rcedit
|
||||
EDITOR_DEF("export/windows/wine", "");
|
||||
EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::STRING, "export/windows/wine", PROPERTY_HINT_GLOBAL_FILE));
|
||||
#endif
|
||||
#endif
|
||||
|
||||
Ref<EditorExportPlatformWindows> platform;
|
||||
platform.instance();
|
||||
|
||||
Ref<Image> img = memnew(Image(_windows_logo));
|
||||
Ref<ImageTexture> logo;
|
||||
logo.instance();
|
||||
logo->create_from_image(img);
|
||||
platform->set_logo(logo);
|
||||
platform->set_name("Windows Desktop");
|
||||
platform->set_extension("exe");
|
||||
platform->set_release_32("windows_32_release.exe");
|
||||
platform->set_debug_32("windows_32_debug.exe");
|
||||
platform->set_release_64("windows_64_release.exe");
|
||||
platform->set_debug_64("windows_64_debug.exe");
|
||||
platform->set_os_name("Windows");
|
||||
|
||||
EditorExport::get_singleton()->add_export_platform(platform);
|
||||
}
|
@ -1,31 +0,0 @@
|
||||
/*************************************************************************/
|
||||
/* export.h */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
|
||||
/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
|
||||
/* */
|
||||
/* 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. */
|
||||
/*************************************************************************/
|
||||
|
||||
void register_windows_exporter();
|
@ -1,175 +0,0 @@
|
||||
/*************************************************************************/
|
||||
/* export.cpp */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
|
||||
/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
|
||||
/* */
|
||||
/* 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 "export.h"
|
||||
|
||||
#include "core/os/file_access.h"
|
||||
#include "editor/editor_export.h"
|
||||
#include "platform/x11/logo.gen.h"
|
||||
#include "scene/resources/texture.h"
|
||||
|
||||
class EditorExportPlatformX11 : public EditorExportPlatformPC {
|
||||
public:
|
||||
virtual Error fixup_embedded_pck(const String &p_path, int64_t p_embedded_start, int64_t p_embedded_size);
|
||||
};
|
||||
|
||||
Error EditorExportPlatformX11::fixup_embedded_pck(const String &p_path, int64_t p_embedded_start, int64_t p_embedded_size) {
|
||||
// Patch the header of the "pck" section in the ELF file so that it corresponds to the embedded data
|
||||
|
||||
FileAccess *f = FileAccess::open(p_path, FileAccess::READ_WRITE);
|
||||
if (!f) {
|
||||
add_message(EXPORT_MESSAGE_ERROR, TTR("PCK Embedding"), vformat(TTR("Failed to open executable file \"%s\"."), p_path));
|
||||
return ERR_CANT_OPEN;
|
||||
}
|
||||
|
||||
// Read and check ELF magic number
|
||||
{
|
||||
uint32_t magic = f->get_32();
|
||||
if (magic != 0x464c457f) { // 0x7F + "ELF"
|
||||
f->close();
|
||||
add_message(EXPORT_MESSAGE_ERROR, TTR("PCK Embedding"), TTR("Executable file header corrupted."));
|
||||
return ERR_FILE_CORRUPT;
|
||||
}
|
||||
}
|
||||
|
||||
// Read program architecture bits from class field
|
||||
|
||||
int bits = f->get_8() * 32;
|
||||
|
||||
if (bits == 32 && p_embedded_size >= 0x100000000) {
|
||||
f->close();
|
||||
add_message(EXPORT_MESSAGE_ERROR, TTR("PCK Embedding"), TTR("32-bit executables cannot have embedded data >= 4 GiB."));
|
||||
return ERR_INVALID_DATA;
|
||||
}
|
||||
|
||||
// Get info about the section header table
|
||||
|
||||
int64_t section_table_pos;
|
||||
int64_t section_header_size;
|
||||
if (bits == 32) {
|
||||
section_header_size = 40;
|
||||
f->seek(0x20);
|
||||
section_table_pos = f->get_32();
|
||||
f->seek(0x30);
|
||||
} else { // 64
|
||||
section_header_size = 64;
|
||||
f->seek(0x28);
|
||||
section_table_pos = f->get_64();
|
||||
f->seek(0x3c);
|
||||
}
|
||||
int num_sections = f->get_16();
|
||||
int string_section_idx = f->get_16();
|
||||
|
||||
// Load the strings table
|
||||
uint8_t *strings;
|
||||
{
|
||||
// Jump to the strings section header
|
||||
f->seek(section_table_pos + string_section_idx * section_header_size);
|
||||
|
||||
// Read strings data size and offset
|
||||
int64_t string_data_pos;
|
||||
int64_t string_data_size;
|
||||
if (bits == 32) {
|
||||
f->seek(f->get_position() + 0x10);
|
||||
string_data_pos = f->get_32();
|
||||
string_data_size = f->get_32();
|
||||
} else { // 64
|
||||
f->seek(f->get_position() + 0x18);
|
||||
string_data_pos = f->get_64();
|
||||
string_data_size = f->get_64();
|
||||
}
|
||||
|
||||
// Read strings data
|
||||
f->seek(string_data_pos);
|
||||
strings = (uint8_t *)memalloc(string_data_size);
|
||||
if (!strings) {
|
||||
f->close();
|
||||
return ERR_OUT_OF_MEMORY;
|
||||
}
|
||||
f->get_buffer(strings, string_data_size);
|
||||
}
|
||||
|
||||
// Search for the "pck" section
|
||||
|
||||
bool found = false;
|
||||
for (int i = 0; i < num_sections; ++i) {
|
||||
int64_t section_header_pos = section_table_pos + i * section_header_size;
|
||||
f->seek(section_header_pos);
|
||||
|
||||
uint32_t name_offset = f->get_32();
|
||||
if (strcmp((char *)strings + name_offset, "pck") == 0) {
|
||||
// "pck" section found, let's patch!
|
||||
|
||||
if (bits == 32) {
|
||||
f->seek(section_header_pos + 0x10);
|
||||
f->store_32(p_embedded_start);
|
||||
f->store_32(p_embedded_size);
|
||||
} else { // 64
|
||||
f->seek(section_header_pos + 0x18);
|
||||
f->store_64(p_embedded_start);
|
||||
f->store_64(p_embedded_size);
|
||||
}
|
||||
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
memfree(strings);
|
||||
f->close();
|
||||
|
||||
if (!found) {
|
||||
add_message(EXPORT_MESSAGE_ERROR, TTR("PCK Embedding"), TTR("Executable \"pck\" section not found."));
|
||||
return ERR_FILE_CORRUPT;
|
||||
}
|
||||
return OK;
|
||||
}
|
||||
|
||||
void register_x11_exporter() {
|
||||
Ref<EditorExportPlatformX11> platform;
|
||||
platform.instance();
|
||||
|
||||
Ref<Image> img = memnew(Image(_x11_logo));
|
||||
Ref<ImageTexture> logo;
|
||||
logo.instance();
|
||||
logo->create_from_image(img);
|
||||
platform->set_logo(logo);
|
||||
platform->set_name("Linux/X11");
|
||||
platform->set_extension("x86");
|
||||
platform->set_extension("x86_64", "binary_format/64_bits");
|
||||
platform->set_release_32("linux_x11_32_release");
|
||||
platform->set_debug_32("linux_x11_32_debug");
|
||||
platform->set_release_64("linux_x11_64_release");
|
||||
platform->set_debug_64("linux_x11_64_debug");
|
||||
platform->set_os_name("X11");
|
||||
platform->set_chmod_flags(0755);
|
||||
|
||||
EditorExport::get_singleton()->add_export_platform(platform);
|
||||
}
|
@ -1,36 +0,0 @@
|
||||
/*************************************************************************/
|
||||
/* export.h */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
|
||||
/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
|
||||
/* */
|
||||
/* 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 X11_EXPORT_H
|
||||
#define X11_EXPORT_H
|
||||
|
||||
void register_x11_exporter();
|
||||
|
||||
#endif // X11_EXPORT_H
|
@ -314,10 +314,10 @@ def generate_scu_files(verbose, is_release_build, env):
|
||||
#"android/export",
|
||||
#"iphone/export",
|
||||
#"javascript/export",
|
||||
"osx/export",
|
||||
#"osx/export",
|
||||
#"uwp/export",
|
||||
"windows/export",
|
||||
"x11/export",
|
||||
#"windows/export",
|
||||
#"x11/export",
|
||||
]
|
||||
)
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user