|
@@ -252,41 +252,25 @@ 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, uint64_t p_seed) {
|
|
|
- ERR_FAIL_COND_V_MSG(p_total < 1, ERR_PARAMETER_RANGE_ERROR, "Must select at least one file to export.");
|
|
|
-
|
|
|
- PackData *pd = (PackData *)p_userdata;
|
|
|
-
|
|
|
- String simplified_path = p_path.simplify_path();
|
|
|
- if (simplified_path.begins_with("uid://")) {
|
|
|
- simplified_path = ResourceUID::uid_to_path(simplified_path).simplify_path();
|
|
|
- print_verbose(vformat(R"(UID referenced exported file name "%s" was replaced with "%s".)", p_path, simplified_path));
|
|
|
- }
|
|
|
-
|
|
|
- SavedData sd;
|
|
|
- sd.path_utf8 = simplified_path.trim_prefix("res://").utf8();
|
|
|
- sd.ofs = pd->f->get_position();
|
|
|
- sd.size = p_data.size();
|
|
|
- sd.encrypted = false;
|
|
|
-
|
|
|
+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) {
|
|
|
+ r_encrypt = false;
|
|
|
for (int i = 0; i < p_enc_in_filters.size(); ++i) {
|
|
|
- if (simplified_path.matchn(p_enc_in_filters[i]) || simplified_path.trim_prefix("res://").matchn(p_enc_in_filters[i])) {
|
|
|
- sd.encrypted = true;
|
|
|
+ if (p_path.matchn(p_enc_in_filters[i]) || p_path.trim_prefix("res://").matchn(p_enc_in_filters[i])) {
|
|
|
+ r_encrypt = true;
|
|
|
break;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
for (int i = 0; i < p_enc_ex_filters.size(); ++i) {
|
|
|
- if (simplified_path.matchn(p_enc_ex_filters[i]) || simplified_path.trim_prefix("res://").matchn(p_enc_ex_filters[i])) {
|
|
|
- sd.encrypted = false;
|
|
|
+ if (p_path.matchn(p_enc_ex_filters[i]) || p_path.trim_prefix("res://").matchn(p_enc_ex_filters[i])) {
|
|
|
+ r_encrypt = false;
|
|
|
break;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
Ref<FileAccessEncrypted> fae;
|
|
|
- Ref<FileAccess> ftmp = pd->f;
|
|
|
-
|
|
|
- if (sd.encrypted) {
|
|
|
+ Ref<FileAccess> ftmp = p_fd;
|
|
|
+ if (r_encrypt) {
|
|
|
Vector<uint8_t> iv;
|
|
|
if (p_seed != 0) {
|
|
|
uint64_t seed = p_seed;
|
|
@@ -319,12 +303,44 @@ Error EditorExportPlatform::_save_pack_file(void *p_userdata, const String &p_pa
|
|
|
ftmp.unref();
|
|
|
fae.unref();
|
|
|
}
|
|
|
+ return OK;
|
|
|
+}
|
|
|
|
|
|
- ERR_FAIL_COND_V(pd->f->get_position() - sd.ofs < (uint64_t)p_data.size(), ERR_FILE_CANT_WRITE);
|
|
|
+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, uint64_t p_seed) {
|
|
|
+ ERR_FAIL_COND_V_MSG(p_total < 1, ERR_PARAMETER_RANGE_ERROR, "Must select at least one file to export.");
|
|
|
|
|
|
- int pad = _get_pad(PCK_PADDING, pd->f->get_position());
|
|
|
- for (int i = 0; i < pad; i++) {
|
|
|
- pd->f->store_8(0);
|
|
|
+ PackData *pd = (PackData *)p_userdata;
|
|
|
+
|
|
|
+ String simplified_path = p_path.simplify_path();
|
|
|
+ if (simplified_path.begins_with("uid://")) {
|
|
|
+ simplified_path = ResourceUID::uid_to_path(simplified_path).simplify_path();
|
|
|
+ print_verbose(vformat(R"(UID referenced exported file name "%s" was replaced with "%s".)", p_path, simplified_path));
|
|
|
+ }
|
|
|
+
|
|
|
+ Ref<FileAccess> ftmp;
|
|
|
+ if (pd->use_sparse_pck) {
|
|
|
+ ftmp = FileAccess::open(pd->path.get_base_dir().path_join(simplified_path.trim_prefix("res://")), FileAccess::WRITE);
|
|
|
+ } else {
|
|
|
+ ftmp = pd->f;
|
|
|
+ }
|
|
|
+
|
|
|
+ SavedData sd;
|
|
|
+ sd.path_utf8 = simplified_path.trim_prefix("res://").utf8();
|
|
|
+ sd.ofs = (pd->use_sparse_pck) ? 0 : pd->f->get_position();
|
|
|
+ sd.size = p_data.size();
|
|
|
+ Error err = _encrypt_and_store_data(ftmp, simplified_path, p_data, p_enc_in_filters, p_enc_ex_filters, p_key, p_seed, sd.encrypted);
|
|
|
+ if (err != OK) {
|
|
|
+ return err;
|
|
|
+ }
|
|
|
+ if (!pd->use_sparse_pck) {
|
|
|
+ ERR_FAIL_COND_V(pd->f->get_position() - sd.ofs < (uint64_t)p_data.size(), ERR_FILE_CANT_WRITE);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!pd->use_sparse_pck) {
|
|
|
+ int pad = _get_pad(PCK_PADDING, pd->f->get_position());
|
|
|
+ for (int i = 0; i < pad; i++) {
|
|
|
+ pd->f->store_8(0);
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
// Store MD5 of original file.
|
|
@@ -1897,6 +1913,105 @@ Dictionary EditorExportPlatform::_save_zip_patch(const Ref<EditorExportPreset> &
|
|
|
return ret;
|
|
|
}
|
|
|
|
|
|
+bool EditorExportPlatform::_store_header(Ref<FileAccess> p_fd, bool p_enc, bool p_sparse, uint64_t &r_file_base_ofs, uint64_t &r_dir_base_ofs) {
|
|
|
+ p_fd->store_32(PACK_HEADER_MAGIC);
|
|
|
+ p_fd->store_32(PACK_FORMAT_VERSION);
|
|
|
+ p_fd->store_32(GODOT_VERSION_MAJOR);
|
|
|
+ p_fd->store_32(GODOT_VERSION_MINOR);
|
|
|
+ p_fd->store_32(GODOT_VERSION_PATCH);
|
|
|
+
|
|
|
+ uint32_t pack_flags = PACK_REL_FILEBASE;
|
|
|
+ if (p_enc) {
|
|
|
+ pack_flags |= PACK_DIR_ENCRYPTED;
|
|
|
+ }
|
|
|
+ if (p_sparse) {
|
|
|
+ pack_flags |= PACK_SPARSE_BUNDLE;
|
|
|
+ }
|
|
|
+ p_fd->store_32(pack_flags); // Flags.
|
|
|
+
|
|
|
+ r_file_base_ofs = p_fd->get_position();
|
|
|
+ p_fd->store_64(0); // Files base offset.
|
|
|
+
|
|
|
+ r_dir_base_ofs = p_fd->get_position();
|
|
|
+ p_fd->store_64(0); // Directory offset.
|
|
|
+
|
|
|
+ for (int i = 0; i < 16; i++) {
|
|
|
+ //reserved
|
|
|
+ p_fd->store_32(0);
|
|
|
+ }
|
|
|
+ return true;
|
|
|
+}
|
|
|
+
|
|
|
+bool EditorExportPlatform::_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) {
|
|
|
+ Ref<FileAccessEncrypted> fae;
|
|
|
+ Ref<FileAccess> fhead = p_fd;
|
|
|
+
|
|
|
+ fhead->store_32(p_pack_data.file_ofs.size()); //amount of files
|
|
|
+
|
|
|
+ if (!p_key.is_empty()) {
|
|
|
+ uint64_t seed = p_seed;
|
|
|
+ fae.instantiate();
|
|
|
+ if (fae.is_null()) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ Vector<uint8_t> iv;
|
|
|
+ if (seed != 0) {
|
|
|
+ for (int i = 0; i < p_pack_data.file_ofs.size(); i++) {
|
|
|
+ for (int64_t j = 0; j < p_pack_data.file_ofs[i].path_utf8.length(); j++) {
|
|
|
+ seed = ((seed << 5) + seed) ^ p_pack_data.file_ofs[i].path_utf8.get_data()[j];
|
|
|
+ }
|
|
|
+ for (int64_t j = 0; j < p_pack_data.file_ofs[i].md5.size(); j++) {
|
|
|
+ seed = ((seed << 5) + seed) ^ p_pack_data.file_ofs[i].md5[j];
|
|
|
+ }
|
|
|
+ seed = ((seed << 5) + seed) ^ (p_pack_data.file_ofs[i].ofs - p_file_base);
|
|
|
+ seed = ((seed << 5) + seed) ^ p_pack_data.file_ofs[i].size;
|
|
|
+ }
|
|
|
+
|
|
|
+ RandomPCG rng = RandomPCG(seed);
|
|
|
+ iv.resize(16);
|
|
|
+ for (int i = 0; i < 16; i++) {
|
|
|
+ iv.write[i] = rng.rand() % 256;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ Error err = fae->open_and_parse(fhead, p_key, FileAccessEncrypted::MODE_WRITE_AES256, false, iv);
|
|
|
+ if (err != OK) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ fhead = fae;
|
|
|
+ }
|
|
|
+ for (int i = 0; i < p_pack_data.file_ofs.size(); i++) {
|
|
|
+ uint32_t string_len = p_pack_data.file_ofs[i].path_utf8.length();
|
|
|
+ uint32_t pad = _get_pad(4, string_len);
|
|
|
+
|
|
|
+ fhead->store_32(string_len + pad);
|
|
|
+ fhead->store_buffer((const uint8_t *)p_pack_data.file_ofs[i].path_utf8.get_data(), string_len);
|
|
|
+ for (uint32_t j = 0; j < pad; j++) {
|
|
|
+ fhead->store_8(0);
|
|
|
+ }
|
|
|
+
|
|
|
+ fhead->store_64(p_pack_data.file_ofs[i].ofs - p_file_base);
|
|
|
+ fhead->store_64(p_pack_data.file_ofs[i].size); // pay attention here, this is where file is
|
|
|
+ fhead->store_buffer(p_pack_data.file_ofs[i].md5.ptr(), 16); //also save md5 for file
|
|
|
+ uint32_t flags = 0;
|
|
|
+ if (p_pack_data.file_ofs[i].encrypted) {
|
|
|
+ flags |= PACK_FILE_ENCRYPTED;
|
|
|
+ }
|
|
|
+ if (p_pack_data.file_ofs[i].removal) {
|
|
|
+ flags |= PACK_FILE_REMOVAL;
|
|
|
+ }
|
|
|
+ fhead->store_32(flags);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (fae.is_valid()) {
|
|
|
+ fhead.unref();
|
|
|
+ fae.unref();
|
|
|
+ }
|
|
|
+ return true;
|
|
|
+}
|
|
|
+
|
|
|
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);
|
|
|
|
|
@@ -1940,31 +2055,10 @@ Error EditorExportPlatform::save_pack(const Ref<EditorExportPreset> &p_preset, b
|
|
|
}
|
|
|
|
|
|
int64_t pck_start_pos = f->get_position();
|
|
|
+ uint64_t file_base_ofs = 0;
|
|
|
+ uint64_t dir_base_ofs = 0;
|
|
|
|
|
|
- // Write header.
|
|
|
- f->store_32(PACK_HEADER_MAGIC);
|
|
|
- f->store_32(PACK_FORMAT_VERSION);
|
|
|
- f->store_32(GODOT_VERSION_MAJOR);
|
|
|
- f->store_32(GODOT_VERSION_MINOR);
|
|
|
- f->store_32(GODOT_VERSION_PATCH);
|
|
|
-
|
|
|
- uint32_t pack_flags = PACK_REL_FILEBASE;
|
|
|
- bool enc_pck = p_preset->get_enc_pck();
|
|
|
- bool enc_directory = p_preset->get_enc_directory();
|
|
|
- if (enc_pck && enc_directory) {
|
|
|
- pack_flags |= PACK_DIR_ENCRYPTED;
|
|
|
- }
|
|
|
- f->store_32(pack_flags); // Flags.
|
|
|
-
|
|
|
- uint64_t file_base_ofs = f->get_position();
|
|
|
- f->store_64(0); // Files base.
|
|
|
-
|
|
|
- uint64_t dir_base_ofs = f->get_position();
|
|
|
- f->store_64(0); // Directory offset.
|
|
|
-
|
|
|
- for (int i = 0; i < 16; i++) {
|
|
|
- f->store_32(0); // Reserved.
|
|
|
- }
|
|
|
+ _store_header(f, p_preset->get_enc_pck() && p_preset->get_enc_directory(), false, file_base_ofs, dir_base_ofs);
|
|
|
|
|
|
// Align for first file.
|
|
|
int file_padding = _get_pad(PCK_PADDING, f->get_position());
|
|
@@ -1982,6 +2076,7 @@ Error EditorExportPlatform::save_pack(const Ref<EditorExportPreset> &p_preset, b
|
|
|
pd.ep = &ep;
|
|
|
pd.f = f;
|
|
|
pd.so_files = p_so_files;
|
|
|
+ pd.path = p_path;
|
|
|
|
|
|
Error err = export_project_files(p_preset, p_debug, p_save_func, p_remove_func, &pd, _pack_add_shared_object);
|
|
|
|
|
@@ -2008,15 +2103,9 @@ Error EditorExportPlatform::save_pack(const Ref<EditorExportPreset> &p_preset, b
|
|
|
f->store_64(dir_offset - pck_start_pos);
|
|
|
f->seek(dir_offset);
|
|
|
|
|
|
- f->store_32(pd.file_ofs.size());
|
|
|
-
|
|
|
- Ref<FileAccessEncrypted> fae;
|
|
|
- Ref<FileAccess> fhead = f;
|
|
|
-
|
|
|
- if (enc_pck && enc_directory) {
|
|
|
- uint64_t seed = p_preset->get_seed();
|
|
|
+ Vector<uint8_t> key;
|
|
|
+ if (p_preset->get_enc_pck() && p_preset->get_enc_directory()) {
|
|
|
String script_key = _get_script_encryption_key(p_preset);
|
|
|
- Vector<uint8_t> key;
|
|
|
key.resize(32);
|
|
|
if (script_key.length() == 64) {
|
|
|
for (int i = 0; i < 32; i++) {
|
|
@@ -2043,67 +2132,11 @@ Error EditorExportPlatform::save_pack(const Ref<EditorExportPreset> &p_preset, b
|
|
|
key.write[i] = v;
|
|
|
}
|
|
|
}
|
|
|
- fae.instantiate();
|
|
|
- if (fae.is_null()) {
|
|
|
- add_message(EXPORT_MESSAGE_ERROR, TTR("Save PCK"), TTR("Can't create encrypted file."));
|
|
|
- return ERR_CANT_CREATE;
|
|
|
- }
|
|
|
-
|
|
|
- Vector<uint8_t> iv;
|
|
|
- if (seed != 0) {
|
|
|
- for (int i = 0; i < pd.file_ofs.size(); i++) {
|
|
|
- for (int64_t j = 0; j < pd.file_ofs[i].path_utf8.length(); j++) {
|
|
|
- seed = ((seed << 5) + seed) ^ pd.file_ofs[i].path_utf8.get_data()[j];
|
|
|
- }
|
|
|
- for (int64_t j = 0; j < pd.file_ofs[i].md5.size(); j++) {
|
|
|
- seed = ((seed << 5) + seed) ^ pd.file_ofs[i].md5[j];
|
|
|
- }
|
|
|
- seed = ((seed << 5) + seed) ^ (pd.file_ofs[i].ofs - file_base);
|
|
|
- seed = ((seed << 5) + seed) ^ pd.file_ofs[i].size;
|
|
|
- }
|
|
|
-
|
|
|
- RandomPCG rng = RandomPCG(seed);
|
|
|
- iv.resize(16);
|
|
|
- for (int i = 0; i < 16; i++) {
|
|
|
- iv.write[i] = rng.rand() % 256;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- err = fae->open_and_parse(f, key, FileAccessEncrypted::MODE_WRITE_AES256, false, iv);
|
|
|
- if (err != OK) {
|
|
|
- add_message(EXPORT_MESSAGE_ERROR, TTR("Save PCK"), TTR("Can't open encrypted file to write."));
|
|
|
- return ERR_CANT_CREATE;
|
|
|
- }
|
|
|
-
|
|
|
- fhead = fae;
|
|
|
- }
|
|
|
-
|
|
|
- for (int i = 0; i < pd.file_ofs.size(); i++) {
|
|
|
- uint32_t string_len = pd.file_ofs[i].path_utf8.length();
|
|
|
- uint32_t pad = _get_pad(4, string_len);
|
|
|
-
|
|
|
- fhead->store_32(string_len + pad);
|
|
|
- fhead->store_buffer((const uint8_t *)pd.file_ofs[i].path_utf8.get_data(), string_len);
|
|
|
- for (uint32_t j = 0; j < pad; j++) {
|
|
|
- fhead->store_8(0);
|
|
|
- }
|
|
|
-
|
|
|
- fhead->store_64(pd.file_ofs[i].ofs - file_base);
|
|
|
- fhead->store_64(pd.file_ofs[i].size);
|
|
|
- fhead->store_buffer(pd.file_ofs[i].md5.ptr(), 16);
|
|
|
- uint32_t flags = 0;
|
|
|
- if (pd.file_ofs[i].encrypted) {
|
|
|
- flags |= PACK_FILE_ENCRYPTED;
|
|
|
- }
|
|
|
- if (pd.file_ofs[i].removal) {
|
|
|
- flags |= PACK_FILE_REMOVAL;
|
|
|
- }
|
|
|
- fhead->store_32(flags);
|
|
|
}
|
|
|
|
|
|
- if (fae.is_valid()) {
|
|
|
- fhead.unref();
|
|
|
- fae.unref();
|
|
|
+ if (!_encrypt_and_store_directory(f, pd, key, p_preset->get_seed(), file_base)) {
|
|
|
+ add_message(EXPORT_MESSAGE_ERROR, TTR("Save PCK"), TTR("Can't create encrypted file."));
|
|
|
+ return ERR_CANT_CREATE;
|
|
|
}
|
|
|
|
|
|
if (p_embed) {
|