You've already forked godot
mirror of
https://github.com/godotengine/godot.git
synced 2025-12-02 16:48:55 +00:00
Add support for delta encoding to patch PCKs
This commit is contained in:
110
core/io/delta_encoding.cpp
Normal file
110
core/io/delta_encoding.cpp
Normal file
@@ -0,0 +1,110 @@
|
|||||||
|
/**************************************************************************/
|
||||||
|
/* delta_encoding.cpp */
|
||||||
|
/**************************************************************************/
|
||||||
|
/* This file is part of: */
|
||||||
|
/* GODOT ENGINE */
|
||||||
|
/* https://godotengine.org */
|
||||||
|
/**************************************************************************/
|
||||||
|
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||||
|
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||||
|
/* */
|
||||||
|
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||||
|
/* a copy of this software and associated documentation files (the */
|
||||||
|
/* "Software"), to deal in the Software without restriction, including */
|
||||||
|
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||||
|
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||||
|
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||||
|
/* the following conditions: */
|
||||||
|
/* */
|
||||||
|
/* The above copyright notice and this permission notice shall be */
|
||||||
|
/* included in all copies or substantial portions of the Software. */
|
||||||
|
/* */
|
||||||
|
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||||
|
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||||
|
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||||
|
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||||
|
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||||
|
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||||
|
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||||
|
/**************************************************************************/
|
||||||
|
|
||||||
|
#include "delta_encoding.h"
|
||||||
|
|
||||||
|
#include <zstd.h>
|
||||||
|
|
||||||
|
#define ERR_FAIL_ZSTD_V_MSG(m_result, m_retval, m_msg) \
|
||||||
|
ERR_FAIL_COND_V_MSG(ZSTD_isError(m_result), m_retval, vformat("%s Zstandard reported error code %d: \"%s\".", m_msg, ZSTD_getErrorCode(m_result), ZSTD_getErrorString(ZSTD_getErrorCode(m_result))))
|
||||||
|
|
||||||
|
struct ZstdCompressionContext {
|
||||||
|
ZSTD_CCtx *context = ZSTD_createCCtx();
|
||||||
|
~ZstdCompressionContext() { ZSTD_freeCCtx(context); }
|
||||||
|
operator ZSTD_CCtx *() { return context; }
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ZstdDecompressionContext {
|
||||||
|
ZSTD_DCtx *context = ZSTD_createDCtx();
|
||||||
|
~ZstdDecompressionContext() { ZSTD_freeDCtx(context); }
|
||||||
|
operator ZSTD_DCtx *() { return context; }
|
||||||
|
};
|
||||||
|
|
||||||
|
static constexpr uint8_t DELTA_MAGIC[4] = { 'G', 'D', 'D', 'L' };
|
||||||
|
static constexpr uint8_t DELTA_VERSION_NUMBER = 1;
|
||||||
|
static constexpr size_t DELTA_HEADER_SIZE = 5;
|
||||||
|
|
||||||
|
Error DeltaEncoding::encode_delta(Span<uint8_t> p_old_data, Span<uint8_t> p_new_data, Vector<uint8_t> &r_delta, int p_compression_level) {
|
||||||
|
size_t zstd_result = ZSTD_compressBound(p_new_data.size());
|
||||||
|
ERR_FAIL_ZSTD_V_MSG(zstd_result, FAILED, "Failed to encode delta. Calculating compression bounds failed.");
|
||||||
|
|
||||||
|
r_delta.reserve_exact(DELTA_HEADER_SIZE + zstd_result);
|
||||||
|
r_delta.resize(DELTA_HEADER_SIZE + zstd_result);
|
||||||
|
|
||||||
|
memcpy(r_delta.ptrw(), DELTA_MAGIC, 4);
|
||||||
|
r_delta.write[4] = DELTA_VERSION_NUMBER;
|
||||||
|
|
||||||
|
ZstdCompressionContext zstd_context;
|
||||||
|
|
||||||
|
ZSTD_parameters zstd_params = ZSTD_getParams(p_compression_level, p_new_data.size(), p_old_data.size());
|
||||||
|
zstd_params.fParams.contentSizeFlag = 1;
|
||||||
|
zstd_params.fParams.checksumFlag = 1;
|
||||||
|
|
||||||
|
zstd_result = ZSTD_CCtx_setParams(zstd_context, zstd_params);
|
||||||
|
ERR_FAIL_ZSTD_V_MSG(zstd_result, FAILED, "Failed to encode delta. Setting compression parameters failed.");
|
||||||
|
|
||||||
|
zstd_result = ZSTD_CCtx_refPrefix(zstd_context, p_old_data.ptr(), p_old_data.size());
|
||||||
|
ERR_FAIL_ZSTD_V_MSG(zstd_result, FAILED, "Failed to encode delta. Setting prefix dictionary failed.");
|
||||||
|
|
||||||
|
zstd_result = ZSTD_compress2(zstd_context, r_delta.ptrw() + DELTA_HEADER_SIZE, r_delta.size() - DELTA_HEADER_SIZE, p_new_data.ptr(), p_new_data.size());
|
||||||
|
ERR_FAIL_ZSTD_V_MSG(zstd_result, FAILED, "Failed to encode delta. Compression failed.");
|
||||||
|
|
||||||
|
r_delta.resize(DELTA_HEADER_SIZE + zstd_result);
|
||||||
|
|
||||||
|
return OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
Error DeltaEncoding::decode_delta(Span<uint8_t> p_old_data, Span<uint8_t> p_delta, Vector<uint8_t> &r_new_data) {
|
||||||
|
ERR_FAIL_COND_V_MSG(p_delta.size() < DELTA_HEADER_SIZE, ERR_INVALID_DATA, vformat("Failed to decode delta. File size (%d) is too small.", p_delta.size()));
|
||||||
|
|
||||||
|
uint8_t magic[4];
|
||||||
|
memcpy(magic, p_delta.ptr(), sizeof(magic));
|
||||||
|
uint8_t version = p_delta[4];
|
||||||
|
|
||||||
|
ERR_FAIL_COND_V_MSG(memcmp(magic, DELTA_MAGIC, 4) != 0, ERR_FILE_CORRUPT, "Failed to decode delta. Header is invalid.");
|
||||||
|
ERR_FAIL_COND_V_MSG(version != DELTA_VERSION_NUMBER, ERR_FILE_UNRECOGNIZED, vformat("Failed to decode delta. Expected version %d but found %d.", DELTA_VERSION_NUMBER, version));
|
||||||
|
|
||||||
|
size_t zstd_result = ZSTD_findDecompressedSize(p_delta.ptr() + DELTA_HEADER_SIZE, p_delta.size() - DELTA_HEADER_SIZE);
|
||||||
|
ERR_FAIL_ZSTD_V_MSG(zstd_result, FAILED, "Failed to decode delta. Unable to find decompressed size.");
|
||||||
|
|
||||||
|
r_new_data.reserve_exact(zstd_result);
|
||||||
|
r_new_data.resize(zstd_result);
|
||||||
|
|
||||||
|
ZstdDecompressionContext zstd_context;
|
||||||
|
|
||||||
|
zstd_result = ZSTD_DCtx_refPrefix(zstd_context, p_old_data.ptr(), p_old_data.size());
|
||||||
|
ERR_FAIL_ZSTD_V_MSG(zstd_result, FAILED, "Failed to decode delta. Setting prefix dictionary failed.");
|
||||||
|
|
||||||
|
zstd_result = ZSTD_decompressDCtx(zstd_context, r_new_data.ptrw(), r_new_data.size(), p_delta.ptr() + DELTA_HEADER_SIZE, p_delta.size() - DELTA_HEADER_SIZE);
|
||||||
|
ERR_FAIL_ZSTD_V_MSG(zstd_result, FAILED, "Failed to decode delta. Decompression failed.");
|
||||||
|
ERR_FAIL_COND_V(zstd_result != (size_t)r_new_data.size(), ERR_FILE_CORRUPT);
|
||||||
|
|
||||||
|
return OK;
|
||||||
|
}
|
||||||
39
core/io/delta_encoding.h
Normal file
39
core/io/delta_encoding.h
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
/**************************************************************************/
|
||||||
|
/* delta_encoding.h */
|
||||||
|
/**************************************************************************/
|
||||||
|
/* This file is part of: */
|
||||||
|
/* GODOT ENGINE */
|
||||||
|
/* https://godotengine.org */
|
||||||
|
/**************************************************************************/
|
||||||
|
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||||
|
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||||
|
/* */
|
||||||
|
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||||
|
/* a copy of this software and associated documentation files (the */
|
||||||
|
/* "Software"), to deal in the Software without restriction, including */
|
||||||
|
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||||
|
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||||
|
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||||
|
/* the following conditions: */
|
||||||
|
/* */
|
||||||
|
/* The above copyright notice and this permission notice shall be */
|
||||||
|
/* included in all copies or substantial portions of the Software. */
|
||||||
|
/* */
|
||||||
|
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||||
|
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||||
|
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||||
|
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||||
|
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||||
|
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||||
|
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||||
|
/**************************************************************************/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "core/io/file_access.h"
|
||||||
|
|
||||||
|
class DeltaEncoding {
|
||||||
|
public:
|
||||||
|
static Error encode_delta(Span<uint8_t> p_old_data, Span<uint8_t> p_new_data, Vector<uint8_t> &r_delta, int p_compression_level = 19);
|
||||||
|
static Error decode_delta(Span<uint8_t> p_old_data, Span<uint8_t> p_delta, Vector<uint8_t> &r_new_data);
|
||||||
|
};
|
||||||
@@ -31,6 +31,7 @@
|
|||||||
#include "file_access_pack.h"
|
#include "file_access_pack.h"
|
||||||
|
|
||||||
#include "core/io/file_access_encrypted.h"
|
#include "core/io/file_access_encrypted.h"
|
||||||
|
#include "core/io/file_access_patched.h"
|
||||||
#include "core/object/script_language.h"
|
#include "core/object/script_language.h"
|
||||||
#include "core/os/os.h"
|
#include "core/os/os.h"
|
||||||
#include "core/version.h"
|
#include "core/version.h"
|
||||||
@@ -45,7 +46,7 @@ Error PackedData::add_pack(const String &p_path, bool p_replace_files, uint64_t
|
|||||||
return ERR_FILE_UNRECOGNIZED;
|
return ERR_FILE_UNRECOGNIZED;
|
||||||
}
|
}
|
||||||
|
|
||||||
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, bool p_bundle) {
|
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, bool p_bundle, bool p_delta) {
|
||||||
String simplified_path = p_path.simplify_path().trim_prefix("res://");
|
String simplified_path = p_path.simplify_path().trim_prefix("res://");
|
||||||
PathMD5 pmd5(simplified_path.md5_buffer());
|
PathMD5 pmd5(simplified_path.md5_buffer());
|
||||||
|
|
||||||
@@ -54,6 +55,7 @@ void PackedData::add_path(const String &p_pkg_path, const String &p_path, uint64
|
|||||||
PackedFile pf;
|
PackedFile pf;
|
||||||
pf.encrypted = p_encrypted;
|
pf.encrypted = p_encrypted;
|
||||||
pf.bundle = p_bundle;
|
pf.bundle = p_bundle;
|
||||||
|
pf.delta = p_delta;
|
||||||
pf.pack = p_pkg_path;
|
pf.pack = p_pkg_path;
|
||||||
pf.offset = p_ofs;
|
pf.offset = p_ofs;
|
||||||
pf.size = p_size;
|
pf.size = p_size;
|
||||||
@@ -62,8 +64,11 @@ void PackedData::add_path(const String &p_pkg_path, const String &p_path, uint64
|
|||||||
}
|
}
|
||||||
pf.src = p_src;
|
pf.src = p_src;
|
||||||
|
|
||||||
if (!exists || p_replace_files) {
|
if (p_delta) {
|
||||||
|
delta_patches[pmd5].push_back(pf);
|
||||||
|
} else if (!exists || p_replace_files) {
|
||||||
files[pmd5] = pf;
|
files[pmd5] = pf;
|
||||||
|
delta_patches[pmd5].clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!exists) {
|
if (!exists) {
|
||||||
@@ -137,6 +142,28 @@ uint8_t *PackedData::get_file_hash(const String &p_path) {
|
|||||||
return E->value.md5;
|
return E->value.md5;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Vector<PackedData::PackedFile> PackedData::get_delta_patches(const String &p_path) const {
|
||||||
|
String simplified_path = p_path.simplify_path().trim_prefix("res://");
|
||||||
|
PathMD5 pmd5(simplified_path.md5_buffer());
|
||||||
|
HashMap<PathMD5, Vector<PackedFile>, PathMD5>::ConstIterator E = delta_patches.find(pmd5);
|
||||||
|
if (!E) {
|
||||||
|
return Vector<PackedFile>();
|
||||||
|
}
|
||||||
|
|
||||||
|
return E->value;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PackedData::has_delta_patches(const String &p_path) const {
|
||||||
|
String simplified_path = p_path.simplify_path().trim_prefix("res://");
|
||||||
|
PathMD5 pmd5(simplified_path.md5_buffer());
|
||||||
|
HashMap<PathMD5, Vector<PackedFile>, PathMD5>::ConstIterator E = delta_patches.find(pmd5);
|
||||||
|
if (!E) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return !E->value.is_empty();
|
||||||
|
}
|
||||||
|
|
||||||
HashSet<String> PackedData::get_file_paths() const {
|
HashSet<String> PackedData::get_file_paths() const {
|
||||||
HashSet<String> file_paths;
|
HashSet<String> file_paths;
|
||||||
_get_file_paths(root, root->name, file_paths);
|
_get_file_paths(root, root->name, file_paths);
|
||||||
@@ -155,6 +182,7 @@ void PackedData::_get_file_paths(PackedDir *p_dir, const String &p_parent_dir, H
|
|||||||
|
|
||||||
void PackedData::clear() {
|
void PackedData::clear() {
|
||||||
files.clear();
|
files.clear();
|
||||||
|
delta_patches.clear();
|
||||||
_free_packed_dirs(root);
|
_free_packed_dirs(root);
|
||||||
root = memnew(PackedDir);
|
root = memnew(PackedDir);
|
||||||
}
|
}
|
||||||
@@ -322,7 +350,7 @@ bool PackedSourcePCK::try_open_pack(const String &p_path, bool p_replace_files,
|
|||||||
if (flags & PACK_FILE_REMOVAL) { // The file was removed.
|
if (flags & PACK_FILE_REMOVAL) { // The file was removed.
|
||||||
PackedData::get_singleton()->remove_path(path);
|
PackedData::get_singleton()->remove_path(path);
|
||||||
} else {
|
} else {
|
||||||
PackedData::get_singleton()->add_path(p_path, path, file_base + ofs, size, md5, this, p_replace_files, (flags & PACK_FILE_ENCRYPTED), sparse_bundle);
|
PackedData::get_singleton()->add_path(p_path, path, file_base + ofs, size, md5, this, p_replace_files, (flags & PACK_FILE_ENCRYPTED), sparse_bundle, (flags & PACK_FILE_DELTA));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -330,7 +358,17 @@ bool PackedSourcePCK::try_open_pack(const String &p_path, bool p_replace_files,
|
|||||||
}
|
}
|
||||||
|
|
||||||
Ref<FileAccess> PackedSourcePCK::get_file(const String &p_path, PackedData::PackedFile *p_file) {
|
Ref<FileAccess> PackedSourcePCK::get_file(const String &p_path, PackedData::PackedFile *p_file) {
|
||||||
return memnew(FileAccessPack(p_path, *p_file));
|
Ref<FileAccess> file(memnew(FileAccessPack(p_path, *p_file)));
|
||||||
|
|
||||||
|
if (PackedData::get_singleton()->has_delta_patches(p_path)) {
|
||||||
|
Ref<FileAccessPatched> file_patched;
|
||||||
|
file_patched.instantiate();
|
||||||
|
Error err = file_patched->open_custom(file);
|
||||||
|
ERR_FAIL_COND_V(err != OK, Ref<FileAccess>());
|
||||||
|
file = file_patched;
|
||||||
|
}
|
||||||
|
|
||||||
|
return file;
|
||||||
}
|
}
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////
|
||||||
@@ -362,7 +400,7 @@ void PackedSourceDirectory::add_directory(const String &p_path, bool p_replace_f
|
|||||||
for (const String &file_name : da->get_files()) {
|
for (const String &file_name : da->get_files()) {
|
||||||
String file_path = p_path.path_join(file_name);
|
String file_path = p_path.path_join(file_name);
|
||||||
uint8_t md5[16] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
|
uint8_t md5[16] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
|
||||||
PackedData::get_singleton()->add_path(p_path, file_path, 0, 0, md5, this, p_replace_files, false, false);
|
PackedData::get_singleton()->add_path(p_path, file_path, 0, 0, md5, this, p_replace_files, false, false, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const String &sub_dir_name : da->get_directories()) {
|
for (const String &sub_dir_name : da->get_directories()) {
|
||||||
@@ -470,6 +508,7 @@ void FileAccessPack::close() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
FileAccessPack::FileAccessPack(const String &p_path, const PackedData::PackedFile &p_file) {
|
FileAccessPack::FileAccessPack(const String &p_path, const PackedData::PackedFile &p_file) {
|
||||||
|
path = p_path;
|
||||||
pf = p_file;
|
pf = p_file;
|
||||||
if (pf.bundle) {
|
if (pf.bundle) {
|
||||||
String simplified_path = p_path.simplify_path();
|
String simplified_path = p_path.simplify_path();
|
||||||
|
|||||||
@@ -54,6 +54,7 @@ enum PackFlags {
|
|||||||
enum PackFileFlags {
|
enum PackFileFlags {
|
||||||
PACK_FILE_ENCRYPTED = 1 << 0,
|
PACK_FILE_ENCRYPTED = 1 << 0,
|
||||||
PACK_FILE_REMOVAL = 1 << 1,
|
PACK_FILE_REMOVAL = 1 << 1,
|
||||||
|
PACK_FILE_DELTA = 1 << 2,
|
||||||
};
|
};
|
||||||
|
|
||||||
class PackSource;
|
class PackSource;
|
||||||
@@ -72,6 +73,7 @@ public:
|
|||||||
PackSource *src = nullptr;
|
PackSource *src = nullptr;
|
||||||
bool encrypted;
|
bool encrypted;
|
||||||
bool bundle;
|
bool bundle;
|
||||||
|
bool delta;
|
||||||
};
|
};
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@@ -103,6 +105,7 @@ private:
|
|||||||
};
|
};
|
||||||
|
|
||||||
HashMap<PathMD5, PackedFile, PathMD5> files;
|
HashMap<PathMD5, PackedFile, PathMD5> files;
|
||||||
|
HashMap<PathMD5, Vector<PackedFile>, PathMD5> delta_patches;
|
||||||
|
|
||||||
Vector<PackSource *> sources;
|
Vector<PackSource *> sources;
|
||||||
|
|
||||||
@@ -116,9 +119,11 @@ private:
|
|||||||
|
|
||||||
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, bool p_bundle = 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, bool p_bundle = false, bool p_delta = false); // for PackSource
|
||||||
void remove_path(const String &p_path);
|
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);
|
||||||
|
Vector<PackedFile> get_delta_patches(const String &p_path) const;
|
||||||
|
bool has_delta_patches(const String &p_path) const;
|
||||||
HashSet<String> get_file_paths() const;
|
HashSet<String> get_file_paths() const;
|
||||||
|
|
||||||
void set_disabled(bool p_disabled) { disabled = p_disabled; }
|
void set_disabled(bool p_disabled) { disabled = p_disabled; }
|
||||||
@@ -166,6 +171,7 @@ class FileAccessPack : public FileAccess {
|
|||||||
GDSOFTCLASS(FileAccessPack, FileAccess);
|
GDSOFTCLASS(FileAccessPack, FileAccess);
|
||||||
PackedData::PackedFile pf;
|
PackedData::PackedFile pf;
|
||||||
|
|
||||||
|
String path;
|
||||||
mutable uint64_t pos;
|
mutable uint64_t pos;
|
||||||
mutable bool eof;
|
mutable bool eof;
|
||||||
uint64_t off;
|
uint64_t off;
|
||||||
@@ -186,6 +192,9 @@ class FileAccessPack : public FileAccess {
|
|||||||
public:
|
public:
|
||||||
virtual bool is_open() const override;
|
virtual bool is_open() const override;
|
||||||
|
|
||||||
|
virtual String get_path() const override { return path; }
|
||||||
|
virtual String get_path_absolute() const override { return path; }
|
||||||
|
|
||||||
virtual void seek(uint64_t p_position) override;
|
virtual void seek(uint64_t p_position) override;
|
||||||
virtual void seek_end(int64_t p_position = 0) override;
|
virtual void seek_end(int64_t p_position = 0) override;
|
||||||
virtual uint64_t get_position() const override;
|
virtual uint64_t get_position() const override;
|
||||||
|
|||||||
203
core/io/file_access_patched.cpp
Normal file
203
core/io/file_access_patched.cpp
Normal file
@@ -0,0 +1,203 @@
|
|||||||
|
/**************************************************************************/
|
||||||
|
/* file_access_patched.cpp */
|
||||||
|
/**************************************************************************/
|
||||||
|
/* This file is part of: */
|
||||||
|
/* GODOT ENGINE */
|
||||||
|
/* https://godotengine.org */
|
||||||
|
/**************************************************************************/
|
||||||
|
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||||
|
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||||
|
/* */
|
||||||
|
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||||
|
/* a copy of this software and associated documentation files (the */
|
||||||
|
/* "Software"), to deal in the Software without restriction, including */
|
||||||
|
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||||
|
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||||
|
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||||
|
/* the following conditions: */
|
||||||
|
/* */
|
||||||
|
/* The above copyright notice and this permission notice shall be */
|
||||||
|
/* included in all copies or substantial portions of the Software. */
|
||||||
|
/* */
|
||||||
|
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||||
|
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||||
|
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||||
|
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||||
|
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||||
|
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||||
|
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||||
|
/**************************************************************************/
|
||||||
|
|
||||||
|
#include "file_access_patched.h"
|
||||||
|
|
||||||
|
#include "file_access_pack.h"
|
||||||
|
|
||||||
|
#include "core/io/delta_encoding.h"
|
||||||
|
#include "core/os/os.h"
|
||||||
|
|
||||||
|
Error FileAccessPatched::_apply_patch() const {
|
||||||
|
ERR_FAIL_COND_V(!is_open(), FAILED);
|
||||||
|
|
||||||
|
String path = old_file->get_path();
|
||||||
|
Vector<PackedData::PackedFile> delta_patches = PackedData::get_singleton()->get_delta_patches(path);
|
||||||
|
Vector<uint8_t> old_file_data = old_file->get_buffer(old_file->get_length());
|
||||||
|
|
||||||
|
for (int i = 0; i < delta_patches.size(); ++i) {
|
||||||
|
const PackedData::PackedFile &delta_patch = delta_patches[i];
|
||||||
|
ERR_FAIL_COND_V(delta_patch.bundle, FAILED);
|
||||||
|
|
||||||
|
Error err = OK;
|
||||||
|
|
||||||
|
uint64_t total_usec_start = OS::get_singleton()->get_ticks_usec();
|
||||||
|
uint64_t io_usec_start = OS::get_singleton()->get_ticks_usec();
|
||||||
|
|
||||||
|
Ref<FileAccess> patch_file = FileAccess::open(delta_patch.pack, FileAccess::READ, &err);
|
||||||
|
ERR_FAIL_COND_V(err != OK, err);
|
||||||
|
|
||||||
|
patch_file->seek(delta_patch.offset);
|
||||||
|
ERR_FAIL_COND_V(patch_file->get_error() != OK, patch_file->get_error());
|
||||||
|
|
||||||
|
Vector<uint8_t> patch_data = patch_file->get_buffer(delta_patch.size);
|
||||||
|
ERR_FAIL_COND_V(patch_data.is_empty(), ERR_FILE_CANT_READ);
|
||||||
|
|
||||||
|
uint64_t io_usec_end = OS::get_singleton()->get_ticks_usec();
|
||||||
|
uint64_t decode_usec_start = OS::get_singleton()->get_ticks_usec();
|
||||||
|
|
||||||
|
Vector<uint8_t> new_file_data;
|
||||||
|
err = DeltaEncoding::decode_delta(old_file_data, patch_data, new_file_data);
|
||||||
|
ERR_FAIL_COND_V_MSG(err != OK, err, vformat("Failed to apply delta patch (%d of %d) to \"%s\".", i + 1, delta_patches.size(), path));
|
||||||
|
|
||||||
|
uint64_t decode_usec_end = OS::get_singleton()->get_ticks_usec();
|
||||||
|
|
||||||
|
old_file_data = new_file_data;
|
||||||
|
|
||||||
|
uint64_t total_usec_end = OS::get_singleton()->get_ticks_usec();
|
||||||
|
|
||||||
|
print_verbose(vformat(U"Applied delta patch to \"%s\" from \"%s\" in %d μs (%d μs I/O, %d μs decoding).", path, delta_patch.pack.get_file(), total_usec_end - total_usec_start, io_usec_end - io_usec_start, decode_usec_end - decode_usec_start));
|
||||||
|
}
|
||||||
|
|
||||||
|
patched_file_data = old_file_data;
|
||||||
|
patched_file.instantiate();
|
||||||
|
return patched_file->open_custom(patched_file_data.ptr(), patched_file_data.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FileAccessPatched::_try_apply_patch() const {
|
||||||
|
if (last_error != OK) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (patched_file.is_valid()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
last_error = _apply_patch();
|
||||||
|
return last_error == OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
Error FileAccessPatched::open_custom(const Ref<FileAccess> &p_old_file) {
|
||||||
|
close();
|
||||||
|
|
||||||
|
if (!p_old_file->is_open()) {
|
||||||
|
last_error = ERR_FILE_CANT_OPEN;
|
||||||
|
return last_error;
|
||||||
|
}
|
||||||
|
|
||||||
|
old_file = p_old_file;
|
||||||
|
|
||||||
|
return OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FileAccessPatched::is_open() const {
|
||||||
|
return old_file.is_valid() && old_file->is_open();
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileAccessPatched::seek(uint64_t p_position) {
|
||||||
|
if (!_try_apply_patch()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
patched_file->seek(p_position);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileAccessPatched::seek_end(int64_t p_position) {
|
||||||
|
if (!_try_apply_patch()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
patched_file->seek_end(p_position);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t FileAccessPatched::get_position() const {
|
||||||
|
if (!_try_apply_patch()) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return patched_file->get_position();
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t FileAccessPatched::get_length() const {
|
||||||
|
if (!_try_apply_patch()) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return patched_file->get_length();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FileAccessPatched::eof_reached() const {
|
||||||
|
if (!_try_apply_patch()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return patched_file->eof_reached();
|
||||||
|
}
|
||||||
|
|
||||||
|
Error FileAccessPatched::get_error() const {
|
||||||
|
if (last_error != OK) {
|
||||||
|
return last_error;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (patched_file.is_valid()) {
|
||||||
|
Error inner_error = patched_file->get_error();
|
||||||
|
if (inner_error != OK) {
|
||||||
|
return inner_error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return last_error;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FileAccessPatched::store_buffer(const uint8_t *p_src, uint64_t p_length) {
|
||||||
|
if (!_try_apply_patch()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return patched_file->store_buffer(p_src, p_length);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t FileAccessPatched::get_buffer(uint8_t *p_dst, uint64_t p_length) const {
|
||||||
|
if (!_try_apply_patch()) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return patched_file->get_buffer(p_dst, p_length);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileAccessPatched::flush() {
|
||||||
|
if (!_try_apply_patch()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
patched_file->flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileAccessPatched::close() {
|
||||||
|
old_file = Ref<FileAccess>();
|
||||||
|
patched_file = Ref<FileAccessMemory>();
|
||||||
|
patched_file_data.clear();
|
||||||
|
last_error = OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FileAccessPatched::file_exists(const String &p_name) {
|
||||||
|
ERR_FAIL_COND_V(old_file.is_null(), false);
|
||||||
|
return old_file->file_exists(p_name);
|
||||||
|
}
|
||||||
84
core/io/file_access_patched.h
Normal file
84
core/io/file_access_patched.h
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
/**************************************************************************/
|
||||||
|
/* file_access_patched.h */
|
||||||
|
/**************************************************************************/
|
||||||
|
/* This file is part of: */
|
||||||
|
/* GODOT ENGINE */
|
||||||
|
/* https://godotengine.org */
|
||||||
|
/**************************************************************************/
|
||||||
|
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||||
|
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||||
|
/* */
|
||||||
|
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||||
|
/* a copy of this software and associated documentation files (the */
|
||||||
|
/* "Software"), to deal in the Software without restriction, including */
|
||||||
|
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||||
|
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||||
|
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||||
|
/* the following conditions: */
|
||||||
|
/* */
|
||||||
|
/* The above copyright notice and this permission notice shall be */
|
||||||
|
/* included in all copies or substantial portions of the Software. */
|
||||||
|
/* */
|
||||||
|
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||||
|
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||||
|
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||||
|
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||||
|
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||||
|
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||||
|
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||||
|
/**************************************************************************/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "file_access.h"
|
||||||
|
#include "file_access_memory.h"
|
||||||
|
|
||||||
|
class FileAccessPatched : public FileAccess {
|
||||||
|
GDSOFTCLASS(FileAccessPatched, FileAccess);
|
||||||
|
|
||||||
|
Ref<FileAccess> old_file;
|
||||||
|
mutable Vector<uint8_t> patched_file_data;
|
||||||
|
mutable Ref<FileAccessMemory> patched_file;
|
||||||
|
mutable Error last_error = OK;
|
||||||
|
|
||||||
|
Error _apply_patch() const;
|
||||||
|
bool _try_apply_patch() const;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual BitField<UnixPermissionFlags> _get_unix_permissions(const String &p_file) override { return 0; }
|
||||||
|
virtual Error _set_unix_permissions(const String &p_file, BitField<UnixPermissionFlags> p_permissions) override { return FAILED; }
|
||||||
|
|
||||||
|
virtual bool _get_hidden_attribute(const String &p_file) override { return false; }
|
||||||
|
virtual Error _set_hidden_attribute(const String &p_file, bool p_hidden) override { return ERR_UNAVAILABLE; }
|
||||||
|
|
||||||
|
virtual bool _get_read_only_attribute(const String &p_file) override { return false; }
|
||||||
|
virtual Error _set_read_only_attribute(const String &p_file, bool p_ro) override { return ERR_UNAVAILABLE; }
|
||||||
|
|
||||||
|
virtual uint64_t _get_modified_time(const String &p_file) override { return 0; }
|
||||||
|
virtual uint64_t _get_access_time(const String &p_file) override { return 0; }
|
||||||
|
virtual int64_t _get_size(const String &p_file) override { return -1; }
|
||||||
|
|
||||||
|
virtual Error open_internal(const String &p_path, int p_mode_flags) override { return ERR_UNAVAILABLE; }
|
||||||
|
|
||||||
|
public:
|
||||||
|
Error open_custom(const Ref<FileAccess> &p_old_file);
|
||||||
|
|
||||||
|
virtual bool is_open() const override;
|
||||||
|
|
||||||
|
virtual void seek(uint64_t p_position) override;
|
||||||
|
virtual void seek_end(int64_t p_position = 0) override;
|
||||||
|
|
||||||
|
virtual uint64_t get_position() const override;
|
||||||
|
virtual uint64_t get_length() const override;
|
||||||
|
virtual bool eof_reached() const override;
|
||||||
|
virtual Error get_error() const override;
|
||||||
|
|
||||||
|
virtual bool store_buffer(const uint8_t *p_src, uint64_t p_length) override;
|
||||||
|
virtual uint64_t get_buffer(uint8_t *p_dst, uint64_t p_length) const override;
|
||||||
|
virtual Error resize(int64_t p_length) override { return ERR_UNAVAILABLE; }
|
||||||
|
|
||||||
|
virtual void flush() override;
|
||||||
|
virtual void close() override;
|
||||||
|
|
||||||
|
virtual bool file_exists(const String &p_name) override;
|
||||||
|
};
|
||||||
@@ -83,7 +83,13 @@ void EditorExport::_save() {
|
|||||||
config->set_value(section, "include_filter", preset->get_include_filter());
|
config->set_value(section, "include_filter", preset->get_include_filter());
|
||||||
config->set_value(section, "exclude_filter", preset->get_exclude_filter());
|
config->set_value(section, "exclude_filter", preset->get_exclude_filter());
|
||||||
config->set_value(section, "export_path", preset->get_export_path());
|
config->set_value(section, "export_path", preset->get_export_path());
|
||||||
|
|
||||||
config->set_value(section, "patches", preset->get_patches());
|
config->set_value(section, "patches", preset->get_patches());
|
||||||
|
config->set_value(section, "patch_delta_encoding", preset->is_patch_delta_encoding_enabled());
|
||||||
|
config->set_value(section, "patch_delta_compression_level_zstd", preset->get_patch_delta_zstd_level());
|
||||||
|
config->set_value(section, "patch_delta_min_reduction", preset->get_patch_delta_min_reduction());
|
||||||
|
config->set_value(section, "patch_delta_include_filters", preset->get_patch_delta_include_filter());
|
||||||
|
config->set_value(section, "patch_delta_exclude_filters", preset->get_patch_delta_exclude_filter());
|
||||||
|
|
||||||
config->set_value(section, "encryption_include_filters", preset->get_enc_in_filter());
|
config->set_value(section, "encryption_include_filters", preset->get_enc_in_filter());
|
||||||
config->set_value(section, "encryption_exclude_filters", preset->get_enc_ex_filter());
|
config->set_value(section, "encryption_exclude_filters", preset->get_enc_ex_filter());
|
||||||
@@ -332,6 +338,22 @@ void EditorExport::load_config() {
|
|||||||
preset->set_script_export_mode(config->get_value(section, "script_export_mode", EditorExportPreset::MODE_SCRIPT_BINARY_TOKENS_COMPRESSED));
|
preset->set_script_export_mode(config->get_value(section, "script_export_mode", EditorExportPreset::MODE_SCRIPT_BINARY_TOKENS_COMPRESSED));
|
||||||
preset->set_patches(config->get_value(section, "patches", Vector<String>()));
|
preset->set_patches(config->get_value(section, "patches", Vector<String>()));
|
||||||
|
|
||||||
|
if (config->has_section_key(section, "patch_delta_encoding")) {
|
||||||
|
preset->set_patch_delta_encoding_enabled(config->get_value(section, "patch_delta_encoding"));
|
||||||
|
}
|
||||||
|
if (config->has_section_key(section, "patch_delta_compression_level_zstd")) {
|
||||||
|
preset->set_patch_delta_zstd_level(config->get_value(section, "patch_delta_compression_level_zstd"));
|
||||||
|
}
|
||||||
|
if (config->has_section_key(section, "patch_delta_min_reduction")) {
|
||||||
|
preset->set_patch_delta_min_reduction(config->get_value(section, "patch_delta_min_reduction"));
|
||||||
|
}
|
||||||
|
if (config->has_section_key(section, "patch_delta_include_filters")) {
|
||||||
|
preset->set_patch_delta_include_filter(config->get_value(section, "patch_delta_include_filters"));
|
||||||
|
}
|
||||||
|
if (config->has_section_key(section, "patch_delta_exclude_filters")) {
|
||||||
|
preset->set_patch_delta_exclude_filter(config->get_value(section, "patch_delta_exclude_filters"));
|
||||||
|
}
|
||||||
|
|
||||||
if (config->has_section_key(section, "seed")) {
|
if (config->has_section_key(section, "seed")) {
|
||||||
preset->set_seed(config->get_value(section, "seed"));
|
preset->set_seed(config->get_value(section, "seed"));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,6 +35,7 @@
|
|||||||
#include "core/config/project_settings.h"
|
#include "core/config/project_settings.h"
|
||||||
#include "core/crypto/crypto_core.h"
|
#include "core/crypto/crypto_core.h"
|
||||||
#include "core/extension/gdextension.h"
|
#include "core/extension/gdextension.h"
|
||||||
|
#include "core/io/delta_encoding.h"
|
||||||
#include "core/io/dir_access.h"
|
#include "core/io/dir_access.h"
|
||||||
#include "core/io/file_access_encrypted.h"
|
#include "core/io/file_access_encrypted.h"
|
||||||
#include "core/io/file_access_pack.h" // PACK_HEADER_MAGIC, PACK_FORMAT_VERSION
|
#include "core/io/file_access_pack.h" // PACK_HEADER_MAGIC, PACK_FORMAT_VERSION
|
||||||
@@ -66,12 +67,12 @@ class EditorExportSaveProxy {
|
|||||||
public:
|
public:
|
||||||
bool has_saved(const String &p_path) const { return saved_paths.has(p_path); }
|
bool has_saved(const String &p_path) const { return saved_paths.has(p_path); }
|
||||||
|
|
||||||
Error save_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) {
|
Error save_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) {
|
||||||
if (tracking_saves) {
|
if (tracking_saves) {
|
||||||
saved_paths.insert(p_path.simplify_path().trim_prefix("res://"));
|
saved_paths.insert(p_path.simplify_path().trim_prefix("res://"));
|
||||||
}
|
}
|
||||||
|
|
||||||
return save_func(p_userdata, p_path, p_data, p_file, p_total, p_enc_in_filters, p_enc_ex_filters, p_key, p_seed);
|
return save_func(p_preset, p_userdata, p_path, p_data, p_file, p_total, p_enc_in_filters, p_enc_ex_filters, p_key, p_seed, p_delta);
|
||||||
}
|
}
|
||||||
|
|
||||||
EditorExportSaveProxy(EditorExportPlatform::EditorExportSaveFunction p_save_func, bool p_track_saves) :
|
EditorExportSaveProxy(EditorExportPlatform::EditorExportSaveFunction p_save_func, bool p_track_saves) :
|
||||||
@@ -218,26 +219,6 @@ bool EditorExportPlatform::fill_log_messages(RichTextLabel *p_log, Error p_err)
|
|||||||
return has_messages;
|
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 EditorExportPlatform::_load_patches(const Vector<String> &p_patches) {
|
||||||
Error err = OK;
|
Error err = OK;
|
||||||
if (!p_patches.is_empty()) {
|
if (!p_patches.is_empty()) {
|
||||||
@@ -310,7 +291,7 @@ Error EditorExportPlatform::_encrypt_and_store_data(Ref<FileAccess> p_fd, const
|
|||||||
return OK;
|
return OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
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) {
|
Error EditorExportPlatform::_save_pack_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) {
|
||||||
ERR_FAIL_COND_V_MSG(p_total < 1, ERR_PARAMETER_RANGE_ERROR, "Must select at least one file to export.");
|
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;
|
PackData *pd = (PackData *)p_userdata;
|
||||||
@@ -328,6 +309,7 @@ Error EditorExportPlatform::_save_pack_file(void *p_userdata, const String &p_pa
|
|||||||
sd.path_utf8 = simplified_path.trim_prefix("res://").utf8();
|
sd.path_utf8 = simplified_path.trim_prefix("res://").utf8();
|
||||||
sd.ofs = (pd->use_sparse_pck) ? 0 : pd->f->get_position();
|
sd.ofs = (pd->use_sparse_pck) ? 0 : pd->f->get_position();
|
||||||
sd.size = p_data.size();
|
sd.size = p_data.size();
|
||||||
|
sd.delta = p_delta;
|
||||||
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);
|
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) {
|
if (err != OK) {
|
||||||
return err;
|
return err;
|
||||||
@@ -363,15 +345,67 @@ Error EditorExportPlatform::_save_pack_file(void *p_userdata, const String &p_pa
|
|||||||
return OK;
|
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, uint64_t p_seed) {
|
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) {
|
||||||
if (_check_hash(PackedData::get_singleton()->get_file_hash(p_path), p_data)) {
|
Ref<FileAccess> old_file = PackedData::get_singleton()->try_open_path(p_path);
|
||||||
return OK;
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
return _save_pack_file(p_userdata, p_path, p_data, p_file, p_total, p_enc_in_filters, p_enc_ex_filters, p_key, p_seed);
|
Vector<uint8_t> old_data = old_file->get_buffer(old_file->get_length());
|
||||||
|
|
||||||
|
// We can't rely on the MD5 as stored in the PCKs, since delta patches could have made it stale.
|
||||||
|
if (p_data == old_data) {
|
||||||
|
return OK; // Do nothing if the file hasn't changed.
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!p_preset->is_patch_delta_encoding_enabled()) {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool delta = false;
|
||||||
|
|
||||||
|
for (const String &filter : p_preset->get_patch_delta_include_filter().split(",", false)) {
|
||||||
|
String filter_stripped = filter.strip_edges();
|
||||||
|
if (p_path.matchn(filter_stripped) || p_path.trim_prefix("res://").matchn(filter_stripped)) {
|
||||||
|
delta = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const String &filter : p_preset->get_patch_delta_exclude_filter().split(",", false)) {
|
||||||
|
String filter_stripped = filter.strip_edges();
|
||||||
|
if (p_path.matchn(filter_stripped) || p_path.trim_prefix("res://").matchn(filter_stripped)) {
|
||||||
|
delta = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Vector<uint8_t> patch_data = p_data;
|
||||||
|
|
||||||
|
if (delta) {
|
||||||
|
Error err = DeltaEncoding::encode_delta(old_data, p_data, patch_data, p_preset->get_patch_delta_zstd_level());
|
||||||
|
if (err != OK) {
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t reduction_bytes = MAX(0, p_data.size() - patch_data.size());
|
||||||
|
double reduction_ratio = reduction_bytes / (double)p_data.size();
|
||||||
|
|
||||||
|
if (reduction_ratio >= p_preset->get_patch_delta_min_reduction()) {
|
||||||
|
print_verbose(vformat("Used delta encoding for patch of \"%s\", resulting in a patch of %d bytes, which reduced the size by %.1f%% (%d bytes) compared to the actual file.", p_path, patch_data.size(), reduction_ratio * 100, reduction_bytes));
|
||||||
|
} else {
|
||||||
|
print_verbose(vformat("Skipped delta encoding for patch of \"%s\", as it resulted in a patch of %d bytes, which only reduced the size by %.1f%% (%d bytes) compared to the actual file.", p_path, patch_data.size(), reduction_ratio * 100, reduction_bytes));
|
||||||
|
patch_data = p_data;
|
||||||
|
delta = false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
print_verbose(vformat("Skipped delta encoding for patch of \"%s\", due to include/exclude filters.", p_path));
|
||||||
|
}
|
||||||
|
|
||||||
|
return _save_pack_file(p_preset, p_userdata, p_path, patch_data, p_file, p_total, p_enc_in_filters, p_enc_ex_filters, p_key, p_seed, delta);
|
||||||
}
|
}
|
||||||
|
|
||||||
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, uint64_t p_seed) {
|
Error EditorExportPlatform::_save_zip_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) {
|
||||||
ERR_FAIL_COND_V_MSG(p_total < 1, ERR_PARAMETER_RANGE_ERROR, "Must select at least one file to export.");
|
ERR_FAIL_COND_V_MSG(p_total < 1, ERR_PARAMETER_RANGE_ERROR, "Must select at least one file to export.");
|
||||||
|
|
||||||
const String path = simplify_path(p_path).replace_first("res://", "");
|
const String path = simplify_path(p_path).replace_first("res://", "");
|
||||||
@@ -403,12 +437,18 @@ Error EditorExportPlatform::_save_zip_file(void *p_userdata, const String &p_pat
|
|||||||
return OK;
|
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, uint64_t p_seed) {
|
Error EditorExportPlatform::_save_zip_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) {
|
||||||
if (_check_hash(PackedData::get_singleton()->get_file_hash(p_path), p_data)) {
|
Ref<FileAccess> old_file = PackedData::get_singleton()->try_open_path(p_path);
|
||||||
return OK;
|
if (old_file.is_valid()) {
|
||||||
|
Vector<uint8_t> old_data = old_file->get_buffer(old_file->get_length());
|
||||||
|
|
||||||
|
// We can't rely on the MD5 as stored in the PCKs, since delta patches could have made it stale.
|
||||||
|
if (p_data == old_data) {
|
||||||
|
return OK; // Do nothing if the file hasn't changed.
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return _save_zip_file(p_userdata, p_path, p_data, p_file, p_total, p_enc_in_filters, p_enc_ex_filters, p_key, p_seed);
|
return _save_zip_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, p_delta);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ref<Texture2D> EditorExportPlatform::get_option_icon(int p_index) const {
|
Ref<Texture2D> EditorExportPlatform::get_option_icon(int p_index) const {
|
||||||
@@ -1036,7 +1076,7 @@ Vector<String> EditorExportPlatform::get_forced_export_files(const Ref<EditorExp
|
|||||||
return files;
|
return files;
|
||||||
}
|
}
|
||||||
|
|
||||||
Error EditorExportPlatform::_script_save_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) {
|
Error EditorExportPlatform::_script_save_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) {
|
||||||
Callable cb = ((ScriptCallbackData *)p_userdata)->file_cb;
|
Callable cb = ((ScriptCallbackData *)p_userdata)->file_cb;
|
||||||
ERR_FAIL_COND_V(!cb.is_valid(), FAILED);
|
ERR_FAIL_COND_V(!cb.is_valid(), FAILED);
|
||||||
|
|
||||||
@@ -1060,7 +1100,7 @@ Error EditorExportPlatform::_script_save_file(void *p_userdata, const String &p_
|
|||||||
return (Error)ret.operator int();
|
return (Error)ret.operator int();
|
||||||
}
|
}
|
||||||
|
|
||||||
Error EditorExportPlatform::_script_add_shared_object(void *p_userdata, const SharedObject &p_so) {
|
Error EditorExportPlatform::_script_add_shared_object(const Ref<EditorExportPreset> &p_preset, void *p_userdata, const SharedObject &p_so) {
|
||||||
Callable cb = ((ScriptCallbackData *)p_userdata)->so_cb;
|
Callable cb = ((ScriptCallbackData *)p_userdata)->so_cb;
|
||||||
if (!cb.is_valid()) {
|
if (!cb.is_valid()) {
|
||||||
return OK; // Optional.
|
return OK; // Optional.
|
||||||
@@ -1216,14 +1256,14 @@ Error EditorExportPlatform::export_project_files(const Ref<EditorExportPreset> &
|
|||||||
for (int i = 0; i < export_plugins.size(); i++) {
|
for (int i = 0; i < export_plugins.size(); i++) {
|
||||||
if (p_so_func) {
|
if (p_so_func) {
|
||||||
for (int j = 0; j < export_plugins[i]->shared_objects.size(); j++) {
|
for (int j = 0; j < export_plugins[i]->shared_objects.size(); j++) {
|
||||||
err = p_so_func(p_udata, export_plugins[i]->shared_objects[j]);
|
err = p_so_func(p_preset, p_udata, export_plugins[i]->shared_objects[j]);
|
||||||
if (err != OK) {
|
if (err != OK) {
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
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 = save_proxy.save_file(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, seed);
|
err = save_proxy.save_file(p_preset, 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, seed, false);
|
||||||
if (err != OK) {
|
if (err != OK) {
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
@@ -1315,9 +1355,8 @@ Error EditorExportPlatform::export_project_files(const Ref<EditorExportPreset> &
|
|||||||
// for continue statements without accidentally skipping an increment.
|
// for continue statements without accidentally skipping an increment.
|
||||||
int idx = total > 0 ? -1 : 0;
|
int idx = total > 0 ? -1 : 0;
|
||||||
|
|
||||||
for (const String &E : paths) {
|
for (const String &path : paths) {
|
||||||
idx++;
|
idx++;
|
||||||
String path = E;
|
|
||||||
String type = ResourceLoader::get_resource_type(path);
|
String type = ResourceLoader::get_resource_type(path);
|
||||||
|
|
||||||
bool has_import_file = FileAccess::exists(path + ".import");
|
bool has_import_file = FileAccess::exists(path + ".import");
|
||||||
@@ -1347,7 +1386,7 @@ Error EditorExportPlatform::export_project_files(const Ref<EditorExportPreset> &
|
|||||||
}
|
}
|
||||||
if (p_so_func) {
|
if (p_so_func) {
|
||||||
for (int j = 0; j < export_plugins[i]->shared_objects.size(); j++) {
|
for (int j = 0; j < export_plugins[i]->shared_objects.size(); j++) {
|
||||||
err = p_so_func(p_udata, export_plugins[i]->shared_objects[j]);
|
err = p_so_func(p_preset, p_udata, export_plugins[i]->shared_objects[j]);
|
||||||
if (err != OK) {
|
if (err != OK) {
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
@@ -1355,7 +1394,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 = save_proxy.save_file(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, seed);
|
err = save_proxy.save_file(p_preset, 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, seed, false);
|
||||||
if (err != OK) {
|
if (err != OK) {
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
@@ -1385,7 +1424,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 = save_proxy.save_file(p_udata, path, array, idx, total, enc_in_filters, enc_ex_filters, key, seed);
|
err = save_proxy.save_file(p_preset, p_udata, path, array, idx, total, enc_in_filters, enc_ex_filters, key, seed, false);
|
||||||
|
|
||||||
if (err != OK) {
|
if (err != OK) {
|
||||||
return err;
|
return err;
|
||||||
@@ -1427,13 +1466,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 = save_proxy.save_file(p_udata, path + ".import", sarr, idx, total, enc_in_filters, enc_ex_filters, key, seed);
|
err = save_proxy.save_file(p_preset, p_udata, path + ".import", sarr, idx, total, enc_in_filters, enc_ex_filters, key, seed, false);
|
||||||
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 = save_proxy.save_file(p_udata, export_path, sarr, idx, total, enc_in_filters, enc_ex_filters, key, seed);
|
err = save_proxy.save_file(p_preset, p_udata, export_path, sarr, idx, total, enc_in_filters, enc_ex_filters, key, seed, false);
|
||||||
if (err != OK) {
|
if (err != OK) {
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
@@ -1461,14 +1500,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 = save_proxy.save_file(p_udata, remapped_path, array, idx, total, enc_in_filters, enc_ex_filters, key, seed);
|
err = save_proxy.save_file(p_preset, p_udata, remapped_path, array, idx, total, enc_in_filters, enc_ex_filters, key, seed, false);
|
||||||
} else if (remap.begins_with("path.")) {
|
} else if (remap.begins_with("path.")) {
|
||||||
String feature = remap.get_slicec('.', 1);
|
String feature = remap.get_slicec('.', 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 = save_proxy.save_file(p_udata, remapped_path, array, idx, total, enc_in_filters, enc_ex_filters, key, seed);
|
err = save_proxy.save_file(p_preset, p_udata, remapped_path, array, idx, total, enc_in_filters, enc_ex_filters, key, seed, false);
|
||||||
} 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);
|
||||||
@@ -1494,7 +1533,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 = save_proxy.save_file(p_udata, path + ".import", sarr, idx, total, enc_in_filters, enc_ex_filters, key, seed);
|
err = save_proxy.save_file(p_preset, p_udata, path + ".import", sarr, idx, total, enc_in_filters, enc_ex_filters, key, seed, false);
|
||||||
|
|
||||||
if (err != OK) {
|
if (err != OK) {
|
||||||
return err;
|
return err;
|
||||||
@@ -1515,7 +1554,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 = save_proxy.save_file(p_udata, export_path, array, idx, total, enc_in_filters, enc_ex_filters, key, seed);
|
err = save_proxy.save_file(p_preset, p_udata, export_path, array, idx, total, enc_in_filters, enc_ex_filters, key, seed, false);
|
||||||
if (err != OK) {
|
if (err != OK) {
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
@@ -1584,7 +1623,7 @@ Error EditorExportPlatform::export_project_files(const Ref<EditorExportPreset> &
|
|||||||
new_file.write[j] = utf8[j];
|
new_file.write[j] = utf8[j];
|
||||||
}
|
}
|
||||||
|
|
||||||
err = save_proxy.save_file(p_udata, from + ".remap", new_file, idx, total, enc_in_filters, enc_ex_filters, key, seed);
|
err = save_proxy.save_file(p_preset, p_udata, from + ".remap", new_file, idx, total, enc_in_filters, enc_ex_filters, key, seed, false);
|
||||||
if (err != OK) {
|
if (err != OK) {
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
@@ -1602,7 +1641,7 @@ Error EditorExportPlatform::export_project_files(const Ref<EditorExportPreset> &
|
|||||||
} else {
|
} else {
|
||||||
array = FileAccess::get_file_as_bytes(forced_export[i]);
|
array = FileAccess::get_file_as_bytes(forced_export[i]);
|
||||||
}
|
}
|
||||||
err = save_proxy.save_file(p_udata, forced_export[i], array, idx, total, enc_in_filters, enc_ex_filters, key, seed);
|
err = save_proxy.save_file(p_preset, p_udata, forced_export[i], array, idx, total, enc_in_filters, enc_ex_filters, key, seed, false);
|
||||||
if (err != OK) {
|
if (err != OK) {
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
@@ -1611,7 +1650,7 @@ Error EditorExportPlatform::export_project_files(const Ref<EditorExportPreset> &
|
|||||||
Dictionary int_export = get_internal_export_files(p_preset, p_debug);
|
Dictionary int_export = get_internal_export_files(p_preset, p_debug);
|
||||||
for (const KeyValue<Variant, Variant> &int_export_kv : int_export) {
|
for (const KeyValue<Variant, Variant> &int_export_kv : int_export) {
|
||||||
const PackedByteArray &array = int_export_kv.value;
|
const PackedByteArray &array = int_export_kv.value;
|
||||||
err = save_proxy.save_file(p_udata, int_export_kv.key, array, idx, total, enc_in_filters, enc_ex_filters, key, seed);
|
err = save_proxy.save_file(p_preset, p_udata, int_export_kv.key, array, idx, total, enc_in_filters, enc_ex_filters, key, seed, false);
|
||||||
if (err != OK) {
|
if (err != OK) {
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
@@ -1624,7 +1663,7 @@ 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);
|
||||||
|
|
||||||
err = save_proxy.save_file(p_udata, "res://" + config_file, data, idx, total, enc_in_filters, enc_ex_filters, key, seed);
|
err = save_proxy.save_file(p_preset, p_udata, "res://" + config_file, data, idx, total, enc_in_filters, enc_ex_filters, key, seed, false);
|
||||||
if (err != OK) {
|
if (err != OK) {
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
@@ -1633,7 +1672,7 @@ Error EditorExportPlatform::export_project_files(const Ref<EditorExportPreset> &
|
|||||||
HashSet<String> currently_loaded_paths = PackedData::get_singleton()->get_file_paths();
|
HashSet<String> currently_loaded_paths = PackedData::get_singleton()->get_file_paths();
|
||||||
for (const String &path : currently_loaded_paths) {
|
for (const String &path : currently_loaded_paths) {
|
||||||
if (!save_proxy.has_saved(path)) {
|
if (!save_proxy.has_saved(path)) {
|
||||||
err = p_remove_func(p_udata, path);
|
err = p_remove_func(p_preset, p_udata, path);
|
||||||
if (err != OK) {
|
if (err != OK) {
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
@@ -1660,7 +1699,7 @@ Vector<uint8_t> EditorExportPlatform::_filter_extension_list_config_file(const S
|
|||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
Error EditorExportPlatform::_pack_add_shared_object(void *p_userdata, const SharedObject &p_so) {
|
Error EditorExportPlatform::_pack_add_shared_object(const Ref<EditorExportPreset> &p_preset, void *p_userdata, const SharedObject &p_so) {
|
||||||
PackData *pack_data = (PackData *)p_userdata;
|
PackData *pack_data = (PackData *)p_userdata;
|
||||||
if (pack_data->so_files) {
|
if (pack_data->so_files) {
|
||||||
pack_data->so_files->push_back(p_so);
|
pack_data->so_files->push_back(p_so);
|
||||||
@@ -1669,7 +1708,7 @@ 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) {
|
Error EditorExportPlatform::_remove_pack_file(const Ref<EditorExportPreset> &p_preset, void *p_userdata, const String &p_path) {
|
||||||
PackData *pd = (PackData *)p_userdata;
|
PackData *pd = (PackData *)p_userdata;
|
||||||
|
|
||||||
SavedData sd;
|
SavedData sd;
|
||||||
@@ -1691,7 +1730,7 @@ Error EditorExportPlatform::_remove_pack_file(void *p_userdata, const String &p_
|
|||||||
return OK;
|
return OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
Error EditorExportPlatform::_zip_add_shared_object(void *p_userdata, const SharedObject &p_so) {
|
Error EditorExportPlatform::_zip_add_shared_object(const Ref<EditorExportPreset> &p_preset, 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) {
|
||||||
zip_data->so_files->push_back(p_so);
|
zip_data->so_files->push_back(p_so);
|
||||||
@@ -1996,6 +2035,9 @@ bool EditorExportPlatform::_encrypt_and_store_directory(Ref<FileAccess> p_fd, Pa
|
|||||||
if (p_pack_data.file_ofs[i].removal) {
|
if (p_pack_data.file_ofs[i].removal) {
|
||||||
flags |= PACK_FILE_REMOVAL;
|
flags |= PACK_FILE_REMOVAL;
|
||||||
}
|
}
|
||||||
|
if (p_pack_data.file_ofs[i].delta) {
|
||||||
|
flags |= PACK_FILE_DELTA;
|
||||||
|
}
|
||||||
fhead->store_32(flags);
|
fhead->store_32(flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -53,9 +53,9 @@ protected:
|
|||||||
static void _bind_methods();
|
static void _bind_methods();
|
||||||
|
|
||||||
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, uint64_t p_seed);
|
typedef Error (*EditorExportSaveFunction)(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);
|
||||||
typedef Error (*EditorExportRemoveFunction)(void *p_userdata, const String &p_path);
|
typedef Error (*EditorExportRemoveFunction)(const Ref<EditorExportPreset> &p_preset, void *p_userdata, const String &p_path);
|
||||||
typedef Error (*EditorExportSaveSharedObject)(void *p_userdata, const SharedObject &p_so);
|
typedef Error (*EditorExportSaveSharedObject)(const Ref<EditorExportPreset> &p_preset, void *p_userdata, const SharedObject &p_so);
|
||||||
|
|
||||||
enum DebugFlags {
|
enum DebugFlags {
|
||||||
DEBUG_FLAG_DUMB_CLIENT = 1,
|
DEBUG_FLAG_DUMB_CLIENT = 1,
|
||||||
@@ -83,6 +83,7 @@ public:
|
|||||||
uint64_t size = 0;
|
uint64_t size = 0;
|
||||||
bool encrypted = false;
|
bool encrypted = false;
|
||||||
bool removal = false;
|
bool removal = false;
|
||||||
|
bool delta = false;
|
||||||
Vector<uint8_t> md5;
|
Vector<uint8_t> md5;
|
||||||
CharString path_utf8;
|
CharString path_utf8;
|
||||||
|
|
||||||
@@ -119,25 +120,23 @@ private:
|
|||||||
void _export_find_customized_resources(const Ref<EditorExportPreset> &p_preset, EditorFileSystemDirectory *p_dir, EditorExportPreset::FileExportMode p_mode, 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);
|
||||||
void _export_find_dependencies(const String &p_path, HashSet<String> &p_paths);
|
void _export_find_dependencies(const String &p_path, HashSet<String> &p_paths);
|
||||||
|
|
||||||
static bool _check_hash(const uint8_t *p_hash, const Vector<uint8_t> &p_data);
|
static Error _save_pack_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);
|
||||||
|
static Error _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);
|
||||||
|
static Error _pack_add_shared_object(const Ref<EditorExportPreset> &p_preset, void *p_userdata, const SharedObject &p_so);
|
||||||
|
|
||||||
static Error _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);
|
static Error _remove_pack_file(const Ref<EditorExportPreset> &p_preset, void *p_userdata, const String &p_path);
|
||||||
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, uint64_t p_seed);
|
|
||||||
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(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);
|
||||||
|
static Error _save_zip_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);
|
||||||
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, uint64_t p_seed);
|
static Error _zip_add_shared_object(const Ref<EditorExportPreset> &p_preset, void *p_userdata, const SharedObject &p_so);
|
||||||
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, uint64_t p_seed);
|
|
||||||
static Error _zip_add_shared_object(void *p_userdata, const SharedObject &p_so);
|
|
||||||
|
|
||||||
struct ScriptCallbackData {
|
struct ScriptCallbackData {
|
||||||
Callable file_cb;
|
Callable file_cb;
|
||||||
Callable so_cb;
|
Callable so_cb;
|
||||||
};
|
};
|
||||||
|
|
||||||
static Error _script_save_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);
|
static Error _script_save_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);
|
||||||
static Error _script_add_shared_object(void *p_userdata, const SharedObject &p_so);
|
static Error _script_add_shared_object(const Ref<EditorExportPreset> &p_preset, void *p_userdata, const SharedObject &p_so);
|
||||||
|
|
||||||
void _edit_files_with_filter(Ref<DirAccess> &da, const Vector<String> &p_filters, HashSet<String> &r_list, bool exclude);
|
void _edit_files_with_filter(Ref<DirAccess> &da, const Vector<String> &p_filters, HashSet<String> &r_list, bool exclude);
|
||||||
void _edit_filter_list(HashSet<String> &r_list, const String &p_filter, bool exclude);
|
void _edit_filter_list(HashSet<String> &r_list, const String &p_filter, bool exclude);
|
||||||
|
|||||||
@@ -28,10 +28,11 @@
|
|||||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||||
/**************************************************************************/
|
/**************************************************************************/
|
||||||
|
|
||||||
#include "editor_export.h"
|
#include "editor_export_preset.h"
|
||||||
|
|
||||||
#include "core/config/project_settings.h"
|
#include "core/config/project_settings.h"
|
||||||
#include "core/io/dir_access.h"
|
#include "core/io/dir_access.h"
|
||||||
|
#include "editor/export/editor_export.h"
|
||||||
#include "editor/settings/editor_settings.h"
|
#include "editor/settings/editor_settings.h"
|
||||||
|
|
||||||
bool EditorExportPreset::_set(const StringName &p_name, const Variant &p_value) {
|
bool EditorExportPreset::_set(const StringName &p_name, const Variant &p_value) {
|
||||||
@@ -440,6 +441,51 @@ Vector<String> EditorExportPreset::get_patches() const {
|
|||||||
return patches;
|
return patches;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void EditorExportPreset::set_patch_delta_encoding_enabled(bool p_enable) {
|
||||||
|
patch_delta_encoding_enabled = p_enable;
|
||||||
|
EditorExport::singleton->save_presets();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool EditorExportPreset::is_patch_delta_encoding_enabled() const {
|
||||||
|
return patch_delta_encoding_enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
void EditorExportPreset::set_patch_delta_zstd_level(int p_level) {
|
||||||
|
patch_delta_zstd_level = p_level;
|
||||||
|
EditorExport::singleton->save_presets();
|
||||||
|
}
|
||||||
|
|
||||||
|
int EditorExportPreset::get_patch_delta_zstd_level() const {
|
||||||
|
return patch_delta_zstd_level;
|
||||||
|
}
|
||||||
|
|
||||||
|
void EditorExportPreset::set_patch_delta_min_reduction(double p_ratio) {
|
||||||
|
patch_delta_min_reduction = p_ratio;
|
||||||
|
EditorExport::singleton->save_presets();
|
||||||
|
}
|
||||||
|
|
||||||
|
double EditorExportPreset::get_patch_delta_min_reduction() const {
|
||||||
|
return patch_delta_min_reduction;
|
||||||
|
}
|
||||||
|
|
||||||
|
void EditorExportPreset::set_patch_delta_include_filter(const String &p_filter) {
|
||||||
|
patch_delta_include_filter = p_filter;
|
||||||
|
EditorExport::singleton->save_presets();
|
||||||
|
}
|
||||||
|
|
||||||
|
String EditorExportPreset::get_patch_delta_include_filter() const {
|
||||||
|
return patch_delta_include_filter;
|
||||||
|
}
|
||||||
|
|
||||||
|
void EditorExportPreset::set_patch_delta_exclude_filter(const String &p_filter) {
|
||||||
|
patch_delta_exclude_filter = p_filter;
|
||||||
|
EditorExport::singleton->save_presets();
|
||||||
|
}
|
||||||
|
|
||||||
|
String EditorExportPreset::get_patch_delta_exclude_filter() const {
|
||||||
|
return patch_delta_exclude_filter;
|
||||||
|
}
|
||||||
|
|
||||||
void EditorExportPreset::set_custom_features(const String &p_custom_features) {
|
void EditorExportPreset::set_custom_features(const String &p_custom_features) {
|
||||||
custom_features = p_custom_features;
|
custom_features = p_custom_features;
|
||||||
EditorExport::singleton->save_presets();
|
EditorExport::singleton->save_presets();
|
||||||
|
|||||||
@@ -73,6 +73,11 @@ private:
|
|||||||
bool dedicated_server = false;
|
bool dedicated_server = false;
|
||||||
|
|
||||||
Vector<String> patches;
|
Vector<String> patches;
|
||||||
|
bool patch_delta_encoding_enabled = false;
|
||||||
|
int patch_delta_zstd_level = 19;
|
||||||
|
double patch_delta_min_reduction = 0.1;
|
||||||
|
String patch_delta_include_filter = "*";
|
||||||
|
String patch_delta_exclude_filter;
|
||||||
|
|
||||||
friend class EditorExport;
|
friend class EditorExport;
|
||||||
friend class EditorExportPlatform;
|
friend class EditorExportPlatform;
|
||||||
@@ -148,11 +153,28 @@ public:
|
|||||||
|
|
||||||
void add_patch(const String &p_path, int p_at_pos = -1);
|
void add_patch(const String &p_path, int p_at_pos = -1);
|
||||||
void set_patch(int p_index, const String &p_path);
|
void set_patch(int p_index, const String &p_path);
|
||||||
|
|
||||||
String get_patch(int p_index);
|
String get_patch(int p_index);
|
||||||
void remove_patch(int p_index);
|
void remove_patch(int p_index);
|
||||||
|
|
||||||
void set_patches(const Vector<String> &p_patches);
|
void set_patches(const Vector<String> &p_patches);
|
||||||
Vector<String> get_patches() const;
|
Vector<String> get_patches() const;
|
||||||
|
|
||||||
|
void set_patch_delta_encoding_enabled(bool p_enable);
|
||||||
|
bool is_patch_delta_encoding_enabled() const;
|
||||||
|
|
||||||
|
void set_patch_delta_zstd_level(int p_level);
|
||||||
|
int get_patch_delta_zstd_level() const;
|
||||||
|
|
||||||
|
void set_patch_delta_min_reduction(double p_ratio);
|
||||||
|
double get_patch_delta_min_reduction() const;
|
||||||
|
|
||||||
|
void set_patch_delta_include_filter(const String &p_filter);
|
||||||
|
String get_patch_delta_include_filter() const;
|
||||||
|
|
||||||
|
void set_patch_delta_exclude_filter(const String &p_filter);
|
||||||
|
String get_patch_delta_exclude_filter() const;
|
||||||
|
|
||||||
void set_custom_features(const String &p_custom_features);
|
void set_custom_features(const String &p_custom_features);
|
||||||
String get_custom_features() const;
|
String get_custom_features() const;
|
||||||
|
|
||||||
|
|||||||
@@ -51,11 +51,14 @@
|
|||||||
#include "scene/gui/option_button.h"
|
#include "scene/gui/option_button.h"
|
||||||
#include "scene/gui/popup_menu.h"
|
#include "scene/gui/popup_menu.h"
|
||||||
#include "scene/gui/rich_text_label.h"
|
#include "scene/gui/rich_text_label.h"
|
||||||
|
#include "scene/gui/spin_box.h"
|
||||||
#include "scene/gui/split_container.h"
|
#include "scene/gui/split_container.h"
|
||||||
#include "scene/gui/tab_container.h"
|
#include "scene/gui/tab_container.h"
|
||||||
#include "scene/gui/texture_rect.h"
|
#include "scene/gui/texture_rect.h"
|
||||||
#include "scene/gui/tree.h"
|
#include "scene/gui/tree.h"
|
||||||
|
|
||||||
|
#include <zstd.h>
|
||||||
|
|
||||||
void ProjectExportTextureFormatError::_on_fix_texture_format_pressed() {
|
void ProjectExportTextureFormatError::_on_fix_texture_format_pressed() {
|
||||||
export_dialog->hide();
|
export_dialog->hide();
|
||||||
ProjectSettingsEditor *project_settings = EditorNode::get_singleton()->get_project_settings();
|
ProjectSettingsEditor *project_settings = EditorNode::get_singleton()->get_project_settings();
|
||||||
@@ -301,6 +304,19 @@ void ProjectExportDialog::_edit_preset(int p_index) {
|
|||||||
exclude_filters->set_text(current->get_exclude_filter());
|
exclude_filters->set_text(current->get_exclude_filter());
|
||||||
server_strip_message->set_visible(current->get_export_filter() == EditorExportPreset::EXPORT_CUSTOMIZED);
|
server_strip_message->set_visible(current->get_export_filter() == EditorExportPreset::EXPORT_CUSTOMIZED);
|
||||||
|
|
||||||
|
bool patch_delta_encoding_enabled = current->is_patch_delta_encoding_enabled();
|
||||||
|
patch_delta_encoding->set_pressed(patch_delta_encoding_enabled);
|
||||||
|
patch_delta_zstd_level->set_editable(patch_delta_encoding_enabled);
|
||||||
|
patch_delta_zstd_level->set_value(current->get_patch_delta_zstd_level());
|
||||||
|
patch_delta_min_reduction->set_editable(patch_delta_encoding_enabled);
|
||||||
|
patch_delta_min_reduction->set_value(current->get_patch_delta_min_reduction() * 100);
|
||||||
|
patch_delta_include_filter->set_editable(patch_delta_encoding_enabled);
|
||||||
|
patch_delta_exclude_filter->set_editable(patch_delta_encoding_enabled);
|
||||||
|
if (!updating_patch_delta_filters) {
|
||||||
|
patch_delta_include_filter->set_text(current->get_patch_delta_include_filter());
|
||||||
|
patch_delta_exclude_filter->set_text(current->get_patch_delta_exclude_filter());
|
||||||
|
}
|
||||||
|
|
||||||
patches->clear();
|
patches->clear();
|
||||||
TreeItem *patch_root = patches->create_item();
|
TreeItem *patch_root = patches->create_item();
|
||||||
Vector<String> patch_list = current->get_patches();
|
Vector<String> patch_list = current->get_patches();
|
||||||
@@ -745,6 +761,11 @@ void ProjectExportDialog::_duplicate_preset() {
|
|||||||
preset->set_include_filter(current->get_include_filter());
|
preset->set_include_filter(current->get_include_filter());
|
||||||
preset->set_exclude_filter(current->get_exclude_filter());
|
preset->set_exclude_filter(current->get_exclude_filter());
|
||||||
preset->set_patches(current->get_patches());
|
preset->set_patches(current->get_patches());
|
||||||
|
preset->set_patch_delta_encoding_enabled(current->is_patch_delta_encoding_enabled());
|
||||||
|
preset->set_patch_delta_zstd_level(current->get_patch_delta_zstd_level());
|
||||||
|
preset->set_patch_delta_min_reduction(current->get_patch_delta_min_reduction());
|
||||||
|
preset->set_patch_delta_include_filter(current->get_patch_delta_include_filter());
|
||||||
|
preset->set_patch_delta_exclude_filter(current->get_patch_delta_exclude_filter());
|
||||||
preset->set_custom_features(current->get_custom_features());
|
preset->set_custom_features(current->get_custom_features());
|
||||||
preset->set_enc_in_filter(current->get_enc_in_filter());
|
preset->set_enc_in_filter(current->get_enc_in_filter());
|
||||||
preset->set_enc_ex_filter(current->get_enc_ex_filter());
|
preset->set_enc_ex_filter(current->get_enc_ex_filter());
|
||||||
@@ -1201,6 +1222,75 @@ void ProjectExportDialog::_set_file_export_mode(int p_id) {
|
|||||||
_propagate_file_export_mode(include_files->get_root(), EditorExportPreset::MODE_FILE_NOT_CUSTOMIZED);
|
_propagate_file_export_mode(include_files->get_root(), EditorExportPreset::MODE_FILE_NOT_CUSTOMIZED);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ProjectExportDialog::_patch_delta_encoding_changed(bool p_pressed) {
|
||||||
|
if (updating) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ref<EditorExportPreset> current = get_current_preset();
|
||||||
|
ERR_FAIL_COND(current.is_null());
|
||||||
|
|
||||||
|
current->set_patch_delta_encoding_enabled(p_pressed);
|
||||||
|
|
||||||
|
_update_current_preset();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ProjectExportDialog::_patch_delta_include_filter_changed(const String &p_filter) {
|
||||||
|
if (updating) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ref<EditorExportPreset> current = get_current_preset();
|
||||||
|
ERR_FAIL_COND(current.is_null());
|
||||||
|
|
||||||
|
current->set_patch_delta_include_filter(patch_delta_include_filter->get_text());
|
||||||
|
|
||||||
|
updating_patch_delta_filters = true;
|
||||||
|
_update_current_preset();
|
||||||
|
updating_patch_delta_filters = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ProjectExportDialog::_patch_delta_exclude_filter_changed(const String &p_filter) {
|
||||||
|
if (updating) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ref<EditorExportPreset> current = get_current_preset();
|
||||||
|
ERR_FAIL_COND(current.is_null());
|
||||||
|
|
||||||
|
current->set_patch_delta_exclude_filter(patch_delta_exclude_filter->get_text());
|
||||||
|
|
||||||
|
updating_patch_delta_filters = true;
|
||||||
|
_update_current_preset();
|
||||||
|
updating_patch_delta_filters = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ProjectExportDialog::_patch_delta_zstd_level_changed(double p_value) {
|
||||||
|
if (updating) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ref<EditorExportPreset> current = get_current_preset();
|
||||||
|
ERR_FAIL_COND(current.is_null());
|
||||||
|
|
||||||
|
current->set_patch_delta_zstd_level((int)p_value);
|
||||||
|
|
||||||
|
_update_current_preset();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ProjectExportDialog::_patch_delta_min_reduction_changed(double p_value) {
|
||||||
|
if (updating) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ref<EditorExportPreset> current = get_current_preset();
|
||||||
|
ERR_FAIL_COND(current.is_null());
|
||||||
|
|
||||||
|
current->set_patch_delta_min_reduction(p_value / 100.0);
|
||||||
|
|
||||||
|
_update_current_preset();
|
||||||
|
}
|
||||||
|
|
||||||
void ProjectExportDialog::_patch_tree_button_clicked(Object *p_item, int p_column, int p_id, int p_mouse_button_index) {
|
void ProjectExportDialog::_patch_tree_button_clicked(Object *p_item, int p_column, int p_id, int p_mouse_button_index) {
|
||||||
TreeItem *ti = Object::cast_to<TreeItem>(p_item);
|
TreeItem *ti = Object::cast_to<TreeItem>(p_item);
|
||||||
|
|
||||||
@@ -1657,11 +1747,52 @@ ProjectExportDialog::ProjectExportDialog() {
|
|||||||
exclude_filters);
|
exclude_filters);
|
||||||
exclude_filters->connect(SceneStringName(text_changed), callable_mp(this, &ProjectExportDialog::_filter_changed));
|
exclude_filters->connect(SceneStringName(text_changed), callable_mp(this, &ProjectExportDialog::_filter_changed));
|
||||||
|
|
||||||
// Patch packages.
|
// Patching.
|
||||||
|
|
||||||
VBoxContainer *patch_vb = memnew(VBoxContainer);
|
VBoxContainer *patch_vb = memnew(VBoxContainer);
|
||||||
sections->add_child(patch_vb);
|
sections->add_child(patch_vb);
|
||||||
patch_vb->set_name(TTR("Patches"));
|
patch_vb->set_name(TTRC("Patching"));
|
||||||
|
|
||||||
|
patch_delta_encoding = memnew(CheckButton);
|
||||||
|
patch_delta_encoding->connect(SceneStringName(toggled), callable_mp(this, &ProjectExportDialog::_patch_delta_encoding_changed));
|
||||||
|
patch_delta_encoding->set_text(TTRC("Enable Delta Encoding"));
|
||||||
|
patch_delta_encoding->set_tooltip_text(TTRC("If checked, any change to a file already present in the base packs will be exported as the difference between the old file and the new file.\n"
|
||||||
|
"Enabling this comes at the cost of longer export times as well as longer load times for patched resources."));
|
||||||
|
patch_vb->add_child(patch_delta_encoding);
|
||||||
|
|
||||||
|
patch_delta_zstd_level = memnew(SpinBox);
|
||||||
|
patch_delta_zstd_level->set_min(ZSTD_minCLevel());
|
||||||
|
patch_delta_zstd_level->set_max(ZSTD_maxCLevel());
|
||||||
|
patch_delta_zstd_level->set_step(1);
|
||||||
|
patch_delta_zstd_level->set_tooltip_text(
|
||||||
|
vformat(TTR("The Zstandard compression level to use when generating delta-encoded patches.\n"
|
||||||
|
"Higher positive levels will reduce patch sizes, at the cost of longer export time, but do not affect the time it takes to apply patches.\n"
|
||||||
|
"Negative levels will reduce the time it takes to apply patches, at the cost of worse compression.\n"
|
||||||
|
"Levels above 19 require more memory both during export and when applying patches, usually for very little benefit.\n"
|
||||||
|
"Level 0 will cause Zstandard to use its default compression level, which is currently level %d."),
|
||||||
|
ZSTD_CLEVEL_DEFAULT));
|
||||||
|
patch_delta_zstd_level->connect(SceneStringName(value_changed), callable_mp(this, &ProjectExportDialog::_patch_delta_zstd_level_changed));
|
||||||
|
patch_vb->add_margin_child(TTRC("Delta Encoding Compression Level"), patch_delta_zstd_level);
|
||||||
|
|
||||||
|
patch_delta_min_reduction = memnew(SpinBox);
|
||||||
|
patch_delta_min_reduction->set_min(0.0);
|
||||||
|
patch_delta_min_reduction->set_max(100.0);
|
||||||
|
patch_delta_min_reduction->set_step(1.0);
|
||||||
|
patch_delta_min_reduction->set_suffix("%");
|
||||||
|
patch_delta_min_reduction->set_tooltip_text(TTRC("How much smaller, when compared to the new file, a delta-encoded patch needs to be for it to be exported.\n"
|
||||||
|
"If the patch is not at least this much smaller, the new file will be exported as-is."));
|
||||||
|
patch_delta_min_reduction->connect(SceneStringName(value_changed), callable_mp(this, &ProjectExportDialog::_patch_delta_min_reduction_changed));
|
||||||
|
patch_vb->add_margin_child(TTRC("Delta Encoding Minimum Size Reduction"), patch_delta_min_reduction);
|
||||||
|
|
||||||
|
patch_delta_include_filter = memnew(LineEdit);
|
||||||
|
patch_delta_include_filter->set_accessibility_name(TTRC("Delta Encoding Include Filters"));
|
||||||
|
patch_delta_include_filter->connect(SceneStringName(text_changed), callable_mp(this, &ProjectExportDialog::_patch_delta_include_filter_changed));
|
||||||
|
patch_vb->add_margin_child(TTRC("Filters to include files/folders from being delta-encoded\n(comma-separated, e.g: *.gdc, scripts/*)"), patch_delta_include_filter);
|
||||||
|
|
||||||
|
patch_delta_exclude_filter = memnew(LineEdit);
|
||||||
|
patch_delta_exclude_filter->set_accessibility_name(TTRC("Delta Encoding Exclude Filters"));
|
||||||
|
patch_delta_exclude_filter->connect(SceneStringName(text_changed), callable_mp(this, &ProjectExportDialog::_patch_delta_exclude_filter_changed));
|
||||||
|
patch_vb->add_margin_child(TTRC("Filters to exclude files/folders from being delta-encoded\n(comma-separated, e.g: *.ctex, textures/*)"), patch_delta_exclude_filter);
|
||||||
|
|
||||||
patches = memnew(Tree);
|
patches = memnew(Tree);
|
||||||
patches->set_v_size_flags(Control::SIZE_EXPAND_FILL);
|
patches->set_v_size_flags(Control::SIZE_EXPAND_FILL);
|
||||||
|
|||||||
@@ -46,6 +46,7 @@ class OptionButton;
|
|||||||
class PopupMenu;
|
class PopupMenu;
|
||||||
class ProjectExportDialog;
|
class ProjectExportDialog;
|
||||||
class RichTextLabel;
|
class RichTextLabel;
|
||||||
|
class SpinBox;
|
||||||
class TabContainer;
|
class TabContainer;
|
||||||
class Tree;
|
class Tree;
|
||||||
class TreeItem;
|
class TreeItem;
|
||||||
@@ -107,6 +108,11 @@ class ProjectExportDialog : public ConfirmationDialog {
|
|||||||
|
|
||||||
RBSet<String> feature_set;
|
RBSet<String> feature_set;
|
||||||
|
|
||||||
|
CheckButton *patch_delta_encoding = nullptr;
|
||||||
|
SpinBox *patch_delta_zstd_level = nullptr;
|
||||||
|
SpinBox *patch_delta_min_reduction = nullptr;
|
||||||
|
LineEdit *patch_delta_include_filter = nullptr;
|
||||||
|
LineEdit *patch_delta_exclude_filter = nullptr;
|
||||||
Tree *patches = nullptr;
|
Tree *patches = nullptr;
|
||||||
int patch_index = -1;
|
int patch_index = -1;
|
||||||
EditorFileDialog *patch_dialog = nullptr;
|
EditorFileDialog *patch_dialog = nullptr;
|
||||||
@@ -158,6 +164,12 @@ class ProjectExportDialog : public ConfirmationDialog {
|
|||||||
void _tree_popup_edited(bool p_arrow_clicked);
|
void _tree_popup_edited(bool p_arrow_clicked);
|
||||||
void _set_file_export_mode(int p_id);
|
void _set_file_export_mode(int p_id);
|
||||||
|
|
||||||
|
bool updating_patch_delta_filters = false;
|
||||||
|
void _patch_delta_encoding_changed(bool p_pressed);
|
||||||
|
void _patch_delta_include_filter_changed(const String &p_filter);
|
||||||
|
void _patch_delta_exclude_filter_changed(const String &p_filter);
|
||||||
|
void _patch_delta_zstd_level_changed(double p_value);
|
||||||
|
void _patch_delta_min_reduction_changed(double p_value);
|
||||||
void _patch_tree_button_clicked(Object *p_item, int p_column, int p_id, int p_mouse_button_index);
|
void _patch_tree_button_clicked(Object *p_item, int p_column, int p_id, int p_mouse_button_index);
|
||||||
void _patch_tree_item_edited();
|
void _patch_tree_item_edited();
|
||||||
void _patch_file_selected(const String &p_path);
|
void _patch_file_selected(const String &p_path);
|
||||||
|
|||||||
@@ -767,7 +767,7 @@ Error EditorExportPlatformAndroid::store_in_apk(APKExportData *ed, const String
|
|||||||
return OK;
|
return OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
Error EditorExportPlatformAndroid::save_apk_so(void *p_userdata, const SharedObject &p_so) {
|
Error EditorExportPlatformAndroid::save_apk_so(const Ref<EditorExportPreset> &p_preset, void *p_userdata, const SharedObject &p_so) {
|
||||||
if (!p_so.path.get_file().begins_with("lib")) {
|
if (!p_so.path.get_file().begins_with("lib")) {
|
||||||
String err = "Android .so file names must start with \"lib\", but got: " + p_so.path;
|
String err = "Android .so file names must start with \"lib\", but got: " + p_so.path;
|
||||||
ERR_PRINT(err);
|
ERR_PRINT(err);
|
||||||
@@ -801,14 +801,14 @@ Error EditorExportPlatformAndroid::save_apk_so(void *p_userdata, const SharedObj
|
|||||||
return OK;
|
return OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
Error EditorExportPlatformAndroid::save_apk_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) {
|
Error EditorExportPlatformAndroid::save_apk_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) {
|
||||||
APKExportData *ed = static_cast<APKExportData *>(p_userdata);
|
APKExportData *ed = static_cast<APKExportData *>(p_userdata);
|
||||||
|
|
||||||
const String simplified_path = simplify_path(p_path);
|
const String simplified_path = simplify_path(p_path);
|
||||||
|
|
||||||
Vector<uint8_t> enc_data;
|
Vector<uint8_t> enc_data;
|
||||||
EditorExportPlatform::SavedData sd;
|
EditorExportPlatform::SavedData sd;
|
||||||
Error err = _store_temp_file(simplified_path, p_data, p_enc_in_filters, p_enc_ex_filters, p_key, p_seed, enc_data, sd);
|
Error err = _store_temp_file(simplified_path, p_data, p_enc_in_filters, p_enc_ex_filters, p_key, p_seed, p_delta, enc_data, sd);
|
||||||
if (err != OK) {
|
if (err != OK) {
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
@@ -822,11 +822,11 @@ Error EditorExportPlatformAndroid::save_apk_file(void *p_userdata, const String
|
|||||||
return OK;
|
return OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
Error EditorExportPlatformAndroid::ignore_apk_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) {
|
Error EditorExportPlatformAndroid::ignore_apk_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) {
|
||||||
return OK;
|
return OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
Error EditorExportPlatformAndroid::copy_gradle_so(void *p_userdata, const SharedObject &p_so) {
|
Error EditorExportPlatformAndroid::copy_gradle_so(const Ref<EditorExportPreset> &p_preset, void *p_userdata, const SharedObject &p_so) {
|
||||||
ERR_FAIL_COND_V_MSG(!p_so.path.get_file().begins_with("lib"), FAILED,
|
ERR_FAIL_COND_V_MSG(!p_so.path.get_file().begins_with("lib"), FAILED,
|
||||||
"Android .so file names must start with \"lib\", but got: " + p_so.path);
|
"Android .so file names must start with \"lib\", but got: " + p_so.path);
|
||||||
Vector<ABI> abis = get_abis();
|
Vector<ABI> abis = get_abis();
|
||||||
|
|||||||
@@ -151,13 +151,13 @@ class EditorExportPlatformAndroid : public EditorExportPlatform {
|
|||||||
|
|
||||||
static Error store_in_apk(APKExportData *ed, const String &p_path, const Vector<uint8_t> &p_data, int compression_method = Z_DEFLATED);
|
static Error store_in_apk(APKExportData *ed, const String &p_path, const Vector<uint8_t> &p_data, int compression_method = Z_DEFLATED);
|
||||||
|
|
||||||
static Error save_apk_so(void *p_userdata, const SharedObject &p_so);
|
static Error save_apk_so(const Ref<EditorExportPreset> &p_preset, void *p_userdata, const SharedObject &p_so);
|
||||||
|
|
||||||
static Error save_apk_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);
|
static Error save_apk_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);
|
||||||
|
|
||||||
static Error ignore_apk_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);
|
static Error ignore_apk_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);
|
||||||
|
|
||||||
static Error copy_gradle_so(void *p_userdata, const SharedObject &p_so);
|
static Error copy_gradle_so(const Ref<EditorExportPreset> &p_preset, void *p_userdata, const SharedObject &p_so);
|
||||||
|
|
||||||
bool _has_read_write_storage_permission(const Vector<String> &p_permissions);
|
bool _has_read_write_storage_permission(const Vector<String> &p_permissions);
|
||||||
|
|
||||||
|
|||||||
@@ -170,14 +170,14 @@ Error store_string_at_path(const String &p_path, const String &p_data) {
|
|||||||
// It is used by the export_project_files method to save all the asset files into the gradle project.
|
// It is used by the export_project_files method to save all the asset files into the gradle project.
|
||||||
// It's functionality mirrors that of the method save_apk_file.
|
// It's functionality mirrors that of the method save_apk_file.
|
||||||
// This method will be called ONLY when gradle build is enabled.
|
// This method will be called ONLY when gradle build is enabled.
|
||||||
Error rename_and_store_file_in_gradle_project(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) {
|
Error rename_and_store_file_in_gradle_project(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) {
|
||||||
CustomExportData *export_data = static_cast<CustomExportData *>(p_userdata);
|
CustomExportData *export_data = static_cast<CustomExportData *>(p_userdata);
|
||||||
|
|
||||||
const String simplified_path = EditorExportPlatform::simplify_path(p_path);
|
const String simplified_path = EditorExportPlatform::simplify_path(p_path);
|
||||||
|
|
||||||
Vector<uint8_t> enc_data;
|
Vector<uint8_t> enc_data;
|
||||||
EditorExportPlatform::SavedData sd;
|
EditorExportPlatform::SavedData sd;
|
||||||
Error err = _store_temp_file(simplified_path, p_data, p_enc_in_filters, p_enc_ex_filters, p_key, p_seed, enc_data, sd);
|
Error err = _store_temp_file(simplified_path, p_data, p_enc_in_filters, p_enc_ex_filters, p_key, p_seed, p_delta, enc_data, sd);
|
||||||
if (err != OK) {
|
if (err != OK) {
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
@@ -398,7 +398,7 @@ String _get_application_tag(const Ref<EditorExportPlatform> &p_export_platform,
|
|||||||
return manifest_application_text;
|
return manifest_application_text;
|
||||||
}
|
}
|
||||||
|
|
||||||
Error _store_temp_file(const String &p_simplified_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, Vector<uint8_t> &r_enc_data, EditorExportPlatform::SavedData &r_sd) {
|
Error _store_temp_file(const String &p_simplified_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 p_delta, Vector<uint8_t> &r_enc_data, EditorExportPlatform::SavedData &r_sd) {
|
||||||
Error err = OK;
|
Error err = OK;
|
||||||
Ref<FileAccess> ftmp = FileAccess::create_temp(FileAccess::WRITE_READ, "export", "tmp", false, &err);
|
Ref<FileAccess> ftmp = FileAccess::create_temp(FileAccess::WRITE_READ, "export", "tmp", false, &err);
|
||||||
if (err != OK) {
|
if (err != OK) {
|
||||||
@@ -407,6 +407,7 @@ Error _store_temp_file(const String &p_simplified_path, const Vector<uint8_t> &p
|
|||||||
r_sd.path_utf8 = p_simplified_path.trim_prefix("res://").utf8();
|
r_sd.path_utf8 = p_simplified_path.trim_prefix("res://").utf8();
|
||||||
r_sd.ofs = 0;
|
r_sd.ofs = 0;
|
||||||
r_sd.size = p_data.size();
|
r_sd.size = p_data.size();
|
||||||
|
r_sd.delta = p_delta;
|
||||||
err = EditorExportPlatform::_encrypt_and_store_data(ftmp, p_simplified_path, p_data, p_enc_in_filters, p_enc_ex_filters, p_key, p_seed, r_sd.encrypted);
|
err = EditorExportPlatform::_encrypt_and_store_data(ftmp, p_simplified_path, p_data, p_enc_in_filters, p_enc_ex_filters, p_key, p_seed, r_sd.encrypted);
|
||||||
if (err != OK) {
|
if (err != OK) {
|
||||||
return err;
|
return err;
|
||||||
|
|||||||
@@ -85,7 +85,7 @@ int _get_app_category_value(int category_index);
|
|||||||
|
|
||||||
String _get_app_category_label(int category_index);
|
String _get_app_category_label(int category_index);
|
||||||
|
|
||||||
Error _store_temp_file(const String &p_simplified_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, Vector<uint8_t> &r_enc_data, EditorExportPlatform::SavedData &r_sd);
|
Error _store_temp_file(const String &p_simplified_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 p_delta, Vector<uint8_t> &r_enc_data, EditorExportPlatform::SavedData &r_sd);
|
||||||
|
|
||||||
// Utility method used to create a directory.
|
// Utility method used to create a directory.
|
||||||
Error create_directory(const String &p_dir);
|
Error create_directory(const String &p_dir);
|
||||||
@@ -103,7 +103,7 @@ Error store_string_at_path(const String &p_path, const String &p_data);
|
|||||||
// It is used by the export_project_files method to save all the asset files into the gradle project.
|
// It is used by the export_project_files method to save all the asset files into the gradle project.
|
||||||
// It's functionality mirrors that of the method save_apk_file.
|
// It's functionality mirrors that of the method save_apk_file.
|
||||||
// This method will be called ONLY when gradle build is enabled.
|
// This method will be called ONLY when gradle build is enabled.
|
||||||
Error rename_and_store_file_in_gradle_project(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);
|
Error rename_and_store_file_in_gradle_project(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);
|
||||||
|
|
||||||
// Creates strings.xml files inside the gradle project for different locales.
|
// Creates strings.xml files inside the gradle project for different locales.
|
||||||
Error _create_project_name_strings_files(const Ref<EditorExportPreset> &p_preset, const String &p_project_name, const String &p_gradle_build_dir, const Dictionary &p_appnames);
|
Error _create_project_name_strings_files(const Ref<EditorExportPreset> &p_preset, const String &p_project_name, const String &p_gradle_build_dir, const Dictionary &p_appnames);
|
||||||
|
|||||||
Reference in New Issue
Block a user