summaryrefslogtreecommitdiffstats
path: root/platform/ios/export/export_plugin.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'platform/ios/export/export_plugin.cpp')
-rw-r--r--platform/ios/export/export_plugin.cpp400
1 files changed, 338 insertions, 62 deletions
diff --git a/platform/ios/export/export_plugin.cpp b/platform/ios/export/export_plugin.cpp
index c35c72d093..490f73a36d 100644
--- a/platform/ios/export/export_plugin.cpp
+++ b/platform/ios/export/export_plugin.cpp
@@ -40,6 +40,8 @@
#include "editor/editor_paths.h"
#include "editor/editor_string_names.h"
#include "editor/export/editor_export.h"
+#include "editor/export/lipo.h"
+#include "editor/export/macho.h"
#include "editor/import/resource_importer_texture_settings.h"
#include "editor/plugins/script_editor_plugin.h"
#include "editor/themes/editor_scale.h"
@@ -248,7 +250,7 @@ bool EditorExportPlatformIOS::get_export_option_visibility(const EditorExportPre
}
bool advanced_options_enabled = p_preset->are_advanced_options_enabled();
- if (p_option.begins_with("privacy")) {
+ if (p_option.begins_with("privacy") || p_option == "application/generate_simulator_library_if_missing") {
return advanced_options_enabled;
}
@@ -288,6 +290,7 @@ void EditorExportPlatformIOS::get_export_options(List<ExportOption> *r_options)
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "application/export_project_only"), false));
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "application/delete_old_export_files_unconditionally"), false));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "application/generate_simulator_library_if_missing"), true));
Vector<PluginConfigIOS> found_plugins = get_plugins();
for (int i = 0; i < found_plugins.size(); i++) {
@@ -400,65 +403,65 @@ void EditorExportPlatformIOS::_fix_config_file(const Ref<EditorExportPreset> &p_
str.parse_utf8((const char *)pfile.ptr(), pfile.size());
Vector<String> lines = str.split("\n");
for (int i = 0; i < lines.size(); i++) {
- if (lines[i].find("$binary") != -1) {
+ if (lines[i].contains("$binary")) {
strnew += lines[i].replace("$binary", p_config.binary_name) + "\n";
- } else if (lines[i].find("$modules_buildfile") != -1) {
+ } else if (lines[i].contains("$modules_buildfile")) {
strnew += lines[i].replace("$modules_buildfile", p_config.modules_buildfile) + "\n";
- } else if (lines[i].find("$modules_fileref") != -1) {
+ } else if (lines[i].contains("$modules_fileref")) {
strnew += lines[i].replace("$modules_fileref", p_config.modules_fileref) + "\n";
- } else if (lines[i].find("$modules_buildphase") != -1) {
+ } else if (lines[i].contains("$modules_buildphase")) {
strnew += lines[i].replace("$modules_buildphase", p_config.modules_buildphase) + "\n";
- } else if (lines[i].find("$modules_buildgrp") != -1) {
+ } else if (lines[i].contains("$modules_buildgrp")) {
strnew += lines[i].replace("$modules_buildgrp", p_config.modules_buildgrp) + "\n";
- } else if (lines[i].find("$name") != -1) {
+ } else if (lines[i].contains("$name")) {
strnew += lines[i].replace("$name", p_config.pkg_name) + "\n";
- } else if (lines[i].find("$bundle_identifier") != -1) {
+ } else if (lines[i].contains("$bundle_identifier")) {
strnew += lines[i].replace("$bundle_identifier", p_preset->get("application/bundle_identifier")) + "\n";
- } else if (lines[i].find("$short_version") != -1) {
+ } else if (lines[i].contains("$short_version")) {
strnew += lines[i].replace("$short_version", p_preset->get_version("application/short_version")) + "\n";
- } else if (lines[i].find("$version") != -1) {
+ } else if (lines[i].contains("$version")) {
strnew += lines[i].replace("$version", p_preset->get_version("application/version")) + "\n";
- } else if (lines[i].find("$min_version") != -1) {
+ } else if (lines[i].contains("$min_version")) {
strnew += lines[i].replace("$min_version", p_preset->get("application/min_ios_version")) + "\n";
- } else if (lines[i].find("$signature") != -1) {
+ } else if (lines[i].contains("$signature")) {
strnew += lines[i].replace("$signature", p_preset->get("application/signature")) + "\n";
- } else if (lines[i].find("$team_id") != -1) {
+ } else if (lines[i].contains("$team_id")) {
strnew += lines[i].replace("$team_id", p_preset->get("application/app_store_team_id")) + "\n";
- } else if (lines[i].find("$default_build_config") != -1) {
+ } else if (lines[i].contains("$default_build_config")) {
strnew += lines[i].replace("$default_build_config", p_debug ? "Debug" : "Release") + "\n";
- } else if (lines[i].find("$export_method") != -1) {
+ } else if (lines[i].contains("$export_method")) {
int export_method = p_preset->get(p_debug ? "application/export_method_debug" : "application/export_method_release");
strnew += lines[i].replace("$export_method", export_method_string[export_method]) + "\n";
- } else if (lines[i].find("$provisioning_profile_uuid_release") != -1) {
+ } else if (lines[i].contains("$provisioning_profile_uuid_release")) {
strnew += lines[i].replace("$provisioning_profile_uuid_release", p_preset->get_or_env("application/provisioning_profile_uuid_release", ENV_IOS_PROFILE_UUID_RELEASE)) + "\n";
- } else if (lines[i].find("$provisioning_profile_uuid_debug") != -1) {
+ } else if (lines[i].contains("$provisioning_profile_uuid_debug")) {
strnew += lines[i].replace("$provisioning_profile_uuid_debug", p_preset->get_or_env("application/provisioning_profile_uuid_debug", ENV_IOS_PROFILE_UUID_DEBUG)) + "\n";
- } else if (lines[i].find("$code_sign_style_debug") != -1) {
+ } else if (lines[i].contains("$code_sign_style_debug")) {
if (dbg_manual) {
strnew += lines[i].replace("$code_sign_style_debug", "Manual") + "\n";
} else {
strnew += lines[i].replace("$code_sign_style_debug", "Automatic") + "\n";
}
- } else if (lines[i].find("$code_sign_style_release") != -1) {
+ } else if (lines[i].contains("$code_sign_style_release")) {
if (rel_manual) {
strnew += lines[i].replace("$code_sign_style_release", "Manual") + "\n";
} else {
strnew += lines[i].replace("$code_sign_style_release", "Automatic") + "\n";
}
- } else if (lines[i].find("$provisioning_profile_uuid") != -1) {
+ } else if (lines[i].contains("$provisioning_profile_uuid")) {
String uuid = p_debug ? p_preset->get_or_env("application/provisioning_profile_uuid_debug", ENV_IOS_PROFILE_UUID_DEBUG) : p_preset->get_or_env("application/provisioning_profile_uuid_release", ENV_IOS_PROFILE_UUID_RELEASE);
strnew += lines[i].replace("$provisioning_profile_uuid", uuid) + "\n";
- } else if (lines[i].find("$code_sign_identity_debug") != -1) {
+ } else if (lines[i].contains("$code_sign_identity_debug")) {
strnew += lines[i].replace("$code_sign_identity_debug", dbg_sign_id) + "\n";
- } else if (lines[i].find("$code_sign_identity_release") != -1) {
+ } else if (lines[i].contains("$code_sign_identity_release")) {
strnew += lines[i].replace("$code_sign_identity_release", rel_sign_id) + "\n";
- } else if (lines[i].find("$additional_plist_content") != -1) {
+ } else if (lines[i].contains("$additional_plist_content")) {
strnew += lines[i].replace("$additional_plist_content", p_config.plist_content) + "\n";
- } else if (lines[i].find("$godot_archs") != -1) {
+ } else if (lines[i].contains("$godot_archs")) {
strnew += lines[i].replace("$godot_archs", p_config.architectures) + "\n";
- } else if (lines[i].find("$linker_flags") != -1) {
+ } else if (lines[i].contains("$linker_flags")) {
strnew += lines[i].replace("$linker_flags", p_config.linker_flags) + "\n";
- } else if (lines[i].find("$targeted_device_family") != -1) {
+ } else if (lines[i].contains("$targeted_device_family")) {
String xcode_value;
switch ((int)p_preset->get("application/targeted_device_family")) {
case 0: // iPhone
@@ -472,16 +475,16 @@ void EditorExportPlatformIOS::_fix_config_file(const Ref<EditorExportPreset> &p_
break;
}
strnew += lines[i].replace("$targeted_device_family", xcode_value) + "\n";
- } else if (lines[i].find("$cpp_code") != -1) {
+ } else if (lines[i].contains("$cpp_code")) {
strnew += lines[i].replace("$cpp_code", p_config.cpp_code) + "\n";
- } else if (lines[i].find("$docs_in_place") != -1) {
+ } else if (lines[i].contains("$docs_in_place")) {
strnew += lines[i].replace("$docs_in_place", ((bool)p_preset->get("user_data/accessible_from_files_app")) ? "<true/>" : "<false/>") + "\n";
- } else if (lines[i].find("$docs_sharing") != -1) {
+ } else if (lines[i].contains("$docs_sharing")) {
strnew += lines[i].replace("$docs_sharing", ((bool)p_preset->get("user_data/accessible_from_itunes_sharing")) ? "<true/>" : "<false/>") + "\n";
- } else if (lines[i].find("$entitlements_push_notifications") != -1) {
+ } else if (lines[i].contains("$entitlements_push_notifications")) {
bool is_on = p_preset->get("capabilities/push_notifications");
strnew += lines[i].replace("$entitlements_push_notifications", is_on ? "<key>aps-environment</key><string>development</string>" : "") + "\n";
- } else if (lines[i].find("$required_device_capabilities") != -1) {
+ } else if (lines[i].contains("$required_device_capabilities")) {
String capabilities;
// I've removed armv7 as we can run on 64bit only devices
@@ -503,7 +506,7 @@ void EditorExportPlatformIOS::_fix_config_file(const Ref<EditorExportPreset> &p_
}
strnew += lines[i].replace("$required_device_capabilities", capabilities);
- } else if (lines[i].find("$interface_orientations") != -1) {
+ } else if (lines[i].contains("$interface_orientations")) {
String orientations;
const DisplayServer::ScreenOrientation screen_orientation =
DisplayServer::ScreenOrientation(int(GLOBAL_GET("display/window/handheld/orientation")));
@@ -541,35 +544,73 @@ void EditorExportPlatformIOS::_fix_config_file(const Ref<EditorExportPreset> &p_
}
strnew += lines[i].replace("$interface_orientations", orientations);
- } else if (lines[i].find("$camera_usage_description") != -1) {
+ } else if (lines[i].contains("$ipad_interface_orientations")) {
+ String orientations;
+ const DisplayServer::ScreenOrientation screen_orientation =
+ DisplayServer::ScreenOrientation(int(GLOBAL_GET("display/window/handheld/orientation")));
+
+ switch (screen_orientation) {
+ case DisplayServer::SCREEN_LANDSCAPE:
+ orientations += "<string>UIInterfaceOrientationLandscapeRight</string>\n";
+ break;
+ case DisplayServer::SCREEN_PORTRAIT:
+ orientations += "<string>UIInterfaceOrientationPortrait</string>\n";
+ break;
+ case DisplayServer::SCREEN_REVERSE_LANDSCAPE:
+ orientations += "<string>UIInterfaceOrientationLandscapeLeft</string>\n";
+ break;
+ case DisplayServer::SCREEN_REVERSE_PORTRAIT:
+ orientations += "<string>UIInterfaceOrientationPortraitUpsideDown</string>\n";
+ break;
+ case DisplayServer::SCREEN_SENSOR_LANDSCAPE:
+ // Allow both landscape orientations depending on sensor direction.
+ orientations += "<string>UIInterfaceOrientationLandscapeLeft</string>\n";
+ orientations += "<string>UIInterfaceOrientationLandscapeRight</string>\n";
+ break;
+ case DisplayServer::SCREEN_SENSOR_PORTRAIT:
+ // Allow both portrait orientations depending on sensor direction.
+ orientations += "<string>UIInterfaceOrientationPortrait</string>\n";
+ orientations += "<string>UIInterfaceOrientationPortraitUpsideDown</string>\n";
+ break;
+ case DisplayServer::SCREEN_SENSOR:
+ // Allow all screen orientations depending on sensor direction.
+ orientations += "<string>UIInterfaceOrientationLandscapeLeft</string>\n";
+ orientations += "<string>UIInterfaceOrientationLandscapeRight</string>\n";
+ orientations += "<string>UIInterfaceOrientationPortrait</string>\n";
+ orientations += "<string>UIInterfaceOrientationPortraitUpsideDown</string>\n";
+ break;
+ }
+
+ strnew += lines[i].replace("$ipad_interface_orientations", orientations);
+ } else if (lines[i].contains("$camera_usage_description")) {
String description = p_preset->get("privacy/camera_usage_description");
strnew += lines[i].replace("$camera_usage_description", description) + "\n";
- } else if (lines[i].find("$microphone_usage_description") != -1) {
+ } else if (lines[i].contains("$microphone_usage_description")) {
String description = p_preset->get("privacy/microphone_usage_description");
strnew += lines[i].replace("$microphone_usage_description", description) + "\n";
- } else if (lines[i].find("$photolibrary_usage_description") != -1) {
+ } else if (lines[i].contains("$photolibrary_usage_description")) {
String description = p_preset->get("privacy/photolibrary_usage_description");
strnew += lines[i].replace("$photolibrary_usage_description", description) + "\n";
- } else if (lines[i].find("$plist_launch_screen_name") != -1) {
+ } else if (lines[i].contains("$plist_launch_screen_name")) {
String value = "<key>UILaunchStoryboardName</key>\n<string>Launch Screen</string>";
strnew += lines[i].replace("$plist_launch_screen_name", value) + "\n";
- } else if (lines[i].find("$pbx_launch_screen_file_reference") != -1) {
+ } else if (lines[i].contains("$pbx_launch_screen_file_reference")) {
String value = "90DD2D9D24B36E8000717FE1 = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = \"Launch Screen.storyboard\"; sourceTree = \"<group>\"; };";
strnew += lines[i].replace("$pbx_launch_screen_file_reference", value) + "\n";
- } else if (lines[i].find("$pbx_launch_screen_copy_files") != -1) {
+ } else if (lines[i].contains("$pbx_launch_screen_copy_files")) {
String value = "90DD2D9D24B36E8000717FE1 /* Launch Screen.storyboard */,";
strnew += lines[i].replace("$pbx_launch_screen_copy_files", value) + "\n";
- } else if (lines[i].find("$pbx_launch_screen_build_phase") != -1) {
+ } else if (lines[i].contains("$pbx_launch_screen_build_phase")) {
String value = "90DD2D9E24B36E8000717FE1 /* Launch Screen.storyboard in Resources */,";
strnew += lines[i].replace("$pbx_launch_screen_build_phase", value) + "\n";
- } else if (lines[i].find("$pbx_launch_screen_build_reference") != -1) {
+ } else if (lines[i].contains("$pbx_launch_screen_build_reference")) {
String value = "90DD2D9E24B36E8000717FE1 /* Launch Screen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 90DD2D9D24B36E8000717FE1 /* Launch Screen.storyboard */; };";
strnew += lines[i].replace("$pbx_launch_screen_build_reference", value) + "\n";
#ifndef DISABLE_DEPRECATED
- } else if (lines[i].find("$pbx_launch_image_usage_setting") != -1) {
+ } else if (lines[i].contains("$pbx_launch_image_usage_setting")) {
strnew += lines[i].replace("$pbx_launch_image_usage_setting", "") + "\n";
#endif
- } else if (lines[i].find("$launch_screen_image_mode") != -1) {
+ } else if (lines[i].contains("$launch_screen_image_mode")) {
int image_scale_mode = p_preset->get("storyboard/image_scale_mode");
String value;
@@ -586,7 +627,7 @@ void EditorExportPlatformIOS::_fix_config_file(const Ref<EditorExportPreset> &p_
}
strnew += lines[i].replace("$launch_screen_image_mode", value) + "\n";
- } else if (lines[i].find("$launch_screen_background_color") != -1) {
+ } else if (lines[i].contains("$launch_screen_background_color")) {
bool use_custom = p_preset->get("storyboard/use_custom_bg_color");
Color color = use_custom ? p_preset->get("storyboard/custom_bg_color") : GLOBAL_GET("application/boot_splash/bg_color");
const String value_format = "red=\"$red\" green=\"$green\" blue=\"$blue\" alpha=\"$alpha\"";
@@ -599,7 +640,7 @@ void EditorExportPlatformIOS::_fix_config_file(const Ref<EditorExportPreset> &p_
String value = value_format.format(value_dictionary, "$_");
strnew += lines[i].replace("$launch_screen_background_color", value) + "\n";
- } else if (lines[i].find("$pbx_locale_file_reference") != -1) {
+ } else if (lines[i].contains("$pbx_locale_file_reference")) {
String locale_files;
Vector<String> translations = GLOBAL_GET("internationalization/locale/translations");
if (translations.size() > 0) {
@@ -618,7 +659,7 @@ void EditorExportPlatformIOS::_fix_config_file(const Ref<EditorExportPreset> &p_
}
}
strnew += lines[i].replace("$pbx_locale_file_reference", locale_files);
- } else if (lines[i].find("$pbx_locale_build_reference") != -1) {
+ } else if (lines[i].contains("$pbx_locale_build_reference")) {
String locale_files;
Vector<String> translations = GLOBAL_GET("internationalization/locale/translations");
if (translations.size() > 0) {
@@ -637,10 +678,10 @@ void EditorExportPlatformIOS::_fix_config_file(const Ref<EditorExportPreset> &p_
}
}
strnew += lines[i].replace("$pbx_locale_build_reference", locale_files);
- } else if (lines[i].find("$swift_runtime_migration") != -1) {
+ } else if (lines[i].contains("$swift_runtime_migration")) {
String value = !p_config.use_swift_runtime ? "" : "LastSwiftMigration = 1250;";
strnew += lines[i].replace("$swift_runtime_migration", value) + "\n";
- } else if (lines[i].find("$swift_runtime_build_settings") != -1) {
+ } else if (lines[i].contains("$swift_runtime_build_settings")) {
String value = !p_config.use_swift_runtime ? "" : R"(
CLANG_ENABLE_MODULES = YES;
SWIFT_OBJC_BRIDGING_HEADER = "$binary/dummy.h";
@@ -648,25 +689,25 @@ void EditorExportPlatformIOS::_fix_config_file(const Ref<EditorExportPreset> &p_
)";
value = value.replace("$binary", p_config.binary_name);
strnew += lines[i].replace("$swift_runtime_build_settings", value) + "\n";
- } else if (lines[i].find("$swift_runtime_fileref") != -1) {
+ } else if (lines[i].contains("$swift_runtime_fileref")) {
String value = !p_config.use_swift_runtime ? "" : R"(
90B4C2AA2680BC560039117A /* dummy.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "dummy.h"; sourceTree = "<group>"; };
90B4C2B52680C7E90039117A /* dummy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "dummy.swift"; sourceTree = "<group>"; };
)";
strnew += lines[i].replace("$swift_runtime_fileref", value) + "\n";
- } else if (lines[i].find("$swift_runtime_binary_files") != -1) {
+ } else if (lines[i].contains("$swift_runtime_binary_files")) {
String value = !p_config.use_swift_runtime ? "" : R"(
90B4C2AA2680BC560039117A /* dummy.h */,
90B4C2B52680C7E90039117A /* dummy.swift */,
)";
strnew += lines[i].replace("$swift_runtime_binary_files", value) + "\n";
- } else if (lines[i].find("$swift_runtime_buildfile") != -1) {
+ } else if (lines[i].contains("$swift_runtime_buildfile")) {
String value = !p_config.use_swift_runtime ? "" : "90B4C2B62680C7E90039117A /* dummy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 90B4C2B52680C7E90039117A /* dummy.swift */; };";
strnew += lines[i].replace("$swift_runtime_buildfile", value) + "\n";
- } else if (lines[i].find("$swift_runtime_build_phase") != -1) {
+ } else if (lines[i].contains("$swift_runtime_build_phase")) {
String value = !p_config.use_swift_runtime ? "" : "90B4C2B62680C7E90039117A /* dummy.swift */,";
strnew += lines[i].replace("$swift_runtime_build_phase", value) + "\n";
- } else if (lines[i].find("$priv_collection") != -1) {
+ } else if (lines[i].contains("$priv_collection")) {
bool section_opened = false;
for (uint64_t j = 0; j < sizeof(data_collect_type_info) / sizeof(data_collect_type_info[0]); ++j) {
bool data_collected = p_preset->get(vformat("privacy/collected_data/%s/collected", data_collect_type_info[j].prop_name));
@@ -710,7 +751,7 @@ void EditorExportPlatformIOS::_fix_config_file(const Ref<EditorExportPreset> &p_
if (section_opened) {
strnew += "\t</array>\n";
}
- } else if (lines[i].find("$priv_tracking") != -1) {
+ } else if (lines[i].contains("$priv_tracking")) {
bool tracking = p_preset->get("privacy/tracking_enabled");
strnew += "\t<key>NSPrivacyTracking</key>\n";
if (tracking) {
@@ -727,7 +768,7 @@ void EditorExportPlatformIOS::_fix_config_file(const Ref<EditorExportPreset> &p_
}
strnew += "\t</array>\n";
}
- } else if (lines[i].find("$priv_api_types") != -1) {
+ } else if (lines[i].contains("$priv_api_types")) {
strnew += "\t<array>\n";
for (uint64_t j = 0; j < sizeof(api_info) / sizeof(api_info[0]); ++j) {
int api_access = p_preset->get(vformat("privacy/%s_access_reasons", api_info[j].prop_name));
@@ -1112,6 +1153,167 @@ struct ExportLibsData {
String dest_dir;
};
+bool EditorExportPlatformIOS::_archive_has_arm64(const String &p_path, uint32_t *r_cputype, uint32_t *r_cpusubtype) const {
+ bool has_arm64_image = false;
+ if (FileAccess::exists(p_path)) {
+ if (LipO::is_lipo(p_path)) {
+ // LipO.
+ Ref<LipO> lipo;
+ lipo.instantiate();
+ if (lipo->open_file(p_path)) {
+ for (int i = 0; i < lipo->get_arch_count(); i++) {
+ if (lipo->get_arch_cputype(i) == 0x100000c && lipo->get_arch_cpusubtype(i) == 0) {
+ has_arm64_image = true;
+ break;
+ }
+ }
+ }
+ lipo->close();
+ } else {
+ // Single architecture archive.
+ Ref<FileAccess> sim_f = FileAccess::open(p_path, FileAccess::READ);
+ if (sim_f.is_valid()) {
+ char magic[9] = {};
+ sim_f->get_buffer((uint8_t *)&magic[0], 8);
+ if (String(magic) == String("!<arch>\n")) {
+ while (!sim_f->eof_reached()) {
+ // Read file metadata.
+ char name_short[17] = {};
+ char size_short[11] = {};
+ sim_f->get_buffer((uint8_t *)&name_short[0], 16);
+ sim_f->seek(sim_f->get_position() + 12 + 6 + 6 + 8); // Skip modification time, owner ID, group ID, file mode.
+ sim_f->get_buffer((uint8_t *)&size_short[0], 10);
+ sim_f->seek(sim_f->get_position() + 2); // Skip end marker.
+
+ int64_t file_size = String(size_short).to_int();
+ int64_t next_off = sim_f->get_position() + file_size;
+
+ String name = String(name_short); // Skip extended name.
+ if (name.is_empty() || file_size == 0) {
+ break;
+ }
+ if (name.begins_with("#1/")) {
+ int64_t name_len = String(name_short).replace("#1/", "").to_int();
+ sim_f->seek(sim_f->get_position() + name_len);
+ }
+
+ // Read file content.
+ uint32_t obj_magic = sim_f->get_32();
+
+ bool swap = (obj_magic == 0xcffaedfe || obj_magic == 0xcefaedfe);
+ if (obj_magic == 0xcefaedfe || obj_magic == 0xfeedface || obj_magic == 0xcffaedfe || obj_magic == 0xfeedfacf) {
+ uint32_t cputype = sim_f->get_32();
+ uint32_t cpusubtype = sim_f->get_32();
+ if (swap) {
+ cputype = BSWAP32(cputype);
+ cpusubtype = BSWAP32(cpusubtype);
+ }
+ if (r_cputype) {
+ *r_cputype = cputype;
+ }
+ if (r_cpusubtype) {
+ *r_cpusubtype = cpusubtype;
+ }
+ if (cputype == 0x100000c && cpusubtype == 0) {
+ has_arm64_image = true;
+ }
+ break;
+ }
+ sim_f->seek(next_off);
+ }
+ }
+ sim_f->close();
+ }
+ }
+ }
+ return has_arm64_image;
+}
+
+int EditorExportPlatformIOS::_archive_convert_to_simulator(const String &p_path) const {
+ int commands_patched = 0;
+ Ref<FileAccess> sim_f = FileAccess::open(p_path, FileAccess::READ_WRITE);
+ if (sim_f.is_valid()) {
+ char magic[9] = {};
+ sim_f->get_buffer((uint8_t *)&magic[0], 8);
+ if (String(magic) == String("!<arch>\n")) {
+ while (!sim_f->eof_reached()) {
+ // Read file metadata.
+ char name_short[17] = {};
+ char size_short[11] = {};
+ sim_f->get_buffer((uint8_t *)&name_short[0], 16);
+ sim_f->seek(sim_f->get_position() + 12 + 6 + 6 + 8); // Skip modification time, owner ID, group ID, file mode.
+ sim_f->get_buffer((uint8_t *)&size_short[0], 10);
+ sim_f->seek(sim_f->get_position() + 2); // Skip end marker.
+
+ int64_t file_size = String(size_short).to_int();
+ int64_t next_off = sim_f->get_position() + file_size;
+
+ String name = String(name_short); // Skip extended name.
+ if (name.is_empty() || file_size == 0) {
+ break;
+ }
+ if (name.begins_with("#1/")) {
+ int64_t name_len = String(name_short).replace("#1/", "").to_int();
+ sim_f->seek(sim_f->get_position() + name_len);
+ }
+
+ // Read file content.
+ uint32_t obj_magic = sim_f->get_32();
+
+ bool swap = (obj_magic == 0xcffaedfe || obj_magic == 0xcefaedfe);
+ if (obj_magic == 0xcefaedfe || obj_magic == 0xfeedface || obj_magic == 0xcffaedfe || obj_magic == 0xfeedfacf) {
+ uint32_t cputype = sim_f->get_32();
+ uint32_t cpusubtype = sim_f->get_32();
+ uint32_t filetype = sim_f->get_32();
+ uint32_t ncmds = sim_f->get_32();
+ sim_f->get_32(); // Commands total size.
+ sim_f->get_32(); // Commands flags.
+ if (obj_magic == 0xcffaedfe || obj_magic == 0xfeedfacf) {
+ sim_f->get_32(); // Reserved, 64-bit only.
+ }
+ if (swap) {
+ ncmds = BSWAP32(ncmds);
+ cputype = BSWAP32(cputype);
+ cpusubtype = BSWAP32(cpusubtype);
+ filetype = BSWAP32(filetype);
+ }
+ if (cputype == 0x100000C && cpusubtype == 0 && filetype == 1) {
+ // ARM64, object file.
+ for (uint32_t i = 0; i < ncmds; i++) {
+ int64_t cmdofs = sim_f->get_position();
+ uint32_t cmdid = sim_f->get_32();
+ uint32_t cmdsize = sim_f->get_32();
+ if (swap) {
+ cmdid = BSWAP32(cmdid);
+ cmdsize = BSWAP32(cmdsize);
+ }
+ if (cmdid == MachO::LoadCommandID::LC_BUILD_VERSION) {
+ int64_t platform = sim_f->get_32();
+ if (swap) {
+ platform = BSWAP32(platform);
+ }
+ if (platform == MachO::PlatformID::PLATFORM_IOS) {
+ sim_f->seek(cmdofs + 4 + 4);
+ uint32_t new_id = MachO::PlatformID::PLATFORM_IOSSIMULATOR;
+ if (swap) {
+ new_id = BSWAP32(new_id);
+ }
+ sim_f->store_32(new_id);
+ commands_patched++;
+ }
+ }
+ sim_f->seek(cmdofs + cmdsize);
+ }
+ }
+ }
+ sim_f->seek(next_off);
+ }
+ }
+ sim_f->close();
+ }
+ return commands_patched;
+}
+
void EditorExportPlatformIOS::_check_xcframework_content(const String &p_path, int &r_total_libs, int &r_static_libs, int &r_dylibs, int &r_frameworks) const {
Ref<PList> plist;
plist.instantiate();
@@ -1319,12 +1521,7 @@ void EditorExportPlatformIOS::_add_assets_to_project(const String &p_out_dir, co
String type;
if (asset.exported_path.ends_with(".framework")) {
- int total_libs = 0;
- int static_libs = 0;
- int dylibs = 0;
- int frameworks = 0;
- _check_xcframework_content(p_out_dir.path_join(asset.exported_path), total_libs, static_libs, dylibs, frameworks);
- if (asset.should_embed && (static_libs != total_libs)) {
+ if (asset.should_embed) {
additional_asset_info_format += "$framework_id = {isa = PBXBuildFile; fileRef = $ref_id; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; };\n";
framework_id = (++current_id).str();
pbx_embeded_frameworks += framework_id + ",\n";
@@ -1332,7 +1529,12 @@ void EditorExportPlatformIOS::_add_assets_to_project(const String &p_out_dir, co
type = "wrapper.framework";
} else if (asset.exported_path.ends_with(".xcframework")) {
- if (asset.should_embed) {
+ int total_libs = 0;
+ int static_libs = 0;
+ int dylibs = 0;
+ int frameworks = 0;
+ _check_xcframework_content(p_out_dir.path_join(asset.exported_path), total_libs, static_libs, dylibs, frameworks);
+ if (asset.should_embed && static_libs != total_libs) {
additional_asset_info_format += "$framework_id = {isa = PBXBuildFile; fileRef = $ref_id; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; };\n";
framework_id = (++current_id).str();
pbx_embeded_frameworks += framework_id + ",\n";
@@ -2108,7 +2310,7 @@ Error EditorExportPlatformIOS::_export_project_helper(const Ref<EditorExportPres
ret = unzGoToNextFile(src_pkg_zip);
}
- /* we're done with our source zip */
+ // We're done with our source zip.
unzClose(src_pkg_zip);
if (!found_library) {
@@ -2116,6 +2318,80 @@ Error EditorExportPlatformIOS::_export_project_helper(const Ref<EditorExportPres
return ERR_FILE_NOT_FOUND;
}
+ // Check and generate missing ARM64 simulator library.
+ if (p_preset->get("application/generate_simulator_library_if_missing").operator bool()) {
+ String sim_lib_path = dest_dir + String(binary_name + ".xcframework").path_join("ios-arm64_x86_64-simulator").path_join("libgodot.a");
+ String dev_lib_path = dest_dir + String(binary_name + ".xcframework").path_join("ios-arm64").path_join("libgodot.a");
+ String tmp_lib_path = EditorPaths::get_singleton()->get_cache_dir().path_join(binary_name + "_lipo_");
+ uint32_t cputype = 0;
+ uint32_t cpusubtype = 0;
+ if (!_archive_has_arm64(sim_lib_path, &cputype, &cpusubtype) && _archive_has_arm64(dev_lib_path) && FileAccess::exists(dev_lib_path)) {
+ add_message(EXPORT_MESSAGE_INFO, TTR("Export"), TTR("ARM64 simulator library, generating from device library."));
+
+ Vector<String> tmp_lib_files;
+ Vector<Vector2i> tmp_lib_cputypes;
+ // Extract/copy simulator lib.
+ if (FileAccess::exists(sim_lib_path)) {
+ if (LipO::is_lipo(sim_lib_path)) {
+ Ref<LipO> lipo;
+ lipo.instantiate();
+ if (lipo->open_file(sim_lib_path)) {
+ for (int i = 0; i < lipo->get_arch_count(); i++) {
+ const String &f_name = tmp_lib_path + itos(tmp_lib_files.size());
+ lipo->extract_arch(i, f_name);
+ tmp_lib_files.push_back(f_name);
+ tmp_lib_cputypes.push_back(Vector2i(lipo->get_arch_cputype(i), lipo->get_arch_cpusubtype(i)));
+ }
+ }
+ } else {
+ const String &f_name = tmp_lib_path + itos(tmp_lib_files.size());
+ tmp_app_path->copy(sim_lib_path, f_name);
+ tmp_lib_files.push_back(f_name);
+ tmp_lib_cputypes.push_back(Vector2i(cputype, cpusubtype));
+ }
+ }
+ // Copy device lib.
+ if (LipO::is_lipo(dev_lib_path)) {
+ Ref<LipO> lipo;
+ lipo.instantiate();
+ if (lipo->open_file(dev_lib_path)) {
+ for (int i = 0; i < lipo->get_arch_count(); i++) {
+ if (lipo->get_arch_cputype(i) == 0x100000c && lipo->get_arch_cpusubtype(i) == 0) {
+ const String &f_name = tmp_lib_path + itos(tmp_lib_files.size());
+ lipo->extract_arch(i, f_name);
+ tmp_lib_files.push_back(f_name);
+ tmp_lib_cputypes.push_back(Vector2i(0x100000c, 0)); // ARM64.
+ break;
+ }
+ }
+ }
+ } else {
+ const String &f_name = tmp_lib_path + itos(tmp_lib_files.size());
+ tmp_app_path->copy(dev_lib_path, f_name);
+ tmp_lib_files.push_back(f_name);
+ tmp_lib_cputypes.push_back(Vector2i(0x100000c, 0)); // ARM64.
+ }
+
+ // Patch device lib.
+ int patch_count = _archive_convert_to_simulator(tmp_lib_path + itos(tmp_lib_files.size() - 1));
+ if (patch_count == 0) {
+ add_message(EXPORT_MESSAGE_WARNING, TTR("Export"), TTR("Unable to generate ARM64 simulator library."));
+ } else {
+ // Repack.
+ Ref<LipO> lipo;
+ lipo.instantiate();
+ lipo->create_file(sim_lib_path, tmp_lib_files, tmp_lib_cputypes);
+ }
+
+ // Cleanup.
+ for (const String &E : tmp_lib_files) {
+ tmp_app_path->remove(E);
+ }
+ }
+ }
+
+ // Generate translations files.
+
Dictionary appnames = GLOBAL_GET("application/config/name_localized");
Dictionary camera_usage_descriptions = p_preset->get("privacy/camera_usage_description_localized");
Dictionary microphone_usage_descriptions = p_preset->get("privacy/microphone_usage_description_localized");