diff --git a/core/io/file_access_pack.cpp b/core/io/file_access_pack.cpp index 9e9e5e74b5..f1124fd539 100644 --- a/core/io/file_access_pack.cpp +++ b/core/io/file_access_pack.cpp @@ -36,9 +36,9 @@ #include "core/os/os.h" #include "core/version.h" -Error PackedData::add_pack(const String &p_path, bool p_replace_files, uint64_t p_offset) { +Error PackedData::add_pack(const String &p_path, bool p_replace_files, uint64_t p_offset, const Vector &p_decryption_key) { for (int i = 0; i < sources.size(); i++) { - if (sources[i]->try_open_pack(p_path, p_replace_files, p_offset)) { + if (sources[i]->try_open_pack(p_path, p_replace_files, p_offset, p_decryption_key)) { return OK; } } @@ -215,7 +215,7 @@ PackedData::~PackedData() { ////////////////////////////////////////////////////////////////// -bool PackedSourcePCK::try_open_pack(const String &p_path, bool p_replace_files, uint64_t p_offset) { +bool PackedSourcePCK::try_open_pack(const String &p_path, bool p_replace_files, uint64_t p_offset, const Vector &p_decryption_key) { Ref f = FileAccess::open(p_path, FileAccess::READ); if (f.is_null()) { return false; @@ -330,9 +330,18 @@ bool PackedSourcePCK::try_open_pack(const String &p_path, bool p_replace_files, ERR_FAIL_COND_V_MSG(fae.is_null(), false, "Can't open encrypted pack directory."); Vector key; - key.resize(32); - for (int i = 0; i < key.size(); i++) { - key.write[i] = script_encryption_key[i]; +#ifdef TOOLS_ENABLED + if (!p_decryption_key.is_empty()) { + ERR_FAIL_COND_V_MSG(p_decryption_key.size() != 32, false, "Decryption key must be 256-bit."); + constexpr uint8_t empty_key[32] = {}; + if (memcmp(script_encryption_key, empty_key, sizeof(empty_key)) == 0) { + key = p_decryption_key; + } + } else +#endif + { + key.resize(32); + memcpy(key.ptrw(), script_encryption_key, 32); } Error err = fae->open_and_parse(f, key, FileAccessEncrypted::MODE_READ, false); @@ -364,8 +373,8 @@ bool PackedSourcePCK::try_open_pack(const String &p_path, bool p_replace_files, return true; } -Ref PackedSourcePCK::get_file(const String &p_path, PackedData::PackedFile *p_file) { - Ref file(memnew(FileAccessPack(p_path, *p_file))); +Ref PackedSourcePCK::get_file(const String &p_path, PackedData::PackedFile *p_file, const Vector &p_decryption_key) { + Ref file(memnew(FileAccessPack(p_path, *p_file, p_decryption_key))); if (PackedData::get_singleton()->has_delta_patches(p_path)) { Ref file_patched; @@ -380,7 +389,7 @@ Ref PackedSourcePCK::get_file(const String &p_path, PackedData::Pack ////////////////////////////////////////////////////////////////// -bool PackedSourceDirectory::try_open_pack(const String &p_path, bool p_replace_files, uint64_t p_offset) { +bool PackedSourceDirectory::try_open_pack(const String &p_path, bool p_replace_files, uint64_t p_offset, const Vector &p_decryption_key) { // Load with offset feature only supported for PCK files. ERR_FAIL_COND_V_MSG(p_offset != 0, false, "Invalid PCK data. Note that loading files with a non-zero offset isn't supported with directories."); @@ -391,7 +400,7 @@ bool PackedSourceDirectory::try_open_pack(const String &p_path, bool p_replace_f return true; } -Ref PackedSourceDirectory::get_file(const String &p_path, PackedData::PackedFile *p_file) { +Ref PackedSourceDirectory::get_file(const String &p_path, PackedData::PackedFile *p_file, const Vector &p_decryption_key) { Ref ret = FileAccess::create_for_path(p_path); ret->reopen(p_path, FileAccess::READ); return ret; @@ -514,16 +523,24 @@ void FileAccessPack::close() { f = Ref(); } -FileAccessPack::FileAccessPack(const String &p_path, const PackedData::PackedFile &p_file) { +FileAccessPack::FileAccessPack(const String &p_path, const PackedData::PackedFile &p_file, const Vector &p_decryption_key) { path = p_path; pf = p_file; if (pf.bundle) { String simplified_path = p_path.simplify_path(); - if (pf.salt.is_empty()) { - f = FileAccess::open(simplified_path, FileAccess::READ | FileAccess::SKIP_PACK); + String path_to_load = simplified_path; +#ifdef TOOLS_ENABLED + if (!pf.salt.is_empty()) { + path_to_load = pf.pack.get_base_dir().path_join((simplified_path + pf.salt).sha256_text()); } else { - f = FileAccess::open("res://" + (simplified_path + pf.salt).sha256_text(), FileAccess::READ | FileAccess::SKIP_PACK); + path_to_load = pf.pack.get_base_dir().path_join(simplified_path.replace("res://", "")); } +#else + if (!pf.salt.is_empty()) { + path_to_load = "res://" + (simplified_path + pf.salt).sha256_text(); + } +#endif + f = FileAccess::open(path_to_load, FileAccess::READ | FileAccess::SKIP_PACK); ERR_FAIL_COND_MSG(f.is_null(), vformat(R"(Can't open pack-referenced file "%s" from sparse pack "%s".)", simplified_path, pf.pack)); off = 0; // For the sparse pack offset is always zero. } else { @@ -539,9 +556,18 @@ FileAccessPack::FileAccessPack(const String &p_path, const PackedData::PackedFil ERR_FAIL_COND_MSG(fae.is_null(), vformat(R"(Can't open encrypted pack-referenced file "%s" from pack "%s".)", p_path, pf.pack)); Vector key; - key.resize(32); - for (int i = 0; i < key.size(); i++) { - key.write[i] = script_encryption_key[i]; +#ifdef TOOLS_ENABLED + if (!p_decryption_key.is_empty()) { + ERR_FAIL_COND_MSG(p_decryption_key.size() != 32, "Decryption key must be 256-bit."); + constexpr uint8_t empty_key[32] = {}; + if (memcmp(script_encryption_key, empty_key, sizeof(empty_key)) == 0) { + key = p_decryption_key; + } + } else +#endif + { + key.resize(32); + memcpy(key.ptrw(), script_encryption_key, 32); } Error err = fae->open_and_parse(f, key, FileAccessEncrypted::MODE_READ, false); diff --git a/core/io/file_access_pack.h b/core/io/file_access_pack.h index 3e43b74193..a54d395170 100644 --- a/core/io/file_access_pack.h +++ b/core/io/file_access_pack.h @@ -132,11 +132,11 @@ public: _FORCE_INLINE_ bool is_disabled() const { return disabled; } static PackedData *get_singleton() { return singleton; } - Error add_pack(const String &p_path, bool p_replace_files, uint64_t p_offset); + Error add_pack(const String &p_path, bool p_replace_files, uint64_t p_offset, const Vector &p_decryption_key = Vector()); void clear(); - _FORCE_INLINE_ Ref try_open_path(const String &p_path); + _FORCE_INLINE_ Ref try_open_path(const String &p_path, const Vector &p_decryption_key = Vector()); _FORCE_INLINE_ bool has_path(const String &p_path); _FORCE_INLINE_ int64_t get_size(const String &p_path); @@ -150,23 +150,23 @@ public: class PackSource { public: - virtual bool try_open_pack(const String &p_path, bool p_replace_files, uint64_t p_offset) = 0; - virtual Ref get_file(const String &p_path, PackedData::PackedFile *p_file) = 0; + virtual bool try_open_pack(const String &p_path, bool p_replace_files, uint64_t p_offset, const Vector &p_decryption_key = Vector()) = 0; + virtual Ref get_file(const String &p_path, PackedData::PackedFile *p_file, const Vector &p_decryption_key = Vector()) = 0; virtual ~PackSource() {} }; class PackedSourcePCK : public PackSource { public: - virtual bool try_open_pack(const String &p_path, bool p_replace_files, uint64_t p_offset) override; - virtual Ref get_file(const String &p_path, PackedData::PackedFile *p_file) override; + virtual bool try_open_pack(const String &p_path, bool p_replace_files, uint64_t p_offset, const Vector &p_decryption_key = Vector()) override; + virtual Ref get_file(const String &p_path, PackedData::PackedFile *p_file, const Vector &p_decryption_key = Vector()) override; }; class PackedSourceDirectory : public PackSource { void add_directory(const String &p_path, bool p_replace_files); public: - virtual bool try_open_pack(const String &p_path, bool p_replace_files, uint64_t p_offset) override; - virtual Ref get_file(const String &p_path, PackedData::PackedFile *p_file) override; + virtual bool try_open_pack(const String &p_path, bool p_replace_files, uint64_t p_offset, const Vector &p_decryption_key = Vector()) override; + virtual Ref get_file(const String &p_path, PackedData::PackedFile *p_file, const Vector &p_decryption_key = Vector()) override; }; class FileAccessPack : public FileAccess { @@ -218,7 +218,7 @@ public: virtual void close() override; - FileAccessPack(const String &p_path, const PackedData::PackedFile &p_file); + FileAccessPack(const String &p_path, const PackedData::PackedFile &p_file, const Vector &p_decryption_key = Vector()); }; int64_t PackedData::get_size(const String &p_path) { @@ -234,7 +234,7 @@ int64_t PackedData::get_size(const String &p_path) { return E->value.size; } -Ref PackedData::try_open_path(const String &p_path) { +Ref PackedData::try_open_path(const String &p_path, const Vector &p_decryption_key) { String simplified_path = p_path.simplify_path().trim_prefix("res://"); PathMD5 pmd5(simplified_path.md5_buffer()); HashMap::Iterator E = files.find(pmd5); @@ -242,7 +242,7 @@ Ref PackedData::try_open_path(const String &p_path) { return nullptr; // Not found. } - return E->value.src->get_file(p_path, &E->value); + return E->value.src->get_file(p_path, &E->value, p_decryption_key); } bool PackedData::has_path(const String &p_path) { diff --git a/core/io/file_access_zip.cpp b/core/io/file_access_zip.cpp index 33bceee52d..2bb9db761d 100644 --- a/core/io/file_access_zip.cpp +++ b/core/io/file_access_zip.cpp @@ -144,7 +144,7 @@ unzFile ZipArchive::get_file_handle(const String &p_file) const { return pkg; } -bool ZipArchive::try_open_pack(const String &p_path, bool p_replace_files, uint64_t p_offset = 0) { +bool ZipArchive::try_open_pack(const String &p_path, bool p_replace_files, uint64_t p_offset, const Vector &p_decryption_key) { // load with offset feature only supported for PCK files ERR_FAIL_COND_V_MSG(p_offset != 0, false, "Invalid PCK data. Note that loading files with a non-zero offset isn't supported with ZIP archives."); @@ -209,7 +209,7 @@ bool ZipArchive::file_exists(const String &p_name) const { return files.has(p_name); } -Ref ZipArchive::get_file(const String &p_path, PackedData::PackedFile *p_file) { +Ref ZipArchive::get_file(const String &p_path, PackedData::PackedFile *p_file, const Vector &p_decryption_key) { return memnew(FileAccessZip(p_path, *p_file)); } diff --git a/core/io/file_access_zip.h b/core/io/file_access_zip.h index a352322886..3cdd1643b5 100644 --- a/core/io/file_access_zip.h +++ b/core/io/file_access_zip.h @@ -61,8 +61,8 @@ public: bool file_exists(const String &p_name) const; - virtual bool try_open_pack(const String &p_path, bool p_replace_files, uint64_t p_offset) override; - Ref get_file(const String &p_path, PackedData::PackedFile *p_file) override; + virtual bool try_open_pack(const String &p_path, bool p_replace_files, uint64_t p_offset, const Vector &p_decryption_key = Vector()) override; + Ref get_file(const String &p_path, PackedData::PackedFile *p_file, const Vector &p_decryption_key = Vector()) override; static ZipArchive *get_singleton(); diff --git a/editor/export/editor_export_platform.cpp b/editor/export/editor_export_platform.cpp index b2ff4d6feb..d5dcbd1372 100644 --- a/editor/export/editor_export_platform.cpp +++ b/editor/export/editor_export_platform.cpp @@ -220,22 +220,154 @@ bool EditorExportPlatform::fill_log_messages(RichTextLabel *p_log, Error p_err) return has_messages; } -Error EditorExportPlatform::_load_patches(const Vector &p_patches) { +Error EditorExportPlatform::_extract_android_assets(const String &p_bundle_path, String &r_pck_path, String &r_temp_dir) { Error err = OK; + + Ref io_fa; + zlib_filefunc_def io = zipio_create_io(&io_fa); + unzFile zip_file = unzOpen2(p_bundle_path.utf8().get_data(), &io); + if (!zip_file) { + return ERR_FILE_CANT_OPEN; + } + + const char *pck_name = "assets.sparsepck"; + String pck_base_dir; + + int ret = unzGoToFirstFile(zip_file); + while (ret == UNZ_OK) { + unz_file_info64 file_info = {}; + char file_name_buf[16384]; + ret = unzGetCurrentFileInfo64(zip_file, &file_info, file_name_buf, sizeof(file_name_buf), nullptr, 0, nullptr, 0); + if (ret != UNZ_OK) { + break; + } + + String file_name = String::utf8(file_name_buf); + if (file_name.ends_with(pck_name)) { + pck_base_dir = file_name.trim_suffix(pck_name); + break; + } + + ret = unzGoToNextFile(zip_file); + } + + if (ret != UNZ_OK || pck_base_dir.is_empty()) { + unzClose(zip_file); + return ERR_FILE_UNRECOGNIZED; + } + + Ref temp_dir = DirAccess::create_temp("export_patch_base", true, &err); + if (err != OK) { + unzClose(zip_file); + return err; + } + + String temp_dir_path = temp_dir->get_current_dir(); + + ret = unzGoToFirstFile(zip_file); + while (ret == UNZ_OK) { + unz_file_info64 zip_file_info = {}; + char file_name_buf[16384]; + if (unzGetCurrentFileInfo64(zip_file, &zip_file_info, file_name_buf, sizeof(file_name_buf), nullptr, 0, nullptr, 0) != UNZ_OK) { + err = ERR_FILE_CORRUPT; + break; + } + + String file_name = String::utf8(file_name_buf); + if (!file_name.begins_with(pck_base_dir)) { + ret = unzGoToNextFile(zip_file); + continue; + } + + String file_path_relative = file_name.trim_prefix(pck_base_dir).simplify_path(); + if (file_path_relative.is_empty()) { + ret = unzGoToNextFile(zip_file); + continue; + } + + String file_output_path = temp_dir_path.path_join(file_path_relative); + err = DirAccess::make_dir_recursive_absolute(file_output_path.get_base_dir()); + if (err != OK) { + break; + } + + if (unzOpenCurrentFile(zip_file) != UNZ_OK) { + err = ERR_FILE_CANT_OPEN; + break; + } + + LocalVector uncomp_data; + uncomp_data.resize(zip_file_info.uncompressed_size); + int read_bytes = unzReadCurrentFile(zip_file, uncomp_data.ptr(), uncomp_data.size()); + unzCloseCurrentFile(zip_file); + + if (read_bytes < 0 || read_bytes != (int)uncomp_data.size()) { + err = ERR_FILE_CANT_READ; + break; + } + + Ref temp_file = FileAccess::open(file_output_path, FileAccess::WRITE, &err); + if (err != OK) { + break; + } + + if (!temp_file->store_buffer(uncomp_data.ptr(), uncomp_data.size())) { + err = ERR_FILE_CANT_WRITE; + break; + } + + ret = unzGoToNextFile(zip_file); + } + + unzClose(zip_file); + + r_pck_path = temp_dir_path.path_join(pck_name); + r_temp_dir = temp_dir_path; + + return err; +} + +Error EditorExportPlatform::_load_patches(const Ref &p_preset, const Vector &p_patches) { if (!p_patches.is_empty()) { for (const String &path : p_patches) { - err = PackedData::get_singleton()->add_pack(path, true, 0); + String pck_path = path; + + if (path.ends_with(".apk") || path.ends_with(".aab")) { + String temp_dir; + Error err = _extract_android_assets(path, pck_path, temp_dir); + if (err != OK) { + _unload_patches(); + add_message(EXPORT_MESSAGE_ERROR, TTR("Patch Creation"), vformat(TTR("Could not extract assets from Android bundle \"%s\", due to error \"%s\"."), path, error_names[err])); + return err; + } + + patch_temp_dirs.push_back(temp_dir); + } + + Error err = PackedData::get_singleton()->add_pack(pck_path, true, 0, _get_script_encryption_key_bytes(p_preset)); if (err != OK) { - add_message(EXPORT_MESSAGE_ERROR, TTR("Patch Creation"), vformat(TTR("Could not load patch pack with path \"%s\"."), path)); + _unload_patches(); + add_message(EXPORT_MESSAGE_ERROR, TTR("Patch Creation"), vformat(TTR("Could not load patch pack with path \"%s\"."), pck_path)); return err; } } } - return err; + + return OK; } void EditorExportPlatform::_unload_patches() { PackedData::get_singleton()->clear(); + + for (const String &temp_dir : patch_temp_dirs) { + Ref temp_dir_da = DirAccess::open(temp_dir); + if (temp_dir_da.is_valid()) { + temp_dir_da->erase_contents_recursive(); + temp_dir_da->remove(temp_dir); + } + } + + patch_temp_dirs.clear(); } Error EditorExportPlatform::_encrypt_and_store_data(Ref p_fd, const String &p_path, const Vector &p_data, const Vector &p_enc_in_filters, const Vector &p_enc_ex_filters, const Vector &p_key, uint64_t p_seed, bool &r_encrypt) { @@ -347,7 +479,7 @@ Error EditorExportPlatform::_save_pack_file(const Ref &p_pre } Error EditorExportPlatform::_save_pack_patch_file(const Ref &p_preset, void *p_userdata, const String &p_path, const Vector &p_data, int p_file, int p_total, const Vector &p_enc_in_filters, const Vector &p_enc_ex_filters, const Vector &p_key, uint64_t p_seed, bool p_delta) { - Ref old_file = PackedData::get_singleton()->try_open_path(p_path); + Ref old_file = PackedData::get_singleton()->try_open_path(p_path, _get_script_encryption_key_bytes(p_preset)); if (old_file.is_null()) { return _save_pack_file(p_preset, p_userdata, p_path, p_data, p_file, p_total, p_enc_in_filters, p_enc_ex_filters, p_key, p_seed, false); } @@ -993,7 +1125,7 @@ String EditorExportPlatform::_export_customize(const String &p_path, LocalVector return save_path.is_empty() ? p_path : save_path; } -String EditorExportPlatform::_get_script_encryption_key(const Ref &p_preset) const { +String EditorExportPlatform::_get_script_encryption_key(const Ref &p_preset) { const String from_env = OS::get_singleton()->get_environment(ENV_SCRIPT_ENCRYPTION_KEY); if (!from_env.is_empty()) { return from_env.to_lower(); @@ -1001,6 +1133,39 @@ String EditorExportPlatform::_get_script_encryption_key(const Refget_script_encryption_key().to_lower(); } +Vector EditorExportPlatform::_get_script_encryption_key_bytes(const Ref &p_preset) { + Vector key; + String script_key = _get_script_encryption_key(p_preset); + if (script_key.length() == 64) { + key.resize(32); + for (int i = 0; i < 32; i++) { + int v = 0; + if (i * 2 < script_key.length()) { + char32_t ct = script_key[i * 2]; + if (is_digit(ct)) { + ct = ct - '0'; + } else if (ct >= 'a' && ct <= 'f') { + ct = 10 + ct - 'a'; + } + v |= ct << 4; + } + + if (i * 2 + 1 < script_key.length()) { + char32_t ct = script_key[i * 2 + 1]; + if (is_digit(ct)) { + ct = ct - '0'; + } else if (ct >= 'a' && ct <= 'f') { + ct = 10 + ct - 'a'; + } + v |= ct; + } + key.write[i] = v; + } + } + + return key; +} + Dictionary EditorExportPlatform::get_internal_export_files(const Ref &p_preset, bool p_debug) { Dictionary files; @@ -1229,33 +1394,7 @@ Error EditorExportPlatform::export_project_files(const Ref & } // Get encryption key. - String script_key = _get_script_encryption_key(p_preset); - key.resize(32); - if (script_key.length() == 64) { - for (int i = 0; i < 32; i++) { - int v = 0; - if (i * 2 < script_key.length()) { - char32_t ct = script_key[i * 2]; - if (is_digit(ct)) { - ct = ct - '0'; - } else if (ct >= 'a' && ct <= 'f') { - ct = 10 + ct - 'a'; - } - v |= ct << 4; - } - - if (i * 2 + 1 < script_key.length()) { - char32_t ct = script_key[i * 2 + 1]; - if (is_digit(ct)) { - ct = ct - '0'; - } else if (ct >= 'a' && ct <= 'f') { - ct = 10 + ct - 'a'; - } - v |= ct; - } - key.write[i] = v; - } - } + key = _get_script_encryption_key_bytes(p_preset); } EditorExportSaveProxy save_proxy(p_save_func, p_remove_func != nullptr); @@ -2168,33 +2307,7 @@ Error EditorExportPlatform::save_pack(const Ref &p_preset, b Vector key; if (p_preset->get_enc_pck() && p_preset->get_enc_directory()) { - String script_key = _get_script_encryption_key(p_preset); - key.resize(32); - if (script_key.length() == 64) { - for (int i = 0; i < 32; i++) { - int v = 0; - if (i * 2 < script_key.length()) { - char32_t ct = script_key[i * 2]; - if (is_digit(ct)) { - ct = ct - '0'; - } else if (ct >= 'a' && ct <= 'f') { - ct = 10 + ct - 'a'; - } - v |= ct << 4; - } - - if (i * 2 + 1 < script_key.length()) { - char32_t ct = script_key[i * 2 + 1]; - if (is_digit(ct)) { - ct = ct - '0'; - } else if (ct >= 'a' && ct <= 'f') { - ct = 10 + ct - 'a'; - } - v |= ct; - } - key.write[i] = v; - } - } + key = _get_script_encryption_key_bytes(p_preset); } if (!_encrypt_and_store_directory(f, pd, key, p_preset->get_seed(), file_base)) { @@ -2288,7 +2401,7 @@ Error EditorExportPlatform::export_zip(const Ref &p_preset, Error EditorExportPlatform::export_pack_patch(const Ref &p_preset, bool p_debug, const String &p_path, const Vector &p_patches, BitField p_flags) { ExportNotifier notifier(*this, p_preset, p_debug, p_path, p_flags); - Error err = _load_patches(p_patches.is_empty() ? p_preset->get_patches() : p_patches); + Error err = _load_patches(p_preset, p_patches.is_empty() ? p_preset->get_patches() : p_patches); if (err != OK) { return err; } @@ -2299,7 +2412,7 @@ Error EditorExportPlatform::export_pack_patch(const Ref &p_p Error EditorExportPlatform::export_zip_patch(const Ref &p_preset, bool p_debug, const String &p_path, const Vector &p_patches, BitField p_flags) { ExportNotifier notifier(*this, p_preset, p_debug, p_path, p_flags); - Error err = _load_patches(p_patches.is_empty() ? p_preset->get_patches() : p_patches); + Error err = _load_patches(p_preset, p_patches.is_empty() ? p_preset->get_patches() : p_patches); if (err != OK) { return err; } diff --git a/editor/export/editor_export_platform.h b/editor/export/editor_export_platform.h index 8d0c29a123..8455417919 100644 --- a/editor/export/editor_export_platform.h +++ b/editor/export/editor_export_platform.h @@ -105,7 +105,8 @@ public: static bool _store_header(Ref p_fd, bool p_enc, bool p_sparse, uint64_t &r_file_base_ofs, uint64_t &r_dir_base_ofs, const String &p_salt); static bool _encrypt_and_store_directory(Ref p_fd, PackData &p_pack_data, const Vector &p_key, uint64_t p_seed, uint64_t p_file_base); static Error _encrypt_and_store_data(Ref p_fd, const String &p_path, const Vector &p_data, const Vector &p_enc_in_filters, const Vector &p_enc_ex_filters, const Vector &p_key, uint64_t p_seed, bool &r_encrypt); - String _get_script_encryption_key(const Ref &p_preset) const; + static String _get_script_encryption_key(const Ref &p_preset); + static Vector _get_script_encryption_key_bytes(const Ref &p_preset); private: struct ZipData { @@ -116,6 +117,7 @@ private: }; Vector messages; + Vector patch_temp_dirs; void _export_find_resources(EditorFileSystemDirectory *p_dir, HashSet &p_paths); void _export_find_customized_resources(const Ref &p_preset, EditorFileSystemDirectory *p_dir, EditorExportPreset::FileExportMode p_mode, HashSet &p_paths); @@ -205,7 +207,8 @@ protected: Error ssh_run_on_remote_no_wait(const String &p_host, const String &p_port, const Vector &p_ssh_args, const String &p_cmd_args, OS::ProcessID *r_pid = nullptr, int p_port_fwd = -1) const; Error ssh_push_to_remote(const String &p_host, const String &p_port, const Vector &p_scp_args, const String &p_src_file, const String &p_dst_file) const; - Error _load_patches(const Vector &p_patches); + Error _extract_android_assets(const String &p_bundle_path, String &r_pck_path, String &r_temp_dir); + Error _load_patches(const Ref &p_preset, const Vector &p_patches); void _unload_patches(); Ref _load_icon_or_splash_image(const String &p_path, Error *r_error) const; diff --git a/editor/export/editor_export_platform_extension.cpp b/editor/export/editor_export_platform_extension.cpp index 72d217c684..3a53f3c310 100644 --- a/editor/export/editor_export_platform_extension.cpp +++ b/editor/export/editor_export_platform_extension.cpp @@ -310,7 +310,7 @@ Error EditorExportPlatformExtension::export_zip(const Ref &p Error EditorExportPlatformExtension::export_pack_patch(const Ref &p_preset, bool p_debug, const String &p_path, const Vector &p_patches, BitField p_flags) { ExportNotifier notifier(*this, p_preset, p_debug, p_path, p_flags); - Error err = _load_patches(p_patches.is_empty() ? p_preset->get_patches() : p_patches); + Error err = _load_patches(p_preset, p_patches.is_empty() ? p_preset->get_patches() : p_patches); if (err != OK) { return err; } @@ -329,7 +329,7 @@ Error EditorExportPlatformExtension::export_pack_patch(const Ref &p_preset, bool p_debug, const String &p_path, const Vector &p_patches, BitField p_flags) { ExportNotifier notifier(*this, p_preset, p_debug, p_path, p_flags); - Error err = _load_patches(p_patches.is_empty() ? p_preset->get_patches() : p_patches); + Error err = _load_patches(p_preset, p_patches.is_empty() ? p_preset->get_patches() : p_patches); if (err != OK) { return err; } diff --git a/editor/export/project_export.cpp b/editor/export/project_export.cpp index c9d59c2672..40fd546823 100644 --- a/editor/export/project_export.cpp +++ b/editor/export/project_export.cpp @@ -1795,6 +1795,8 @@ ProjectExportDialog::ProjectExportDialog() { patch_dialog = memnew(EditorFileDialog); patch_dialog->add_filter("*.pck", TTR("Godot Project Pack")); + patch_dialog->add_filter("*.aab", TTR("Android App Bundle")); + patch_dialog->add_filter("*.apk", TTR("Android Package")); patch_dialog->set_access(EditorFileDialog::ACCESS_FILESYSTEM); patch_dialog->set_file_mode(EditorFileDialog::FILE_MODE_OPEN_FILE); patch_dialog->connect("file_selected", callable_mp(this, &ProjectExportDialog::_patch_file_selected));