|
@@ -167,6 +167,44 @@ bool EditorExportPlatform::fill_log_messages(RichTextLabel *p_log, Error p_err)
|
|
|
return has_messages;
|
|
|
}
|
|
|
|
|
|
+bool EditorExportPlatform::_check_hash(const uint8_t *p_hash, const Vector<uint8_t> &p_data) {
|
|
|
+ if (p_hash == nullptr) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ unsigned char hash[16];
|
|
|
+ Error err = CryptoCore::md5(p_data.ptr(), p_data.size(), hash);
|
|
|
+ if (err != OK) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ for (int i = 0; i < 16; i++) {
|
|
|
+ if (p_hash[i] != hash[i]) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return true;
|
|
|
+}
|
|
|
+
|
|
|
+Error EditorExportPlatform::_load_patches(const Vector<String> &p_patches) {
|
|
|
+ Error err = OK;
|
|
|
+ if (!p_patches.is_empty()) {
|
|
|
+ for (const String &path : p_patches) {
|
|
|
+ err = PackedData::get_singleton()->add_pack(path, true, 0);
|
|
|
+ if (err != OK) {
|
|
|
+ add_message(EXPORT_MESSAGE_ERROR, TTR("Patch Creation"), vformat(TTR("Could not load patch pack with path \"%s\"."), path));
|
|
|
+ return err;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return err;
|
|
|
+}
|
|
|
+
|
|
|
+void EditorExportPlatform::_unload_patches() {
|
|
|
+ PackedData::get_singleton()->clear();
|
|
|
+}
|
|
|
+
|
|
|
Error EditorExportPlatform::_save_pack_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) {
|
|
|
ERR_FAIL_COND_V_MSG(p_total < 1, ERR_PARAMETER_RANGE_ERROR, "Must select at least one file to export.");
|
|
|
|
|
@@ -237,6 +275,14 @@ Error EditorExportPlatform::_save_pack_file(void *p_userdata, const String &p_pa
|
|
|
return OK;
|
|
|
}
|
|
|
|
|
|
+Error EditorExportPlatform::_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) {
|
|
|
+ if (_check_hash(PackedData::get_singleton()->get_file_hash(p_path), p_data)) {
|
|
|
+ return OK;
|
|
|
+ }
|
|
|
+
|
|
|
+ return _save_pack_file(p_userdata, p_path, p_data, p_file, p_total, p_enc_in_filters, p_enc_ex_filters, p_key);
|
|
|
+}
|
|
|
+
|
|
|
Error EditorExportPlatform::_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) {
|
|
|
ERR_FAIL_COND_V_MSG(p_total < 1, ERR_PARAMETER_RANGE_ERROR, "Must select at least one file to export.");
|
|
|
|
|
@@ -260,6 +306,8 @@ Error EditorExportPlatform::_save_zip_file(void *p_userdata, const String &p_pat
|
|
|
zipWriteInFileInZip(zip, p_data.ptr(), p_data.size());
|
|
|
zipCloseFileInZip(zip);
|
|
|
|
|
|
+ zd->file_count += 1;
|
|
|
+
|
|
|
if (zd->ep->step(TTR("Storing File:") + " " + p_path, 2 + p_file * 100 / p_total, false)) {
|
|
|
return ERR_SKIP;
|
|
|
}
|
|
@@ -267,6 +315,14 @@ Error EditorExportPlatform::_save_zip_file(void *p_userdata, const String &p_pat
|
|
|
return OK;
|
|
|
}
|
|
|
|
|
|
+Error EditorExportPlatform::_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) {
|
|
|
+ if (_check_hash(PackedData::get_singleton()->get_file_hash(p_path), p_data)) {
|
|
|
+ return OK;
|
|
|
+ }
|
|
|
+
|
|
|
+ return _save_zip_file(p_userdata, p_path, p_data, p_file, p_total, p_enc_in_filters, p_enc_ex_filters, p_key);
|
|
|
+}
|
|
|
+
|
|
|
Ref<ImageTexture> EditorExportPlatform::get_option_icon(int p_index) const {
|
|
|
Ref<Theme> theme = EditorNode::get_singleton()->get_editor_theme();
|
|
|
ERR_FAIL_COND_V(theme.is_null(), Ref<ImageTexture>());
|
|
@@ -1561,7 +1617,7 @@ Dictionary EditorExportPlatform::_save_pack(const Ref<EditorExportPreset> &p_pre
|
|
|
Vector<SharedObject> so_files;
|
|
|
int64_t embedded_start = 0;
|
|
|
int64_t embedded_size = 0;
|
|
|
- Error err_code = save_pack(p_preset, p_debug, p_path, &so_files, p_embed, &embedded_start, &embedded_size);
|
|
|
+ Error err_code = save_pack(p_preset, p_debug, p_path, &so_files, nullptr, p_embed, &embedded_start, &embedded_size);
|
|
|
|
|
|
Dictionary ret;
|
|
|
ret["result"] = err_code;
|
|
@@ -1605,9 +1661,55 @@ Dictionary EditorExportPlatform::_save_zip(const Ref<EditorExportPreset> &p_pres
|
|
|
return ret;
|
|
|
}
|
|
|
|
|
|
-Error EditorExportPlatform::save_pack(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) {
|
|
|
+Dictionary EditorExportPlatform::_save_pack_patch(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path) {
|
|
|
+ Vector<SharedObject> so_files;
|
|
|
+ Error err_code = save_pack_patch(p_preset, p_debug, p_path, &so_files);
|
|
|
+
|
|
|
+ Dictionary ret;
|
|
|
+ ret["result"] = err_code;
|
|
|
+ if (err_code == OK) {
|
|
|
+ Array arr;
|
|
|
+ for (const SharedObject &E : so_files) {
|
|
|
+ Dictionary so;
|
|
|
+ so["path"] = E.path;
|
|
|
+ so["tags"] = E.tags;
|
|
|
+ so["target_folder"] = E.target;
|
|
|
+ arr.push_back(so);
|
|
|
+ }
|
|
|
+ ret["so_files"] = arr;
|
|
|
+ }
|
|
|
+
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
+Dictionary EditorExportPlatform::_save_zip_patch(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path) {
|
|
|
+ Vector<SharedObject> so_files;
|
|
|
+ Error err_code = save_zip_patch(p_preset, p_debug, p_path, &so_files);
|
|
|
+
|
|
|
+ Dictionary ret;
|
|
|
+ ret["result"] = err_code;
|
|
|
+ if (err_code == OK) {
|
|
|
+ Array arr;
|
|
|
+ for (const SharedObject &E : so_files) {
|
|
|
+ Dictionary so;
|
|
|
+ so["path"] = E.path;
|
|
|
+ so["tags"] = E.tags;
|
|
|
+ so["target_folder"] = E.target;
|
|
|
+ arr.push_back(so);
|
|
|
+ }
|
|
|
+ ret["so_files"] = arr;
|
|
|
+ }
|
|
|
+
|
|
|
+ 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) {
|
|
|
EditorProgress ep("savepack", TTR("Packing"), 102, true);
|
|
|
|
|
|
+ if (p_save_func == nullptr) {
|
|
|
+ p_save_func = _save_pack_file;
|
|
|
+ }
|
|
|
+
|
|
|
// Create the temporary export directory if it doesn't exist.
|
|
|
Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
|
|
|
da->make_dir_recursive(EditorPaths::get_singleton()->get_cache_dir());
|
|
@@ -1624,7 +1726,7 @@ Error EditorExportPlatform::save_pack(const Ref<EditorExportPreset> &p_preset, b
|
|
|
pd.f = ftmp;
|
|
|
pd.so_files = p_so_files;
|
|
|
|
|
|
- Error err = export_project_files(p_preset, p_debug, _save_pack_file, &pd, _pack_add_shared_object);
|
|
|
+ Error err = export_project_files(p_preset, p_debug, p_save_func, &pd, _pack_add_shared_object);
|
|
|
|
|
|
// Close temp file.
|
|
|
pd.f.unref();
|
|
@@ -1636,6 +1738,12 @@ Error EditorExportPlatform::save_pack(const Ref<EditorExportPreset> &p_preset, b
|
|
|
return err;
|
|
|
}
|
|
|
|
|
|
+ if (pd.file_ofs.is_empty()) {
|
|
|
+ DirAccess::remove_file_or_error(tmppath);
|
|
|
+ add_message(EXPORT_MESSAGE_ERROR, TTR("Save PCK"), TTR("No files or changes to export."));
|
|
|
+ return FAILED;
|
|
|
+ }
|
|
|
+
|
|
|
pd.file_ofs.sort(); //do sort, so we can do binary search later
|
|
|
|
|
|
Ref<FileAccess> f;
|
|
@@ -1831,28 +1939,56 @@ Error EditorExportPlatform::save_pack(const Ref<EditorExportPreset> &p_preset, b
|
|
|
return OK;
|
|
|
}
|
|
|
|
|
|
-Error EditorExportPlatform::save_zip(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, Vector<SharedObject> *p_so_files) {
|
|
|
+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);
|
|
|
+}
|
|
|
+
|
|
|
+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) {
|
|
|
EditorProgress ep("savezip", TTR("Packing"), 102, true);
|
|
|
|
|
|
+ if (p_save_func == nullptr) {
|
|
|
+ p_save_func = _save_zip_file;
|
|
|
+ }
|
|
|
+
|
|
|
+ String tmppath = EditorPaths::get_singleton()->get_cache_dir().path_join("packtmp");
|
|
|
+
|
|
|
Ref<FileAccess> io_fa;
|
|
|
zlib_filefunc_def io = zipio_create_io(&io_fa);
|
|
|
- zipFile zip = zipOpen2(p_path.utf8().get_data(), APPEND_STATUS_CREATE, nullptr, &io);
|
|
|
+ zipFile zip = zipOpen2(tmppath.utf8().get_data(), APPEND_STATUS_CREATE, nullptr, &io);
|
|
|
|
|
|
ZipData zd;
|
|
|
zd.ep = &ep;
|
|
|
zd.zip = zip;
|
|
|
zd.so_files = p_so_files;
|
|
|
|
|
|
- Error err = export_project_files(p_preset, p_debug, _save_zip_file, &zd, _zip_add_shared_object);
|
|
|
+ Error err = export_project_files(p_preset, p_debug, p_save_func, &zd, _zip_add_shared_object);
|
|
|
if (err != OK && err != ERR_SKIP) {
|
|
|
add_message(EXPORT_MESSAGE_ERROR, TTR("Save ZIP"), TTR("Failed to export project files."));
|
|
|
}
|
|
|
|
|
|
zipClose(zip, nullptr);
|
|
|
|
|
|
+ Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
|
|
|
+
|
|
|
+ if (zd.file_count == 0) {
|
|
|
+ da->remove(tmppath);
|
|
|
+ add_message(EXPORT_MESSAGE_ERROR, TTR("Save PCK"), TTR("No files or changes to export."));
|
|
|
+ return FAILED;
|
|
|
+ }
|
|
|
+
|
|
|
+ err = da->rename(tmppath, p_path);
|
|
|
+ if (err != OK) {
|
|
|
+ da->remove(tmppath);
|
|
|
+ add_message(EXPORT_MESSAGE_ERROR, TTR("Save ZIP"), vformat(TTR("Failed to move temporary file \"%s\" to \"%s\"."), tmppath, p_path));
|
|
|
+ }
|
|
|
+
|
|
|
return OK;
|
|
|
}
|
|
|
|
|
|
+Error EditorExportPlatform::save_zip_patch(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, Vector<SharedObject> *p_so_files) {
|
|
|
+ return save_zip(p_preset, p_debug, p_path, p_so_files, _save_zip_patch_file);
|
|
|
+}
|
|
|
+
|
|
|
Error EditorExportPlatform::export_pack(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, BitField<EditorExportPlatform::DebugFlags> p_flags) {
|
|
|
ExportNotifier notifier(*this, p_preset, p_debug, p_path, p_flags);
|
|
|
return save_pack(p_preset, p_debug, p_path);
|
|
@@ -1863,6 +1999,28 @@ Error EditorExportPlatform::export_zip(const Ref<EditorExportPreset> &p_preset,
|
|
|
return save_zip(p_preset, p_debug, p_path);
|
|
|
}
|
|
|
|
|
|
+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);
|
|
|
+ if (err != OK) {
|
|
|
+ return err;
|
|
|
+ }
|
|
|
+ err = save_pack_patch(p_preset, p_debug, p_path);
|
|
|
+ _unload_patches();
|
|
|
+ return err;
|
|
|
+}
|
|
|
+
|
|
|
+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);
|
|
|
+ if (err != OK) {
|
|
|
+ return err;
|
|
|
+ }
|
|
|
+ err = save_zip_patch(p_preset, p_debug, p_path);
|
|
|
+ _unload_patches();
|
|
|
+ return err;
|
|
|
+}
|
|
|
+
|
|
|
Vector<String> EditorExportPlatform::gen_export_flags(BitField<EditorExportPlatform::DebugFlags> p_flags) {
|
|
|
Vector<String> ret;
|
|
|
String host = EDITOR_GET("network/debug/remote_host");
|
|
@@ -2115,6 +2273,8 @@ void EditorExportPlatform::_bind_methods() {
|
|
|
|
|
|
ClassDB::bind_method(D_METHOD("save_pack", "preset", "debug", "path", "embed"), &EditorExportPlatform::_save_pack, DEFVAL(false));
|
|
|
ClassDB::bind_method(D_METHOD("save_zip", "preset", "debug", "path"), &EditorExportPlatform::_save_zip);
|
|
|
+ ClassDB::bind_method(D_METHOD("save_pack_patch", "preset", "debug", "path"), &EditorExportPlatform::_save_pack_patch);
|
|
|
+ ClassDB::bind_method(D_METHOD("save_zip_patch", "preset", "debug", "path"), &EditorExportPlatform::_save_zip_patch);
|
|
|
|
|
|
ClassDB::bind_method(D_METHOD("gen_export_flags", "flags"), &EditorExportPlatform::gen_export_flags);
|
|
|
|
|
@@ -2123,6 +2283,8 @@ void EditorExportPlatform::_bind_methods() {
|
|
|
ClassDB::bind_method(D_METHOD("export_project", "preset", "debug", "path", "flags"), &EditorExportPlatform::export_project, DEFVAL(0));
|
|
|
ClassDB::bind_method(D_METHOD("export_pack", "preset", "debug", "path", "flags"), &EditorExportPlatform::export_pack, DEFVAL(0));
|
|
|
ClassDB::bind_method(D_METHOD("export_zip", "preset", "debug", "path", "flags"), &EditorExportPlatform::export_zip, DEFVAL(0));
|
|
|
+ ClassDB::bind_method(D_METHOD("export_pack_patch", "preset", "debug", "path", "patches", "flags"), &EditorExportPlatform::export_pack_patch, DEFVAL(PackedStringArray()), DEFVAL(0));
|
|
|
+ ClassDB::bind_method(D_METHOD("export_zip_patch", "preset", "debug", "path", "patches", "flags"), &EditorExportPlatform::export_zip_patch, DEFVAL(PackedStringArray()), DEFVAL(0));
|
|
|
|
|
|
ClassDB::bind_method(D_METHOD("clear_messages"), &EditorExportPlatform::clear_messages);
|
|
|
ClassDB::bind_method(D_METHOD("add_message", "type", "category", "message"), &EditorExportPlatform::add_message);
|