diff options
Diffstat (limited to 'platform/ios')
-rw-r--r-- | platform/ios/display_server_ios.mm | 9 | ||||
-rw-r--r-- | platform/ios/doc_classes/EditorExportPlatformIOS.xml | 3 | ||||
-rw-r--r-- | platform/ios/export/export_plugin.cpp | 242 | ||||
-rw-r--r-- | platform/ios/export/export_plugin.h | 2 | ||||
-rw-r--r-- | platform/ios/export/logo.svg | 2 | ||||
-rw-r--r-- | platform/ios/export/run_icon.svg | 2 |
6 files changed, 255 insertions, 5 deletions
diff --git a/platform/ios/display_server_ios.mm b/platform/ios/display_server_ios.mm index a454dd5ba0..802fbefc0d 100644 --- a/platform/ios/display_server_ios.mm +++ b/platform/ios/display_server_ios.mm @@ -91,6 +91,7 @@ DisplayServerIOS::DisplayServerIOS(const String &p_rendering_driver, WindowMode ERR_PRINT(vformat("Failed to initialize %s context", rendering_driver)); memdelete(rendering_context); rendering_context = nullptr; + r_error = ERR_UNAVAILABLE; return; } @@ -107,7 +108,13 @@ DisplayServerIOS::DisplayServerIOS(const String &p_rendering_driver, WindowMode rendering_context->window_set_vsync_mode(MAIN_WINDOW_ID, p_vsync_mode); rendering_device = memnew(RenderingDevice); - rendering_device->initialize(rendering_context, MAIN_WINDOW_ID); + if (rendering_device->initialize(rendering_context, MAIN_WINDOW_ID) != OK) { + rendering_device = nullptr; + memdelete(rendering_context); + rendering_context = nullptr; + r_error = ERR_UNAVAILABLE; + return; + } rendering_device->screen_create(MAIN_WINDOW_ID); RendererCompositorRD::make_current(); diff --git a/platform/ios/doc_classes/EditorExportPlatformIOS.xml b/platform/ios/doc_classes/EditorExportPlatformIOS.xml index 20c1647843..87994ef22b 100644 --- a/platform/ios/doc_classes/EditorExportPlatformIOS.xml +++ b/platform/ios/doc_classes/EditorExportPlatformIOS.xml @@ -41,6 +41,9 @@ <member name="application/export_project_only" type="bool" setter="" getter=""> If [code]true[/code], exports iOS project files without building an XCArchive or [code].ipa[/code] file. If [code]false[/code], exports iOS project files and builds an XCArchive and [code].ipa[/code] file at the same time. When combining Godot with Fastlane or other build pipelines, you may want to set this to [code]true[/code]. </member> + <member name="application/generate_simulator_library_if_missing" type="bool" setter="" getter=""> + If [code]true[/code], and ARM64 simulator library is missing from the export template, it is automatically generated from ARM64 device library. + </member> <member name="application/icon_interpolation" type="int" setter="" getter=""> Interpolation method used to resize application icon. </member> diff --git a/platform/ios/export/export_plugin.cpp b/platform/ios/export/export_plugin.cpp index 2194804b3b..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++) { @@ -1150,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(); @@ -2146,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) { @@ -2154,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"); diff --git a/platform/ios/export/export_plugin.h b/platform/ios/export/export_plugin.h index 197f27da76..1964906c27 100644 --- a/platform/ios/export/export_plugin.h +++ b/platform/ios/export/export_plugin.h @@ -135,6 +135,8 @@ class EditorExportPlatformIOS : public EditorExportPlatform { Vector<ExportArchitecture> _get_supported_architectures() const; Vector<String> _get_preset_architectures(const Ref<EditorExportPreset> &p_preset) const; + bool _archive_has_arm64(const String &p_path, uint32_t *r_cputype = nullptr, uint32_t *r_cpusubtype = nullptr) const; + int _archive_convert_to_simulator(const String &p_path) const; void _check_xcframework_content(const String &p_path, int &r_total_libs, int &r_static_libs, int &r_dylibs, int &r_frameworks) const; Error _convert_to_framework(const String &p_source, const String &p_destination, const String &p_id) const; diff --git a/platform/ios/export/logo.svg b/platform/ios/export/logo.svg index 47a72bcf49..5897f9f60f 100644 --- a/platform/ios/export/logo.svg +++ b/platform/ios/export/logo.svg @@ -1 +1 @@ -<svg height="32" width="32" xmlns="http://www.w3.org/2000/svg"><path d="M1 23.27h2.504V12.61H1zm1.247-12.057c.784 0 1.398-.603 1.398-1.358 0-.764-.614-1.367-1.398-1.367-.774 0-1.388.603-1.388 1.367 0 .755.614 1.358 1.388 1.358zm9.594-2.695c-4.233 0-6.888 2.886-6.888 7.502s2.654 7.492 6.888 7.492c4.224 0 6.88-2.876 6.88-7.492s-2.656-7.502-6.88-7.502zm0 2.212c2.585 0 4.234 2.052 4.234 5.29 0 3.228-1.649 5.28-4.234 5.28-2.594 0-4.233-2.052-4.233-5.28 0-3.238 1.639-5.29 4.233-5.29zm7.936 8.458c.11 2.675 2.303 4.324 5.641 4.324 3.51 0 5.723-1.73 5.723-4.485 0-2.162-1.247-3.379-4.194-4.053l-1.67-.382c-1.78-.422-2.513-.985-2.513-1.95 0-1.208 1.106-2.012 2.745-2.012 1.66 0 2.796.814 2.916 2.172H30.9c-.06-2.554-2.172-4.284-5.37-4.284-3.158 0-5.4 1.74-5.4 4.314 0 2.072 1.267 3.36 3.942 3.973l1.88.442c1.83.433 2.575 1.036 2.575 2.082 0 1.207-1.217 2.072-2.967 2.072-1.77 0-3.107-.875-3.268-2.213h-2.514z" fill="#bfbfbf"/></svg> +<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32"><path fill="#bfbfbf" d="M1 23.27h2.504V12.61H1zm1.247-12.057c.784 0 1.398-.603 1.398-1.358 0-.764-.614-1.367-1.398-1.367-.774 0-1.388.603-1.388 1.367 0 .755.614 1.358 1.388 1.358zm9.594-2.695c-4.233 0-6.888 2.886-6.888 7.502s2.654 7.492 6.888 7.492c4.224 0 6.88-2.876 6.88-7.492s-2.656-7.502-6.88-7.502zm0 2.212c2.585 0 4.234 2.052 4.234 5.29 0 3.228-1.649 5.28-4.234 5.28-2.594 0-4.233-2.052-4.233-5.28 0-3.238 1.639-5.29 4.233-5.29zm7.936 8.458c.11 2.675 2.303 4.324 5.641 4.324 3.51 0 5.723-1.73 5.723-4.485 0-2.162-1.247-3.379-4.194-4.053l-1.67-.382c-1.78-.422-2.513-.985-2.513-1.95 0-1.208 1.106-2.012 2.745-2.012 1.66 0 2.796.814 2.916 2.172H30.9c-.06-2.554-2.172-4.284-5.37-4.284-3.158 0-5.4 1.74-5.4 4.314 0 2.072 1.267 3.36 3.942 3.973l1.88.442c1.83.433 2.575 1.036 2.575 2.082 0 1.207-1.217 2.072-2.967 2.072-1.77 0-3.107-.875-3.268-2.213h-2.514z"/></svg>
\ No newline at end of file diff --git a/platform/ios/export/run_icon.svg b/platform/ios/export/run_icon.svg index 859c58409e..9a9460bf49 100644 --- a/platform/ios/export/run_icon.svg +++ b/platform/ios/export/run_icon.svg @@ -1 +1 @@ -<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><path fill="#bfbfbf" d="M.462 11.653H1.72V6.296H.462Zm.627-6.059a.687.687 0 0 0 .702-.682.688.688 0 0 0-.702-.687.687.687 0 0 0-.698.687c0 .38.309.682.698.682zM5.91 4.24c-2.127 0-3.461 1.45-3.461 3.77 0 2.32 1.333 3.765 3.461 3.765 2.123 0 3.457-1.445 3.457-3.765 0-2.32-1.334-3.77-3.457-3.77zm0 1.112c1.299 0 2.128 1.03 2.128 2.658 0 1.622-.829 2.653-2.128 2.653-1.304 0-2.127-1.03-2.127-2.653 0-1.627.823-2.658 2.127-2.658zm3.988 4.25c.055 1.344 1.157 2.173 2.835 2.173 1.764 0 2.876-.87 2.876-2.254 0-1.086-.627-1.698-2.108-2.037l-.839-.192c-.895-.212-1.263-.495-1.263-.98 0-.607.556-1.01 1.38-1.01.834 0 1.405.408 1.465 1.09h1.244c-.03-1.283-1.092-2.152-2.699-2.152-1.587 0-2.714.874-2.714 2.168 0 1.041.637 1.688 1.981 1.997l.945.222c.92.217 1.294.52 1.294 1.046 0 .606-.611 1.041-1.49 1.041-.89 0-1.562-.44-1.643-1.112H9.899Z" style="stroke-width:.502532;fill:#fff"/></svg> +<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><path fill="#fff" d="M.462 11.653H1.72V6.296H.462Zm.627-6.059a.687.687 0 0 0 .702-.682.688.688 0 0 0-.702-.687.687.687 0 0 0-.698.687c0 .38.309.682.698.682zM5.91 4.24c-2.127 0-3.461 1.45-3.461 3.77 0 2.32 1.333 3.765 3.461 3.765 2.123 0 3.457-1.445 3.457-3.765 0-2.32-1.334-3.77-3.457-3.77zm0 1.112c1.299 0 2.128 1.03 2.128 2.658 0 1.622-.829 2.653-2.128 2.653-1.304 0-2.127-1.03-2.127-2.653 0-1.627.823-2.658 2.127-2.658zm3.988 4.25c.055 1.344 1.157 2.173 2.835 2.173 1.764 0 2.876-.87 2.876-2.254 0-1.086-.627-1.698-2.108-2.037l-.839-.192c-.895-.212-1.263-.495-1.263-.98 0-.607.556-1.01 1.38-1.01.834 0 1.405.408 1.465 1.09h1.244c-.03-1.283-1.092-2.152-2.699-2.152-1.587 0-2.714.874-2.714 2.168 0 1.041.637 1.688 1.981 1.997l.945.222c.92.217 1.294.52 1.294 1.046 0 .606-.611 1.041-1.49 1.041-.89 0-1.562-.44-1.643-1.112H9.899Z"/></svg>
\ No newline at end of file |