export.cpp 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689
  1. /*************************************************************************/
  2. /* export.cpp */
  3. /*************************************************************************/
  4. /* This file is part of: */
  5. /* GODOT ENGINE */
  6. /* https://godotengine.org */
  7. /*************************************************************************/
  8. /* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
  9. /* Copyright (c) 2014-2020 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 "export.h"
  31. #include "editor/editor_import_export.h"
  32. #include "editor/editor_node.h"
  33. #include "editor/editor_settings.h"
  34. #include "globals.h"
  35. #include "io/marshalls.h"
  36. #include "io/resource_saver.h"
  37. #include "io/zip_io.h"
  38. #include "os/file_access.h"
  39. #include "os/os.h"
  40. #include "platform/osx/logo.gen.h"
  41. #include "string.h"
  42. #include "version.h"
  43. class EditorExportPlatformOSX : public EditorExportPlatform {
  44. OBJ_TYPE(EditorExportPlatformOSX, EditorExportPlatform);
  45. String custom_release_package;
  46. String custom_debug_package;
  47. int version_code;
  48. String app_name;
  49. String info;
  50. String icon;
  51. String identifier;
  52. String short_version;
  53. String version;
  54. String signature;
  55. String copyright;
  56. String identity;
  57. String entitlements;
  58. bool high_resolution;
  59. Ref<ImageTexture> logo;
  60. void _fix_plist(Vector<uint8_t> &plist, const String &p_binary);
  61. void _make_icon(const Image &p_icon, Vector<uint8_t> &data);
  62. Error _code_sign(const String &p_path);
  63. Error _create_dmg(const String &p_dmg_path, const String &p_pkg_name, const String &p_app_path_name);
  64. #ifdef OSX_ENABLED
  65. bool use_codesign() const { return true; }
  66. bool use_dmg() const { return true; }
  67. #else
  68. bool use_codesign() const { return false; }
  69. bool use_dmg() const { return false; }
  70. #endif
  71. protected:
  72. bool _set(const StringName &p_name, const Variant &p_value);
  73. bool _get(const StringName &p_name, Variant &r_ret) const;
  74. void _get_property_list(List<PropertyInfo> *p_list) const;
  75. public:
  76. virtual String get_name() const { return "Mac OSX"; }
  77. virtual ImageCompression get_image_compression() const { return IMAGE_COMPRESSION_BC; }
  78. virtual Ref<Texture> get_logo() const { return logo; }
  79. virtual bool poll_devices() { return false; }
  80. virtual int get_device_count() const { return 0; }
  81. virtual String get_device_name(int p_device) const { return String(); }
  82. virtual String get_device_info(int p_device) const { return String(); }
  83. virtual Error run(int p_device, int p_flags = 0);
  84. virtual bool requires_password(bool p_debug) const { return false; }
  85. virtual String get_binary_extension() const { return use_dmg() ? "dmg" : "zip"; }
  86. virtual Error export_project(const String &p_path, bool p_debug, int p_flags = 0);
  87. virtual bool can_export(String *r_error = NULL) const;
  88. EditorExportPlatformOSX();
  89. ~EditorExportPlatformOSX();
  90. };
  91. bool EditorExportPlatformOSX::_set(const StringName &p_name, const Variant &p_value) {
  92. String n = p_name;
  93. if (n == "custom_package/debug")
  94. custom_debug_package = p_value;
  95. else if (n == "custom_package/release")
  96. custom_release_package = p_value;
  97. else if (n == "application/name")
  98. app_name = p_value;
  99. else if (n == "application/info")
  100. info = p_value;
  101. else if (n == "application/icon")
  102. icon = p_value;
  103. else if (n == "application/identifier")
  104. identifier = p_value;
  105. else if (n == "application/signature")
  106. signature = p_value;
  107. else if (n == "application/short_version")
  108. short_version = p_value;
  109. else if (n == "application/version")
  110. version = p_value;
  111. else if (n == "application/copyright")
  112. copyright = p_value;
  113. else if (n == "display/high_res")
  114. high_resolution = p_value;
  115. else if (n == "codesign/identity")
  116. identity = p_value;
  117. else if (n == "codesign/entitlements")
  118. entitlements = p_value;
  119. else
  120. return false;
  121. return true;
  122. }
  123. bool EditorExportPlatformOSX::_get(const StringName &p_name, Variant &r_ret) const {
  124. String n = p_name;
  125. if (n == "custom_package/debug")
  126. r_ret = custom_debug_package;
  127. else if (n == "custom_package/release")
  128. r_ret = custom_release_package;
  129. else if (n == "application/name")
  130. r_ret = app_name;
  131. else if (n == "application/info")
  132. r_ret = info;
  133. else if (n == "application/icon")
  134. r_ret = icon;
  135. else if (n == "application/identifier")
  136. r_ret = identifier;
  137. else if (n == "application/signature")
  138. r_ret = signature;
  139. else if (n == "application/short_version")
  140. r_ret = short_version;
  141. else if (n == "application/version")
  142. r_ret = version;
  143. else if (n == "application/copyright")
  144. r_ret = copyright;
  145. else if (n == "display/high_res")
  146. r_ret = high_resolution;
  147. else if (n == "codesign/identity")
  148. r_ret = identity;
  149. else if (n == "codesign/entitlements")
  150. r_ret = entitlements;
  151. else
  152. return false;
  153. return true;
  154. }
  155. void EditorExportPlatformOSX::_get_property_list(List<PropertyInfo> *p_list) const {
  156. p_list->push_back(PropertyInfo(Variant::STRING, "custom_package/debug", PROPERTY_HINT_GLOBAL_FILE, "zip"));
  157. p_list->push_back(PropertyInfo(Variant::STRING, "custom_package/release", PROPERTY_HINT_GLOBAL_FILE, "zip"));
  158. p_list->push_back(PropertyInfo(Variant::STRING, "application/name"));
  159. p_list->push_back(PropertyInfo(Variant::STRING, "application/info"));
  160. p_list->push_back(PropertyInfo(Variant::STRING, "application/icon", PROPERTY_HINT_FILE, "png"));
  161. p_list->push_back(PropertyInfo(Variant::STRING, "application/identifier"));
  162. p_list->push_back(PropertyInfo(Variant::STRING, "application/signature"));
  163. p_list->push_back(PropertyInfo(Variant::STRING, "application/short_version"));
  164. p_list->push_back(PropertyInfo(Variant::STRING, "application/version"));
  165. p_list->push_back(PropertyInfo(Variant::STRING, "application/copyright"));
  166. p_list->push_back(PropertyInfo(Variant::BOOL, "display/high_res"));
  167. p_list->push_back(PropertyInfo(Variant::STRING, "codesign/identity"));
  168. p_list->push_back(PropertyInfo(Variant::STRING, "codesign/entitlements"));
  169. }
  170. void EditorExportPlatformOSX::_make_icon(const Image &p_icon, Vector<uint8_t> &icon) {
  171. Ref<ImageTexture> it = memnew(ImageTexture);
  172. int size = 512;
  173. Vector<uint8_t> data;
  174. data.resize(8);
  175. data[0] = 'i';
  176. data[1] = 'c';
  177. data[2] = 'n';
  178. data[3] = 's';
  179. const char *name[] = { "ic09", "ic08", "ic07", "icp6", "icp5", "icp4" };
  180. int index = 0;
  181. while (size >= 16) {
  182. Image copy = p_icon;
  183. copy.convert(Image::FORMAT_RGBA);
  184. copy.resize(size, size);
  185. it->create_from_image(copy);
  186. String path = EditorSettings::get_singleton()->get_settings_path() + "/tmp/icon.png";
  187. ResourceSaver::save(path, it);
  188. FileAccess *f = FileAccess::open(path, FileAccess::READ);
  189. ERR_FAIL_COND(!f);
  190. int ofs = data.size();
  191. uint32_t len = f->get_len();
  192. data.resize(data.size() + len + 8);
  193. f->get_buffer(&data[ofs + 8], len);
  194. memdelete(f);
  195. len += 8;
  196. len = BSWAP32(len);
  197. copymem(&data[ofs], name[index], 4);
  198. encode_uint32(len, &data[ofs + 4]);
  199. index++;
  200. size /= 2;
  201. }
  202. uint32_t total_len = data.size();
  203. total_len = BSWAP32(total_len);
  204. encode_uint32(total_len, &data[4]);
  205. icon = data;
  206. }
  207. void EditorExportPlatformOSX::_fix_plist(Vector<uint8_t> &plist, const String &p_binary) {
  208. String str;
  209. String strnew;
  210. str.parse_utf8((const char *)plist.ptr(), plist.size());
  211. Vector<String> lines = str.split("\n");
  212. for (int i = 0; i < lines.size(); i++) {
  213. if (lines[i].find("$binary") != -1) {
  214. strnew += lines[i].replace("$binary", p_binary) + "\n";
  215. } else if (lines[i].find("$name") != -1) {
  216. strnew += lines[i].replace("$name", p_binary) + "\n";
  217. } else if (lines[i].find("$info") != -1) {
  218. strnew += lines[i].replace("$info", info) + "\n";
  219. } else if (lines[i].find("$identifier") != -1) {
  220. strnew += lines[i].replace("$identifier", identifier) + "\n";
  221. } else if (lines[i].find("$short_version") != -1) {
  222. strnew += lines[i].replace("$short_version", short_version) + "\n";
  223. } else if (lines[i].find("$version") != -1) {
  224. strnew += lines[i].replace("$version", version) + "\n";
  225. } else if (lines[i].find("$signature") != -1) {
  226. strnew += lines[i].replace("$signature", signature) + "\n";
  227. } else if (lines[i].find("$copyright") != -1) {
  228. strnew += lines[i].replace("$copyright", copyright) + "\n";
  229. } else if (lines[i].find("$highres") != -1) {
  230. strnew += lines[i].replace("$highres", high_resolution ? "<true/>" : "<false/>") + "\n";
  231. } else {
  232. strnew += lines[i] + "\n";
  233. }
  234. }
  235. CharString cs = strnew.utf8();
  236. plist.resize(cs.size() - 1);
  237. for (int i = 0; i < cs.size() - 1; i++) {
  238. plist[i] = cs[i];
  239. }
  240. }
  241. /**
  242. If we're running the OSX version of the Godot editor we'll:
  243. - export our application bundle to a temporary folder
  244. - attempt to code sign it
  245. - and then wrap it up in a DMG
  246. **/
  247. Error EditorExportPlatformOSX::_code_sign(const String &p_path) {
  248. List<String> args;
  249. if (entitlements != "") {
  250. // this should point to our entitlements.plist file that sandboxes our application, I don't know if this should also be placed in our app bundle
  251. args.push_back("-entitlements");
  252. args.push_back(entitlements);
  253. }
  254. args.push_back("-s");
  255. args.push_back(identity);
  256. args.push_back("-v"); // provide some more feedback
  257. args.push_back(p_path);
  258. String str;
  259. Error err = OS::get_singleton()->execute("/usr/bin/codesign", args, true, NULL, &str, NULL, true);
  260. ERR_FAIL_COND_V(err != OK, err);
  261. print_line("codesign: " + str);
  262. if (str.find("no identity found") != -1) {
  263. EditorNode::add_io_error("codesign: no identity found");
  264. return FAILED;
  265. }
  266. return OK;
  267. }
  268. Error EditorExportPlatformOSX::_create_dmg(const String &p_dmg_path, const String &p_pkg_name, const String &p_app_path_name) {
  269. OS::get_singleton()->move_path_to_trash(p_dmg_path);
  270. List<String> args;
  271. args.push_back("create");
  272. args.push_back(p_dmg_path);
  273. args.push_back("-volname");
  274. args.push_back(p_pkg_name);
  275. args.push_back("-fs");
  276. args.push_back("HFS+");
  277. args.push_back("-srcfolder");
  278. args.push_back(p_app_path_name);
  279. String str;
  280. Error err = OS::get_singleton()->execute("/usr/bin/hdiutil", args, true, NULL, &str, NULL, true);
  281. ERR_FAIL_COND_V(err != OK, err);
  282. print_line("hdiutil returned: " + str);
  283. if (str.find("create failed") != -1) {
  284. if (str.find("File exists") != -1) {
  285. EditorNode::add_io_error("hdiutil: create failed - file exists");
  286. } else {
  287. EditorNode::add_io_error("hdiutil: create failed");
  288. }
  289. return FAILED;
  290. }
  291. return OK;
  292. }
  293. Error EditorExportPlatformOSX::export_project(const String &p_path, bool p_debug, int p_flags) {
  294. EditorProgress ep("export", "Exporting for OSX", 104);
  295. String src_pkg = p_debug ? custom_debug_package : custom_release_package;
  296. if (src_pkg == "") {
  297. String err;
  298. src_pkg = find_export_template("osx.zip", &err);
  299. if (src_pkg == "") {
  300. EditorNode::add_io_error(err);
  301. return ERR_FILE_NOT_FOUND;
  302. }
  303. }
  304. FileAccess *src_f = NULL;
  305. zlib_filefunc_def io = zipio_create_io_from_file(&src_f);
  306. ep.step("Creating app", 0);
  307. unzFile pkg = unzOpen2(src_pkg.utf8().get_data(), &io);
  308. if (!pkg) {
  309. EditorNode::add_io_error("Could not find template app to export:\n" + src_pkg);
  310. return ERR_FILE_NOT_FOUND;
  311. }
  312. int ret = unzGoToFirstFile(pkg);
  313. zlib_filefunc_def io2 = io;
  314. FileAccess *dst_f = NULL;
  315. io2.opaque = &dst_f;
  316. zipFile dpkg = NULL;
  317. if (!use_dmg()) {
  318. dpkg = zipOpen2(p_path.utf8().get_data(), APPEND_STATUS_CREATE, NULL, &io2);
  319. if (!dpkg) {
  320. unzClose(pkg);
  321. return ERR_CANT_OPEN;
  322. }
  323. }
  324. String binary_to_use = "godot_osx_" + String(p_debug ? "debug" : "release") + ".64";
  325. String pkg_name;
  326. if (app_name != "")
  327. pkg_name = app_name;
  328. else if (String(Globals::get_singleton()->get("application/name")) != "")
  329. pkg_name = String(Globals::get_singleton()->get("application/name"));
  330. else
  331. pkg_name = "Unnamed";
  332. Error err = OK;
  333. String tmp_app_path_name = "";
  334. if (use_dmg()) {
  335. // We're on OSX so we can export to DMG, but first we create our application bundle
  336. tmp_app_path_name = EditorSettings::get_singleton()->get_settings_path() + "/tmp/" + pkg_name + ".app";
  337. print_line("Exporting to " + tmp_app_path_name);
  338. DirAccess *da_tmp_app = DirAccess::create_for_path(tmp_app_path_name);
  339. if (!da_tmp_app) {
  340. err = ERR_CANT_CREATE;
  341. }
  342. // Create our folder structure or rely on unzip?
  343. if (err == OK) {
  344. print_line("Creating " + tmp_app_path_name + "/Contents/MacOS");
  345. err = da_tmp_app->make_dir_recursive(tmp_app_path_name + "/Contents/MacOS");
  346. }
  347. if (err == OK) {
  348. print_line("Creating " + tmp_app_path_name + "/Contents/Resources");
  349. err = da_tmp_app->make_dir_recursive(tmp_app_path_name + "/Contents/Resources");
  350. }
  351. }
  352. bool found_binary = false;
  353. while (ret == UNZ_OK && err == OK) {
  354. bool is_execute = false;
  355. //get filename
  356. unz_file_info info;
  357. char fname[16384];
  358. ret = unzGetCurrentFileInfo(pkg, &info, fname, 16384, NULL, 0, NULL, 0);
  359. String file = fname;
  360. print_line("READ: " + file);
  361. Vector<uint8_t> data;
  362. data.resize(info.uncompressed_size);
  363. //read
  364. unzOpenCurrentFile(pkg);
  365. unzReadCurrentFile(pkg, data.ptr(), data.size());
  366. unzCloseCurrentFile(pkg);
  367. //write
  368. file = file.replace_first("osx_template.app/", "");
  369. if (file == "Contents/Info.plist") {
  370. print_line("parse plist");
  371. _fix_plist(data, pkg_name);
  372. }
  373. if (file.begins_with("Contents/MacOS/godot_")) {
  374. if (file != "Contents/MacOS/" + binary_to_use) {
  375. ret = unzGoToNextFile(pkg);
  376. continue; //ignore!
  377. }
  378. found_binary = true;
  379. is_execute = true;
  380. file = "Contents/MacOS/" + pkg_name;
  381. }
  382. if (file == "Contents/Resources/icon.icns") {
  383. //see if there is an icon
  384. String iconpath = Globals::get_singleton()->get("application/icon");
  385. print_line("icon? " + iconpath);
  386. if (iconpath != "") {
  387. Image icon;
  388. icon.load(iconpath);
  389. if (!icon.empty()) {
  390. print_line("loaded?");
  391. _make_icon(icon, data);
  392. }
  393. }
  394. }
  395. if (data.size() > 0) {
  396. print_line("ADDING: " + file + " size: " + itos(data.size()));
  397. if (use_dmg()) {
  398. // write it into our application bundle
  399. file = tmp_app_path_name + "/" + file;
  400. // write the file
  401. FileAccess *f = FileAccess::open(file, FileAccess::WRITE);
  402. if (f) {
  403. f->store_buffer(data.ptr(), data.size());
  404. f->close();
  405. if (is_execute) {
  406. // Chmod with 0755 if the file is executable
  407. err = f->_chmod(file, 0755);
  408. }
  409. memdelete(f);
  410. } else {
  411. err = ERR_CANT_CREATE;
  412. }
  413. } else {
  414. zip_fileinfo fi;
  415. file = pkg_name + ".app/" + file;
  416. fi.tmz_date.tm_hour = info.tmu_date.tm_hour;
  417. fi.tmz_date.tm_min = info.tmu_date.tm_min;
  418. fi.tmz_date.tm_sec = info.tmu_date.tm_sec;
  419. fi.tmz_date.tm_mon = info.tmu_date.tm_mon;
  420. fi.tmz_date.tm_mday = info.tmu_date.tm_mday;
  421. fi.tmz_date.tm_year = info.tmu_date.tm_year;
  422. fi.dosDate = info.dosDate;
  423. fi.internal_fa = info.internal_fa;
  424. fi.external_fa = info.external_fa;
  425. int zerr = zipOpenNewFileInZip(dpkg,
  426. file.utf8().get_data(),
  427. &fi,
  428. NULL,
  429. 0,
  430. NULL,
  431. 0,
  432. NULL,
  433. Z_DEFLATED,
  434. Z_DEFAULT_COMPRESSION);
  435. print_line("OPEN ERR: " + itos(zerr));
  436. zerr = zipWriteInFileInZip(dpkg, data.ptr(), data.size());
  437. print_line("WRITE ERR: " + itos(zerr));
  438. zipCloseFileInZip(dpkg);
  439. }
  440. }
  441. ret = unzGoToNextFile(pkg);
  442. }
  443. if (!found_binary) {
  444. ERR_PRINTS("Requested template binary '" + binary_to_use + "' not found. It might be missing from your template archive.");
  445. err = ERR_FILE_NOT_FOUND;
  446. }
  447. if (err == OK) {
  448. ep.step("Making PKG", 1);
  449. String pack_path;
  450. if (use_dmg()) {
  451. pack_path = tmp_app_path_name + "/Contents/Resources/" + pkg_name + ".pck";
  452. } else {
  453. pack_path = EditorSettings::get_singleton()->get_settings_path() + "/tmp/data.pck";
  454. }
  455. FileAccess *pfs = FileAccess::open(pack_path, FileAccess::WRITE);
  456. if (pfs) {
  457. err = save_pack(pfs);
  458. memdelete(pfs);
  459. } else {
  460. err = ERR_CANT_OPEN;
  461. }
  462. if (use_dmg()) {
  463. if (err == OK && use_codesign()) {
  464. /* see if we can code sign our new package */
  465. if (err == OK && identity != "") {
  466. ep.step("Code signing bundle", 2);
  467. /* the order in which we code sign is important, this is a bit of a shame or we could do this in our loop that extracts the files from our ZIP */
  468. // start with our application
  469. err = _code_sign(tmp_app_path_name + "/Contents/MacOS/" + pkg_name);
  470. }
  471. ///@TODO we should check the contents of /Contents/Frameworks for frameworks to sign
  472. if (err == OK && identity != "") {
  473. // we should probably loop through all resources and sign them?
  474. err = _code_sign(tmp_app_path_name + "/Contents/Resources/icon.icns");
  475. }
  476. if (err == OK && identity != "") {
  477. err = _code_sign(pack_path);
  478. }
  479. if (err == OK && identity != "") {
  480. err = _code_sign(tmp_app_path_name + "/Contents/Info.plist");
  481. }
  482. }
  483. if (err == OK) {
  484. // and finally create a DMG
  485. ep.step("Making DMG", 3);
  486. err = _create_dmg(p_path, pkg_name, tmp_app_path_name);
  487. }
  488. // Clean up temporary .app dir
  489. OS::get_singleton()->move_path_to_trash(tmp_app_path_name);
  490. } else if (err == OK) {
  491. //write datapack
  492. int zerr = zipOpenNewFileInZip(dpkg,
  493. (pkg_name + ".app/Contents/Resources/data.pck").utf8().get_data(),
  494. NULL,
  495. NULL,
  496. 0,
  497. NULL,
  498. 0,
  499. NULL,
  500. Z_DEFLATED,
  501. Z_DEFAULT_COMPRESSION);
  502. FileAccess *pf = FileAccess::open(pack_path, FileAccess::READ);
  503. if (pf) {
  504. const int BSIZE = 16384;
  505. uint8_t buf[BSIZE];
  506. while (true) {
  507. int r = pf->get_buffer(buf, BSIZE);
  508. if (r <= 0)
  509. break;
  510. zipWriteInFileInZip(dpkg, buf, r);
  511. }
  512. zipCloseFileInZip(dpkg);
  513. memdelete(pf);
  514. } else {
  515. err = ERR_CANT_OPEN;
  516. }
  517. }
  518. }
  519. if (dpkg) {
  520. zipClose(dpkg, NULL);
  521. }
  522. unzClose(pkg);
  523. return err;
  524. }
  525. Error EditorExportPlatformOSX::run(int p_device, int p_flags) {
  526. return OK;
  527. }
  528. EditorExportPlatformOSX::EditorExportPlatformOSX() {
  529. Image img(_osx_logo);
  530. logo = Ref<ImageTexture>(memnew(ImageTexture));
  531. logo->create_from_image(img);
  532. info = "Made with Godot Engine";
  533. identifier = "org.godotengine.macgame";
  534. signature = "godotmacgame";
  535. short_version = "1.0";
  536. version = "1.0";
  537. high_resolution = false;
  538. identity = "";
  539. entitlements = "";
  540. }
  541. bool EditorExportPlatformOSX::can_export(String *r_error) const {
  542. bool valid = true;
  543. String err;
  544. if (!exists_export_template("osx.zip")) {
  545. valid = false;
  546. err += "No export templates found.\nDownload and install export templates.\n";
  547. }
  548. if (custom_debug_package != "" && !FileAccess::exists(custom_debug_package)) {
  549. valid = false;
  550. err += "Custom debug package not found.\n";
  551. }
  552. if (custom_release_package != "" && !FileAccess::exists(custom_release_package)) {
  553. valid = false;
  554. err += "Custom release package not found.\n";
  555. }
  556. if (r_error)
  557. *r_error = err;
  558. return valid;
  559. }
  560. EditorExportPlatformOSX::~EditorExportPlatformOSX() {
  561. }
  562. void register_osx_exporter() {
  563. Ref<EditorExportPlatformOSX> exporter = Ref<EditorExportPlatformOSX>(memnew(EditorExportPlatformOSX));
  564. EditorImportExport::get_singleton()->add_export_platform(exporter);
  565. }