editor_export.cpp 62 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023
  1. /*************************************************************************/
  2. /* editor_export.cpp */
  3. /*************************************************************************/
  4. /* This file is part of: */
  5. /* GODOT ENGINE */
  6. /* https://godotengine.org */
  7. /*************************************************************************/
  8. /* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
  9. /* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
  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. #include "editor_export.h"
  31. #include "core/config/project_settings.h"
  32. #include "core/crypto/crypto_core.h"
  33. #include "core/extension/native_extension.h"
  34. #include "core/io/config_file.h"
  35. #include "core/io/dir_access.h"
  36. #include "core/io/file_access.h"
  37. #include "core/io/file_access_encrypted.h"
  38. #include "core/io/file_access_pack.h" // PACK_HEADER_MAGIC, PACK_FORMAT_VERSION
  39. #include "core/io/resource_loader.h"
  40. #include "core/io/resource_saver.h"
  41. #include "core/io/zip_io.h"
  42. #include "core/object/script_language.h"
  43. #include "core/version.h"
  44. #include "editor/editor_file_system.h"
  45. #include "editor/plugins/script_editor_plugin.h"
  46. #include "editor_node.h"
  47. #include "editor_settings.h"
  48. #include "scene/resources/resource_format_text.h"
  49. static int _get_pad(int p_alignment, int p_n) {
  50. int rest = p_n % p_alignment;
  51. int pad = 0;
  52. if (rest > 0) {
  53. pad = p_alignment - rest;
  54. };
  55. return pad;
  56. }
  57. #define PCK_PADDING 16
  58. bool EditorExportPreset::_set(const StringName &p_name, const Variant &p_value) {
  59. if (values.has(p_name)) {
  60. values[p_name] = p_value;
  61. EditorExport::singleton->save_presets();
  62. return true;
  63. }
  64. return false;
  65. }
  66. bool EditorExportPreset::_get(const StringName &p_name, Variant &r_ret) const {
  67. if (values.has(p_name)) {
  68. r_ret = values[p_name];
  69. return true;
  70. }
  71. return false;
  72. }
  73. void EditorExportPreset::_get_property_list(List<PropertyInfo> *p_list) const {
  74. for (const PropertyInfo &E : properties) {
  75. if (platform->get_option_visibility(E.name, values)) {
  76. p_list->push_back(E);
  77. }
  78. }
  79. }
  80. Ref<EditorExportPlatform> EditorExportPreset::get_platform() const {
  81. return platform;
  82. }
  83. void EditorExportPreset::update_files_to_export() {
  84. Vector<String> to_remove;
  85. for (Set<String>::Element *E = selected_files.front(); E; E = E->next()) {
  86. if (!FileAccess::exists(E->get())) {
  87. to_remove.push_back(E->get());
  88. }
  89. }
  90. for (int i = 0; i < to_remove.size(); ++i) {
  91. selected_files.erase(to_remove[i]);
  92. }
  93. }
  94. Vector<String> EditorExportPreset::get_files_to_export() const {
  95. Vector<String> files;
  96. for (Set<String>::Element *E = selected_files.front(); E; E = E->next()) {
  97. files.push_back(E->get());
  98. }
  99. return files;
  100. }
  101. void EditorExportPreset::set_name(const String &p_name) {
  102. name = p_name;
  103. EditorExport::singleton->save_presets();
  104. }
  105. String EditorExportPreset::get_name() const {
  106. return name;
  107. }
  108. void EditorExportPreset::set_runnable(bool p_enable) {
  109. runnable = p_enable;
  110. EditorExport::singleton->save_presets();
  111. }
  112. bool EditorExportPreset::is_runnable() const {
  113. return runnable;
  114. }
  115. void EditorExportPreset::set_export_filter(ExportFilter p_filter) {
  116. export_filter = p_filter;
  117. EditorExport::singleton->save_presets();
  118. }
  119. EditorExportPreset::ExportFilter EditorExportPreset::get_export_filter() const {
  120. return export_filter;
  121. }
  122. void EditorExportPreset::set_include_filter(const String &p_include) {
  123. include_filter = p_include;
  124. EditorExport::singleton->save_presets();
  125. }
  126. String EditorExportPreset::get_include_filter() const {
  127. return include_filter;
  128. }
  129. void EditorExportPreset::set_export_path(const String &p_path) {
  130. export_path = p_path;
  131. /* NOTE(SonerSound): if there is a need to implement a PropertyHint that specifically indicates a relative path,
  132. * this should be removed. */
  133. if (export_path.is_absolute_path()) {
  134. String res_path = OS::get_singleton()->get_resource_dir();
  135. export_path = res_path.path_to_file(export_path);
  136. }
  137. EditorExport::singleton->save_presets();
  138. }
  139. String EditorExportPreset::get_export_path() const {
  140. return export_path;
  141. }
  142. void EditorExportPreset::set_exclude_filter(const String &p_exclude) {
  143. exclude_filter = p_exclude;
  144. EditorExport::singleton->save_presets();
  145. }
  146. String EditorExportPreset::get_exclude_filter() const {
  147. return exclude_filter;
  148. }
  149. void EditorExportPreset::add_export_file(const String &p_path) {
  150. selected_files.insert(p_path);
  151. EditorExport::singleton->save_presets();
  152. }
  153. void EditorExportPreset::remove_export_file(const String &p_path) {
  154. selected_files.erase(p_path);
  155. EditorExport::singleton->save_presets();
  156. }
  157. bool EditorExportPreset::has_export_file(const String &p_path) {
  158. return selected_files.has(p_path);
  159. }
  160. void EditorExportPreset::set_custom_features(const String &p_custom_features) {
  161. custom_features = p_custom_features;
  162. EditorExport::singleton->save_presets();
  163. }
  164. String EditorExportPreset::get_custom_features() const {
  165. return custom_features;
  166. }
  167. void EditorExportPreset::set_enc_in_filter(const String &p_filter) {
  168. enc_in_filters = p_filter;
  169. EditorExport::singleton->save_presets();
  170. }
  171. String EditorExportPreset::get_enc_in_filter() const {
  172. return enc_in_filters;
  173. }
  174. void EditorExportPreset::set_enc_ex_filter(const String &p_filter) {
  175. enc_ex_filters = p_filter;
  176. EditorExport::singleton->save_presets();
  177. }
  178. String EditorExportPreset::get_enc_ex_filter() const {
  179. return enc_ex_filters;
  180. }
  181. void EditorExportPreset::set_enc_pck(bool p_enabled) {
  182. enc_pck = p_enabled;
  183. EditorExport::singleton->save_presets();
  184. }
  185. bool EditorExportPreset::get_enc_pck() const {
  186. return enc_pck;
  187. }
  188. void EditorExportPreset::set_enc_directory(bool p_enabled) {
  189. enc_directory = p_enabled;
  190. EditorExport::singleton->save_presets();
  191. }
  192. bool EditorExportPreset::get_enc_directory() const {
  193. return enc_directory;
  194. }
  195. void EditorExportPreset::set_script_export_mode(int p_mode) {
  196. script_mode = p_mode;
  197. EditorExport::singleton->save_presets();
  198. }
  199. int EditorExportPreset::get_script_export_mode() const {
  200. return script_mode;
  201. }
  202. void EditorExportPreset::set_script_encryption_key(const String &p_key) {
  203. script_key = p_key;
  204. EditorExport::singleton->save_presets();
  205. }
  206. String EditorExportPreset::get_script_encryption_key() const {
  207. return script_key;
  208. }
  209. ///////////////////////////////////
  210. void EditorExportPlatform::gen_debug_flags(Vector<String> &r_flags, int p_flags) {
  211. String host = EditorSettings::get_singleton()->get("network/debug/remote_host");
  212. int remote_port = (int)EditorSettings::get_singleton()->get("network/debug/remote_port");
  213. if (p_flags & DEBUG_FLAG_REMOTE_DEBUG_LOCALHOST) {
  214. host = "localhost";
  215. }
  216. if (p_flags & DEBUG_FLAG_DUMB_CLIENT) {
  217. int port = EditorSettings::get_singleton()->get("filesystem/file_server/port");
  218. String passwd = EditorSettings::get_singleton()->get("filesystem/file_server/password");
  219. r_flags.push_back("--remote-fs");
  220. r_flags.push_back(host + ":" + itos(port));
  221. if (passwd != "") {
  222. r_flags.push_back("--remote-fs-password");
  223. r_flags.push_back(passwd);
  224. }
  225. }
  226. if (p_flags & DEBUG_FLAG_REMOTE_DEBUG) {
  227. r_flags.push_back("--remote-debug");
  228. r_flags.push_back(get_debug_protocol() + host + ":" + String::num(remote_port));
  229. List<String> breakpoints;
  230. ScriptEditor::get_singleton()->get_breakpoints(&breakpoints);
  231. if (breakpoints.size()) {
  232. r_flags.push_back("--breakpoints");
  233. String bpoints;
  234. for (const List<String>::Element *E = breakpoints.front(); E; E = E->next()) {
  235. bpoints += E->get().replace(" ", "%20");
  236. if (E->next()) {
  237. bpoints += ",";
  238. }
  239. }
  240. r_flags.push_back(bpoints);
  241. }
  242. }
  243. if (p_flags & DEBUG_FLAG_VIEW_COLLISONS) {
  244. r_flags.push_back("--debug-collisions");
  245. }
  246. if (p_flags & DEBUG_FLAG_VIEW_NAVIGATION) {
  247. r_flags.push_back("--debug-navigation");
  248. }
  249. }
  250. Error EditorExportPlatform::_save_pack_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) {
  251. ERR_FAIL_COND_V_MSG(p_total < 1, ERR_PARAMETER_RANGE_ERROR, "Must select at least one file to export.");
  252. PackData *pd = (PackData *)p_userdata;
  253. SavedData sd;
  254. sd.path_utf8 = p_path.utf8();
  255. sd.ofs = pd->f->get_position();
  256. sd.size = p_data.size();
  257. sd.encrypted = false;
  258. for (int i = 0; i < p_enc_in_filters.size(); ++i) {
  259. if (p_path.matchn(p_enc_in_filters[i]) || p_path.replace("res://", "").matchn(p_enc_in_filters[i])) {
  260. sd.encrypted = true;
  261. break;
  262. }
  263. }
  264. for (int i = 0; i < p_enc_ex_filters.size(); ++i) {
  265. if (p_path.matchn(p_enc_ex_filters[i]) || p_path.replace("res://", "").matchn(p_enc_ex_filters[i])) {
  266. sd.encrypted = false;
  267. break;
  268. }
  269. }
  270. FileAccessEncrypted *fae = nullptr;
  271. FileAccess *ftmp = pd->f;
  272. if (sd.encrypted) {
  273. fae = memnew(FileAccessEncrypted);
  274. ERR_FAIL_COND_V(!fae, ERR_SKIP);
  275. Error err = fae->open_and_parse(ftmp, p_key, FileAccessEncrypted::MODE_WRITE_AES256, false);
  276. ERR_FAIL_COND_V(err != OK, ERR_SKIP);
  277. ftmp = fae;
  278. }
  279. // Store file content.
  280. ftmp->store_buffer(p_data.ptr(), p_data.size());
  281. if (fae) {
  282. fae->release();
  283. memdelete(fae);
  284. }
  285. int pad = _get_pad(PCK_PADDING, pd->f->get_position());
  286. for (int i = 0; i < pad; i++) {
  287. pd->f->store_8(Math::rand() % 256);
  288. }
  289. // Store MD5 of original file.
  290. {
  291. unsigned char hash[16];
  292. CryptoCore::md5(p_data.ptr(), p_data.size(), hash);
  293. sd.md5.resize(16);
  294. for (int i = 0; i < 16; i++) {
  295. sd.md5.write[i] = hash[i];
  296. }
  297. }
  298. pd->file_ofs.push_back(sd);
  299. if (pd->ep->step(TTR("Storing File:") + " " + p_path, 2 + p_file * 100 / p_total, false)) {
  300. return ERR_SKIP;
  301. }
  302. return OK;
  303. }
  304. Error EditorExportPlatform::_save_zip_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) {
  305. ERR_FAIL_COND_V_MSG(p_total < 1, ERR_PARAMETER_RANGE_ERROR, "Must select at least one file to export.");
  306. String path = p_path.replace_first("res://", "");
  307. ZipData *zd = (ZipData *)p_userdata;
  308. zipFile zip = (zipFile)zd->zip;
  309. zipOpenNewFileInZip(zip,
  310. path.utf8().get_data(),
  311. nullptr,
  312. nullptr,
  313. 0,
  314. nullptr,
  315. 0,
  316. nullptr,
  317. Z_DEFLATED,
  318. Z_DEFAULT_COMPRESSION);
  319. zipWriteInFileInZip(zip, p_data.ptr(), p_data.size());
  320. zipCloseFileInZip(zip);
  321. if (zd->ep->step(TTR("Storing File:") + " " + p_path, 2 + p_file * 100 / p_total, false)) {
  322. return ERR_SKIP;
  323. }
  324. return OK;
  325. }
  326. Ref<ImageTexture> EditorExportPlatform::get_option_icon(int p_index) const {
  327. Ref<Theme> theme = EditorNode::get_singleton()->get_editor_theme();
  328. ERR_FAIL_COND_V(theme.is_null(), Ref<ImageTexture>());
  329. if (EditorNode::get_singleton()->get_main_control()->is_layout_rtl()) {
  330. return theme->get_icon("PlayBackwards", "EditorIcons");
  331. } else {
  332. return theme->get_icon("Play", "EditorIcons");
  333. }
  334. }
  335. String EditorExportPlatform::find_export_template(String template_file_name, String *err) const {
  336. String current_version = VERSION_FULL_CONFIG;
  337. String template_path = EditorSettings::get_singleton()->get_templates_dir().plus_file(current_version).plus_file(template_file_name);
  338. if (FileAccess::exists(template_path)) {
  339. return template_path;
  340. }
  341. // Not found
  342. if (err) {
  343. *err += TTR("No export template found at the expected path:") + "\n" + template_path + "\n";
  344. }
  345. return String();
  346. }
  347. bool EditorExportPlatform::exists_export_template(String template_file_name, String *err) const {
  348. return find_export_template(template_file_name, err) != "";
  349. }
  350. Ref<EditorExportPreset> EditorExportPlatform::create_preset() {
  351. Ref<EditorExportPreset> preset;
  352. preset.instantiate();
  353. preset->platform = Ref<EditorExportPlatform>(this);
  354. List<ExportOption> options;
  355. get_export_options(&options);
  356. for (const ExportOption &E : options) {
  357. preset->properties.push_back(E.option);
  358. preset->values[E.option.name] = E.default_value;
  359. }
  360. return preset;
  361. }
  362. void EditorExportPlatform::_export_find_resources(EditorFileSystemDirectory *p_dir, Set<String> &p_paths) {
  363. for (int i = 0; i < p_dir->get_subdir_count(); i++) {
  364. _export_find_resources(p_dir->get_subdir(i), p_paths);
  365. }
  366. for (int i = 0; i < p_dir->get_file_count(); i++) {
  367. if (p_dir->get_file_type(i) == "TextFile") {
  368. continue;
  369. }
  370. p_paths.insert(p_dir->get_file_path(i));
  371. }
  372. }
  373. void EditorExportPlatform::_export_find_dependencies(const String &p_path, Set<String> &p_paths) {
  374. if (p_paths.has(p_path)) {
  375. return;
  376. }
  377. p_paths.insert(p_path);
  378. EditorFileSystemDirectory *dir;
  379. int file_idx;
  380. dir = EditorFileSystem::get_singleton()->find_file(p_path, &file_idx);
  381. if (!dir) {
  382. return;
  383. }
  384. Vector<String> deps = dir->get_file_deps(file_idx);
  385. for (int i = 0; i < deps.size(); i++) {
  386. _export_find_dependencies(deps[i], p_paths);
  387. }
  388. }
  389. void EditorExportPlatform::_edit_files_with_filter(DirAccess *da, const Vector<String> &p_filters, Set<String> &r_list, bool exclude) {
  390. da->list_dir_begin();
  391. String cur_dir = da->get_current_dir().replace("\\", "/");
  392. if (!cur_dir.ends_with("/")) {
  393. cur_dir += "/";
  394. }
  395. String cur_dir_no_prefix = cur_dir.replace("res://", "");
  396. Vector<String> dirs;
  397. String f;
  398. while ((f = da->get_next()) != "") {
  399. if (da->current_is_dir()) {
  400. dirs.push_back(f);
  401. } else {
  402. String fullpath = cur_dir + f;
  403. // Test also against path without res:// so that filters like `file.txt` can work.
  404. String fullpath_no_prefix = cur_dir_no_prefix + f;
  405. for (int i = 0; i < p_filters.size(); ++i) {
  406. if (fullpath.matchn(p_filters[i]) || fullpath_no_prefix.matchn(p_filters[i])) {
  407. if (!exclude) {
  408. r_list.insert(fullpath);
  409. } else {
  410. r_list.erase(fullpath);
  411. }
  412. }
  413. }
  414. }
  415. }
  416. da->list_dir_end();
  417. for (int i = 0; i < dirs.size(); ++i) {
  418. String dir = dirs[i];
  419. if (dir.begins_with(".")) {
  420. continue;
  421. }
  422. if (EditorFileSystem::_should_skip_directory(cur_dir + dir)) {
  423. continue;
  424. }
  425. da->change_dir(dir);
  426. _edit_files_with_filter(da, p_filters, r_list, exclude);
  427. da->change_dir("..");
  428. }
  429. }
  430. void EditorExportPlatform::_edit_filter_list(Set<String> &r_list, const String &p_filter, bool exclude) {
  431. if (p_filter == "") {
  432. return;
  433. }
  434. Vector<String> split = p_filter.split(",");
  435. Vector<String> filters;
  436. for (int i = 0; i < split.size(); i++) {
  437. String f = split[i].strip_edges();
  438. if (f.is_empty()) {
  439. continue;
  440. }
  441. filters.push_back(f);
  442. }
  443. DirAccess *da = DirAccess::open("res://");
  444. ERR_FAIL_NULL(da);
  445. _edit_files_with_filter(da, filters, r_list, exclude);
  446. memdelete(da);
  447. }
  448. void EditorExportPlugin::set_export_preset(const Ref<EditorExportPreset> &p_preset) {
  449. if (p_preset.is_valid()) {
  450. export_preset = p_preset;
  451. }
  452. }
  453. Ref<EditorExportPreset> EditorExportPlugin::get_export_preset() const {
  454. return export_preset;
  455. }
  456. void EditorExportPlugin::add_file(const String &p_path, const Vector<uint8_t> &p_file, bool p_remap) {
  457. ExtraFile ef;
  458. ef.data = p_file;
  459. ef.path = p_path;
  460. ef.remap = p_remap;
  461. extra_files.push_back(ef);
  462. }
  463. void EditorExportPlugin::add_shared_object(const String &p_path, const Vector<String> &tags) {
  464. shared_objects.push_back(SharedObject(p_path, tags));
  465. }
  466. void EditorExportPlugin::add_ios_framework(const String &p_path) {
  467. ios_frameworks.push_back(p_path);
  468. }
  469. void EditorExportPlugin::add_ios_embedded_framework(const String &p_path) {
  470. ios_embedded_frameworks.push_back(p_path);
  471. }
  472. Vector<String> EditorExportPlugin::get_ios_frameworks() const {
  473. return ios_frameworks;
  474. }
  475. Vector<String> EditorExportPlugin::get_ios_embedded_frameworks() const {
  476. return ios_embedded_frameworks;
  477. }
  478. void EditorExportPlugin::add_ios_plist_content(const String &p_plist_content) {
  479. ios_plist_content += p_plist_content + "\n";
  480. }
  481. String EditorExportPlugin::get_ios_plist_content() const {
  482. return ios_plist_content;
  483. }
  484. void EditorExportPlugin::add_ios_linker_flags(const String &p_flags) {
  485. if (ios_linker_flags.length() > 0) {
  486. ios_linker_flags += ' ';
  487. }
  488. ios_linker_flags += p_flags;
  489. }
  490. String EditorExportPlugin::get_ios_linker_flags() const {
  491. return ios_linker_flags;
  492. }
  493. void EditorExportPlugin::add_ios_bundle_file(const String &p_path) {
  494. ios_bundle_files.push_back(p_path);
  495. }
  496. Vector<String> EditorExportPlugin::get_ios_bundle_files() const {
  497. return ios_bundle_files;
  498. }
  499. void EditorExportPlugin::add_ios_cpp_code(const String &p_code) {
  500. ios_cpp_code += p_code;
  501. }
  502. String EditorExportPlugin::get_ios_cpp_code() const {
  503. return ios_cpp_code;
  504. }
  505. void EditorExportPlugin::add_ios_project_static_lib(const String &p_path) {
  506. ios_project_static_libs.push_back(p_path);
  507. }
  508. Vector<String> EditorExportPlugin::get_ios_project_static_libs() const {
  509. return ios_project_static_libs;
  510. }
  511. void EditorExportPlugin::_export_file_script(const String &p_path, const String &p_type, const Vector<String> &p_features) {
  512. GDVIRTUAL_CALL(_export_file, p_path, p_type, p_features);
  513. }
  514. void EditorExportPlugin::_export_begin_script(const Vector<String> &p_features, bool p_debug, const String &p_path, int p_flags) {
  515. GDVIRTUAL_CALL(_export_begin, p_features, p_debug, p_path, p_flags);
  516. }
  517. void EditorExportPlugin::_export_end_script() {
  518. GDVIRTUAL_CALL(_export_end);
  519. }
  520. void EditorExportPlugin::_export_file(const String &p_path, const String &p_type, const Set<String> &p_features) {
  521. }
  522. void EditorExportPlugin::_export_begin(const Set<String> &p_features, bool p_debug, const String &p_path, int p_flags) {
  523. }
  524. void EditorExportPlugin::skip() {
  525. skipped = true;
  526. }
  527. void EditorExportPlugin::_bind_methods() {
  528. ClassDB::bind_method(D_METHOD("add_shared_object", "path", "tags"), &EditorExportPlugin::add_shared_object);
  529. ClassDB::bind_method(D_METHOD("add_ios_project_static_lib", "path"), &EditorExportPlugin::add_ios_project_static_lib);
  530. ClassDB::bind_method(D_METHOD("add_file", "path", "file", "remap"), &EditorExportPlugin::add_file);
  531. ClassDB::bind_method(D_METHOD("add_ios_framework", "path"), &EditorExportPlugin::add_ios_framework);
  532. ClassDB::bind_method(D_METHOD("add_ios_embedded_framework", "path"), &EditorExportPlugin::add_ios_embedded_framework);
  533. ClassDB::bind_method(D_METHOD("add_ios_plist_content", "plist_content"), &EditorExportPlugin::add_ios_plist_content);
  534. ClassDB::bind_method(D_METHOD("add_ios_linker_flags", "flags"), &EditorExportPlugin::add_ios_linker_flags);
  535. ClassDB::bind_method(D_METHOD("add_ios_bundle_file", "path"), &EditorExportPlugin::add_ios_bundle_file);
  536. ClassDB::bind_method(D_METHOD("add_ios_cpp_code", "code"), &EditorExportPlugin::add_ios_cpp_code);
  537. ClassDB::bind_method(D_METHOD("skip"), &EditorExportPlugin::skip);
  538. GDVIRTUAL_BIND(_export_file, "path", "type", "features");
  539. GDVIRTUAL_BIND(_export_begin, "features", "is_debug", "path", "flags");
  540. GDVIRTUAL_BIND(_export_end);
  541. }
  542. EditorExportPlugin::EditorExportPlugin() {
  543. skipped = false;
  544. }
  545. EditorExportPlatform::FeatureContainers EditorExportPlatform::get_feature_containers(const Ref<EditorExportPreset> &p_preset) {
  546. Ref<EditorExportPlatform> platform = p_preset->get_platform();
  547. List<String> feature_list;
  548. platform->get_platform_features(&feature_list);
  549. platform->get_preset_features(p_preset, &feature_list);
  550. FeatureContainers result;
  551. for (const String &E : feature_list) {
  552. result.features.insert(E);
  553. result.features_pv.push_back(E);
  554. }
  555. if (p_preset->get_custom_features() != String()) {
  556. Vector<String> tmp_custom_list = p_preset->get_custom_features().split(",");
  557. for (int i = 0; i < tmp_custom_list.size(); i++) {
  558. String f = tmp_custom_list[i].strip_edges();
  559. if (f != String()) {
  560. result.features.insert(f);
  561. result.features_pv.push_back(f);
  562. }
  563. }
  564. }
  565. return result;
  566. }
  567. EditorExportPlatform::ExportNotifier::ExportNotifier(EditorExportPlatform &p_platform, const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags) {
  568. FeatureContainers features = p_platform.get_feature_containers(p_preset);
  569. Vector<Ref<EditorExportPlugin>> export_plugins = EditorExport::get_singleton()->get_export_plugins();
  570. //initial export plugin callback
  571. for (int i = 0; i < export_plugins.size(); i++) {
  572. if (export_plugins[i]->get_script_instance()) { //script based
  573. export_plugins.write[i]->_export_begin_script(features.features_pv, p_debug, p_path, p_flags);
  574. } else {
  575. export_plugins.write[i]->_export_begin(features.features, p_debug, p_path, p_flags);
  576. }
  577. }
  578. }
  579. EditorExportPlatform::ExportNotifier::~ExportNotifier() {
  580. Vector<Ref<EditorExportPlugin>> export_plugins = EditorExport::get_singleton()->get_export_plugins();
  581. for (int i = 0; i < export_plugins.size(); i++) {
  582. if (export_plugins[i]->get_script_instance()) {
  583. export_plugins.write[i]->_export_end_script();
  584. }
  585. export_plugins.write[i]->_export_end();
  586. }
  587. }
  588. Error EditorExportPlatform::export_project_files(const Ref<EditorExportPreset> &p_preset, EditorExportSaveFunction p_func, void *p_udata, EditorExportSaveSharedObject p_so_func) {
  589. //figure out paths of files that will be exported
  590. Set<String> paths;
  591. Vector<String> path_remaps;
  592. if (p_preset->get_export_filter() == EditorExportPreset::EXPORT_ALL_RESOURCES) {
  593. //find stuff
  594. _export_find_resources(EditorFileSystem::get_singleton()->get_filesystem(), paths);
  595. } else if (p_preset->get_export_filter() == EditorExportPreset::EXCLUDE_SELECTED_RESOURCES) {
  596. _export_find_resources(EditorFileSystem::get_singleton()->get_filesystem(), paths);
  597. Vector<String> files = p_preset->get_files_to_export();
  598. for (int i = 0; i < files.size(); i++) {
  599. paths.erase(files[i]);
  600. }
  601. } else {
  602. bool scenes_only = p_preset->get_export_filter() == EditorExportPreset::EXPORT_SELECTED_SCENES;
  603. Vector<String> files = p_preset->get_files_to_export();
  604. for (int i = 0; i < files.size(); i++) {
  605. if (scenes_only && ResourceLoader::get_resource_type(files[i]) != "PackedScene") {
  606. continue;
  607. }
  608. _export_find_dependencies(files[i], paths);
  609. }
  610. // Add autoload resources and their dependencies
  611. List<PropertyInfo> props;
  612. ProjectSettings::get_singleton()->get_property_list(&props);
  613. for (const PropertyInfo &pi : props) {
  614. if (!pi.name.begins_with("autoload/")) {
  615. continue;
  616. }
  617. String autoload_path = ProjectSettings::get_singleton()->get(pi.name);
  618. if (autoload_path.begins_with("*")) {
  619. autoload_path = autoload_path.substr(1);
  620. }
  621. _export_find_dependencies(autoload_path, paths);
  622. }
  623. }
  624. //add native icons to non-resource include list
  625. _edit_filter_list(paths, String("*.icns"), false);
  626. _edit_filter_list(paths, String("*.ico"), false);
  627. _edit_filter_list(paths, p_preset->get_include_filter(), false);
  628. _edit_filter_list(paths, p_preset->get_exclude_filter(), true);
  629. // Ignore import files, since these are automatically added to the jar later with the resources
  630. _edit_filter_list(paths, String("*.import"), true);
  631. // Get encryption filters.
  632. bool enc_pck = p_preset->get_enc_pck();
  633. Vector<String> enc_in_filters;
  634. Vector<String> enc_ex_filters;
  635. Vector<uint8_t> key;
  636. if (enc_pck) {
  637. Vector<String> enc_in_split = p_preset->get_enc_in_filter().split(",");
  638. for (int i = 0; i < enc_in_split.size(); i++) {
  639. String f = enc_in_split[i].strip_edges();
  640. if (f.is_empty()) {
  641. continue;
  642. }
  643. enc_in_filters.push_back(f);
  644. }
  645. Vector<String> enc_ex_split = p_preset->get_enc_ex_filter().split(",");
  646. for (int i = 0; i < enc_ex_split.size(); i++) {
  647. String f = enc_ex_split[i].strip_edges();
  648. if (f.is_empty()) {
  649. continue;
  650. }
  651. enc_ex_filters.push_back(f);
  652. }
  653. // Get encryption key.
  654. String script_key = p_preset->get_script_encryption_key().to_lower();
  655. key.resize(32);
  656. if (script_key.length() == 64) {
  657. for (int i = 0; i < 32; i++) {
  658. int v = 0;
  659. if (i * 2 < script_key.length()) {
  660. char32_t ct = script_key[i * 2];
  661. if (ct >= '0' && ct <= '9') {
  662. ct = ct - '0';
  663. } else if (ct >= 'a' && ct <= 'f') {
  664. ct = 10 + ct - 'a';
  665. }
  666. v |= ct << 4;
  667. }
  668. if (i * 2 + 1 < script_key.length()) {
  669. char32_t ct = script_key[i * 2 + 1];
  670. if (ct >= '0' && ct <= '9') {
  671. ct = ct - '0';
  672. } else if (ct >= 'a' && ct <= 'f') {
  673. ct = 10 + ct - 'a';
  674. }
  675. v |= ct;
  676. }
  677. key.write[i] = v;
  678. }
  679. }
  680. }
  681. Error err = OK;
  682. Vector<Ref<EditorExportPlugin>> export_plugins = EditorExport::get_singleton()->get_export_plugins();
  683. for (int i = 0; i < export_plugins.size(); i++) {
  684. export_plugins.write[i]->set_export_preset(p_preset);
  685. if (p_so_func) {
  686. for (int j = 0; j < export_plugins[i]->shared_objects.size(); j++) {
  687. err = p_so_func(p_udata, export_plugins[i]->shared_objects[j]);
  688. if (err != OK) {
  689. return err;
  690. }
  691. }
  692. }
  693. for (int j = 0; j < export_plugins[i]->extra_files.size(); j++) {
  694. err = p_func(p_udata, export_plugins[i]->extra_files[j].path, export_plugins[i]->extra_files[j].data, 0, paths.size(), enc_in_filters, enc_ex_filters, key);
  695. if (err != OK) {
  696. return err;
  697. }
  698. }
  699. export_plugins.write[i]->_clear();
  700. }
  701. FeatureContainers feature_containers = get_feature_containers(p_preset);
  702. Set<String> &features = feature_containers.features;
  703. Vector<String> &features_pv = feature_containers.features_pv;
  704. //store everything in the export medium
  705. int idx = 0;
  706. int total = paths.size();
  707. for (Set<String>::Element *E = paths.front(); E; E = E->next()) {
  708. String path = E->get();
  709. String type = ResourceLoader::get_resource_type(path);
  710. if (FileAccess::exists(path + ".import")) {
  711. //file is imported, replace by what it imports
  712. Ref<ConfigFile> config;
  713. config.instantiate();
  714. err = config->load(path + ".import");
  715. if (err != OK) {
  716. ERR_PRINT("Could not parse: '" + path + "', not exported.");
  717. continue;
  718. }
  719. String importer_type = config->get_value("remap", "importer");
  720. if (importer_type == "keep") {
  721. //just keep file as-is
  722. Vector<uint8_t> array = FileAccess::get_file_as_array(path);
  723. err = p_func(p_udata, path, array, idx, total, enc_in_filters, enc_ex_filters, key);
  724. if (err != OK) {
  725. return err;
  726. }
  727. continue;
  728. }
  729. List<String> remaps;
  730. config->get_section_keys("remap", &remaps);
  731. Set<String> remap_features;
  732. for (const String &F : remaps) {
  733. String remap = F;
  734. String feature = remap.get_slice(".", 1);
  735. if (features.has(feature)) {
  736. remap_features.insert(feature);
  737. }
  738. }
  739. if (remap_features.size() > 1) {
  740. this->resolve_platform_feature_priorities(p_preset, remap_features);
  741. }
  742. err = OK;
  743. for (const String &F : remaps) {
  744. String remap = F;
  745. if (remap == "path") {
  746. String remapped_path = config->get_value("remap", remap);
  747. Vector<uint8_t> array = FileAccess::get_file_as_array(remapped_path);
  748. err = p_func(p_udata, remapped_path, array, idx, total, enc_in_filters, enc_ex_filters, key);
  749. } else if (remap.begins_with("path.")) {
  750. String feature = remap.get_slice(".", 1);
  751. if (remap_features.has(feature)) {
  752. String remapped_path = config->get_value("remap", remap);
  753. Vector<uint8_t> array = FileAccess::get_file_as_array(remapped_path);
  754. err = p_func(p_udata, remapped_path, array, idx, total, enc_in_filters, enc_ex_filters, key);
  755. }
  756. }
  757. }
  758. if (err != OK) {
  759. return err;
  760. }
  761. //also save the .import file
  762. Vector<uint8_t> array = FileAccess::get_file_as_array(path + ".import");
  763. err = p_func(p_udata, path + ".import", array, idx, total, enc_in_filters, enc_ex_filters, key);
  764. if (err != OK) {
  765. return err;
  766. }
  767. } else {
  768. bool do_export = true;
  769. for (int i = 0; i < export_plugins.size(); i++) {
  770. if (export_plugins[i]->get_script_instance()) { //script based
  771. export_plugins.write[i]->_export_file_script(path, type, features_pv);
  772. } else {
  773. export_plugins.write[i]->_export_file(path, type, features);
  774. }
  775. if (p_so_func) {
  776. for (int j = 0; j < export_plugins[i]->shared_objects.size(); j++) {
  777. err = p_so_func(p_udata, export_plugins[i]->shared_objects[j]);
  778. if (err != OK) {
  779. return err;
  780. }
  781. }
  782. }
  783. for (int j = 0; j < export_plugins[i]->extra_files.size(); j++) {
  784. err = p_func(p_udata, export_plugins[i]->extra_files[j].path, export_plugins[i]->extra_files[j].data, idx, total, enc_in_filters, enc_ex_filters, key);
  785. if (err != OK) {
  786. return err;
  787. }
  788. if (export_plugins[i]->extra_files[j].remap) {
  789. do_export = false; //if remap, do not
  790. path_remaps.push_back(path);
  791. path_remaps.push_back(export_plugins[i]->extra_files[j].path);
  792. }
  793. }
  794. if (export_plugins[i]->skipped) {
  795. do_export = false;
  796. }
  797. export_plugins.write[i]->_clear();
  798. if (!do_export) {
  799. break; //apologies, not exporting
  800. }
  801. }
  802. //just store it as it comes
  803. if (do_export) {
  804. Vector<uint8_t> array = FileAccess::get_file_as_array(path);
  805. err = p_func(p_udata, path, array, idx, total, enc_in_filters, enc_ex_filters, key);
  806. if (err != OK) {
  807. return err;
  808. }
  809. }
  810. }
  811. idx++;
  812. }
  813. //save config!
  814. Vector<String> custom_list;
  815. if (p_preset->get_custom_features() != String()) {
  816. Vector<String> tmp_custom_list = p_preset->get_custom_features().split(",");
  817. for (int i = 0; i < tmp_custom_list.size(); i++) {
  818. String f = tmp_custom_list[i].strip_edges();
  819. if (f != String()) {
  820. custom_list.push_back(f);
  821. }
  822. }
  823. }
  824. ProjectSettings::CustomMap custom_map;
  825. if (path_remaps.size()) {
  826. if (true) { //new remap mode, use always as it's friendlier with multiple .pck exports
  827. for (int i = 0; i < path_remaps.size(); i += 2) {
  828. String from = path_remaps[i];
  829. String to = path_remaps[i + 1];
  830. String remap_file = "[remap]\n\npath=\"" + to.c_escape() + "\"\n";
  831. CharString utf8 = remap_file.utf8();
  832. Vector<uint8_t> new_file;
  833. new_file.resize(utf8.length());
  834. for (int j = 0; j < utf8.length(); j++) {
  835. new_file.write[j] = utf8[j];
  836. }
  837. err = p_func(p_udata, from + ".remap", new_file, idx, total, enc_in_filters, enc_ex_filters, key);
  838. if (err != OK) {
  839. return err;
  840. }
  841. }
  842. } else {
  843. //old remap mode, will still work, but it's unused because it's not multiple pck export friendly
  844. custom_map["path_remap/remapped_paths"] = path_remaps;
  845. }
  846. }
  847. // Store icon and splash images directly, they need to bypass the import system and be loaded as images
  848. String icon = ProjectSettings::get_singleton()->get("application/config/icon");
  849. String splash = ProjectSettings::get_singleton()->get("application/boot_splash/image");
  850. if (icon != String() && FileAccess::exists(icon)) {
  851. Vector<uint8_t> array = FileAccess::get_file_as_array(icon);
  852. err = p_func(p_udata, icon, array, idx, total, enc_in_filters, enc_ex_filters, key);
  853. if (err != OK) {
  854. return err;
  855. }
  856. }
  857. if (splash != String() && FileAccess::exists(splash) && icon != splash) {
  858. Vector<uint8_t> array = FileAccess::get_file_as_array(splash);
  859. err = p_func(p_udata, splash, array, idx, total, enc_in_filters, enc_ex_filters, key);
  860. if (err != OK) {
  861. return err;
  862. }
  863. }
  864. String resource_cache_file = ResourceUID::get_cache_file();
  865. if (FileAccess::exists(resource_cache_file)) {
  866. Vector<uint8_t> array = FileAccess::get_file_as_array(resource_cache_file);
  867. err = p_func(p_udata, resource_cache_file, array, idx, total, enc_in_filters, enc_ex_filters, key);
  868. if (err != OK) {
  869. return err;
  870. }
  871. }
  872. String extension_list_config_file = NativeExtension::get_extension_list_config_file();
  873. if (FileAccess::exists(extension_list_config_file)) {
  874. Vector<uint8_t> array = FileAccess::get_file_as_array(extension_list_config_file);
  875. err = p_func(p_udata, extension_list_config_file, array, idx, total, enc_in_filters, enc_ex_filters, key);
  876. if (err != OK) {
  877. return err;
  878. }
  879. }
  880. // Store text server data if it is supported.
  881. if (TS->has_feature(TextServer::FEATURE_USE_SUPPORT_DATA)) {
  882. bool use_data = ProjectSettings::get_singleton()->get("internationalization/locale/include_text_server_data");
  883. if (use_data) {
  884. // Try using user provided data file.
  885. String ts_data = "res://" + TS->get_support_data_filename();
  886. if (FileAccess::exists(ts_data)) {
  887. Vector<uint8_t> array = FileAccess::get_file_as_array(ts_data);
  888. err = p_func(p_udata, ts_data, array, idx, total, enc_in_filters, enc_ex_filters, key);
  889. if (err != OK) {
  890. return err;
  891. }
  892. } else {
  893. // Use default text server data.
  894. String icu_data_file = EditorPaths::get_singleton()->get_cache_dir().plus_file("tmp_icu_data");
  895. TS->save_support_data(icu_data_file);
  896. Vector<uint8_t> array = FileAccess::get_file_as_array(icu_data_file);
  897. err = p_func(p_udata, ts_data, array, idx, total, enc_in_filters, enc_ex_filters, key);
  898. DirAccess::remove_file_or_error(icu_data_file);
  899. if (err != OK) {
  900. return err;
  901. }
  902. }
  903. }
  904. }
  905. String config_file = "project.binary";
  906. String engine_cfb = EditorPaths::get_singleton()->get_cache_dir().plus_file("tmp" + config_file);
  907. ProjectSettings::get_singleton()->save_custom(engine_cfb, custom_map, custom_list);
  908. Vector<uint8_t> data = FileAccess::get_file_as_array(engine_cfb);
  909. DirAccess::remove_file_or_error(engine_cfb);
  910. return p_func(p_udata, "res://" + config_file, data, idx, total, enc_in_filters, enc_ex_filters, key);
  911. }
  912. Error EditorExportPlatform::_add_shared_object(void *p_userdata, const SharedObject &p_so) {
  913. PackData *pack_data = (PackData *)p_userdata;
  914. if (pack_data->so_files) {
  915. pack_data->so_files->push_back(p_so);
  916. }
  917. return OK;
  918. }
  919. Error EditorExportPlatform::save_pack(const Ref<EditorExportPreset> &p_preset, const String &p_path, Vector<SharedObject> *p_so_files, bool p_embed, int64_t *r_embedded_start, int64_t *r_embedded_size) {
  920. EditorProgress ep("savepack", TTR("Packing"), 102, true);
  921. // Create the temporary export directory if it doesn't exist.
  922. DirAccessRef da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
  923. da->make_dir_recursive(EditorPaths::get_singleton()->get_cache_dir());
  924. String tmppath = EditorPaths::get_singleton()->get_cache_dir().plus_file("packtmp");
  925. FileAccess *ftmp = FileAccess::open(tmppath, FileAccess::WRITE);
  926. ERR_FAIL_COND_V_MSG(!ftmp, ERR_CANT_CREATE, "Cannot create file '" + tmppath + "'.");
  927. PackData pd;
  928. pd.ep = &ep;
  929. pd.f = ftmp;
  930. pd.so_files = p_so_files;
  931. Error err = export_project_files(p_preset, _save_pack_file, &pd, _add_shared_object);
  932. memdelete(ftmp); //close tmp file
  933. if (err != OK) {
  934. DirAccess::remove_file_or_error(tmppath);
  935. ERR_PRINT("Failed to export project files");
  936. return err;
  937. }
  938. pd.file_ofs.sort(); //do sort, so we can do binary search later
  939. FileAccess *f;
  940. int64_t embed_pos = 0;
  941. if (!p_embed) {
  942. // Regular output to separate PCK file
  943. f = FileAccess::open(p_path, FileAccess::WRITE);
  944. if (!f) {
  945. DirAccess::remove_file_or_error(tmppath);
  946. ERR_FAIL_V(ERR_CANT_CREATE);
  947. }
  948. } else {
  949. // Append to executable
  950. f = FileAccess::open(p_path, FileAccess::READ_WRITE);
  951. if (!f) {
  952. DirAccess::remove_file_or_error(tmppath);
  953. ERR_FAIL_V(ERR_FILE_CANT_OPEN);
  954. }
  955. f->seek_end();
  956. embed_pos = f->get_position();
  957. if (r_embedded_start) {
  958. *r_embedded_start = embed_pos;
  959. }
  960. // Ensure embedded PCK starts at a 64-bit multiple
  961. int pad = f->get_position() % 8;
  962. for (int i = 0; i < pad; i++) {
  963. f->store_8(0);
  964. }
  965. }
  966. int64_t pck_start_pos = f->get_position();
  967. f->store_32(PACK_HEADER_MAGIC);
  968. f->store_32(PACK_FORMAT_VERSION);
  969. f->store_32(VERSION_MAJOR);
  970. f->store_32(VERSION_MINOR);
  971. f->store_32(VERSION_PATCH);
  972. uint32_t pack_flags = 0;
  973. bool enc_pck = p_preset->get_enc_pck();
  974. bool enc_directory = p_preset->get_enc_directory();
  975. if (enc_pck && enc_directory) {
  976. pack_flags |= PACK_DIR_ENCRYPTED;
  977. }
  978. f->store_32(pack_flags); // flags
  979. uint64_t file_base_ofs = f->get_position();
  980. f->store_64(0); // files base
  981. for (int i = 0; i < 16; i++) {
  982. //reserved
  983. f->store_32(0);
  984. }
  985. f->store_32(pd.file_ofs.size()); //amount of files
  986. FileAccessEncrypted *fae = nullptr;
  987. FileAccess *fhead = f;
  988. if (enc_pck && enc_directory) {
  989. String script_key = p_preset->get_script_encryption_key().to_lower();
  990. Vector<uint8_t> key;
  991. key.resize(32);
  992. if (script_key.length() == 64) {
  993. for (int i = 0; i < 32; i++) {
  994. int v = 0;
  995. if (i * 2 < script_key.length()) {
  996. char32_t ct = script_key[i * 2];
  997. if (ct >= '0' && ct <= '9') {
  998. ct = ct - '0';
  999. } else if (ct >= 'a' && ct <= 'f') {
  1000. ct = 10 + ct - 'a';
  1001. }
  1002. v |= ct << 4;
  1003. }
  1004. if (i * 2 + 1 < script_key.length()) {
  1005. char32_t ct = script_key[i * 2 + 1];
  1006. if (ct >= '0' && ct <= '9') {
  1007. ct = ct - '0';
  1008. } else if (ct >= 'a' && ct <= 'f') {
  1009. ct = 10 + ct - 'a';
  1010. }
  1011. v |= ct;
  1012. }
  1013. key.write[i] = v;
  1014. }
  1015. }
  1016. fae = memnew(FileAccessEncrypted);
  1017. ERR_FAIL_COND_V(!fae, ERR_SKIP);
  1018. err = fae->open_and_parse(f, key, FileAccessEncrypted::MODE_WRITE_AES256, false);
  1019. ERR_FAIL_COND_V(err != OK, ERR_SKIP);
  1020. fhead = fae;
  1021. }
  1022. for (int i = 0; i < pd.file_ofs.size(); i++) {
  1023. uint32_t string_len = pd.file_ofs[i].path_utf8.length();
  1024. uint32_t pad = _get_pad(4, string_len);
  1025. fhead->store_32(string_len + pad);
  1026. fhead->store_buffer((const uint8_t *)pd.file_ofs[i].path_utf8.get_data(), string_len);
  1027. for (uint32_t j = 0; j < pad; j++) {
  1028. fhead->store_8(0);
  1029. }
  1030. fhead->store_64(pd.file_ofs[i].ofs);
  1031. fhead->store_64(pd.file_ofs[i].size); // pay attention here, this is where file is
  1032. fhead->store_buffer(pd.file_ofs[i].md5.ptr(), 16); //also save md5 for file
  1033. uint32_t flags = 0;
  1034. if (pd.file_ofs[i].encrypted) {
  1035. flags |= PACK_FILE_ENCRYPTED;
  1036. }
  1037. fhead->store_32(flags);
  1038. }
  1039. if (fae) {
  1040. fae->release();
  1041. memdelete(fae);
  1042. }
  1043. int header_padding = _get_pad(PCK_PADDING, f->get_position());
  1044. for (int i = 0; i < header_padding; i++) {
  1045. f->store_8(Math::rand() % 256);
  1046. }
  1047. uint64_t file_base = f->get_position();
  1048. f->seek(file_base_ofs);
  1049. f->store_64(file_base); // update files base
  1050. f->seek(file_base);
  1051. // Save the rest of the data.
  1052. ftmp = FileAccess::open(tmppath, FileAccess::READ);
  1053. if (!ftmp) {
  1054. memdelete(f);
  1055. DirAccess::remove_file_or_error(tmppath);
  1056. ERR_FAIL_V_MSG(ERR_CANT_CREATE, "Can't open file to read from path '" + String(tmppath) + "'.");
  1057. }
  1058. const int bufsize = 16384;
  1059. uint8_t buf[bufsize];
  1060. while (true) {
  1061. uint64_t got = ftmp->get_buffer(buf, bufsize);
  1062. if (got == 0) {
  1063. break;
  1064. }
  1065. f->store_buffer(buf, got);
  1066. }
  1067. memdelete(ftmp);
  1068. if (p_embed) {
  1069. // Ensure embedded data ends at a 64-bit multiple
  1070. uint64_t embed_end = f->get_position() - embed_pos + 12;
  1071. uint64_t pad = embed_end % 8;
  1072. for (uint64_t i = 0; i < pad; i++) {
  1073. f->store_8(0);
  1074. }
  1075. uint64_t pck_size = f->get_position() - pck_start_pos;
  1076. f->store_64(pck_size);
  1077. f->store_32(PACK_HEADER_MAGIC);
  1078. if (r_embedded_size) {
  1079. *r_embedded_size = f->get_position() - embed_pos;
  1080. }
  1081. }
  1082. memdelete(f);
  1083. DirAccess::remove_file_or_error(tmppath);
  1084. return OK;
  1085. }
  1086. Error EditorExportPlatform::save_zip(const Ref<EditorExportPreset> &p_preset, const String &p_path) {
  1087. EditorProgress ep("savezip", TTR("Packing"), 102, true);
  1088. FileAccess *src_f;
  1089. zlib_filefunc_def io = zipio_create_io_from_file(&src_f);
  1090. zipFile zip = zipOpen2(p_path.utf8().get_data(), APPEND_STATUS_CREATE, nullptr, &io);
  1091. ZipData zd;
  1092. zd.ep = &ep;
  1093. zd.zip = zip;
  1094. Error err = export_project_files(p_preset, _save_zip_file, &zd);
  1095. if (err != OK && err != ERR_SKIP) {
  1096. ERR_PRINT("Failed to export project files");
  1097. }
  1098. zipClose(zip, nullptr);
  1099. return OK;
  1100. }
  1101. Error EditorExportPlatform::export_pack(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags) {
  1102. ExportNotifier notifier(*this, p_preset, p_debug, p_path, p_flags);
  1103. return save_pack(p_preset, p_path);
  1104. }
  1105. Error EditorExportPlatform::export_zip(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags) {
  1106. ExportNotifier notifier(*this, p_preset, p_debug, p_path, p_flags);
  1107. return save_zip(p_preset, p_path);
  1108. }
  1109. void EditorExportPlatform::gen_export_flags(Vector<String> &r_flags, int p_flags) {
  1110. String host = EditorSettings::get_singleton()->get("network/debug/remote_host");
  1111. int remote_port = (int)EditorSettings::get_singleton()->get("network/debug/remote_port");
  1112. if (p_flags & DEBUG_FLAG_REMOTE_DEBUG_LOCALHOST) {
  1113. host = "localhost";
  1114. }
  1115. if (p_flags & DEBUG_FLAG_DUMB_CLIENT) {
  1116. int port = EditorSettings::get_singleton()->get("filesystem/file_server/port");
  1117. String passwd = EditorSettings::get_singleton()->get("filesystem/file_server/password");
  1118. r_flags.push_back("--remote-fs");
  1119. r_flags.push_back(host + ":" + itos(port));
  1120. if (passwd != "") {
  1121. r_flags.push_back("--remote-fs-password");
  1122. r_flags.push_back(passwd);
  1123. }
  1124. }
  1125. if (p_flags & DEBUG_FLAG_REMOTE_DEBUG) {
  1126. r_flags.push_back("--remote-debug");
  1127. r_flags.push_back(get_debug_protocol() + host + ":" + String::num(remote_port));
  1128. List<String> breakpoints;
  1129. ScriptEditor::get_singleton()->get_breakpoints(&breakpoints);
  1130. if (breakpoints.size()) {
  1131. r_flags.push_back("--breakpoints");
  1132. String bpoints;
  1133. for (List<String>::Element *E = breakpoints.front(); E; E = E->next()) {
  1134. bpoints += E->get().replace(" ", "%20");
  1135. if (E->next()) {
  1136. bpoints += ",";
  1137. }
  1138. }
  1139. r_flags.push_back(bpoints);
  1140. }
  1141. }
  1142. if (p_flags & DEBUG_FLAG_VIEW_COLLISONS) {
  1143. r_flags.push_back("--debug-collisions");
  1144. }
  1145. if (p_flags & DEBUG_FLAG_VIEW_NAVIGATION) {
  1146. r_flags.push_back("--debug-navigation");
  1147. }
  1148. }
  1149. EditorExportPlatform::EditorExportPlatform() {
  1150. }
  1151. ////
  1152. EditorExport *EditorExport::singleton = nullptr;
  1153. void EditorExport::_save() {
  1154. Ref<ConfigFile> config;
  1155. config.instantiate();
  1156. for (int i = 0; i < export_presets.size(); i++) {
  1157. Ref<EditorExportPreset> preset = export_presets[i];
  1158. String section = "preset." + itos(i);
  1159. config->set_value(section, "name", preset->get_name());
  1160. config->set_value(section, "platform", preset->get_platform()->get_name());
  1161. config->set_value(section, "runnable", preset->is_runnable());
  1162. config->set_value(section, "custom_features", preset->get_custom_features());
  1163. bool save_files = false;
  1164. switch (preset->get_export_filter()) {
  1165. case EditorExportPreset::EXPORT_ALL_RESOURCES: {
  1166. config->set_value(section, "export_filter", "all_resources");
  1167. } break;
  1168. case EditorExportPreset::EXPORT_SELECTED_SCENES: {
  1169. config->set_value(section, "export_filter", "scenes");
  1170. save_files = true;
  1171. } break;
  1172. case EditorExportPreset::EXPORT_SELECTED_RESOURCES: {
  1173. config->set_value(section, "export_filter", "resources");
  1174. save_files = true;
  1175. } break;
  1176. case EditorExportPreset::EXCLUDE_SELECTED_RESOURCES: {
  1177. config->set_value(section, "export_filter", "exclude");
  1178. save_files = true;
  1179. } break;
  1180. }
  1181. if (save_files) {
  1182. Vector<String> export_files = preset->get_files_to_export();
  1183. config->set_value(section, "export_files", export_files);
  1184. }
  1185. config->set_value(section, "include_filter", preset->get_include_filter());
  1186. config->set_value(section, "exclude_filter", preset->get_exclude_filter());
  1187. config->set_value(section, "export_path", preset->get_export_path());
  1188. config->set_value(section, "encryption_include_filters", preset->get_enc_in_filter());
  1189. config->set_value(section, "encryption_exclude_filters", preset->get_enc_ex_filter());
  1190. config->set_value(section, "encrypt_pck", preset->get_enc_pck());
  1191. config->set_value(section, "encrypt_directory", preset->get_enc_directory());
  1192. config->set_value(section, "script_export_mode", preset->get_script_export_mode());
  1193. config->set_value(section, "script_encryption_key", preset->get_script_encryption_key());
  1194. String option_section = "preset." + itos(i) + ".options";
  1195. for (const PropertyInfo &E : preset->get_properties()) {
  1196. config->set_value(option_section, E.name, preset->get(E.name));
  1197. }
  1198. }
  1199. config->save("res://export_presets.cfg");
  1200. }
  1201. void EditorExport::save_presets() {
  1202. if (block_save) {
  1203. return;
  1204. }
  1205. save_timer->start();
  1206. }
  1207. void EditorExport::_bind_methods() {
  1208. ADD_SIGNAL(MethodInfo("export_presets_updated"));
  1209. }
  1210. void EditorExport::add_export_platform(const Ref<EditorExportPlatform> &p_platform) {
  1211. export_platforms.push_back(p_platform);
  1212. }
  1213. int EditorExport::get_export_platform_count() {
  1214. return export_platforms.size();
  1215. }
  1216. Ref<EditorExportPlatform> EditorExport::get_export_platform(int p_idx) {
  1217. ERR_FAIL_INDEX_V(p_idx, export_platforms.size(), Ref<EditorExportPlatform>());
  1218. return export_platforms[p_idx];
  1219. }
  1220. void EditorExport::add_export_preset(const Ref<EditorExportPreset> &p_preset, int p_at_pos) {
  1221. if (p_at_pos < 0) {
  1222. export_presets.push_back(p_preset);
  1223. } else {
  1224. export_presets.insert(p_at_pos, p_preset);
  1225. }
  1226. }
  1227. String EditorExportPlatform::test_etc2() const {
  1228. // String driver = ProjectSettings::get_singleton()->get("rendering/driver/driver_name");
  1229. // bool etc_supported = ProjectSettings::get_singleton()->get("rendering/textures/vram_compression/import_etc");
  1230. // bool etc2_supported = ProjectSettings::get_singleton()->get("rendering/textures/vram_compression/import_etc2");
  1231. String driver = ProjectSettings::get_singleton()->get("rendering/driver/driver_name");
  1232. bool etc_supported = ProjectSettings::get_singleton()->get("rendering/textures/vram_compression/import_etc");
  1233. bool etc2_supported = ProjectSettings::get_singleton()->get("rendering/textures/vram_compression/import_etc2");
  1234. if (driver == "OpenGL3" && !etc_supported) {
  1235. return TTR("Target platform requires 'ETC' texture compression for OpenGL. Enable 'Import Etc' in Project Settings.");
  1236. } else if (driver == "Vulkan" && !etc2_supported) {
  1237. // FIXME: Review if this is true for Vulkan.
  1238. return TTR("Target platform requires 'ETC2' texture compression for Vulkan. Enable 'Import Etc 2' in Project Settings.");
  1239. }
  1240. return String();
  1241. }
  1242. String EditorExportPlatform::test_etc2_or_pvrtc() const {
  1243. String driver = ProjectSettings::get_singleton()->get("rendering/driver/driver_name");
  1244. bool etc2_supported = ProjectSettings::get_singleton()->get("rendering/textures/vram_compression/import_etc2");
  1245. bool pvrtc_supported = ProjectSettings::get_singleton()->get("rendering/textures/vram_compression/import_pvrtc");
  1246. // String driver = ProjectSettings::get_singleton()->get("rendering/driver/driver_name");
  1247. // bool etc2_supported = ProjectSettings::get_singleton()->get("rendering/textures/vram_compression/import_etc2");
  1248. // bool pvrtc_supported = ProjectSettings::get_singleton()->get("rendering/textures/vram_compression/import_pvrtc");
  1249. if (driver == "OpenGL3" && !pvrtc_supported) {
  1250. return TTR("Target platform requires 'PVRTC' texture compression for OpenGL. Enable 'Import Pvrtc' in Project Settings.");
  1251. } else if (driver == "Vulkan" && !etc2_supported && !pvrtc_supported) {
  1252. // FIXME: Review if this is true for Vulkan.
  1253. return TTR("Target platform requires 'ETC2' or 'PVRTC' texture compression for Vulkan. Enable 'Import Etc 2' or 'Import Pvrtc' in Project Settings.");
  1254. }
  1255. return String();
  1256. }
  1257. int EditorExport::get_export_preset_count() const {
  1258. return export_presets.size();
  1259. }
  1260. Ref<EditorExportPreset> EditorExport::get_export_preset(int p_idx) {
  1261. ERR_FAIL_INDEX_V(p_idx, export_presets.size(), Ref<EditorExportPreset>());
  1262. return export_presets[p_idx];
  1263. }
  1264. void EditorExport::remove_export_preset(int p_idx) {
  1265. export_presets.remove(p_idx);
  1266. save_presets();
  1267. }
  1268. void EditorExport::add_export_plugin(const Ref<EditorExportPlugin> &p_plugin) {
  1269. if (export_plugins.find(p_plugin) == -1) {
  1270. export_plugins.push_back(p_plugin);
  1271. }
  1272. }
  1273. void EditorExport::remove_export_plugin(const Ref<EditorExportPlugin> &p_plugin) {
  1274. export_plugins.erase(p_plugin);
  1275. }
  1276. Vector<Ref<EditorExportPlugin>> EditorExport::get_export_plugins() {
  1277. return export_plugins;
  1278. }
  1279. void EditorExport::_notification(int p_what) {
  1280. switch (p_what) {
  1281. case NOTIFICATION_ENTER_TREE: {
  1282. load_config();
  1283. } break;
  1284. case NOTIFICATION_PROCESS: {
  1285. update_export_presets();
  1286. } break;
  1287. }
  1288. }
  1289. void EditorExport::load_config() {
  1290. Ref<ConfigFile> config;
  1291. config.instantiate();
  1292. Error err = config->load("res://export_presets.cfg");
  1293. if (err != OK) {
  1294. return;
  1295. }
  1296. block_save = true;
  1297. int index = 0;
  1298. while (true) {
  1299. String section = "preset." + itos(index);
  1300. if (!config->has_section(section)) {
  1301. break;
  1302. }
  1303. String platform = config->get_value(section, "platform");
  1304. Ref<EditorExportPreset> preset;
  1305. for (int i = 0; i < export_platforms.size(); i++) {
  1306. if (export_platforms[i]->get_name() == platform) {
  1307. preset = export_platforms.write[i]->create_preset();
  1308. break;
  1309. }
  1310. }
  1311. if (!preset.is_valid()) {
  1312. index++;
  1313. ERR_CONTINUE(!preset.is_valid());
  1314. }
  1315. preset->set_name(config->get_value(section, "name"));
  1316. preset->set_runnable(config->get_value(section, "runnable"));
  1317. if (config->has_section_key(section, "custom_features")) {
  1318. preset->set_custom_features(config->get_value(section, "custom_features"));
  1319. }
  1320. String export_filter = config->get_value(section, "export_filter");
  1321. bool get_files = false;
  1322. if (export_filter == "all_resources") {
  1323. preset->set_export_filter(EditorExportPreset::EXPORT_ALL_RESOURCES);
  1324. } else if (export_filter == "scenes") {
  1325. preset->set_export_filter(EditorExportPreset::EXPORT_SELECTED_SCENES);
  1326. get_files = true;
  1327. } else if (export_filter == "resources") {
  1328. preset->set_export_filter(EditorExportPreset::EXPORT_SELECTED_RESOURCES);
  1329. get_files = true;
  1330. } else if (export_filter == "exclude") {
  1331. preset->set_export_filter(EditorExportPreset::EXCLUDE_SELECTED_RESOURCES);
  1332. get_files = true;
  1333. }
  1334. if (get_files) {
  1335. Vector<String> files = config->get_value(section, "export_files");
  1336. for (int i = 0; i < files.size(); i++) {
  1337. if (!FileAccess::exists(files[i])) {
  1338. preset->remove_export_file(files[i]);
  1339. } else {
  1340. preset->add_export_file(files[i]);
  1341. }
  1342. }
  1343. }
  1344. preset->set_include_filter(config->get_value(section, "include_filter"));
  1345. preset->set_exclude_filter(config->get_value(section, "exclude_filter"));
  1346. preset->set_export_path(config->get_value(section, "export_path", ""));
  1347. if (config->has_section_key(section, "encrypt_pck")) {
  1348. preset->set_enc_pck(config->get_value(section, "encrypt_pck"));
  1349. }
  1350. if (config->has_section_key(section, "encrypt_directory")) {
  1351. preset->set_enc_directory(config->get_value(section, "encrypt_directory"));
  1352. }
  1353. if (config->has_section_key(section, "encryption_include_filters")) {
  1354. preset->set_enc_in_filter(config->get_value(section, "encryption_include_filters"));
  1355. }
  1356. if (config->has_section_key(section, "encryption_exclude_filters")) {
  1357. preset->set_enc_ex_filter(config->get_value(section, "encryption_exclude_filters"));
  1358. }
  1359. if (config->has_section_key(section, "script_export_mode")) {
  1360. preset->set_script_export_mode(config->get_value(section, "script_export_mode"));
  1361. }
  1362. if (config->has_section_key(section, "script_encryption_key")) {
  1363. preset->set_script_encryption_key(config->get_value(section, "script_encryption_key"));
  1364. }
  1365. String option_section = "preset." + itos(index) + ".options";
  1366. List<String> options;
  1367. config->get_section_keys(option_section, &options);
  1368. for (const String &E : options) {
  1369. Variant value = config->get_value(option_section, E);
  1370. preset->set(E, value);
  1371. }
  1372. add_export_preset(preset);
  1373. index++;
  1374. }
  1375. block_save = false;
  1376. }
  1377. void EditorExport::update_export_presets() {
  1378. Map<StringName, List<EditorExportPlatform::ExportOption>> platform_options;
  1379. for (int i = 0; i < export_platforms.size(); i++) {
  1380. Ref<EditorExportPlatform> platform = export_platforms[i];
  1381. if (platform->should_update_export_options()) {
  1382. List<EditorExportPlatform::ExportOption> options;
  1383. platform->get_export_options(&options);
  1384. platform_options[platform->get_name()] = options;
  1385. }
  1386. }
  1387. bool export_presets_updated = false;
  1388. for (int i = 0; i < export_presets.size(); i++) {
  1389. Ref<EditorExportPreset> preset = export_presets[i];
  1390. if (platform_options.has(preset->get_platform()->get_name())) {
  1391. export_presets_updated = true;
  1392. List<EditorExportPlatform::ExportOption> options = platform_options[preset->get_platform()->get_name()];
  1393. // Copy the previous preset values
  1394. Map<StringName, Variant> previous_values = preset->values;
  1395. // Clear the preset properties and values prior to reloading
  1396. preset->properties.clear();
  1397. preset->values.clear();
  1398. for (const EditorExportPlatform::ExportOption &E : options) {
  1399. preset->properties.push_back(E.option);
  1400. StringName option_name = E.option.name;
  1401. preset->values[option_name] = previous_values.has(option_name) ? previous_values[option_name] : E.default_value;
  1402. }
  1403. }
  1404. }
  1405. if (export_presets_updated) {
  1406. emit_signal(_export_presets_updated);
  1407. }
  1408. }
  1409. bool EditorExport::poll_export_platforms() {
  1410. bool changed = false;
  1411. for (int i = 0; i < export_platforms.size(); i++) {
  1412. if (export_platforms.write[i]->poll_export()) {
  1413. changed = true;
  1414. }
  1415. }
  1416. return changed;
  1417. }
  1418. EditorExport::EditorExport() {
  1419. save_timer = memnew(Timer);
  1420. add_child(save_timer);
  1421. save_timer->set_wait_time(0.8);
  1422. save_timer->set_one_shot(true);
  1423. save_timer->connect("timeout", callable_mp(this, &EditorExport::_save));
  1424. block_save = false;
  1425. _export_presets_updated = "export_presets_updated";
  1426. singleton = this;
  1427. set_process(true);
  1428. }
  1429. EditorExport::~EditorExport() {
  1430. }
  1431. //////////
  1432. void EditorExportPlatformPC::get_preset_features(const Ref<EditorExportPreset> &p_preset, List<String> *r_features) {
  1433. if (p_preset->get("texture_format/s3tc")) {
  1434. r_features->push_back("s3tc");
  1435. }
  1436. if (p_preset->get("texture_format/etc")) {
  1437. r_features->push_back("etc");
  1438. }
  1439. if (p_preset->get("texture_format/etc2")) {
  1440. r_features->push_back("etc2");
  1441. }
  1442. if (p_preset->get("binary_format/64_bits")) {
  1443. r_features->push_back("64");
  1444. } else {
  1445. r_features->push_back("32");
  1446. }
  1447. }
  1448. void EditorExportPlatformPC::get_export_options(List<ExportOption> *r_options) {
  1449. r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "custom_template/debug", PROPERTY_HINT_GLOBAL_FILE), ""));
  1450. r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "custom_template/release", PROPERTY_HINT_GLOBAL_FILE), ""));
  1451. r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "binary_format/64_bits"), true));
  1452. r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "binary_format/embed_pck"), false));
  1453. r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "texture_format/bptc"), false));
  1454. r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "texture_format/s3tc"), true));
  1455. r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "texture_format/etc"), false));
  1456. r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "texture_format/etc2"), false));
  1457. r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "texture_format/no_bptc_fallbacks"), true));
  1458. }
  1459. String EditorExportPlatformPC::get_name() const {
  1460. return name;
  1461. }
  1462. String EditorExportPlatformPC::get_os_name() const {
  1463. return os_name;
  1464. }
  1465. Ref<Texture2D> EditorExportPlatformPC::get_logo() const {
  1466. return logo;
  1467. }
  1468. bool EditorExportPlatformPC::can_export(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates) const {
  1469. String err;
  1470. bool valid = false;
  1471. // Look for export templates (first official, and if defined custom templates).
  1472. bool use64 = p_preset->get("binary_format/64_bits");
  1473. bool dvalid = exists_export_template(use64 ? debug_file_64 : debug_file_32, &err);
  1474. bool rvalid = exists_export_template(use64 ? release_file_64 : release_file_32, &err);
  1475. if (p_preset->get("custom_template/debug") != "") {
  1476. dvalid = FileAccess::exists(p_preset->get("custom_template/debug"));
  1477. if (!dvalid) {
  1478. err += TTR("Custom debug template not found.") + "\n";
  1479. }
  1480. }
  1481. if (p_preset->get("custom_template/release") != "") {
  1482. rvalid = FileAccess::exists(p_preset->get("custom_template/release"));
  1483. if (!rvalid) {
  1484. err += TTR("Custom release template not found.") + "\n";
  1485. }
  1486. }
  1487. valid = dvalid || rvalid;
  1488. r_missing_templates = !valid;
  1489. if (!err.is_empty()) {
  1490. r_error = err;
  1491. }
  1492. return valid;
  1493. }
  1494. List<String> EditorExportPlatformPC::get_binary_extensions(const Ref<EditorExportPreset> &p_preset) const {
  1495. List<String> list;
  1496. for (const KeyValue<String, String> &E : extensions) {
  1497. if (p_preset->get(E.key)) {
  1498. list.push_back(extensions[E.key]);
  1499. return list;
  1500. }
  1501. }
  1502. if (extensions.has("default")) {
  1503. list.push_back(extensions["default"]);
  1504. return list;
  1505. }
  1506. return list;
  1507. }
  1508. Error EditorExportPlatformPC::export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags) {
  1509. ExportNotifier notifier(*this, p_preset, p_debug, p_path, p_flags);
  1510. if (!DirAccess::exists(p_path.get_base_dir())) {
  1511. return ERR_FILE_BAD_PATH;
  1512. }
  1513. String custom_debug = p_preset->get("custom_template/debug");
  1514. String custom_release = p_preset->get("custom_template/release");
  1515. String template_path = p_debug ? custom_debug : custom_release;
  1516. template_path = template_path.strip_edges();
  1517. if (template_path == String()) {
  1518. if (p_preset->get("binary_format/64_bits")) {
  1519. if (p_debug) {
  1520. template_path = find_export_template(debug_file_64);
  1521. } else {
  1522. template_path = find_export_template(release_file_64);
  1523. }
  1524. } else {
  1525. if (p_debug) {
  1526. template_path = find_export_template(debug_file_32);
  1527. } else {
  1528. template_path = find_export_template(release_file_32);
  1529. }
  1530. }
  1531. }
  1532. if (template_path != String() && !FileAccess::exists(template_path)) {
  1533. EditorNode::get_singleton()->show_warning(TTR("Template file not found:") + "\n" + template_path);
  1534. return ERR_FILE_NOT_FOUND;
  1535. }
  1536. DirAccess *da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
  1537. Error err = da->copy(template_path, p_path, get_chmod_flags());
  1538. memdelete(da);
  1539. if (err == OK) {
  1540. String pck_path;
  1541. if (p_preset->get("binary_format/embed_pck")) {
  1542. pck_path = p_path;
  1543. } else {
  1544. pck_path = p_path.get_basename() + ".pck";
  1545. }
  1546. Vector<SharedObject> so_files;
  1547. int64_t embedded_pos;
  1548. int64_t embedded_size;
  1549. err = save_pack(p_preset, pck_path, &so_files, p_preset->get("binary_format/embed_pck"), &embedded_pos, &embedded_size);
  1550. if (err == OK && p_preset->get("binary_format/embed_pck")) {
  1551. if (embedded_size >= 0x100000000 && !p_preset->get("binary_format/64_bits")) {
  1552. EditorNode::get_singleton()->show_warning(TTR("On 32-bit exports the embedded PCK cannot be bigger than 4 GiB."));
  1553. return ERR_INVALID_PARAMETER;
  1554. }
  1555. FixUpEmbeddedPckFunc fixup_func = get_fixup_embedded_pck_func();
  1556. if (fixup_func) {
  1557. err = fixup_func(p_path, embedded_pos, embedded_size);
  1558. }
  1559. }
  1560. if (err == OK && !so_files.is_empty()) {
  1561. //if shared object files, copy them
  1562. da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
  1563. for (int i = 0; i < so_files.size() && err == OK; i++) {
  1564. err = da->copy(so_files[i].path, p_path.get_base_dir().plus_file(so_files[i].path.get_file()));
  1565. if (err == OK) {
  1566. err = sign_shared_object(p_preset, p_debug, p_path.get_base_dir().plus_file(so_files[i].path.get_file()));
  1567. }
  1568. }
  1569. memdelete(da);
  1570. }
  1571. }
  1572. return err;
  1573. }
  1574. Error EditorExportPlatformPC::sign_shared_object(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path) {
  1575. return OK;
  1576. }
  1577. void EditorExportPlatformPC::set_extension(const String &p_extension, const String &p_feature_key) {
  1578. extensions[p_feature_key] = p_extension;
  1579. }
  1580. void EditorExportPlatformPC::set_name(const String &p_name) {
  1581. name = p_name;
  1582. }
  1583. void EditorExportPlatformPC::set_os_name(const String &p_name) {
  1584. os_name = p_name;
  1585. }
  1586. void EditorExportPlatformPC::set_logo(const Ref<Texture2D> &p_logo) {
  1587. logo = p_logo;
  1588. }
  1589. void EditorExportPlatformPC::set_release_64(const String &p_file) {
  1590. release_file_64 = p_file;
  1591. }
  1592. void EditorExportPlatformPC::set_release_32(const String &p_file) {
  1593. release_file_32 = p_file;
  1594. }
  1595. void EditorExportPlatformPC::set_debug_64(const String &p_file) {
  1596. debug_file_64 = p_file;
  1597. }
  1598. void EditorExportPlatformPC::set_debug_32(const String &p_file) {
  1599. debug_file_32 = p_file;
  1600. }
  1601. void EditorExportPlatformPC::get_platform_features(List<String> *r_features) {
  1602. r_features->push_back("pc"); //all pcs support "pc"
  1603. r_features->push_back("s3tc"); //all pcs support "s3tc" compression
  1604. r_features->push_back(get_os_name().to_lower()); //OS name is a feature
  1605. }
  1606. void EditorExportPlatformPC::resolve_platform_feature_priorities(const Ref<EditorExportPreset> &p_preset, Set<String> &p_features) {
  1607. if (p_features.has("bptc")) {
  1608. if (p_preset->has("texture_format/no_bptc_fallbacks")) {
  1609. p_features.erase("s3tc");
  1610. }
  1611. }
  1612. }
  1613. int EditorExportPlatformPC::get_chmod_flags() const {
  1614. return chmod_flags;
  1615. }
  1616. void EditorExportPlatformPC::set_chmod_flags(int p_flags) {
  1617. chmod_flags = p_flags;
  1618. }
  1619. EditorExportPlatformPC::FixUpEmbeddedPckFunc EditorExportPlatformPC::get_fixup_embedded_pck_func() const {
  1620. return fixup_embedded_pck_func;
  1621. }
  1622. void EditorExportPlatformPC::set_fixup_embedded_pck_func(FixUpEmbeddedPckFunc p_fixup_embedded_pck_func) {
  1623. fixup_embedded_pck_func = p_fixup_embedded_pck_func;
  1624. }
  1625. EditorExportPlatformPC::EditorExportPlatformPC() {
  1626. chmod_flags = -1;
  1627. fixup_embedded_pck_func = nullptr;
  1628. }
  1629. ///////////////////////
  1630. void EditorExportTextSceneToBinaryPlugin::_export_file(const String &p_path, const String &p_type, const Set<String> &p_features) {
  1631. String extension = p_path.get_extension().to_lower();
  1632. if (extension != "tres" && extension != "tscn") {
  1633. return;
  1634. }
  1635. bool convert = GLOBAL_GET("editor/export/convert_text_resources_to_binary");
  1636. if (!convert) {
  1637. return;
  1638. }
  1639. String tmp_path = EditorPaths::get_singleton()->get_cache_dir().plus_file("tmpfile.res");
  1640. Error err = ResourceFormatLoaderText::convert_file_to_binary(p_path, tmp_path);
  1641. if (err != OK) {
  1642. DirAccess::remove_file_or_error(tmp_path);
  1643. ERR_FAIL();
  1644. }
  1645. Vector<uint8_t> data = FileAccess::get_file_as_array(tmp_path);
  1646. if (data.size() == 0) {
  1647. DirAccess::remove_file_or_error(tmp_path);
  1648. ERR_FAIL();
  1649. }
  1650. DirAccess::remove_file_or_error(tmp_path);
  1651. add_file(p_path + ".converted.res", data, true);
  1652. }
  1653. EditorExportTextSceneToBinaryPlugin::EditorExportTextSceneToBinaryPlugin() {
  1654. GLOBAL_DEF("editor/export/convert_text_resources_to_binary", false);
  1655. }