Allow providing Android APK/AAB as base packs for patch PCKs

This commit is contained in:
Mikael Hermansson 2026-02-20 20:27:26 +01:00
parent 0df713417b
commit f21053e4ef
8 changed files with 242 additions and 98 deletions

View file

@ -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<uint8_t> &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<uint8_t> &p_decryption_key) {
Ref<FileAccess> 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<uint8_t> 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<FileAccess> PackedSourcePCK::get_file(const String &p_path, PackedData::PackedFile *p_file) {
Ref<FileAccess> file(memnew(FileAccessPack(p_path, *p_file)));
Ref<FileAccess> PackedSourcePCK::get_file(const String &p_path, PackedData::PackedFile *p_file, const Vector<uint8_t> &p_decryption_key) {
Ref<FileAccess> file(memnew(FileAccessPack(p_path, *p_file, p_decryption_key)));
if (PackedData::get_singleton()->has_delta_patches(p_path)) {
Ref<FileAccessPatched> file_patched;
@ -380,7 +389,7 @@ Ref<FileAccess> 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<uint8_t> &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<FileAccess> PackedSourceDirectory::get_file(const String &p_path, PackedData::PackedFile *p_file) {
Ref<FileAccess> PackedSourceDirectory::get_file(const String &p_path, PackedData::PackedFile *p_file, const Vector<uint8_t> &p_decryption_key) {
Ref<FileAccess> ret = FileAccess::create_for_path(p_path);
ret->reopen(p_path, FileAccess::READ);
return ret;
@ -514,16 +523,24 @@ void FileAccessPack::close() {
f = Ref<FileAccess>();
}
FileAccessPack::FileAccessPack(const String &p_path, const PackedData::PackedFile &p_file) {
FileAccessPack::FileAccessPack(const String &p_path, const PackedData::PackedFile &p_file, const Vector<uint8_t> &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<uint8_t> 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);

View file

@ -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<uint8_t> &p_decryption_key = Vector<uint8_t>());
void clear();
_FORCE_INLINE_ Ref<FileAccess> try_open_path(const String &p_path);
_FORCE_INLINE_ Ref<FileAccess> try_open_path(const String &p_path, const Vector<uint8_t> &p_decryption_key = Vector<uint8_t>());
_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<FileAccess> 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<uint8_t> &p_decryption_key = Vector<uint8_t>()) = 0;
virtual Ref<FileAccess> get_file(const String &p_path, PackedData::PackedFile *p_file, const Vector<uint8_t> &p_decryption_key = Vector<uint8_t>()) = 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<FileAccess> 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<uint8_t> &p_decryption_key = Vector<uint8_t>()) override;
virtual Ref<FileAccess> get_file(const String &p_path, PackedData::PackedFile *p_file, const Vector<uint8_t> &p_decryption_key = Vector<uint8_t>()) 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<FileAccess> 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<uint8_t> &p_decryption_key = Vector<uint8_t>()) override;
virtual Ref<FileAccess> get_file(const String &p_path, PackedData::PackedFile *p_file, const Vector<uint8_t> &p_decryption_key = Vector<uint8_t>()) 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<uint8_t> &p_decryption_key = Vector<uint8_t>());
};
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<FileAccess> PackedData::try_open_path(const String &p_path) {
Ref<FileAccess> PackedData::try_open_path(const String &p_path, const Vector<uint8_t> &p_decryption_key) {
String simplified_path = p_path.simplify_path().trim_prefix("res://");
PathMD5 pmd5(simplified_path.md5_buffer());
HashMap<PathMD5, PackedFile, PathMD5>::Iterator E = files.find(pmd5);
@ -242,7 +242,7 @@ Ref<FileAccess> 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) {

View file

@ -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<uint8_t> &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<FileAccess> ZipArchive::get_file(const String &p_path, PackedData::PackedFile *p_file) {
Ref<FileAccess> ZipArchive::get_file(const String &p_path, PackedData::PackedFile *p_file, const Vector<uint8_t> &p_decryption_key) {
return memnew(FileAccessZip(p_path, *p_file));
}

View file

@ -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<FileAccess> 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<uint8_t> &p_decryption_key = Vector<uint8_t>()) override;
Ref<FileAccess> get_file(const String &p_path, PackedData::PackedFile *p_file, const Vector<uint8_t> &p_decryption_key = Vector<uint8_t>()) override;
static ZipArchive *get_singleton();

View file

@ -220,22 +220,154 @@ bool EditorExportPlatform::fill_log_messages(RichTextLabel *p_log, Error p_err)
return has_messages;
}
Error EditorExportPlatform::_load_patches(const Vector<String> &p_patches) {
Error EditorExportPlatform::_extract_android_assets(const String &p_bundle_path, String &r_pck_path, String &r_temp_dir) {
Error err = OK;
Ref<FileAccess> 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<DirAccess> 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<uint8_t> 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<FileAccess> 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<EditorExportPreset> &p_preset, const Vector<String> &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<DirAccess> 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<FileAccess> p_fd, const String &p_path, const Vector<uint8_t> &p_data, const Vector<String> &p_enc_in_filters, const Vector<String> &p_enc_ex_filters, const Vector<uint8_t> &p_key, uint64_t p_seed, bool &r_encrypt) {
@ -347,7 +479,7 @@ Error EditorExportPlatform::_save_pack_file(const Ref<EditorExportPreset> &p_pre
}
Error EditorExportPlatform::_save_pack_patch_file(const Ref<EditorExportPreset> &p_preset, 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, uint64_t p_seed, bool p_delta) {
Ref<FileAccess> old_file = PackedData::get_singleton()->try_open_path(p_path);
Ref<FileAccess> 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<EditorExportPreset> &p_preset) const {
String EditorExportPlatform::_get_script_encryption_key(const Ref<EditorExportPreset> &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 Ref<EditorExportPr
return p_preset->get_script_encryption_key().to_lower();
}
Vector<uint8_t> EditorExportPlatform::_get_script_encryption_key_bytes(const Ref<EditorExportPreset> &p_preset) {
Vector<uint8_t> 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<EditorExportPreset> &p_preset, bool p_debug) {
Dictionary files;
@ -1229,33 +1394,7 @@ Error EditorExportPlatform::export_project_files(const Ref<EditorExportPreset> &
}
// 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<EditorExportPreset> &p_preset, b
Vector<uint8_t> 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<EditorExportPreset> &p_preset,
Error EditorExportPlatform::export_pack_patch(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, const Vector<String> &p_patches, BitField<EditorExportPlatform::DebugFlags> 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<EditorExportPreset> &p_p
Error EditorExportPlatform::export_zip_patch(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, const Vector<String> &p_patches, BitField<EditorExportPlatform::DebugFlags> 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;
}

View file

@ -105,7 +105,8 @@ public:
static bool _store_header(Ref<FileAccess> 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<FileAccess> p_fd, PackData &p_pack_data, const Vector<uint8_t> &p_key, uint64_t p_seed, uint64_t p_file_base);
static Error _encrypt_and_store_data(Ref<FileAccess> p_fd, const String &p_path, const Vector<uint8_t> &p_data, const Vector<String> &p_enc_in_filters, const Vector<String> &p_enc_ex_filters, const Vector<uint8_t> &p_key, uint64_t p_seed, bool &r_encrypt);
String _get_script_encryption_key(const Ref<EditorExportPreset> &p_preset) const;
static String _get_script_encryption_key(const Ref<EditorExportPreset> &p_preset);
static Vector<uint8_t> _get_script_encryption_key_bytes(const Ref<EditorExportPreset> &p_preset);
private:
struct ZipData {
@ -116,6 +117,7 @@ private:
};
Vector<ExportMessage> messages;
Vector<String> patch_temp_dirs;
void _export_find_resources(EditorFileSystemDirectory *p_dir, HashSet<String> &p_paths);
void _export_find_customized_resources(const Ref<EditorExportPreset> &p_preset, EditorFileSystemDirectory *p_dir, EditorExportPreset::FileExportMode p_mode, HashSet<String> &p_paths);
@ -205,7 +207,8 @@ protected:
Error ssh_run_on_remote_no_wait(const String &p_host, const String &p_port, const Vector<String> &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<String> &p_scp_args, const String &p_src_file, const String &p_dst_file) const;
Error _load_patches(const Vector<String> &p_patches);
Error _extract_android_assets(const String &p_bundle_path, String &r_pck_path, String &r_temp_dir);
Error _load_patches(const Ref<EditorExportPreset> &p_preset, const Vector<String> &p_patches);
void _unload_patches();
Ref<Image> _load_icon_or_splash_image(const String &p_path, Error *r_error) const;

View file

@ -310,7 +310,7 @@ Error EditorExportPlatformExtension::export_zip(const Ref<EditorExportPreset> &p
Error EditorExportPlatformExtension::export_pack_patch(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, const Vector<String> &p_patches, BitField<EditorExportPlatform::DebugFlags> 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<EditorExportPre
Error EditorExportPlatformExtension::export_zip_patch(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, const Vector<String> &p_patches, BitField<EditorExportPlatform::DebugFlags> 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;
}

View file

@ -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));