1/**************************************************************************/
2/* editor_export_platform_pc.cpp */
3/**************************************************************************/
4/* This file is part of: */
5/* GODOT ENGINE */
6/* https://godotengine.org */
7/**************************************************************************/
8/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
9/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
10/* */
11/* Permission is hereby granted, free of charge, to any person obtaining */
12/* a copy of this software and associated documentation files (the */
13/* "Software"), to deal in the Software without restriction, including */
14/* without limitation the rights to use, copy, modify, merge, publish, */
15/* distribute, sublicense, and/or sell copies of the Software, and to */
16/* permit persons to whom the Software is furnished to do so, subject to */
17/* the following conditions: */
18/* */
19/* The above copyright notice and this permission notice shall be */
20/* included in all copies or substantial portions of the Software. */
21/* */
22/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
23/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
24/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
25/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
26/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
27/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
28/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
29/**************************************************************************/
30
31#include "editor_export_platform_pc.h"
32
33#include "core/config/project_settings.h"
34#include "scene/resources/image_texture.h"
35
36void EditorExportPlatformPC::get_preset_features(const Ref<EditorExportPreset> &p_preset, List<String> *r_features) const {
37 if (p_preset->get("texture_format/bptc")) {
38 r_features->push_back("bptc");
39 }
40 if (p_preset->get("texture_format/s3tc")) {
41 r_features->push_back("s3tc");
42 }
43 if (p_preset->get("texture_format/etc")) {
44 r_features->push_back("etc");
45 }
46 if (p_preset->get("texture_format/etc2")) {
47 r_features->push_back("etc2");
48 }
49 // PC platforms only have one architecture per export, since
50 // we export a single executable instead of a bundle.
51 r_features->push_back(p_preset->get("binary_format/architecture"));
52}
53
54void EditorExportPlatformPC::get_export_options(List<ExportOption> *r_options) const {
55 String ext_filter = (get_os_name() == "Windows") ? "*.exe" : "";
56 r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "custom_template/debug", PROPERTY_HINT_GLOBAL_FILE, ext_filter), ""));
57 r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "custom_template/release", PROPERTY_HINT_GLOBAL_FILE, ext_filter), ""));
58
59 r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "debug/export_console_wrapper", PROPERTY_HINT_ENUM, "No,Debug Only,Debug and Release"), 1));
60
61 r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "binary_format/embed_pck"), false));
62
63 r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "texture_format/bptc"), true));
64 r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "texture_format/s3tc"), true));
65 r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "texture_format/etc"), false));
66 r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "texture_format/etc2"), false));
67}
68
69String EditorExportPlatformPC::get_name() const {
70 return name;
71}
72
73String EditorExportPlatformPC::get_os_name() const {
74 return os_name;
75}
76
77Ref<Texture2D> EditorExportPlatformPC::get_logo() const {
78 return logo;
79}
80
81bool EditorExportPlatformPC::has_valid_export_configuration(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates, bool p_debug) const {
82 String err;
83 bool valid = false;
84
85 // Look for export templates (first official, and if defined custom templates).
86 String arch = p_preset->get("binary_format/architecture");
87 bool dvalid = exists_export_template(get_template_file_name("debug", arch), &err);
88 bool rvalid = exists_export_template(get_template_file_name("release", arch), &err);
89
90 if (p_preset->get("custom_template/debug") != "") {
91 dvalid = FileAccess::exists(p_preset->get("custom_template/debug"));
92 if (!dvalid) {
93 err += TTR("Custom debug template not found.") + "\n";
94 }
95 }
96 if (p_preset->get("custom_template/release") != "") {
97 rvalid = FileAccess::exists(p_preset->get("custom_template/release"));
98 if (!rvalid) {
99 err += TTR("Custom release template not found.") + "\n";
100 }
101 }
102
103 valid = dvalid || rvalid;
104 r_missing_templates = !valid;
105
106 if (!err.is_empty()) {
107 r_error = err;
108 }
109 return valid;
110}
111
112bool EditorExportPlatformPC::has_valid_project_configuration(const Ref<EditorExportPreset> &p_preset, String &r_error) const {
113 return true;
114}
115
116Error EditorExportPlatformPC::export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags) {
117 ExportNotifier notifier(*this, p_preset, p_debug, p_path, p_flags);
118
119 Error err = prepare_template(p_preset, p_debug, p_path, p_flags);
120 if (err == OK) {
121 err = modify_template(p_preset, p_debug, p_path, p_flags);
122 }
123 if (err == OK) {
124 err = export_project_data(p_preset, p_debug, p_path, p_flags);
125 }
126
127 return err;
128}
129
130Error EditorExportPlatformPC::prepare_template(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags) {
131 if (!DirAccess::exists(p_path.get_base_dir())) {
132 add_message(EXPORT_MESSAGE_ERROR, TTR("Prepare Template"), TTR("The given export path doesn't exist."));
133 return ERR_FILE_BAD_PATH;
134 }
135
136 String custom_debug = p_preset->get("custom_template/debug");
137 String custom_release = p_preset->get("custom_template/release");
138
139 String template_path = p_debug ? custom_debug : custom_release;
140
141 template_path = template_path.strip_edges();
142
143 if (template_path.is_empty()) {
144 template_path = find_export_template(get_template_file_name(p_debug ? "debug" : "release", p_preset->get("binary_format/architecture")));
145 }
146
147 if (!template_path.is_empty() && !FileAccess::exists(template_path)) {
148 add_message(EXPORT_MESSAGE_ERROR, TTR("Prepare Template"), vformat(TTR("Template file not found: \"%s\"."), template_path));
149 return ERR_FILE_NOT_FOUND;
150 }
151
152 String wrapper_template_path = template_path.get_basename() + "_console.exe";
153 int con_wrapper_mode = p_preset->get("debug/export_console_wrapper");
154 bool copy_wrapper = (con_wrapper_mode == 1 && p_debug) || (con_wrapper_mode == 2);
155
156 Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
157 da->make_dir_recursive(p_path.get_base_dir());
158 Error err = da->copy(template_path, p_path, get_chmod_flags());
159 if (err == OK && copy_wrapper && FileAccess::exists(wrapper_template_path)) {
160 err = da->copy(wrapper_template_path, p_path.get_basename() + ".console.exe", get_chmod_flags());
161 }
162 if (err != OK) {
163 add_message(EXPORT_MESSAGE_ERROR, TTR("Prepare Template"), TTR("Failed to copy export template."));
164 }
165
166 return err;
167}
168
169Error EditorExportPlatformPC::export_project_data(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags) {
170 String pck_path;
171 if (p_preset->get("binary_format/embed_pck")) {
172 pck_path = p_path;
173 } else {
174 pck_path = p_path.get_basename() + ".pck";
175 }
176
177 Vector<SharedObject> so_files;
178
179 int64_t embedded_pos;
180 int64_t embedded_size;
181 Error err = save_pack(p_preset, p_debug, pck_path, &so_files, p_preset->get("binary_format/embed_pck"), &embedded_pos, &embedded_size);
182 if (err == OK && p_preset->get("binary_format/embed_pck")) {
183 if (embedded_size >= 0x100000000 && String(p_preset->get("binary_format/architecture")).contains("32")) {
184 add_message(EXPORT_MESSAGE_ERROR, TTR("PCK Embedding"), TTR("On 32-bit exports the embedded PCK cannot be bigger than 4 GiB."));
185 return ERR_INVALID_PARAMETER;
186 }
187
188 err = fixup_embedded_pck(p_path, embedded_pos, embedded_size);
189 }
190
191 if (err == OK && !so_files.is_empty()) {
192 // If shared object files, copy them.
193 Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
194 for (int i = 0; i < so_files.size() && err == OK; i++) {
195 String src_path = ProjectSettings::get_singleton()->globalize_path(so_files[i].path);
196 String target_path;
197 if (so_files[i].target.is_empty()) {
198 target_path = p_path.get_base_dir();
199 } else {
200 target_path = p_path.get_base_dir().path_join(so_files[i].target);
201 da->make_dir_recursive(target_path);
202 }
203 target_path = target_path.path_join(src_path.get_file());
204
205 if (da->dir_exists(src_path)) {
206 err = da->make_dir_recursive(target_path);
207 if (err == OK) {
208 err = da->copy_dir(src_path, target_path, -1, true);
209 }
210 } else {
211 err = da->copy(src_path, target_path);
212 if (err == OK) {
213 err = sign_shared_object(p_preset, p_debug, target_path);
214 }
215 }
216 }
217 }
218
219 return err;
220}
221
222Error EditorExportPlatformPC::sign_shared_object(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path) {
223 return OK;
224}
225
226void EditorExportPlatformPC::set_name(const String &p_name) {
227 name = p_name;
228}
229
230void EditorExportPlatformPC::set_os_name(const String &p_name) {
231 os_name = p_name;
232}
233
234void EditorExportPlatformPC::set_logo(const Ref<Texture2D> &) {
235 logo = p_logo;
236}
237
238void EditorExportPlatformPC::get_platform_features(List<String> *r_features) const {
239 r_features->push_back("pc"); //all pcs support "pc"
240 r_features->push_back("s3tc"); //all pcs support "s3tc" compression
241 r_features->push_back(get_os_name().to_lower()); //OS name is a feature
242}
243
244void EditorExportPlatformPC::resolve_platform_feature_priorities(const Ref<EditorExportPreset> &p_preset, HashSet<String> &p_features) {
245}
246
247int EditorExportPlatformPC::get_chmod_flags() const {
248 return chmod_flags;
249}
250
251void EditorExportPlatformPC::set_chmod_flags(int p_flags) {
252 chmod_flags = p_flags;
253}
254