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 | |
36 | void 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 | |
54 | void 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 | |
69 | String EditorExportPlatformPC::get_name() const { |
70 | return name; |
71 | } |
72 | |
73 | String EditorExportPlatformPC::get_os_name() const { |
74 | return os_name; |
75 | } |
76 | |
77 | Ref<Texture2D> EditorExportPlatformPC::get_logo() const { |
78 | return logo; |
79 | } |
80 | |
81 | bool 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 | |
112 | bool EditorExportPlatformPC::has_valid_project_configuration(const Ref<EditorExportPreset> &p_preset, String &r_error) const { |
113 | return true; |
114 | } |
115 | |
116 | Error 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 | |
130 | Error 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 | |
169 | Error 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 | |
222 | Error EditorExportPlatformPC::sign_shared_object(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path) { |
223 | return OK; |
224 | } |
225 | |
226 | void EditorExportPlatformPC::set_name(const String &p_name) { |
227 | name = p_name; |
228 | } |
229 | |
230 | void EditorExportPlatformPC::set_os_name(const String &p_name) { |
231 | os_name = p_name; |
232 | } |
233 | |
234 | void EditorExportPlatformPC::set_logo(const Ref<Texture2D> &p_logo) { |
235 | logo = p_logo; |
236 | } |
237 | |
238 | void 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 | |
244 | void EditorExportPlatformPC::resolve_platform_feature_priorities(const Ref<EditorExportPreset> &p_preset, HashSet<String> &p_features) { |
245 | } |
246 | |
247 | int EditorExportPlatformPC::get_chmod_flags() const { |
248 | return chmod_flags; |
249 | } |
250 | |
251 | void EditorExportPlatformPC::set_chmod_flags(int p_flags) { |
252 | chmod_flags = p_flags; |
253 | } |
254 | |