summaryrefslogtreecommitdiffstats
path: root/platform/macos/export/export_plugin.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'platform/macos/export/export_plugin.cpp')
-rw-r--r--platform/macos/export/export_plugin.cpp211
1 files changed, 157 insertions, 54 deletions
diff --git a/platform/macos/export/export_plugin.cpp b/platform/macos/export/export_plugin.cpp
index 6e750d0a16..9cc57e4066 100644
--- a/platform/macos/export/export_plugin.cpp
+++ b/platform/macos/export/export_plugin.cpp
@@ -37,13 +37,14 @@
#include "run_icon_svg.gen.h"
#include "core/io/image_loader.h"
+#include "core/io/plist.h"
#include "core/string/translation.h"
#include "drivers/png/png_driver_common.h"
#include "editor/editor_node.h"
#include "editor/editor_paths.h"
-#include "editor/editor_scale.h"
#include "editor/editor_string_names.h"
#include "editor/import/resource_importer_texture_settings.h"
+#include "editor/themes/editor_scale.h"
#include "scene/resources/image_texture.h"
#include "modules/modules_enabled.gen.h" // For svg and regex.
@@ -353,6 +354,9 @@ List<String> EditorExportPlatformMacOS::get_binary_extensions(const Ref<EditorEx
list.push_back("dmg");
#endif
list.push_back("zip");
+#ifndef WINDOWS_ENABLED
+ list.push_back("app");
+#endif
} else if (dist_type == 2) {
#ifdef MACOS_ENABLED
list.push_back("pkg");
@@ -388,6 +392,8 @@ void EditorExportPlatformMacOS::get_export_options(List<ExportOption> *r_options
r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "application/export_angle", PROPERTY_HINT_ENUM, "Auto,Yes,No"), 0, true));
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "display/high_res"), true));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/additional_plist_content", PROPERTY_HINT_MULTILINE_TEXT), ""));
+
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "xcode/platform_build"), "14C18"));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "xcode/sdk_version"), "13.1"));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "xcode/sdk_build"), "22C55"));
@@ -494,65 +500,49 @@ void _rgba8_to_packbits_encode(int p_ch, int p_size, Vector<uint8_t> &p_source,
int src_len = p_size * p_size;
Vector<uint8_t> result;
- result.resize(src_len * 1.25); //temp vector for rle encoded data, make it 25% larger for worst case scenario
- int res_size = 0;
-
- uint8_t buf[128];
- int buf_size = 0;
int i = 0;
+ const uint8_t *src = p_source.ptr();
while (i < src_len) {
- uint8_t cur = p_source.ptr()[i * 4 + p_ch];
-
- if (i < src_len - 2) {
- if ((p_source.ptr()[(i + 1) * 4 + p_ch] == cur) && (p_source.ptr()[(i + 2) * 4 + p_ch] == cur)) {
- if (buf_size > 0) {
- result.write[res_size++] = (uint8_t)(buf_size - 1);
- memcpy(&result.write[res_size], &buf, buf_size);
- res_size += buf_size;
- buf_size = 0;
- }
-
- uint8_t lim = i + 130 >= src_len ? src_len - i - 1 : 130;
- bool hit_lim = true;
-
- for (int j = 3; j <= lim; j++) {
- if (p_source.ptr()[(i + j) * 4 + p_ch] != cur) {
- hit_lim = false;
- i = i + j - 1;
- result.write[res_size++] = (uint8_t)(j - 3 + 0x80);
- result.write[res_size++] = cur;
- break;
- }
- }
- if (hit_lim) {
- result.write[res_size++] = (uint8_t)(lim - 3 + 0x80);
- result.write[res_size++] = cur;
- i = i + lim;
- }
- } else {
- buf[buf_size++] = cur;
- if (buf_size == 128) {
- result.write[res_size++] = (uint8_t)(buf_size - 1);
- memcpy(&result.write[res_size], &buf, buf_size);
- res_size += buf_size;
- buf_size = 0;
- }
+ Vector<uint8_t> seq;
+
+ uint8_t count = 0;
+ while (count <= 0x7f && i < src_len) {
+ if (i + 2 < src_len && src[i * 4 + p_ch] == src[(i + 1) * 4 + p_ch] && src[i] == src[(i + 2) * 4 + p_ch]) {
+ break;
}
- } else {
- buf[buf_size++] = cur;
- result.write[res_size++] = (uint8_t)(buf_size - 1);
- memcpy(&result.write[res_size], &buf, buf_size);
- res_size += buf_size;
- buf_size = 0;
+ seq.push_back(src[i * 4 + p_ch]);
+ i++;
+ count++;
+ }
+ if (!seq.is_empty()) {
+ result.push_back(count - 1);
+ result.append_array(seq);
+ }
+ if (i >= src_len) {
+ break;
}
- i++;
+ uint8_t rep = src[i * 4 + p_ch];
+ count = 0;
+ while (count <= 0x7f && i < src_len && src[i * 4 + p_ch] == rep) {
+ i++;
+ count++;
+ }
+ if (count >= 3) {
+ result.push_back(0x80 + count - 3);
+ result.push_back(rep);
+ } else {
+ result.push_back(count - 1);
+ for (int j = 0; j < count; j++) {
+ result.push_back(rep);
+ }
+ }
}
int ofs = p_dest.size();
- p_dest.resize(p_dest.size() + res_size);
- memcpy(&p_dest.write[ofs], result.ptr(), res_size);
+ p_dest.resize(p_dest.size() + result.size());
+ memcpy(&p_dest.write[ofs], result.ptr(), result.size());
}
void EditorExportPlatformMacOS::_make_icon(const Ref<EditorExportPreset> &p_preset, const Ref<Image> &p_icon, Vector<uint8_t> &p_data) {
@@ -615,6 +605,9 @@ void EditorExportPlatformMacOS::_make_icon(const Ref<EditorExportPreset> &p_pres
_rgba8_to_packbits_encode(1, icon_infos[i].size, src_data, data); // Encode G.
_rgba8_to_packbits_encode(2, icon_infos[i].size, src_data, data); // Encode B.
+ // Note: workaround for macOS icon decoder bug corrupting last RLE encoded value.
+ data.push_back(0x00);
+
int len = data.size() - ofs;
len = BSWAP32(len);
memcpy(&data.write[ofs], icon_infos[i].name, 4);
@@ -672,6 +665,8 @@ void EditorExportPlatformMacOS::_fix_plist(const Ref<EditorExportPreset> &p_pres
strnew += lines[i].replace("$min_version", p_preset->get("application/min_macos_version")) + "\n";
} else if (lines[i].find("$highres") != -1) {
strnew += lines[i].replace("$highres", p_preset->get("display/high_res") ? "\t<true/>" : "\t<false/>") + "\n";
+ } else if (lines[i].find("$additional_plist_content") != -1) {
+ strnew += lines[i].replace("$additional_plist_content", p_preset->get("application/additional_plist_content")) + "\n";
} else if (lines[i].find("$platfbuild") != -1) {
strnew += lines[i].replace("$platfbuild", p_preset->get("xcode/platform_build")) + "\n";
} else if (lines[i].find("$sdkver") != -1) {
@@ -1111,10 +1106,73 @@ Error EditorExportPlatformMacOS::_copy_and_sign_files(Ref<DirAccess> &dir_access
add_message(EXPORT_MESSAGE_INFO, TTR("Export"), vformat(TTR("Relative symlinks are not supported, exported \"%s\" might be broken!"), p_src_path.get_file()));
#endif
print_verbose("export framework: " + p_src_path + " -> " + p_in_app_path);
+
+ bool plist_misssing = false;
+ Ref<PList> plist;
+ plist.instantiate();
+ plist->load_file(p_src_path.path_join("Resources").path_join("Info.plist"));
+
+ Ref<PListNode> root_node = plist->get_root();
+ if (root_node.is_null()) {
+ plist_misssing = true;
+ } else {
+ Dictionary root = root_node->get_value();
+ if (!root.has("CFBundleExecutable") || !root.has("CFBundleIdentifier") || !root.has("CFBundlePackageType") || !root.has("CFBundleInfoDictionaryVersion") || !root.has("CFBundleName") || !root.has("CFBundleSupportedPlatforms")) {
+ plist_misssing = true;
+ }
+ }
+
err = dir_access->make_dir_recursive(p_in_app_path);
if (err == OK) {
err = dir_access->copy_dir(p_src_path, p_in_app_path, -1, true);
}
+ if (err == OK && plist_misssing) {
+ add_message(EXPORT_MESSAGE_WARNING, TTR("Export"), vformat(TTR("\"%s\": Info.plist missing or invalid, new Info.plist generated."), p_src_path.get_file()));
+ // Generate Info.plist
+ String lib_name = p_src_path.get_basename().get_file();
+ String lib_id = p_preset->get("application/bundle_identifier");
+ String lib_clean_name = lib_name;
+ for (int i = 0; i < lib_clean_name.length(); i++) {
+ if (!is_ascii_alphanumeric_char(lib_clean_name[i]) && lib_clean_name[i] != '.' && lib_clean_name[i] != '-') {
+ lib_clean_name[i] = '-';
+ }
+ }
+
+ String info_plist_format = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
+ "<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n"
+ "<plist version=\"1.0\">\n"
+ " <dict>\n"
+ " <key>CFBundleExecutable</key>\n"
+ " <string>$name</string>\n"
+ " <key>CFBundleIdentifier</key>\n"
+ " <string>$id.framework.$cl_name</string>\n"
+ " <key>CFBundleInfoDictionaryVersion</key>\n"
+ " <string>6.0</string>\n"
+ " <key>CFBundleName</key>\n"
+ " <string>$name</string>\n"
+ " <key>CFBundlePackageType</key>\n"
+ " <string>FMWK</string>\n"
+ " <key>CFBundleShortVersionString</key>\n"
+ " <string>1.0.0</string>\n"
+ " <key>CFBundleSupportedPlatforms</key>\n"
+ " <array>\n"
+ " <string>MacOSX</string>\n"
+ " </array>\n"
+ " <key>CFBundleVersion</key>\n"
+ " <string>1.0.0</string>\n"
+ " <key>LSMinimumSystemVersion</key>\n"
+ " <string>10.12</string>\n"
+ " </dict>\n"
+ "</plist>";
+
+ String info_plist = info_plist_format.replace("$id", lib_id).replace("$name", lib_name).replace("$cl_name", lib_clean_name);
+
+ err = dir_access->make_dir_recursive(p_in_app_path.path_join("Resources"));
+ Ref<FileAccess> f = FileAccess::open(p_in_app_path.path_join("Resources").path_join("Info.plist"), FileAccess::WRITE);
+ if (f.is_valid()) {
+ f->store_string(info_plist);
+ }
+ }
} else {
print_verbose("export dylib: " + p_src_path + " -> " + p_in_app_path);
err = dir_access->copy(p_src_path, p_in_app_path);
@@ -1949,6 +2007,8 @@ Error EditorExportPlatformMacOS::export_project(const Ref<EditorExportPreset> &p
err = _code_sign(p_preset, tmp_app_path_name, ent_path);
}
+ String noto_path = p_path;
+ bool noto_enabled = (p_preset->get("notarization/notarization").operator int() > 0);
if (export_format == "dmg") {
// Create a DMG.
if (err == OK) {
@@ -1990,17 +2050,36 @@ Error EditorExportPlatformMacOS::export_project(const Ref<EditorExportPreset> &p
zipClose(zip, nullptr);
}
+ } else if (export_format == "app" && noto_enabled) {
+ // Create temporary ZIP.
+ if (err == OK) {
+ noto_path = EditorPaths::get_singleton()->get_cache_dir().path_join(pkg_name + ".zip");
+
+ if (ep.step(TTR("Making ZIP"), 3)) {
+ return ERR_SKIP;
+ }
+ if (FileAccess::exists(noto_path)) {
+ OS::get_singleton()->move_to_trash(noto_path);
+ }
+
+ Ref<FileAccess> io_fa_dst;
+ zlib_filefunc_def io_dst = zipio_create_io(&io_fa_dst);
+ zipFile zip = zipOpen2(noto_path.utf8().get_data(), APPEND_STATUS_CREATE, nullptr, &io_dst);
+
+ zip_folder_recursive(zip, tmp_base_path_name, tmp_app_dir_name, pkg_name);
+
+ zipClose(zip, nullptr);
+ }
}
- bool noto_enabled = (p_preset->get("notarization/notarization").operator int() > 0);
if (err == OK && noto_enabled) {
- if (export_format == "app" || export_format == "pkg") {
+ if (export_format == "pkg") {
add_message(EXPORT_MESSAGE_INFO, TTR("Notarization"), TTR("Notarization requires the app to be archived first, select the DMG or ZIP export format instead."));
} else {
if (ep.step(TTR("Sending archive for notarization"), 4)) {
return ERR_SKIP;
}
- err = _notarize(p_preset, p_path);
+ err = _notarize(p_preset, noto_path);
}
}
@@ -2019,6 +2098,10 @@ Error EditorExportPlatformMacOS::export_project(const Ref<EditorExportPreset> &p
tmp_app_dir->change_dir("..");
tmp_app_dir->remove(pkg_name);
}
+ } else if (noto_path != p_path) {
+ if (FileAccess::exists(noto_path)) {
+ DirAccess::remove_file_or_error(noto_path);
+ }
}
}
@@ -2095,6 +2178,26 @@ bool EditorExportPlatformMacOS::has_valid_project_configuration(const Ref<Editor
};
}
+ const String &additional_plist_content = p_preset->get("application/additional_plist_content");
+ if (!additional_plist_content.is_empty()) {
+ const String &plist = vformat("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
+ "<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">"
+ "<plist version=\"1.0\">"
+ "<dict>\n"
+ "%s\n"
+ "</dict>\n"
+ "</plist>\n",
+ additional_plist_content);
+
+ String plist_err;
+ Ref<PList> plist_parser;
+ plist_parser.instantiate();
+ if (!plist_parser->load_string(plist, plist_err)) {
+ err += TTR("Invalid additional PList content: ") + plist_err + "\n";
+ valid = false;
+ }
+ }
+
List<ExportOption> options;
get_export_options(&options);
for (const EditorExportPlatform::ExportOption &E : options) {