diff options
Diffstat (limited to 'platform/ios/export/export_plugin.cpp')
-rw-r--r-- | platform/ios/export/export_plugin.cpp | 400 |
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"); |