diff options
Diffstat (limited to 'platform/windows/export')
-rw-r--r-- | platform/windows/export/export.cpp | 59 | ||||
-rw-r--r-- | platform/windows/export/export.h | 58 | ||||
-rw-r--r-- | platform/windows/export/export_plugin.cpp | 519 | ||||
-rw-r--r-- | platform/windows/export/export_plugin.h | 97 |
4 files changed, 606 insertions, 127 deletions
diff --git a/platform/windows/export/export.cpp b/platform/windows/export/export.cpp index 8f91756c02..4112bb84b5 100644 --- a/platform/windows/export/export.cpp +++ b/platform/windows/export/export.cpp @@ -1,32 +1,32 @@ -/*************************************************************************/ -/* export.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 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. */ -/*************************************************************************/ +/**************************************************************************/ +/* export.cpp */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* 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 "export.h" @@ -51,7 +51,6 @@ void register_windows_exporter() { Ref<EditorExportPlatformWindows> platform; platform.instantiate(); - platform->set_logo(ImageTexture::create_from_image(memnew(Image(_windows_logo)))); platform->set_name("Windows Desktop"); platform->set_os_name("Windows"); diff --git a/platform/windows/export/export.h b/platform/windows/export/export.h index 1054e04b1e..f5bf83bb48 100644 --- a/platform/windows/export/export.h +++ b/platform/windows/export/export.h @@ -1,32 +1,32 @@ -/*************************************************************************/ -/* export.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 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. */ -/*************************************************************************/ +/**************************************************************************/ +/* export.h */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* 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. */ +/**************************************************************************/ #ifndef WINDOWS_EXPORT_H #define WINDOWS_EXPORT_H diff --git a/platform/windows/export/export_plugin.cpp b/platform/windows/export/export_plugin.cpp index d15380ac7a..4107a8a17e 100644 --- a/platform/windows/export/export_plugin.cpp +++ b/platform/windows/export/export_plugin.cpp @@ -1,37 +1,160 @@ -/*************************************************************************/ -/* export_plugin.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 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. */ -/*************************************************************************/ +/**************************************************************************/ +/* export_plugin.cpp */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* 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 "export_plugin.h" #include "core/config/project_settings.h" +#include "core/io/image_loader.h" #include "editor/editor_node.h" +#include "editor/editor_paths.h" +#include "editor/editor_scale.h" +#include "platform/windows/logo_svg.gen.h" +#include "platform/windows/run_icon_svg.gen.h" + +#include "modules/modules_enabled.gen.h" // For svg. +#ifdef MODULE_SVG_ENABLED +#include "modules/svg/image_loader_svg.h" +#endif + +Error EditorExportPlatformWindows::_process_icon(const Ref<EditorExportPreset> &p_preset, const String &p_src_path, const String &p_dst_path) { + static const uint8_t icon_size[] = { 16, 32, 48, 64, 128, 0 /*256*/ }; + + struct IconData { + Vector<uint8_t> data; + uint8_t pal_colors = 0; + uint16_t planes = 0; + uint16_t bpp = 32; + }; + + HashMap<uint8_t, IconData> images; + Error err; + + if (p_src_path.get_extension() == "ico") { + Ref<FileAccess> f = FileAccess::open(p_src_path, FileAccess::READ, &err); + if (err != OK) { + return err; + } + + // Read ICONDIR. + f->get_16(); // Reserved. + uint16_t icon_type = f->get_16(); // Image type: 1 - ICO. + uint16_t icon_count = f->get_16(); // Number of images. + ERR_FAIL_COND_V(icon_type != 1, ERR_CANT_OPEN); + + for (uint16_t i = 0; i < icon_count; i++) { + // Read ICONDIRENTRY. + uint16_t w = f->get_8(); // Width in pixels. + uint16_t h = f->get_8(); // Height in pixels. + uint8_t pal_colors = f->get_8(); // Number of colors in the palette (0 - no palette). + f->get_8(); // Reserved. + uint16_t planes = f->get_16(); // Number of color planes. + uint16_t bpp = f->get_16(); // Bits per pixel. + uint32_t img_size = f->get_32(); // Image data size in bytes. + uint32_t img_offset = f->get_32(); // Image data offset. + if (w != h) { + continue; + } + + // Read image data. + uint64_t prev_offset = f->get_position(); + images[w].pal_colors = pal_colors; + images[w].planes = planes; + images[w].bpp = bpp; + images[w].data.resize(img_size); + f->seek(img_offset); + f->get_buffer(images[w].data.ptrw(), img_size); + f->seek(prev_offset); + } + } else { + Ref<Image> src_image; + src_image.instantiate(); + err = ImageLoader::load_image(p_src_path, src_image); + ERR_FAIL_COND_V(err != OK || src_image->is_empty(), ERR_CANT_OPEN); + for (size_t i = 0; i < sizeof(icon_size) / sizeof(icon_size[0]); ++i) { + int size = (icon_size[i] == 0) ? 256 : icon_size[i]; + + Ref<Image> res_image = src_image->duplicate(); + ERR_FAIL_COND_V(res_image.is_null() || res_image->is_empty(), ERR_CANT_OPEN); + res_image->resize(size, size, (Image::Interpolation)(p_preset->get("application/icon_interpolation").operator int())); + images[icon_size[i]].data = res_image->save_png_to_buffer(); + } + } + + uint16_t valid_icon_count = 0; + for (size_t i = 0; i < sizeof(icon_size) / sizeof(icon_size[0]); ++i) { + if (images.has(icon_size[i])) { + valid_icon_count++; + } else { + int size = (icon_size[i] == 0) ? 256 : icon_size[i]; + add_message(EXPORT_MESSAGE_WARNING, TTR("Resources Modification"), vformat(TTR("Icon size \"%d\" is missing."), size)); + } + } + ERR_FAIL_COND_V(valid_icon_count == 0, ERR_CANT_OPEN); + + Ref<FileAccess> fw = FileAccess::open(p_dst_path, FileAccess::WRITE, &err); + if (err != OK) { + return err; + } + + // Write ICONDIR. + fw->store_16(0); // Reserved. + fw->store_16(1); // Image type: 1 - ICO. + fw->store_16(valid_icon_count); // Number of images. + + // Write ICONDIRENTRY. + uint32_t img_offset = 6 + 16 * valid_icon_count; + for (size_t i = 0; i < sizeof(icon_size) / sizeof(icon_size[0]); ++i) { + if (images.has(icon_size[i])) { + const IconData &di = images[icon_size[i]]; + fw->store_8(icon_size[i]); // Width in pixels. + fw->store_8(icon_size[i]); // Height in pixels. + fw->store_8(di.pal_colors); // Number of colors in the palette (0 - no palette). + fw->store_8(0); // Reserved. + fw->store_16(di.planes); // Number of color planes. + fw->store_16(di.bpp); // Bits per pixel. + fw->store_32(di.data.size()); // Image data size in bytes. + fw->store_32(img_offset); // Image data offset. + + img_offset += di.data.size(); + } + } + + // Write image data. + for (size_t i = 0; i < sizeof(icon_size) / sizeof(icon_size[0]); ++i) { + if (images.has(icon_size[i])) { + const IconData &di = images[icon_size[i]]; + fw->store_buffer(di.data.ptr(), di.data.size()); + } + } + return OK; +} Error EditorExportPlatformWindows::sign_shared_object(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path) { if (p_preset->get("codesign/enable")) { @@ -53,9 +176,39 @@ Error EditorExportPlatformWindows::modify_template(const Ref<EditorExportPreset> } Error EditorExportPlatformWindows::export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags) { - String pck_path = p_path; - if (p_preset->get("binary_format/embed_pck")) { - pck_path = p_path.get_basename() + ".tmp"; + bool export_as_zip = p_path.ends_with("zip"); + bool embedded = p_preset->get("binary_format/embed_pck"); + + String pkg_name; + if (String(ProjectSettings::get_singleton()->get("application/config/name")) != "") { + pkg_name = String(ProjectSettings::get_singleton()->get("application/config/name")); + } else { + pkg_name = "Unnamed"; + } + + pkg_name = OS::get_singleton()->get_safe_dir_name(pkg_name); + + // Setup temp folder. + String path = p_path; + String tmp_dir_path = EditorPaths::get_singleton()->get_cache_dir().path_join(pkg_name); + Ref<DirAccess> tmp_app_dir = DirAccess::create_for_path(tmp_dir_path); + if (export_as_zip) { + if (tmp_app_dir.is_null()) { + return ERR_CANT_CREATE; + } + if (DirAccess::exists(tmp_dir_path)) { + if (tmp_app_dir->change_dir(tmp_dir_path) == OK) { + tmp_app_dir->erase_contents_recursive(); + } + } + tmp_app_dir->make_dir_recursive(tmp_dir_path); + path = tmp_dir_path.path_join(p_path.get_file().get_basename() + ".exe"); + } + + // Export project. + String pck_path = path; + if (embedded) { + pck_path = pck_path.get_basename() + ".tmp"; } Error err = EditorExportPlatformPC::export_project(p_preset, p_debug, pck_path, p_flags); if (p_preset->get("codesign/enable") && err == OK) { @@ -66,7 +219,7 @@ Error EditorExportPlatformWindows::export_project(const Ref<EditorExportPreset> } } - if (p_preset->get("binary_format/embed_pck") && err == OK) { + if (embedded && err == OK) { Ref<DirAccess> tmp_dir = DirAccess::create_for_path(p_path.get_base_dir()); err = tmp_dir->rename(pck_path, p_path); if (err != OK) { @@ -74,6 +227,27 @@ Error EditorExportPlatformWindows::export_project(const Ref<EditorExportPreset> } } + // ZIP project. + if (export_as_zip) { + if (FileAccess::exists(p_path)) { + OS::get_singleton()->move_to_trash(p_path); + } + + Ref<FileAccess> io_fa_dst; + zlib_filefunc_def io_dst = zipio_create_io(&io_fa_dst); + zipFile zip = zipOpen2(p_path.utf8().get_data(), APPEND_STATUS_CREATE, nullptr, &io_dst); + + zip_folder_recursive(zip, tmp_dir_path, "", pkg_name); + + zipClose(zip, nullptr); + + if (tmp_app_dir->change_dir(tmp_dir_path) == OK) { + tmp_app_dir->erase_contents_recursive(); + tmp_app_dir->change_dir(".."); + tmp_app_dir->remove(pkg_name); + } + } + return err; } @@ -84,6 +258,7 @@ String EditorExportPlatformWindows::get_template_file_name(const String &p_targe List<String> EditorExportPlatformWindows::get_binary_extensions(const Ref<EditorExportPreset> &p_preset) const { List<String> list; list.push_back("exe"); + list.push_back("zip"); return list; } @@ -97,6 +272,7 @@ bool EditorExportPlatformWindows::get_export_option_visibility(const EditorExpor void EditorExportPlatformWindows::get_export_options(List<ExportOption> *r_options) { EditorExportPlatformPC::get_export_options(r_options); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "binary_format/architecture", PROPERTY_HINT_ENUM, "x86_64,x86_32,arm64"), "x86_64")); r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/enable"), false)); @@ -110,7 +286,9 @@ void EditorExportPlatformWindows::get_export_options(List<ExportOption> *r_optio r_options->push_back(ExportOption(PropertyInfo(Variant::PACKED_STRING_ARRAY, "codesign/custom_options"), PackedStringArray())); r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "application/modify_resources"), true)); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/icon", PROPERTY_HINT_FILE, "*.ico"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/icon", PROPERTY_HINT_FILE, "*.ico,*.png,*.webp,*.svg"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/console_wrapper_icon", PROPERTY_HINT_FILE, "*.ico,*.png,*.webp,*.svg"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "application/icon_interpolation", PROPERTY_HINT_ENUM, "Nearest neighbor,Bilinear,Cubic,Trilinear,Lanczos"), 4)); r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/file_version", PROPERTY_HINT_PLACEHOLDER_TEXT, "1.0.0.0"), "")); r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/product_version", PROPERTY_HINT_PLACEHOLDER_TEXT, "1.0.0.0"), "")); r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/company_name", PROPERTY_HINT_PLACEHOLDER_TEXT, "Company Name"), "")); @@ -118,9 +296,32 @@ void EditorExportPlatformWindows::get_export_options(List<ExportOption> *r_optio r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/file_description"), "")); r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/copyright"), "")); r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/trademarks"), "")); + + String run_script = "Expand-Archive -LiteralPath '{temp_dir}\\{archive_name}' -DestinationPath '{temp_dir}'\n" + "$action = New-ScheduledTaskAction -Execute '{temp_dir}\\{exe_name}' -Argument '{cmd_args}'\n" + "$trigger = New-ScheduledTaskTrigger -Once -At 00:00\n" + "$settings = New-ScheduledTaskSettingsSet\n" + "$task = New-ScheduledTask -Action $action -Trigger $trigger -Settings $settings\n" + "Register-ScheduledTask godot_remote_debug -InputObject $task -Force:$true\n" + "Start-ScheduledTask -TaskName godot_remote_debug\n" + "while (Get-ScheduledTask -TaskName godot_remote_debug | ? State -eq running) { Start-Sleep -Milliseconds 100 }\n" + "Unregister-ScheduledTask -TaskName godot_remote_debug -Confirm:$false -ErrorAction:SilentlyContinue"; + + String cleanup_script = "Stop-ScheduledTask -TaskName godot_remote_debug -ErrorAction:SilentlyContinue\n" + "Unregister-ScheduledTask -TaskName godot_remote_debug -Confirm:$false -ErrorAction:SilentlyContinue\n" + "Remove-Item -Recurse -Force '{temp_dir}'"; + + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "ssh_remote_deploy/enabled"), false)); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "ssh_remote_deploy/host"), "user@host_ip")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "ssh_remote_deploy/port"), "22")); + + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "ssh_remote_deploy/extra_args_ssh", PROPERTY_HINT_MULTILINE_TEXT), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "ssh_remote_deploy/extra_args_scp", PROPERTY_HINT_MULTILINE_TEXT), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "ssh_remote_deploy/run_script", PROPERTY_HINT_MULTILINE_TEXT), run_script)); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "ssh_remote_deploy/cleanup_script", PROPERTY_HINT_MULTILINE_TEXT), cleanup_script)); } -Error EditorExportPlatformWindows::_rcedit_add_data(const Ref<EditorExportPreset> &p_preset, const String &p_path, bool p_set_icon) { +Error EditorExportPlatformWindows::_rcedit_add_data(const Ref<EditorExportPreset> &p_preset, const String &p_path, bool p_console_icon) { String rcedit_path = EDITOR_GET("export/windows/rcedit"); if (rcedit_path != String() && !FileAccess::exists(rcedit_path)) { @@ -147,6 +348,21 @@ Error EditorExportPlatformWindows::_rcedit_add_data(const Ref<EditorExportPreset #endif String icon_path = ProjectSettings::get_singleton()->globalize_path(p_preset->get("application/icon")); + if (p_console_icon) { + String console_icon_path = ProjectSettings::get_singleton()->globalize_path(p_preset->get("application/console_wrapper_icon")); + if (!console_icon_path.is_empty() && FileAccess::exists(console_icon_path)) { + icon_path = console_icon_path; + } + } + + String tmp_icon_path = EditorPaths::get_singleton()->get_cache_dir().path_join("_rcedit.ico"); + if (!icon_path.is_empty()) { + if (_process_icon(p_preset, icon_path, tmp_icon_path) != OK) { + add_message(EXPORT_MESSAGE_WARNING, TTR("Resources Modification"), vformat(TTR("Invalid icon file \"%s\"."), icon_path)); + icon_path = String(); + } + } + String file_verion = p_preset->get("application/file_version"); String product_version = p_preset->get("application/product_version"); String company_name = p_preset->get("application/company_name"); @@ -158,9 +374,9 @@ Error EditorExportPlatformWindows::_rcedit_add_data(const Ref<EditorExportPreset List<String> args; args.push_back(p_path); - if (!icon_path.is_empty() && p_set_icon) { + if (!icon_path.is_empty()) { args.push_back("--set-icon"); - args.push_back(icon_path); + args.push_back(tmp_icon_path); } if (!file_verion.is_empty()) { args.push_back("--set-file-version"); @@ -204,6 +420,11 @@ Error EditorExportPlatformWindows::_rcedit_add_data(const Ref<EditorExportPreset String str; Error err = OS::get_singleton()->execute(rcedit_path, args, &str, nullptr, true); + + if (FileAccess::exists(tmp_icon_path)) { + DirAccess::remove_file_or_error(tmp_icon_path); + } + if (err != OK || (str.find("not found") != -1) || (str.find("not recognized") != -1)) { add_message(EXPORT_MESSAGE_WARNING, TTR("Resources Modification"), TTR("Could not start rcedit executable. Configure rcedit path in the Editor Settings (Export > Windows > rcedit), or disable \"Application > Modify Resources\" in the export preset.")); return err; @@ -522,3 +743,227 @@ Error EditorExportPlatformWindows::fixup_embedded_pck(const String &p_path, int6 } return OK; } + +Ref<Texture2D> EditorExportPlatformWindows::get_run_icon() const { + return run_icon; +} + +bool EditorExportPlatformWindows::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() && preset->get("ssh_remote_deploy/enabled").operator bool()); + if (ssh_pid != 0 || !cleanup_commands.is_empty()) { + if (menu_options == 0) { + cleanup(); + } else { + menu_options += 1; + } + } + return menu_options != prev; +} + +Ref<ImageTexture> EditorExportPlatformWindows::get_option_icon(int p_index) const { + return p_index == 1 ? stop_icon : EditorExportPlatform::get_option_icon(p_index); +} + +int EditorExportPlatformWindows::get_options_count() const { + return menu_options; +} + +String EditorExportPlatformWindows::get_option_label(int p_index) const { + return (p_index) ? TTR("Stop and uninstall") : TTR("Run on remote Windows system"); +} + +String EditorExportPlatformWindows::get_option_tooltip(int p_index) const { + return (p_index) ? TTR("Stop and uninstall running project from the remote system") : TTR("Run exported project on remote Windows system"); +} + +void EditorExportPlatformWindows::cleanup() { + if (ssh_pid != 0 && OS::get_singleton()->is_process_running(ssh_pid)) { + print_line("Terminating connection..."); + OS::get_singleton()->kill(ssh_pid); + OS::get_singleton()->delay_usec(1000); + } + + if (!cleanup_commands.is_empty()) { + print_line("Stopping and deleting previous version..."); + for (const SSHCleanupCommand &cmd : cleanup_commands) { + if (cmd.wait) { + ssh_run_on_remote(cmd.host, cmd.port, cmd.ssh_args, cmd.cmd_args); + } else { + ssh_run_on_remote_no_wait(cmd.host, cmd.port, cmd.ssh_args, cmd.cmd_args); + } + } + } + ssh_pid = 0; + cleanup_commands.clear(); +} + +Error EditorExportPlatformWindows::run(const Ref<EditorExportPreset> &p_preset, int p_device, int p_debug_flags) { + cleanup(); + if (p_device) { // Stop command, cleanup only. + return OK; + } + + EditorProgress ep("run", TTR("Running..."), 5); + + const String dest = EditorPaths::get_singleton()->get_cache_dir().path_join("windows"); + Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); + if (!da->dir_exists(dest)) { + Error err = da->make_dir_recursive(dest); + if (err != OK) { + EditorNode::get_singleton()->show_warning(TTR("Could not create temp directory:") + "\n" + dest); + return err; + } + } + + String host = p_preset->get("ssh_remote_deploy/host").operator String(); + String port = p_preset->get("ssh_remote_deploy/port").operator String(); + if (port.is_empty()) { + port = "22"; + } + Vector<String> extra_args_ssh = p_preset->get("ssh_remote_deploy/extra_args_ssh").operator String().split(" ", false); + Vector<String> extra_args_scp = p_preset->get("ssh_remote_deploy/extra_args_scp").operator String().split(" ", false); + + const String basepath = dest.path_join("tmp_windows_export"); + +#define CLEANUP_AND_RETURN(m_err) \ + { \ + if (da->file_exists(basepath + ".zip")) { \ + da->remove(basepath + ".zip"); \ + } \ + if (da->file_exists(basepath + "_start.ps1")) { \ + da->remove(basepath + "_start.ps1"); \ + } \ + if (da->file_exists(basepath + "_clean.ps1")) { \ + da->remove(basepath + "_clean.ps1"); \ + } \ + return m_err; \ + } \ + ((void)0) + + if (ep.step(TTR("Exporting project..."), 1)) { + return ERR_SKIP; + } + Error err = export_project(p_preset, true, basepath + ".zip", p_debug_flags); + if (err != OK) { + DirAccess::remove_file_or_error(basepath + ".zip"); + return err; + } + + String cmd_args; + { + Vector<String> cmd_args_list; + gen_debug_flags(cmd_args_list, p_debug_flags); + for (int i = 0; i < cmd_args_list.size(); i++) { + if (i != 0) { + cmd_args += " "; + } + cmd_args += cmd_args_list[i]; + } + } + + const bool use_remote = (p_debug_flags & DEBUG_FLAG_REMOTE_DEBUG) || (p_debug_flags & DEBUG_FLAG_DUMB_CLIENT); + int dbg_port = EditorSettings::get_singleton()->get("network/debug/remote_port"); + + print_line("Creating temporary directory..."); + ep.step(TTR("Creating temporary directory..."), 2); + String temp_dir; + err = ssh_run_on_remote(host, port, extra_args_ssh, "powershell -command \\\"\\$tmp = Join-Path \\$Env:Temp \\$(New-Guid); New-Item -Type Directory -Path \\$tmp | Out-Null; Write-Output \\$tmp\\\"", &temp_dir); + if (err != OK || temp_dir.is_empty()) { + CLEANUP_AND_RETURN(err); + } + + print_line("Uploading archive..."); + ep.step(TTR("Uploading archive..."), 3); + err = ssh_push_to_remote(host, port, extra_args_scp, basepath + ".zip", temp_dir); + if (err != OK) { + CLEANUP_AND_RETURN(err); + } + + { + String run_script = p_preset->get("ssh_remote_deploy/run_script"); + run_script = run_script.replace("{temp_dir}", temp_dir); + run_script = run_script.replace("{archive_name}", basepath.get_file() + ".zip"); + run_script = run_script.replace("{exe_name}", basepath.get_file() + ".exe"); + run_script = run_script.replace("{cmd_args}", cmd_args); + + Ref<FileAccess> f = FileAccess::open(basepath + "_start.ps1", FileAccess::WRITE); + if (f.is_null()) { + CLEANUP_AND_RETURN(err); + } + + f->store_string(run_script); + } + + { + String clean_script = p_preset->get("ssh_remote_deploy/cleanup_script"); + clean_script = clean_script.replace("{temp_dir}", temp_dir); + clean_script = clean_script.replace("{archive_name}", basepath.get_file() + ".zip"); + clean_script = clean_script.replace("{exe_name}", basepath.get_file() + ".exe"); + clean_script = clean_script.replace("{cmd_args}", cmd_args); + + Ref<FileAccess> f = FileAccess::open(basepath + "_clean.ps1", FileAccess::WRITE); + if (f.is_null()) { + CLEANUP_AND_RETURN(err); + } + + f->store_string(clean_script); + } + + print_line("Uploading scripts..."); + ep.step(TTR("Uploading scripts..."), 4); + err = ssh_push_to_remote(host, port, extra_args_scp, basepath + "_start.ps1", temp_dir); + if (err != OK) { + CLEANUP_AND_RETURN(err); + } + err = ssh_push_to_remote(host, port, extra_args_scp, basepath + "_clean.ps1", temp_dir); + if (err != OK) { + CLEANUP_AND_RETURN(err); + } + + print_line("Starting project..."); + ep.step(TTR("Starting project..."), 5); + err = ssh_run_on_remote_no_wait(host, port, extra_args_ssh, vformat("powershell -file \"%s\\%s\"", temp_dir, basepath.get_file() + "_start.ps1"), &ssh_pid, (use_remote) ? dbg_port : -1); + if (err != OK) { + CLEANUP_AND_RETURN(err); + } + + cleanup_commands.clear(); + cleanup_commands.push_back(SSHCleanupCommand(host, port, extra_args_ssh, vformat("powershell -file \"%s\\%s\"", temp_dir, basepath.get_file() + "_clean.ps1"))); + + print_line("Project started."); + + CLEANUP_AND_RETURN(OK); +#undef CLEANUP_AND_RETURN +} + +EditorExportPlatformWindows::EditorExportPlatformWindows() { +#ifdef MODULE_SVG_ENABLED + Ref<Image> img = memnew(Image); + const bool upsample = !Math::is_equal_approx(Math::round(EDSCALE), EDSCALE); + + ImageLoaderSVG img_loader; + img_loader.create_image_from_string(img, _windows_logo_svg, EDSCALE, upsample, false); + set_logo(ImageTexture::create_from_image(img)); + + img_loader.create_image_from_string(img, _windows_run_icon_svg, EDSCALE, upsample, false); + run_icon = ImageTexture::create_from_image(img); +#endif + + Ref<Theme> theme = EditorNode::get_singleton()->get_editor_theme(); + if (theme.is_valid()) { + stop_icon = theme->get_icon(SNAME("Stop"), SNAME("EditorIcons")); + } else { + stop_icon.instantiate(); + } +} diff --git a/platform/windows/export/export_plugin.h b/platform/windows/export/export_plugin.h index ec3b60aa76..fa75a17a1f 100644 --- a/platform/windows/export/export_plugin.h +++ b/platform/windows/export/export_plugin.h @@ -1,32 +1,32 @@ -/*************************************************************************/ -/* export_plugin.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 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. */ -/*************************************************************************/ +/**************************************************************************/ +/* export_plugin.h */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* 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. */ +/**************************************************************************/ #ifndef WINDOWS_EXPORT_PLUGIN_H #define WINDOWS_EXPORT_PLUGIN_H @@ -35,10 +35,34 @@ #include "core/os/os.h" #include "editor/editor_settings.h" #include "editor/export/editor_export_platform_pc.h" -#include "platform/windows/logo.gen.h" class EditorExportPlatformWindows : public EditorExportPlatformPC { - Error _rcedit_add_data(const Ref<EditorExportPreset> &p_preset, const String &p_path, bool p_set_icon); + struct SSHCleanupCommand { + String host; + String port; + Vector<String> ssh_args; + String cmd_args; + bool wait = false; + + SSHCleanupCommand(){}; + SSHCleanupCommand(const String &p_host, const String &p_port, const Vector<String> &p_ssh_arg, const String &p_cmd_args, bool p_wait = false) { + host = p_host; + port = p_port; + ssh_args = p_ssh_arg; + cmd_args = p_cmd_args; + wait = p_wait; + }; + }; + + Ref<ImageTexture> run_icon; + Ref<ImageTexture> stop_icon; + + Vector<SSHCleanupCommand> cleanup_commands; + OS::ProcessID ssh_pid = 0; + int menu_options = 0; + + Error _process_icon(const Ref<EditorExportPreset> &p_preset, const String &p_src_path, const String &p_dst_path); + Error _rcedit_add_data(const Ref<EditorExportPreset> &p_preset, const String &p_path, bool p_console_icon); Error _code_sign(const Ref<EditorExportPreset> &p_preset, const String &p_path); public: @@ -52,6 +76,17 @@ public: virtual bool get_export_option_visibility(const EditorExportPreset *p_preset, const String &p_option, const HashMap<StringName, Variant> &p_options) const override; virtual String get_template_file_name(const String &p_target, const String &p_arch) const override; virtual Error fixup_embedded_pck(const String &p_path, int64_t p_embedded_start, int64_t p_embedded_size) override; + + virtual Ref<Texture2D> get_run_icon() const override; + virtual bool poll_export() override; + virtual Ref<ImageTexture> get_option_icon(int p_index) const override; + virtual int get_options_count() const override; + virtual String get_option_label(int p_index) const override; + virtual String get_option_tooltip(int p_index) const override; + virtual Error run(const Ref<EditorExportPreset> &p_preset, int p_device, int p_debug_flags) override; + virtual void cleanup() override; + + EditorExportPlatformWindows(); }; #endif // WINDOWS_EXPORT_PLUGIN_H |