export.cpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548
  1. /*************************************************************************/
  2. /* export.cpp */
  3. /*************************************************************************/
  4. /* This file is part of: */
  5. /* GODOT ENGINE */
  6. /* http://www.godotengine.org */
  7. /*************************************************************************/
  8. /* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
  9. /* */
  10. /* Permission is hereby granted, free of charge, to any person obtaining */
  11. /* a copy of this software and associated documentation files (the */
  12. /* "Software"), to deal in the Software without restriction, including */
  13. /* without limitation the rights to use, copy, modify, merge, publish, */
  14. /* distribute, sublicense, and/or sell copies of the Software, and to */
  15. /* permit persons to whom the Software is furnished to do so, subject to */
  16. /* the following conditions: */
  17. /* */
  18. /* The above copyright notice and this permission notice shall be */
  19. /* included in all copies or substantial portions of the Software. */
  20. /* */
  21. /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
  22. /* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
  23. /* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
  24. /* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
  25. /* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
  26. /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
  27. /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
  28. /*************************************************************************/
  29. #include "version.h"
  30. #include "export.h"
  31. #include "tools/editor/editor_settings.h"
  32. #include "tools/editor/editor_import_export.h"
  33. #include "tools/editor/editor_node.h"
  34. #include "io/zip_io.h"
  35. #include "io/marshalls.h"
  36. #include "io/resource_saver.h"
  37. #include "globals.h"
  38. #include "os/file_access.h"
  39. #include "os/os.h"
  40. #include "platform/osx/logo.h"
  41. #include "string.h"
  42. class EditorExportPlatformOSX : public EditorExportPlatform {
  43. GDCLASS( EditorExportPlatformOSX,EditorExportPlatform );
  44. String custom_release_package;
  45. String custom_debug_package;
  46. enum BitsMode {
  47. BITS_FAT,
  48. BITS_64,
  49. BITS_32
  50. };
  51. int version_code;
  52. String app_name;
  53. String info;
  54. String icon;
  55. String identifier;
  56. String short_version;
  57. String version;
  58. String signature;
  59. String copyright;
  60. BitsMode bits_mode;
  61. bool high_resolution;
  62. Ref<ImageTexture> logo;
  63. void _fix_plist(Vector<uint8_t>& plist, const String &p_binary);
  64. void _make_icon(const Image& p_icon,Vector<uint8_t>& data);
  65. protected:
  66. bool _set(const StringName& p_name, const Variant& p_value);
  67. bool _get(const StringName& p_name,Variant &r_ret) const;
  68. void _get_property_list( List<PropertyInfo> *p_list) const;
  69. public:
  70. virtual String get_name() const { return "Mac OSX"; }
  71. virtual ImageCompression get_image_compression() const { return IMAGE_COMPRESSION_BC; }
  72. virtual Ref<Texture> get_logo() const { return logo; }
  73. virtual bool poll_devices() { return false;}
  74. virtual int get_device_count() const { return 0; }
  75. virtual String get_device_name(int p_device) const { return String(); }
  76. virtual String get_device_info(int p_device) const { return String(); }
  77. virtual Error run(int p_device,int p_flags=0);
  78. virtual bool requires_password(bool p_debug) const { return false; }
  79. virtual String get_binary_extension() const { return "zip"; }
  80. virtual Error export_project(const String& p_path,bool p_debug,int p_flags=0);
  81. virtual bool can_export(String *r_error=NULL) const;
  82. EditorExportPlatformOSX();
  83. ~EditorExportPlatformOSX();
  84. };
  85. bool EditorExportPlatformOSX::_set(const StringName& p_name, const Variant& p_value) {
  86. String n=p_name;
  87. if (n=="custom_package/debug")
  88. custom_debug_package=p_value;
  89. else if (n=="custom_package/release")
  90. custom_release_package=p_value;
  91. else if (n=="application/name")
  92. app_name=p_value;
  93. else if (n=="application/info")
  94. info=p_value;
  95. else if (n=="application/icon")
  96. icon=p_value;
  97. else if (n=="application/identifier")
  98. identifier=p_value;
  99. else if (n=="application/signature")
  100. signature=p_value;
  101. else if (n=="application/short_version")
  102. short_version=p_value;
  103. else if (n=="application/version")
  104. version=p_value;
  105. else if (n=="application/copyright")
  106. copyright=p_value;
  107. else if (n=="application/bits_mode")
  108. bits_mode=BitsMode(int(p_value));
  109. else if (n=="display/high_res")
  110. high_resolution=p_value;
  111. else
  112. return false;
  113. return true;
  114. }
  115. bool EditorExportPlatformOSX::_get(const StringName& p_name,Variant &r_ret) const{
  116. String n=p_name;
  117. if (n=="custom_package/debug")
  118. r_ret=custom_debug_package;
  119. else if (n=="custom_package/release")
  120. r_ret=custom_release_package;
  121. else if (n=="application/name")
  122. r_ret=app_name;
  123. else if (n=="application/info")
  124. r_ret=info;
  125. else if (n=="application/icon")
  126. r_ret=icon;
  127. else if (n=="application/identifier")
  128. r_ret=identifier;
  129. else if (n=="application/signature")
  130. r_ret=signature;
  131. else if (n=="application/short_version")
  132. r_ret=short_version;
  133. else if (n=="application/version")
  134. r_ret=version;
  135. else if (n=="application/copyright")
  136. r_ret=copyright;
  137. else if (n=="application/bits_mode")
  138. r_ret=bits_mode;
  139. else if (n=="display/high_res")
  140. r_ret=high_resolution;
  141. else
  142. return false;
  143. return true;
  144. }
  145. void EditorExportPlatformOSX::_get_property_list( List<PropertyInfo> *p_list) const{
  146. p_list->push_back( PropertyInfo( Variant::STRING, "custom_package/debug", PROPERTY_HINT_GLOBAL_FILE,"zip"));
  147. p_list->push_back( PropertyInfo( Variant::STRING, "custom_package/release", PROPERTY_HINT_GLOBAL_FILE,"zip"));
  148. p_list->push_back( PropertyInfo( Variant::STRING, "application/name") );
  149. p_list->push_back( PropertyInfo( Variant::STRING, "application/info") );
  150. p_list->push_back( PropertyInfo( Variant::STRING, "application/icon",PROPERTY_HINT_FILE,"png") );
  151. p_list->push_back( PropertyInfo( Variant::STRING, "application/identifier") );
  152. p_list->push_back( PropertyInfo( Variant::STRING, "application/signature") );
  153. p_list->push_back( PropertyInfo( Variant::STRING, "application/short_version") );
  154. p_list->push_back( PropertyInfo( Variant::STRING, "application/version") );
  155. p_list->push_back( PropertyInfo( Variant::STRING, "application/copyright") );
  156. p_list->push_back( PropertyInfo( Variant::INT, "application/bits_mode", PROPERTY_HINT_ENUM, "Fat (32 & 64 bits),64 bits,32 bits") );
  157. p_list->push_back( PropertyInfo( Variant::BOOL, "display/high_res") );
  158. }
  159. void EditorExportPlatformOSX::_make_icon(const Image& p_icon,Vector<uint8_t>& icon) {
  160. Ref<ImageTexture> it = memnew( ImageTexture );
  161. int size=512;
  162. Vector<uint8_t> data;
  163. data.resize(8);
  164. data[0]='i';
  165. data[1]='c';
  166. data[2]='n';
  167. data[3]='s';
  168. const char *name[]={"ic09","ic08","ic07","icp6","icp5","icp4"};
  169. int index=0;
  170. while(size>=16) {
  171. Image copy = p_icon;
  172. copy.convert(Image::FORMAT_RGBA8);
  173. copy.resize(size,size);
  174. it->create_from_image(copy);
  175. String path = EditorSettings::get_singleton()->get_settings_path()+"/tmp/icon.png";
  176. ResourceSaver::save(path,it);
  177. FileAccess *f = FileAccess::open(path,FileAccess::READ);
  178. ERR_FAIL_COND(!f);
  179. int ofs = data.size();
  180. uint32_t len = f->get_len();
  181. data.resize(data.size()+len+8);
  182. f->get_buffer(&data[ofs+8],len);
  183. memdelete(f);
  184. len+=8;
  185. len=BSWAP32(len);
  186. copymem(&data[ofs],name[index],4);
  187. encode_uint32(len,&data[ofs+4]);
  188. index++;
  189. size/=2;
  190. }
  191. uint32_t total_len = data.size();
  192. total_len = BSWAP32(total_len);
  193. encode_uint32(total_len,&data[4]);
  194. icon=data;
  195. }
  196. void EditorExportPlatformOSX::_fix_plist(Vector<uint8_t>& plist,const String& p_binary) {
  197. String str;
  198. String strnew;
  199. str.parse_utf8((const char*)plist.ptr(),plist.size());
  200. Vector<String> lines=str.split("\n");
  201. for(int i=0;i<lines.size();i++) {
  202. if (lines[i].find("$binary")!=-1) {
  203. strnew+=lines[i].replace("$binary",p_binary)+"\n";
  204. } else if (lines[i].find("$name")!=-1) {
  205. strnew+=lines[i].replace("$name",p_binary)+"\n";
  206. } else if (lines[i].find("$info")!=-1) {
  207. strnew+=lines[i].replace("$info",info)+"\n";
  208. } else if (lines[i].find("$identifier")!=-1) {
  209. strnew+=lines[i].replace("$identifier",identifier)+"\n";
  210. } else if (lines[i].find("$short_version")!=-1) {
  211. strnew+=lines[i].replace("$short_version",short_version)+"\n";
  212. } else if (lines[i].find("$version")!=-1) {
  213. strnew+=lines[i].replace("$version",version)+"\n";
  214. } else if (lines[i].find("$signature")!=-1) {
  215. strnew+=lines[i].replace("$signature",signature)+"\n";
  216. } else if (lines[i].find("$copyright")!=-1) {
  217. strnew+=lines[i].replace("$copyright",copyright)+"\n";
  218. } else if (lines[i].find("$highres")!=-1) {
  219. strnew+=lines[i].replace("$highres",high_resolution?"<true/>":"<false/>")+"\n";
  220. } else {
  221. strnew+=lines[i]+"\n";
  222. }
  223. }
  224. CharString cs = strnew.utf8();
  225. plist.resize(cs.size());
  226. for(int i=9;i<cs.size();i++) {
  227. plist[i]=cs[i];
  228. }
  229. }
  230. Error EditorExportPlatformOSX::export_project(const String& p_path, bool p_debug, int p_flags) {
  231. String src_pkg;
  232. EditorProgress ep("export","Exporting for OSX",104);
  233. if (p_debug)
  234. src_pkg=custom_debug_package;
  235. else
  236. src_pkg=custom_release_package;
  237. if (src_pkg=="") {
  238. String err;
  239. src_pkg=find_export_template("osx.zip", &err);
  240. if (src_pkg=="") {
  241. EditorNode::add_io_error(err);
  242. return ERR_FILE_NOT_FOUND;
  243. }
  244. }
  245. FileAccess *src_f=NULL;
  246. zlib_filefunc_def io = zipio_create_io_from_file(&src_f);
  247. ep.step("Creating app",0);
  248. unzFile pkg = unzOpen2(src_pkg.utf8().get_data(), &io);
  249. if (!pkg) {
  250. EditorNode::add_io_error("Could not find template app to export:\n"+src_pkg);
  251. return ERR_FILE_NOT_FOUND;
  252. }
  253. ERR_FAIL_COND_V(!pkg, ERR_CANT_OPEN);
  254. int ret = unzGoToFirstFile(pkg);
  255. zlib_filefunc_def io2=io;
  256. FileAccess *dst_f=NULL;
  257. io2.opaque=&dst_f;
  258. zipFile dpkg=zipOpen2(p_path.utf8().get_data(),APPEND_STATUS_CREATE,NULL,&io2);
  259. String binary_to_use = "godot_osx_" + String(p_debug ? "debug" : "release") + ".";
  260. binary_to_use += String(bits_mode==BITS_FAT ? "fat" : bits_mode==BITS_64 ? "64" : "32");
  261. print_line("binary: "+binary_to_use);
  262. String pkg_name;
  263. if (app_name!="")
  264. pkg_name=app_name;
  265. else if (String(GlobalConfig::get_singleton()->get("application/name"))!="")
  266. pkg_name=String(GlobalConfig::get_singleton()->get("application/name"));
  267. else
  268. pkg_name="Unnamed";
  269. bool found_binary = false;
  270. while(ret==UNZ_OK) {
  271. //get filename
  272. unz_file_info info;
  273. char fname[16384];
  274. ret = unzGetCurrentFileInfo(pkg,&info,fname,16384,NULL,0,NULL,0);
  275. String file=fname;
  276. print_line("READ: "+file);
  277. Vector<uint8_t> data;
  278. data.resize(info.uncompressed_size);
  279. //read
  280. unzOpenCurrentFile(pkg);
  281. unzReadCurrentFile(pkg,data.ptr(),data.size());
  282. unzCloseCurrentFile(pkg);
  283. //write
  284. file = file.replace_first("osx_template.app/","");
  285. if (file=="Contents/Info.plist") {
  286. print_line("parse plist");
  287. _fix_plist(data,pkg_name);
  288. }
  289. if (file.begins_with("Contents/MacOS/godot_")) {
  290. if (file!="Contents/MacOS/"+binary_to_use) {
  291. ret = unzGoToNextFile(pkg);
  292. continue; //ignore!
  293. }
  294. found_binary = true;
  295. file="Contents/MacOS/"+pkg_name;
  296. }
  297. if (file=="Contents/Resources/icon.icns") {
  298. //see if there is an icon
  299. String iconpath = GlobalConfig::get_singleton()->get("application/icon");
  300. print_line("icon? "+iconpath);
  301. if (iconpath!="") {
  302. Image icon;
  303. icon.load(iconpath);
  304. if (!icon.empty()) {
  305. print_line("loaded?");
  306. _make_icon(icon,data);
  307. }
  308. }
  309. //bleh?
  310. }
  311. file=pkg_name+".app/"+file;
  312. if (data.size()>0) {
  313. print_line("ADDING: "+file+" size: "+itos(data.size()));
  314. zip_fileinfo fi;
  315. fi.tmz_date.tm_hour=info.tmu_date.tm_hour;
  316. fi.tmz_date.tm_min=info.tmu_date.tm_min;
  317. fi.tmz_date.tm_sec=info.tmu_date.tm_sec;
  318. fi.tmz_date.tm_mon=info.tmu_date.tm_mon;
  319. fi.tmz_date.tm_mday=info.tmu_date.tm_mday;
  320. fi.tmz_date.tm_year=info.tmu_date.tm_year;
  321. fi.dosDate=info.dosDate;
  322. fi.internal_fa=info.internal_fa;
  323. fi.external_fa=info.external_fa;
  324. int err = zipOpenNewFileInZip(dpkg,
  325. file.utf8().get_data(),
  326. &fi,
  327. NULL,
  328. 0,
  329. NULL,
  330. 0,
  331. NULL,
  332. Z_DEFLATED,
  333. Z_DEFAULT_COMPRESSION);
  334. print_line("OPEN ERR: "+itos(err));
  335. err = zipWriteInFileInZip(dpkg,data.ptr(),data.size());
  336. print_line("WRITE ERR: "+itos(err));
  337. zipCloseFileInZip(dpkg);
  338. }
  339. ret = unzGoToNextFile(pkg);
  340. }
  341. if (!found_binary) {
  342. ERR_PRINTS("Requested template binary '"+binary_to_use+"' not found. It might be missing from your template archive.");
  343. zipClose(dpkg,NULL);
  344. unzClose(pkg);
  345. return ERR_FILE_NOT_FOUND;
  346. }
  347. ep.step("Making PKG",1);
  348. String pack_path=EditorSettings::get_singleton()->get_settings_path()+"/tmp/data.pck";
  349. FileAccess *pfs = FileAccess::open(pack_path,FileAccess::WRITE);
  350. Error err = save_pack(pfs);
  351. memdelete(pfs);
  352. if (err) {
  353. zipClose(dpkg,NULL);
  354. unzClose(pkg);
  355. return err;
  356. }
  357. {
  358. //write datapack
  359. zipOpenNewFileInZip(dpkg,
  360. (pkg_name+".app/Contents/Resources/data.pck").utf8().get_data(),
  361. NULL,
  362. NULL,
  363. 0,
  364. NULL,
  365. 0,
  366. NULL,
  367. Z_DEFLATED,
  368. Z_DEFAULT_COMPRESSION);
  369. FileAccess *pf = FileAccess::open(pack_path,FileAccess::READ);
  370. ERR_FAIL_COND_V(!pf,ERR_CANT_OPEN);
  371. const int BSIZE = 16384;
  372. uint8_t buf[BSIZE];
  373. while(true) {
  374. int r = pf->get_buffer(buf,BSIZE);
  375. if (r<=0)
  376. break;
  377. zipWriteInFileInZip(dpkg,buf,r);
  378. }
  379. zipCloseFileInZip(dpkg);
  380. memdelete(pf);
  381. }
  382. zipClose(dpkg,NULL);
  383. unzClose(pkg);
  384. return OK;
  385. }
  386. Error EditorExportPlatformOSX::run(int p_device, int p_flags) {
  387. return OK;
  388. }
  389. EditorExportPlatformOSX::EditorExportPlatformOSX() {
  390. Image img( _osx_logo );
  391. logo = Ref<ImageTexture>( memnew( ImageTexture ));
  392. logo->create_from_image(img);
  393. info="Made with Godot Engine";
  394. identifier="org.godotengine.macgame";
  395. signature="godotmacgame";
  396. short_version="1.0";
  397. version="1.0";
  398. bits_mode=BITS_FAT;
  399. high_resolution=false;
  400. }
  401. bool EditorExportPlatformOSX::can_export(String *r_error) const {
  402. bool valid=true;
  403. String err;
  404. if (!exists_export_template("osx.zip")) {
  405. valid=false;
  406. err+="No export templates found.\nDownload and install export templates.\n";
  407. }
  408. if (custom_debug_package!="" && !FileAccess::exists(custom_debug_package)) {
  409. valid=false;
  410. err+="Custom debug package not found.\n";
  411. }
  412. if (custom_release_package!="" && !FileAccess::exists(custom_release_package)) {
  413. valid=false;
  414. err+="Custom release package not found.\n";
  415. }
  416. if (r_error)
  417. *r_error=err;
  418. return valid;
  419. }
  420. EditorExportPlatformOSX::~EditorExportPlatformOSX() {
  421. }
  422. void register_osx_exporter() {
  423. Ref<EditorExportPlatformOSX> exporter = Ref<EditorExportPlatformOSX>( memnew(EditorExportPlatformOSX) );
  424. EditorImportExport::get_singleton()->add_export_platform(exporter);
  425. }