Browse Source

Merge pull request #97356 from reduz/pck-file-removal

Add ability for PCK patches to remove files
Thaddeus Crews 9 months ago
parent
commit
d76fbb7a40

+ 57 - 11
core/io/file_access_pack.cpp

@@ -48,7 +48,7 @@ Error PackedData::add_pack(const String &p_path, bool p_replace_files, uint64_t
 }
 }
 
 
 void PackedData::add_path(const String &p_pkg_path, const String &p_path, uint64_t p_ofs, uint64_t p_size, const uint8_t *p_md5, PackSource *p_src, bool p_replace_files, bool p_encrypted) {
 void PackedData::add_path(const String &p_pkg_path, const String &p_path, uint64_t p_ofs, uint64_t p_size, const uint8_t *p_md5, PackSource *p_src, bool p_replace_files, bool p_encrypted) {
-	String simplified_path = p_path.simplify_path();
+	String simplified_path = p_path.simplify_path().trim_prefix("res://");
 	PathMD5 pmd5(simplified_path.md5_buffer());
 	PathMD5 pmd5(simplified_path.md5_buffer());
 
 
 	bool exists = files.has(pmd5);
 	bool exists = files.has(pmd5);
@@ -68,13 +68,11 @@ void PackedData::add_path(const String &p_pkg_path, const String &p_path, uint64
 	}
 	}
 
 
 	if (!exists) {
 	if (!exists) {
-		//search for dir
-		String p = simplified_path.replace_first("res://", "");
+		// Search for directory.
 		PackedDir *cd = root;
 		PackedDir *cd = root;
 
 
-		if (p.contains("/")) { //in a subdir
-
-			Vector<String> ds = p.get_base_dir().split("/");
+		if (simplified_path.contains("/")) { // In a subdirectory.
+			Vector<String> ds = simplified_path.get_base_dir().split("/");
 
 
 			for (int j = 0; j < ds.size(); j++) {
 			for (int j = 0; j < ds.size(); j++) {
 				if (!cd->subdirs.has(ds[j])) {
 				if (!cd->subdirs.has(ds[j])) {
@@ -89,13 +87,40 @@ void PackedData::add_path(const String &p_pkg_path, const String &p_path, uint64
 			}
 			}
 		}
 		}
 		String filename = simplified_path.get_file();
 		String filename = simplified_path.get_file();
-		// Don't add as a file if the path points to a directory
+		// Don't add as a file if the path points to a directory.
 		if (!filename.is_empty()) {
 		if (!filename.is_empty()) {
 			cd->files.insert(filename);
 			cd->files.insert(filename);
 		}
 		}
 	}
 	}
 }
 }
 
 
+void PackedData::remove_path(const String &p_path) {
+	String simplified_path = p_path.simplify_path().trim_prefix("res://");
+	PathMD5 pmd5(simplified_path.md5_buffer());
+	if (!files.has(pmd5)) {
+		return;
+	}
+
+	// Search for directory.
+	PackedDir *cd = root;
+
+	if (simplified_path.contains("/")) { // In a subdirectory.
+		Vector<String> ds = simplified_path.get_base_dir().split("/");
+
+		for (int j = 0; j < ds.size(); j++) {
+			if (!cd->subdirs.has(ds[j])) {
+				return; // Subdirectory does not exist, do not bother creating.
+			} else {
+				cd = cd->subdirs[ds[j]];
+			}
+		}
+	}
+
+	cd->files.erase(simplified_path.get_file());
+
+	files.erase(pmd5);
+}
+
 void PackedData::add_pack_source(PackSource *p_source) {
 void PackedData::add_pack_source(PackSource *p_source) {
 	if (p_source != nullptr) {
 	if (p_source != nullptr) {
 		sources.push_back(p_source);
 		sources.push_back(p_source);
@@ -103,15 +128,32 @@ void PackedData::add_pack_source(PackSource *p_source) {
 }
 }
 
 
 uint8_t *PackedData::get_file_hash(const String &p_path) {
 uint8_t *PackedData::get_file_hash(const String &p_path) {
-	PathMD5 pmd5(p_path.md5_buffer());
+	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);
 	HashMap<PathMD5, PackedFile, PathMD5>::Iterator E = files.find(pmd5);
-	if (!E || E->value.offset == 0) {
+	if (!E) {
 		return nullptr;
 		return nullptr;
 	}
 	}
 
 
 	return E->value.md5;
 	return E->value.md5;
 }
 }
 
 
+HashSet<String> PackedData::get_file_paths() const {
+	HashSet<String> file_paths;
+	_get_file_paths(root, root->name, file_paths);
+	return file_paths;
+}
+
+void PackedData::_get_file_paths(PackedDir *p_dir, const String &p_parent_dir, HashSet<String> &r_paths) const {
+	for (const String &E : p_dir->files) {
+		r_paths.insert(p_parent_dir.path_join(E));
+	}
+
+	for (const KeyValue<String, PackedDir *> &E : p_dir->subdirs) {
+		_get_file_paths(E.value, p_parent_dir.path_join(E.key), r_paths);
+	}
+}
+
 void PackedData::clear() {
 void PackedData::clear() {
 	files.clear();
 	files.clear();
 	_free_packed_dirs(root);
 	_free_packed_dirs(root);
@@ -269,13 +311,17 @@ bool PackedSourcePCK::try_open_pack(const String &p_path, bool p_replace_files,
 		String path;
 		String path;
 		path.parse_utf8(cs.ptr());
 		path.parse_utf8(cs.ptr());
 
 
-		uint64_t ofs = file_base + f->get_64();
+		uint64_t ofs = f->get_64();
 		uint64_t size = f->get_64();
 		uint64_t size = f->get_64();
 		uint8_t md5[16];
 		uint8_t md5[16];
 		f->get_buffer(md5, 16);
 		f->get_buffer(md5, 16);
 		uint32_t flags = f->get_32();
 		uint32_t flags = f->get_32();
 
 
-		PackedData::get_singleton()->add_path(p_path, path, ofs + p_offset, size, md5, this, p_replace_files, (flags & PACK_FILE_ENCRYPTED));
+		if (flags & PACK_FILE_REMOVAL) { // The file was removed.
+			PackedData::get_singleton()->remove_path(path);
+		} else {
+			PackedData::get_singleton()->add_path(p_path, path, file_base + ofs + p_offset, size, md5, this, p_replace_files, (flags & PACK_FILE_ENCRYPTED));
+		}
 	}
 	}
 
 
 	return true;
 	return true;

+ 7 - 6
core/io/file_access_pack.h

@@ -49,7 +49,8 @@ enum PackFlags {
 };
 };
 
 
 enum PackFileFlags {
 enum PackFileFlags {
-	PACK_FILE_ENCRYPTED = 1 << 0
+	PACK_FILE_ENCRYPTED = 1 << 0,
+	PACK_FILE_REMOVAL = 1 << 1,
 };
 };
 
 
 class PackSource;
 class PackSource;
@@ -107,11 +108,14 @@ private:
 	bool disabled = false;
 	bool disabled = false;
 
 
 	void _free_packed_dirs(PackedDir *p_dir);
 	void _free_packed_dirs(PackedDir *p_dir);
+	void _get_file_paths(PackedDir *p_dir, const String &p_parent_dir, HashSet<String> &r_paths) const;
 
 
 public:
 public:
 	void add_pack_source(PackSource *p_source);
 	void add_pack_source(PackSource *p_source);
 	void add_path(const String &p_pkg_path, const String &p_path, uint64_t p_ofs, uint64_t p_size, const uint8_t *p_md5, PackSource *p_src, bool p_replace_files, bool p_encrypted = false); // for PackSource
 	void add_path(const String &p_pkg_path, const String &p_path, uint64_t p_ofs, uint64_t p_size, const uint8_t *p_md5, PackSource *p_src, bool p_replace_files, bool p_encrypted = false); // for PackSource
+	void remove_path(const String &p_path);
 	uint8_t *get_file_hash(const String &p_path);
 	uint8_t *get_file_hash(const String &p_path);
+	HashSet<String> get_file_paths() const;
 
 
 	void set_disabled(bool p_disabled) { disabled = p_disabled; }
 	void set_disabled(bool p_disabled) { disabled = p_disabled; }
 	_FORCE_INLINE_ bool is_disabled() const { return disabled; }
 	_FORCE_INLINE_ bool is_disabled() const { return disabled; }
@@ -190,14 +194,11 @@ public:
 };
 };
 
 
 Ref<FileAccess> PackedData::try_open_path(const String &p_path) {
 Ref<FileAccess> PackedData::try_open_path(const String &p_path) {
-	String simplified_path = p_path.simplify_path();
+	String simplified_path = p_path.simplify_path().trim_prefix("res://");
 	PathMD5 pmd5(simplified_path.md5_buffer());
 	PathMD5 pmd5(simplified_path.md5_buffer());
 	HashMap<PathMD5, PackedFile, PathMD5>::Iterator E = files.find(pmd5);
 	HashMap<PathMD5, PackedFile, PathMD5>::Iterator E = files.find(pmd5);
 	if (!E) {
 	if (!E) {
-		return nullptr; //not found
-	}
-	if (E->value.offset == 0) {
-		return nullptr; //was erased
+		return nullptr; // Not found.
 	}
 	}
 
 
 	return E->value.src->get_file(p_path, &E->value);
 	return E->value.src->get_file(p_path, &E->value);

+ 34 - 7
core/io/pck_packer.cpp

@@ -48,7 +48,8 @@ static int _get_pad(int p_alignment, int p_n) {
 
 
 void PCKPacker::_bind_methods() {
 void PCKPacker::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("pck_start", "pck_path", "alignment", "key", "encrypt_directory"), &PCKPacker::pck_start, DEFVAL(32), DEFVAL("0000000000000000000000000000000000000000000000000000000000000000"), DEFVAL(false));
 	ClassDB::bind_method(D_METHOD("pck_start", "pck_path", "alignment", "key", "encrypt_directory"), &PCKPacker::pck_start, DEFVAL(32), DEFVAL("0000000000000000000000000000000000000000000000000000000000000000"), DEFVAL(false));
-	ClassDB::bind_method(D_METHOD("add_file", "pck_path", "source_path", "encrypt"), &PCKPacker::add_file, DEFVAL(false));
+	ClassDB::bind_method(D_METHOD("add_file", "target_path", "source_path", "encrypt"), &PCKPacker::add_file, DEFVAL(false));
+	ClassDB::bind_method(D_METHOD("add_file_removal", "target_path"), &PCKPacker::add_file_removal);
 	ClassDB::bind_method(D_METHOD("flush", "verbose"), &PCKPacker::flush, DEFVAL(false));
 	ClassDB::bind_method(D_METHOD("flush", "verbose"), &PCKPacker::flush, DEFVAL(false));
 }
 }
 
 
@@ -106,23 +107,42 @@ Error PCKPacker::pck_start(const String &p_pck_path, int p_alignment, const Stri
 	return OK;
 	return OK;
 }
 }
 
 
-Error PCKPacker::add_file(const String &p_pck_path, const String &p_src, bool p_encrypt) {
+Error PCKPacker::add_file_removal(const String &p_target_path) {
 	ERR_FAIL_COND_V_MSG(file.is_null(), ERR_INVALID_PARAMETER, "File must be opened before use.");
 	ERR_FAIL_COND_V_MSG(file.is_null(), ERR_INVALID_PARAMETER, "File must be opened before use.");
 
 
-	Ref<FileAccess> f = FileAccess::open(p_src, FileAccess::READ);
+	File pf;
+	// Simplify path here and on every 'files' access so that paths that have extra '/'
+	// symbols or 'res://' in them still match the MD5 hash for the saved path.
+	pf.path = p_target_path.simplify_path().trim_prefix("res://");
+	pf.ofs = ofs;
+	pf.size = 0;
+	pf.removal = true;
+
+	pf.md5.resize(16);
+	pf.md5.fill(0);
+
+	files.push_back(pf);
+
+	return OK;
+}
+
+Error PCKPacker::add_file(const String &p_target_path, const String &p_source_path, bool p_encrypt) {
+	ERR_FAIL_COND_V_MSG(file.is_null(), ERR_INVALID_PARAMETER, "File must be opened before use.");
+
+	Ref<FileAccess> f = FileAccess::open(p_source_path, FileAccess::READ);
 	if (f.is_null()) {
 	if (f.is_null()) {
 		return ERR_FILE_CANT_OPEN;
 		return ERR_FILE_CANT_OPEN;
 	}
 	}
 
 
 	File pf;
 	File pf;
 	// Simplify path here and on every 'files' access so that paths that have extra '/'
 	// Simplify path here and on every 'files' access so that paths that have extra '/'
-	// symbols in them still match to the MD5 hash for the saved path.
-	pf.path = p_pck_path.simplify_path();
-	pf.src_path = p_src;
+	// symbols or 'res://' in them still match the MD5 hash for the saved path.
+	pf.path = p_target_path.simplify_path().trim_prefix("res://");
+	pf.src_path = p_source_path;
 	pf.ofs = ofs;
 	pf.ofs = ofs;
 	pf.size = f->get_length();
 	pf.size = f->get_length();
 
 
-	Vector<uint8_t> data = FileAccess::get_file_as_bytes(p_src);
+	Vector<uint8_t> data = FileAccess::get_file_as_bytes(p_source_path);
 	{
 	{
 		unsigned char hash[16];
 		unsigned char hash[16];
 		CryptoCore::md5(data.ptr(), data.size(), hash);
 		CryptoCore::md5(data.ptr(), data.size(), hash);
@@ -195,6 +215,9 @@ Error PCKPacker::flush(bool p_verbose) {
 		if (files[i].encrypted) {
 		if (files[i].encrypted) {
 			flags |= PACK_FILE_ENCRYPTED;
 			flags |= PACK_FILE_ENCRYPTED;
 		}
 		}
+		if (files[i].removal) {
+			flags |= PACK_FILE_REMOVAL;
+		}
 		fhead->store_32(flags);
 		fhead->store_32(flags);
 	}
 	}
 
 
@@ -218,6 +241,10 @@ Error PCKPacker::flush(bool p_verbose) {
 
 
 	int count = 0;
 	int count = 0;
 	for (int i = 0; i < files.size(); i++) {
 	for (int i = 0; i < files.size(); i++) {
+		if (files[i].removal) {
+			continue;
+		}
+
 		Ref<FileAccess> src = FileAccess::open(files[i].src_path, FileAccess::READ);
 		Ref<FileAccess> src = FileAccess::open(files[i].src_path, FileAccess::READ);
 		uint64_t to_write = files[i].size;
 		uint64_t to_write = files[i].size;
 
 

+ 3 - 1
core/io/pck_packer.h

@@ -53,13 +53,15 @@ class PCKPacker : public RefCounted {
 		uint64_t ofs = 0;
 		uint64_t ofs = 0;
 		uint64_t size = 0;
 		uint64_t size = 0;
 		bool encrypted = false;
 		bool encrypted = false;
+		bool removal = false;
 		Vector<uint8_t> md5;
 		Vector<uint8_t> md5;
 	};
 	};
 	Vector<File> files;
 	Vector<File> files;
 
 
 public:
 public:
 	Error pck_start(const String &p_pck_path, int p_alignment = 32, const String &p_key = "0000000000000000000000000000000000000000000000000000000000000000", bool p_encrypt_directory = false);
 	Error pck_start(const String &p_pck_path, int p_alignment = 32, const String &p_key = "0000000000000000000000000000000000000000000000000000000000000000", bool p_encrypt_directory = false);
-	Error add_file(const String &p_pck_path, const String &p_src, bool p_encrypt = false);
+	Error add_file(const String &p_target_path, const String &p_source_path, bool p_encrypt = false);
+	Error add_file_removal(const String &p_target_path);
 	Error flush(bool p_verbose = false);
 	Error flush(bool p_verbose = false);
 
 
 	PCKPacker() {}
 	PCKPacker() {}

+ 9 - 2
doc/classes/PCKPacker.xml

@@ -26,11 +26,18 @@
 	<methods>
 	<methods>
 		<method name="add_file">
 		<method name="add_file">
 			<return type="int" enum="Error" />
 			<return type="int" enum="Error" />
-			<param index="0" name="pck_path" type="String" />
+			<param index="0" name="target_path" type="String" />
 			<param index="1" name="source_path" type="String" />
 			<param index="1" name="source_path" type="String" />
 			<param index="2" name="encrypt" type="bool" default="false" />
 			<param index="2" name="encrypt" type="bool" default="false" />
 			<description>
 			<description>
-				Adds the [param source_path] file to the current PCK package at the [param pck_path] internal path (should start with [code]res://[/code]).
+				Adds the [param source_path] file to the current PCK package at the [param target_path] internal path. The [code]res://[/code] prefix for [param target_path] is optional and stripped internally.
+			</description>
+		</method>
+		<method name="add_file_removal">
+			<return type="int" enum="Error" />
+			<param index="0" name="target_path" type="String" />
+			<description>
+				Registers a file removal of the [param target_path] internal path to the PCK. This is mainly used for patches. If the file at this path has been loaded from a previous PCK, it will be removed. The [code]res://[/code] prefix for [param target_path] is optional and stripped internally.
 			</description>
 			</description>
 		</method>
 		</method>
 		<method name="flush">
 		<method name="flush">

+ 89 - 22
editor/export/editor_export_platform.cpp

@@ -59,6 +59,17 @@ static int _get_pad(int p_alignment, int p_n) {
 	return pad;
 	return pad;
 }
 }
 
 
+template <typename T>
+static bool _has_pack_path(const T &p_paths, const String &p_path) {
+	for (const String &E : p_paths) {
+		if (E.simplify_path().trim_prefix("res://") == p_path) {
+			return true;
+		}
+	}
+
+	return false;
+}
+
 #define PCK_PADDING 16
 #define PCK_PADDING 16
 
 
 bool EditorExportPlatform::fill_log_messages(RichTextLabel *p_log, Error p_err) {
 bool EditorExportPlatform::fill_log_messages(RichTextLabel *p_log, Error p_err) {
@@ -210,21 +221,23 @@ Error EditorExportPlatform::_save_pack_file(void *p_userdata, const String &p_pa
 
 
 	PackData *pd = (PackData *)p_userdata;
 	PackData *pd = (PackData *)p_userdata;
 
 
+	String simplified_path = p_path.simplify_path();
+
 	SavedData sd;
 	SavedData sd;
-	sd.path_utf8 = p_path.utf8();
+	sd.path_utf8 = simplified_path.trim_prefix("res://").utf8();
 	sd.ofs = pd->f->get_position();
 	sd.ofs = pd->f->get_position();
 	sd.size = p_data.size();
 	sd.size = p_data.size();
 	sd.encrypted = false;
 	sd.encrypted = false;
 
 
 	for (int i = 0; i < p_enc_in_filters.size(); ++i) {
 	for (int i = 0; i < p_enc_in_filters.size(); ++i) {
-		if (p_path.matchn(p_enc_in_filters[i]) || p_path.replace("res://", "").matchn(p_enc_in_filters[i])) {
+		if (simplified_path.matchn(p_enc_in_filters[i]) || simplified_path.trim_prefix("res://").matchn(p_enc_in_filters[i])) {
 			sd.encrypted = true;
 			sd.encrypted = true;
 			break;
 			break;
 		}
 		}
 	}
 	}
 
 
 	for (int i = 0; i < p_enc_ex_filters.size(); ++i) {
 	for (int i = 0; i < p_enc_ex_filters.size(); ++i) {
-		if (p_path.matchn(p_enc_ex_filters[i]) || p_path.replace("res://", "").matchn(p_enc_ex_filters[i])) {
+		if (simplified_path.matchn(p_enc_ex_filters[i]) || simplified_path.trim_prefix("res://").matchn(p_enc_ex_filters[i])) {
 			sd.encrypted = false;
 			sd.encrypted = false;
 			break;
 			break;
 		}
 		}
@@ -965,10 +978,10 @@ Error EditorExportPlatform::_export_project_files(const Ref<EditorExportPreset>
 	ScriptCallbackData data;
 	ScriptCallbackData data;
 	data.file_cb = p_save_func;
 	data.file_cb = p_save_func;
 	data.so_cb = p_so_func;
 	data.so_cb = p_so_func;
-	return export_project_files(p_preset, p_debug, _script_save_file, &data, _script_add_shared_object);
+	return export_project_files(p_preset, p_debug, _script_save_file, nullptr, &data, _script_add_shared_object);
 }
 }
 
 
-Error EditorExportPlatform::export_project_files(const Ref<EditorExportPreset> &p_preset, bool p_debug, EditorExportSaveFunction p_func, void *p_udata, EditorExportSaveSharedObject p_so_func) {
+Error EditorExportPlatform::export_project_files(const Ref<EditorExportPreset> &p_preset, bool p_debug, EditorExportSaveFunction p_save_func, EditorExportRemoveFunction p_remove_func, void *p_udata, EditorExportSaveSharedObject p_so_func) {
 	//figure out paths of files that will be exported
 	//figure out paths of files that will be exported
 	HashSet<String> paths;
 	HashSet<String> paths;
 	Vector<String> path_remaps;
 	Vector<String> path_remaps;
@@ -1082,6 +1095,7 @@ Error EditorExportPlatform::export_project_files(const Ref<EditorExportPreset> &
 
 
 	Error err = OK;
 	Error err = OK;
 	Vector<Ref<EditorExportPlugin>> export_plugins = EditorExport::get_singleton()->get_export_plugins();
 	Vector<Ref<EditorExportPlugin>> export_plugins = EditorExport::get_singleton()->get_export_plugins();
+	Vector<String> extra_paths;
 
 
 	struct SortByName {
 	struct SortByName {
 		bool operator()(const Ref<EditorExportPlugin> &left, const Ref<EditorExportPlugin> &right) const {
 		bool operator()(const Ref<EditorExportPlugin> &left, const Ref<EditorExportPlugin> &right) const {
@@ -1102,10 +1116,12 @@ Error EditorExportPlatform::export_project_files(const Ref<EditorExportPreset> &
 			}
 			}
 		}
 		}
 		for (int j = 0; j < export_plugins[i]->extra_files.size(); j++) {
 		for (int j = 0; j < export_plugins[i]->extra_files.size(); j++) {
-			err = p_func(p_udata, export_plugins[i]->extra_files[j].path, export_plugins[i]->extra_files[j].data, 0, paths.size(), enc_in_filters, enc_ex_filters, key);
+			err = p_save_func(p_udata, export_plugins[i]->extra_files[j].path, export_plugins[i]->extra_files[j].data, 0, paths.size(), enc_in_filters, enc_ex_filters, key);
 			if (err != OK) {
 			if (err != OK) {
 				return err;
 				return err;
 			}
 			}
+
+			extra_paths.push_back(export_plugins[i]->extra_files[j].path);
 		}
 		}
 
 
 		export_plugins.write[i]->_clear();
 		export_plugins.write[i]->_clear();
@@ -1218,7 +1234,7 @@ Error EditorExportPlatform::export_project_files(const Ref<EditorExportPreset> &
 			}
 			}
 
 
 			for (int j = 0; j < export_plugins[i]->extra_files.size(); j++) {
 			for (int j = 0; j < export_plugins[i]->extra_files.size(); j++) {
-				err = p_func(p_udata, export_plugins[i]->extra_files[j].path, export_plugins[i]->extra_files[j].data, idx, total, enc_in_filters, enc_ex_filters, key);
+				err = p_save_func(p_udata, export_plugins[i]->extra_files[j].path, export_plugins[i]->extra_files[j].data, idx, total, enc_in_filters, enc_ex_filters, key);
 				if (err != OK) {
 				if (err != OK) {
 					return err;
 					return err;
 				}
 				}
@@ -1227,6 +1243,8 @@ Error EditorExportPlatform::export_project_files(const Ref<EditorExportPreset> &
 					path_remaps.push_back(path);
 					path_remaps.push_back(path);
 					path_remaps.push_back(export_plugins[i]->extra_files[j].path);
 					path_remaps.push_back(export_plugins[i]->extra_files[j].path);
 				}
 				}
+
+				extra_paths.push_back(export_plugins[i]->extra_files[j].path);
 			}
 			}
 
 
 			if (export_plugins[i]->skipped) {
 			if (export_plugins[i]->skipped) {
@@ -1248,7 +1266,7 @@ Error EditorExportPlatform::export_project_files(const Ref<EditorExportPreset> &
 			if (importer_type == "keep") {
 			if (importer_type == "keep") {
 				// Just keep file as-is.
 				// Just keep file as-is.
 				Vector<uint8_t> array = FileAccess::get_file_as_bytes(path);
 				Vector<uint8_t> array = FileAccess::get_file_as_bytes(path);
-				err = p_func(p_udata, path, array, idx, total, enc_in_filters, enc_ex_filters, key);
+				err = p_save_func(p_udata, path, array, idx, total, enc_in_filters, enc_ex_filters, key);
 
 
 				if (err != OK) {
 				if (err != OK) {
 					return err;
 					return err;
@@ -1291,13 +1309,13 @@ Error EditorExportPlatform::export_project_files(const Ref<EditorExportPreset> &
 				sarr.resize(cs.size());
 				sarr.resize(cs.size());
 				memcpy(sarr.ptrw(), cs.ptr(), sarr.size());
 				memcpy(sarr.ptrw(), cs.ptr(), sarr.size());
 
 
-				err = p_func(p_udata, path + ".import", sarr, idx, total, enc_in_filters, enc_ex_filters, key);
+				err = p_save_func(p_udata, path + ".import", sarr, idx, total, enc_in_filters, enc_ex_filters, key);
 				if (err != OK) {
 				if (err != OK) {
 					return err;
 					return err;
 				}
 				}
 				// Now actual remapped file:
 				// Now actual remapped file:
 				sarr = FileAccess::get_file_as_bytes(export_path);
 				sarr = FileAccess::get_file_as_bytes(export_path);
-				err = p_func(p_udata, export_path, sarr, idx, total, enc_in_filters, enc_ex_filters, key);
+				err = p_save_func(p_udata, export_path, sarr, idx, total, enc_in_filters, enc_ex_filters, key);
 				if (err != OK) {
 				if (err != OK) {
 					return err;
 					return err;
 				}
 				}
@@ -1327,14 +1345,14 @@ Error EditorExportPlatform::export_project_files(const Ref<EditorExportPreset> &
 					if (remap == "path") {
 					if (remap == "path") {
 						String remapped_path = config->get_value("remap", remap);
 						String remapped_path = config->get_value("remap", remap);
 						Vector<uint8_t> array = FileAccess::get_file_as_bytes(remapped_path);
 						Vector<uint8_t> array = FileAccess::get_file_as_bytes(remapped_path);
-						err = p_func(p_udata, remapped_path, array, idx, total, enc_in_filters, enc_ex_filters, key);
+						err = p_save_func(p_udata, remapped_path, array, idx, total, enc_in_filters, enc_ex_filters, key);
 					} else if (remap.begins_with("path.")) {
 					} else if (remap.begins_with("path.")) {
 						String feature = remap.get_slice(".", 1);
 						String feature = remap.get_slice(".", 1);
 
 
 						if (remap_features.has(feature)) {
 						if (remap_features.has(feature)) {
 							String remapped_path = config->get_value("remap", remap);
 							String remapped_path = config->get_value("remap", remap);
 							Vector<uint8_t> array = FileAccess::get_file_as_bytes(remapped_path);
 							Vector<uint8_t> array = FileAccess::get_file_as_bytes(remapped_path);
-							err = p_func(p_udata, remapped_path, array, idx, total, enc_in_filters, enc_ex_filters, key);
+							err = p_save_func(p_udata, remapped_path, array, idx, total, enc_in_filters, enc_ex_filters, key);
 						} else {
 						} else {
 							// Remove paths if feature not enabled.
 							// Remove paths if feature not enabled.
 							config->erase_section_key("remap", remap);
 							config->erase_section_key("remap", remap);
@@ -1360,7 +1378,7 @@ Error EditorExportPlatform::export_project_files(const Ref<EditorExportPreset> &
 				sarr.resize(cs.size());
 				sarr.resize(cs.size());
 				memcpy(sarr.ptrw(), cs.ptr(), sarr.size());
 				memcpy(sarr.ptrw(), cs.ptr(), sarr.size());
 
 
-				err = p_func(p_udata, path + ".import", sarr, idx, total, enc_in_filters, enc_ex_filters, key);
+				err = p_save_func(p_udata, path + ".import", sarr, idx, total, enc_in_filters, enc_ex_filters, key);
 
 
 				if (err != OK) {
 				if (err != OK) {
 					return err;
 					return err;
@@ -1381,7 +1399,7 @@ Error EditorExportPlatform::export_project_files(const Ref<EditorExportPreset> &
 			}
 			}
 
 
 			Vector<uint8_t> array = FileAccess::get_file_as_bytes(export_path);
 			Vector<uint8_t> array = FileAccess::get_file_as_bytes(export_path);
-			err = p_func(p_udata, export_path, array, idx, total, enc_in_filters, enc_ex_filters, key);
+			err = p_save_func(p_udata, export_path, array, idx, total, enc_in_filters, enc_ex_filters, key);
 			if (err != OK) {
 			if (err != OK) {
 				return err;
 				return err;
 			}
 			}
@@ -1445,7 +1463,7 @@ Error EditorExportPlatform::export_project_files(const Ref<EditorExportPreset> &
 					new_file.write[j] = utf8[j];
 					new_file.write[j] = utf8[j];
 				}
 				}
 
 
-				err = p_func(p_udata, from + ".remap", new_file, idx, total, enc_in_filters, enc_ex_filters, key);
+				err = p_save_func(p_udata, from + ".remap", new_file, idx, total, enc_in_filters, enc_ex_filters, key);
 				if (err != OK) {
 				if (err != OK) {
 					return err;
 					return err;
 				}
 				}
@@ -1459,7 +1477,7 @@ Error EditorExportPlatform::export_project_files(const Ref<EditorExportPreset> &
 	Vector<String> forced_export = get_forced_export_files();
 	Vector<String> forced_export = get_forced_export_files();
 	for (int i = 0; i < forced_export.size(); i++) {
 	for (int i = 0; i < forced_export.size(); i++) {
 		Vector<uint8_t> array = FileAccess::get_file_as_bytes(forced_export[i]);
 		Vector<uint8_t> array = FileAccess::get_file_as_bytes(forced_export[i]);
-		err = p_func(p_udata, forced_export[i], array, idx, total, enc_in_filters, enc_ex_filters, key);
+		err = p_save_func(p_udata, forced_export[i], array, idx, total, enc_in_filters, enc_ex_filters, key);
 		if (err != OK) {
 		if (err != OK) {
 			return err;
 			return err;
 		}
 		}
@@ -1471,7 +1489,30 @@ Error EditorExportPlatform::export_project_files(const Ref<EditorExportPreset> &
 	Vector<uint8_t> data = FileAccess::get_file_as_bytes(engine_cfb);
 	Vector<uint8_t> data = FileAccess::get_file_as_bytes(engine_cfb);
 	DirAccess::remove_file_or_error(engine_cfb);
 	DirAccess::remove_file_or_error(engine_cfb);
 
 
-	return p_func(p_udata, "res://" + config_file, data, idx, total, enc_in_filters, enc_ex_filters, key);
+	err = p_save_func(p_udata, "res://" + config_file, data, idx, total, enc_in_filters, enc_ex_filters, key);
+	if (err != OK) {
+		return err;
+	}
+
+	if (p_remove_func) {
+		for (const String &E : PackedData::get_singleton()->get_file_paths()) {
+			String simplified_path = E.simplify_path();
+			if (simplified_path == config_file) {
+				continue;
+			}
+
+			String pack_path = simplified_path.trim_suffix(".remap");
+
+			if (!_has_pack_path(paths, pack_path) && !_has_pack_path(extra_paths, pack_path) && !_has_pack_path(path_remaps, pack_path) && !_has_pack_path(forced_export, pack_path)) {
+				err = p_remove_func(p_udata, E);
+				if (err != OK) {
+					return err;
+				}
+			}
+		}
+	}
+
+	return OK;
 }
 }
 
 
 Error EditorExportPlatform::_pack_add_shared_object(void *p_userdata, const SharedObject &p_so) {
 Error EditorExportPlatform::_pack_add_shared_object(void *p_userdata, const SharedObject &p_so) {
@@ -1483,6 +1524,29 @@ Error EditorExportPlatform::_pack_add_shared_object(void *p_userdata, const Shar
 	return OK;
 	return OK;
 }
 }
 
 
+Error EditorExportPlatform::_remove_pack_file(void *p_userdata, const String &p_path) {
+	PackData *pd = (PackData *)p_userdata;
+
+	SavedData sd;
+	sd.path_utf8 = p_path.utf8();
+	sd.ofs = pd->f->get_position();
+	sd.size = 0;
+	sd.removal = true;
+
+	// This padding will likely never be added, as we should already be aligned when removals are added.
+	int pad = _get_pad(PCK_PADDING, pd->f->get_position());
+	for (int i = 0; i < pad; i++) {
+		pd->f->store_8(0);
+	}
+
+	sd.md5.resize(16);
+	sd.md5.fill(0);
+
+	pd->file_ofs.push_back(sd);
+
+	return OK;
+}
+
 Error EditorExportPlatform::_zip_add_shared_object(void *p_userdata, const SharedObject &p_so) {
 Error EditorExportPlatform::_zip_add_shared_object(void *p_userdata, const SharedObject &p_so) {
 	ZipData *zip_data = (ZipData *)p_userdata;
 	ZipData *zip_data = (ZipData *)p_userdata;
 	if (zip_data->so_files) {
 	if (zip_data->so_files) {
@@ -1613,7 +1677,7 @@ Dictionary EditorExportPlatform::_save_pack(const Ref<EditorExportPreset> &p_pre
 	Vector<SharedObject> so_files;
 	Vector<SharedObject> so_files;
 	int64_t embedded_start = 0;
 	int64_t embedded_start = 0;
 	int64_t embedded_size = 0;
 	int64_t embedded_size = 0;
-	Error err_code = save_pack(p_preset, p_debug, p_path, &so_files, nullptr, p_embed, &embedded_start, &embedded_size);
+	Error err_code = save_pack(p_preset, p_debug, p_path, &so_files, nullptr, nullptr, p_embed, &embedded_start, &embedded_size);
 
 
 	Dictionary ret;
 	Dictionary ret;
 	ret["result"] = err_code;
 	ret["result"] = err_code;
@@ -1699,7 +1763,7 @@ Dictionary EditorExportPlatform::_save_zip_patch(const Ref<EditorExportPreset> &
 	return ret;
 	return ret;
 }
 }
 
 
-Error EditorExportPlatform::save_pack(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, Vector<SharedObject> *p_so_files, EditorExportSaveFunction p_save_func, bool p_embed, int64_t *r_embedded_start, int64_t *r_embedded_size) {
+Error EditorExportPlatform::save_pack(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, Vector<SharedObject> *p_so_files, EditorExportSaveFunction p_save_func, EditorExportRemoveFunction p_remove_func, bool p_embed, int64_t *r_embedded_start, int64_t *r_embedded_size) {
 	EditorProgress ep("savepack", TTR("Packing"), 102, true);
 	EditorProgress ep("savepack", TTR("Packing"), 102, true);
 
 
 	if (p_save_func == nullptr) {
 	if (p_save_func == nullptr) {
@@ -1722,7 +1786,7 @@ Error EditorExportPlatform::save_pack(const Ref<EditorExportPreset> &p_preset, b
 	pd.f = ftmp;
 	pd.f = ftmp;
 	pd.so_files = p_so_files;
 	pd.so_files = p_so_files;
 
 
-	Error err = export_project_files(p_preset, p_debug, p_save_func, &pd, _pack_add_shared_object);
+	Error err = export_project_files(p_preset, p_debug, p_save_func, p_remove_func, &pd, _pack_add_shared_object);
 
 
 	// Close temp file.
 	// Close temp file.
 	pd.f.unref();
 	pd.f.unref();
@@ -1868,6 +1932,9 @@ Error EditorExportPlatform::save_pack(const Ref<EditorExportPreset> &p_preset, b
 		if (pd.file_ofs[i].encrypted) {
 		if (pd.file_ofs[i].encrypted) {
 			flags |= PACK_FILE_ENCRYPTED;
 			flags |= PACK_FILE_ENCRYPTED;
 		}
 		}
+		if (pd.file_ofs[i].removal) {
+			flags |= PACK_FILE_REMOVAL;
+		}
 		fhead->store_32(flags);
 		fhead->store_32(flags);
 	}
 	}
 
 
@@ -1936,7 +2003,7 @@ Error EditorExportPlatform::save_pack(const Ref<EditorExportPreset> &p_preset, b
 }
 }
 
 
 Error EditorExportPlatform::save_pack_patch(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, Vector<SharedObject> *p_so_files, bool p_embed, int64_t *r_embedded_start, int64_t *r_embedded_size) {
 Error EditorExportPlatform::save_pack_patch(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, Vector<SharedObject> *p_so_files, bool p_embed, int64_t *r_embedded_start, int64_t *r_embedded_size) {
-	return save_pack(p_preset, p_debug, p_path, p_so_files, _save_pack_patch_file, p_embed, r_embedded_start, r_embedded_size);
+	return save_pack(p_preset, p_debug, p_path, p_so_files, _save_pack_patch_file, _remove_pack_file, p_embed, r_embedded_start, r_embedded_size);
 }
 }
 
 
 Error EditorExportPlatform::save_zip(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, Vector<SharedObject> *p_so_files, EditorExportSaveFunction p_save_func) {
 Error EditorExportPlatform::save_zip(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, Vector<SharedObject> *p_so_files, EditorExportSaveFunction p_save_func) {
@@ -1957,7 +2024,7 @@ Error EditorExportPlatform::save_zip(const Ref<EditorExportPreset> &p_preset, bo
 	zd.zip = zip;
 	zd.zip = zip;
 	zd.so_files = p_so_files;
 	zd.so_files = p_so_files;
 
 
-	Error err = export_project_files(p_preset, p_debug, p_save_func, &zd, _zip_add_shared_object);
+	Error err = export_project_files(p_preset, p_debug, p_save_func, nullptr, &zd, _zip_add_shared_object);
 	if (err != OK && err != ERR_SKIP) {
 	if (err != OK && err != ERR_SKIP) {
 		add_message(EXPORT_MESSAGE_ERROR, TTR("Save ZIP"), TTR("Failed to export project files."));
 		add_message(EXPORT_MESSAGE_ERROR, TTR("Save ZIP"), TTR("Failed to export project files."));
 	}
 	}

+ 6 - 2
editor/export/editor_export_platform.h

@@ -54,6 +54,7 @@ protected:
 
 
 public:
 public:
 	typedef Error (*EditorExportSaveFunction)(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);
 	typedef Error (*EditorExportSaveFunction)(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);
+	typedef Error (*EditorExportRemoveFunction)(void *p_userdata, const String &p_path);
 	typedef Error (*EditorExportSaveSharedObject)(void *p_userdata, const SharedObject &p_so);
 	typedef Error (*EditorExportSaveSharedObject)(void *p_userdata, const SharedObject &p_so);
 
 
 	enum DebugFlags {
 	enum DebugFlags {
@@ -82,6 +83,7 @@ private:
 		uint64_t ofs = 0;
 		uint64_t ofs = 0;
 		uint64_t size = 0;
 		uint64_t size = 0;
 		bool encrypted = false;
 		bool encrypted = false;
+		bool removal = false;
 		Vector<uint8_t> md5;
 		Vector<uint8_t> md5;
 		CharString path_utf8;
 		CharString path_utf8;
 
 
@@ -116,6 +118,8 @@ private:
 	static Error _save_pack_patch_file(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);
 	static Error _save_pack_patch_file(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);
 	static Error _pack_add_shared_object(void *p_userdata, const SharedObject &p_so);
 	static Error _pack_add_shared_object(void *p_userdata, const SharedObject &p_so);
 
 
+	static Error _remove_pack_file(void *p_userdata, const String &p_path);
+
 	static Error _save_zip_file(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);
 	static Error _save_zip_file(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);
 	static Error _save_zip_patch_file(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);
 	static Error _save_zip_patch_file(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);
 	static Error _zip_add_shared_object(void *p_userdata, const SharedObject &p_so);
 	static Error _zip_add_shared_object(void *p_userdata, const SharedObject &p_so);
@@ -287,7 +291,7 @@ public:
 	Array get_current_presets() const;
 	Array get_current_presets() const;
 
 
 	Error _export_project_files(const Ref<EditorExportPreset> &p_preset, bool p_debug, const Callable &p_save_func, const Callable &p_so_func);
 	Error _export_project_files(const Ref<EditorExportPreset> &p_preset, bool p_debug, const Callable &p_save_func, const Callable &p_so_func);
-	Error export_project_files(const Ref<EditorExportPreset> &p_preset, bool p_debug, EditorExportSaveFunction p_func, void *p_udata, EditorExportSaveSharedObject p_so_func = nullptr);
+	Error export_project_files(const Ref<EditorExportPreset> &p_preset, bool p_debug, EditorExportSaveFunction p_save_func, EditorExportRemoveFunction p_remove_func, void *p_udata, EditorExportSaveSharedObject p_so_func = nullptr);
 
 
 	Dictionary _save_pack(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, bool p_embed = false);
 	Dictionary _save_pack(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, bool p_embed = false);
 	Dictionary _save_zip(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path);
 	Dictionary _save_zip(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path);
@@ -295,7 +299,7 @@ public:
 	Dictionary _save_pack_patch(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path);
 	Dictionary _save_pack_patch(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path);
 	Dictionary _save_zip_patch(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path);
 	Dictionary _save_zip_patch(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path);
 
 
-	Error save_pack(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, Vector<SharedObject> *p_so_files = nullptr, EditorExportSaveFunction p_save_func = nullptr, bool p_embed = false, int64_t *r_embedded_start = nullptr, int64_t *r_embedded_size = nullptr);
+	Error save_pack(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, Vector<SharedObject> *p_so_files = nullptr, EditorExportSaveFunction p_save_func = nullptr, EditorExportRemoveFunction p_remove_func = nullptr, bool p_embed = false, int64_t *r_embedded_start = nullptr, int64_t *r_embedded_size = nullptr);
 	Error save_zip(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, Vector<SharedObject> *p_so_files = nullptr, EditorExportSaveFunction p_save_func = nullptr);
 	Error save_zip(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, Vector<SharedObject> *p_so_files = nullptr, EditorExportSaveFunction p_save_func = nullptr);
 
 
 	Error save_pack_patch(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, Vector<SharedObject> *p_so_files = nullptr, bool p_embed = false, int64_t *r_embedded_start = nullptr, int64_t *r_embedded_size = nullptr);
 	Error save_pack_patch(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, Vector<SharedObject> *p_so_files = nullptr, bool p_embed = false, int64_t *r_embedded_start = nullptr, int64_t *r_embedded_size = nullptr);

+ 1 - 1
editor/export/editor_export_platform_pc.cpp

@@ -194,7 +194,7 @@ Error EditorExportPlatformPC::export_project_data(const Ref<EditorExportPreset>
 
 
 	int64_t embedded_pos;
 	int64_t embedded_pos;
 	int64_t embedded_size;
 	int64_t embedded_size;
-	Error err = save_pack(p_preset, p_debug, pck_path, &so_files, nullptr, p_preset->get("binary_format/embed_pck"), &embedded_pos, &embedded_size);
+	Error err = save_pack(p_preset, p_debug, pck_path, &so_files, nullptr, nullptr, p_preset->get("binary_format/embed_pck"), &embedded_pos, &embedded_size);
 	if (err == OK && p_preset->get("binary_format/embed_pck")) {
 	if (err == OK && p_preset->get("binary_format/embed_pck")) {
 		if (embedded_size >= 0x100000000 && String(p_preset->get("binary_format/architecture")).contains("32")) {
 		if (embedded_size >= 0x100000000 && String(p_preset->get("binary_format/architecture")).contains("32")) {
 			add_message(EXPORT_MESSAGE_ERROR, TTR("PCK Embedding"), TTR("On 32-bit exports the embedded PCK cannot be bigger than 4 GiB."));
 			add_message(EXPORT_MESSAGE_ERROR, TTR("PCK Embedding"), TTR("On 32-bit exports the embedded PCK cannot be bigger than 4 GiB."));

+ 4 - 4
platform/android/export/export_plugin.cpp

@@ -3165,9 +3165,9 @@ Error EditorExportPlatformAndroid::export_project_helper(const Ref<EditorExportP
 			user_data.libs_directory = gradle_build_directory.path_join("libs");
 			user_data.libs_directory = gradle_build_directory.path_join("libs");
 			user_data.debug = p_debug;
 			user_data.debug = p_debug;
 			if (p_flags.has_flag(DEBUG_FLAG_DUMB_CLIENT)) {
 			if (p_flags.has_flag(DEBUG_FLAG_DUMB_CLIENT)) {
-				err = export_project_files(p_preset, p_debug, ignore_apk_file, &user_data, copy_gradle_so);
+				err = export_project_files(p_preset, p_debug, ignore_apk_file, nullptr, &user_data, copy_gradle_so);
 			} else {
 			} else {
-				err = export_project_files(p_preset, p_debug, rename_and_store_file_in_gradle_project, &user_data, copy_gradle_so);
+				err = export_project_files(p_preset, p_debug, rename_and_store_file_in_gradle_project, nullptr, &user_data, copy_gradle_so);
 			}
 			}
 			if (err != OK) {
 			if (err != OK) {
 				add_message(EXPORT_MESSAGE_ERROR, TTR("Export"), TTR("Could not export project files to gradle project."));
 				add_message(EXPORT_MESSAGE_ERROR, TTR("Export"), TTR("Could not export project files to gradle project."));
@@ -3558,7 +3558,7 @@ Error EditorExportPlatformAndroid::export_project_helper(const Ref<EditorExportP
 		APKExportData ed;
 		APKExportData ed;
 		ed.ep = &ep;
 		ed.ep = &ep;
 		ed.apk = unaligned_apk;
 		ed.apk = unaligned_apk;
-		err = export_project_files(p_preset, p_debug, ignore_apk_file, &ed, save_apk_so);
+		err = export_project_files(p_preset, p_debug, ignore_apk_file, nullptr, &ed, save_apk_so);
 	} else {
 	} else {
 		if (apk_expansion) {
 		if (apk_expansion) {
 			err = save_apk_expansion_file(p_preset, p_debug, p_path);
 			err = save_apk_expansion_file(p_preset, p_debug, p_path);
@@ -3570,7 +3570,7 @@ Error EditorExportPlatformAndroid::export_project_helper(const Ref<EditorExportP
 			APKExportData ed;
 			APKExportData ed;
 			ed.ep = &ep;
 			ed.ep = &ep;
 			ed.apk = unaligned_apk;
 			ed.apk = unaligned_apk;
-			err = export_project_files(p_preset, p_debug, save_apk_file, &ed, save_apk_so);
+			err = export_project_files(p_preset, p_debug, save_apk_file, nullptr, &ed, save_apk_so);
 		}
 		}
 	}
 	}