diff --git a/platform/macos/export/plist.cpp b/core/io/plist.cpp similarity index 95% rename from platform/macos/export/plist.cpp rename to core/io/plist.cpp index f494c58fc97..86737609bf5 100644 --- a/platform/macos/export/plist.cpp +++ b/core/io/plist.cpp @@ -448,7 +448,9 @@ PList::PList() { } PList::PList(const String &p_string) { - load_string(p_string); + String err_str; + bool ok = load_string(p_string, err_str); + ERR_FAIL_COND_MSG(!ok, "PList: " + err_str); } uint64_t PList::read_bplist_var_size_int(Ref p_file, uint8_t p_size) { @@ -642,11 +644,15 @@ bool PList::load_file(const String &p_filename) { String ret; ret.parse_utf8((const char *)array.ptr(), array.size()); - return load_string(ret); + String err_str; + bool ok = load_string(ret, err_str); + ERR_FAIL_COND_V_MSG(!ok, false, "PList: " + err_str); + + return true; } } -bool PList::load_string(const String &p_string) { +bool PList::load_string(const String &p_string, String &r_err_out) { root = Ref(); int pos = 0; @@ -657,14 +663,16 @@ bool PList::load_string(const String &p_string) { 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."); + r_err_out = "Unexpected end of data. No tags found."; + return false; } 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.is_empty()) { - ERR_FAIL_V_MSG(false, "PList: Invalid token name."); + r_err_out = "Invalid token name."; + return false; } String value; if (token[0] == '?' || token[0] == '!') { // Skip and @@ -684,7 +692,8 @@ bool PList::load_string(const String &p_string) { } if (!in_plist) { - ERR_FAIL_V_MSG(false, "PList: Node outside of tag."); + r_err_out = "Node outside of tag."; + return false; } if (token == "dict") { @@ -693,13 +702,15 @@ bool PList::load_string(const String &p_string) { Ref 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."); + r_err_out = "Can't push subnode, invalid parent type."; + return false; } stack.push_back(dict); } else { // Add root node. if (!root.is_null()) { - ERR_FAIL_V_MSG(false, "PList: Root node already set."); + r_err_out = "Root node already set."; + return false; } Ref dict = PListNode::new_dict(); stack.push_back(dict); @@ -711,7 +722,8 @@ bool PList::load_string(const String &p_string) { if (token == "/dict") { // Exit current dict. if (stack.is_empty() || stack.back()->get()->data_type != PList::PLNodeType::PL_NODE_TYPE_DICT) { - ERR_FAIL_V_MSG(false, "PList: Mismatched tag."); + r_err_out = "Mismatched tag."; + return false; } stack.pop_back(); continue; @@ -722,13 +734,15 @@ bool PList::load_string(const String &p_string) { // Add subnode end enter it. Ref 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."); + r_err_out = "Can't push subnode, invalid parent type."; + return false; } stack.push_back(arr); } else { // Add root node. if (!root.is_null()) { - ERR_FAIL_V_MSG(false, "PList: Root node already set."); + r_err_out = "Root node already set."; + return false; } Ref arr = PListNode::new_array(); stack.push_back(arr); @@ -740,7 +754,8 @@ bool PList::load_string(const String &p_string) { if (token == "/array") { // Exit current array. if (stack.is_empty() || stack.back()->get()->data_type != PList::PLNodeType::PL_NODE_TYPE_ARRAY) { - ERR_FAIL_V_MSG(false, "PList: Mismatched tag."); + r_err_out = "Mismatched tag."; + return false; } stack.pop_back(); continue; @@ -751,13 +766,15 @@ bool PList::load_string(const String &p_string) { } else { int end_token_s = p_string.find(" tag.", token)); + r_err_out = vformat("Mismatched <%s> tag.", token); + return false; } 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> token pair.", token, end_token)); + r_err_out = vformat("Mismatched <%s> and <%s> token pair.", token, end_token); + return false; } value = p_string.substr(open_token_e + 1, end_token_s - open_token_e - 1); } @@ -780,15 +797,18 @@ bool PList::load_string(const String &p_string) { } else if (token == "date") { var = PListNode::new_date(value); } else { - ERR_FAIL_V_MSG(false, "PList: Invalid value type."); + r_err_out = vformat("Invalid value type: %s.", token); + return false; } if (stack.is_empty() || !stack.back()->get()->push_subnode(var, key)) { - ERR_FAIL_V_MSG(false, "PList: Can't push subnode, invalid parent type."); + r_err_out = "Can't push subnode, invalid parent type."; + return false; } } } if (!stack.is_empty() || !done_plist) { - ERR_FAIL_V_MSG(false, "PList: Unexpected end of data. Root node is not closed."); + r_err_out = "Unexpected end of data. Root node is not closed."; + return false; } return true; } diff --git a/platform/macos/export/plist.h b/core/io/plist.h similarity index 97% rename from platform/macos/export/plist.h rename to core/io/plist.h index 28b02e4eb7c..7d8b8ef0b42 100644 --- a/platform/macos/export/plist.h +++ b/core/io/plist.h @@ -28,8 +28,8 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /**************************************************************************/ -#ifndef MACOS_PLIST_H -#define MACOS_PLIST_H +#ifndef PLIST_H +#define PLIST_H // Property list file format (application/x-plist) parser, property list ASN-1 serialization. @@ -75,7 +75,7 @@ public: PList(const String &p_string); bool load_file(const String &p_filename); - bool load_string(const String &p_string); + bool load_string(const String &p_string, String &r_err_out); PackedByteArray save_asn1() const; String save_text() const; @@ -125,4 +125,4 @@ public: ~PListNode() {} }; -#endif // MACOS_PLIST_H +#endif // PLIST_H diff --git a/misc/dist/macos_template.app/Contents/Info.plist b/misc/dist/macos_template.app/Contents/Info.plist index 40966d2ceda..78bb559c0e4 100644 --- a/misc/dist/macos_template.app/Contents/Info.plist +++ b/misc/dist/macos_template.app/Contents/Info.plist @@ -58,5 +58,6 @@ $usage_descriptions NSHighResolutionCapable $highres +$additional_plist_content diff --git a/platform/ios/doc_classes/EditorExportPlatformIOS.xml b/platform/ios/doc_classes/EditorExportPlatformIOS.xml index 588b20669b6..35ef6d6a784 100644 --- a/platform/ios/doc_classes/EditorExportPlatformIOS.xml +++ b/platform/ios/doc_classes/EditorExportPlatformIOS.xml @@ -10,6 +10,13 @@ $DOCS_URL/tutorials/platform/ios/index.html + + Additional data added to the root [code]<dict>[/code] section of the [url=https://developer.apple.com/documentation/bundleresources/information_property_list]Info.plist[/url] file. The value should be an XML section with pairs of key-value elements, e.g.: + [codeblock] + <key>key_name</key> + <string>value</string> + [/codeblock] + Apple Team ID, unique 10-character string. To locate your Team ID check "Membership details" section in your Apple developer account dashboard, or "Organizational Unit" of your code signing certificate. See [url=https://developer.apple.com/help/account/manage-your-team/locate-your-team-id]Locate your Team ID[/url]. diff --git a/platform/ios/export/export_plugin.cpp b/platform/ios/export/export_plugin.cpp index 56fbfc31fc0..395cd5d760f 100644 --- a/platform/ios/export/export_plugin.cpp +++ b/platform/ios/export/export_plugin.cpp @@ -34,6 +34,7 @@ #include "run_icon_svg.gen.h" #include "core/io/json.h" +#include "core/io/plist.h" #include "core/string/translation.h" #include "editor/editor_node.h" #include "editor/editor_paths.h" @@ -154,6 +155,8 @@ void EditorExportPlatformIOS::get_export_options(List *r_options) r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/min_ios_version"), "12.0")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/additional_plist_content", PROPERTY_HINT_MULTILINE_TEXT), "")); + 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::BOOL, "application/export_project_only"), false)); @@ -1498,6 +1501,8 @@ Error EditorExportPlatformIOS::_export_project_helper(const Refget("application/additional_plist_content").operator String() + "\n"; + Vector assets; Ref tmp_app_path = DirAccess::create_for_path(dest_dir); @@ -1867,6 +1872,26 @@ bool EditorExportPlatformIOS::has_valid_export_configuration(const Refget("application/additional_plist_content"); + if (!additional_plist_content.is_empty()) { + const String &plist = vformat("\n" + "" + "" + "\n" + "%s\n" + "\n" + "\n", + additional_plist_content); + + String plist_err; + Ref plist_parser; + plist_parser.instantiate(); + if (!plist_parser->load_string(plist, plist_err)) { + err += TTR("Invalid additional PList content: ") + plist_err + "\n"; + valid = false; + } + } + if (!err.is_empty()) { r_error = err; } diff --git a/platform/macos/doc_classes/EditorExportPlatformMacOS.xml b/platform/macos/doc_classes/EditorExportPlatformMacOS.xml index 85fefe65c09..506b0dffb88 100644 --- a/platform/macos/doc_classes/EditorExportPlatformMacOS.xml +++ b/platform/macos/doc_classes/EditorExportPlatformMacOS.xml @@ -10,6 +10,13 @@ $DOCS_URL/tutorials//export/running_on_macos.html + + Additional data added to the root [code]<dict>[/code] section of the [url=https://developer.apple.com/documentation/bundleresources/information_property_list]Info.plist[/url] file. The value should be an XML section with pairs of key-value elements, e.g.: + [codeblock] + <key>key_name</key> + <string>value</string> + [/codeblock] + Application category for the App Store. diff --git a/platform/macos/export/codesign.cpp b/platform/macos/export/codesign.cpp index 2b8898e6a1b..082c0abea76 100644 --- a/platform/macos/export/codesign.cpp +++ b/platform/macos/export/codesign.cpp @@ -32,8 +32,8 @@ #include "lipo.h" #include "macho.h" -#include "plist.h" +#include "core/io/plist.h" #include "core/os/os.h" #include "editor/editor_paths.h" #include "editor/editor_settings.h" diff --git a/platform/macos/export/codesign.h b/platform/macos/export/codesign.h index 3e61751a960..49d53b376e7 100644 --- a/platform/macos/export/codesign.h +++ b/platform/macos/export/codesign.h @@ -41,11 +41,10 @@ // - 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. -#include "plist.h" - #include "core/crypto/crypto_core.h" #include "core/io/dir_access.h" #include "core/io/file_access.h" +#include "core/io/plist.h" #include "core/object/ref_counted.h" #include "modules/modules_enabled.gen.h" // For regex. diff --git a/platform/macos/export/export_plugin.cpp b/platform/macos/export/export_plugin.cpp index 6e750d0a16f..c3470820103 100644 --- a/platform/macos/export/export_plugin.cpp +++ b/platform/macos/export/export_plugin.cpp @@ -37,6 +37,7 @@ #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" @@ -388,6 +389,8 @@ void EditorExportPlatformMacOS::get_export_options(List *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")); @@ -672,6 +675,8 @@ void EditorExportPlatformMacOS::_fix_plist(const Ref &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" : "\t") + "\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) { @@ -2095,6 +2100,26 @@ bool EditorExportPlatformMacOS::has_valid_project_configuration(const Refget("application/additional_plist_content"); + if (!additional_plist_content.is_empty()) { + const String &plist = vformat("\n" + "" + "" + "\n" + "%s\n" + "\n" + "\n", + additional_plist_content); + + String plist_err; + Ref 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 options; get_export_options(&options); for (const EditorExportPlatform::ExportOption &E : options) {