summaryrefslogtreecommitdiff
path: root/platform
diff options
context:
space:
mode:
Diffstat (limited to 'platform')
-rw-r--r--platform/android/export/export.cpp2827
-rw-r--r--platform/android/export/gradle_export_util.h45
-rw-r--r--platform/iphone/export/export.cpp1923
-rw-r--r--platform/iphone/platform_config.h3
-rw-r--r--platform/javascript/export/export.cpp672
-rw-r--r--platform/linuxbsd/SCsub4
-rw-r--r--platform/linuxbsd/detect.py4
-rw-r--r--platform/linuxbsd/detect_prime_x11.cpp2
-rw-r--r--platform/linuxbsd/display_server_x11.cpp127
-rw-r--r--platform/linuxbsd/display_server_x11.h15
-rw-r--r--platform/linuxbsd/gl_manager_x11.cpp (renamed from platform/linuxbsd/context_gl_x11.cpp)251
-rw-r--r--platform/linuxbsd/gl_manager_x11.h (renamed from platform/linuxbsd/context_gl_x11.h)83
-rw-r--r--platform/linuxbsd/platform_config.h3
-rw-r--r--platform/osx/platform_config.h2
-rw-r--r--platform/windows/SCsub2
-rw-r--r--platform/windows/detect.py3
-rw-r--r--platform/windows/display_server_windows.cpp80
-rw-r--r--platform/windows/display_server_windows.h13
-rw-r--r--platform/windows/gl_manager_windows.cpp353
-rw-r--r--platform/windows/gl_manager_windows.h (renamed from platform/windows/context_gl_windows.h)94
-rw-r--r--platform/windows/os_windows.h4
-rw-r--r--platform/windows/platform_config.h3
22 files changed, 6360 insertions, 153 deletions
diff --git a/platform/android/export/export.cpp b/platform/android/export/export.cpp
index 8df61831c2..e01ad004a0 100644
--- a/platform/android/export/export.cpp
+++ b/platform/android/export/export.cpp
@@ -31,6 +31,2833 @@
#include "export.h"
#include "export_plugin.h"
+//#include "core/config/project_settings.h"
+//#include "core/io/image_loader.h"
+//#include "core/io/marshalls.h"
+//#include "core/io/zip_io.h"
+//#include "core/os/dir_access.h"
+//#include "core/os/file_access.h"
+//#include "core/os/os.h"
+//#include "core/version.h"
+//#include "drivers/png/png_driver_common.h"
+//#include "editor/editor_export.h"
+//#include "editor/editor_log.h"
+//#include "editor/editor_node.h"
+//#include "editor/editor_settings.h"
+//#include "main/splash.gen.h"
+//#include "platform/android/export/gradle_export_util.h"
+//#include "platform/android/logo.gen.h"
+//#include "platform/android/plugin/godot_plugin_config.h"
+//#include "platform/android/run_icon.gen.h"
+
+//#include <string.h>
+
+//static const char *android_perms[] = {
+// "ACCESS_CHECKIN_PROPERTIES",
+// "ACCESS_COARSE_LOCATION",
+// "ACCESS_FINE_LOCATION",
+// "ACCESS_LOCATION_EXTRA_COMMANDS",
+// "ACCESS_MOCK_LOCATION",
+// "ACCESS_NETWORK_STATE",
+// "ACCESS_SURFACE_FLINGER",
+// "ACCESS_WIFI_STATE",
+// "ACCOUNT_MANAGER",
+// "ADD_VOICEMAIL",
+// "AUTHENTICATE_ACCOUNTS",
+// "BATTERY_STATS",
+// "BIND_ACCESSIBILITY_SERVICE",
+// "BIND_APPWIDGET",
+// "BIND_DEVICE_ADMIN",
+// "BIND_INPUT_METHOD",
+// "BIND_NFC_SERVICE",
+// "BIND_NOTIFICATION_LISTENER_SERVICE",
+// "BIND_PRINT_SERVICE",
+// "BIND_REMOTEVIEWS",
+// "BIND_TEXT_SERVICE",
+// "BIND_VPN_SERVICE",
+// "BIND_WALLPAPER",
+// "BLUETOOTH",
+// "BLUETOOTH_ADMIN",
+// "BLUETOOTH_PRIVILEGED",
+// "BRICK",
+// "BROADCAST_PACKAGE_REMOVED",
+// "BROADCAST_SMS",
+// "BROADCAST_STICKY",
+// "BROADCAST_WAP_PUSH",
+// "CALL_PHONE",
+// "CALL_PRIVILEGED",
+// "CAMERA",
+// "CAPTURE_AUDIO_OUTPUT",
+// "CAPTURE_SECURE_VIDEO_OUTPUT",
+// "CAPTURE_VIDEO_OUTPUT",
+// "CHANGE_COMPONENT_ENABLED_STATE",
+// "CHANGE_CONFIGURATION",
+// "CHANGE_NETWORK_STATE",
+// "CHANGE_WIFI_MULTICAST_STATE",
+// "CHANGE_WIFI_STATE",
+// "CLEAR_APP_CACHE",
+// "CLEAR_APP_USER_DATA",
+// "CONTROL_LOCATION_UPDATES",
+// "DELETE_CACHE_FILES",
+// "DELETE_PACKAGES",
+// "DEVICE_POWER",
+// "DIAGNOSTIC",
+// "DISABLE_KEYGUARD",
+// "DUMP",
+// "EXPAND_STATUS_BAR",
+// "FACTORY_TEST",
+// "FLASHLIGHT",
+// "FORCE_BACK",
+// "GET_ACCOUNTS",
+// "GET_PACKAGE_SIZE",
+// "GET_TASKS",
+// "GET_TOP_ACTIVITY_INFO",
+// "GLOBAL_SEARCH",
+// "HARDWARE_TEST",
+// "INJECT_EVENTS",
+// "INSTALL_LOCATION_PROVIDER",
+// "INSTALL_PACKAGES",
+// "INSTALL_SHORTCUT",
+// "INTERNAL_SYSTEM_WINDOW",
+// "INTERNET",
+// "KILL_BACKGROUND_PROCESSES",
+// "LOCATION_HARDWARE",
+// "MANAGE_ACCOUNTS",
+// "MANAGE_APP_TOKENS",
+// "MANAGE_DOCUMENTS",
+// "MASTER_CLEAR",
+// "MEDIA_CONTENT_CONTROL",
+// "MODIFY_AUDIO_SETTINGS",
+// "MODIFY_PHONE_STATE",
+// "MOUNT_FORMAT_FILESYSTEMS",
+// "MOUNT_UNMOUNT_FILESYSTEMS",
+// "NFC",
+// "PERSISTENT_ACTIVITY",
+// "PROCESS_OUTGOING_CALLS",
+// "READ_CALENDAR",
+// "READ_CALL_LOG",
+// "READ_CONTACTS",
+// "READ_EXTERNAL_STORAGE",
+// "READ_FRAME_BUFFER",
+// "READ_HISTORY_BOOKMARKS",
+// "READ_INPUT_STATE",
+// "READ_LOGS",
+// "READ_PHONE_STATE",
+// "READ_PROFILE",
+// "READ_SMS",
+// "READ_SOCIAL_STREAM",
+// "READ_SYNC_SETTINGS",
+// "READ_SYNC_STATS",
+// "READ_USER_DICTIONARY",
+// "REBOOT",
+// "RECEIVE_BOOT_COMPLETED",
+// "RECEIVE_MMS",
+// "RECEIVE_SMS",
+// "RECEIVE_WAP_PUSH",
+// "RECORD_AUDIO",
+// "REORDER_TASKS",
+// "RESTART_PACKAGES",
+// "SEND_RESPOND_VIA_MESSAGE",
+// "SEND_SMS",
+// "SET_ACTIVITY_WATCHER",
+// "SET_ALARM",
+// "SET_ALWAYS_FINISH",
+// "SET_ANIMATION_SCALE",
+// "SET_DEBUG_APP",
+// "SET_ORIENTATION",
+// "SET_POINTER_SPEED",
+// "SET_PREFERRED_APPLICATIONS",
+// "SET_PROCESS_LIMIT",
+// "SET_TIME",
+// "SET_TIME_ZONE",
+// "SET_WALLPAPER",
+// "SET_WALLPAPER_HINTS",
+// "SIGNAL_PERSISTENT_PROCESSES",
+// "STATUS_BAR",
+// "SUBSCRIBED_FEEDS_READ",
+// "SUBSCRIBED_FEEDS_WRITE",
+// "SYSTEM_ALERT_WINDOW",
+// "TRANSMIT_IR",
+// "UNINSTALL_SHORTCUT",
+// "UPDATE_DEVICE_STATS",
+// "USE_CREDENTIALS",
+// "USE_SIP",
+// "VIBRATE",
+// "WAKE_LOCK",
+// "WRITE_APN_SETTINGS",
+// "WRITE_CALENDAR",
+// "WRITE_CALL_LOG",
+// "WRITE_CONTACTS",
+// "WRITE_EXTERNAL_STORAGE",
+// "WRITE_GSERVICES",
+// "WRITE_HISTORY_BOOKMARKS",
+// "WRITE_PROFILE",
+// "WRITE_SECURE_SETTINGS",
+// "WRITE_SETTINGS",
+// "WRITE_SMS",
+// "WRITE_SOCIAL_STREAM",
+// "WRITE_SYNC_SETTINGS",
+// "WRITE_USER_DICTIONARY",
+// nullptr
+//};
+
+//static const char *SPLASH_IMAGE_EXPORT_PATH = "res/drawable/splash.png";
+//static const char *SPLASH_BG_COLOR_PATH = "res/drawable/splash_bg_color.png";
+
+//struct LauncherIcon {
+// const char *export_path;
+// int dimensions = 0;
+//};
+
+//static const int icon_densities_count = 6;
+//static const char *launcher_icon_option = "launcher_icons/main_192x192";
+//static const char *launcher_adaptive_icon_foreground_option = "launcher_icons/adaptive_foreground_432x432";
+//static const char *launcher_adaptive_icon_background_option = "launcher_icons/adaptive_background_432x432";
+
+//static const LauncherIcon launcher_icons[icon_densities_count] = {
+// { "res/mipmap-xxxhdpi-v4/icon.png", 192 },
+// { "res/mipmap-xxhdpi-v4/icon.png", 144 },
+// { "res/mipmap-xhdpi-v4/icon.png", 96 },
+// { "res/mipmap-hdpi-v4/icon.png", 72 },
+// { "res/mipmap-mdpi-v4/icon.png", 48 },
+// { "res/mipmap/icon.png", 192 }
+//};
+
+//static const LauncherIcon launcher_adaptive_icon_foregrounds[icon_densities_count] = {
+// { "res/mipmap-xxxhdpi-v4/icon_foreground.png", 432 },
+// { "res/mipmap-xxhdpi-v4/icon_foreground.png", 324 },
+// { "res/mipmap-xhdpi-v4/icon_foreground.png", 216 },
+// { "res/mipmap-hdpi-v4/icon_foreground.png", 162 },
+// { "res/mipmap-mdpi-v4/icon_foreground.png", 108 },
+// { "res/mipmap/icon_foreground.png", 432 }
+//};
+
+//static const LauncherIcon launcher_adaptive_icon_backgrounds[icon_densities_count] = {
+// { "res/mipmap-xxxhdpi-v4/icon_background.png", 432 },
+// { "res/mipmap-xxhdpi-v4/icon_background.png", 324 },
+// { "res/mipmap-xhdpi-v4/icon_background.png", 216 },
+// { "res/mipmap-hdpi-v4/icon_background.png", 162 },
+// { "res/mipmap-mdpi-v4/icon_background.png", 108 },
+// { "res/mipmap/icon_background.png", 432 }
+//};
+
+//static const int EXPORT_FORMAT_APK = 0;
+//static const int EXPORT_FORMAT_AAB = 1;
+
+//class EditorExportPlatformAndroid : public EditorExportPlatform {
+// GDCLASS(EditorExportPlatformAndroid, EditorExportPlatform);
+
+// Ref<ImageTexture> logo;
+// Ref<ImageTexture> run_icon;
+
+// struct Device {
+// String id;
+// String name;
+// String description;
+// int api_level = 0;
+// };
+
+// struct APKExportData {
+// zipFile apk;
+// EditorProgress *ep = nullptr;
+// };
+
+// Vector<PluginConfig> plugins;
+// String last_plugin_names;
+// uint64_t last_custom_build_time = 0;
+// volatile bool plugins_changed;
+// Mutex plugins_lock;
+// Vector<Device> devices;
+// volatile bool devices_changed;
+// Mutex device_lock;
+// Thread *check_for_changes_thread;
+// volatile bool quit_request;
+
+// static void _check_for_changes_poll_thread(void *ud) {
+// EditorExportPlatformAndroid *ea = (EditorExportPlatformAndroid *)ud;
+
+// while (!ea->quit_request) {
+// // Check for plugins updates
+// {
+// // Nothing to do if we already know the plugins have changed.
+// if (!ea->plugins_changed) {
+// Vector<PluginConfig> loaded_plugins = get_plugins();
+
+// MutexLock lock(ea->plugins_lock);
+
+// if (ea->plugins.size() != loaded_plugins.size()) {
+// ea->plugins_changed = true;
+// } else {
+// for (int i = 0; i < ea->plugins.size(); i++) {
+// if (ea->plugins[i].name != loaded_plugins[i].name) {
+// ea->plugins_changed = true;
+// break;
+// }
+// }
+// }
+
+// if (ea->plugins_changed) {
+// ea->plugins = loaded_plugins;
+// }
+// }
+// }
+
+// // Check for devices updates
+// String adb = get_adb_path();
+// if (FileAccess::exists(adb)) {
+// String devices;
+// List<String> args;
+// args.push_back("devices");
+// int ec;
+// OS::get_singleton()->execute(adb, args, true, nullptr, &devices, &ec);
+
+// Vector<String> ds = devices.split("\n");
+// Vector<String> ldevices;
+// for (int i = 1; i < ds.size(); i++) {
+// String d = ds[i];
+// int dpos = d.find("device");
+// if (dpos == -1) {
+// continue;
+// }
+// d = d.substr(0, dpos).strip_edges();
+// ldevices.push_back(d);
+// }
+
+// MutexLock lock(ea->device_lock);
+
+// bool different = false;
+
+// if (ea->devices.size() != ldevices.size()) {
+// different = true;
+// } else {
+// for (int i = 0; i < ea->devices.size(); i++) {
+// if (ea->devices[i].id != ldevices[i]) {
+// different = true;
+// break;
+// }
+// }
+// }
+
+// if (different) {
+// Vector<Device> ndevices;
+
+// for (int i = 0; i < ldevices.size(); i++) {
+// Device d;
+// d.id = ldevices[i];
+// for (int j = 0; j < ea->devices.size(); j++) {
+// if (ea->devices[j].id == ldevices[i]) {
+// d.description = ea->devices[j].description;
+// d.name = ea->devices[j].name;
+// d.api_level = ea->devices[j].api_level;
+// }
+// }
+
+// if (d.description == "") {
+// //in the oven, request!
+// args.clear();
+// args.push_back("-s");
+// args.push_back(d.id);
+// args.push_back("shell");
+// args.push_back("getprop");
+// int ec2;
+// String dp;
+
+// OS::get_singleton()->execute(adb, args, true, nullptr, &dp, &ec2);
+
+// Vector<String> props = dp.split("\n");
+// String vendor;
+// String device;
+// d.description = "Device ID: " + d.id + "\n";
+// d.api_level = 0;
+// for (int j = 0; j < props.size(); j++) {
+// // got information by `shell cat /system/build.prop` before and its format is "property=value"
+// // it's now changed to use `shell getporp` because of permission issue with Android 8.0 and above
+// // its format is "[property]: [value]" so changed it as like build.prop
+// String p = props[j];
+// p = p.replace("]: ", "=");
+// p = p.replace("[", "");
+// p = p.replace("]", "");
+
+// if (p.begins_with("ro.product.model=")) {
+// device = p.get_slice("=", 1).strip_edges();
+// } else if (p.begins_with("ro.product.brand=")) {
+// vendor = p.get_slice("=", 1).strip_edges().capitalize();
+// } else if (p.begins_with("ro.build.display.id=")) {
+// d.description += "Build: " + p.get_slice("=", 1).strip_edges() + "\n";
+// } else if (p.begins_with("ro.build.version.release=")) {
+// d.description += "Release: " + p.get_slice("=", 1).strip_edges() + "\n";
+// } else if (p.begins_with("ro.build.version.sdk=")) {
+// d.api_level = p.get_slice("=", 1).to_int();
+// } else if (p.begins_with("ro.product.cpu.abi=")) {
+// d.description += "CPU: " + p.get_slice("=", 1).strip_edges() + "\n";
+// } else if (p.begins_with("ro.product.manufacturer=")) {
+// d.description += "Manufacturer: " + p.get_slice("=", 1).strip_edges() + "\n";
+// } else if (p.begins_with("ro.board.platform=")) {
+// d.description += "Chipset: " + p.get_slice("=", 1).strip_edges() + "\n";
+// } else if (p.begins_with("ro.opengles.version=")) {
+// uint32_t opengl = p.get_slice("=", 1).to_int();
+// d.description += "OpenGL: " + itos(opengl >> 16) + "." + itos((opengl >> 8) & 0xFF) + "." + itos((opengl)&0xFF) + "\n";
+// }
+// }
+
+// d.name = vendor + " " + device;
+// if (device == String()) {
+// continue;
+// }
+// }
+
+// ndevices.push_back(d);
+// }
+
+// ea->devices = ndevices;
+// ea->devices_changed = true;
+// }
+// }
+
+// uint64_t sleep = 200;
+// uint64_t wait = 3000000;
+// uint64_t time = OS::get_singleton()->get_ticks_usec();
+// while (OS::get_singleton()->get_ticks_usec() - time < wait) {
+// OS::get_singleton()->delay_usec(1000 * sleep);
+// if (ea->quit_request) {
+// break;
+// }
+// }
+// }
+
+// if (EditorSettings::get_singleton()->get("export/android/shutdown_adb_on_exit")) {
+// String adb = get_adb_path();
+// if (!FileAccess::exists(adb)) {
+// return; //adb not configured
+// }
+
+// List<String> args;
+// args.push_back("kill-server");
+// OS::get_singleton()->execute(adb, args, true);
+// };
+// }
+
+// String get_project_name(const String &p_name) const {
+// String aname;
+// if (p_name != "") {
+// aname = p_name;
+// } else {
+// aname = ProjectSettings::get_singleton()->get("application/config/name");
+// }
+
+// if (aname == "") {
+// aname = VERSION_NAME;
+// }
+
+// return aname;
+// }
+
+// String get_package_name(const String &p_package) const {
+// String pname = p_package;
+// String basename = ProjectSettings::get_singleton()->get("application/config/name");
+// basename = basename.to_lower();
+
+// String name;
+// bool first = true;
+// for (int i = 0; i < basename.length(); i++) {
+// char32_t c = basename[i];
+// if (c >= '0' && c <= '9' && first) {
+// continue;
+// }
+// if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9')) {
+// name += String::chr(c);
+// first = false;
+// }
+// }
+// if (name == "") {
+// name = "noname";
+// }
+
+// pname = pname.replace("$genname", name);
+
+// return pname;
+// }
+
+// bool is_package_name_valid(const String &p_package, String *r_error = nullptr) const {
+// String pname = p_package;
+
+// if (pname.length() == 0) {
+// if (r_error) {
+// *r_error = TTR("Package name is missing.");
+// }
+// return false;
+// }
+
+// int segments = 0;
+// bool first = true;
+// for (int i = 0; i < pname.length(); i++) {
+// char32_t c = pname[i];
+// if (first && c == '.') {
+// if (r_error) {
+// *r_error = TTR("Package segments must be of non-zero length.");
+// }
+// return false;
+// }
+// if (c == '.') {
+// segments++;
+// first = true;
+// continue;
+// }
+// if (!((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_')) {
+// if (r_error) {
+// *r_error = vformat(TTR("The character '%s' is not allowed in Android application package names."), String::chr(c));
+// }
+// return false;
+// }
+// if (first && (c >= '0' && c <= '9')) {
+// if (r_error) {
+// *r_error = TTR("A digit cannot be the first character in a package segment.");
+// }
+// return false;
+// }
+// if (first && c == '_') {
+// if (r_error) {
+// *r_error = vformat(TTR("The character '%s' cannot be the first character in a package segment."), String::chr(c));
+// }
+// return false;
+// }
+// first = false;
+// }
+
+// if (segments == 0) {
+// if (r_error) {
+// *r_error = TTR("The package must have at least one '.' separator.");
+// }
+// return false;
+// }
+
+// if (first) {
+// if (r_error) {
+// *r_error = TTR("Package segments must be of non-zero length.");
+// }
+// return false;
+// }
+
+// return true;
+// }
+
+// static bool _should_compress_asset(const String &p_path, const Vector<uint8_t> &p_data) {
+// /*
+// * By not compressing files with little or not benefit in doing so,
+// * a performance gain is expected attime. Moreover, if the APK is
+// * zip-aligned, assets stored as they are can be efficiently read by
+// * Android by memory-mapping them.
+// */
+
+// // -- Unconditional uncompress to mimic AAPT plus some other
+
+// static const char *unconditional_compress_ext[] = {
+// // From https://github.com/android/platform_frameworks_base/blob/master/tools/aapt/Package.cpp
+// // These formats are already compressed, or don't compress well:
+// ".jpg", ".jpeg", ".png", ".gif",
+// ".wav", ".mp2", ".mp3", ".ogg", ".aac",
+// ".mpg", ".mpeg", ".mid", ".midi", ".smf", ".jet",
+// ".rtttl", ".imy", ".xmf", ".mp4", ".m4a",
+// ".m4v", ".3gp", ".3gpp", ".3g2", ".3gpp2",
+// ".amr", ".awb", ".wma", ".wmv",
+// // Godot-specific:
+// ".webp", // Same reasoning as .png
+// ".cfb", // Don't let small config files slow-down startup
+// ".scn", // Binary scenes are usually already compressed
+// ".stex", // Streamable textures are usually already compressed
+// // Trailer for easier processing
+// nullptr
+// };
+
+// for (const char **ext = unconditional_compress_ext; *ext; ++ext) {
+// if (p_path.to_lower().ends_with(String(*ext))) {
+// return false;
+// }
+// }
+
+// // -- Compressed resource?
+
+// if (p_data.size() >= 4 && p_data[0] == 'R' && p_data[1] == 'S' && p_data[2] == 'C' && p_data[3] == 'C') {
+// // Already compressed
+// return false;
+// }
+
+// // --- TODO: Decide on texture resources according to their image compression setting
+
+// return true;
+// }
+
+// static zip_fileinfo get_zip_fileinfo() {
+// OS::Time time = OS::get_singleton()->get_time();
+// OS::Date date = OS::get_singleton()->get_date();
+
+// zip_fileinfo zipfi;
+// zipfi.tmz_date.tm_hour = time.hour;
+// zipfi.tmz_date.tm_mday = date.day;
+// zipfi.tmz_date.tm_min = time.min;
+// zipfi.tmz_date.tm_mon = date.month - 1; // tm_mon is zero indexed
+// zipfi.tmz_date.tm_sec = time.sec;
+// zipfi.tmz_date.tm_year = date.year;
+// zipfi.dosDate = 0;
+// zipfi.external_fa = 0;
+// zipfi.internal_fa = 0;
+
+// return zipfi;
+// }
+
+// static Vector<String> get_abis() {
+// Vector<String> abis;
+// abis.push_back("armeabi-v7a");
+// abis.push_back("arm64-v8a");
+// abis.push_back("x86");
+// abis.push_back("x86_64");
+// return abis;
+// }
+
+// /// List the gdap files in the directory specified by the p_path parameter.
+// static Vector<String> list_gdap_files(const String &p_path) {
+// Vector<String> dir_files;
+// DirAccessRef da = DirAccess::open(p_path);
+// if (da) {
+// da->list_dir_begin();
+// while (true) {
+// String file = da->get_next();
+// if (file == "") {
+// break;
+// }
+
+// if (da->current_is_dir() || da->current_is_hidden()) {
+// continue;
+// }
+
+// if (file.ends_with(PLUGIN_CONFIG_EXT)) {
+// dir_files.push_back(file);
+// }
+// }
+// da->list_dir_end();
+// }
+
+// return dir_files;
+// }
+
+// static Vector<PluginConfig> get_plugins() {
+// Vector<PluginConfig> loaded_plugins;
+
+// String plugins_dir = ProjectSettings::get_singleton()->get_resource_path().plus_file("android/plugins");
+
+// // Add the prebuilt plugins
+// loaded_plugins.append_array(get_prebuilt_plugins(plugins_dir));
+
+// if (DirAccess::exists(plugins_dir)) {
+// Vector<String> plugins_filenames = list_gdap_files(plugins_dir);
+
+// if (!plugins_filenames.is_empty()) {
+// Ref<ConfigFile> config_file = memnew(ConfigFile);
+// for (int i = 0; i < plugins_filenames.size(); i++) {
+// PluginConfig config = load_plugin_config(config_file, plugins_dir.plus_file(plugins_filenames[i]));
+// if (config.valid_config) {
+// loaded_plugins.push_back(config);
+// } else {
+// print_error("Invalid plugin config file " + plugins_filenames[i]);
+// }
+// }
+// }
+// }
+
+// return loaded_plugins;
+// }
+
+// static Vector<PluginConfig> get_enabled_plugins(const Ref<EditorExportPreset> &p_presets) {
+// Vector<PluginConfig> enabled_plugins;
+// Vector<PluginConfig> all_plugins = get_plugins();
+// for (int i = 0; i < all_plugins.size(); i++) {
+// PluginConfig plugin = all_plugins[i];
+// bool enabled = p_presets->get("plugins/" + plugin.name);
+// if (enabled) {
+// enabled_plugins.push_back(plugin);
+// }
+// }
+
+// return enabled_plugins;
+// }
+
+// static Error store_in_apk(APKExportData *ed, const String &p_path, const Vector<uint8_t> &p_data, int compression_method = Z_DEFLATED) {
+// zip_fileinfo zipfi = get_zip_fileinfo();
+// zipOpenNewFileInZip(ed->apk,
+// p_path.utf8().get_data(),
+// &zipfi,
+// nullptr,
+// 0,
+// nullptr,
+// 0,
+// nullptr,
+// compression_method,
+// Z_DEFAULT_COMPRESSION);
+
+// zipWriteInFileInZip(ed->apk, p_data.ptr(), p_data.size());
+// zipCloseFileInZip(ed->apk);
+
+// return OK;
+// }
+
+// static Error save_apk_so(void *p_userdata, const SharedObject &p_so) {
+// if (!p_so.path.get_file().begins_with("lib")) {
+// String err = "Android .so file names must start with \"lib\", but got: " + p_so.path;
+// ERR_PRINT(err);
+// return FAILED;
+// }
+// APKExportData *ed = (APKExportData *)p_userdata;
+// Vector<String> abis = get_abis();
+// bool exported = false;
+// for (int i = 0; i < p_so.tags.size(); ++i) {
+// // shared objects can be fat (compatible with multiple ABIs)
+// int abi_index = abis.find(p_so.tags[i]);
+// if (abi_index != -1) {
+// exported = true;
+// String abi = abis[abi_index];
+// String dst_path = String("lib").plus_file(abi).plus_file(p_so.path.get_file());
+// Vector<uint8_t> array = FileAccess::get_file_as_array(p_so.path);
+// Error store_err = store_in_apk(ed, dst_path, array);
+// ERR_FAIL_COND_V_MSG(store_err, store_err, "Cannot store in apk file '" + dst_path + "'.");
+// }
+// }
+// if (!exported) {
+// String abis_string = String(" ").join(abis);
+// String err = "Cannot determine ABI for library \"" + p_so.path + "\". One of the supported ABIs must be used as a tag: " + abis_string;
+// ERR_PRINT(err);
+// return FAILED;
+// }
+// return OK;
+// }
+
+// static Error save_apk_file(void *p_userdata, const String &p_path, const Vector<uint8_t> &p_data, int p_file, int p_total, const Vector<String> &p_enc_in_filters, const Vector<String> &p_enc_ex_filters, const Vector<uint8_t> &p_key) {
+// APKExportData *ed = (APKExportData *)p_userdata;
+// String dst_path = p_path.replace_first("res://", "assets/");
+
+// store_in_apk(ed, dst_path, p_data, _should_compress_asset(p_path, p_data) ? Z_DEFLATED : 0);
+// return OK;
+// }
+
+// static Error ignore_apk_file(void *p_userdata, const String &p_path, const Vector<uint8_t> &p_data, int p_file, int p_total, const Vector<String> &p_enc_in_filters, const Vector<String> &p_enc_ex_filters, const Vector<uint8_t> &p_key) {
+// return OK;
+// }
+
+// void _get_permissions(const Ref<EditorExportPreset> &p_preset, bool p_give_internet, Vector<String> &r_permissions) {
+// const char **aperms = android_perms;
+// while (*aperms) {
+// bool enabled = p_preset->get("permissions/" + String(*aperms).to_lower());
+// if (enabled) {
+// r_permissions.push_back("android.permission." + String(*aperms));
+// }
+// aperms++;
+// }
+// PackedStringArray user_perms = p_preset->get("permissions/custom_permissions");
+// for (int i = 0; i < user_perms.size(); i++) {
+// String user_perm = user_perms[i].strip_edges();
+// if (!user_perm.is_empty()) {
+// r_permissions.push_back(user_perm);
+// }
+// }
+// if (p_give_internet) {
+// if (r_permissions.find("android.permission.INTERNET") == -1) {
+// r_permissions.push_back("android.permission.INTERNET");
+// }
+// }
+
+// int xr_mode_index = p_preset->get("xr_features/xr_mode");
+// if (xr_mode_index == 1 /* XRMode.OVR */) {
+// int hand_tracking_index = p_preset->get("xr_features/hand_tracking"); // 0: none, 1: optional, 2: required
+// if (hand_tracking_index > 0) {
+// if (r_permissions.find("com.oculus.permission.HAND_TRACKING") == -1) {
+// r_permissions.push_back("com.oculus.permission.HAND_TRACKING");
+// }
+// }
+// }
+// }
+
+// void _write_tmp_manifest(const Ref<EditorExportPreset> &p_preset, bool p_give_internet, bool p_debug) {
+// String manifest_text =
+// "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
+// "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n"
+// " xmlns:tools=\"http://schemas.android.com/tools\">\n";
+
+// manifest_text += _get_screen_sizes_tag(p_preset);
+// manifest_text += _get_gles_tag();
+
+// Vector<String> perms;
+// _get_permissions(p_preset, p_give_internet, perms);
+// for (int i = 0; i < perms.size(); i++) {
+// manifest_text += vformat(" <uses-permission android:name=\"%s\" />\n", perms.get(i));
+// }
+
+// manifest_text += _get_xr_features_tag(p_preset);
+// manifest_text += _get_instrumentation_tag(p_preset);
+// String plugins_names = get_plugins_names(get_enabled_plugins(p_preset));
+// manifest_text += _get_application_tag(p_preset, plugins_names);
+// manifest_text += "</manifest>\n";
+// String manifest_path = vformat("res://android/build/src/%s/AndroidManifest.xml", (p_debug ? "debug" : "release"));
+// store_string_at_path(manifest_path, manifest_text);
+// }
+
+// void _fix_manifest(const Ref<EditorExportPreset> &p_preset, Vector<uint8_t> &p_manifest, bool p_give_internet) {
+// // Leaving the unused types commented because looking these constants up
+// // again later would be annoying
+// // const int CHUNK_AXML_FILE = 0x00080003;
+// // const int CHUNK_RESOURCEIDS = 0x00080180;
+// const int CHUNK_STRINGS = 0x001C0001;
+// // const int CHUNK_XML_END_NAMESPACE = 0x00100101;
+// const int CHUNK_XML_END_TAG = 0x00100103;
+// // const int CHUNK_XML_START_NAMESPACE = 0x00100100;
+// const int CHUNK_XML_START_TAG = 0x00100102;
+// // const int CHUNK_XML_TEXT = 0x00100104;
+// const int UTF8_FLAG = 0x00000100;
+
+// Vector<String> string_table;
+
+// uint32_t ofs = 8;
+
+// uint32_t string_count = 0;
+// //uint32_t styles_count = 0;
+// uint32_t string_flags = 0;
+// uint32_t string_data_offset = 0;
+
+// //uint32_t styles_offset = 0;
+// uint32_t string_table_begins = 0;
+// uint32_t string_table_ends = 0;
+// Vector<uint8_t> stable_extra;
+
+// String version_name = p_preset->get("version/name");
+// int version_code = p_preset->get("version/code");
+// String package_name = p_preset->get("package/unique_name");
+
+// const int screen_orientation = _get_android_orientation_value(_get_screen_orientation());
+
+// bool screen_support_small = p_preset->get("screen/support_small");
+// bool screen_support_normal = p_preset->get("screen/support_normal");
+// bool screen_support_large = p_preset->get("screen/support_large");
+// bool screen_support_xlarge = p_preset->get("screen/support_xlarge");
+
+// int xr_mode_index = p_preset->get("xr_features/xr_mode");
+// bool focus_awareness = p_preset->get("xr_features/focus_awareness");
+
+// String plugins_names = get_plugins_names(get_enabled_plugins(p_preset));
+
+// Vector<String> perms;
+// // Write permissions into the perms variable.
+// _get_permissions(p_preset, p_give_internet, perms);
+
+// while (ofs < (uint32_t)p_manifest.size()) {
+// uint32_t chunk = decode_uint32(&p_manifest[ofs]);
+// uint32_t size = decode_uint32(&p_manifest[ofs + 4]);
+
+// switch (chunk) {
+// case CHUNK_STRINGS: {
+// int iofs = ofs + 8;
+
+// string_count = decode_uint32(&p_manifest[iofs]);
+// //styles_count = decode_uint32(&p_manifest[iofs + 4]);
+// string_flags = decode_uint32(&p_manifest[iofs + 8]);
+// string_data_offset = decode_uint32(&p_manifest[iofs + 12]);
+// //styles_offset = decode_uint32(&p_manifest[iofs + 16]);
+// /*
+// printf("string count: %i\n",string_count);
+// printf("flags: %i\n",string_flags);
+// printf("sdata ofs: %i\n",string_data_offset);
+// printf("styles ofs: %i\n",styles_offset);
+// */
+// uint32_t st_offset = iofs + 20;
+// string_table.resize(string_count);
+// uint32_t string_end = 0;
+
+// string_table_begins = st_offset;
+
+// for (uint32_t i = 0; i < string_count; i++) {
+// uint32_t string_at = decode_uint32(&p_manifest[st_offset + i * 4]);
+// string_at += st_offset + string_count * 4;
+
+// ERR_FAIL_COND_MSG(string_flags & UTF8_FLAG, "Unimplemented, can't read UTF-8 string table.");
+
+// if (string_flags & UTF8_FLAG) {
+// } else {
+// uint32_t len = decode_uint16(&p_manifest[string_at]);
+// Vector<char32_t> ucstring;
+// ucstring.resize(len + 1);
+// for (uint32_t j = 0; j < len; j++) {
+// uint16_t c = decode_uint16(&p_manifest[string_at + 2 + 2 * j]);
+// ucstring.write[j] = c;
+// }
+// string_end = MAX(string_at + 2 + 2 * len, string_end);
+// ucstring.write[len] = 0;
+// string_table.write[i] = ucstring.ptr();
+// }
+// }
+
+// for (uint32_t i = string_end; i < (ofs + size); i++) {
+// stable_extra.push_back(p_manifest[i]);
+// }
+
+// string_table_ends = ofs + size;
+
+// } break;
+// case CHUNK_XML_START_TAG: {
+// int iofs = ofs + 8;
+// uint32_t name = decode_uint32(&p_manifest[iofs + 12]);
+
+// String tname = string_table[name];
+// uint32_t attrcount = decode_uint32(&p_manifest[iofs + 20]);
+// iofs += 28;
+// bool is_focus_aware_metadata = false;
+
+// for (uint32_t i = 0; i < attrcount; i++) {
+// uint32_t attr_nspace = decode_uint32(&p_manifest[iofs]);
+// uint32_t attr_name = decode_uint32(&p_manifest[iofs + 4]);
+// uint32_t attr_value = decode_uint32(&p_manifest[iofs + 8]);
+// uint32_t attr_resid = decode_uint32(&p_manifest[iofs + 16]);
+
+// const String value = (attr_value != 0xFFFFFFFF) ? string_table[attr_value] : "Res #" + itos(attr_resid);
+// String attrname = string_table[attr_name];
+// const String nspace = (attr_nspace != 0xFFFFFFFF) ? string_table[attr_nspace] : "";
+
+// //replace project information
+// if (tname == "manifest" && attrname == "package") {
+// string_table.write[attr_value] = get_package_name(package_name);
+// }
+
+// if (tname == "manifest" && attrname == "versionCode") {
+// encode_uint32(version_code, &p_manifest.write[iofs + 16]);
+// }
+
+// if (tname == "manifest" && attrname == "versionName") {
+// if (attr_value == 0xFFFFFFFF) {
+// WARN_PRINT("Version name in a resource, should be plain text");
+// } else {
+// string_table.write[attr_value] = version_name;
+// }
+// }
+
+// if (tname == "instrumentation" && attrname == "targetPackage") {
+// string_table.write[attr_value] = get_package_name(package_name);
+// }
+
+// if (tname == "activity" && attrname == "screenOrientation") {
+// encode_uint32(screen_orientation, &p_manifest.write[iofs + 16]);
+// }
+
+// if (tname == "supports-screens") {
+// if (attrname == "smallScreens") {
+// encode_uint32(screen_support_small ? 0xFFFFFFFF : 0, &p_manifest.write[iofs + 16]);
+
+// } else if (attrname == "normalScreens") {
+// encode_uint32(screen_support_normal ? 0xFFFFFFFF : 0, &p_manifest.write[iofs + 16]);
+
+// } else if (attrname == "largeScreens") {
+// encode_uint32(screen_support_large ? 0xFFFFFFFF : 0, &p_manifest.write[iofs + 16]);
+
+// } else if (attrname == "xlargeScreens") {
+// encode_uint32(screen_support_xlarge ? 0xFFFFFFFF : 0, &p_manifest.write[iofs + 16]);
+// }
+// }
+
+// // FIXME: `attr_value != 0xFFFFFFFF` below added as a stopgap measure for GH-32553,
+// // but the issue should be debugged further and properly addressed.
+// if (tname == "meta-data" && attrname == "name" && value == "xr_mode_metadata_name") {
+// // Update the meta-data 'android:name' attribute based on the selected XR mode.
+// if (xr_mode_index == 1 /* XRMode.OVR */) {
+// string_table.write[attr_value] = "com.samsung.android.vr.application.mode";
+// }
+// }
+
+// if (tname == "meta-data" && attrname == "value" && value == "xr_mode_metadata_value") {
+// // Update the meta-data 'android:value' attribute based on the selected XR mode.
+// if (xr_mode_index == 1 /* XRMode.OVR */) {
+// string_table.write[attr_value] = "vr_only";
+// }
+// }
+
+// if (tname == "meta-data" && attrname == "value" && is_focus_aware_metadata) {
+// // Update the focus awareness meta-data value
+// encode_uint32(xr_mode_index == /* XRMode.OVR */ 1 && focus_awareness ? 0xFFFFFFFF : 0, &p_manifest.write[iofs + 16]);
+// }
+
+// if (tname == "meta-data" && attrname == "value" && value == "plugins_value" && !plugins_names.is_empty()) {
+// // Update the meta-data 'android:value' attribute with the list of enabled plugins.
+// string_table.write[attr_value] = plugins_names;
+// }
+
+// is_focus_aware_metadata = tname == "meta-data" && attrname == "name" && value == "com.oculus.vr.focusaware";
+// iofs += 20;
+// }
+
+// } break;
+// case CHUNK_XML_END_TAG: {
+// int iofs = ofs + 8;
+// uint32_t name = decode_uint32(&p_manifest[iofs + 12]);
+// String tname = string_table[name];
+
+// if (tname == "uses-feature") {
+// Vector<String> feature_names;
+// Vector<bool> feature_required_list;
+// Vector<int> feature_versions;
+
+// if (xr_mode_index == 1 /* XRMode.OVR */) {
+// // Check for degrees of freedom
+// int dof_index = p_preset->get("xr_features/degrees_of_freedom"); // 0: none, 1: 3dof and 6dof, 2: 6dof
+
+// if (dof_index > 0) {
+// feature_names.push_back("android.hardware.vr.headtracking");
+// feature_required_list.push_back(dof_index == 2);
+// feature_versions.push_back(1);
+// }
+
+// // Check for hand tracking
+// int hand_tracking_index = p_preset->get("xr_features/hand_tracking"); // 0: none, 1: optional, 2: required
+// if (hand_tracking_index > 0) {
+// feature_names.push_back("oculus.software.handtracking");
+// feature_required_list.push_back(hand_tracking_index == 2);
+// feature_versions.push_back(-1); // no version attribute should be added.
+// }
+// }
+
+// if (feature_names.size() > 0) {
+// ofs += 24; // skip over end tag
+
+// // save manifest ending so we can restore it
+// Vector<uint8_t> manifest_end;
+// uint32_t manifest_cur_size = p_manifest.size();
+
+// manifest_end.resize(p_manifest.size() - ofs);
+// memcpy(manifest_end.ptrw(), &p_manifest[ofs], manifest_end.size());
+
+// int32_t attr_name_string = string_table.find("name");
+// ERR_FAIL_COND_MSG(attr_name_string == -1, "Template does not have 'name' attribute.");
+
+// int32_t ns_android_string = string_table.find("http://schemas.android.com/apk/res/android");
+// if (ns_android_string == -1) {
+// string_table.push_back("http://schemas.android.com/apk/res/android");
+// ns_android_string = string_table.size() - 1;
+// }
+
+// int32_t attr_uses_feature_string = string_table.find("uses-feature");
+// if (attr_uses_feature_string == -1) {
+// string_table.push_back("uses-feature");
+// attr_uses_feature_string = string_table.size() - 1;
+// }
+
+// int32_t attr_required_string = string_table.find("required");
+// if (attr_required_string == -1) {
+// string_table.push_back("required");
+// attr_required_string = string_table.size() - 1;
+// }
+
+// for (int i = 0; i < feature_names.size(); i++) {
+// String feature_name = feature_names[i];
+// bool feature_required = feature_required_list[i];
+// int feature_version = feature_versions[i];
+// bool has_version_attribute = feature_version != -1;
+
+// print_line("Adding feature " + feature_name);
+
+// int32_t feature_string = string_table.find(feature_name);
+// if (feature_string == -1) {
+// string_table.push_back(feature_name);
+// feature_string = string_table.size() - 1;
+// }
+
+// String required_value_string = feature_required ? "true" : "false";
+// int32_t required_value = string_table.find(required_value_string);
+// if (required_value == -1) {
+// string_table.push_back(required_value_string);
+// required_value = string_table.size() - 1;
+// }
+
+// int32_t attr_version_string = -1;
+// int32_t version_value = -1;
+// int tag_size;
+// int attr_count;
+// if (has_version_attribute) {
+// attr_version_string = string_table.find("version");
+// if (attr_version_string == -1) {
+// string_table.push_back("version");
+// attr_version_string = string_table.size() - 1;
+// }
+
+// version_value = string_table.find(itos(feature_version));
+// if (version_value == -1) {
+// string_table.push_back(itos(feature_version));
+// version_value = string_table.size() - 1;
+// }
+
+// tag_size = 96; // node and three attrs + end node
+// attr_count = 3;
+// } else {
+// tag_size = 76; // node and two attrs + end node
+// attr_count = 2;
+// }
+// manifest_cur_size += tag_size + 24;
+// p_manifest.resize(manifest_cur_size);
+
+// // start tag
+// encode_uint16(0x102, &p_manifest.write[ofs]); // type
+// encode_uint16(16, &p_manifest.write[ofs + 2]); // headersize
+// encode_uint32(tag_size, &p_manifest.write[ofs + 4]); // size
+// encode_uint32(0, &p_manifest.write[ofs + 8]); // lineno
+// encode_uint32(-1, &p_manifest.write[ofs + 12]); // comment
+// encode_uint32(-1, &p_manifest.write[ofs + 16]); // ns
+// encode_uint32(attr_uses_feature_string, &p_manifest.write[ofs + 20]); // name
+// encode_uint16(20, &p_manifest.write[ofs + 24]); // attr_start
+// encode_uint16(20, &p_manifest.write[ofs + 26]); // attr_size
+// encode_uint16(attr_count, &p_manifest.write[ofs + 28]); // num_attrs
+// encode_uint16(0, &p_manifest.write[ofs + 30]); // id_index
+// encode_uint16(0, &p_manifest.write[ofs + 32]); // class_index
+// encode_uint16(0, &p_manifest.write[ofs + 34]); // style_index
+
+// // android:name attribute
+// encode_uint32(ns_android_string, &p_manifest.write[ofs + 36]); // ns
+// encode_uint32(attr_name_string, &p_manifest.write[ofs + 40]); // 'name'
+// encode_uint32(feature_string, &p_manifest.write[ofs + 44]); // raw_value
+// encode_uint16(8, &p_manifest.write[ofs + 48]); // typedvalue_size
+// p_manifest.write[ofs + 50] = 0; // typedvalue_always0
+// p_manifest.write[ofs + 51] = 0x03; // typedvalue_type (string)
+// encode_uint32(feature_string, &p_manifest.write[ofs + 52]); // typedvalue reference
+
+// // android:required attribute
+// encode_uint32(ns_android_string, &p_manifest.write[ofs + 56]); // ns
+// encode_uint32(attr_required_string, &p_manifest.write[ofs + 60]); // 'name'
+// encode_uint32(required_value, &p_manifest.write[ofs + 64]); // raw_value
+// encode_uint16(8, &p_manifest.write[ofs + 68]); // typedvalue_size
+// p_manifest.write[ofs + 70] = 0; // typedvalue_always0
+// p_manifest.write[ofs + 71] = 0x03; // typedvalue_type (string)
+// encode_uint32(required_value, &p_manifest.write[ofs + 72]); // typedvalue reference
+
+// ofs += 76;
+
+// if (has_version_attribute) {
+// // android:version attribute
+// encode_uint32(ns_android_string, &p_manifest.write[ofs]); // ns
+// encode_uint32(attr_version_string, &p_manifest.write[ofs + 4]); // 'name'
+// encode_uint32(version_value, &p_manifest.write[ofs + 8]); // raw_value
+// encode_uint16(8, &p_manifest.write[ofs + 12]); // typedvalue_size
+// p_manifest.write[ofs + 14] = 0; // typedvalue_always0
+// p_manifest.write[ofs + 15] = 0x03; // typedvalue_type (string)
+// encode_uint32(version_value, &p_manifest.write[ofs + 16]); // typedvalue reference
+
+// ofs += 20;
+// }
+
+// // end tag
+// encode_uint16(0x103, &p_manifest.write[ofs]); // type
+// encode_uint16(16, &p_manifest.write[ofs + 2]); // headersize
+// encode_uint32(24, &p_manifest.write[ofs + 4]); // size
+// encode_uint32(0, &p_manifest.write[ofs + 8]); // lineno
+// encode_uint32(-1, &p_manifest.write[ofs + 12]); // comment
+// encode_uint32(-1, &p_manifest.write[ofs + 16]); // ns
+// encode_uint32(attr_uses_feature_string, &p_manifest.write[ofs + 20]); // name
+
+// ofs += 24;
+// }
+// memcpy(&p_manifest.write[ofs], manifest_end.ptr(), manifest_end.size());
+// ofs -= 24; // go back over back end
+// }
+// }
+// if (tname == "manifest") {
+// // save manifest ending so we can restore it
+// Vector<uint8_t> manifest_end;
+// uint32_t manifest_cur_size = p_manifest.size();
+
+// manifest_end.resize(p_manifest.size() - ofs);
+// memcpy(manifest_end.ptrw(), &p_manifest[ofs], manifest_end.size());
+
+// int32_t attr_name_string = string_table.find("name");
+// ERR_FAIL_COND_MSG(attr_name_string == -1, "Template does not have 'name' attribute.");
+
+// int32_t ns_android_string = string_table.find("android");
+// ERR_FAIL_COND_MSG(ns_android_string == -1, "Template does not have 'android' namespace.");
+
+// int32_t attr_uses_permission_string = string_table.find("uses-permission");
+// if (attr_uses_permission_string == -1) {
+// string_table.push_back("uses-permission");
+// attr_uses_permission_string = string_table.size() - 1;
+// }
+
+// for (int i = 0; i < perms.size(); ++i) {
+// print_line("Adding permission " + perms[i]);
+
+// manifest_cur_size += 56 + 24; // node + end node
+// p_manifest.resize(manifest_cur_size);
+
+// // Add permission to the string pool
+// int32_t perm_string = string_table.find(perms[i]);
+// if (perm_string == -1) {
+// string_table.push_back(perms[i]);
+// perm_string = string_table.size() - 1;
+// }
+
+// // start tag
+// encode_uint16(0x102, &p_manifest.write[ofs]); // type
+// encode_uint16(16, &p_manifest.write[ofs + 2]); // headersize
+// encode_uint32(56, &p_manifest.write[ofs + 4]); // size
+// encode_uint32(0, &p_manifest.write[ofs + 8]); // lineno
+// encode_uint32(-1, &p_manifest.write[ofs + 12]); // comment
+// encode_uint32(-1, &p_manifest.write[ofs + 16]); // ns
+// encode_uint32(attr_uses_permission_string, &p_manifest.write[ofs + 20]); // name
+// encode_uint16(20, &p_manifest.write[ofs + 24]); // attr_start
+// encode_uint16(20, &p_manifest.write[ofs + 26]); // attr_size
+// encode_uint16(1, &p_manifest.write[ofs + 28]); // num_attrs
+// encode_uint16(0, &p_manifest.write[ofs + 30]); // id_index
+// encode_uint16(0, &p_manifest.write[ofs + 32]); // class_index
+// encode_uint16(0, &p_manifest.write[ofs + 34]); // style_index
+
+// // attribute
+// encode_uint32(ns_android_string, &p_manifest.write[ofs + 36]); // ns
+// encode_uint32(attr_name_string, &p_manifest.write[ofs + 40]); // 'name'
+// encode_uint32(perm_string, &p_manifest.write[ofs + 44]); // raw_value
+// encode_uint16(8, &p_manifest.write[ofs + 48]); // typedvalue_size
+// p_manifest.write[ofs + 50] = 0; // typedvalue_always0
+// p_manifest.write[ofs + 51] = 0x03; // typedvalue_type (string)
+// encode_uint32(perm_string, &p_manifest.write[ofs + 52]); // typedvalue reference
+
+// ofs += 56;
+
+// // end tag
+// encode_uint16(0x103, &p_manifest.write[ofs]); // type
+// encode_uint16(16, &p_manifest.write[ofs + 2]); // headersize
+// encode_uint32(24, &p_manifest.write[ofs + 4]); // size
+// encode_uint32(0, &p_manifest.write[ofs + 8]); // lineno
+// encode_uint32(-1, &p_manifest.write[ofs + 12]); // comment
+// encode_uint32(-1, &p_manifest.write[ofs + 16]); // ns
+// encode_uint32(attr_uses_permission_string, &p_manifest.write[ofs + 20]); // name
+
+// ofs += 24;
+// }
+
+// // copy footer back in
+// memcpy(&p_manifest.write[ofs], manifest_end.ptr(), manifest_end.size());
+// }
+// } break;
+// }
+
+// ofs += size;
+// }
+
+// //create new andriodmanifest binary
+
+// Vector<uint8_t> ret;
+// ret.resize(string_table_begins + string_table.size() * 4);
+
+// for (uint32_t i = 0; i < string_table_begins; i++) {
+// ret.write[i] = p_manifest[i];
+// }
+
+// ofs = 0;
+// for (int i = 0; i < string_table.size(); i++) {
+// encode_uint32(ofs, &ret.write[string_table_begins + i * 4]);
+// ofs += string_table[i].length() * 2 + 2 + 2;
+// }
+
+// ret.resize(ret.size() + ofs);
+// string_data_offset = ret.size() - ofs;
+// uint8_t *chars = &ret.write[string_data_offset];
+// for (int i = 0; i < string_table.size(); i++) {
+// String s = string_table[i];
+// encode_uint16(s.length(), chars);
+// chars += 2;
+// for (int j = 0; j < s.length(); j++) {
+// encode_uint16(s[j], chars);
+// chars += 2;
+// }
+// encode_uint16(0, chars);
+// chars += 2;
+// }
+
+// for (int i = 0; i < stable_extra.size(); i++) {
+// ret.push_back(stable_extra[i]);
+// }
+
+// //pad
+// while (ret.size() % 4) {
+// ret.push_back(0);
+// }
+
+// uint32_t new_stable_end = ret.size();
+
+// uint32_t extra = (p_manifest.size() - string_table_ends);
+// ret.resize(new_stable_end + extra);
+// for (uint32_t i = 0; i < extra; i++) {
+// ret.write[new_stable_end + i] = p_manifest[string_table_ends + i];
+// }
+
+// while (ret.size() % 4) {
+// ret.push_back(0);
+// }
+// encode_uint32(ret.size(), &ret.write[4]); //update new file size
+
+// encode_uint32(new_stable_end - 8, &ret.write[12]); //update new string table size
+// encode_uint32(string_table.size(), &ret.write[16]); //update new number of strings
+// encode_uint32(string_data_offset - 8, &ret.write[28]); //update new string data offset
+
+// p_manifest = ret;
+// }
+
+// static String _parse_string(const uint8_t *p_bytes, bool p_utf8) {
+// uint32_t offset = 0;
+// uint32_t len = 0;
+
+// if (p_utf8) {
+// uint8_t byte = p_bytes[offset];
+// if (byte & 0x80) {
+// offset += 2;
+// } else {
+// offset += 1;
+// }
+// byte = p_bytes[offset];
+// offset++;
+// if (byte & 0x80) {
+// len = byte & 0x7F;
+// len = (len << 8) + p_bytes[offset];
+// offset++;
+// } else {
+// len = byte;
+// }
+// } else {
+// len = decode_uint16(&p_bytes[offset]);
+// offset += 2;
+// if (len & 0x8000) {
+// len &= 0x7FFF;
+// len = (len << 16) + decode_uint16(&p_bytes[offset]);
+// offset += 2;
+// }
+// }
+
+// if (p_utf8) {
+// Vector<uint8_t> str8;
+// str8.resize(len + 1);
+// for (uint32_t i = 0; i < len; i++) {
+// str8.write[i] = p_bytes[offset + i];
+// }
+// str8.write[len] = 0;
+// String str;
+// str.parse_utf8((const char *)str8.ptr());
+// return str;
+// } else {
+// String str;
+// for (uint32_t i = 0; i < len; i++) {
+// char32_t c = decode_uint16(&p_bytes[offset + i * 2]);
+// if (c == 0) {
+// break;
+// }
+// str += String::chr(c);
+// }
+// return str;
+// }
+// }
+
+// void _fix_resources(const Ref<EditorExportPreset> &p_preset, Vector<uint8_t> &r_manifest) {
+// const int UTF8_FLAG = 0x00000100;
+
+// uint32_t string_block_len = decode_uint32(&r_manifest[16]);
+// uint32_t string_count = decode_uint32(&r_manifest[20]);
+// uint32_t string_flags = decode_uint32(&r_manifest[28]);
+// const uint32_t string_table_begins = 40;
+
+// Vector<String> string_table;
+
+// String package_name = p_preset->get("package/name");
+
+// for (uint32_t i = 0; i < string_count; i++) {
+// uint32_t offset = decode_uint32(&r_manifest[string_table_begins + i * 4]);
+// offset += string_table_begins + string_count * 4;
+
+// String str = _parse_string(&r_manifest[offset], string_flags & UTF8_FLAG);
+
+// if (str.begins_with("godot-project-name")) {
+// if (str == "godot-project-name") {
+// //project name
+// str = get_project_name(package_name);
+
+// } else {
+// String lang = str.substr(str.rfind("-") + 1, str.length()).replace("-", "_");
+// String prop = "application/config/name_" + lang;
+// if (ProjectSettings::get_singleton()->has_setting(prop)) {
+// str = ProjectSettings::get_singleton()->get(prop);
+// } else {
+// str = get_project_name(package_name);
+// }
+// }
+// }
+
+// string_table.push_back(str);
+// }
+
+// //write a new string table, but use 16 bits
+// Vector<uint8_t> ret;
+// ret.resize(string_table_begins + string_table.size() * 4);
+
+// for (uint32_t i = 0; i < string_table_begins; i++) {
+// ret.write[i] = r_manifest[i];
+// }
+
+// int ofs = 0;
+// for (int i = 0; i < string_table.size(); i++) {
+// encode_uint32(ofs, &ret.write[string_table_begins + i * 4]);
+// ofs += string_table[i].length() * 2 + 2 + 2;
+// }
+
+// ret.resize(ret.size() + ofs);
+// uint8_t *chars = &ret.write[ret.size() - ofs];
+// for (int i = 0; i < string_table.size(); i++) {
+// String s = string_table[i];
+// encode_uint16(s.length(), chars);
+// chars += 2;
+// for (int j = 0; j < s.length(); j++) {
+// encode_uint16(s[j], chars);
+// chars += 2;
+// }
+// encode_uint16(0, chars);
+// chars += 2;
+// }
+
+// //pad
+// while (ret.size() % 4) {
+// ret.push_back(0);
+// }
+
+// //change flags to not use utf8
+// encode_uint32(string_flags & ~0x100, &ret.write[28]);
+// //change length
+// encode_uint32(ret.size() - 12, &ret.write[16]);
+// //append the rest...
+// int rest_from = 12 + string_block_len;
+// int rest_to = ret.size();
+// int rest_len = (r_manifest.size() - rest_from);
+// ret.resize(ret.size() + (r_manifest.size() - rest_from));
+// for (int i = 0; i < rest_len; i++) {
+// ret.write[rest_to + i] = r_manifest[rest_from + i];
+// }
+// //finally update the size
+// encode_uint32(ret.size(), &ret.write[4]);
+
+// r_manifest = ret;
+// //printf("end\n");
+// }
+
+// void _load_image_data(const Ref<Image> &p_splash_image, Vector<uint8_t> &p_data) {
+// Vector<uint8_t> png_buffer;
+// Error err = PNGDriverCommon::image_to_png(p_splash_image, png_buffer);
+// if (err == OK) {
+// p_data.resize(png_buffer.size());
+// memcpy(p_data.ptrw(), png_buffer.ptr(), p_data.size());
+// } else {
+// String err_str = String("Failed to convert splash image to png.");
+// WARN_PRINT(err_str.utf8().get_data());
+// }
+// }
+
+// void _process_launcher_icons(const String &p_file_name, const Ref<Image> &p_source_image, int dimension, Vector<uint8_t> &p_data) {
+// Ref<Image> working_image = p_source_image;
+
+// if (p_source_image->get_width() != dimension || p_source_image->get_height() != dimension) {
+// working_image = p_source_image->duplicate();
+// working_image->resize(dimension, dimension, Image::Interpolation::INTERPOLATE_LANCZOS);
+// }
+
+// Vector<uint8_t> png_buffer;
+// Error err = PNGDriverCommon::image_to_png(working_image, png_buffer);
+// if (err == OK) {
+// p_data.resize(png_buffer.size());
+// memcpy(p_data.ptrw(), png_buffer.ptr(), p_data.size());
+// } else {
+// String err_str = String("Failed to convert resized icon (") + p_file_name + ") to png.";
+// WARN_PRINT(err_str.utf8().get_data());
+// }
+// }
+
+// void load_splash_refs(Ref<Image> &splash_image, Ref<Image> &splash_bg_color_image) {
+// // TODO: Figure out how to handle remaining boot splash parameters (e.g: fullsize, filter)
+// String project_splash_path = ProjectSettings::get_singleton()->get("application/boot_splash/image");
+
+// if (!project_splash_path.is_empty()) {
+// splash_image.instance();
+// const Error err = ImageLoader::load_image(project_splash_path, splash_image);
+// if (err) {
+// splash_image.unref();
+// }
+// }
+
+// if (splash_image.is_null()) {
+// // Use the default
+// splash_image = Ref<Image>(memnew(Image(boot_splash_png)));
+// }
+
+// // Setup the splash bg color
+// bool bg_color_valid;
+// Color bg_color = ProjectSettings::get_singleton()->get("application/boot_splash/bg_color", &bg_color_valid);
+// if (!bg_color_valid) {
+// bg_color = boot_splash_bg_color;
+// }
+
+// splash_bg_color_image.instance();
+// splash_bg_color_image->create(splash_image->get_width(), splash_image->get_height(), false, splash_image->get_format());
+// splash_bg_color_image->fill(bg_color);
+// }
+
+// void load_icon_refs(const Ref<EditorExportPreset> &p_preset, Ref<Image> &icon, Ref<Image> &foreground, Ref<Image> &background) {
+// String project_icon_path = ProjectSettings::get_singleton()->get("application/config/icon");
+
+// icon.instance();
+// foreground.instance();
+// background.instance();
+
+// // Regular icon: user selection -> project icon -> default.
+// String path = static_cast<String>(p_preset->get(launcher_icon_option)).strip_edges();
+// if (path.is_empty() || ImageLoader::load_image(path, icon) != OK) {
+// ImageLoader::load_image(project_icon_path, icon);
+// }
+
+// // Adaptive foreground: user selection -> regular icon (user selection -> project icon -> default).
+// path = static_cast<String>(p_preset->get(launcher_adaptive_icon_foreground_option)).strip_edges();
+// if (path.is_empty() || ImageLoader::load_image(path, foreground) != OK) {
+// foreground = icon;
+// }
+
+// // Adaptive background: user selection -> default.
+// path = static_cast<String>(p_preset->get(launcher_adaptive_icon_background_option)).strip_edges();
+// if (!path.is_empty()) {
+// ImageLoader::load_image(path, background);
+// }
+// }
+
+// void store_image(const LauncherIcon launcher_icon, const Vector<uint8_t> &data) {
+// store_image(launcher_icon.export_path, data);
+// }
+
+// void store_image(const String &export_path, const Vector<uint8_t> &data) {
+// String img_path = export_path.insert(0, "res://android/build/");
+// store_file_at_path(img_path, data);
+// }
+
+// void _copy_icons_to_gradle_project(const Ref<EditorExportPreset> &p_preset,
+// const Ref<Image> &splash_image,
+// const Ref<Image> &splash_bg_color_image,
+// const Ref<Image> &main_image,
+// const Ref<Image> &foreground,
+// const Ref<Image> &background) {
+// // Store the splash image
+// if (splash_image.is_valid() && !splash_image->is_empty()) {
+// Vector<uint8_t> data;
+// _load_image_data(splash_image, data);
+// store_image(SPLASH_IMAGE_EXPORT_PATH, data);
+// }
+
+// // Store the splash bg color image
+// if (splash_bg_color_image.is_valid() && !splash_bg_color_image->is_empty()) {
+// Vector<uint8_t> data;
+// _load_image_data(splash_bg_color_image, data);
+// store_image(SPLASH_BG_COLOR_PATH, data);
+// }
+
+// // Prepare images to be resized for the icons. If some image ends up being uninitialized,
+// // the default image from the export template will be used.
+
+// for (int i = 0; i < icon_densities_count; ++i) {
+// if (main_image.is_valid() && !main_image->is_empty()) {
+// Vector<uint8_t> data;
+// _process_launcher_icons(launcher_icons[i].export_path, main_image, launcher_icons[i].dimensions, data);
+// store_image(launcher_icons[i], data);
+// }
+
+// if (foreground.is_valid() && !foreground->is_empty()) {
+// Vector<uint8_t> data;
+// _process_launcher_icons(launcher_adaptive_icon_foregrounds[i].export_path, foreground,
+// launcher_adaptive_icon_foregrounds[i].dimensions, data);
+// store_image(launcher_adaptive_icon_foregrounds[i], data);
+// }
+
+// if (background.is_valid() && !background->is_empty()) {
+// Vector<uint8_t> data;
+// _process_launcher_icons(launcher_adaptive_icon_backgrounds[i].export_path, background,
+// launcher_adaptive_icon_backgrounds[i].dimensions, data);
+// store_image(launcher_adaptive_icon_backgrounds[i], data);
+// }
+// }
+// }
+
+// static Vector<String> get_enabled_abis(const Ref<EditorExportPreset> &p_preset) {
+// Vector<String> abis = get_abis();
+// Vector<String> enabled_abis;
+// for (int i = 0; i < abis.size(); ++i) {
+// bool is_enabled = p_preset->get("architectures/" + abis[i]);
+// if (is_enabled) {
+// enabled_abis.push_back(abis[i]);
+// }
+// }
+// return enabled_abis;
+// }
+
+//public:
+// typedef Error (*EditorExportSaveFunction)(void *p_userdata, const String &p_path, const Vector<uint8_t> &p_data, int p_file, int p_total, const Vector<String> &p_enc_in_filters, const Vector<String> &p_enc_ex_filters, const Vector<uint8_t> &p_key);
+
+//public:
+// virtual void get_preset_features(const Ref<EditorExportPreset> &p_preset, List<String> *r_features) override {
+// String driver = ProjectSettings::get_singleton()->get("rendering/driver/driver_name");
+// if (driver == "GLES2") {
+// r_features->push_back("etc");
+// }
+// // FIXME: Review what texture formats are used for Vulkan.
+// if (driver == "Vulkan") {
+// r_features->push_back("etc2");
+// }
+
+// Vector<String> abis = get_enabled_abis(p_preset);
+// for (int i = 0; i < abis.size(); ++i) {
+// r_features->push_back(abis[i]);
+// }
+// }
+
+// virtual void get_export_options(List<ExportOption> *r_options) override {
+// r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "custom_template/debug", PROPERTY_HINT_GLOBAL_FILE, "*.apk"), ""));
+// r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "custom_template/release", PROPERTY_HINT_GLOBAL_FILE, "*.apk"), ""));
+// r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "custom_template/use_custom_build"), false));
+// r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "custom_template/export_format", PROPERTY_HINT_ENUM, "Export APK,Export AAB"), EXPORT_FORMAT_APK));
+
+// Vector<PluginConfig> plugins_configs = get_plugins();
+// for (int i = 0; i < plugins_configs.size(); i++) {
+// print_verbose("Found Android plugin " + plugins_configs[i].name);
+// r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "plugins/" + plugins_configs[i].name), false));
+// }
+// plugins_changed = false;
+
+// Vector<String> abis = get_abis();
+// for (int i = 0; i < abis.size(); ++i) {
+// String abi = abis[i];
+// bool is_default = (abi == "armeabi-v7a" || abi == "arm64-v8a");
+// r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "architectures/" + abi), is_default));
+// }
+
+// r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "keystore/debug", PROPERTY_HINT_GLOBAL_FILE, "*.keystore,*.jks"), ""));
+// r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "keystore/debug_user"), ""));
+// r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "keystore/debug_password"), ""));
+// r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "keystore/release", PROPERTY_HINT_GLOBAL_FILE, "*.keystore,*.jks"), ""));
+// r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "keystore/release_user"), ""));
+// r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "keystore/release_password"), ""));
+
+// r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "one_click_deploy/clear_previous_install"), false));
+
+// r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "version/code", PROPERTY_HINT_RANGE, "1,4096,1,or_greater"), 1));
+// r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "version/name"), "1.0"));
+// r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "package/unique_name", PROPERTY_HINT_PLACEHOLDER_TEXT, "ext.domain.name"), "org.godotengine.$genname"));
+// r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "package/name", PROPERTY_HINT_PLACEHOLDER_TEXT, "Game Name [default if blank]"), ""));
+// r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "package/signed"), true));
+
+// r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, launcher_icon_option, PROPERTY_HINT_FILE, "*.png"), ""));
+// r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, launcher_adaptive_icon_foreground_option, PROPERTY_HINT_FILE, "*.png"), ""));
+// r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, launcher_adaptive_icon_background_option, PROPERTY_HINT_FILE, "*.png"), ""));
+
+// r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "graphics/32_bits_framebuffer"), true));
+// r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "graphics/opengl_debug"), false));
+
+// r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "xr_features/xr_mode", PROPERTY_HINT_ENUM, "Regular,Oculus Mobile VR"), 0));
+// r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "xr_features/degrees_of_freedom", PROPERTY_HINT_ENUM, "None,3DOF and 6DOF,6DOF"), 0));
+// r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "xr_features/hand_tracking", PROPERTY_HINT_ENUM, "None,Optional,Required"), 0));
+// r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "xr_features/focus_awareness"), false));
+
+// r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "screen/immersive_mode"), true));
+// r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "screen/support_small"), true));
+// r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "screen/support_normal"), true));
+// r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "screen/support_large"), true));
+// r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "screen/support_xlarge"), true));
+
+// r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "command_line/extra_args"), ""));
+
+// r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "apk_expansion/enable"), false));
+// r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "apk_expansion/SALT"), ""));
+// r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "apk_expansion/public_key", PROPERTY_HINT_MULTILINE_TEXT), ""));
+
+// r_options->push_back(ExportOption(PropertyInfo(Variant::PACKED_STRING_ARRAY, "permissions/custom_permissions"), PackedStringArray()));
+
+// const char **perms = android_perms;
+// while (*perms) {
+// r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "permissions/" + String(*perms).to_lower()), false));
+// perms++;
+// }
+// }
+
+// virtual String get_name() const override {
+// return "Android";
+// }
+
+// virtual String get_os_name() const override {
+// return "Android";
+// }
+
+// virtual Ref<Texture2D> get_logo() const override {
+// return logo;
+// }
+
+// virtual bool should_update_export_options() override {
+// bool export_options_changed = plugins_changed;
+// if (export_options_changed) {
+// // don't clear unless we're reporting true, to avoid race
+// plugins_changed = false;
+// }
+// return export_options_changed;
+// }
+
+// virtual bool poll_export() override {
+// bool dc = devices_changed;
+// if (dc) {
+// // don't clear unless we're reporting true, to avoid race
+// devices_changed = false;
+// }
+// return dc;
+// }
+
+// virtual int get_options_count() const override {
+// MutexLock lock(device_lock);
+// return devices.size();
+// }
+
+// virtual String get_options_tooltip() const override {
+// return TTR("Select device from the list");
+// }
+
+// virtual String get_option_label(int p_index) const override {
+// ERR_FAIL_INDEX_V(p_index, devices.size(), "");
+// MutexLock lock(device_lock);
+// return devices[p_index].name;
+// }
+
+// virtual String get_option_tooltip(int p_index) const override {
+// ERR_FAIL_INDEX_V(p_index, devices.size(), "");
+// MutexLock lock(device_lock);
+// String s = devices[p_index].description;
+// if (devices.size() == 1) {
+// // Tooltip will be:
+// // Name
+// // Description
+// s = devices[p_index].name + "\n\n" + s;
+// }
+// return s;
+// }
+
+// virtual Error run(const Ref<EditorExportPreset> &p_preset, int p_device, int p_debug_flags) override {
+// ERR_FAIL_INDEX_V(p_device, devices.size(), ERR_INVALID_PARAMETER);
+
+// String can_export_error;
+// bool can_export_missing_templates;
+// if (!can_export(p_preset, can_export_error, can_export_missing_templates)) {
+// EditorNode::add_io_error(can_export_error);
+// return ERR_UNCONFIGURED;
+// }
+
+// MutexLock lock(device_lock);
+
+// EditorProgress ep("run", "Running on " + devices[p_device].name, 3);
+
+// String adb = get_adb_path();
+
+// // Export_temp APK.
+// if (ep.step("Exporting APK...", 0)) {
+// return ERR_SKIP;
+// }
+
+// const bool use_remote = (p_debug_flags & DEBUG_FLAG_REMOTE_DEBUG) || (p_debug_flags & DEBUG_FLAG_DUMB_CLIENT);
+// const bool use_reverse = devices[p_device].api_level >= 21;
+
+// if (use_reverse) {
+// p_debug_flags |= DEBUG_FLAG_REMOTE_DEBUG_LOCALHOST;
+// }
+
+// String tmp_export_path = EditorSettings::get_singleton()->get_cache_dir().plus_file("tmpexport.apk");
+
+//#define CLEANUP_AND_RETURN(m_err) \
+// { \
+// DirAccess::remove_file_or_error(tmp_export_path); \
+// return m_err; \
+// }
+
+// // Export to temporary APK before sending to device.
+// Error err = export_project_helper(p_preset, true, tmp_export_path, EXPORT_FORMAT_APK, true, p_debug_flags);
+
+// if (err != OK) {
+// CLEANUP_AND_RETURN(err);
+// }
+
+// List<String> args;
+// int rv;
+
+// bool remove_prev = p_preset->get("one_click_deploy/clear_previous_install");
+// String version_name = p_preset->get("version/name");
+// String package_name = p_preset->get("package/unique_name");
+
+// if (remove_prev) {
+// if (ep.step("Uninstalling...", 1)) {
+// CLEANUP_AND_RETURN(ERR_SKIP);
+// }
+
+// print_line("Uninstalling previous version: " + devices[p_device].name);
+
+// args.push_back("-s");
+// args.push_back(devices[p_device].id);
+// args.push_back("uninstall");
+// args.push_back(get_package_name(package_name));
+
+// err = OS::get_singleton()->execute(adb, args, true, nullptr, nullptr, &rv);
+// }
+
+// print_line("Installing to device (please wait...): " + devices[p_device].name);
+// if (ep.step("Installing to device, please wait...", 2)) {
+// CLEANUP_AND_RETURN(ERR_SKIP);
+// }
+
+// args.clear();
+// args.push_back("-s");
+// args.push_back(devices[p_device].id);
+// args.push_back("install");
+// args.push_back("-r");
+// args.push_back(tmp_export_path);
+
+// err = OS::get_singleton()->execute(adb, args, true, nullptr, nullptr, &rv);
+// if (err || rv != 0) {
+// EditorNode::add_io_error("Could not install to device.");
+// CLEANUP_AND_RETURN(ERR_CANT_CREATE);
+// }
+
+// if (use_remote) {
+// if (use_reverse) {
+// static const char *const msg = "--- Device API >= 21; debugging over USB ---";
+// EditorNode::get_singleton()->get_log()->add_message(msg, EditorLog::MSG_TYPE_EDITOR);
+// print_line(String(msg).to_upper());
+
+// args.clear();
+// args.push_back("-s");
+// args.push_back(devices[p_device].id);
+// args.push_back("reverse");
+// args.push_back("--remove-all");
+// OS::get_singleton()->execute(adb, args, true, nullptr, nullptr, &rv);
+
+// if (p_debug_flags & DEBUG_FLAG_REMOTE_DEBUG) {
+// int dbg_port = EditorSettings::get_singleton()->get("network/debug/remote_port");
+// args.clear();
+// args.push_back("-s");
+// args.push_back(devices[p_device].id);
+// args.push_back("reverse");
+// args.push_back("tcp:" + itos(dbg_port));
+// args.push_back("tcp:" + itos(dbg_port));
+
+// OS::get_singleton()->execute(adb, args, true, nullptr, nullptr, &rv);
+// print_line("Reverse result: " + itos(rv));
+// }
+
+// if (p_debug_flags & DEBUG_FLAG_DUMB_CLIENT) {
+// int fs_port = EditorSettings::get_singleton()->get("filesystem/file_server/port");
+
+// args.clear();
+// args.push_back("-s");
+// args.push_back(devices[p_device].id);
+// args.push_back("reverse");
+// args.push_back("tcp:" + itos(fs_port));
+// args.push_back("tcp:" + itos(fs_port));
+
+// err = OS::get_singleton()->execute(adb, args, true, nullptr, nullptr, &rv);
+// print_line("Reverse result2: " + itos(rv));
+// }
+// } else {
+// static const char *const msg = "--- Device API < 21; debugging over Wi-Fi ---";
+// EditorNode::get_singleton()->get_log()->add_message(msg, EditorLog::MSG_TYPE_EDITOR);
+// print_line(String(msg).to_upper());
+// }
+// }
+
+// if (ep.step("Running on device...", 3)) {
+// CLEANUP_AND_RETURN(ERR_SKIP);
+// }
+// args.clear();
+// args.push_back("-s");
+// args.push_back(devices[p_device].id);
+// args.push_back("shell");
+// args.push_back("am");
+// args.push_back("start");
+// if ((bool)EditorSettings::get_singleton()->get("export/android/force_system_user") && devices[p_device].api_level >= 17) { // Multi-user introduced in Android 17
+// args.push_back("--user");
+// args.push_back("0");
+// }
+// args.push_back("-a");
+// args.push_back("android.intent.action.MAIN");
+// args.push_back("-n");
+// args.push_back(get_package_name(package_name) + "/com.godot.game.GodotApp");
+
+// err = OS::get_singleton()->execute(adb, args, true, nullptr, nullptr, &rv);
+// if (err || rv != 0) {
+// EditorNode::add_io_error("Could not execute on device.");
+// CLEANUP_AND_RETURN(ERR_CANT_CREATE);
+// }
+
+// CLEANUP_AND_RETURN(OK);
+//#undef CLEANUP_AND_RETURN
+// }
+
+// virtual Ref<Texture2D> get_run_icon() const override {
+// return run_icon;
+// }
+
+// static String get_adb_path() {
+// String exe_ext = "";
+// if (OS::get_singleton()->get_name() == "Windows") {
+// exe_ext = ".exe";
+// }
+// String sdk_path = EditorSettings::get_singleton()->get("export/android/android_sdk_path");
+// return sdk_path.plus_file("platform-tools/adb" + exe_ext);
+// }
+
+// static String get_apksigner_path() {
+// String exe_ext = "";
+// if (OS::get_singleton()->get_name() == "Windows") {
+// exe_ext = ".bat";
+// }
+// String apksigner_command_name = "apksigner" + exe_ext;
+// String sdk_path = EditorSettings::get_singleton()->get("export/android/android_sdk_path");
+// String apksigner_path = "";
+
+// Error errn;
+// String build_tools_dir = sdk_path.plus_file("build-tools");
+// DirAccessRef da = DirAccess::open(build_tools_dir, &errn);
+// if (errn != OK) {
+// print_error("Unable to open Android 'build-tools' directory.");
+// return apksigner_path;
+// }
+
+// // There are additional versions directories we need to go through.
+// da->list_dir_begin();
+// String sub_dir = da->get_next();
+// while (!sub_dir.is_empty()) {
+// if (!sub_dir.begins_with(".") && da->current_is_dir()) {
+// // Check if the tool is here.
+// String tool_path = build_tools_dir.plus_file(sub_dir).plus_file(apksigner_command_name);
+// if (FileAccess::exists(tool_path)) {
+// apksigner_path = tool_path;
+// break;
+// }
+// }
+// sub_dir = da->get_next();
+// }
+// da->list_dir_end();
+
+// if (apksigner_path.is_empty()) {
+// EditorNode::get_singleton()->show_warning(TTR("Unable to find the 'apksigner' tool."));
+// }
+
+// return apksigner_path;
+// }
+
+// virtual bool can_export(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates) const override {
+// String err;
+// bool valid = false;
+
+// // Look for export templates (first official, and if defined custom templates).
+
+// if (!bool(p_preset->get("custom_template/use_custom_build"))) {
+// String template_err;
+// bool dvalid = false;
+// bool rvalid = false;
+
+// if (p_preset->get("custom_template/debug") != "") {
+// dvalid = FileAccess::exists(p_preset->get("custom_template/debug"));
+// if (!dvalid) {
+// template_err += TTR("Custom debug template not found.") + "\n";
+// }
+// } else {
+// dvalid = exists_export_template("android_debug.apk", &template_err);
+// }
+
+// if (p_preset->get("custom_template/release") != "") {
+// rvalid = FileAccess::exists(p_preset->get("custom_template/release"));
+// if (!rvalid) {
+// template_err += TTR("Custom release template not found.") + "\n";
+// }
+// } else {
+// rvalid = exists_export_template("android_release.apk", &template_err);
+// }
+
+// valid = dvalid || rvalid;
+// if (!valid) {
+// err += template_err;
+// }
+// } else {
+// valid = exists_export_template("android_source.zip", &err);
+
+// if (!FileAccess::exists("res://android/build/build.gradle")) {
+// err += TTR("Android build template not installed in the project. Install it from the Project menu.") + "\n";
+// valid = false;
+// }
+// }
+// r_missing_templates = !valid;
+
+// // Validate the rest of the configuration.
+
+// String dk = p_preset->get("keystore/debug");
+
+// if (!FileAccess::exists(dk)) {
+// dk = EditorSettings::get_singleton()->get("export/android/debug_keystore");
+// if (!FileAccess::exists(dk)) {
+// valid = false;
+// err += TTR("Debug keystore not configured in the Editor Settings nor in the preset.") + "\n";
+// }
+// }
+
+// String rk = p_preset->get("keystore/release");
+
+// if (!rk.is_empty() && !FileAccess::exists(rk)) {
+// valid = false;
+// err += TTR("Release keystore incorrectly configured in the export preset.") + "\n";
+// }
+
+// String sdk_path = EditorSettings::get_singleton()->get("export/android/android_sdk_path");
+// if (sdk_path == "") {
+// err += TTR("A valid Android SDK path is required in Editor Settings.") + "\n";
+// valid = false;
+// } else {
+// Error errn;
+// // Check for the platform-tools directory.
+// DirAccessRef da = DirAccess::open(sdk_path.plus_file("platform-tools"), &errn);
+// if (errn != OK) {
+// err += TTR("Invalid Android SDK path in Editor Settings.");
+// err += TTR("Missing 'platform-tools' directory!");
+// err += "\n";
+// valid = false;
+// }
+
+// // Validate that adb is available
+// String adb_path = get_adb_path();
+// if (!FileAccess::exists(adb_path)) {
+// err += TTR("Unable to find Android SDK platform-tools' adb command.");
+// err += TTR("Please check in the Android SDK directory specified in Editor Settings.");
+// err += "\n";
+// valid = false;
+// }
+
+// // Check for the build-tools directory.
+// DirAccessRef build_tools_da = DirAccess::open(sdk_path.plus_file("build-tools"), &errn);
+// if (errn != OK) {
+// err += TTR("Invalid Android SDK path in Editor Settings.");
+// err += TTR("Missing 'build-tools' directory!");
+// err += "\n";
+// valid = false;
+// }
+
+// // Validate that apksigner is available
+// String apksigner_path = get_apksigner_path();
+// if (!FileAccess::exists(apksigner_path)) {
+// err += TTR("Unable to find Android SDK build-tools' apksigner command.");
+// err += TTR("Please check in the Android SDK directory specified in Editor Settings.");
+// err += "\n";
+// valid = false;
+// }
+// }
+
+// bool apk_expansion = p_preset->get("apk_expansion/enable");
+
+// if (apk_expansion) {
+// String apk_expansion_pkey = p_preset->get("apk_expansion/public_key");
+
+// if (apk_expansion_pkey == "") {
+// valid = false;
+
+// err += TTR("Invalid public key for APK expansion.") + "\n";
+// }
+// }
+
+// String pn = p_preset->get("package/unique_name");
+// String pn_err;
+
+// if (!is_package_name_valid(get_package_name(pn), &pn_err)) {
+// valid = false;
+// err += TTR("Invalid package name:") + " " + pn_err + "\n";
+// }
+
+// String etc_error = test_etc2();
+// if (etc_error != String()) {
+// valid = false;
+// err += etc_error;
+// }
+
+// // Ensure that `Use Custom Build` is enabled if a plugin is selected.
+// String enabled_plugins_names = get_plugins_names(get_enabled_plugins(p_preset));
+// bool custom_build_enabled = p_preset->get("custom_template/use_custom_build");
+// if (!enabled_plugins_names.is_empty() && !custom_build_enabled) {
+// valid = false;
+// err += TTR("\"Use Custom Build\" must be enabled to use the plugins.");
+// err += "\n";
+// }
+
+// // Validate the Xr features are properly populated
+// int xr_mode_index = p_preset->get("xr_features/xr_mode");
+// int degrees_of_freedom = p_preset->get("xr_features/degrees_of_freedom");
+// int hand_tracking = p_preset->get("xr_features/hand_tracking");
+// bool focus_awareness = p_preset->get("xr_features/focus_awareness");
+// if (xr_mode_index != /* XRMode.OVR*/ 1) {
+// if (degrees_of_freedom > 0) {
+// valid = false;
+// err += TTR("\"Degrees Of Freedom\" is only valid when \"Xr Mode\" is \"Oculus Mobile VR\".");
+// err += "\n";
+// }
+
+// if (hand_tracking > 0) {
+// valid = false;
+// err += TTR("\"Hand Tracking\" is only valid when \"Xr Mode\" is \"Oculus Mobile VR\".");
+// err += "\n";
+// }
+
+// if (focus_awareness) {
+// valid = false;
+// err += TTR("\"Focus Awareness\" is only valid when \"Xr Mode\" is \"Oculus Mobile VR\".");
+// err += "\n";
+// }
+// }
+
+// if (int(p_preset->get("custom_template/export_format")) == EXPORT_FORMAT_AAB &&
+// !bool(p_preset->get("custom_template/use_custom_build"))) {
+// valid = false;
+// err += TTR("\"Export AAB\" is only valid when \"Use Custom Build\" is enabled.");
+// err += "\n";
+// }
+
+// r_error = err;
+// return valid;
+// }
+
+// virtual List<String> get_binary_extensions(const Ref<EditorExportPreset> &p_preset) const override {
+// List<String> list;
+// list.push_back("apk");
+// list.push_back("aab");
+// return list;
+// }
+
+// inline bool is_clean_build_required(Vector<PluginConfig> enabled_plugins) {
+// String plugin_names = get_plugins_names(enabled_plugins);
+// bool first_build = last_custom_build_time == 0;
+// bool have_plugins_changed = false;
+
+// if (!first_build) {
+// have_plugins_changed = plugin_names != last_plugin_names;
+// if (!have_plugins_changed) {
+// for (int i = 0; i < enabled_plugins.size(); i++) {
+// if (enabled_plugins.get(i).last_updated > last_custom_build_time) {
+// have_plugins_changed = true;
+// break;
+// }
+// }
+// }
+// }
+
+// last_custom_build_time = OS::get_singleton()->get_unix_time();
+// last_plugin_names = plugin_names;
+
+// return have_plugins_changed || first_build;
+// }
+
+// String get_apk_expansion_fullpath(const Ref<EditorExportPreset> &p_preset, const String &p_path) {
+// int version_code = p_preset->get("version/code");
+// String package_name = p_preset->get("package/unique_name");
+// String apk_file_name = "main." + itos(version_code) + "." + get_package_name(package_name) + ".obb";
+// String fullpath = p_path.get_base_dir().plus_file(apk_file_name);
+// return fullpath;
+// }
+
+// Error save_apk_expansion_file(const Ref<EditorExportPreset> &p_preset, const String &p_path) {
+// String fullpath = get_apk_expansion_fullpath(p_preset, p_path);
+// Error err = save_pack(p_preset, fullpath);
+// return err;
+// }
+
+// void get_command_line_flags(const Ref<EditorExportPreset> &p_preset, const String &p_path, int p_flags, Vector<uint8_t> &r_command_line_flags) {
+// String cmdline = p_preset->get("command_line/extra_args");
+// Vector<String> command_line_strings = cmdline.strip_edges().split(" ");
+// for (int i = 0; i < command_line_strings.size(); i++) {
+// if (command_line_strings[i].strip_edges().length() == 0) {
+// command_line_strings.remove(i);
+// i--;
+// }
+// }
+
+// gen_export_flags(command_line_strings, p_flags);
+
+// bool apk_expansion = p_preset->get("apk_expansion/enable");
+// if (apk_expansion) {
+// String fullpath = get_apk_expansion_fullpath(p_preset, p_path);
+// String apk_expansion_public_key = p_preset->get("apk_expansion/public_key");
+
+// command_line_strings.push_back("--use_apk_expansion");
+// command_line_strings.push_back("--apk_expansion_md5");
+// command_line_strings.push_back(FileAccess::get_md5(fullpath));
+// command_line_strings.push_back("--apk_expansion_key");
+// command_line_strings.push_back(apk_expansion_public_key.strip_edges());
+// }
+
+// int xr_mode_index = p_preset->get("xr_features/xr_mode");
+// if (xr_mode_index == 1) {
+// command_line_strings.push_back("--xr_mode_ovr");
+// } else { // XRMode.REGULAR is the default.
+// command_line_strings.push_back("--xr_mode_regular");
+// }
+
+// bool use_32_bit_framebuffer = p_preset->get("graphics/32_bits_framebuffer");
+// if (use_32_bit_framebuffer) {
+// command_line_strings.push_back("--use_depth_32");
+// }
+
+// bool immersive = p_preset->get("screen/immersive_mode");
+// if (immersive) {
+// command_line_strings.push_back("--use_immersive");
+// }
+
+// bool debug_opengl = p_preset->get("graphics/opengl_debug");
+// if (debug_opengl) {
+// command_line_strings.push_back("--debug_opengl");
+// }
+
+// if (command_line_strings.size()) {
+// r_command_line_flags.resize(4);
+// encode_uint32(command_line_strings.size(), &r_command_line_flags.write[0]);
+// for (int i = 0; i < command_line_strings.size(); i++) {
+// print_line(itos(i) + " param: " + command_line_strings[i]);
+// CharString command_line_argument = command_line_strings[i].utf8();
+// int base = r_command_line_flags.size();
+// int length = command_line_argument.length();
+// if (length == 0)
+// continue;
+// r_command_line_flags.resize(base + 4 + length);
+// encode_uint32(length, &r_command_line_flags.write[base]);
+// copymem(&r_command_line_flags.write[base + 4], command_line_argument.ptr(), length);
+// }
+// }
+// }
+
+// Error sign_apk(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &export_path, EditorProgress &ep) {
+// int export_format = int(p_preset->get("custom_template/export_format"));
+// String export_label = export_format == EXPORT_FORMAT_AAB ? "AAB" : "APK";
+// String release_keystore = p_preset->get("keystore/release");
+// String release_username = p_preset->get("keystore/release_user");
+// String release_password = p_preset->get("keystore/release_password");
+
+// String apksigner = get_apksigner_path();
+// if (!FileAccess::exists(apksigner)) {
+// EditorNode::add_io_error("'apksigner' could not be found.\nPlease check the command is available in the Android SDK build-tools directory.\nThe resulting " + export_label + " is unsigned.");
+// return OK;
+// }
+
+// String keystore;
+// String password;
+// String user;
+// if (p_debug) {
+// keystore = p_preset->get("keystore/debug");
+// password = p_preset->get("keystore/debug_password");
+// user = p_preset->get("keystore/debug_user");
+
+// if (keystore.is_empty()) {
+// keystore = EditorSettings::get_singleton()->get("export/android/debug_keystore");
+// password = EditorSettings::get_singleton()->get("export/android/debug_keystore_pass");
+// user = EditorSettings::get_singleton()->get("export/android/debug_keystore_user");
+// }
+
+// if (ep.step("Signing debug " + export_label + "...", 104)) {
+// return ERR_SKIP;
+// }
+
+// } else {
+// keystore = release_keystore;
+// password = release_password;
+// user = release_username;
+
+// if (ep.step("Signing release " + export_label + "...", 104)) {
+// return ERR_SKIP;
+// }
+// }
+
+// if (!FileAccess::exists(keystore)) {
+// EditorNode::add_io_error("Could not find keystore, unable to export.");
+// return ERR_FILE_CANT_OPEN;
+// }
+
+// List<String> args;
+// args.push_back("sign");
+// args.push_back("--verbose");
+// args.push_back("--ks");
+// args.push_back(keystore);
+// args.push_back("--ks-pass");
+// args.push_back("pass:" + password);
+// args.push_back("--ks-key-alias");
+// args.push_back(user);
+// args.push_back(export_path);
+// int retval;
+// OS::get_singleton()->execute(apksigner, args, true, NULL, NULL, &retval);
+// if (retval) {
+// EditorNode::add_io_error("'apksigner' returned with error #" + itos(retval));
+// return ERR_CANT_CREATE;
+// }
+
+// if (ep.step("Verifying " + export_label + "...", 105)) {
+// return ERR_SKIP;
+// }
+
+// args.clear();
+// args.push_back("verify");
+// args.push_back("--verbose");
+// args.push_back(export_path);
+
+// OS::get_singleton()->execute(apksigner, args, true, NULL, NULL, &retval);
+// if (retval) {
+// EditorNode::add_io_error("'apksigner' verification of " + export_label + " failed.");
+// return ERR_CANT_CREATE;
+// }
+// return OK;
+// }
+
+// void _clear_assets_directory() {
+// DirAccessRef da_res = DirAccess::create(DirAccess::ACCESS_RESOURCES);
+// if (da_res->dir_exists("res://android/build/assets")) {
+// DirAccessRef da_assets = DirAccess::open("res://android/build/assets");
+// da_assets->erase_contents_recursive();
+// da_res->remove("res://android/build/assets");
+// }
+// }
+
+// virtual Error export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags = 0) override {
+// int export_format = int(p_preset->get("custom_template/export_format"));
+// bool should_sign = p_preset->get("package/signed");
+// return export_project_helper(p_preset, p_debug, p_path, export_format, should_sign, p_flags);
+// }
+
+// Error export_project_helper(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int export_format, bool should_sign, int p_flags) {
+// ExportNotifier notifier(*this, p_preset, p_debug, p_path, p_flags);
+
+// String src_apk;
+// Error err;
+
+// EditorProgress ep("export", "Exporting for Android", 105, true);
+
+// bool use_custom_build = bool(p_preset->get("custom_template/use_custom_build"));
+// bool p_give_internet = p_flags & (DEBUG_FLAG_DUMB_CLIENT | DEBUG_FLAG_REMOTE_DEBUG);
+// bool apk_expansion = p_preset->get("apk_expansion/enable");
+// Vector<String> enabled_abis = get_enabled_abis(p_preset);
+
+// Ref<Image> splash_image;
+// Ref<Image> splash_bg_color_image;
+// load_splash_refs(splash_image, splash_bg_color_image);
+
+// Ref<Image> main_image;
+// Ref<Image> foreground;
+// Ref<Image> background;
+
+// load_icon_refs(p_preset, main_image, foreground, background);
+
+// Vector<uint8_t> command_line_flags;
+// // Write command line flags into the command_line_flags variable.
+// get_command_line_flags(p_preset, p_path, p_flags, command_line_flags);
+
+// if (export_format == EXPORT_FORMAT_AAB) {
+// if (!p_path.ends_with(".aab")) {
+// EditorNode::get_singleton()->show_warning(TTR("Invalid filename! Android App Bundle requires the *.aab extension."));
+// return ERR_UNCONFIGURED;
+// }
+// if (apk_expansion) {
+// EditorNode::get_singleton()->show_warning(TTR("APK Expansion not compatible with Android App Bundle."));
+// return ERR_UNCONFIGURED;
+// }
+// }
+// if (export_format == EXPORT_FORMAT_APK && !p_path.ends_with(".apk")) {
+// EditorNode::get_singleton()->show_warning(
+// TTR("Invalid filename! Android APK requires the *.apk extension."));
+// return ERR_UNCONFIGURED;
+// }
+// if (export_format > EXPORT_FORMAT_AAB || export_format < EXPORT_FORMAT_APK) {
+// EditorNode::add_io_error("Unsupported export format!\n");
+// return ERR_UNCONFIGURED; //TODO: is this the right error?
+// }
+
+// if (use_custom_build) {
+// //test that installed build version is alright
+// {
+// FileAccessRef f = FileAccess::open("res://android/.build_version", FileAccess::READ);
+// if (!f) {
+// EditorNode::get_singleton()->show_warning(TTR("Trying to build from a custom built template, but no version info for it exists. Please reinstall from the 'Project' menu."));
+// return ERR_UNCONFIGURED;
+// }
+// String version = f->get_line().strip_edges();
+// f->close();
+// if (version != VERSION_FULL_CONFIG) {
+// EditorNode::get_singleton()->show_warning(vformat(TTR("Android build version mismatch:\n Template installed: %s\n Godot Version: %s\nPlease reinstall Android build template from 'Project' menu."), version, VERSION_FULL_CONFIG));
+// return ERR_UNCONFIGURED;
+// }
+// }
+// String sdk_path = EDITOR_GET("export/android/android_sdk_path");
+// ERR_FAIL_COND_V_MSG(sdk_path == "", ERR_UNCONFIGURED, "Android SDK path must be configured in Editor Settings at 'export/android/android_sdk_path'.");
+
+// // TODO: should we use "package/name" or "application/config/name"?
+// String project_name = get_project_name(p_preset->get("package/name"));
+// err = _create_project_name_strings_files(p_preset, project_name); //project name localization.
+// if (err != OK) {
+// EditorNode::add_io_error("Unable to overwrite res://android/build/res/*.xml files with project name");
+// }
+// // Copies the project icon files into the appropriate Gradle project directory.
+// _copy_icons_to_gradle_project(p_preset, splash_image, splash_bg_color_image, main_image, foreground, background);
+// // Write an AndroidManifest.xml file into the Gradle project directory.
+// _write_tmp_manifest(p_preset, p_give_internet, p_debug);
+
+// //stores all the project files inside the Gradle project directory. Also includes all ABIs
+// _clear_assets_directory();
+// if (!apk_expansion) {
+// err = export_project_files(p_preset, rename_and_store_file_in_gradle_project, NULL, ignore_so_file);
+// if (err != OK) {
+// EditorNode::add_io_error("Could not export project files to gradle project\n");
+// return err;
+// }
+// } else {
+// err = save_apk_expansion_file(p_preset, p_path);
+// if (err != OK) {
+// EditorNode::add_io_error("Could not write expansion package file!");
+// return err;
+// }
+// }
+// store_file_at_path("res://android/build/assets/_cl_", command_line_flags);
+
+// OS::get_singleton()->set_environment("ANDROID_HOME", sdk_path); //set and overwrite if required
+// String build_command;
+
+//#ifdef WINDOWS_ENABLED
+// build_command = "gradlew.bat";
+//#else
+// build_command = "gradlew";
+//#endif
+
+// String build_path = ProjectSettings::get_singleton()->get_resource_path().plus_file("android/build");
+// build_command = build_path.plus_file(build_command);
+
+// String package_name = get_package_name(p_preset->get("package/unique_name"));
+// String version_code = itos(p_preset->get("version/code"));
+// String version_name = p_preset->get("version/name");
+// String enabled_abi_string = String("|").join(enabled_abis);
+// String sign_flag = should_sign ? "true" : "false";
+// String zipalign_flag = "true";
+
+// Vector<PluginConfig> enabled_plugins = get_enabled_plugins(p_preset);
+// String local_plugins_binaries = get_plugins_binaries(BINARY_TYPE_LOCAL, enabled_plugins);
+// String remote_plugins_binaries = get_plugins_binaries(BINARY_TYPE_REMOTE, enabled_plugins);
+// String custom_maven_repos = get_plugins_custom_maven_repos(enabled_plugins);
+// bool clean_build_required = is_clean_build_required(enabled_plugins);
+
+// List<String> cmdline;
+// if (clean_build_required) {
+// cmdline.push_back("clean");
+// }
+
+// String build_type = p_debug ? "Debug" : "Release";
+// if (export_format == EXPORT_FORMAT_AAB) {
+// String bundle_build_command = vformat("bundle%s", build_type);
+// cmdline.push_back(bundle_build_command);
+// } else if (export_format == EXPORT_FORMAT_APK) {
+// String apk_build_command = vformat("assemble%s", build_type);
+// cmdline.push_back(apk_build_command);
+// }
+
+// cmdline.push_back("-Pexport_package_name=" + package_name); // argument to specify the package name.
+// cmdline.push_back("-Pexport_version_code=" + version_code); // argument to specify the version code.
+// cmdline.push_back("-Pexport_version_name=" + version_name); // argument to specify the version name.
+// cmdline.push_back("-Pexport_enabled_abis=" + enabled_abi_string); // argument to specify enabled ABIs.
+// cmdline.push_back("-Pplugins_local_binaries=" + local_plugins_binaries); // argument to specify the list of plugins local dependencies.
+// cmdline.push_back("-Pplugins_remote_binaries=" + remote_plugins_binaries); // argument to specify the list of plugins remote dependencies.
+// cmdline.push_back("-Pplugins_maven_repos=" + custom_maven_repos); // argument to specify the list of custom maven repos for the plugins dependencies.
+// cmdline.push_back("-Pperform_zipalign=" + zipalign_flag); // argument to specify whether the build should be zipaligned.
+// cmdline.push_back("-Pperform_signing=" + sign_flag); // argument to specify whether the build should be signed.
+// if (should_sign && !p_debug) {
+// // Pass the release keystore info as well
+// String release_keystore = p_preset->get("keystore/release");
+// String release_username = p_preset->get("keystore/release_user");
+// String release_password = p_preset->get("keystore/release_password");
+// if (!FileAccess::exists(release_keystore)) {
+// EditorNode::add_io_error("Could not find keystore, unable to export.");
+// return ERR_FILE_CANT_OPEN;
+// }
+
+// cmdline.push_back("-Prelease_keystore_file=" + release_keystore); // argument to specify the release keystore file.
+// cmdline.push_back("-Prelease_keystore_alias=" + release_username); // argument to specify the release keystore alias.
+// cmdline.push_back("-Prelease_keystore_password=" + release_password); // argument to specity the release keystore password.
+// }
+// cmdline.push_back("-p"); // argument to specify the start directory.
+// cmdline.push_back(build_path); // start directory.
+
+// int result = EditorNode::get_singleton()->execute_and_show_output(TTR("Building Android Project (gradle)"), build_command, cmdline);
+// if (result != 0) {
+// EditorNode::get_singleton()->show_warning(TTR("Building of Android project failed, check output for the error.\nAlternatively visit docs.godotengine.org for Android build documentation."));
+// return ERR_CANT_CREATE;
+// }
+
+// List<String> copy_args;
+// String copy_command;
+// if (export_format == EXPORT_FORMAT_AAB) {
+// copy_command = vformat("copyAndRename%sAab", build_type);
+// } else if (export_format == EXPORT_FORMAT_APK) {
+// copy_command = vformat("copyAndRename%sApk", build_type);
+// }
+
+// copy_args.push_back(copy_command);
+
+// copy_args.push_back("-p"); // argument to specify the start directory.
+// copy_args.push_back(build_path); // start directory.
+
+// String export_filename = p_path.get_file();
+// String export_path = p_path.get_base_dir();
+// if (export_path.is_rel_path()) {
+// export_path = OS::get_singleton()->get_resource_dir().plus_file(export_path);
+// }
+// export_path = ProjectSettings::get_singleton()->globalize_path(export_path).simplify_path();
+
+// copy_args.push_back("-Pexport_path=file:" + export_path);
+// copy_args.push_back("-Pexport_filename=" + export_filename);
+
+// int copy_result = EditorNode::get_singleton()->execute_and_show_output(TTR("Moving output"), build_command, copy_args);
+// if (copy_result != 0) {
+// EditorNode::get_singleton()->show_warning(TTR("Unable to copy and rename export file, check gradle project directory for outputs."));
+// return ERR_CANT_CREATE;
+// }
+
+// return OK;
+// }
+// // This is the start of the Legacy build system
+// if (p_debug)
+// src_apk = p_preset->get("custom_template/debug");
+// else
+// src_apk = p_preset->get("custom_template/release");
+// src_apk = src_apk.strip_edges();
+// if (src_apk == "") {
+// if (p_debug) {
+// src_apk = find_export_template("android_debug.apk");
+// } else {
+// src_apk = find_export_template("android_release.apk");
+// }
+// if (src_apk == "") {
+// EditorNode::add_io_error("Package not found: " + src_apk);
+// return ERR_FILE_NOT_FOUND;
+// }
+// }
+
+// if (!DirAccess::exists(p_path.get_base_dir())) {
+// return ERR_FILE_BAD_PATH;
+// }
+
+// FileAccess *src_f = nullptr;
+// zlib_filefunc_def io = zipio_create_io_from_file(&src_f);
+
+// if (ep.step("Creating APK...", 0)) {
+// return ERR_SKIP;
+// }
+
+// unzFile pkg = unzOpen2(src_apk.utf8().get_data(), &io);
+// if (!pkg) {
+// EditorNode::add_io_error("Could not find template APK to export:\n" + src_apk);
+// return ERR_FILE_NOT_FOUND;
+// }
+
+// int ret = unzGoToFirstFile(pkg);
+
+// zlib_filefunc_def io2 = io;
+// FileAccess *dst_f = nullptr;
+// io2.opaque = &dst_f;
+
+// String tmp_unaligned_path = EditorSettings::get_singleton()->get_cache_dir().plus_file("tmpexport-unaligned.apk");
+
+//#define CLEANUP_AND_RETURN(m_err) \
+// { \
+// DirAccess::remove_file_or_error(tmp_unaligned_path); \
+// return m_err; \
+// }
+
+// zipFile unaligned_apk = zipOpen2(tmp_unaligned_path.utf8().get_data(), APPEND_STATUS_CREATE, nullptr, &io2);
+
+// String cmdline = p_preset->get("command_line/extra_args");
+
+// String version_name = p_preset->get("version/name");
+// String package_name = p_preset->get("package/unique_name");
+
+// String apk_expansion_pkey = p_preset->get("apk_expansion/public_key");
+
+// Vector<String> invalid_abis(enabled_abis);
+// while (ret == UNZ_OK) {
+// //get filename
+// unz_file_info info;
+// char fname[16384];
+// ret = unzGetCurrentFileInfo(pkg, &info, fname, 16384, nullptr, 0, nullptr, 0);
+
+// bool skip = false;
+
+// String file = fname;
+
+// Vector<uint8_t> data;
+// data.resize(info.uncompressed_size);
+
+// //read
+// unzOpenCurrentFile(pkg);
+// unzReadCurrentFile(pkg, data.ptrw(), data.size());
+// unzCloseCurrentFile(pkg);
+
+// //write
+// if (file == "AndroidManifest.xml") {
+// _fix_manifest(p_preset, data, p_give_internet);
+// }
+// if (file == "resources.arsc") {
+// _fix_resources(p_preset, data);
+// }
+
+// // Process the splash image
+// if (file == SPLASH_IMAGE_EXPORT_PATH && splash_image.is_valid() && !splash_image->is_empty()) {
+// _load_image_data(splash_image, data);
+// }
+
+// // Process the splash bg color image
+// if (file == SPLASH_BG_COLOR_PATH && splash_bg_color_image.is_valid() && !splash_bg_color_image->is_empty()) {
+// _load_image_data(splash_bg_color_image, data);
+// }
+
+// for (int i = 0; i < icon_densities_count; ++i) {
+// if (main_image.is_valid() && !main_image->is_empty()) {
+// if (file == launcher_icons[i].export_path) {
+// _process_launcher_icons(file, main_image, launcher_icons[i].dimensions, data);
+// }
+// }
+// if (foreground.is_valid() && !foreground->is_empty()) {
+// if (file == launcher_adaptive_icon_foregrounds[i].export_path) {
+// _process_launcher_icons(file, foreground, launcher_adaptive_icon_foregrounds[i].dimensions, data);
+// }
+// }
+// if (background.is_valid() && !background->is_empty()) {
+// if (file == launcher_adaptive_icon_backgrounds[i].export_path) {
+// _process_launcher_icons(file, background, launcher_adaptive_icon_backgrounds[i].dimensions, data);
+// }
+// }
+// }
+
+// if (file.ends_with(".so")) {
+// bool enabled = false;
+// for (int i = 0; i < enabled_abis.size(); ++i) {
+// if (file.begins_with("lib/" + enabled_abis[i] + "/")) {
+// invalid_abis.erase(enabled_abis[i]);
+// enabled = true;
+// break;
+// }
+// }
+// if (!enabled) {
+// skip = true;
+// }
+// }
+
+// if (file.begins_with("META-INF") && should_sign) {
+// skip = true;
+// }
+
+// if (!skip) {
+// print_line("ADDING: " + file);
+
+// // Respect decision on compression made by AAPT for the export template
+// const bool uncompressed = info.compression_method == 0;
+
+// zip_fileinfo zipfi = get_zip_fileinfo();
+
+// zipOpenNewFileInZip(unaligned_apk,
+// file.utf8().get_data(),
+// &zipfi,
+// nullptr,
+// 0,
+// nullptr,
+// 0,
+// nullptr,
+// uncompressed ? 0 : Z_DEFLATED,
+// Z_DEFAULT_COMPRESSION);
+
+// zipWriteInFileInZip(unaligned_apk, data.ptr(), data.size());
+// zipCloseFileInZip(unaligned_apk);
+// }
+
+// ret = unzGoToNextFile(pkg);
+// }
+
+// if (!invalid_abis.is_empty()) {
+// String unsupported_arch = String(", ").join(invalid_abis);
+// EditorNode::add_io_error("Missing libraries in the export template for the selected architectures: " + unsupported_arch + ".\n" +
+// "Please build a template with all required libraries, or uncheck the missing architectures in the export preset.");
+// CLEANUP_AND_RETURN(ERR_FILE_NOT_FOUND);
+// }
+
+// if (ep.step("Adding files...", 1)) {
+// CLEANUP_AND_RETURN(ERR_SKIP);
+// }
+// err = OK;
+
+// if (p_flags & DEBUG_FLAG_DUMB_CLIENT) {
+// APKExportData ed;
+// ed.ep = &ep;
+// ed.apk = unaligned_apk;
+// err = export_project_files(p_preset, ignore_apk_file, &ed, save_apk_so);
+// } else {
+// if (apk_expansion) {
+// err = save_apk_expansion_file(p_preset, p_path);
+// if (err != OK) {
+// EditorNode::add_io_error("Could not write expansion package file!");
+// return err;
+// }
+// } else {
+// APKExportData ed;
+// ed.ep = &ep;
+// ed.apk = unaligned_apk;
+// err = export_project_files(p_preset, save_apk_file, &ed, save_apk_so);
+// }
+// }
+
+// if (err != OK) {
+// unzClose(pkg);
+// EditorNode::add_io_error("Could not export project files");
+// CLEANUP_AND_RETURN(ERR_SKIP);
+// }
+
+// zip_fileinfo zipfi = get_zip_fileinfo();
+// zipOpenNewFileInZip(unaligned_apk,
+// "assets/_cl_",
+// &zipfi,
+// NULL,
+// 0,
+// NULL,
+// 0,
+// NULL,
+// 0, // No compress (little size gain and potentially slower startup)
+// Z_DEFAULT_COMPRESSION);
+// zipWriteInFileInZip(unaligned_apk, command_line_flags.ptr(), command_line_flags.size());
+// zipCloseFileInZip(unaligned_apk);
+// zipClose(unaligned_apk, nullptr);
+// unzClose(pkg);
+
+// if (err != OK) {
+// CLEANUP_AND_RETURN(err);
+// }
+
+// // Let's zip-align (must be done before signing)
+
+// static const int ZIP_ALIGNMENT = 4;
+
+// // If we're not signing the apk, then the next step should be the last.
+// const int next_step = should_sign ? 103 : 105;
+// if (ep.step("Aligning APK...", next_step)) {
+// CLEANUP_AND_RETURN(ERR_SKIP);
+// }
+
+// unzFile tmp_unaligned = unzOpen2(tmp_unaligned_path.utf8().get_data(), &io);
+// if (!tmp_unaligned) {
+// EditorNode::add_io_error("Could not unzip temporary unaligned APK.");
+// CLEANUP_AND_RETURN(ERR_FILE_NOT_FOUND);
+// }
+
+// ret = unzGoToFirstFile(tmp_unaligned);
+
+// io2 = io;
+// dst_f = nullptr;
+// io2.opaque = &dst_f;
+// zipFile final_apk = zipOpen2(p_path.utf8().get_data(), APPEND_STATUS_CREATE, nullptr, &io2);
+
+// // Take files from the unaligned APK and write them out to the aligned one
+// // in raw mode, i.e. not uncompressing and recompressing, aligning them as needed,
+// // following what is done in https://github.com/android/platform_build/blob/master/tools/zipalign/ZipAlign.cpp
+// int bias = 0;
+// while (ret == UNZ_OK) {
+// unz_file_info info;
+// memset(&info, 0, sizeof(info));
+
+// char fname[16384];
+// char extra[16384];
+// ret = unzGetCurrentFileInfo(tmp_unaligned, &info, fname, 16384, extra, 16384 - ZIP_ALIGNMENT, nullptr, 0);
+
+// String file = fname;
+
+// Vector<uint8_t> data;
+// data.resize(info.compressed_size);
+
+// // read
+// int method, level;
+// unzOpenCurrentFile2(tmp_unaligned, &method, &level, 1); // raw read
+// long file_offset = unzGetCurrentFileZStreamPos64(tmp_unaligned);
+// unzReadCurrentFile(tmp_unaligned, data.ptrw(), data.size());
+// unzCloseCurrentFile(tmp_unaligned);
+
+// // align
+// int padding = 0;
+// if (!info.compression_method) {
+// // Uncompressed file => Align
+// long new_offset = file_offset + bias;
+// padding = (ZIP_ALIGNMENT - (new_offset % ZIP_ALIGNMENT)) % ZIP_ALIGNMENT;
+// }
+
+// memset(extra + info.size_file_extra, 0, padding);
+
+// zip_fileinfo fileinfo = get_zip_fileinfo();
+// zipOpenNewFileInZip2(final_apk,
+// file.utf8().get_data(),
+// &fileinfo,
+// extra,
+// info.size_file_extra + padding,
+// nullptr,
+// 0,
+// nullptr,
+// method,
+// level,
+// 1); // raw write
+// zipWriteInFileInZip(final_apk, data.ptr(), data.size());
+// zipCloseFileInZipRaw(final_apk, info.uncompressed_size, info.crc);
+
+// bias += padding;
+
+// ret = unzGoToNextFile(tmp_unaligned);
+// }
+
+// zipClose(final_apk, nullptr);
+// unzClose(tmp_unaligned);
+
+// if (should_sign) {
+// // Signing must be done last as any additional modifications to the
+// // file will invalidate the signature.
+// err = sign_apk(p_preset, p_debug, p_path, ep);
+// if (err != OK) {
+// CLEANUP_AND_RETURN(err);
+// }
+// }
+
+// CLEANUP_AND_RETURN(OK);
+// }
+
+// virtual void get_platform_features(List<String> *r_features) override {
+// r_features->push_back("mobile");
+// r_features->push_back("Android");
+// }
+
+// virtual void resolve_platform_feature_priorities(const Ref<EditorExportPreset> &p_preset, Set<String> &p_features) override {
+// }
+
+// EditorExportPlatformAndroid() {
+// Ref<Image> img = memnew(Image(_android_logo));
+// logo.instance();
+// logo->create_from_image(img);
+
+// img = Ref<Image>(memnew(Image(_android_run_icon)));
+// run_icon.instance();
+// run_icon->create_from_image(img);
+
+// devices_changed = true;
+// plugins_changed = true;
+// quit_request = false;
+// check_for_changes_thread = Thread::create(_check_for_changes_poll_thread, this);
+// }
+
+// ~EditorExportPlatformAndroid() {
+// quit_request = true;
+// Thread::wait_to_finish(check_for_changes_thread);
+// memdelete(check_for_changes_thread);
+// }
+//};
void register_android_exporter() {
String exe_ext;
diff --git a/platform/android/export/gradle_export_util.h b/platform/android/export/gradle_export_util.h
index 744022f1f9..7c773309e8 100644
--- a/platform/android/export/gradle_export_util.h
+++ b/platform/android/export/gradle_export_util.h
@@ -74,6 +74,51 @@ Error rename_and_store_file_in_gradle_project(void *p_userdata, const String &p_
// Creates strings.xml files inside the gradle project for different locales.
Error _create_project_name_strings_files(const Ref<EditorExportPreset> &p_preset, const String &project_name);
+//Error _create_project_name_strings_files(const Ref<EditorExportPreset> &p_preset, const String &project_name) {
+// // Stores the string into the default values directory.
+// String processed_default_xml_string = vformat(godot_project_name_xml_string, project_name.xml_escape(true));
+// store_string_at_path("res://android/build/res/values/godot_project_name_string.xml", processed_default_xml_string);
+
+// // Searches the Gradle project res/ directory to find all supported locales
+// DirAccessRef da = DirAccess::open("res://android/build/res");
+// if (!da) {
+// return ERR_CANT_OPEN;
+// }
+// da->list_dir_begin();
+// while (true) {
+// String file = da->get_next();
+// if (file == "") {
+// break;
+// }
+// if (!file.begins_with("values-")) {
+// // NOTE: This assumes all directories that start with "values-" are for localization.
+// continue;
+// }
+// String locale = file.replace("values-", "").replace("-r", "_");
+// String property_name = "application/config/name_" + locale;
+// String locale_directory = "res://android/build/res/" + file + "/godot_project_name_string.xml";
+// if (ProjectSettings::get_singleton()->has_setting(property_name)) {
+// String locale_project_name = ProjectSettings::get_singleton()->get(property_name);
+// String processed_xml_string = vformat(godot_project_name_xml_string, locale_project_name.xml_escape(true));
+// store_string_at_path(locale_directory, processed_xml_string);
+// } else {
+// // TODO: Once the legacy build system is deprecated we don't need to have xml files for this else branch
+// store_string_at_path(locale_directory, processed_default_xml_string);
+// }
+// }
+// da->list_dir_end();
+// return OK;
+//}
+
+//String bool_to_string(bool v) {
+// return v ? "true" : "false";
+//}
+
+//String _get_gles_tag() {
+// bool min_gles3 = ProjectSettings::get_singleton()->get("rendering/driver/driver_name") == "GLES3" &&
+// !ProjectSettings::get_singleton()->get("rendering/quality/driver/fallback_to_gles2");
+// return min_gles3 ? " <uses-feature android:glEsVersion=\"0x00030000\" android:required=\"true\" />\n" : "";
+//}
String bool_to_string(bool v);
diff --git a/platform/iphone/export/export.cpp b/platform/iphone/export/export.cpp
index 208626ae36..6cec365885 100644
--- a/platform/iphone/export/export.cpp
+++ b/platform/iphone/export/export.cpp
@@ -31,6 +31,1929 @@
#include "export.h"
#include "export_plugin.h"
+//#include "core/config/project_settings.h"
+//#include "core/io/image_loader.h"
+//#include "core/io/marshalls.h"
+//#include "core/io/resource_saver.h"
+//#include "core/io/zip_io.h"
+//#include "core/os/file_access.h"
+//#include "core/os/os.h"
+//#include "core/version.h"
+//#include "editor/editor_export.h"
+//#include "editor/editor_node.h"
+//#include "editor/editor_settings.h"
+//#include "main/splash.gen.h"
+//#include "platform/iphone/logo.gen.h"
+//#include "platform/iphone/plugin/godot_plugin_config.h"
+//#include "string.h"
+
+//#include <sys/stat.h>
+
+//class EditorExportPlatformIOS : public EditorExportPlatform {
+// GDCLASS(EditorExportPlatformIOS, EditorExportPlatform);
+
+// int version_code;
+
+// Ref<ImageTexture> logo;
+
+// // Plugins
+// volatile bool plugins_changed;
+// Thread *check_for_changes_thread;
+// volatile bool quit_request;
+// Mutex plugins_lock;
+// Vector<PluginConfig> plugins;
+
+// typedef Error (*FileHandler)(String p_file, void *p_userdata);
+// static Error _walk_dir_recursive(DirAccess *p_da, FileHandler p_handler, void *p_userdata);
+// static Error _codesign(String p_file, void *p_userdata);
+// void _blend_and_rotate(Ref<Image> &p_dst, Ref<Image> &p_src, bool p_rot);
+
+// struct IOSConfigData {
+// String pkg_name;
+// String binary_name;
+// String plist_content;
+// String architectures;
+// String linker_flags;
+// String cpp_code;
+// String modules_buildfile;
+// String modules_fileref;
+// String modules_buildphase;
+// String modules_buildgrp;
+// Vector<String> capabilities;
+// };
+// struct ExportArchitecture {
+// String name;
+// bool is_default = false;
+
+// ExportArchitecture() {}
+
+// ExportArchitecture(String p_name, bool p_is_default) {
+// name = p_name;
+// is_default = p_is_default;
+// }
+// };
+
+// struct IOSExportAsset {
+// String exported_path;
+// bool is_framework = false; // framework is anything linked to the binary, otherwise it's a resource
+// bool should_embed = false;
+// };
+
+// String _get_additional_plist_content();
+// String _get_linker_flags();
+// String _get_cpp_code();
+// void _fix_config_file(const Ref<EditorExportPreset> &p_preset, Vector<uint8_t> &pfile, const IOSConfigData &p_config, bool p_debug);
+// Error _export_loading_screen_images(const Ref<EditorExportPreset> &p_preset, const String &p_dest_dir);
+// Error _export_loading_screen_file(const Ref<EditorExportPreset> &p_preset, const String &p_dest_dir);
+// Error _export_icons(const Ref<EditorExportPreset> &p_preset, const String &p_iconset_dir);
+
+// Vector<ExportArchitecture> _get_supported_architectures();
+// Vector<String> _get_preset_architectures(const Ref<EditorExportPreset> &p_preset);
+
+// void _add_assets_to_project(const Ref<EditorExportPreset> &p_preset, Vector<uint8_t> &p_project_data, const Vector<IOSExportAsset> &p_additional_assets);
+// Error _export_additional_assets(const String &p_out_dir, const Vector<String> &p_assets, bool p_is_framework, bool p_should_embed, Vector<IOSExportAsset> &r_exported_assets);
+// Error _copy_asset(const String &p_out_dir, const String &p_asset, const String *p_custom_file_name, bool p_is_framework, bool p_should_embed, Vector<IOSExportAsset> &r_exported_assets);
+// Error _export_additional_assets(const String &p_out_dir, const Vector<SharedObject> &p_libraries, Vector<IOSExportAsset> &r_exported_assets);
+// Error _export_ios_plugins(const Ref<EditorExportPreset> &p_preset, IOSConfigData &p_config_data, const String &dest_dir, Vector<IOSExportAsset> &r_exported_assets, bool p_debug);
+
+// bool is_package_name_valid(const String &p_package, String *r_error = nullptr) const {
+// String pname = p_package;
+
+// if (pname.length() == 0) {
+// if (r_error) {
+// *r_error = TTR("Identifier is missing.");
+// }
+// return false;
+// }
+
+// for (int i = 0; i < pname.length(); i++) {
+// char32_t c = pname[i];
+// if (!((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '-' || c == '.')) {
+// if (r_error) {
+// *r_error = vformat(TTR("The character '%s' is not allowed in Identifier."), String::chr(c));
+// }
+// return false;
+// }
+// }
+
+// return true;
+// }
+
+// static void _check_for_changes_poll_thread(void *ud) {
+// EditorExportPlatformIOS *ea = (EditorExportPlatformIOS *)ud;
+
+// while (!ea->quit_request) {
+// // Nothing to do if we already know the plugins have changed.
+// if (!ea->plugins_changed) {
+// MutexLock lock(ea->plugins_lock);
+
+// Vector<PluginConfig> loaded_plugins = get_plugins();
+
+// if (ea->plugins.size() != loaded_plugins.size()) {
+// ea->plugins_changed = true;
+// } else {
+// for (int i = 0; i < ea->plugins.size(); i++) {
+// if (ea->plugins[i].name != loaded_plugins[i].name || ea->plugins[i].last_updated != loaded_plugins[i].last_updated) {
+// ea->plugins_changed = true;
+// break;
+// }
+// }
+// }
+// }
+
+// uint64_t wait = 3000000;
+// uint64_t time = OS::get_singleton()->get_ticks_usec();
+// while (OS::get_singleton()->get_ticks_usec() - time < wait) {
+// OS::get_singleton()->delay_usec(300000);
+
+// if (ea->quit_request) {
+// break;
+// }
+// }
+// }
+// }
+
+//protected:
+// virtual void get_preset_features(const Ref<EditorExportPreset> &p_preset, List<String> *r_features) override;
+// virtual void get_export_options(List<ExportOption> *r_options) override;
+
+//public:
+// virtual String get_name() const override { return "iOS"; }
+// virtual String get_os_name() const override { return "iOS"; }
+// virtual Ref<Texture2D> get_logo() const override { return logo; }
+
+// virtual bool should_update_export_options() override {
+// bool export_options_changed = plugins_changed;
+// if (export_options_changed) {
+// // don't clear unless we're reporting true, to avoid race
+// plugins_changed = false;
+// }
+// return export_options_changed;
+// }
+
+// virtual List<String> get_binary_extensions(const Ref<EditorExportPreset> &p_preset) const override {
+// List<String> list;
+// list.push_back("ipa");
+// return list;
+// }
+// virtual Error export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags = 0) override;
+
+// virtual bool can_export(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates) const override;
+
+// virtual void get_platform_features(List<String> *r_features) override {
+// r_features->push_back("mobile");
+// r_features->push_back("iOS");
+// }
+
+// virtual void resolve_platform_feature_priorities(const Ref<EditorExportPreset> &p_preset, Set<String> &p_features) override {
+// }
+
+// EditorExportPlatformIOS();
+// ~EditorExportPlatformIOS();
+
+// /// List the gdip files in the directory specified by the p_path parameter.
+// static Vector<String> list_plugin_config_files(const String &p_path, bool p_check_directories) {
+// Vector<String> dir_files;
+// DirAccessRef da = DirAccess::open(p_path);
+// if (da) {
+// da->list_dir_begin();
+// while (true) {
+// String file = da->get_next();
+// if (file.is_empty()) {
+// break;
+// }
+
+// if (file == "." || file == "..") {
+// continue;
+// }
+
+// if (da->current_is_hidden()) {
+// continue;
+// }
+
+// if (da->current_is_dir()) {
+// if (p_check_directories) {
+// Vector<String> directory_files = list_plugin_config_files(p_path.plus_file(file), false);
+// for (int i = 0; i < directory_files.size(); ++i) {
+// dir_files.push_back(file.plus_file(directory_files[i]));
+// }
+// }
+
+// continue;
+// }
+
+// if (file.ends_with(PLUGIN_CONFIG_EXT)) {
+// dir_files.push_back(file);
+// }
+// }
+// da->list_dir_end();
+// }
+
+// return dir_files;
+// }
+
+// static Vector<PluginConfig> get_plugins() {
+// Vector<PluginConfig> loaded_plugins;
+
+// String plugins_dir = ProjectSettings::get_singleton()->get_resource_path().plus_file("ios/plugins");
+
+// if (DirAccess::exists(plugins_dir)) {
+// Vector<String> plugins_filenames = list_plugin_config_files(plugins_dir, true);
+
+// if (!plugins_filenames.is_empty()) {
+// Ref<ConfigFile> config_file = memnew(ConfigFile);
+// for (int i = 0; i < plugins_filenames.size(); i++) {
+// PluginConfig config = load_plugin_config(config_file, plugins_dir.plus_file(plugins_filenames[i]));
+// if (config.valid_config) {
+// loaded_plugins.push_back(config);
+// } else {
+// print_error("Invalid plugin config file " + plugins_filenames[i]);
+// }
+// }
+// }
+// }
+
+// return loaded_plugins;
+// }
+
+// static Vector<PluginConfig> get_enabled_plugins(const Ref<EditorExportPreset> &p_presets) {
+// Vector<PluginConfig> enabled_plugins;
+// Vector<PluginConfig> all_plugins = get_plugins();
+// for (int i = 0; i < all_plugins.size(); i++) {
+// PluginConfig plugin = all_plugins[i];
+// bool enabled = p_presets->get("plugins/" + plugin.name);
+// if (enabled) {
+// enabled_plugins.push_back(plugin);
+// }
+// }
+
+// return enabled_plugins;
+// }
+//};
+
+//void EditorExportPlatformIOS::get_preset_features(const Ref<EditorExportPreset> &p_preset, List<String> *r_features) {
+// String driver = ProjectSettings::get_singleton()->get("rendering/driver/driver_name");
+// r_features->push_back("pvrtc");
+// if (driver == "Vulkan") {
+// // FIXME: Review if this is correct.
+// r_features->push_back("etc2");
+// }
+
+// Vector<String> architectures = _get_preset_architectures(p_preset);
+// for (int i = 0; i < architectures.size(); ++i) {
+// r_features->push_back(architectures[i]);
+// }
+//}
+
+//Vector<EditorExportPlatformIOS::ExportArchitecture> EditorExportPlatformIOS::_get_supported_architectures() {
+// Vector<ExportArchitecture> archs;
+// archs.push_back(ExportArchitecture("armv7", false)); // Disabled by default, not included in official templates.
+// archs.push_back(ExportArchitecture("arm64", true));
+// return archs;
+//}
+
+//struct LoadingScreenInfo {
+// const char *preset_key;
+// const char *export_name;
+// int width = 0;
+// int height = 0;
+// bool rotate = false;
+//};
+
+//static const LoadingScreenInfo loading_screen_infos[] = {
+// { "landscape_launch_screens/iphone_2436x1125", "Default-Landscape-X.png", 2436, 1125, false },
+// { "landscape_launch_screens/iphone_2208x1242", "Default-Landscape-736h@3x.png", 2208, 1242, false },
+// { "landscape_launch_screens/ipad_1024x768", "Default-Landscape.png", 1024, 768, false },
+// { "landscape_launch_screens/ipad_2048x1536", "Default-Landscape@2x.png", 2048, 1536, false },
+
+// { "portrait_launch_screens/iphone_640x960", "Default-480h@2x.png", 640, 960, true },
+// { "portrait_launch_screens/iphone_640x1136", "Default-568h@2x.png", 640, 1136, true },
+// { "portrait_launch_screens/iphone_750x1334", "Default-667h@2x.png", 750, 1334, true },
+// { "portrait_launch_screens/iphone_1125x2436", "Default-Portrait-X.png", 1125, 2436, true },
+// { "portrait_launch_screens/ipad_768x1024", "Default-Portrait.png", 768, 1024, true },
+// { "portrait_launch_screens/ipad_1536x2048", "Default-Portrait@2x.png", 1536, 2048, true },
+// { "portrait_launch_screens/iphone_1242x2208", "Default-Portrait-736h@3x.png", 1242, 2208, true }
+//};
+
+//void EditorExportPlatformIOS::get_export_options(List<ExportOption> *r_options) {
+// r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "custom_template/debug", PROPERTY_HINT_GLOBAL_FILE, "*.zip"), ""));
+// r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "custom_template/release", PROPERTY_HINT_GLOBAL_FILE, "*.zip"), ""));
+
+// Vector<ExportArchitecture> architectures = _get_supported_architectures();
+// for (int i = 0; i < architectures.size(); ++i) {
+// r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "architectures/" + architectures[i].name), architectures[i].is_default));
+// }
+
+// r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/app_store_team_id"), ""));
+
+// r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/provisioning_profile_uuid_debug"), ""));
+// r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/code_sign_identity_debug", PROPERTY_HINT_PLACEHOLDER_TEXT, "iPhone Developer"), "iPhone Developer"));
+// r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "application/export_method_debug", PROPERTY_HINT_ENUM, "App Store,Development,Ad-Hoc,Enterprise"), 1));
+// r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/provisioning_profile_uuid_release"), ""));
+// r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/code_sign_identity_release", PROPERTY_HINT_PLACEHOLDER_TEXT, "iPhone Distribution"), ""));
+// r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "application/export_method_release", PROPERTY_HINT_ENUM, "App Store,Development,Ad-Hoc,Enterprise"), 0));
+
+// r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/name", PROPERTY_HINT_PLACEHOLDER_TEXT, "Game Name"), ""));
+// r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/info"), "Made with Godot Engine"));
+// r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/bundle_identifier", PROPERTY_HINT_PLACEHOLDER_TEXT, "com.example.game"), ""));
+// r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/signature"), ""));
+// r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/short_version"), "1.0"));
+// r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/version"), "1.0"));
+// r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/copyright"), ""));
+
+// Vector<PluginConfig> found_plugins = get_plugins();
+// for (int i = 0; i < found_plugins.size(); i++) {
+// r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "plugins/" + found_plugins[i].name), false));
+// }
+// plugins_changed = false;
+// plugins = found_plugins;
+
+// r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "capabilities/access_wifi"), false));
+// r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "capabilities/push_notifications"), false));
+
+// r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "user_data/accessible_from_files_app"), false));
+// r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "user_data/accessible_from_itunes_sharing"), false));
+
+// r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "privacy/camera_usage_description", PROPERTY_HINT_PLACEHOLDER_TEXT, "Provide a message if you need to use the camera"), ""));
+// r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "privacy/microphone_usage_description", PROPERTY_HINT_PLACEHOLDER_TEXT, "Provide a message if you need to use the microphone"), ""));
+// r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "privacy/photolibrary_usage_description", PROPERTY_HINT_PLACEHOLDER_TEXT, "Provide a message if you need access to the photo library"), ""));
+
+// r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "orientation/portrait"), true));
+// r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "orientation/landscape_left"), true));
+// r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "orientation/landscape_right"), true));
+// r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "orientation/portrait_upside_down"), true));
+
+// r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "icons/generate_missing"), false));
+
+// r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "required_icons/iphone_120x120", PROPERTY_HINT_FILE, "*.png"), "")); // Home screen on iPhone/iPod Touch with retina display
+// r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "required_icons/ipad_76x76", PROPERTY_HINT_FILE, "*.png"), "")); // Home screen on iPad
+// r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "required_icons/app_store_1024x1024", PROPERTY_HINT_FILE, "*.png"), "")); // App Store
+
+// r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "optional_icons/iphone_180x180", PROPERTY_HINT_FILE, "*.png"), "")); // Home screen on iPhone with retina HD display
+// r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "optional_icons/ipad_152x152", PROPERTY_HINT_FILE, "*.png"), "")); // Home screen on iPad with retina display
+// r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "optional_icons/ipad_167x167", PROPERTY_HINT_FILE, "*.png"), "")); // Home screen on iPad Pro
+// r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "optional_icons/spotlight_40x40", PROPERTY_HINT_FILE, "*.png"), "")); // Spotlight
+// r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "optional_icons/spotlight_80x80", PROPERTY_HINT_FILE, "*.png"), "")); // Spotlight on devices with retina display
+
+// r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "storyboard/use_launch_screen_storyboard"), false));
+// r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "storyboard/image_scale_mode", PROPERTY_HINT_ENUM, "Same as Logo,Center,Scale To Fit,Scale To Fill,Scale"), 0));
+// r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "storyboard/custom_image@2x", PROPERTY_HINT_FILE, "*.png"), ""));
+// r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "storyboard/custom_image@3x", PROPERTY_HINT_FILE, "*.png"), ""));
+// r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "storyboard/use_custom_bg_color"), false));
+// r_options->push_back(ExportOption(PropertyInfo(Variant::COLOR, "storyboard/custom_bg_color"), Color()));
+
+// r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "launch_screens/generate_missing"), false));
+
+// for (uint64_t i = 0; i < sizeof(loading_screen_infos) / sizeof(loading_screen_infos[0]); ++i) {
+// r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, loading_screen_infos[i].preset_key, PROPERTY_HINT_FILE, "*.png"), ""));
+// }
+//}
+
+//void EditorExportPlatformIOS::_fix_config_file(const Ref<EditorExportPreset> &p_preset, Vector<uint8_t> &pfile, const IOSConfigData &p_config, bool p_debug) {
+// static const String export_method_string[] = {
+// "app-store",
+// "development",
+// "ad-hoc",
+// "enterprise"
+// };
+// static const String storyboard_image_scale_mode[] = {
+// "center",
+// "scaleAspectFit",
+// "scaleAspectFill",
+// "scaleToFill"
+// };
+// String str;
+// String strnew;
+// 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) {
+// strnew += lines[i].replace("$binary", p_config.binary_name) + "\n";
+// } else if (lines[i].find("$modules_buildfile") != -1) {
+// strnew += lines[i].replace("$modules_buildfile", p_config.modules_buildfile) + "\n";
+// } else if (lines[i].find("$modules_fileref") != -1) {
+// strnew += lines[i].replace("$modules_fileref", p_config.modules_fileref) + "\n";
+// } else if (lines[i].find("$modules_buildphase") != -1) {
+// strnew += lines[i].replace("$modules_buildphase", p_config.modules_buildphase) + "\n";
+// } else if (lines[i].find("$modules_buildgrp") != -1) {
+// strnew += lines[i].replace("$modules_buildgrp", p_config.modules_buildgrp) + "\n";
+// } else if (lines[i].find("$name") != -1) {
+// strnew += lines[i].replace("$name", p_config.pkg_name) + "\n";
+// } else if (lines[i].find("$info") != -1) {
+// strnew += lines[i].replace("$info", p_preset->get("application/info")) + "\n";
+// } else if (lines[i].find("$bundle_identifier") != -1) {
+// strnew += lines[i].replace("$bundle_identifier", p_preset->get("application/bundle_identifier")) + "\n";
+// } else if (lines[i].find("$short_version") != -1) {
+// strnew += lines[i].replace("$short_version", p_preset->get("application/short_version")) + "\n";
+// } else if (lines[i].find("$version") != -1) {
+// strnew += lines[i].replace("$version", p_preset->get("application/version")) + "\n";
+// } else if (lines[i].find("$signature") != -1) {
+// strnew += lines[i].replace("$signature", p_preset->get("application/signature")) + "\n";
+// } else if (lines[i].find("$copyright") != -1) {
+// strnew += lines[i].replace("$copyright", p_preset->get("application/copyright")) + "\n";
+// } else if (lines[i].find("$team_id") != -1) {
+// strnew += lines[i].replace("$team_id", p_preset->get("application/app_store_team_id")) + "\n";
+// } else if (lines[i].find("$export_method") != -1) {
+// 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) {
+// strnew += lines[i].replace("$provisioning_profile_uuid_release", p_preset->get("application/provisioning_profile_uuid_release")) + "\n";
+// } else if (lines[i].find("$provisioning_profile_uuid_debug") != -1) {
+// strnew += lines[i].replace("$provisioning_profile_uuid_debug", p_preset->get("application/provisioning_profile_uuid_debug")) + "\n";
+// } else if (lines[i].find("$provisioning_profile_uuid") != -1) {
+// String uuid = p_debug ? p_preset->get("application/provisioning_profile_uuid_debug") : p_preset->get("application/provisioning_profile_uuid_release");
+// strnew += lines[i].replace("$provisioning_profile_uuid", uuid) + "\n";
+// } else if (lines[i].find("$code_sign_identity_debug") != -1) {
+// strnew += lines[i].replace("$code_sign_identity_debug", p_preset->get("application/code_sign_identity_debug")) + "\n";
+// } else if (lines[i].find("$code_sign_identity_release") != -1) {
+// strnew += lines[i].replace("$code_sign_identity_release", p_preset->get("application/code_sign_identity_release")) + "\n";
+// } else if (lines[i].find("$additional_plist_content") != -1) {
+// strnew += lines[i].replace("$additional_plist_content", p_config.plist_content) + "\n";
+// } else if (lines[i].find("$godot_archs") != -1) {
+// strnew += lines[i].replace("$godot_archs", p_config.architectures) + "\n";
+// } else if (lines[i].find("$linker_flags") != -1) {
+// strnew += lines[i].replace("$linker_flags", p_config.linker_flags) + "\n";
+// } else if (lines[i].find("$cpp_code") != -1) {
+// strnew += lines[i].replace("$cpp_code", p_config.cpp_code) + "\n";
+// } else if (lines[i].find("$docs_in_place") != -1) {
+// 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) {
+// 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) {
+// 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) {
+// String capabilities;
+
+// // I've removed armv7 as we can run on 64bit only devices
+// // Note that capabilities listed here are requirements for the app to be installed.
+// // They don't enable anything.
+// Vector<String> capabilities_list = p_config.capabilities;
+
+// if ((bool)p_preset->get("capabilities/access_wifi") && !capabilities_list.has("wifi")) {
+// capabilities_list.push_back("wifi");
+// }
+
+// for (int idx = 0; idx < capabilities_list.size(); idx++) {
+// capabilities += "<string>" + capabilities_list[idx] + "</string>\n";
+// }
+
+// strnew += lines[i].replace("$required_device_capabilities", capabilities);
+// } else if (lines[i].find("$interface_orientations") != -1) {
+// String orientations;
+
+// if ((bool)p_preset->get("orientation/portrait")) {
+// orientations += "<string>UIInterfaceOrientationPortrait</string>\n";
+// }
+// if ((bool)p_preset->get("orientation/landscape_left")) {
+// orientations += "<string>UIInterfaceOrientationLandscapeLeft</string>\n";
+// }
+// if ((bool)p_preset->get("orientation/landscape_right")) {
+// orientations += "<string>UIInterfaceOrientationLandscapeRight</string>\n";
+// }
+// if ((bool)p_preset->get("orientation/portrait_upside_down")) {
+// orientations += "<string>UIInterfaceOrientationPortraitUpsideDown</string>\n";
+// }
+
+// strnew += lines[i].replace("$interface_orientations", orientations);
+// } else if (lines[i].find("$camera_usage_description") != -1) {
+// 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) {
+// 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) {
+// 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) {
+// bool is_on = p_preset->get("storyboard/use_launch_screen_storyboard");
+// String value = is_on ? "<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) {
+// bool is_on = p_preset->get("storyboard/use_launch_screen_storyboard");
+// String value = is_on ? "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) {
+// bool is_on = p_preset->get("storyboard/use_launch_screen_storyboard");
+// String value = is_on ? "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) {
+// bool is_on = p_preset->get("storyboard/use_launch_screen_storyboard");
+// String value = is_on ? "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) {
+// bool is_on = p_preset->get("storyboard/use_launch_screen_storyboard");
+// String value = is_on ? "90DD2D9E24B36E8000717FE1 /* Launch Screen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 90DD2D9D24B36E8000717FE1 /* Launch Screen.storyboard */; };" : "";
+// strnew += lines[i].replace("$pbx_launch_screen_build_reference", value) + "\n";
+// } else if (lines[i].find("$pbx_launch_image_usage_setting") != -1) {
+// bool is_on = p_preset->get("storyboard/use_launch_screen_storyboard");
+// String value = is_on ? "" : "ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage;";
+// strnew += lines[i].replace("$pbx_launch_image_usage_setting", value) + "\n";
+// } else if (lines[i].find("$launch_screen_image_mode") != -1) {
+// int image_scale_mode = p_preset->get("storyboard/image_scale_mode");
+// String value;
+
+// switch (image_scale_mode) {
+// case 0: {
+// String logo_path = ProjectSettings::get_singleton()->get("application/boot_splash/image");
+// bool is_on = ProjectSettings::get_singleton()->get("application/boot_splash/fullsize");
+// // If custom logo is not specified, Godot does not scale default one, so we should do the same.
+// value = (is_on && logo_path.length() > 0) ? "scaleAspectFit" : "center";
+// } break;
+// default: {
+// value = storyboard_image_scale_mode[image_scale_mode - 1];
+// }
+// }
+
+// strnew += lines[i].replace("$launch_screen_image_mode", value) + "\n";
+// } else if (lines[i].find("$launch_screen_background_color") != -1) {
+// bool use_custom = p_preset->get("storyboard/use_custom_bg_color");
+// Color color = use_custom ? p_preset->get("storyboard/custom_bg_color") : ProjectSettings::get_singleton()->get("application/boot_splash/bg_color");
+// const String value_format = "red=\"$red\" green=\"$green\" blue=\"$blue\" alpha=\"$alpha\"";
+
+// Dictionary value_dictionary;
+// value_dictionary["red"] = color.r;
+// value_dictionary["green"] = color.g;
+// value_dictionary["blue"] = color.b;
+// value_dictionary["alpha"] = color.a;
+// String value = value_format.format(value_dictionary, "$_");
+
+// strnew += lines[i].replace("$launch_screen_background_color", value) + "\n";
+// } else {
+// strnew += lines[i] + "\n";
+// }
+// }
+
+// // !BAS! I'm assuming the 9 in the original code was a typo. I've added -1 or else it seems to also be adding our terminating zero...
+// // should apply the same fix in our OSX export.
+// CharString cs = strnew.utf8();
+// pfile.resize(cs.size() - 1);
+// for (int i = 0; i < cs.size() - 1; i++) {
+// pfile.write[i] = cs[i];
+// }
+//}
+
+//String EditorExportPlatformIOS::_get_additional_plist_content() {
+// Vector<Ref<EditorExportPlugin>> export_plugins = EditorExport::get_singleton()->get_export_plugins();
+// String result;
+// for (int i = 0; i < export_plugins.size(); ++i) {
+// result += export_plugins[i]->get_ios_plist_content();
+// }
+// return result;
+//}
+
+//String EditorExportPlatformIOS::_get_linker_flags() {
+// Vector<Ref<EditorExportPlugin>> export_plugins = EditorExport::get_singleton()->get_export_plugins();
+// String result;
+// for (int i = 0; i < export_plugins.size(); ++i) {
+// String flags = export_plugins[i]->get_ios_linker_flags();
+// if (flags.length() == 0) {
+// continue;
+// }
+// if (result.length() > 0) {
+// result += ' ';
+// }
+// result += flags;
+// }
+// // the flags will be enclosed in quotes, so need to escape them
+// return result.replace("\"", "\\\"");
+//}
+
+//String EditorExportPlatformIOS::_get_cpp_code() {
+// Vector<Ref<EditorExportPlugin>> export_plugins = EditorExport::get_singleton()->get_export_plugins();
+// String result;
+// for (int i = 0; i < export_plugins.size(); ++i) {
+// result += export_plugins[i]->get_ios_cpp_code();
+// }
+// return result;
+//}
+
+//void EditorExportPlatformIOS::_blend_and_rotate(Ref<Image> &p_dst, Ref<Image> &p_src, bool p_rot) {
+// ERR_FAIL_COND(p_dst.is_null());
+// ERR_FAIL_COND(p_src.is_null());
+
+// int sw = p_rot ? p_src->get_height() : p_src->get_width();
+// int sh = p_rot ? p_src->get_width() : p_src->get_height();
+
+// int x_pos = (p_dst->get_width() - sw) / 2;
+// int y_pos = (p_dst->get_height() - sh) / 2;
+
+// int xs = (x_pos >= 0) ? 0 : -x_pos;
+// int ys = (y_pos >= 0) ? 0 : -y_pos;
+
+// if (sw + x_pos > p_dst->get_width()) {
+// sw = p_dst->get_width() - x_pos;
+// }
+// if (sh + y_pos > p_dst->get_height()) {
+// sh = p_dst->get_height() - y_pos;
+// }
+
+// for (int y = ys; y < sh; y++) {
+// for (int x = xs; x < sw; x++) {
+// Color sc = p_rot ? p_src->get_pixel(p_src->get_width() - y - 1, x) : p_src->get_pixel(x, y);
+// Color dc = p_dst->get_pixel(x_pos + x, y_pos + y);
+// dc.r = (double)(sc.a * sc.r + dc.a * (1.0 - sc.a) * dc.r);
+// dc.g = (double)(sc.a * sc.g + dc.a * (1.0 - sc.a) * dc.g);
+// dc.b = (double)(sc.a * sc.b + dc.a * (1.0 - sc.a) * dc.b);
+// dc.a = (double)(sc.a + dc.a * (1.0 - sc.a));
+// p_dst->set_pixel(x_pos + x, y_pos + y, dc);
+// }
+// }
+//}
+
+//struct IconInfo {
+// const char *preset_key;
+// const char *idiom;
+// const char *export_name;
+// const char *actual_size_side;
+// const char *scale;
+// const char *unscaled_size;
+// bool is_required = false;
+//};
+
+//static const IconInfo icon_infos[] = {
+// { "required_icons/iphone_120x120", "iphone", "Icon-120.png", "120", "2x", "60x60", true },
+// { "required_icons/iphone_120x120", "iphone", "Icon-120.png", "120", "3x", "40x40", true },
+
+// { "required_icons/ipad_76x76", "ipad", "Icon-76.png", "76", "1x", "76x76", true },
+// { "required_icons/app_store_1024x1024", "ios-marketing", "Icon-1024.png", "1024", "1x", "1024x1024", true },
+
+// { "optional_icons/iphone_180x180", "iphone", "Icon-180.png", "180", "3x", "60x60", false },
+
+// { "optional_icons/ipad_152x152", "ipad", "Icon-152.png", "152", "2x", "76x76", false },
+
+// { "optional_icons/ipad_167x167", "ipad", "Icon-167.png", "167", "2x", "83.5x83.5", false },
+
+// { "optional_icons/spotlight_40x40", "ipad", "Icon-40.png", "40", "1x", "40x40", false },
+
+// { "optional_icons/spotlight_80x80", "iphone", "Icon-80.png", "80", "2x", "40x40", false },
+// { "optional_icons/spotlight_80x80", "ipad", "Icon-80.png", "80", "2x", "40x40", false }
+//};
+
+//Error EditorExportPlatformIOS::_export_icons(const Ref<EditorExportPreset> &p_preset, const String &p_iconset_dir) {
+// String json_description = "{\"images\":[";
+// String sizes;
+
+// DirAccess *da = DirAccess::open(p_iconset_dir);
+// ERR_FAIL_COND_V_MSG(!da, ERR_CANT_OPEN, "Cannot open directory '" + p_iconset_dir + "'.");
+
+// for (uint64_t i = 0; i < (sizeof(icon_infos) / sizeof(icon_infos[0])); ++i) {
+// IconInfo info = icon_infos[i];
+// int side_size = String(info.actual_size_side).to_int();
+// String icon_path = p_preset->get(info.preset_key);
+// if (icon_path.length() == 0) {
+// if ((bool)p_preset->get("icons/generate_missing")) {
+// // Resize main app icon
+// icon_path = ProjectSettings::get_singleton()->get("application/config/icon");
+// Ref<Image> img = memnew(Image);
+// Error err = ImageLoader::load_image(icon_path, img);
+// if (err != OK) {
+// ERR_PRINT("Invalid icon (" + String(info.preset_key) + "): '" + icon_path + "'.");
+// return ERR_UNCONFIGURED;
+// }
+// img->resize(side_size, side_size);
+// err = img->save_png(p_iconset_dir + info.export_name);
+// if (err) {
+// String err_str = String("Failed to export icon(" + String(info.preset_key) + "): '" + icon_path + "'.");
+// ERR_PRINT(err_str.utf8().get_data());
+// return err;
+// }
+// } else {
+// if (info.is_required) {
+// String err_str = String("Required icon (") + info.preset_key + ") is not specified in the preset.";
+// ERR_PRINT(err_str);
+// return ERR_UNCONFIGURED;
+// } else {
+// String err_str = String("Icon (") + info.preset_key + ") is not specified in the preset.";
+// WARN_PRINT(err_str);
+// }
+// continue;
+// }
+// } else {
+// // Load custom icon
+// Ref<Image> img = memnew(Image);
+// Error err = ImageLoader::load_image(icon_path, img);
+// if (err != OK) {
+// ERR_PRINT("Invalid icon (" + String(info.preset_key) + "): '" + icon_path + "'.");
+// return ERR_UNCONFIGURED;
+// }
+// if (img->get_width() != side_size || img->get_height() != side_size) {
+// ERR_PRINT("Invalid icon size (" + String(info.preset_key) + "): '" + icon_path + "'.");
+// return ERR_UNCONFIGURED;
+// }
+
+// err = da->copy(icon_path, p_iconset_dir + info.export_name);
+// if (err) {
+// memdelete(da);
+// String err_str = String("Failed to export icon(" + String(info.preset_key) + "): '" + icon_path + "'.");
+// ERR_PRINT(err_str.utf8().get_data());
+// return err;
+// }
+// }
+// sizes += String(info.actual_size_side) + "\n";
+// if (i > 0) {
+// json_description += ",";
+// }
+// json_description += String("{");
+// json_description += String("\"idiom\":") + "\"" + info.idiom + "\",";
+// json_description += String("\"size\":") + "\"" + info.unscaled_size + "\",";
+// json_description += String("\"scale\":") + "\"" + info.scale + "\",";
+// json_description += String("\"filename\":") + "\"" + info.export_name + "\"";
+// json_description += String("}");
+// }
+// json_description += "]}";
+// memdelete(da);
+
+// FileAccess *json_file = FileAccess::open(p_iconset_dir + "Contents.json", FileAccess::WRITE);
+// ERR_FAIL_COND_V(!json_file, ERR_CANT_CREATE);
+// CharString json_utf8 = json_description.utf8();
+// json_file->store_buffer((const uint8_t *)json_utf8.get_data(), json_utf8.length());
+// memdelete(json_file);
+
+// FileAccess *sizes_file = FileAccess::open(p_iconset_dir + "sizes", FileAccess::WRITE);
+// ERR_FAIL_COND_V(!sizes_file, ERR_CANT_CREATE);
+// CharString sizes_utf8 = sizes.utf8();
+// sizes_file->store_buffer((const uint8_t *)sizes_utf8.get_data(), sizes_utf8.length());
+// memdelete(sizes_file);
+
+// return OK;
+//}
+
+//Error EditorExportPlatformIOS::_export_loading_screen_file(const Ref<EditorExportPreset> &p_preset, const String &p_dest_dir) {
+// const String custom_launch_image_2x = p_preset->get("storyboard/custom_image@2x");
+// const String custom_launch_image_3x = p_preset->get("storyboard/custom_image@3x");
+
+// if (custom_launch_image_2x.length() > 0 && custom_launch_image_3x.length() > 0) {
+// Ref<Image> image;
+// String image_path = p_dest_dir.plus_file("splash@2x.png");
+// image.instance();
+// Error err = image->load(custom_launch_image_2x);
+
+// if (err) {
+// image.unref();
+// return err;
+// }
+
+// if (image->save_png(image_path) != OK) {
+// return ERR_FILE_CANT_WRITE;
+// }
+
+// image.unref();
+// image_path = p_dest_dir.plus_file("splash@3x.png");
+// image.instance();
+// err = image->load(custom_launch_image_3x);
+
+// if (err) {
+// image.unref();
+// return err;
+// }
+
+// if (image->save_png(image_path) != OK) {
+// return ERR_FILE_CANT_WRITE;
+// }
+// } else {
+// Ref<Image> splash;
+
+// const String splash_path = ProjectSettings::get_singleton()->get("application/boot_splash/image");
+
+// if (!splash_path.is_empty()) {
+// splash.instance();
+// const Error err = splash->load(splash_path);
+// if (err) {
+// splash.unref();
+// }
+// }
+
+// if (splash.is_null()) {
+// splash = Ref<Image>(memnew(Image(boot_splash_png)));
+// }
+
+// // Using same image for both @2x and @3x
+// // because Godot's own boot logo uses single image for all resolutions.
+// // Also not using @1x image, because devices using this image variant
+// // are not supported by iOS 9, which is minimal target.
+// const String splash_png_path_2x = p_dest_dir.plus_file("splash@2x.png");
+// const String splash_png_path_3x = p_dest_dir.plus_file("splash@3x.png");
+
+// if (splash->save_png(splash_png_path_2x) != OK) {
+// return ERR_FILE_CANT_WRITE;
+// }
+
+// if (splash->save_png(splash_png_path_3x) != OK) {
+// return ERR_FILE_CANT_WRITE;
+// }
+// }
+
+// return OK;
+//}
+
+//Error EditorExportPlatformIOS::_export_loading_screen_images(const Ref<EditorExportPreset> &p_preset, const String &p_dest_dir) {
+// DirAccess *da = DirAccess::open(p_dest_dir);
+// ERR_FAIL_COND_V_MSG(!da, ERR_CANT_OPEN, "Cannot open directory '" + p_dest_dir + "'.");
+
+// for (uint64_t i = 0; i < sizeof(loading_screen_infos) / sizeof(loading_screen_infos[0]); ++i) {
+// LoadingScreenInfo info = loading_screen_infos[i];
+// String loading_screen_file = p_preset->get(info.preset_key);
+// if (loading_screen_file.size() > 0) {
+// // Load custom loading screens
+// Ref<Image> img = memnew(Image);
+// Error err = ImageLoader::load_image(loading_screen_file, img);
+// if (err != OK) {
+// ERR_PRINT("Invalid loading screen (" + String(info.preset_key) + "): '" + loading_screen_file + "'.");
+// return ERR_UNCONFIGURED;
+// }
+// if (img->get_width() != info.width || img->get_height() != info.height) {
+// ERR_PRINT("Invalid loading screen size (" + String(info.preset_key) + "): '" + loading_screen_file + "'.");
+// return ERR_UNCONFIGURED;
+// }
+// err = da->copy(loading_screen_file, p_dest_dir + info.export_name);
+// if (err) {
+// memdelete(da);
+// String err_str = String("Failed to export loading screen (") + info.preset_key + ") from path '" + loading_screen_file + "'.";
+// ERR_PRINT(err_str.utf8().get_data());
+// return err;
+// }
+// } else if ((bool)p_preset->get("launch_screens/generate_missing")) {
+// // Generate loading screen from the splash screen
+// Color boot_bg_color = ProjectSettings::get_singleton()->get("application/boot_splash/bg_color");
+// String boot_logo_path = ProjectSettings::get_singleton()->get("application/boot_splash/image");
+// bool boot_logo_scale = ProjectSettings::get_singleton()->get("application/boot_splash/fullsize");
+
+// Ref<Image> img = memnew(Image);
+// img->create(info.width, info.height, false, Image::FORMAT_RGBA8);
+// img->fill(boot_bg_color);
+
+// Ref<Image> img_bs;
+
+// if (boot_logo_path.length() > 0) {
+// img_bs = Ref<Image>(memnew(Image));
+// ImageLoader::load_image(boot_logo_path, img_bs);
+// }
+// if (!img_bs.is_valid()) {
+// img_bs = Ref<Image>(memnew(Image(boot_splash_png)));
+// }
+// if (img_bs.is_valid()) {
+// float aspect_ratio = (float)img_bs->get_width() / (float)img_bs->get_height();
+// if (info.rotate) {
+// if (boot_logo_scale) {
+// if (info.width * aspect_ratio <= info.height) {
+// img_bs->resize(info.width * aspect_ratio, info.width);
+// } else {
+// img_bs->resize(info.height, info.height / aspect_ratio);
+// }
+// }
+// } else {
+// if (boot_logo_scale) {
+// if (info.height * aspect_ratio <= info.width) {
+// img_bs->resize(info.height * aspect_ratio, info.height);
+// } else {
+// img_bs->resize(info.width, info.width / aspect_ratio);
+// }
+// }
+// }
+// _blend_and_rotate(img, img_bs, info.rotate);
+// }
+// Error err = img->save_png(p_dest_dir + info.export_name);
+// if (err) {
+// String err_str = String("Failed to export loading screen (") + info.preset_key + ") from splash screen.";
+// WARN_PRINT(err_str.utf8().get_data());
+// }
+// } else {
+// String err_str = String("No loading screen (") + info.preset_key + ") specified.";
+// WARN_PRINT(err_str.utf8().get_data());
+// }
+// }
+// memdelete(da);
+
+// return OK;
+//}
+
+//Error EditorExportPlatformIOS::_walk_dir_recursive(DirAccess *p_da, FileHandler p_handler, void *p_userdata) {
+// Vector<String> dirs;
+// String path;
+// String current_dir = p_da->get_current_dir();
+// p_da->list_dir_begin();
+// while ((path = p_da->get_next()).length() != 0) {
+// if (p_da->current_is_dir()) {
+// if (path != "." && path != "..") {
+// dirs.push_back(path);
+// }
+// } else {
+// Error err = p_handler(current_dir.plus_file(path), p_userdata);
+// if (err) {
+// p_da->list_dir_end();
+// return err;
+// }
+// }
+// }
+// p_da->list_dir_end();
+
+// for (int i = 0; i < dirs.size(); ++i) {
+// String dir = dirs[i];
+// p_da->change_dir(dir);
+// Error err = _walk_dir_recursive(p_da, p_handler, p_userdata);
+// p_da->change_dir("..");
+// if (err) {
+// return err;
+// }
+// }
+
+// return OK;
+//}
+
+//struct CodesignData {
+// const Ref<EditorExportPreset> &preset;
+// bool debug = false;
+
+// CodesignData(const Ref<EditorExportPreset> &p_preset, bool p_debug) :
+// preset(p_preset),
+// debug(p_debug) {
+// }
+//};
+
+//Error EditorExportPlatformIOS::_codesign(String p_file, void *p_userdata) {
+// if (p_file.ends_with(".dylib")) {
+// CodesignData *data = (CodesignData *)p_userdata;
+// print_line(String("Signing ") + p_file);
+// List<String> codesign_args;
+// codesign_args.push_back("-f");
+// codesign_args.push_back("-s");
+// codesign_args.push_back(data->preset->get(data->debug ? "application/code_sign_identity_debug" : "application/code_sign_identity_release"));
+// codesign_args.push_back(p_file);
+// return OS::get_singleton()->execute("codesign", codesign_args, true);
+// }
+// return OK;
+//}
+
+//struct PbxId {
+//private:
+// static char _hex_char(uint8_t four_bits) {
+// if (four_bits < 10) {
+// return ('0' + four_bits);
+// }
+// return 'A' + (four_bits - 10);
+// }
+
+// static String _hex_pad(uint32_t num) {
+// Vector<char> ret;
+// ret.resize(sizeof(num) * 2);
+// for (uint64_t i = 0; i < sizeof(num) * 2; ++i) {
+// uint8_t four_bits = (num >> (sizeof(num) * 8 - (i + 1) * 4)) & 0xF;
+// ret.write[i] = _hex_char(four_bits);
+// }
+// return String::utf8(ret.ptr(), ret.size());
+// }
+
+//public:
+// uint32_t high_bits;
+// uint32_t mid_bits;
+// uint32_t low_bits;
+
+// String str() const {
+// return _hex_pad(high_bits) + _hex_pad(mid_bits) + _hex_pad(low_bits);
+// }
+
+// PbxId &operator++() {
+// low_bits++;
+// if (!low_bits) {
+// mid_bits++;
+// if (!mid_bits) {
+// high_bits++;
+// }
+// }
+
+// return *this;
+// }
+//};
+
+//struct ExportLibsData {
+// Vector<String> lib_paths;
+// String dest_dir;
+//};
+
+//void EditorExportPlatformIOS::_add_assets_to_project(const Ref<EditorExportPreset> &p_preset, Vector<uint8_t> &p_project_data, const Vector<IOSExportAsset> &p_additional_assets) {
+// // that is just a random number, we just need Godot IDs not to clash with
+// // existing IDs in the project.
+// PbxId current_id = { 0x58938401, 0, 0 };
+// String pbx_files;
+// String pbx_frameworks_build;
+// String pbx_frameworks_refs;
+// String pbx_resources_build;
+// String pbx_resources_refs;
+// String pbx_embeded_frameworks;
+
+// const String file_info_format = String("$build_id = {isa = PBXBuildFile; fileRef = $ref_id; };\n") +
+// "$ref_id = {isa = PBXFileReference; lastKnownFileType = $file_type; name = \"$name\"; path = \"$file_path\"; sourceTree = \"<group>\"; };\n";
+
+// for (int i = 0; i < p_additional_assets.size(); ++i) {
+// String additional_asset_info_format = file_info_format;
+
+// String build_id = (++current_id).str();
+// String ref_id = (++current_id).str();
+// String framework_id = "";
+
+// const IOSExportAsset &asset = p_additional_assets[i];
+
+// String type;
+// if (asset.exported_path.ends_with(".framework")) {
+// 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";
+// }
+
+// type = "wrapper.framework";
+// } else if (asset.exported_path.ends_with(".xcframework")) {
+// 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";
+// }
+
+// type = "wrapper.xcframework";
+// } else if (asset.exported_path.ends_with(".dylib")) {
+// type = "compiled.mach-o.dylib";
+// } else if (asset.exported_path.ends_with(".a")) {
+// type = "archive.ar";
+// } else {
+// type = "file";
+// }
+
+// String &pbx_build = asset.is_framework ? pbx_frameworks_build : pbx_resources_build;
+// String &pbx_refs = asset.is_framework ? pbx_frameworks_refs : pbx_resources_refs;
+
+// if (pbx_build.length() > 0) {
+// pbx_build += ",\n";
+// pbx_refs += ",\n";
+// }
+// pbx_build += build_id;
+// pbx_refs += ref_id;
+
+// Dictionary format_dict;
+// format_dict["build_id"] = build_id;
+// format_dict["ref_id"] = ref_id;
+// format_dict["name"] = asset.exported_path.get_file();
+// format_dict["file_path"] = asset.exported_path;
+// format_dict["file_type"] = type;
+// if (framework_id.length() > 0) {
+// format_dict["framework_id"] = framework_id;
+// }
+// pbx_files += additional_asset_info_format.format(format_dict, "$_");
+// }
+
+// // Note, frameworks like gamekit are always included in our project.pbxprof file
+// // even if turned off in capabilities.
+
+// String str = String::utf8((const char *)p_project_data.ptr(), p_project_data.size());
+// str = str.replace("$additional_pbx_files", pbx_files);
+// str = str.replace("$additional_pbx_frameworks_build", pbx_frameworks_build);
+// str = str.replace("$additional_pbx_frameworks_refs", pbx_frameworks_refs);
+// str = str.replace("$additional_pbx_resources_build", pbx_resources_build);
+// str = str.replace("$additional_pbx_resources_refs", pbx_resources_refs);
+// str = str.replace("$pbx_embeded_frameworks", pbx_embeded_frameworks);
+
+// CharString cs = str.utf8();
+// p_project_data.resize(cs.size() - 1);
+// for (int i = 0; i < cs.size() - 1; i++) {
+// p_project_data.write[i] = cs[i];
+// }
+//}
+
+//Error EditorExportPlatformIOS::_copy_asset(const String &p_out_dir, const String &p_asset, const String *p_custom_file_name, bool p_is_framework, bool p_should_embed, Vector<IOSExportAsset> &r_exported_assets) {
+// DirAccess *filesystem_da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
+// ERR_FAIL_COND_V_MSG(!filesystem_da, ERR_CANT_CREATE, "Cannot create DirAccess for path '" + p_out_dir + "'.");
+
+// String binary_name = p_out_dir.get_file().get_basename();
+
+// DirAccess *da = DirAccess::create_for_path(p_asset);
+// if (!da) {
+// memdelete(filesystem_da);
+// ERR_FAIL_V_MSG(ERR_CANT_CREATE, "Can't create directory: " + p_asset + ".");
+// }
+// bool file_exists = da->file_exists(p_asset);
+// bool dir_exists = da->dir_exists(p_asset);
+// if (!file_exists && !dir_exists) {
+// memdelete(da);
+// memdelete(filesystem_da);
+// return ERR_FILE_NOT_FOUND;
+// }
+
+// String base_dir = p_asset.get_base_dir().replace("res://", "");
+// String destination_dir;
+// String destination;
+// String asset_path;
+
+// bool create_framework = false;
+
+// if (p_is_framework && p_asset.ends_with(".dylib")) {
+// // For iOS we need to turn .dylib into .framework
+// // to be able to send application to AppStore
+// asset_path = String("dylibs").plus_file(base_dir);
+
+// String file_name;
+
+// if (!p_custom_file_name) {
+// file_name = p_asset.get_basename().get_file();
+// } else {
+// file_name = *p_custom_file_name;
+// }
+
+// String framework_name = file_name + ".framework";
+
+// asset_path = asset_path.plus_file(framework_name);
+// destination_dir = p_out_dir.plus_file(asset_path);
+// destination = destination_dir.plus_file(file_name);
+// create_framework = true;
+// } else if (p_is_framework && (p_asset.ends_with(".framework") || p_asset.ends_with(".xcframework"))) {
+// asset_path = String("dylibs").plus_file(base_dir);
+
+// String file_name;
+
+// if (!p_custom_file_name) {
+// file_name = p_asset.get_file();
+// } else {
+// file_name = *p_custom_file_name;
+// }
+
+// asset_path = asset_path.plus_file(file_name);
+// destination_dir = p_out_dir.plus_file(asset_path);
+// destination = destination_dir;
+// } else {
+// asset_path = base_dir;
+
+// String file_name;
+
+// if (!p_custom_file_name) {
+// file_name = p_asset.get_file();
+// } else {
+// file_name = *p_custom_file_name;
+// }
+
+// destination_dir = p_out_dir.plus_file(asset_path);
+// asset_path = asset_path.plus_file(file_name);
+// destination = p_out_dir.plus_file(asset_path);
+// }
+
+// if (!filesystem_da->dir_exists(destination_dir)) {
+// Error make_dir_err = filesystem_da->make_dir_recursive(destination_dir);
+// if (make_dir_err) {
+// memdelete(da);
+// memdelete(filesystem_da);
+// return make_dir_err;
+// }
+// }
+
+// Error err = dir_exists ? da->copy_dir(p_asset, destination) : da->copy(p_asset, destination);
+// memdelete(da);
+// if (err) {
+// memdelete(filesystem_da);
+// return err;
+// }
+// IOSExportAsset exported_asset = { binary_name.plus_file(asset_path), p_is_framework, p_should_embed };
+// r_exported_assets.push_back(exported_asset);
+
+// if (create_framework) {
+// String file_name;
+
+// if (!p_custom_file_name) {
+// file_name = p_asset.get_basename().get_file();
+// } else {
+// file_name = *p_custom_file_name;
+// }
+
+// String framework_name = file_name + ".framework";
+
+// // Performing `install_name_tool -id @rpath/{name}.framework/{name} ./{name}` on dylib
+// {
+// List<String> install_name_args;
+// install_name_args.push_back("-id");
+// install_name_args.push_back(String("@rpath").plus_file(framework_name).plus_file(file_name));
+// install_name_args.push_back(destination);
+
+// OS::get_singleton()->execute("install_name_tool", install_name_args, true);
+// }
+
+// // Creating Info.plist
+// {
+// 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>CFBundleShortVersionString</key>\n"
+// "<string>1.0</string>\n"
+// "<key>CFBundleIdentifier</key>\n"
+// "<string>com.gdnative.framework.$name</string>\n"
+// "<key>CFBundleName</key>\n"
+// "<string>$name</string>\n"
+// "<key>CFBundleExecutable</key>\n"
+// "<string>$name</string>\n"
+// "<key>DTPlatformName</key>\n"
+// "<string>iphoneos</string>\n"
+// "<key>CFBundleInfoDictionaryVersion</key>\n"
+// "<string>6.0</string>\n"
+// "<key>CFBundleVersion</key>\n"
+// "<string>1</string>\n"
+// "<key>CFBundlePackageType</key>\n"
+// "<string>FMWK</string>\n"
+// "<key>MinimumOSVersion</key>\n"
+// "<string>10.0</string>\n"
+// "</dict>\n"
+// "</plist>";
+
+// String info_plist = info_plist_format.replace("$name", file_name);
+
+// FileAccess *f = FileAccess::open(destination_dir.plus_file("Info.plist"), FileAccess::WRITE);
+// if (f) {
+// f->store_string(info_plist);
+// f->close();
+// memdelete(f);
+// }
+// }
+// }
+
+// memdelete(filesystem_da);
+
+// return OK;
+//}
+
+//Error EditorExportPlatformIOS::_export_additional_assets(const String &p_out_dir, const Vector<String> &p_assets, bool p_is_framework, bool p_should_embed, Vector<IOSExportAsset> &r_exported_assets) {
+// for (int f_idx = 0; f_idx < p_assets.size(); ++f_idx) {
+// String asset = p_assets[f_idx];
+// if (!asset.begins_with("res://")) {
+// // either SDK-builtin or already a part of the export template
+// IOSExportAsset exported_asset = { asset, p_is_framework, p_should_embed };
+// r_exported_assets.push_back(exported_asset);
+// } else {
+// Error err = _copy_asset(p_out_dir, asset, nullptr, p_is_framework, p_should_embed, r_exported_assets);
+// ERR_FAIL_COND_V(err, err);
+// }
+// }
+
+// return OK;
+//}
+
+//Error EditorExportPlatformIOS::_export_additional_assets(const String &p_out_dir, const Vector<SharedObject> &p_libraries, Vector<IOSExportAsset> &r_exported_assets) {
+// Vector<Ref<EditorExportPlugin>> export_plugins = EditorExport::get_singleton()->get_export_plugins();
+// for (int i = 0; i < export_plugins.size(); i++) {
+// Vector<String> linked_frameworks = export_plugins[i]->get_ios_frameworks();
+// Error err = _export_additional_assets(p_out_dir, linked_frameworks, true, false, r_exported_assets);
+// ERR_FAIL_COND_V(err, err);
+
+// Vector<String> embedded_frameworks = export_plugins[i]->get_ios_embedded_frameworks();
+// err = _export_additional_assets(p_out_dir, embedded_frameworks, true, true, r_exported_assets);
+// ERR_FAIL_COND_V(err, err);
+
+// Vector<String> project_static_libs = export_plugins[i]->get_ios_project_static_libs();
+// for (int j = 0; j < project_static_libs.size(); j++) {
+// project_static_libs.write[j] = project_static_libs[j].get_file(); // Only the file name as it's copied to the project
+// }
+// err = _export_additional_assets(p_out_dir, project_static_libs, true, true, r_exported_assets);
+// ERR_FAIL_COND_V(err, err);
+
+// Vector<String> ios_bundle_files = export_plugins[i]->get_ios_bundle_files();
+// err = _export_additional_assets(p_out_dir, ios_bundle_files, false, false, r_exported_assets);
+// ERR_FAIL_COND_V(err, err);
+// }
+
+// Vector<String> library_paths;
+// for (int i = 0; i < p_libraries.size(); ++i) {
+// library_paths.push_back(p_libraries[i].path);
+// }
+// Error err = _export_additional_assets(p_out_dir, library_paths, true, true, r_exported_assets);
+// ERR_FAIL_COND_V(err, err);
+
+// return OK;
+//}
+
+//Vector<String> EditorExportPlatformIOS::_get_preset_architectures(const Ref<EditorExportPreset> &p_preset) {
+// Vector<ExportArchitecture> all_archs = _get_supported_architectures();
+// Vector<String> enabled_archs;
+// for (int i = 0; i < all_archs.size(); ++i) {
+// bool is_enabled = p_preset->get("architectures/" + all_archs[i].name);
+// if (is_enabled) {
+// enabled_archs.push_back(all_archs[i].name);
+// }
+// }
+// return enabled_archs;
+//}
+
+//Error EditorExportPlatformIOS::_export_ios_plugins(const Ref<EditorExportPreset> &p_preset, IOSConfigData &p_config_data, const String &dest_dir, Vector<IOSExportAsset> &r_exported_assets, bool p_debug) {
+// String plugin_definition_cpp_code;
+// String plugin_initialization_cpp_code;
+// String plugin_deinitialization_cpp_code;
+
+// Vector<String> plugin_linked_dependencies;
+// Vector<String> plugin_embedded_dependencies;
+// Vector<String> plugin_files;
+
+// Vector<PluginConfig> enabled_plugins = get_enabled_plugins(p_preset);
+
+// Vector<String> added_linked_dependenciy_names;
+// Vector<String> added_embedded_dependenciy_names;
+// HashMap<String, String> plist_values;
+
+// Error err;
+
+// for (int i = 0; i < enabled_plugins.size(); i++) {
+// PluginConfig plugin = enabled_plugins[i];
+
+// // Export plugin binary.
+// if (!plugin.supports_targets) {
+// err = _copy_asset(dest_dir, plugin.binary, nullptr, true, true, r_exported_assets);
+// } else {
+// String plugin_binary_dir = plugin.binary.get_base_dir();
+// String plugin_name_prefix = plugin.binary.get_basename().get_file();
+// String plugin_file = plugin_name_prefix + "." + (p_debug ? "debug" : "release") + ".a";
+// String result_file_name = plugin.binary.get_file();
+
+// err = _copy_asset(dest_dir, plugin_binary_dir.plus_file(plugin_file), &result_file_name, true, true, r_exported_assets);
+// }
+
+// ERR_FAIL_COND_V(err, err);
+
+// // Adding dependencies.
+// // Use separate container for names to check for duplicates.
+// for (int j = 0; j < plugin.linked_dependencies.size(); j++) {
+// String dependency = plugin.linked_dependencies[j];
+// String name = dependency.get_file();
+
+// if (added_linked_dependenciy_names.has(name)) {
+// continue;
+// }
+
+// added_linked_dependenciy_names.push_back(name);
+// plugin_linked_dependencies.push_back(dependency);
+// }
+
+// for (int j = 0; j < plugin.system_dependencies.size(); j++) {
+// String dependency = plugin.system_dependencies[j];
+// String name = dependency.get_file();
+
+// if (added_linked_dependenciy_names.has(name)) {
+// continue;
+// }
+
+// added_linked_dependenciy_names.push_back(name);
+// plugin_linked_dependencies.push_back(dependency);
+// }
+
+// for (int j = 0; j < plugin.embedded_dependencies.size(); j++) {
+// String dependency = plugin.embedded_dependencies[j];
+// String name = dependency.get_file();
+
+// if (added_embedded_dependenciy_names.has(name)) {
+// continue;
+// }
+
+// added_embedded_dependenciy_names.push_back(name);
+// plugin_embedded_dependencies.push_back(dependency);
+// }
+
+// plugin_files.append_array(plugin.files_to_copy);
+
+// // Capabilities
+// // Also checking for duplicates.
+// for (int j = 0; j < plugin.capabilities.size(); j++) {
+// String capability = plugin.capabilities[j];
+
+// if (p_config_data.capabilities.has(capability)) {
+// continue;
+// }
+
+// p_config_data.capabilities.push_back(capability);
+// }
+
+// // Plist
+// // Using hash map container to remove duplicates
+// const String *K = nullptr;
+
+// while ((K = plugin.plist.next(K))) {
+// String key = *K;
+// String value = plugin.plist[key];
+
+// if (key.is_empty() || value.is_empty()) {
+// continue;
+// }
+
+// plist_values[key] = value;
+// }
+
+// // CPP Code
+// String definition_comment = "// Plugin: " + plugin.name + "\n";
+// String initialization_method = plugin.initialization_method + "();\n";
+// String deinitialization_method = plugin.deinitialization_method + "();\n";
+
+// plugin_definition_cpp_code += definition_comment +
+// "extern void " + initialization_method +
+// "extern void " + deinitialization_method + "\n";
+
+// plugin_initialization_cpp_code += "\t" + initialization_method;
+// plugin_deinitialization_cpp_code += "\t" + deinitialization_method;
+// }
+
+// // Updating `Info.plist`
+// {
+// const String *K = nullptr;
+// while ((K = plist_values.next(K))) {
+// String key = *K;
+// String value = plist_values[key];
+
+// if (key.is_empty() || value.is_empty()) {
+// continue;
+// }
+
+// p_config_data.plist_content += "<key>" + key + "</key><string>" + value + "</string>\n";
+// }
+// }
+
+// // Export files
+// {
+// // Export linked plugin dependency
+// err = _export_additional_assets(dest_dir, plugin_linked_dependencies, true, false, r_exported_assets);
+// ERR_FAIL_COND_V(err, err);
+
+// // Export embedded plugin dependency
+// err = _export_additional_assets(dest_dir, plugin_embedded_dependencies, true, true, r_exported_assets);
+// ERR_FAIL_COND_V(err, err);
+
+// // Export plugin files
+// err = _export_additional_assets(dest_dir, plugin_files, false, false, r_exported_assets);
+// ERR_FAIL_COND_V(err, err);
+// }
+
+// // Update CPP
+// {
+// Dictionary plugin_format;
+// plugin_format["definition"] = plugin_definition_cpp_code;
+// plugin_format["initialization"] = plugin_initialization_cpp_code;
+// plugin_format["deinitialization"] = plugin_deinitialization_cpp_code;
+
+// String plugin_cpp_code = "\n// Godot Plugins\n"
+// "void godot_ios_plugins_initialize();\n"
+// "void godot_ios_plugins_deinitialize();\n"
+// "// Exported Plugins\n\n"
+// "$definition"
+// "// Use Plugins\n"
+// "void godot_ios_plugins_initialize() {\n"
+// "$initialization"
+// "}\n\n"
+// "void godot_ios_plugins_deinitialize() {\n"
+// "$deinitialization"
+// "}\n";
+
+// p_config_data.cpp_code += plugin_cpp_code.format(plugin_format, "$_");
+// }
+// return OK;
+//}
+
+//Error EditorExportPlatformIOS::export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags) {
+// ExportNotifier notifier(*this, p_preset, p_debug, p_path, p_flags);
+
+// String src_pkg_name;
+// String dest_dir = p_path.get_base_dir() + "/";
+// String binary_name = p_path.get_file().get_basename();
+
+// EditorProgress ep("export", "Exporting for iOS", 5, true);
+
+// String team_id = p_preset->get("application/app_store_team_id");
+// ERR_FAIL_COND_V_MSG(team_id.length() == 0, ERR_CANT_OPEN, "App Store Team ID not specified - cannot configure the project.");
+
+// if (p_debug) {
+// src_pkg_name = p_preset->get("custom_template/debug");
+// } else {
+// src_pkg_name = p_preset->get("custom_template/release");
+// }
+
+// if (src_pkg_name == "") {
+// String err;
+// src_pkg_name = find_export_template("iphone.zip", &err);
+// if (src_pkg_name == "") {
+// EditorNode::add_io_error(err);
+// return ERR_FILE_NOT_FOUND;
+// }
+// }
+
+// if (!DirAccess::exists(dest_dir)) {
+// return ERR_FILE_BAD_PATH;
+// }
+
+// DirAccess *da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
+// if (da) {
+// String current_dir = da->get_current_dir();
+
+// // remove leftovers from last export so they don't interfere
+// // in case some files are no longer needed
+// if (da->change_dir(dest_dir + binary_name + ".xcodeproj") == OK) {
+// da->erase_contents_recursive();
+// }
+// if (da->change_dir(dest_dir + binary_name) == OK) {
+// da->erase_contents_recursive();
+// }
+
+// da->change_dir(current_dir);
+
+// if (!da->dir_exists(dest_dir + binary_name)) {
+// Error err = da->make_dir(dest_dir + binary_name);
+// if (err) {
+// memdelete(da);
+// return err;
+// }
+// }
+// memdelete(da);
+// }
+
+// if (ep.step("Making .pck", 0)) {
+// return ERR_SKIP;
+// }
+// String pack_path = dest_dir + binary_name + ".pck";
+// Vector<SharedObject> libraries;
+// Error err = save_pack(p_preset, pack_path, &libraries);
+// if (err) {
+// return err;
+// }
+
+// if (ep.step("Extracting and configuring Xcode project", 1)) {
+// return ERR_SKIP;
+// }
+
+// String library_to_use = "libgodot.iphone." + String(p_debug ? "debug" : "release") + ".fat.a";
+
+// print_line("Static library: " + library_to_use);
+// String pkg_name;
+// if (p_preset->get("application/name") != "") {
+// pkg_name = p_preset->get("application/name"); // app_name
+// } else if (String(ProjectSettings::get_singleton()->get("application/config/name")) != "") {
+// pkg_name = String(ProjectSettings::get_singleton()->get("application/config/name"));
+// } else {
+// pkg_name = "Unnamed";
+// }
+
+// bool found_library = false;
+// int total_size = 0;
+
+// const String project_file = "godot_ios.xcodeproj/project.pbxproj";
+// Set<String> files_to_parse;
+// files_to_parse.insert("godot_ios/godot_ios-Info.plist");
+// files_to_parse.insert(project_file);
+// files_to_parse.insert("godot_ios/export_options.plist");
+// files_to_parse.insert("godot_ios/dummy.cpp");
+// files_to_parse.insert("godot_ios.xcodeproj/project.xcworkspace/contents.xcworkspacedata");
+// files_to_parse.insert("godot_ios.xcodeproj/xcshareddata/xcschemes/godot_ios.xcscheme");
+// files_to_parse.insert("godot_ios/godot_ios.entitlements");
+// files_to_parse.insert("godot_ios/Launch Screen.storyboard");
+
+// IOSConfigData config_data = {
+// pkg_name,
+// binary_name,
+// _get_additional_plist_content(),
+// String(" ").join(_get_preset_architectures(p_preset)),
+// _get_linker_flags(),
+// _get_cpp_code(),
+// "",
+// "",
+// "",
+// "",
+// Vector<String>()
+// };
+
+// Vector<IOSExportAsset> assets;
+
+// DirAccess *tmp_app_path = DirAccess::create_for_path(dest_dir);
+// ERR_FAIL_COND_V(!tmp_app_path, ERR_CANT_CREATE);
+
+// print_line("Unzipping...");
+// FileAccess *src_f = nullptr;
+// zlib_filefunc_def io = zipio_create_io_from_file(&src_f);
+// unzFile src_pkg_zip = unzOpen2(src_pkg_name.utf8().get_data(), &io);
+// if (!src_pkg_zip) {
+// EditorNode::add_io_error("Could not open export template (not a zip file?):\n" + src_pkg_name);
+// return ERR_CANT_OPEN;
+// }
+
+// err = _export_ios_plugins(p_preset, config_data, dest_dir + binary_name, assets, p_debug);
+// ERR_FAIL_COND_V(err, err);
+
+// //export rest of the files
+// int ret = unzGoToFirstFile(src_pkg_zip);
+// Vector<uint8_t> project_file_data;
+// while (ret == UNZ_OK) {
+//#if defined(OSX_ENABLED) || defined(X11_ENABLED)
+// bool is_execute = false;
+//#endif
+
+// //get filename
+// unz_file_info info;
+// char fname[16384];
+// ret = unzGetCurrentFileInfo(src_pkg_zip, &info, fname, 16384, nullptr, 0, nullptr, 0);
+
+// String file = fname;
+
+// print_line("READ: " + file);
+// Vector<uint8_t> data;
+// data.resize(info.uncompressed_size);
+
+// //read
+// unzOpenCurrentFile(src_pkg_zip);
+// unzReadCurrentFile(src_pkg_zip, data.ptrw(), data.size());
+// unzCloseCurrentFile(src_pkg_zip);
+
+// //write
+
+// file = file.replace_first("iphone/", "");
+
+// if (files_to_parse.has(file)) {
+// _fix_config_file(p_preset, data, config_data, p_debug);
+// } else if (file.begins_with("libgodot.iphone")) {
+// if (file != library_to_use) {
+// ret = unzGoToNextFile(src_pkg_zip);
+// continue; //ignore!
+// }
+// found_library = true;
+//#if defined(OSX_ENABLED) || defined(X11_ENABLED)
+// is_execute = true;
+//#endif
+// file = "godot_ios.a";
+// }
+
+// if (file == project_file) {
+// project_file_data = data;
+// }
+
+// ///@TODO need to parse logo files
+
+// if (data.size() > 0) {
+// file = file.replace("godot_ios", binary_name);
+
+// print_line("ADDING: " + file + " size: " + itos(data.size()));
+// total_size += data.size();
+
+// /* write it into our folder structure */
+// file = dest_dir + file;
+
+// /* make sure this folder exists */
+// String dir_name = file.get_base_dir();
+// if (!tmp_app_path->dir_exists(dir_name)) {
+// print_line("Creating " + dir_name);
+// Error dir_err = tmp_app_path->make_dir_recursive(dir_name);
+// if (dir_err) {
+// ERR_PRINT("Can't create '" + dir_name + "'.");
+// unzClose(src_pkg_zip);
+// memdelete(tmp_app_path);
+// return ERR_CANT_CREATE;
+// }
+// }
+
+// /* write the file */
+// FileAccess *f = FileAccess::open(file, FileAccess::WRITE);
+// if (!f) {
+// ERR_PRINT("Can't write '" + file + "'.");
+// unzClose(src_pkg_zip);
+// memdelete(tmp_app_path);
+// return ERR_CANT_CREATE;
+// };
+// f->store_buffer(data.ptr(), data.size());
+// f->close();
+// memdelete(f);
+
+//#if defined(OSX_ENABLED) || defined(X11_ENABLED)
+// if (is_execute) {
+// // we need execute rights on this file
+// chmod(file.utf8().get_data(), 0755);
+// }
+//#endif
+// }
+
+// ret = unzGoToNextFile(src_pkg_zip);
+// }
+
+// /* we're done with our source zip */
+// unzClose(src_pkg_zip);
+
+// if (!found_library) {
+// ERR_PRINT("Requested template library '" + library_to_use + "' not found. It might be missing from your template archive.");
+// memdelete(tmp_app_path);
+// return ERR_FILE_NOT_FOUND;
+// }
+
+// // Copy project static libs to the project
+// Vector<Ref<EditorExportPlugin>> export_plugins = EditorExport::get_singleton()->get_export_plugins();
+// for (int i = 0; i < export_plugins.size(); i++) {
+// Vector<String> project_static_libs = export_plugins[i]->get_ios_project_static_libs();
+// for (int j = 0; j < project_static_libs.size(); j++) {
+// const String &static_lib_path = project_static_libs[j];
+// String dest_lib_file_path = dest_dir + static_lib_path.get_file();
+// Error lib_copy_err = tmp_app_path->copy(static_lib_path, dest_lib_file_path);
+// if (lib_copy_err != OK) {
+// ERR_PRINT("Can't copy '" + static_lib_path + "'.");
+// memdelete(tmp_app_path);
+// return lib_copy_err;
+// }
+// }
+// }
+
+// String iconset_dir = dest_dir + binary_name + "/Images.xcassets/AppIcon.appiconset/";
+// err = OK;
+// if (!tmp_app_path->dir_exists(iconset_dir)) {
+// err = tmp_app_path->make_dir_recursive(iconset_dir);
+// }
+// memdelete(tmp_app_path);
+// if (err) {
+// return err;
+// }
+
+// err = _export_icons(p_preset, iconset_dir);
+// if (err) {
+// return err;
+// }
+
+// bool use_storyboard = p_preset->get("storyboard/use_launch_screen_storyboard");
+
+// String launch_image_path = dest_dir + binary_name + "/Images.xcassets/LaunchImage.launchimage/";
+// String splash_image_path = dest_dir + binary_name + "/Images.xcassets/SplashImage.imageset/";
+
+// DirAccess *launch_screen_da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
+
+// if (!launch_screen_da) {
+// return ERR_CANT_CREATE;
+// }
+
+// if (use_storyboard) {
+// print_line("Using Launch Storyboard");
+
+// if (launch_screen_da->change_dir(launch_image_path) == OK) {
+// launch_screen_da->erase_contents_recursive();
+// launch_screen_da->remove(launch_image_path);
+// }
+
+// err = _export_loading_screen_file(p_preset, splash_image_path);
+// } else {
+// print_line("Using Launch Images");
+
+// const String launch_screen_path = dest_dir + binary_name + "/Launch Screen.storyboard";
+
+// launch_screen_da->remove(launch_screen_path);
+
+// if (launch_screen_da->change_dir(splash_image_path) == OK) {
+// launch_screen_da->erase_contents_recursive();
+// launch_screen_da->remove(splash_image_path);
+// }
+
+// err = _export_loading_screen_images(p_preset, launch_image_path);
+// }
+
+// memdelete(launch_screen_da);
+
+// if (err) {
+// return err;
+// }
+
+// print_line("Exporting additional assets");
+// _export_additional_assets(dest_dir + binary_name, libraries, assets);
+// _add_assets_to_project(p_preset, project_file_data, assets);
+// String project_file_name = dest_dir + binary_name + ".xcodeproj/project.pbxproj";
+// FileAccess *f = FileAccess::open(project_file_name, FileAccess::WRITE);
+// if (!f) {
+// ERR_PRINT("Can't write '" + project_file_name + "'.");
+// return ERR_CANT_CREATE;
+// };
+// f->store_buffer(project_file_data.ptr(), project_file_data.size());
+// f->close();
+// memdelete(f);
+
+//#ifdef OSX_ENABLED
+// if (ep.step("Code-signing dylibs", 2)) {
+// return ERR_SKIP;
+// }
+// DirAccess *dylibs_dir = DirAccess::open(dest_dir + binary_name + "/dylibs");
+// ERR_FAIL_COND_V(!dylibs_dir, ERR_CANT_OPEN);
+// CodesignData codesign_data(p_preset, p_debug);
+// err = _walk_dir_recursive(dylibs_dir, _codesign, &codesign_data);
+// memdelete(dylibs_dir);
+// ERR_FAIL_COND_V(err, err);
+
+// if (ep.step("Making .xcarchive", 3)) {
+// return ERR_SKIP;
+// }
+// String archive_path = p_path.get_basename() + ".xcarchive";
+// List<String> archive_args;
+// archive_args.push_back("-project");
+// archive_args.push_back(dest_dir + binary_name + ".xcodeproj");
+// archive_args.push_back("-scheme");
+// archive_args.push_back(binary_name);
+// archive_args.push_back("-sdk");
+// archive_args.push_back("iphoneos");
+// archive_args.push_back("-configuration");
+// archive_args.push_back(p_debug ? "Debug" : "Release");
+// archive_args.push_back("-destination");
+// archive_args.push_back("generic/platform=iOS");
+// archive_args.push_back("archive");
+// archive_args.push_back("-archivePath");
+// archive_args.push_back(archive_path);
+// err = OS::get_singleton()->execute("xcodebuild", archive_args, true);
+// ERR_FAIL_COND_V(err, err);
+
+// if (ep.step("Making .ipa", 4)) {
+// return ERR_SKIP;
+// }
+// List<String> export_args;
+// export_args.push_back("-exportArchive");
+// export_args.push_back("-archivePath");
+// export_args.push_back(archive_path);
+// export_args.push_back("-exportOptionsPlist");
+// export_args.push_back(dest_dir + binary_name + "/export_options.plist");
+// export_args.push_back("-allowProvisioningUpdates");
+// export_args.push_back("-exportPath");
+// export_args.push_back(dest_dir);
+// err = OS::get_singleton()->execute("xcodebuild", export_args, true);
+// ERR_FAIL_COND_V(err, err);
+//#else
+// print_line(".ipa can only be built on macOS. Leaving Xcode project without building the package.");
+//#endif
+
+// return OK;
+//}
+
+//bool EditorExportPlatformIOS::can_export(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates) const {
+// String err;
+// bool valid = false;
+
+// // Look for export templates (first official, and if defined custom templates).
+
+// bool dvalid = exists_export_template("iphone.zip", &err);
+// bool rvalid = dvalid; // Both in the same ZIP.
+
+// if (p_preset->get("custom_template/debug") != "") {
+// dvalid = FileAccess::exists(p_preset->get("custom_template/debug"));
+// if (!dvalid) {
+// err += TTR("Custom debug template not found.") + "\n";
+// }
+// }
+// if (p_preset->get("custom_template/release") != "") {
+// rvalid = FileAccess::exists(p_preset->get("custom_template/release"));
+// if (!rvalid) {
+// err += TTR("Custom release template not found.") + "\n";
+// }
+// }
+
+// valid = dvalid || rvalid;
+// r_missing_templates = !valid;
+
+// // Validate the rest of the configuration.
+
+// String team_id = p_preset->get("application/app_store_team_id");
+// if (team_id.length() == 0) {
+// err += TTR("App Store Team ID not specified - cannot configure the project.") + "\n";
+// valid = false;
+// }
+
+// String identifier = p_preset->get("application/bundle_identifier");
+// String pn_err;
+// if (!is_package_name_valid(identifier, &pn_err)) {
+// err += TTR("Invalid Identifier:") + " " + pn_err + "\n";
+// valid = false;
+// }
+
+// for (uint64_t i = 0; i < (sizeof(icon_infos) / sizeof(icon_infos[0])); ++i) {
+// IconInfo info = icon_infos[i];
+// String icon_path = p_preset->get(info.preset_key);
+// if (icon_path.length() == 0) {
+// if (info.is_required) {
+// err += TTR("Required icon is not specified in the preset.") + "\n";
+// valid = false;
+// }
+// break;
+// }
+// }
+
+// String etc_error = test_etc2_or_pvrtc();
+// if (etc_error != String()) {
+// valid = false;
+// err += etc_error;
+// }
+
+// if (!err.is_empty()) {
+// r_error = err;
+// }
+
+// return valid;
+//}
+
+//EditorExportPlatformIOS::EditorExportPlatformIOS() {
+// Ref<Image> img = memnew(Image(_iphone_logo));
+// logo.instance();
+// logo->create_from_image(img);
+
+// plugins_changed = true;
+// quit_request = false;
+
+// check_for_changes_thread = Thread::create(_check_for_changes_poll_thread, this);
+//}
+
+//EditorExportPlatformIOS::~EditorExportPlatformIOS() {
+// quit_request = true;
+// Thread::wait_to_finish(check_for_changes_thread);
+// memdelete(check_for_changes_thread);
+//}
void register_iphone_exporter() {
Ref<EditorExportPlatformIOS> platform;
diff --git a/platform/iphone/platform_config.h b/platform/iphone/platform_config.h
index 88ad4a3f67..f8a8eb15d2 100644
--- a/platform/iphone/platform_config.h
+++ b/platform/iphone/platform_config.h
@@ -30,6 +30,9 @@
#include <alloca.h>
+#define GLES2_INCLUDE_H <ES2/gl.h>
+#define GLES3_INCLUDE_H <ES3/gl.h>
+
#define PLATFORM_REFCOUNT
#define PTHREAD_RENAME_SELF
diff --git a/platform/javascript/export/export.cpp b/platform/javascript/export/export.cpp
index 889b0bbd02..503e58f9a8 100644
--- a/platform/javascript/export/export.cpp
+++ b/platform/javascript/export/export.cpp
@@ -32,6 +32,678 @@
#include "export_plugin.h"
+/*
+
+class EditorHTTPServer : public Reference {
+private:
+ Ref<TCP_Server> server;
+ Ref<StreamPeerTCP> connection;
+ uint64_t time = 0;
+ uint8_t req_buf[4096];
+ int req_pos = 0;
+
+ void _clear_client() {
+ connection = Ref<StreamPeerTCP>();
+ memset(req_buf, 0, sizeof(req_buf));
+ time = 0;
+ req_pos = 0;
+ }
+
+public:
+ EditorHTTPServer() {
+ server.instance();
+ stop();
+ }
+
+ void stop() {
+ server->stop();
+ _clear_client();
+ }
+
+ Error listen(int p_port, IP_Address p_address) {
+ return server->listen(p_port, p_address);
+ }
+
+ bool is_listening() const {
+ return server->is_listening();
+ }
+
+ void _send_response() {
+ Vector<String> psa = String((char *)req_buf).split("\r\n");
+ int len = psa.size();
+ ERR_FAIL_COND_MSG(len < 4, "Not enough response headers, got: " + itos(len) + ", expected >= 4.");
+
+ Vector<String> req = psa[0].split(" ", false);
+ ERR_FAIL_COND_MSG(req.size() < 2, "Invalid protocol or status code.");
+
+ // Wrong protocol
+ ERR_FAIL_COND_MSG(req[0] != "GET" || req[2] != "HTTP/1.1", "Invalid method or HTTP version.");
+
+ const String cache_path = EditorSettings::get_singleton()->get_cache_dir();
+ const String basereq = "/tmp_js_export";
+ String filepath;
+ String ctype;
+ if (req[1] == basereq + ".html") {
+ filepath = cache_path.plus_file(req[1].get_file());
+ ctype = "text/html";
+ } else if (req[1] == basereq + ".js") {
+ filepath = cache_path.plus_file(req[1].get_file());
+ ctype = "application/javascript";
+ } else if (req[1] == basereq + ".audio.worklet.js") {
+ filepath = cache_path.plus_file(req[1].get_file());
+ ctype = "application/javascript";
+ } else if (req[1] == basereq + ".worker.js") {
+ filepath = cache_path.plus_file(req[1].get_file());
+ ctype = "application/javascript";
+ } else if (req[1] == basereq + ".pck") {
+ filepath = cache_path.plus_file(req[1].get_file());
+ ctype = "application/octet-stream";
+ } else if (req[1] == basereq + ".png" || req[1] == "/favicon.png") {
+ // Also allow serving the generated favicon for a smoother loading experience.
+ if (req[1] == "/favicon.png") {
+ filepath = EditorSettings::get_singleton()->get_cache_dir().plus_file("favicon.png");
+ } else {
+ filepath = basereq + ".png";
+ }
+ ctype = "image/png";
+ } else if (req[1] == basereq + ".side.wasm") {
+ filepath = cache_path.plus_file(req[1].get_file());
+ ctype = "application/wasm";
+ } else if (req[1] == basereq + ".wasm") {
+ filepath = cache_path.plus_file(req[1].get_file());
+ ctype = "application/wasm";
+ } else if (req[1].ends_with(".wasm")) {
+ filepath = cache_path.plus_file(req[1].get_file()); // TODO dangerous?
+ ctype = "application/wasm";
+ }
+ if (filepath.is_empty() || !FileAccess::exists(filepath)) {
+ String s = "HTTP/1.1 404 Not Found\r\n";
+ s += "Connection: Close\r\n";
+ s += "\r\n";
+ CharString cs = s.utf8();
+ connection->put_data((const uint8_t *)cs.get_data(), cs.size() - 1);
+ return;
+ }
+ FileAccess *f = FileAccess::open(filepath, FileAccess::READ);
+ ERR_FAIL_COND(!f);
+ String s = "HTTP/1.1 200 OK\r\n";
+ s += "Connection: Close\r\n";
+ s += "Content-Type: " + ctype + "\r\n";
+ s += "Access-Control-Allow-Origin: *\r\n";
+ s += "Cross-Origin-Opener-Policy: same-origin\r\n";
+ s += "Cross-Origin-Embedder-Policy: require-corp\r\n";
+ s += "\r\n";
+ CharString cs = s.utf8();
+ Error err = connection->put_data((const uint8_t *)cs.get_data(), cs.size() - 1);
+ if (err != OK) {
+ memdelete(f);
+ ERR_FAIL();
+ }
+
+ while (true) {
+ uint8_t bytes[4096];
+ int read = f->get_buffer(bytes, 4096);
+ if (read < 1) {
+ break;
+ }
+ err = connection->put_data(bytes, read);
+ if (err != OK) {
+ memdelete(f);
+ ERR_FAIL();
+ }
+ }
+ memdelete(f);
+ }
+
+ void poll() {
+ if (!server->is_listening()) {
+ return;
+ }
+ if (connection.is_null()) {
+ if (!server->is_connection_available()) {
+ return;
+ }
+ connection = server->take_connection();
+ time = OS::get_singleton()->get_ticks_usec();
+ }
+ if (OS::get_singleton()->get_ticks_usec() - time > 1000000) {
+ _clear_client();
+ return;
+ }
+ if (connection->get_status() != StreamPeerTCP::STATUS_CONNECTED) {
+ return;
+ }
+
+ while (true) {
+ char *r = (char *)req_buf;
+ int l = req_pos - 1;
+ if (l > 3 && r[l] == '\n' && r[l - 1] == '\r' && r[l - 2] == '\n' && r[l - 3] == '\r') {
+ _send_response();
+ _clear_client();
+ return;
+ }
+
+ int read = 0;
+ ERR_FAIL_COND(req_pos >= 4096);
+ Error err = connection->get_partial_data(&req_buf[req_pos], 1, read);
+ if (err != OK) {
+ // Got an error
+ _clear_client();
+ return;
+ } else if (read != 1) {
+ // Busy, wait next poll
+ return;
+ }
+ req_pos += read;
+ }
+ }
+};
+
+class EditorExportPlatformJavaScript : public EditorExportPlatform {
+ GDCLASS(EditorExportPlatformJavaScript, EditorExportPlatform);
+
+ Ref<ImageTexture> logo;
+ Ref<ImageTexture> run_icon;
+ Ref<ImageTexture> stop_icon;
+ int menu_options = 0;
+
+ Ref<EditorHTTPServer> server;
+ bool server_quit = false;
+ Mutex server_lock;
+ Thread *server_thread = nullptr;
+
+ enum ExportMode {
+ EXPORT_MODE_NORMAL = 0,
+ EXPORT_MODE_THREADS = 1,
+ EXPORT_MODE_GDNATIVE = 2,
+ };
+
+ String _get_template_name(ExportMode p_mode, bool p_debug) const {
+ String name = "webassembly";
+ switch (p_mode) {
+ case EXPORT_MODE_THREADS:
+ name += "_threads";
+ break;
+ case EXPORT_MODE_GDNATIVE:
+ name += "_gdnative";
+ break;
+ default:
+ break;
+ }
+ if (p_debug) {
+ name += "_debug.zip";
+ } else {
+ name += "_release.zip";
+ }
+ return name;
+ }
+
+ void _fix_html(Vector<uint8_t> &p_html, const Ref<EditorExportPreset> &p_preset, const String &p_name, bool p_debug, int p_flags, const Vector<SharedObject> p_shared_objects);
+
+ static void _server_thread_poll(void *data);
+
+public:
+ virtual void get_preset_features(const Ref<EditorExportPreset> &p_preset, List<String> *r_features) override;
+
+ virtual void get_export_options(List<ExportOption> *r_options) override;
+
+ virtual String get_name() const override;
+ virtual String get_os_name() const override;
+ virtual Ref<Texture2D> get_logo() const override;
+
+ virtual bool can_export(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates) const override;
+ virtual List<String> get_binary_extensions(const Ref<EditorExportPreset> &p_preset) const override;
+ virtual Error export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags = 0) override;
+
+ virtual bool poll_export() override;
+ virtual int get_options_count() const override;
+ virtual String get_option_label(int p_index) const override { return p_index ? TTR("Stop HTTP Server") : TTR("Run in Browser"); }
+ virtual String get_option_tooltip(int p_index) const override { return p_index ? TTR("Stop HTTP Server") : TTR("Run exported HTML in the system's default browser."); }
+ virtual Ref<ImageTexture> get_option_icon(int p_index) const override;
+ virtual Error run(const Ref<EditorExportPreset> &p_preset, int p_option, int p_debug_flags) override;
+ virtual Ref<Texture2D> get_run_icon() const override;
+
+ virtual void get_platform_features(List<String> *r_features) override {
+ r_features->push_back("web");
+ r_features->push_back(get_os_name());
+ }
+
+ virtual void resolve_platform_feature_priorities(const Ref<EditorExportPreset> &p_preset, Set<String> &p_features) override {
+ }
+
+ String get_debug_protocol() const override { return "ws://"; }
+
+ EditorExportPlatformJavaScript();
+ ~EditorExportPlatformJavaScript();
+};
+
+void EditorExportPlatformJavaScript::_fix_html(Vector<uint8_t> &p_html, const Ref<EditorExportPreset> &p_preset, const String &p_name, bool p_debug, int p_flags, const Vector<SharedObject> p_shared_objects) {
+ String str_template = String::utf8(reinterpret_cast<const char *>(p_html.ptr()), p_html.size());
+ String str_export;
+ Vector<String> lines = str_template.split("\n");
+ Vector<String> flags;
+ String flags_json;
+ gen_export_flags(flags, p_flags);
+ flags_json = JSON::print(flags);
+ String libs;
+ for (int i = 0; i < p_shared_objects.size(); i++) {
+ libs += "\"" + p_shared_objects[i].path.get_file() + "\",";
+ }
+
+ for (int i = 0; i < lines.size(); i++) {
+ String current_line = lines[i];
+ current_line = current_line.replace("$GODOT_BASENAME", p_name);
+ current_line = current_line.replace("$GODOT_PROJECT_NAME", ProjectSettings::get_singleton()->get_setting("application/config/name"));
+ current_line = current_line.replace("$GODOT_HEAD_INCLUDE", p_preset->get("html/head_include"));
+ current_line = current_line.replace("$GODOT_FULL_WINDOW", p_preset->get("html/full_window_size") ? "true" : "false");
+ current_line = current_line.replace("$GODOT_GDNATIVE_LIBS", libs);
+ current_line = current_line.replace("$GODOT_DEBUG_ENABLED", p_debug ? "true" : "false");
+ current_line = current_line.replace("$GODOT_ARGS", flags_json);
+ str_export += current_line + "\n";
+ }
+
+ CharString cs = str_export.utf8();
+ p_html.resize(cs.length());
+ for (int i = 0; i < cs.length(); i++) {
+ p_html.write[i] = cs[i];
+ }
+}
+
+void EditorExportPlatformJavaScript::get_preset_features(const Ref<EditorExportPreset> &p_preset, List<String> *r_features) {
+ if (p_preset->get("vram_texture_compression/for_desktop")) {
+ r_features->push_back("s3tc");
+ }
+
+ if (p_preset->get("vram_texture_compression/for_mobile")) {
+ String driver = ProjectSettings::get_singleton()->get("rendering/driver/driver_name");
+ if (driver == "GLES2") {
+ r_features->push_back("etc");
+ } else if (driver == "Vulkan") {
+ // FIXME: Review if this is correct.
+ r_features->push_back("etc2");
+ }
+ }
+ ExportMode mode = (ExportMode)(int)p_preset->get("variant/export_type");
+ if (mode == EXPORT_MODE_THREADS) {
+ r_features->push_back("threads");
+ } else if (mode == EXPORT_MODE_GDNATIVE) {
+ r_features->push_back("wasm32");
+ }
+}
+
+void EditorExportPlatformJavaScript::get_export_options(List<ExportOption> *r_options) {
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "custom_template/debug", PROPERTY_HINT_GLOBAL_FILE, "*.zip"), ""));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "custom_template/release", PROPERTY_HINT_GLOBAL_FILE, "*.zip"), ""));
+
+ r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "variant/export_type", PROPERTY_HINT_ENUM, "Regular,Threads,GDNative"), 0)); // Export type.
+ r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "vram_texture_compression/for_desktop"), true)); // S3TC
+ r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "vram_texture_compression/for_mobile"), false)); // ETC or ETC2, depending on renderer
+
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "html/custom_html_shell", PROPERTY_HINT_FILE, "*.html"), ""));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "html/head_include", PROPERTY_HINT_MULTILINE_TEXT), ""));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "html/full_window_size"), true));
+}
+
+String EditorExportPlatformJavaScript::get_name() const {
+ return "HTML5";
+}
+
+String EditorExportPlatformJavaScript::get_os_name() const {
+ return "HTML5";
+}
+
+Ref<Texture2D> EditorExportPlatformJavaScript::get_logo() const {
+ return logo;
+}
+
+bool EditorExportPlatformJavaScript::can_export(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates) const {
+ String err;
+ bool valid = false;
+ ExportMode mode = (ExportMode)(int)p_preset->get("variant/export_type");
+
+ // Look for export templates (first official, and if defined custom templates).
+ bool dvalid = exists_export_template(_get_template_name(mode, true), &err);
+ bool rvalid = exists_export_template(_get_template_name(mode, false), &err);
+
+ if (p_preset->get("custom_template/debug") != "") {
+ dvalid = FileAccess::exists(p_preset->get("custom_template/debug"));
+ if (!dvalid) {
+ err += TTR("Custom debug template not found.") + "\n";
+ }
+ }
+ if (p_preset->get("custom_template/release") != "") {
+ rvalid = FileAccess::exists(p_preset->get("custom_template/release"));
+ if (!rvalid) {
+ err += TTR("Custom release template not found.") + "\n";
+ }
+ }
+
+ valid = dvalid || rvalid;
+ r_missing_templates = !valid;
+
+ // Validate the rest of the configuration.
+
+ if (p_preset->get("vram_texture_compression/for_mobile")) {
+ String etc_error = test_etc2();
+ if (etc_error != String()) {
+ valid = false;
+ err += etc_error;
+ }
+ }
+
+ if (!err.is_empty()) {
+ r_error = err;
+ }
+
+ return valid;
+}
+
+List<String> EditorExportPlatformJavaScript::get_binary_extensions(const Ref<EditorExportPreset> &p_preset) const {
+ List<String> list;
+ list.push_back("html");
+ return list;
+}
+
+Error EditorExportPlatformJavaScript::export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags) {
+ ExportNotifier notifier(*this, p_preset, p_debug, p_path, p_flags);
+
+ String custom_debug = p_preset->get("custom_template/debug");
+ String custom_release = p_preset->get("custom_template/release");
+ String custom_html = p_preset->get("html/custom_html_shell");
+
+ String template_path = p_debug ? custom_debug : custom_release;
+
+ template_path = template_path.strip_edges();
+
+ if (template_path == String()) {
+ ExportMode mode = (ExportMode)(int)p_preset->get("variant/export_type");
+ template_path = find_export_template(_get_template_name(mode, p_debug));
+ }
+
+ if (!DirAccess::exists(p_path.get_base_dir())) {
+ return ERR_FILE_BAD_PATH;
+ }
+
+ if (template_path != String() && !FileAccess::exists(template_path)) {
+ EditorNode::get_singleton()->show_warning(TTR("Template file not found:") + "\n" + template_path);
+ return ERR_FILE_NOT_FOUND;
+ }
+
+ Vector<SharedObject> shared_objects;
+ String pck_path = p_path.get_basename() + ".pck";
+ Error error = save_pack(p_preset, pck_path, &shared_objects);
+ if (error != OK) {
+ EditorNode::get_singleton()->show_warning(TTR("Could not write file:") + "\n" + pck_path);
+ return error;
+ }
+ DirAccess *da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
+ for (int i = 0; i < shared_objects.size(); i++) {
+ String dst = p_path.get_base_dir().plus_file(shared_objects[i].path.get_file());
+ error = da->copy(shared_objects[i].path, dst);
+ if (error != OK) {
+ EditorNode::get_singleton()->show_warning(TTR("Could not write file:") + "\n" + shared_objects[i].path.get_file());
+ memdelete(da);
+ return error;
+ }
+ }
+ memdelete(da);
+
+ FileAccess *src_f = nullptr;
+ zlib_filefunc_def io = zipio_create_io_from_file(&src_f);
+ unzFile pkg = unzOpen2(template_path.utf8().get_data(), &io);
+
+ if (!pkg) {
+ EditorNode::get_singleton()->show_warning(TTR("Could not open template for export:") + "\n" + template_path);
+ return ERR_FILE_NOT_FOUND;
+ }
+
+ if (unzGoToFirstFile(pkg) != UNZ_OK) {
+ EditorNode::get_singleton()->show_warning(TTR("Invalid export template:") + "\n" + template_path);
+ unzClose(pkg);
+ return ERR_FILE_CORRUPT;
+ }
+
+ do {
+ //get filename
+ unz_file_info info;
+ char fname[16384];
+ unzGetCurrentFileInfo(pkg, &info, fname, 16384, nullptr, 0, nullptr, 0);
+
+ String file = fname;
+
+ Vector<uint8_t> data;
+ data.resize(info.uncompressed_size);
+
+ //read
+ unzOpenCurrentFile(pkg);
+ unzReadCurrentFile(pkg, data.ptrw(), data.size());
+ unzCloseCurrentFile(pkg);
+
+ //write
+
+ if (file == "godot.html") {
+ if (!custom_html.is_empty()) {
+ continue;
+ }
+ _fix_html(data, p_preset, p_path.get_file().get_basename(), p_debug, p_flags, shared_objects);
+ file = p_path.get_file();
+
+ } else if (file == "godot.js") {
+ file = p_path.get_file().get_basename() + ".js";
+
+ } else if (file == "godot.worker.js") {
+ file = p_path.get_file().get_basename() + ".worker.js";
+
+ } else if (file == "godot.side.wasm") {
+ file = p_path.get_file().get_basename() + ".side.wasm";
+
+ } else if (file == "godot.audio.worklet.js") {
+ file = p_path.get_file().get_basename() + ".audio.worklet.js";
+
+ } else if (file == "godot.wasm") {
+ file = p_path.get_file().get_basename() + ".wasm";
+ }
+
+ String dst = p_path.get_base_dir().plus_file(file);
+ FileAccess *f = FileAccess::open(dst, FileAccess::WRITE);
+ if (!f) {
+ EditorNode::get_singleton()->show_warning(TTR("Could not write file:") + "\n" + dst);
+ unzClose(pkg);
+ return ERR_FILE_CANT_WRITE;
+ }
+ f->store_buffer(data.ptr(), data.size());
+ memdelete(f);
+
+ } while (unzGoToNextFile(pkg) == UNZ_OK);
+ unzClose(pkg);
+
+ if (!custom_html.is_empty()) {
+ FileAccess *f = FileAccess::open(custom_html, FileAccess::READ);
+ if (!f) {
+ EditorNode::get_singleton()->show_warning(TTR("Could not read custom HTML shell:") + "\n" + custom_html);
+ return ERR_FILE_CANT_READ;
+ }
+ Vector<uint8_t> buf;
+ buf.resize(f->get_len());
+ f->get_buffer(buf.ptrw(), buf.size());
+ memdelete(f);
+ _fix_html(buf, p_preset, p_path.get_file().get_basename(), p_debug, p_flags, shared_objects);
+
+ f = FileAccess::open(p_path, FileAccess::WRITE);
+ if (!f) {
+ EditorNode::get_singleton()->show_warning(TTR("Could not write file:") + "\n" + p_path);
+ return ERR_FILE_CANT_WRITE;
+ }
+ f->store_buffer(buf.ptr(), buf.size());
+ memdelete(f);
+ }
+
+ Ref<Image> splash;
+ const String splash_path = String(GLOBAL_GET("application/boot_splash/image")).strip_edges();
+ if (!splash_path.is_empty()) {
+ splash.instance();
+ const Error err = splash->load(splash_path);
+ if (err) {
+ EditorNode::get_singleton()->show_warning(TTR("Could not read boot splash image file:") + "\n" + splash_path + "\n" + TTR("Using default boot splash image."));
+ splash.unref();
+ }
+ }
+ if (splash.is_null()) {
+ splash = Ref<Image>(memnew(Image(boot_splash_png)));
+ }
+ const String splash_png_path = p_path.get_base_dir().plus_file(p_path.get_file().get_basename() + ".png");
+ if (splash->save_png(splash_png_path) != OK) {
+ EditorNode::get_singleton()->show_warning(TTR("Could not write file:") + "\n" + splash_png_path);
+ return ERR_FILE_CANT_WRITE;
+ }
+
+ // Save a favicon that can be accessed without waiting for the project to finish loading.
+ // This way, the favicon can be displayed immediately when loading the page.
+ Ref<Image> favicon;
+ const String favicon_path = String(GLOBAL_GET("application/config/icon")).strip_edges();
+ if (!favicon_path.is_empty()) {
+ favicon.instance();
+ const Error err = favicon->load(favicon_path);
+ if (err) {
+ favicon.unref();
+ }
+ }
+
+ if (favicon.is_valid()) {
+ const String favicon_png_path = p_path.get_base_dir().plus_file("favicon.png");
+ if (favicon->save_png(favicon_png_path) != OK) {
+ EditorNode::get_singleton()->show_warning(TTR("Could not write file:") + "\n" + favicon_png_path);
+ return ERR_FILE_CANT_WRITE;
+ }
+ }
+
+ return OK;
+}
+
+bool EditorExportPlatformJavaScript::poll_export() {
+ Ref<EditorExportPreset> preset;
+
+ for (int i = 0; i < EditorExport::get_singleton()->get_export_preset_count(); i++) {
+ Ref<EditorExportPreset> ep = EditorExport::get_singleton()->get_export_preset(i);
+ if (ep->is_runnable() && ep->get_platform() == this) {
+ preset = ep;
+ break;
+ }
+ }
+
+ int prev = menu_options;
+ menu_options = preset.is_valid();
+ if (server->is_listening()) {
+ if (menu_options == 0) {
+ MutexLock lock(server_lock);
+ server->stop();
+ } else {
+ menu_options += 1;
+ }
+ }
+ return menu_options != prev;
+}
+
+Ref<ImageTexture> EditorExportPlatformJavaScript::get_option_icon(int p_index) const {
+ return p_index == 1 ? stop_icon : EditorExportPlatform::get_option_icon(p_index);
+}
+
+int EditorExportPlatformJavaScript::get_options_count() const {
+ return menu_options;
+}
+
+Error EditorExportPlatformJavaScript::run(const Ref<EditorExportPreset> &p_preset, int p_option, int p_debug_flags) {
+ if (p_option == 1) {
+ MutexLock lock(server_lock);
+ server->stop();
+ return OK;
+ }
+
+ const String basepath = EditorSettings::get_singleton()->get_cache_dir().plus_file("tmp_js_export");
+ Error err = export_project(p_preset, true, basepath + ".html", p_debug_flags);
+ if (err != OK) {
+ // Export generates several files, clean them up on failure.
+ DirAccess::remove_file_or_error(basepath + ".html");
+ DirAccess::remove_file_or_error(basepath + ".js");
+ DirAccess::remove_file_or_error(basepath + ".worker.js");
+ DirAccess::remove_file_or_error(basepath + ".audio.worklet.js");
+ DirAccess::remove_file_or_error(basepath + ".pck");
+ DirAccess::remove_file_or_error(basepath + ".png");
+ DirAccess::remove_file_or_error(basepath + ".side.wasm");
+ DirAccess::remove_file_or_error(basepath + ".wasm");
+ DirAccess::remove_file_or_error(EditorSettings::get_singleton()->get_cache_dir().plus_file("favicon.png"));
+ return err;
+ }
+
+ const uint16_t bind_port = EDITOR_GET("export/web/http_port");
+ // Resolve host if needed.
+ const String bind_host = EDITOR_GET("export/web/http_host");
+ IP_Address bind_ip;
+ if (bind_host.is_valid_ip_address()) {
+ bind_ip = bind_host;
+ } else {
+ bind_ip = IP::get_singleton()->resolve_hostname(bind_host);
+ }
+ ERR_FAIL_COND_V_MSG(!bind_ip.is_valid(), ERR_INVALID_PARAMETER, "Invalid editor setting 'export/web/http_host': '" + bind_host + "'. Try using '127.0.0.1'.");
+
+ // Restart server.
+ {
+ MutexLock lock(server_lock);
+
+ server->stop();
+ err = server->listen(bind_port, bind_ip);
+ }
+ ERR_FAIL_COND_V_MSG(err != OK, err, "Unable to start HTTP server.");
+
+ OS::get_singleton()->shell_open(String("http://" + bind_host + ":" + itos(bind_port) + "/tmp_js_export.html"));
+ // FIXME: Find out how to clean up export files after running the successfully
+ // exported game. Might not be trivial.
+ return OK;
+}
+
+Ref<Texture2D> EditorExportPlatformJavaScript::get_run_icon() const {
+ return run_icon;
+}
+
+void EditorExportPlatformJavaScript::_server_thread_poll(void *data) {
+ EditorExportPlatformJavaScript *ej = (EditorExportPlatformJavaScript *)data;
+ while (!ej->server_quit) {
+ OS::get_singleton()->delay_usec(1000);
+ {
+ MutexLock lock(ej->server_lock);
+ ej->server->poll();
+ }
+ }
+}
+
+EditorExportPlatformJavaScript::EditorExportPlatformJavaScript() {
+ server.instance();
+ server_thread = Thread::create(_server_thread_poll, this);
+
+ Ref<Image> img = memnew(Image(_javascript_logo));
+ logo.instance();
+ logo->create_from_image(img);
+
+ img = Ref<Image>(memnew(Image(_javascript_run_icon)));
+ run_icon.instance();
+ run_icon->create_from_image(img);
+
+ Ref<Theme> theme = EditorNode::get_singleton()->get_editor_theme();
+ if (theme.is_valid()) {
+ stop_icon = theme->get_icon("Stop", "EditorIcons");
+ } else {
+ stop_icon.instance();
+ }
+}
+
+EditorExportPlatformJavaScript::~EditorExportPlatformJavaScript() {
+ server->stop();
+ server_quit = true;
+ Thread::wait_to_finish(server_thread);
+ memdelete(server_thread);
+}
+
+*/
void register_javascript_exporter() {
EDITOR_DEF("export/web/http_host", "localhost");
EDITOR_DEF("export/web/http_port", 8060);
diff --git a/platform/linuxbsd/SCsub b/platform/linuxbsd/SCsub
index 8aebd57fd2..05393f6246 100644
--- a/platform/linuxbsd/SCsub
+++ b/platform/linuxbsd/SCsub
@@ -14,12 +14,14 @@ common_linuxbsd = [
if "x11" in env and env["x11"]:
common_linuxbsd += [
- "context_gl_x11.cpp",
+ "gl_manager_x11.cpp",
"detect_prime_x11.cpp",
"display_server_x11.cpp",
"key_mapping_x11.cpp",
]
+#"context_gl_x11.cpp",
+
if "vulkan" in env and env["vulkan"]:
common_linuxbsd.append("vulkan_context_x11.cpp")
diff --git a/platform/linuxbsd/detect.py b/platform/linuxbsd/detect.py
index 4b3804d049..04da082b16 100644
--- a/platform/linuxbsd/detect.py
+++ b/platform/linuxbsd/detect.py
@@ -381,8 +381,8 @@ def configure(env):
# No pkgconfig file for glslang so far
env.Append(LIBS=["glslang", "SPIRV"])
- # env.Append(CPPDEFINES=['OPENGL_ENABLED'])
- env.Append(LIBS=["GL"])
+ env.Append(CPPDEFINES=["OPENGL_ENABLED"])
+ env.Append(LIBS=["GL"])
env.Append(LIBS=["pthread"])
diff --git a/platform/linuxbsd/detect_prime_x11.cpp b/platform/linuxbsd/detect_prime_x11.cpp
index da1c95a593..f074c9e9d8 100644
--- a/platform/linuxbsd/detect_prime_x11.cpp
+++ b/platform/linuxbsd/detect_prime_x11.cpp
@@ -31,7 +31,7 @@
#ifdef X11_ENABLED
#if defined(OPENGL_ENABLED)
-#include "detect_prime.h"
+#include "detect_prime_x11.h"
#include "core/string/print_string.h"
#include "core/string/ustring.h"
diff --git a/platform/linuxbsd/display_server_x11.cpp b/platform/linuxbsd/display_server_x11.cpp
index dd296ec3b4..18247deafb 100644
--- a/platform/linuxbsd/display_server_x11.cpp
+++ b/platform/linuxbsd/display_server_x11.cpp
@@ -34,6 +34,7 @@
#include "core/config/project_settings.h"
#include "core/string/print_string.h"
+#include "core/string/ustring.h"
#include "detect_prime_x11.h"
#include "key_mapping_x11.h"
#include "main/main.h"
@@ -43,6 +44,10 @@
#include "servers/rendering/renderer_rd/renderer_compositor_rd.h"
#endif
+#if defined(GLES_X11_ENABLED)
+#include "drivers/gles2/rasterizer_gles2.h"
+#endif
+
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
@@ -884,6 +889,12 @@ void DisplayServerX11::delete_sub_window(WindowID p_id) {
context_vulkan->window_destroy(p_id);
}
#endif
+#ifdef GLES_X11_ENABLED
+ if (gl_manager) {
+ gl_manager->window_destroy(p_id);
+ }
+#endif
+
XUnmapWindow(x11_display, wd.x11_window);
XDestroyWindow(x11_display, wd.x11_window);
if (wd.xic) {
@@ -1052,6 +1063,13 @@ int DisplayServerX11::window_get_current_screen(WindowID p_window) const {
return screen_index;
}
+void DisplayServerX11::gl_window_make_current(DisplayServer::WindowID p_window_id) {
+#if defined(GLES_X11_ENABLED)
+ if (gl_manager)
+ gl_manager->window_make_current(p_window_id);
+#endif
+}
+
void DisplayServerX11::window_set_current_screen(int p_screen, WindowID p_window) {
_THREAD_SAFE_METHOD_
@@ -2648,6 +2666,11 @@ void DisplayServerX11::_window_changed(XEvent *event) {
context_vulkan->window_resize(window_id, wd.size.width, wd.size.height);
}
#endif
+#if defined(GLES_X11_ENABLED)
+ if (gl_manager) {
+ gl_manager->window_resize(window_id, wd.size.width, wd.size.height);
+ }
+#endif
if (!wd.rect_changed_callback.is_null()) {
Rect2i r = new_rect;
@@ -3524,12 +3547,23 @@ void DisplayServerX11::process_events() {
}
void DisplayServerX11::release_rendering_thread() {
+#if defined(GLES_X11_ENABLED)
+// gl_manager->release_current();
+#endif
}
void DisplayServerX11::make_rendering_thread() {
+#if defined(GLES_X11_ENABLED)
+// gl_manager->make_current();
+#endif
}
void DisplayServerX11::swap_buffers() {
+#if defined(GLES_X11_ENABLED)
+ if (gl_manager) {
+ gl_manager->swap_buffers();
+ }
+#endif
}
void DisplayServerX11::_update_context(WindowData &wd) {
@@ -3671,17 +3705,31 @@ void DisplayServerX11::set_icon(const Ref<Image> &p_icon) {
void DisplayServerX11::window_set_vsync_mode(DisplayServer::VSyncMode p_vsync_mode, WindowID p_window) {
_THREAD_SAFE_METHOD_
#if defined(VULKAN_ENABLED)
- context_vulkan->set_vsync_mode(p_window, p_vsync_mode);
+ if (context_vulkan) {
+ context_vulkan->set_vsync_mode(p_window, p_vsync_mode);
+ }
+#endif
+
+#if defined(GLES_X11_ENABLED)
+ if (gl_manager) {
+ gl_manager->set_use_vsync(p_vsync_mode == DisplayServer::VSYNC_ENABLED);
+ }
#endif
}
DisplayServer::VSyncMode DisplayServerX11::window_get_vsync_mode(WindowID p_window) const {
_THREAD_SAFE_METHOD_
#if defined(VULKAN_ENABLED)
- return context_vulkan->get_vsync_mode(p_window);
-#else
- return DisplayServer::VSYNC_ENABLED;
+ if (context_vulkan) {
+ return context_vulkan->get_vsync_mode(p_window);
+ }
+#endif
+#if defined(GLES_X11_ENABLED)
+ if (gl_manager) {
+ return gl_manager->is_using_vsync() ? DisplayServer::VSYNC_ENABLED : DisplayServer::VSYNC_DISABLED;
+ }
#endif
+ return DisplayServer::VSYNC_ENABLED;
}
Vector<String> DisplayServerX11::get_rendering_drivers_func() {
@@ -3690,8 +3738,10 @@ Vector<String> DisplayServerX11::get_rendering_drivers_func() {
#ifdef VULKAN_ENABLED
drivers.push_back("vulkan");
#endif
-#ifdef OPENGL_ENABLED
- drivers.push_back("opengl");
+#ifdef GLES_X11_ENABLED
+ // drivers.push_back("opengl");
+ drivers.push_back("GLES2");
+ drivers.push_back("GLES3");
#endif
return drivers;
@@ -3876,6 +3926,13 @@ DisplayServerX11::WindowID DisplayServerX11::_create_window(WindowMode p_mode, V
ERR_FAIL_COND_V_MSG(err != OK, INVALID_WINDOW_ID, "Can't create a Vulkan window");
}
#endif
+#ifdef GLES_X11_ENABLED
+ print_line("rendering_driver " + rendering_driver);
+ if (gl_manager) {
+ Error err = gl_manager->window_create(id, wd.x11_window, x11_display, p_rect.size.width, p_rect.size.height);
+ ERR_FAIL_COND_V_MSG(err != OK, INVALID_WINDOW_ID, "Can't create a GLES2 window");
+ }
+#endif
//set_class_hint(x11_display, wd.x11_window);
XFlush(x11_display);
@@ -4061,10 +4118,13 @@ DisplayServerX11::DisplayServerX11(const String &p_rendering_driver, WindowMode
rendering_driver = p_rendering_driver;
#ifndef _MSC_VER
-#warning Forcing vulkan rendering driver because OpenGL not implemented yet
+//#warning Forcing vulkan rendering driver because OpenGL not implemented yet
+//#warning Forcing opengl rendering driver because selecting properly is too much effort
#endif
- rendering_driver = "vulkan";
+ // rendering_driver = "vulkan";
+ //rendering_driver = "GLES2";
+ bool driver_found = false;
#if defined(VULKAN_ENABLED)
if (rendering_driver == "vulkan") {
context_vulkan = memnew(VulkanContextX11);
@@ -4074,11 +4134,13 @@ DisplayServerX11::DisplayServerX11(const String &p_rendering_driver, WindowMode
r_error = ERR_CANT_CREATE;
ERR_FAIL_MSG("Could not initialize Vulkan");
}
+ driver_found = true;
}
#endif
// Init context and rendering device
-#if defined(OPENGL_ENABLED)
- if (rendering_driver == "opengl_es") {
+#if defined(GLES_X11_ENABLED)
+ print_line("rendering_driver " + rendering_driver);
+ if (rendering_driver == "GLES2") {
if (getenv("DRI_PRIME") == nullptr) {
int use_prime = -1;
@@ -4120,28 +4182,37 @@ DisplayServerX11::DisplayServerX11(const String &p_rendering_driver, WindowMode
}
}
- ContextGL_X11::ContextType opengl_api_type = ContextGL_X11::GLES_2_0_COMPATIBLE;
+ GLManager_X11::ContextType opengl_api_type = GLManager_X11::GLES_2_0_COMPATIBLE;
- context_gles2 = memnew(ContextGL_X11(x11_display, x11_window, current_videomode, opengl_api_type));
+ gl_manager = memnew(GLManager_X11(p_resolution, opengl_api_type));
- if (context_gles2->initialize() != OK) {
- memdelete(context_gles2);
- context_gles2 = nullptr;
- ERR_FAIL_V(ERR_UNAVAILABLE);
+ if (gl_manager->initialize() != OK) {
+ memdelete(gl_manager);
+ gl_manager = nullptr;
+ r_error = ERR_UNAVAILABLE;
+ return;
}
+ driver_found = true;
- context_gles2->set_use_vsync(current_videomode.use_vsync);
+ // gl_manager->set_use_vsync(current_videomode.use_vsync);
- if (RasterizerGLES2::is_viable() == OK) {
- RasterizerGLES2::register_config();
+ if (true) {
+ // if (RasterizerGLES2::is_viable() == OK) {
+ // RasterizerGLES2::register_config();
RasterizerGLES2::make_current();
} else {
- memdelete(context_gles2);
- context_gles2 = nullptr;
- ERR_FAIL_V(ERR_UNAVAILABLE);
+ memdelete(gl_manager);
+ gl_manager = nullptr;
+ r_error = ERR_UNAVAILABLE;
+ return;
}
}
#endif
+ if (!driver_found) {
+ r_error = ERR_UNAVAILABLE;
+ ERR_FAIL_MSG("Video driver not found");
+ }
+
Point2i window_position(
(screen_get_size(0).width - p_resolution.width) / 2,
(screen_get_size(0).height - p_resolution.height) / 2);
@@ -4344,6 +4415,11 @@ DisplayServerX11::~DisplayServerX11() {
context_vulkan->window_destroy(E.key);
}
#endif
+#ifdef GLES_X11_ENABLED
+ if (rendering_driver == "GLES2") {
+ gl_manager->window_destroy(E->key());
+ }
+#endif
WindowData &wd = E.value;
if (wd.xic) {
@@ -4368,6 +4444,13 @@ DisplayServerX11::~DisplayServerX11() {
}
#endif
+#ifdef GLES_X11_ENABLED
+ if (gl_manager) {
+ memdelete(gl_manager);
+ gl_manager = nullptr;
+ }
+#endif
+
if (xrandr_handle) {
dlclose(xrandr_handle);
}
diff --git a/platform/linuxbsd/display_server_x11.h b/platform/linuxbsd/display_server_x11.h
index 4575714bf2..bbf0e64fd3 100644
--- a/platform/linuxbsd/display_server_x11.h
+++ b/platform/linuxbsd/display_server_x11.h
@@ -31,6 +31,8 @@
#ifndef DISPLAY_SERVER_X11_H
#define DISPLAY_SERVER_X11_H
+#include "drivers/gles_common/rasterizer_platforms.h"
+
#ifdef X11_ENABLED
#include "servers/display_server.h"
@@ -46,8 +48,8 @@
#include "servers/rendering/renderer_compositor.h"
#include "servers/rendering_server.h"
-#if defined(OPENGL_ENABLED)
-#include "context_gl_x11.h"
+#if defined(GLES_X11_ENABLED)
+#include "gl_manager_x11.h"
#endif
#if defined(VULKAN_ENABLED)
@@ -99,12 +101,12 @@ class DisplayServerX11 : public DisplayServer {
Atom requested;
int xdnd_version;
-#if defined(OPENGL_ENABLED)
- ContextGL_X11 *context_gles2;
+#if defined(GLES_X11_ENABLED)
+ GLManager_X11 *gl_manager = nullptr;
#endif
#if defined(VULKAN_ENABLED)
- VulkanContextX11 *context_vulkan;
- RenderingDeviceVulkan *rendering_device_vulkan;
+ VulkanContextX11 *context_vulkan = nullptr;
+ RenderingDeviceVulkan *rendering_device_vulkan = nullptr;
#endif
#if defined(DBUS_ENABLED)
@@ -337,6 +339,7 @@ public:
virtual void window_set_max_size(const Size2i p_size, WindowID p_window = MAIN_WINDOW_ID) override;
virtual Size2i window_get_max_size(WindowID p_window = MAIN_WINDOW_ID) const override;
+ virtual void gl_window_make_current(DisplayServer::WindowID p_window_id);
virtual void window_set_transient(WindowID p_window, WindowID p_parent) override;
diff --git a/platform/linuxbsd/context_gl_x11.cpp b/platform/linuxbsd/gl_manager_x11.cpp
index 1f92370ab7..e3d12dcb01 100644
--- a/platform/linuxbsd/context_gl_x11.cpp
+++ b/platform/linuxbsd/gl_manager_x11.cpp
@@ -1,5 +1,5 @@
/*************************************************************************/
-/* context_gl_x11.cpp */
+/* gl_manager_x11.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@@ -28,10 +28,11 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#include "context_gl_x11.h"
+#include "gl_manager_x11.h"
#ifdef X11_ENABLED
-#if defined(OPENGL_ENABLED)
+#if defined(GLES_X11_ENABLED)
+
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
@@ -45,20 +46,17 @@
typedef GLXContext (*GLXCREATECONTEXTATTRIBSARBPROC)(Display *, GLXFBConfig, GLXContext, Bool, const int *);
-struct ContextGL_X11_Private {
+struct GLManager_X11_Private {
::GLXContext glx_context;
};
-void ContextGL_X11::release_current() {
- glXMakeCurrent(x11_display, None, nullptr);
-}
-
-void ContextGL_X11::make_current() {
- glXMakeCurrent(x11_display, x11_window, p->glx_context);
-}
-
-void ContextGL_X11::swap_buffers() {
- glXSwapBuffers(x11_display, x11_window);
+GLManager_X11::GLDisplay::~GLDisplay() {
+ if (context) {
+ //release_current();
+ glXDestroyContext(x11_display, context->glx_context);
+ memdelete(context);
+ context = nullptr;
+ }
}
static bool ctxErrorOccurred = false;
@@ -67,20 +65,35 @@ static int ctxErrorHandler(Display *dpy, XErrorEvent *ev) {
return 0;
}
-static void set_class_hint(Display *p_display, Window p_window) {
- XClassHint *classHint;
-
- /* set the name and class hints for the window manager to use */
- classHint = XAllocClassHint();
- if (classHint) {
- classHint->res_name = (char *)"Godot_Engine";
- classHint->res_class = (char *)"Godot";
+int GLManager_X11::_find_or_create_display(Display *p_x11_display) {
+ for (unsigned int n = 0; n < _displays.size(); n++) {
+ const GLDisplay &d = _displays[n];
+ if (d.x11_display == p_x11_display)
+ return n;
}
- XSetClassHint(p_display, p_window, classHint);
- XFree(classHint);
+
+ // create
+ GLDisplay d_temp;
+ d_temp.x11_display = p_x11_display;
+ _displays.push_back(d_temp);
+ int new_display_id = _displays.size() - 1;
+
+ // create context
+ GLDisplay &d = _displays[new_display_id];
+
+ d.context = memnew(GLManager_X11_Private);
+ ;
+ d.context->glx_context = 0;
+
+ //Error err = _create_context(d);
+ _create_context(d);
+ return new_display_id;
}
-Error ContextGL_X11::initialize() {
+Error GLManager_X11::_create_context(GLDisplay &gl_display) {
+ // some aliases
+ ::Display *x11_display = gl_display.x11_display;
+
//const char *extensions = glXQueryExtensionsString(x11_display, DefaultScreen(x11_display));
GLXCREATECONTEXTATTRIBSARBPROC glXCreateContextAttribsARB = (GLXCREATECONTEXTATTRIBSARBPROC)glXGetProcAddress((const GLubyte *)"glXCreateContextAttribsARB");
@@ -114,10 +127,9 @@ Error ContextGL_X11::initialize() {
GLXFBConfig fbconfig = 0;
XVisualInfo *vi = nullptr;
- XSetWindowAttributes swa;
- swa.event_mask = StructureNotifyMask;
- swa.border_pixel = 0;
- unsigned long valuemask = CWBorderPixel | CWColormap | CWEventMask;
+ gl_display.x_swa.event_mask = StructureNotifyMask;
+ gl_display.x_swa.border_pixel = 0;
+ gl_display.x_valuemask = CWBorderPixel | CWColormap | CWEventMask;
if (OS::get_singleton()->is_layered_allowed()) {
GLXFBConfig *fbc = glXChooseFBConfig(x11_display, DefaultScreen(x11_display), visual_attribs_layered, &fbcount);
@@ -141,12 +153,13 @@ Error ContextGL_X11::initialize() {
}
}
XFree(fbc);
+
ERR_FAIL_COND_V(!fbconfig, ERR_UNCONFIGURED);
- swa.background_pixmap = None;
- swa.background_pixel = 0;
- swa.border_pixmap = None;
- valuemask |= CWBackPixel;
+ gl_display.x_swa.background_pixmap = None;
+ gl_display.x_swa.background_pixel = 0;
+ gl_display.x_swa.border_pixmap = None;
+ gl_display.x_valuemask |= CWBackPixel;
} else {
GLXFBConfig *fbc = glXChooseFBConfig(x11_display, DefaultScreen(x11_display), visual_attribs, &fbcount);
@@ -162,52 +175,167 @@ Error ContextGL_X11::initialize() {
switch (context_type) {
case GLES_2_0_COMPATIBLE: {
- p->glx_context = glXCreateNewContext(x11_display, fbconfig, GLX_RGBA_TYPE, 0, true);
- ERR_FAIL_COND_V(!p->glx_context, ERR_UNCONFIGURED);
+ gl_display.context->glx_context = glXCreateNewContext(gl_display.x11_display, fbconfig, GLX_RGBA_TYPE, 0, true);
+ ERR_FAIL_COND_V(!gl_display.context->glx_context, ERR_UNCONFIGURED);
} break;
}
- swa.colormap = XCreateColormap(x11_display, RootWindow(x11_display, vi->screen), vi->visual, AllocNone);
- x11_window = XCreateWindow(x11_display, RootWindow(x11_display, vi->screen), 0, 0, OS::get_singleton()->get_video_mode().width, OS::get_singleton()->get_video_mode().height, 0, vi->depth, InputOutput, vi->visual, valuemask, &swa);
- XStoreName(x11_display, x11_window, "Godot Engine");
-
- ERR_FAIL_COND_V(!x11_window, ERR_UNCONFIGURED);
- set_class_hint(x11_display, x11_window);
- XMapWindow(x11_display, x11_window);
+ gl_display.x_swa.colormap = XCreateColormap(x11_display, RootWindow(x11_display, vi->screen), vi->visual, AllocNone);
XSync(x11_display, False);
XSetErrorHandler(oldHandler);
- glXMakeCurrent(x11_display, x11_window, p->glx_context);
+ // make our own copy of the vi data
+ // for later creating windows using this display
+ if (vi) {
+ gl_display.x_vi = *vi;
+ }
XFree(vi);
return OK;
}
-int ContextGL_X11::get_window_width() {
- XWindowAttributes xwa;
- XGetWindowAttributes(x11_display, x11_window, &xwa);
+Error GLManager_X11::window_create(DisplayServer::WindowID p_window_id, ::Window p_window, Display *p_display, int p_width, int p_height) {
+ print_line("window_create window id " + itos(p_window_id));
+
+ // make sure vector is big enough...
+ // we can mirror the external vector, it is simpler
+ // to keep the IDs identical for fast lookup
+ if (p_window_id >= (int)_windows.size()) {
+ _windows.resize(p_window_id + 1);
+ }
+
+ GLWindow &win = _windows[p_window_id];
+ win.in_use = true;
+ win.window_id = p_window_id;
+ win.width = p_width;
+ win.height = p_height;
+ win.x11_window = p_window;
+ win.gldisplay_id = _find_or_create_display(p_display);
+
+ // the display could be invalid .. check NYI
+ GLDisplay &gl_display = _displays[win.gldisplay_id];
+ //const XVisualInfo &vi = gl_display.x_vi;
+ //XSetWindowAttributes &swa = gl_display.x_swa;
+ ::Display *x11_display = gl_display.x11_display;
+ ::Window &x11_window = win.x11_window;
+
+ if (!glXMakeCurrent(x11_display, x11_window, gl_display.context->glx_context)) {
+ ERR_PRINT("glXMakeCurrent failed");
+ }
+
+ _internal_set_current_window(&win);
+
+ return OK;
+}
+
+void GLManager_X11::_internal_set_current_window(GLWindow *p_win) {
+ _current_window = p_win;
+
+ // quick access to x info
+ _x_windisp.x11_window = _current_window->x11_window;
+ const GLDisplay &disp = get_current_display();
+ _x_windisp.x11_display = disp.x11_display;
+}
+
+void GLManager_X11::window_resize(DisplayServer::WindowID p_window_id, int p_width, int p_height) {
+ get_window(p_window_id).width = p_width;
+ get_window(p_window_id).height = p_height;
+}
+
+void GLManager_X11::window_destroy(DisplayServer::WindowID p_window_id) {
+ GLWindow &win = get_window(p_window_id);
+ win.in_use = false;
+
+ if (_current_window == &win) {
+ _current_window = nullptr;
+ _x_windisp.x11_display = nullptr;
+ _x_windisp.x11_window = -1;
+ }
+}
+
+void GLManager_X11::release_current() {
+ if (!_current_window)
+ return;
+ glXMakeCurrent(_x_windisp.x11_display, None, nullptr);
+}
+
+void GLManager_X11::window_make_current(DisplayServer::WindowID p_window_id) {
+ if (p_window_id == -1)
+ return;
+
+ GLWindow &win = _windows[p_window_id];
+ if (!win.in_use)
+ return;
+
+ // noop
+ if (&win == _current_window)
+ return;
+
+ const GLDisplay &disp = get_display(win.gldisplay_id);
+
+ glXMakeCurrent(disp.x11_display, win.x11_window, disp.context->glx_context);
- return xwa.width;
+ _internal_set_current_window(&win);
}
-int ContextGL_X11::get_window_height() {
- XWindowAttributes xwa;
- XGetWindowAttributes(x11_display, x11_window, &xwa);
+void GLManager_X11::make_current() {
+ if (!_current_window)
+ return;
+ if (!_current_window->in_use) {
+ WARN_PRINT("current window not in use!");
+ return;
+ }
+ const GLDisplay &disp = get_current_display();
+ glXMakeCurrent(_x_windisp.x11_display, _x_windisp.x11_window, disp.context->glx_context);
+}
- return xwa.height;
+void GLManager_X11::swap_buffers() {
+ // NO NEED TO CALL SWAP BUFFERS for each window...
+ // see https://www.khronos.org/registry/OpenGL-Refpages/gl2.1/xhtml/glXSwapBuffers.xml
+
+ if (!_current_window)
+ return;
+ if (!_current_window->in_use) {
+ WARN_PRINT("current window not in use!");
+ return;
+ }
+
+ // print_line("\tswap_buffers");
+
+ // only for debugging without drawing anything
+ // glClearColor(Math::randf(), 0, 1, 1);
+ //glClear(GL_COLOR_BUFFER_BIT);
+
+ //const GLDisplay &disp = get_current_display();
+ glXSwapBuffers(_x_windisp.x11_display, _x_windisp.x11_window);
}
-void ContextGL_X11::set_use_vsync(bool p_use) {
+Error GLManager_X11::initialize() {
+ return OK;
+}
+
+void GLManager_X11::set_use_vsync(bool p_use) {
static bool setup = false;
static PFNGLXSWAPINTERVALEXTPROC glXSwapIntervalEXT = nullptr;
static PFNGLXSWAPINTERVALSGIPROC glXSwapIntervalMESA = nullptr;
static PFNGLXSWAPINTERVALSGIPROC glXSwapIntervalSGI = nullptr;
+ // force vsync in the editor for now, as a safety measure
+ bool is_editor = Engine::get_singleton()->is_editor_hint();
+ if (is_editor) {
+ p_use = true;
+ }
+
+ // we need an active window to get a display to set the vsync
+ if (!_current_window)
+ return;
+ const GLDisplay &disp = get_current_display();
+
if (!setup) {
setup = true;
- String extensions = glXQueryExtensionsString(x11_display, DefaultScreen(x11_display));
+ String extensions = glXQueryExtensionsString(disp.x11_display, DefaultScreen(disp.x11_display));
if (extensions.find("GLX_EXT_swap_control") != -1)
glXSwapIntervalEXT = (PFNGLXSWAPINTERVALEXTPROC)glXGetProcAddressARB((const GLubyte *)"glXSwapIntervalEXT");
if (extensions.find("GLX_MESA_swap_control") != -1)
@@ -222,35 +350,28 @@ void ContextGL_X11::set_use_vsync(bool p_use) {
glXSwapIntervalSGI(val);
} else if (glXSwapIntervalEXT) {
GLXDrawable drawable = glXGetCurrentDrawable();
- glXSwapIntervalEXT(x11_display, drawable, val);
+ glXSwapIntervalEXT(disp.x11_display, drawable, val);
} else
return;
use_vsync = p_use;
}
-bool ContextGL_X11::is_using_vsync() const {
+bool GLManager_X11::is_using_vsync() const {
return use_vsync;
}
-ContextGL_X11::ContextGL_X11(::Display *p_x11_display, ::Window &p_x11_window, const OS::VideoMode &p_default_video_mode, ContextType p_context_type) :
- x11_window(p_x11_window) {
- default_video_mode = p_default_video_mode;
- x11_display = p_x11_display;
-
+GLManager_X11::GLManager_X11(const Vector2i &p_size, ContextType p_context_type) {
context_type = p_context_type;
double_buffer = false;
direct_render = false;
glx_minor = glx_major = 0;
- p = memnew(ContextGL_X11_Private);
- p->glx_context = 0;
use_vsync = false;
+ _current_window = nullptr;
}
-ContextGL_X11::~ContextGL_X11() {
+GLManager_X11::~GLManager_X11() {
release_current();
- glXDestroyContext(x11_display, p->glx_context);
- memdelete(p);
}
#endif
diff --git a/platform/linuxbsd/context_gl_x11.h b/platform/linuxbsd/gl_manager_x11.h
index d089886f4d..6781312074 100644
--- a/platform/linuxbsd/context_gl_x11.h
+++ b/platform/linuxbsd/gl_manager_x11.h
@@ -1,5 +1,5 @@
/*************************************************************************/
-/* context_gl_x11.h */
+/* gl_manager_x11.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@@ -28,53 +28,100 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef CONTEXT_GL_X11_H
-#define CONTEXT_GL_X11_H
+#pragma once
#ifdef X11_ENABLED
-#if defined(OPENGL_ENABLED)
+#include "drivers/gles_common/rasterizer_platforms.h"
+
+#if defined(GLES_X11_ENABLED)
#include "core/os/os.h"
+#include "core/templates/local_vector.h"
+#include "servers/display_server.h"
#include <X11/Xlib.h>
#include <X11/extensions/Xrender.h>
-struct ContextGL_X11_Private;
+struct GLManager_X11_Private;
-class ContextGL_X11 {
+class GLManager_X11 {
public:
enum ContextType {
GLES_2_0_COMPATIBLE,
};
private:
- ContextGL_X11_Private *p;
- OS::VideoMode default_video_mode;
- ::Display *x11_display;
- ::Window &x11_window;
+ // any data specific to the window
+ struct GLWindow {
+ GLWindow() { in_use = false; }
+ bool in_use;
+
+ // the external ID .. should match the GL window number .. unused I think
+ DisplayServer::WindowID window_id;
+ int width;
+ int height;
+ ::Window x11_window;
+ int gldisplay_id;
+ };
+
+ struct GLDisplay {
+ GLDisplay() { context = nullptr; }
+ ~GLDisplay();
+ GLManager_X11_Private *context;
+ ::Display *x11_display;
+ XVisualInfo x_vi;
+ XSetWindowAttributes x_swa;
+ unsigned long x_valuemask;
+ };
+
+ // just for convenience, window and display struct
+ struct XWinDisp {
+ ::Window x11_window;
+ ::Display *x11_display;
+ } _x_windisp;
+
+ LocalVector<GLWindow> _windows;
+ LocalVector<GLDisplay> _displays;
+
+ GLWindow *_current_window;
+
+ void _internal_set_current_window(GLWindow *p_win);
+
+ GLWindow &get_window(unsigned int id) { return _windows[id]; }
+ const GLWindow &get_window(unsigned int id) const { return _windows[id]; }
+
+ const GLDisplay &get_current_display() const { return _displays[_current_window->gldisplay_id]; }
+ const GLDisplay &get_display(unsigned int id) { return _displays[id]; }
+
bool double_buffer;
bool direct_render;
int glx_minor, glx_major;
bool use_vsync;
ContextType context_type;
+private:
+ int _find_or_create_display(Display *p_x11_display);
+ Error _create_context(GLDisplay &gl_display);
+
public:
+ Error window_create(DisplayServer::WindowID p_window_id, ::Window p_window, Display *p_display, int p_width, int p_height);
+ void window_destroy(DisplayServer::WindowID p_window_id);
+ void window_resize(DisplayServer::WindowID p_window_id, int p_width, int p_height);
+
void release_current();
void make_current();
void swap_buffers();
- int get_window_width();
- int get_window_height();
+
+ void window_make_current(DisplayServer::WindowID p_window_id);
Error initialize();
void set_use_vsync(bool p_use);
bool is_using_vsync() const;
- ContextGL_X11(::Display *p_x11_display, ::Window &p_x11_window, const OS::VideoMode &p_default_video_mode, ContextType p_context_type);
- ~ContextGL_X11();
+ GLManager_X11(const Vector2i &p_size, ContextType p_context_type);
+ ~GLManager_X11();
};
-#endif
-
-#endif
-#endif
+#endif // GLES_X11_ENABLED
+#endif // X11_ENABLED
diff --git a/platform/linuxbsd/platform_config.h b/platform/linuxbsd/platform_config.h
index 3195d08935..cdf989fee7 100644
--- a/platform/linuxbsd/platform_config.h
+++ b/platform/linuxbsd/platform_config.h
@@ -43,3 +43,6 @@
#define PTHREAD_BSD_SET_NAME
#endif
#endif
+
+#define GLES3_INCLUDE_H "thirdparty/glad/glad/glad.h"
+#define GLES2_INCLUDE_H "thirdparty/glad/glad/glad.h"
diff --git a/platform/osx/platform_config.h b/platform/osx/platform_config.h
index 2d0fd872dc..d36a72f9a1 100644
--- a/platform/osx/platform_config.h
+++ b/platform/osx/platform_config.h
@@ -30,4 +30,6 @@
#include <alloca.h>
+#define GLES3_INCLUDE_H "thirdparty/glad/glad/glad.h"
+#define GLES2_INCLUDE_H "thirdparty/glad/glad/glad.h"
#define PTHREAD_RENAME_SELF
diff --git a/platform/windows/SCsub b/platform/windows/SCsub
index 47d8e14680..76234c3065 100644
--- a/platform/windows/SCsub
+++ b/platform/windows/SCsub
@@ -15,7 +15,7 @@ common_win = [
"joypad_windows.cpp",
"windows_terminal_logger.cpp",
"vulkan_context_win.cpp",
- "context_gl_windows.cpp",
+ "gl_manager_windows.cpp",
]
res_file = "godot_res.rc"
diff --git a/platform/windows/detect.py b/platform/windows/detect.py
index 6752885f37..d8a1ff4c80 100644
--- a/platform/windows/detect.py
+++ b/platform/windows/detect.py
@@ -453,8 +453,7 @@ def configure_mingw(env):
if not env["use_volk"]:
env.Append(LIBS=["vulkan"])
- ## TODO !!! Re-enable when OpenGLES Rendering Device is implemented !!!
- # env.Append(CPPDEFINES=['OPENGL_ENABLED'])
+ env.Append(CPPDEFINES=["OPENGL_ENABLED"])
env.Append(LIBS=["opengl32"])
env.Append(CPPDEFINES=["MINGW_ENABLED", ("MINGW_HAS_SECURE_API", 1)])
diff --git a/platform/windows/display_server_windows.cpp b/platform/windows/display_server_windows.cpp
index 905c4142a8..4b09edabdb 100644
--- a/platform/windows/display_server_windows.cpp
+++ b/platform/windows/display_server_windows.cpp
@@ -38,6 +38,10 @@
#include <avrt.h>
+#if defined(GLES_WINDOWS_ENABLED)
+#include "drivers/gles2/rasterizer_gles2.h"
+#endif
+
static String format_error_message(DWORD id) {
LPWSTR messageBuffer = nullptr;
size_t size = FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
@@ -533,6 +537,11 @@ void DisplayServerWindows::delete_sub_window(WindowID p_window) {
context_vulkan->window_destroy(p_window);
}
#endif
+#ifdef GLES_WINDOWS_ENABLED
+ if (rendering_driver == "GLES2") {
+ gl_manager->window_destroy(p_window);
+ }
+#endif
if ((tablet_get_current_driver() == "wintab") && wintab_available && windows[p_window].wtctx) {
wintab_WTClose(windows[p_window].wtctx);
@@ -542,6 +551,12 @@ void DisplayServerWindows::delete_sub_window(WindowID p_window) {
windows.erase(p_window);
}
+void DisplayServerWindows::gl_window_make_current(DisplayServer::WindowID p_window_id) {
+#if defined(GLES_WINDOWS_ENABLED)
+ gl_manager->window_make_current(p_window_id);
+#endif
+}
+
void DisplayServerWindows::window_attach_instance_id(ObjectID p_instance, WindowID p_window) {
_THREAD_SAFE_METHOD_
@@ -812,6 +827,11 @@ void DisplayServerWindows::window_set_size(const Size2i p_size, WindowID p_windo
context_vulkan->window_resize(p_window, w, h);
}
#endif
+#if defined(GLES_WINDOWS_ENABLED)
+ if (rendering_driver == "GLES2") {
+ gl_manager->window_resize(p_window, w, h);
+ }
+#endif
if (wd.fullscreen) {
return;
@@ -1591,6 +1611,9 @@ void DisplayServerWindows::make_rendering_thread() {
}
void DisplayServerWindows::swap_buffers() {
+#if defined(GLES_WINDOWS_ENABLED)
+ gl_manager->swap_buffers();
+#endif
}
void DisplayServerWindows::set_native_icon(const String &p_filename) {
@@ -3086,6 +3109,14 @@ DisplayServer::WindowID DisplayServerWindows::_create_window(WindowMode p_mode,
}
#endif
+#ifdef GLES_WINDOWS_ENABLED
+ print_line("rendering_driver " + rendering_driver);
+ if (rendering_driver == "GLES2") {
+ Error err = gl_manager->window_create(id, wd.hWnd, hInstance, WindowRect.right - WindowRect.left, WindowRect.bottom - WindowRect.top);
+ ERR_FAIL_COND_V_MSG(err != OK, INVALID_WINDOW_ID, "Can't create a GLES2 window");
+ }
+#endif
+
RegisterTouchWindow(wd.hWnd, 0);
TRACKMOUSEEVENT tme;
@@ -3292,7 +3323,10 @@ DisplayServerWindows::DisplayServerWindows(const String &p_rendering_driver, Win
use_raw_input = false;
}
- rendering_driver = "vulkan";
+ // hard coded render drivers...
+ // rendering_driver = "vulkan";
+ // rendering_driver = "GLES2";
+ print_line("rendering_driver " + rendering_driver);
#if defined(VULKAN_ENABLED)
if (rendering_driver == "vulkan") {
@@ -3305,7 +3339,35 @@ DisplayServerWindows::DisplayServerWindows(const String &p_rendering_driver, Win
}
}
#endif
+ // Init context and rendering device
+#if defined(GLES_WINDOWS_ENABLED)
+ if (rendering_driver == "GLES2") {
+ GLManager_Windows::ContextType opengl_api_type = GLManager_Windows::GLES_2_0_COMPATIBLE;
+
+ gl_manager = memnew(GLManager_Windows(opengl_api_type));
+
+ if (gl_manager->initialize() != OK) {
+ memdelete(gl_manager);
+ gl_manager = nullptr;
+ r_error = ERR_UNAVAILABLE;
+ return;
+ }
+
+ // gl_manager->set_use_vsync(current_videomode.use_vsync);
+
+ if (true) {
+ RasterizerGLES2::make_current();
+ } else {
+ memdelete(gl_manager);
+ gl_manager = nullptr;
+ r_error = ERR_UNAVAILABLE;
+ return;
+ }
+ }
+#endif
+
+ /*
#if defined(OPENGL_ENABLED)
if (rendering_driver_index == VIDEO_DRIVER_GLES2) {
context_gles2 = memnew(ContextGL_Windows(hWnd, false));
@@ -3328,7 +3390,7 @@ DisplayServerWindows::DisplayServerWindows(const String &p_rendering_driver, Win
}
}
#endif
-
+ */
Point2i window_position(
(screen_get_size(0).width - p_resolution.width) / 2,
(screen_get_size(0).height - p_resolution.height) / 2);
@@ -3386,8 +3448,8 @@ Vector<String> DisplayServerWindows::get_rendering_drivers_func() {
#ifdef VULKAN_ENABLED
drivers.push_back("vulkan");
#endif
-#ifdef OPENGL_ENABLED
- drivers.push_back("opengl");
+#ifdef GLES_WINDOWS_ENABLED
+ drivers.push_back("GLES2");
#endif
return drivers;
@@ -3417,6 +3479,10 @@ DisplayServerWindows::~DisplayServerWindows() {
SetWindowLongPtr(windows[MAIN_WINDOW_ID].hWnd, GWLP_WNDPROC, (LONG_PTR)user_proc);
};
+#ifdef GLES_WINDOWS_ENABLED
+ // destroy windows .. NYI?
+#endif
+
if (windows.has(MAIN_WINDOW_ID)) {
#ifdef VULKAN_ENABLED
if (rendering_driver == "vulkan") {
@@ -3445,4 +3511,10 @@ DisplayServerWindows::~DisplayServerWindows() {
if (restore_mouse_trails > 1) {
SystemParametersInfoA(SPI_SETMOUSETRAILS, restore_mouse_trails, 0, 0);
}
+#ifdef GLES_WINDOWS_ENABLED
+ if (gl_manager) {
+ memdelete(gl_manager);
+ gl_manager = nullptr;
+ }
+#endif
}
diff --git a/platform/windows/display_server_windows.h b/platform/windows/display_server_windows.h
index 22c4f96a90..145c147208 100644
--- a/platform/windows/display_server_windows.h
+++ b/platform/windows/display_server_windows.h
@@ -51,15 +51,15 @@
#include "drivers/xaudio2/audio_driver_xaudio2.h"
#endif
-#if defined(OPENGL_ENABLED)
-#include "context_gl_windows.h"
-#endif
-
#if defined(VULKAN_ENABLED)
#include "drivers/vulkan/rendering_device_vulkan.h"
#include "platform/windows/vulkan_context_win.h"
#endif
+#if defined(GLES_WINDOWS_ENABLED)
+#include "gl_manager_windows.h"
+#endif
+
#include <fcntl.h>
#include <io.h>
#include <stdio.h>
@@ -304,8 +304,8 @@ class DisplayServerWindows : public DisplayServer {
int old_x, old_y;
Point2i center;
-#if defined(OPENGL_ENABLED)
- ContextGL_Windows *context_gles2;
+#if defined(GLES_WINDOWS_ENABLED)
+ GLManager_Windows *gl_manager;
#endif
#if defined(VULKAN_ENABLED)
@@ -477,6 +477,7 @@ public:
virtual void window_attach_instance_id(ObjectID p_instance, WindowID p_window = MAIN_WINDOW_ID) override;
virtual ObjectID window_get_attached_instance_id(WindowID p_window = MAIN_WINDOW_ID) const override;
+ virtual void gl_window_make_current(DisplayServer::WindowID p_window_id);
virtual void window_set_rect_changed_callback(const Callable &p_callable, WindowID p_window = MAIN_WINDOW_ID) override;
diff --git a/platform/windows/gl_manager_windows.cpp b/platform/windows/gl_manager_windows.cpp
new file mode 100644
index 0000000000..b6107088a2
--- /dev/null
+++ b/platform/windows/gl_manager_windows.cpp
@@ -0,0 +1,353 @@
+/*************************************************************************/
+/* gl_manager_windows.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "gl_manager_windows.h"
+
+#ifdef WINDOWS_ENABLED
+#ifdef GLES_WINDOWS_ENABLED
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <dwmapi.h>
+
+#define WGL_CONTEXT_MAJOR_VERSION_ARB 0x2091
+#define WGL_CONTEXT_MINOR_VERSION_ARB 0x2092
+#define WGL_CONTEXT_FLAGS_ARB 0x2094
+#define WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB 0x00000002
+#define WGL_CONTEXT_PROFILE_MASK_ARB 0x9126
+#define WGL_CONTEXT_CORE_PROFILE_BIT_ARB 0x00000001
+
+#define _WGL_CONTEXT_DEBUG_BIT_ARB 0x0001
+
+#if defined(__GNUC__)
+// Workaround GCC warning from -Wcast-function-type.
+#define wglGetProcAddress (void *)wglGetProcAddress
+#endif
+
+typedef HGLRC(APIENTRY *PFNWGLCREATECONTEXTATTRIBSARBPROC)(HDC, HGLRC, const int *);
+
+int GLManager_Windows::_find_or_create_display(GLWindow &win) {
+ // find display NYI, only 1 supported so far
+ if (_displays.size())
+ return 0;
+
+ // for (unsigned int n = 0; n < _displays.size(); n++) {
+ // const GLDisplay &d = _displays[n];
+ // if (d.x11_display == p_x11_display)
+ // return n;
+ // }
+
+ // create
+ GLDisplay d_temp;
+ _displays.push_back(d_temp);
+ int new_display_id = _displays.size() - 1;
+
+ // create context
+ GLDisplay &d = _displays[new_display_id];
+ Error err = _create_context(win, d);
+
+ if (err != OK) {
+ // not good
+ // delete the _display?
+ _displays.remove(new_display_id);
+ return -1;
+ }
+
+ return new_display_id;
+}
+
+Error GLManager_Windows::_create_context(GLWindow &win, GLDisplay &gl_display) {
+ static PIXELFORMATDESCRIPTOR pfd = {
+ sizeof(PIXELFORMATDESCRIPTOR), // Size Of This Pixel Format Descriptor
+ 1,
+ PFD_DRAW_TO_WINDOW | // Format Must Support Window
+ PFD_SUPPORT_OPENGL | // Format Must Support OpenGL
+ PFD_DOUBLEBUFFER,
+ (BYTE)PFD_TYPE_RGBA,
+ (BYTE)(OS::get_singleton()->is_layered_allowed() ? 32 : 24),
+ (BYTE)0, (BYTE)0, (BYTE)0, (BYTE)0, (BYTE)0, (BYTE)0, // Color Bits Ignored
+ (BYTE)(OS::get_singleton()->is_layered_allowed() ? 8 : 0), // Alpha Buffer
+ (BYTE)0, // Shift Bit Ignored
+ (BYTE)0, // No Accumulation Buffer
+ (BYTE)0, (BYTE)0, (BYTE)0, (BYTE)0, // Accumulation Bits Ignored
+ (BYTE)24, // 24Bit Z-Buffer (Depth Buffer)
+ (BYTE)0, // No Stencil Buffer
+ (BYTE)0, // No Auxiliary Buffer
+ (BYTE)PFD_MAIN_PLANE, // Main Drawing Layer
+ (BYTE)0, // Reserved
+ 0, 0, 0 // Layer Masks Ignored
+ };
+
+ // alias
+ HDC hDC = win.hDC;
+
+ int pixel_format = ChoosePixelFormat(hDC, &pfd);
+ if (!pixel_format) // Did Windows Find A Matching Pixel Format?
+ {
+ return ERR_CANT_CREATE; // Return FALSE
+ }
+
+ BOOL ret = SetPixelFormat(hDC, pixel_format, &pfd);
+ if (!ret) // Are We Able To Set The Pixel Format?
+ {
+ return ERR_CANT_CREATE; // Return FALSE
+ }
+
+ gl_display.hRC = wglCreateContext(hDC);
+ if (!gl_display.hRC) // Are We Able To Get A Rendering Context?
+ {
+ return ERR_CANT_CREATE; // Return FALSE
+ }
+
+ wglMakeCurrent(hDC, gl_display.hRC);
+
+ if (opengl_3_context) {
+ int attribs[] = {
+ WGL_CONTEXT_MAJOR_VERSION_ARB, 3, //we want a 3.3 context
+ WGL_CONTEXT_MINOR_VERSION_ARB, 3,
+ //and it shall be forward compatible so that we can only use up to date functionality
+ WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_CORE_PROFILE_BIT_ARB,
+ WGL_CONTEXT_FLAGS_ARB, WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB /*| _WGL_CONTEXT_DEBUG_BIT_ARB*/,
+ 0
+ }; //zero indicates the end of the array
+
+ PFNWGLCREATECONTEXTATTRIBSARBPROC wglCreateContextAttribsARB = nullptr; //pointer to the method
+ wglCreateContextAttribsARB = (PFNWGLCREATECONTEXTATTRIBSARBPROC)wglGetProcAddress("wglCreateContextAttribsARB");
+
+ if (wglCreateContextAttribsARB == nullptr) //OpenGL 3.0 is not supported
+ {
+ wglDeleteContext(gl_display.hRC);
+ gl_display.hRC = 0;
+ return ERR_CANT_CREATE;
+ }
+
+ HGLRC new_hRC = wglCreateContextAttribsARB(hDC, 0, attribs);
+ if (!new_hRC) {
+ wglDeleteContext(gl_display.hRC);
+ gl_display.hRC = 0;
+ return ERR_CANT_CREATE; // Return false
+ }
+ wglMakeCurrent(hDC, nullptr);
+ wglDeleteContext(gl_display.hRC);
+ gl_display.hRC = new_hRC;
+
+ if (!wglMakeCurrent(hDC, gl_display.hRC)) // Try To Activate The Rendering Context
+ {
+ wglDeleteContext(gl_display.hRC);
+ gl_display.hRC = 0;
+ return ERR_CANT_CREATE; // Return FALSE
+ }
+ }
+
+ return OK;
+}
+
+Error GLManager_Windows::window_create(DisplayServer::WindowID p_window_id, HWND p_hwnd, HINSTANCE p_hinstance, int p_width, int p_height) {
+ print_line("window_create window id " + itos(p_window_id));
+
+ HDC hdc = GetDC(p_hwnd);
+ if (!hdc) {
+ return ERR_CANT_CREATE; // Return FALSE
+ }
+
+ // make sure vector is big enough...
+ // we can mirror the external vector, it is simpler
+ // to keep the IDs identical for fast lookup
+ if (p_window_id >= (int)_windows.size()) {
+ _windows.resize(p_window_id + 1);
+ }
+
+ GLWindow &win = _windows[p_window_id];
+ win.in_use = true;
+ win.window_id = p_window_id;
+ win.width = p_width;
+ win.height = p_height;
+ win.hwnd = p_hwnd;
+ win.hDC = hdc;
+
+ win.gldisplay_id = _find_or_create_display(win);
+
+ if (win.gldisplay_id == -1) {
+ // release DC?
+ _windows.remove(_windows.size() - 1);
+ return FAILED;
+ }
+
+ // the display could be invalid .. check NYI
+ GLDisplay &gl_display = _displays[win.gldisplay_id];
+
+ // make current
+ window_make_current(_windows.size() - 1);
+
+ return OK;
+}
+
+void GLManager_Windows::_internal_set_current_window(GLWindow *p_win) {
+ _current_window = p_win;
+}
+
+void GLManager_Windows::window_resize(DisplayServer::WindowID p_window_id, int p_width, int p_height) {
+ get_window(p_window_id).width = p_width;
+ get_window(p_window_id).height = p_height;
+}
+
+int GLManager_Windows::window_get_width(DisplayServer::WindowID p_window_id) {
+ return get_window(p_window_id).width;
+}
+
+int GLManager_Windows::window_get_height(DisplayServer::WindowID p_window_id) {
+ return get_window(p_window_id).height;
+}
+
+void GLManager_Windows::window_destroy(DisplayServer::WindowID p_window_id) {
+ GLWindow &win = get_window(p_window_id);
+ win.in_use = false;
+
+ if (_current_window == &win) {
+ _current_window = nullptr;
+ }
+}
+
+void GLManager_Windows::release_current() {
+ if (!_current_window)
+ return;
+
+ wglMakeCurrent(_current_window->hDC, nullptr);
+}
+
+void GLManager_Windows::window_make_current(DisplayServer::WindowID p_window_id) {
+ if (p_window_id == -1)
+ return;
+
+ GLWindow &win = _windows[p_window_id];
+ if (!win.in_use)
+ return;
+
+ // noop
+ if (&win == _current_window)
+ return;
+
+ const GLDisplay &disp = get_display(win.gldisplay_id);
+ wglMakeCurrent(win.hDC, disp.hRC);
+
+ _internal_set_current_window(&win);
+}
+
+void GLManager_Windows::make_current() {
+ if (!_current_window)
+ return;
+ if (!_current_window->in_use) {
+ WARN_PRINT("current window not in use!");
+ return;
+ }
+ const GLDisplay &disp = get_current_display();
+ wglMakeCurrent(_current_window->hDC, disp.hRC);
+}
+
+void GLManager_Windows::swap_buffers() {
+ // NO NEED TO CALL SWAP BUFFERS for each window...
+ // see https://www.khronos.org/registry/OpenGL-Refpages/gl2.1/xhtml/glXSwapBuffers.xml
+
+ if (!_current_window)
+ return;
+ if (!_current_window->in_use) {
+ WARN_PRINT("current window not in use!");
+ return;
+ }
+
+ // print_line("\tswap_buffers");
+
+ // only for debugging without drawing anything
+ // glClearColor(Math::randf(), 0, 1, 1);
+ //glClear(GL_COLOR_BUFFER_BIT);
+
+ // const GLDisplay &disp = get_current_display();
+ SwapBuffers(_current_window->hDC);
+}
+
+Error GLManager_Windows::initialize() {
+ wglSwapIntervalEXT = (PFNWGLSWAPINTERVALEXTPROC)wglGetProcAddress("wglSwapIntervalEXT");
+ wglGetSwapIntervalEXT = (PFNWGLGETSWAPINTERVALEXTPROC)wglGetProcAddress("wglGetSwapIntervalEXT");
+ //glWrapperInit(wrapper_get_proc_address);
+
+ return OK;
+}
+
+void GLManager_Windows::set_use_vsync(bool p_use) {
+ /*
+ static bool setup = false;
+ static PFNGLXSWAPINTERVALEXTPROC glXSwapIntervalEXT = nullptr;
+ static PFNGLXSWAPINTERVALSGIPROC glXSwapIntervalMESA = nullptr;
+ static PFNGLXSWAPINTERVALSGIPROC glXSwapIntervalSGI = nullptr;
+
+ if (!setup) {
+ setup = true;
+ String extensions = glXQueryExtensionsString(x11_display, DefaultScreen(x11_display));
+ if (extensions.find("GLX_EXT_swap_control") != -1)
+ glXSwapIntervalEXT = (PFNGLXSWAPINTERVALEXTPROC)glXGetProcAddressARB((const GLubyte *)"glXSwapIntervalEXT");
+ if (extensions.find("GLX_MESA_swap_control") != -1)
+ glXSwapIntervalMESA = (PFNGLXSWAPINTERVALSGIPROC)glXGetProcAddressARB((const GLubyte *)"glXSwapIntervalMESA");
+ if (extensions.find("GLX_SGI_swap_control") != -1)
+ glXSwapIntervalSGI = (PFNGLXSWAPINTERVALSGIPROC)glXGetProcAddressARB((const GLubyte *)"glXSwapIntervalSGI");
+ }
+ int val = p_use ? 1 : 0;
+ if (glXSwapIntervalMESA) {
+ glXSwapIntervalMESA(val);
+ } else if (glXSwapIntervalSGI) {
+ glXSwapIntervalSGI(val);
+ } else if (glXSwapIntervalEXT) {
+ GLXDrawable drawable = glXGetCurrentDrawable();
+ glXSwapIntervalEXT(x11_display, drawable, val);
+ } else
+ return;
+ use_vsync = p_use;
+ */
+}
+
+bool GLManager_Windows::is_using_vsync() const {
+ return use_vsync;
+}
+
+GLManager_Windows::GLManager_Windows(ContextType p_context_type) {
+ context_type = p_context_type;
+
+ direct_render = false;
+ glx_minor = glx_major = 0;
+ use_vsync = false;
+ _current_window = nullptr;
+}
+
+GLManager_Windows::~GLManager_Windows() {
+ release_current();
+}
+
+#endif // OPENGL_ENABLED
+#endif // WINDOWS
diff --git a/platform/windows/context_gl_windows.h b/platform/windows/gl_manager_windows.h
index feff1d825b..7d0aa0d624 100644
--- a/platform/windows/context_gl_windows.h
+++ b/platform/windows/gl_manager_windows.h
@@ -1,5 +1,5 @@
/*************************************************************************/
-/* context_gl_windows.h */
+/* gl_manager_windows.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@@ -28,50 +28,102 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#if defined(OPENGL_ENABLED) || defined(GLES_ENABLED)
+#pragma once
-// Author: Juan Linietsky <reduzio@gmail.com>, (C) 2008
+#ifdef WINDOWS_ENABLED
-#ifndef CONTEXT_GL_WIN_H
-#define CONTEXT_GL_WIN_H
+#ifdef GLES_WINDOWS_ENABLED
#include "core/error/error_list.h"
#include "core/os/os.h"
+#include "core/templates/local_vector.h"
+#include "servers/display_server.h"
-#define WIN32_LEAN_AND_MEAN
#include <windows.h>
typedef bool(APIENTRY *PFNWGLSWAPINTERVALEXTPROC)(int interval);
typedef int(APIENTRY *PFNWGLGETSWAPINTERVALEXTPROC)(void);
-class ContextGL_Windows {
- HDC hDC;
- HGLRC hRC;
- unsigned int pixel_format;
- HWND hWnd;
- bool opengl_3_context;
- bool use_vsync;
+class GLManager_Windows {
+public:
+ enum ContextType {
+ GLES_2_0_COMPATIBLE,
+ };
+
+private:
+ // any data specific to the window
+ struct GLWindow {
+ GLWindow() { in_use = false; }
+ bool in_use;
+
+ // the external ID .. should match the GL window number .. unused I think
+ DisplayServer::WindowID window_id;
+ int width;
+ int height;
+
+ // windows specific
+ HDC hDC;
+ HWND hwnd;
+
+ int gldisplay_id;
+ };
+
+ struct GLDisplay {
+ // windows specific
+ HGLRC hRC;
+ };
+
+ LocalVector<GLWindow> _windows;
+ LocalVector<GLDisplay> _displays;
+
+ GLWindow *_current_window;
+ bool opengl_3_context = false;
PFNWGLSWAPINTERVALEXTPROC wglSwapIntervalEXT;
PFNWGLGETSWAPINTERVALEXTPROC wglGetSwapIntervalEXT;
+ // funcs
+ void _internal_set_current_window(GLWindow *p_win);
+
+ GLWindow &get_window(unsigned int id) { return _windows[id]; }
+ const GLWindow &get_window(unsigned int id) const { return _windows[id]; }
+
+ const GLDisplay &get_current_display() const { return _displays[_current_window->gldisplay_id]; }
+ const GLDisplay &get_display(unsigned int id) { return _displays[id]; }
+
+ bool direct_render;
+ int glx_minor, glx_major;
+ bool use_vsync;
+ ContextType context_type;
+
+private:
+ int _find_or_create_display(GLWindow &win);
+ Error _create_context(GLWindow &win, GLDisplay &gl_display);
+
public:
- void release_current();
+ Error window_create(DisplayServer::WindowID p_window_id, HWND p_hwnd, HINSTANCE p_hinstance, int p_width, int p_height);
+ void window_destroy(DisplayServer::WindowID p_window_id);
+ void window_resize(DisplayServer::WindowID p_window_id, int p_width, int p_height);
- void make_current();
+ // get directly from the cached GLWindow
+ int window_get_width(DisplayServer::WindowID p_window_id = 0);
+ int window_get_height(DisplayServer::WindowID p_window_id = 0);
- int get_window_width();
- int get_window_height();
+ void release_current();
+ void make_current();
void swap_buffers();
+ void window_make_current(DisplayServer::WindowID p_window_id);
+
Error initialize();
void set_use_vsync(bool p_use);
bool is_using_vsync() const;
- ContextGL_Windows(HWND hwnd, bool p_opengl_3_context);
- ~ContextGL_Windows();
+ GLManager_Windows(ContextType p_context_type);
+ ~GLManager_Windows();
};
-#endif
-#endif
+#endif // OPENGL_ENABLED
+
+#endif // WINDOWS
diff --git a/platform/windows/os_windows.h b/platform/windows/os_windows.h
index b8eca956c8..1342d95575 100644
--- a/platform/windows/os_windows.h
+++ b/platform/windows/os_windows.h
@@ -46,10 +46,6 @@
#include "drivers/xaudio2/audio_driver_xaudio2.h"
#endif
-#if defined(OPENGL_ENABLED)
-#include "context_gl_windows.h"
-#endif
-
#if defined(VULKAN_ENABLED)
#include "drivers/vulkan/rendering_device_vulkan.h"
#include "platform/windows/vulkan_context_win.h"
diff --git a/platform/windows/platform_config.h b/platform/windows/platform_config.h
index 481f583f6f..87e1743d49 100644
--- a/platform/windows/platform_config.h
+++ b/platform/windows/platform_config.h
@@ -29,3 +29,6 @@
/*************************************************************************/
#include <malloc.h>
+
+#define GLES3_INCLUDE_H "thirdparty/glad/glad/glad.h"
+#define GLES2_INCLUDE_H "thirdparty/glad/glad/glad.h"