From bb1b51f470b20aefa8bc1e401dcf76844c7609d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pedro=20J=2E=20Est=C3=A9banez?= Date: Sat, 11 Jun 2016 13:08:42 +0200 Subject: Keep certain assets uncompressed on Android export --- platform/android/export/export.cpp | 61 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 58 insertions(+), 3 deletions(-) diff --git a/platform/android/export/export.cpp b/platform/android/export/export.cpp index 060819b90e..8545db72f3 100644 --- a/platform/android/export/export.cpp +++ b/platform/android/export/export.cpp @@ -231,6 +231,7 @@ class EditorExportPlatformAndroid : public EditorExportPlatform { void _fix_manifest(Vector& p_manifest, bool p_give_internet); void _fix_resources(Vector& p_manifest); static Error save_apk_file(void *p_userdata,const String& p_path, const Vector& p_data,int p_file,int p_total); + static bool _should_compress_asset(const String& p_path); protected: @@ -1001,7 +1002,7 @@ Error EditorExportPlatformAndroid::save_apk_file(void *p_userdata,const String& NULL, 0, NULL, - Z_DEFLATED, + _should_compress_asset(p_path) ? Z_DEFLATED : 0, Z_DEFAULT_COMPRESSION); @@ -1012,6 +1013,56 @@ Error EditorExportPlatformAndroid::save_apk_file(void *p_userdata,const String& } +bool EditorExportPlatformAndroid::_should_compress_asset(const String& p_path) { + + /* + * By not compressing files with little or not benefit in doing so, + * a performance gain is expected at runtime. Moreover, if the APK is + * zip-aligned, assets stored as they are can be efficiently read by + * Android by memory-mapping them. + */ + + // -- Unconditional uncompress to mimic AAPT plus some other + + static const char* unconditional_compress_ext[] = { + // From https://github.com/android/platform_frameworks_base/blob/master/tools/aapt/Package.cpp + // These formats are already compressed, or don't compress well: + ".jpg", ".jpeg", ".png", ".gif", + ".wav", ".mp2", ".mp3", ".ogg", ".aac", + ".mpg", ".mpeg", ".mid", ".midi", ".smf", ".jet", + ".rtttl", ".imy", ".xmf", ".mp4", ".m4a", + ".m4v", ".3gp", ".3gpp", ".3g2", ".3gpp2", + ".amr", ".awb", ".wma", ".wmv", + // Godot-specific: + ".webp", // Same reasoning as .png + ".cfb", // Don't let small config files slow-down startup + // Trailer for easier processing + NULL + }; + + for (const char** ext=unconditional_compress_ext; *ext; ++ext) { + if (p_path.to_lower().ends_with(String(*ext))) { + return false; + } + } + + // -- Compressed resource? + + FileAccess *f=FileAccess::open(p_path,FileAccess::READ); + ERR_FAIL_COND_V(!f,true); + + uint8_t header[4]; + f->get_buffer(header,4); + if (header[0]=='R' && header[1]=='S' && header[2]=='C' && header[3]=='C') { + // Already compressed + return false; + } + + // --- TODO: Decide on texture resources according to their image compression setting + + return true; +} + Error EditorExportPlatformAndroid::export_project(const String& p_path, bool p_debug, int p_flags) { @@ -1137,6 +1188,10 @@ Error EditorExportPlatformAndroid::export_project(const String& p_path, bool p_d print_line("ADDING: "+file); if (!skip) { + + // Respect decision on compression made by AAPT for the export template + const bool uncompressed = info.compression_method == 0; + zipOpenNewFileInZip(apk, file.utf8().get_data(), NULL, @@ -1145,7 +1200,7 @@ Error EditorExportPlatformAndroid::export_project(const String& p_path, bool p_d NULL, 0, NULL, - Z_DEFLATED, + uncompressed ? 0 : Z_DEFLATED, Z_DEFAULT_COMPRESSION); zipWriteInFileInZip(apk,data.ptr(),data.size()); @@ -1243,7 +1298,7 @@ Error EditorExportPlatformAndroid::export_project(const String& p_path, bool p_d NULL, 0, NULL, - Z_DEFLATED, + 0, // No compress (little size gain and potentially slower startup) Z_DEFAULT_COMPRESSION); zipWriteInFileInZip(apk,clf.ptr(),clf.size()); -- cgit v1.2.3 From 1b189ad9435b6b119a594c4972aee88bc3ca3e29 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pedro=20J=2E=20Est=C3=A9banez?= Date: Sat, 11 Jun 2016 13:11:42 +0200 Subject: Fix typo --- platform/android/export/export.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/android/export/export.cpp b/platform/android/export/export.cpp index 8545db72f3..069edb7975 100644 --- a/platform/android/export/export.cpp +++ b/platform/android/export/export.cpp @@ -1381,7 +1381,7 @@ Error EditorExportPlatformAndroid::export_project(const String& p_path, bool p_d err = OS::get_singleton()->execute(jarsigner,args,true,NULL,NULL,&retval); if (retval) { - EditorNode::add_io_error("'jarsigner' verificaiton of APK failed. Make sure to use jarsigner from Java 6."); + EditorNode::add_io_error("'jarsigner' verification of APK failed. Make sure to use jarsigner from Java 6."); return ERR_CANT_CREATE; } -- cgit v1.2.3 From e1948d520af3df173e33bc46087b1717215293dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pedro=20J=2E=20Est=C3=A9banez?= Date: Mon, 13 Jun 2016 00:19:45 +0200 Subject: Zip-align exported APK --- core/io/unzip.c | 17 ++++-- platform/android/export/export.cpp | 112 +++++++++++++++++++++++++++++++++---- 2 files changed, 113 insertions(+), 16 deletions(-) diff --git a/core/io/unzip.c b/core/io/unzip.c index 78672677f9..7aa0a86d13 100644 --- a/core/io/unzip.c +++ b/core/io/unzip.c @@ -1031,10 +1031,19 @@ local int unz64local_GetCurrentFileInfoInternal (unzFile file, if (lSeek!=0) { - if (ZSEEK64(s->z_filefunc, s->filestream,lSeek,ZLIB_FILEFUNC_SEEK_CUR)==0) - lSeek=0; - else - err=UNZ_ERRNO; + if (lSeek<0) { + // WORKAROUND for backwards seeking + z_off_t pos = ZTELL64(s->z_filefunc, s->filestream); + if (ZSEEK64(s->z_filefunc, s->filestream,pos+lSeek,ZLIB_FILEFUNC_SEEK_SET)==0) + lSeek=0; + else + err=UNZ_ERRNO; + } else { + if (ZSEEK64(s->z_filefunc, s->filestream,lSeek,ZLIB_FILEFUNC_SEEK_CUR)==0) + lSeek=0; + else + err=UNZ_ERRNO; + } } while(acc < file_info.size_file_extra) diff --git a/platform/android/export/export.cpp b/platform/android/export/export.cpp index 069edb7975..b35e3a8055 100644 --- a/platform/android/export/export.cpp +++ b/platform/android/export/export.cpp @@ -9,6 +9,7 @@ #include "os/file_access.h" #include "os/os.h" #include "platform/android/logo.h" +#include static const char* android_perms[]={ @@ -1069,7 +1070,7 @@ Error EditorExportPlatformAndroid::export_project(const String& p_path, bool p_d String src_apk; - EditorProgress ep("export","Exporting for Android",104); + EditorProgress ep("export","Exporting for Android",105); if (p_debug) src_apk=custom_debug_package; @@ -1109,7 +1110,8 @@ Error EditorExportPlatformAndroid::export_project(const String& p_path, bool p_d zlib_filefunc_def io2=io; FileAccess *dst_f=NULL; io2.opaque=&dst_f; - zipFile apk=zipOpen2(p_path.utf8().get_data(),APPEND_STATUS_CREATE,NULL,&io2); + String unaligned_path=EditorSettings::get_singleton()->get_settings_path()+"/tmp/tmpexport-unaligned.apk"; + zipFile unaligned_apk=zipOpen2(unaligned_path.utf8().get_data(),APPEND_STATUS_CREATE,NULL,&io2); while(ret==UNZ_OK) { @@ -1192,7 +1194,7 @@ Error EditorExportPlatformAndroid::export_project(const String& p_path, bool p_d // Respect decision on compression made by AAPT for the export template const bool uncompressed = info.compression_method == 0; - zipOpenNewFileInZip(apk, + zipOpenNewFileInZip(unaligned_apk, file.utf8().get_data(), NULL, NULL, @@ -1203,8 +1205,8 @@ Error EditorExportPlatformAndroid::export_project(const String& p_path, bool p_d uncompressed ? 0 : Z_DEFLATED, Z_DEFAULT_COMPRESSION); - zipWriteInFileInZip(apk,data.ptr(),data.size()); - zipCloseFileInZip(apk); + zipWriteInFileInZip(unaligned_apk,data.ptr(),data.size()); + zipCloseFileInZip(unaligned_apk); } ret = unzGoToNextFile(pkg); @@ -1261,7 +1263,7 @@ Error EditorExportPlatformAndroid::export_project(const String& p_path, bool p_d APKExportData ed; ed.ep=&ep; - ed.apk=apk; + ed.apk=unaligned_apk; err = export_project_files(save_apk_file,&ed,false); } @@ -1290,7 +1292,7 @@ Error EditorExportPlatformAndroid::export_project(const String& p_path, bool p_d print_line(itos(i)+" param: "+cl[i]); } - zipOpenNewFileInZip(apk, + zipOpenNewFileInZip(unaligned_apk, "assets/_cl_", NULL, NULL, @@ -1301,12 +1303,12 @@ Error EditorExportPlatformAndroid::export_project(const String& p_path, bool p_d 0, // No compress (little size gain and potentially slower startup) Z_DEFAULT_COMPRESSION); - zipWriteInFileInZip(apk,clf.ptr(),clf.size()); - zipCloseFileInZip(apk); + zipWriteInFileInZip(unaligned_apk,clf.ptr(),clf.size()); + zipCloseFileInZip(unaligned_apk); } - zipClose(apk,NULL); + zipClose(unaligned_apk,NULL); unzClose(pkg); if (err) { @@ -1363,7 +1365,7 @@ Error EditorExportPlatformAndroid::export_project(const String& p_path, bool p_d args.push_back(keystore); args.push_back("-storepass"); args.push_back(password); - args.push_back(p_path); + args.push_back(unaligned_path); args.push_back(user); int retval; int err = OS::get_singleton()->execute(jarsigner,args,true,NULL,NULL,&retval); @@ -1376,7 +1378,7 @@ Error EditorExportPlatformAndroid::export_project(const String& p_path, bool p_d args.clear(); args.push_back("-verify"); - args.push_back(p_path); + args.push_back(unaligned_path); args.push_back("-verbose"); err = OS::get_singleton()->execute(jarsigner,args,true,NULL,NULL,&retval); @@ -1386,6 +1388,92 @@ Error EditorExportPlatformAndroid::export_project(const String& p_path, bool p_d } } + + + + // Let's zip-align (must be done after signing) + + static const int ZIP_ALIGNMENT = 4; + + ep.step("Aligning APK..",105); + + unzFile tmp_unaligned = unzOpen2(unaligned_path.utf8().get_data(), &io); + if (!tmp_unaligned) { + + EditorNode::add_io_error("Could not find temp unaligned APK."); + return ERR_FILE_NOT_FOUND; + } + + ERR_FAIL_COND_V(!tmp_unaligned, ERR_CANT_OPEN); + ret = unzGoToFirstFile(tmp_unaligned); + + io2=io; + dst_f=NULL; + io2.opaque=&dst_f; + zipFile final_apk=zipOpen2(p_path.utf8().get_data(),APPEND_STATUS_CREATE,NULL,&io2); + + // Take files from the unaligned APK and write them out to the aligned one + // in raw mode, i.e. not uncompressing and recompressing, aligning them as needed, + // following what is done in https://github.com/android/platform_build/blob/master/tools/zipalign/ZipAlign.cpp + int bias = 0; + while(ret==UNZ_OK) { + + unz_file_info info; + memset(&info, 0, sizeof(info)); + + char fname[16384]; + char extra[16384]; + ret = unzGetCurrentFileInfo(tmp_unaligned,&info,fname,16384,extra,16384-ZIP_ALIGNMENT,NULL,0); + + String file=fname; + + Vector data; + data.resize(info.compressed_size); + + // read + int method, level; + unzOpenCurrentFile2(tmp_unaligned, &method, &level, 1); // raw read + long file_offset = unzGetCurrentFileZStreamPos64(tmp_unaligned); + unzReadCurrentFile(tmp_unaligned,data.ptr(),data.size()); + unzCloseCurrentFile(tmp_unaligned); + + // align + int padding = 0; + if (!info.compression_method) { + // Uncompressed file => Align + long new_offset = file_offset + bias; + padding = (ZIP_ALIGNMENT - (new_offset % ZIP_ALIGNMENT)) % ZIP_ALIGNMENT; + } + + memset(extra + info.size_file_extra, 0, padding); + + // write + zipOpenNewFileInZip2(final_apk, + file.utf8().get_data(), + NULL, + extra, + info.size_file_extra + padding, + NULL, + 0, + NULL, + method, + level, + 1); // raw write + zipWriteInFileInZip(final_apk,data.ptr(),data.size()); + zipCloseFileInZipRaw(final_apk,info.uncompressed_size,info.crc); + + bias += padding; + + ret = unzGoToNextFile(tmp_unaligned); + } + + zipClose(final_apk,NULL); + unzClose(tmp_unaligned); + + if (err) { + return err; + } + return OK; } -- cgit v1.2.3