summaryrefslogtreecommitdiffstats
path: root/core
diff options
context:
space:
mode:
authorGeorge L. Albany <Megacake1234@gmail.com>2024-11-12 20:29:24 +0000
committerGitHub <noreply@github.com>2024-11-12 20:29:24 +0000
commitac1a49725fc038ae11ef9060fecb2b0f9c6333b2 (patch)
treec7341bd56c977259578b127886c9a88eeef11820 /core
parent5094c2a5f7d506b0e685120f14d1df42e1e9d495 (diff)
parent3a73c6ebd18bff0fa125be58d3ac9c7a63bab61d (diff)
downloadredot-engine-ac1a49725fc038ae11ef9060fecb2b0f9c6333b2.tar.gz
Merge pull request #855 from Spartan322/merge/cb411fa
Merge commit godotengine/godot@cb411fa
Diffstat (limited to 'core')
-rw-r--r--core/core_bind.compat.inc5
-rw-r--r--core/core_bind.cpp39
-rw-r--r--core/core_bind.h20
-rw-r--r--core/debugger/local_debugger.cpp6
-rw-r--r--core/io/file_access_pack.cpp68
-rw-r--r--core/io/file_access_pack.h13
-rw-r--r--core/io/http_client_tcp.cpp7
-rw-r--r--core/io/image.cpp220
-rw-r--r--core/io/image.h251
-rw-r--r--core/io/marshalls.cpp405
-rw-r--r--core/io/net_socket.h2
-rw-r--r--core/io/packet_peer_udp.cpp18
-rw-r--r--core/io/pck_packer.cpp41
-rw-r--r--core/io/pck_packer.h4
-rw-r--r--core/io/resource_format_binary.cpp4
-rw-r--r--core/io/resource_format_binary.h1
-rw-r--r--core/io/resource_importer.cpp4
-rw-r--r--core/io/resource_importer.h3
-rw-r--r--core/io/resource_loader.cpp84
-rw-r--r--core/io/resource_loader.h4
-rw-r--r--core/io/stream_peer.cpp4
-rw-r--r--core/io/tcp_server.cpp2
-rw-r--r--core/os/os.h17
-rw-r--r--core/string/translation_domain.cpp5
-rw-r--r--core/string/translation_server.cpp139
-rw-r--r--core/string/translation_server.h20
-rw-r--r--core/templates/hashfuncs.h10
-rw-r--r--core/variant/callable.cpp25
-rw-r--r--core/variant/callable.h6
-rw-r--r--core/variant/callable_bind.cpp71
-rw-r--r--core/variant/callable_bind.h6
-rw-r--r--core/variant/variant.cpp18
-rw-r--r--core/variant/variant_call.cpp1
-rw-r--r--core/variant/variant_setget.cpp4
34 files changed, 980 insertions, 547 deletions
diff --git a/core/core_bind.compat.inc b/core/core_bind.compat.inc
index 16ed8cd5d1..7495943a05 100644
--- a/core/core_bind.compat.inc
+++ b/core/core_bind.compat.inc
@@ -46,11 +46,16 @@ void Semaphore::_bind_compatibility_methods() {
// OS
+String OS::_read_string_from_stdin_bind_compat_91201() {
+ return read_string_from_stdin(1024);
+}
+
Dictionary OS::_execute_with_pipe_bind_compat_94434(const String &p_path, const Vector<String> &p_arguments) {
return execute_with_pipe(p_path, p_arguments, true);
}
void OS::_bind_compatibility_methods() {
+ ClassDB::bind_compatibility_method(D_METHOD("read_string_from_stdin"), &OS::_read_string_from_stdin_bind_compat_91201);
ClassDB::bind_compatibility_method(D_METHOD("execute_with_pipe", "path", "arguments"), &OS::_execute_with_pipe_bind_compat_94434);
}
diff --git a/core/core_bind.cpp b/core/core_bind.cpp
index 172ca71370..fadb4fed8b 100644
--- a/core/core_bind.cpp
+++ b/core/core_bind.cpp
@@ -134,6 +134,10 @@ ResourceUID::ID ResourceLoader::get_resource_uid(const String &p_path) {
return ::ResourceLoader::get_resource_uid(p_path);
}
+Vector<String> ResourceLoader::list_directory(const String &p_directory) {
+ return ::ResourceLoader::list_directory(p_directory);
+}
+
void ResourceLoader::_bind_methods() {
ClassDB::bind_method(D_METHOD("load_threaded_request", "path", "type_hint", "use_sub_threads", "cache_mode"), &ResourceLoader::load_threaded_request, DEFVAL(""), DEFVAL(false), DEFVAL(CACHE_MODE_REUSE));
ClassDB::bind_method(D_METHOD("load_threaded_get_status", "path", "progress"), &ResourceLoader::load_threaded_get_status, DEFVAL_ARRAY);
@@ -149,6 +153,7 @@ void ResourceLoader::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_cached_ref", "path"), &ResourceLoader::get_cached_ref);
ClassDB::bind_method(D_METHOD("exists", "path", "type_hint"), &ResourceLoader::exists, DEFVAL(""));
ClassDB::bind_method(D_METHOD("get_resource_uid", "path"), &ResourceLoader::get_resource_uid);
+ ClassDB::bind_method(D_METHOD("list_directory", "directory_path"), &ResourceLoader::list_directory);
BIND_ENUM_CONSTANT(THREAD_LOAD_INVALID_RESOURCE);
BIND_ENUM_CONSTANT(THREAD_LOAD_IN_PROGRESS);
@@ -305,8 +310,24 @@ Error OS::shell_show_in_file_manager(const String &p_path, bool p_open_folder) {
return ::OS::get_singleton()->shell_show_in_file_manager(p_path, p_open_folder);
}
-String OS::read_string_from_stdin() {
- return ::OS::get_singleton()->get_stdin_string();
+String OS::read_string_from_stdin(int64_t p_buffer_size) {
+ return ::OS::get_singleton()->get_stdin_string(p_buffer_size);
+}
+
+PackedByteArray OS::read_buffer_from_stdin(int64_t p_buffer_size) {
+ return ::OS::get_singleton()->get_stdin_buffer(p_buffer_size);
+}
+
+OS::StdHandleType OS::get_stdin_type() const {
+ return (OS::StdHandleType)::OS::get_singleton()->get_stdin_type();
+}
+
+OS::StdHandleType OS::get_stdout_type() const {
+ return (OS::StdHandleType)::OS::get_singleton()->get_stdout_type();
+}
+
+OS::StdHandleType OS::get_stderr_type() const {
+ return (OS::StdHandleType)::OS::get_singleton()->get_stderr_type();
}
int OS::execute(const String &p_path, const Vector<String> &p_arguments, Array r_output, bool p_read_stderr, bool p_open_console) {
@@ -630,7 +651,13 @@ void OS::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_system_font_path", "font_name", "weight", "stretch", "italic"), &OS::get_system_font_path, DEFVAL(400), DEFVAL(100), DEFVAL(false));
ClassDB::bind_method(D_METHOD("get_system_font_path_for_text", "font_name", "text", "locale", "script", "weight", "stretch", "italic"), &OS::get_system_font_path_for_text, DEFVAL(String()), DEFVAL(String()), DEFVAL(400), DEFVAL(100), DEFVAL(false));
ClassDB::bind_method(D_METHOD("get_executable_path"), &OS::get_executable_path);
- ClassDB::bind_method(D_METHOD("read_string_from_stdin"), &OS::read_string_from_stdin);
+
+ ClassDB::bind_method(D_METHOD("read_string_from_stdin", "buffer_size"), &OS::read_string_from_stdin);
+ ClassDB::bind_method(D_METHOD("read_buffer_from_stdin", "buffer_size"), &OS::read_buffer_from_stdin);
+ ClassDB::bind_method(D_METHOD("get_stdin_type"), &OS::get_stdin_type);
+ ClassDB::bind_method(D_METHOD("get_stdout_type"), &OS::get_stdout_type);
+ ClassDB::bind_method(D_METHOD("get_stderr_type"), &OS::get_stderr_type);
+
ClassDB::bind_method(D_METHOD("execute", "path", "arguments", "output", "read_stderr", "open_console"), &OS::execute, DEFVAL_ARRAY, DEFVAL(false), DEFVAL(false));
ClassDB::bind_method(D_METHOD("execute_with_pipe", "path", "arguments", "blocking"), &OS::execute_with_pipe, DEFVAL(true));
ClassDB::bind_method(D_METHOD("create_process", "path", "arguments", "open_console"), &OS::create_process, DEFVAL(false));
@@ -722,6 +749,12 @@ void OS::_bind_methods() {
BIND_ENUM_CONSTANT(SYSTEM_DIR_MUSIC);
BIND_ENUM_CONSTANT(SYSTEM_DIR_PICTURES);
BIND_ENUM_CONSTANT(SYSTEM_DIR_RINGTONES);
+
+ BIND_ENUM_CONSTANT(STD_HANDLE_INVALID);
+ BIND_ENUM_CONSTANT(STD_HANDLE_CONSOLE);
+ BIND_ENUM_CONSTANT(STD_HANDLE_FILE);
+ BIND_ENUM_CONSTANT(STD_HANDLE_PIPE);
+ BIND_ENUM_CONSTANT(STD_HANDLE_UNKNOWN);
}
////// Geometry2D //////
diff --git a/core/core_bind.h b/core/core_bind.h
index 86828365f9..2ac76e193a 100644
--- a/core/core_bind.h
+++ b/core/core_bind.h
@@ -88,6 +88,8 @@ public:
bool exists(const String &p_path, const String &p_type_hint = "");
ResourceUID::ID get_resource_uid(const String &p_path);
+ Vector<String> list_directory(const String &p_directory);
+
ResourceLoader() { singleton = this; }
};
@@ -134,6 +136,7 @@ protected:
#ifndef DISABLE_DEPRECATED
Dictionary _execute_with_pipe_bind_compat_94434(const String &p_path, const Vector<String> &p_arguments);
+ String _read_string_from_stdin_bind_compat_91201();
static void _bind_compatibility_methods();
#endif
@@ -148,6 +151,14 @@ public:
PackedByteArray get_entropy(int p_bytes);
String get_system_ca_certificates();
+ enum StdHandleType {
+ STD_HANDLE_INVALID,
+ STD_HANDLE_CONSOLE,
+ STD_HANDLE_FILE,
+ STD_HANDLE_PIPE,
+ STD_HANDLE_UNKNOWN,
+ };
+
virtual PackedStringArray get_connected_midi_inputs();
virtual void open_midi_inputs();
virtual void close_midi_inputs();
@@ -168,7 +179,13 @@ public:
String get_system_font_path(const String &p_font_name, int p_weight = 400, int p_stretch = 100, bool p_italic = false) const;
Vector<String> get_system_font_path_for_text(const String &p_font_name, const String &p_text, const String &p_locale = String(), const String &p_script = String(), int p_weight = 400, int p_stretch = 100, bool p_italic = false) const;
String get_executable_path() const;
- String read_string_from_stdin();
+
+ String read_string_from_stdin(int64_t p_buffer_size = 1024);
+ PackedByteArray read_buffer_from_stdin(int64_t p_buffer_size = 1024);
+ StdHandleType get_stdin_type() const;
+ StdHandleType get_stdout_type() const;
+ StdHandleType get_stderr_type() const;
+
int execute(const String &p_path, const Vector<String> &p_arguments, Array r_output = ClassDB::default_array_arg, bool p_read_stderr = false, bool p_open_console = false);
Dictionary execute_with_pipe(const String &p_path, const Vector<String> &p_arguments, bool p_blocking = true);
int create_process(const String &p_path, const Vector<String> &p_arguments, bool p_open_console = false);
@@ -647,6 +664,7 @@ VARIANT_BITFIELD_CAST(core_bind::ResourceSaver::SaverFlags);
VARIANT_ENUM_CAST(core_bind::OS::RenderingDriver);
VARIANT_ENUM_CAST(core_bind::OS::SystemDir);
+VARIANT_ENUM_CAST(core_bind::OS::StdHandleType);
VARIANT_ENUM_CAST(core_bind::Geometry2D::PolyBooleanOperation);
VARIANT_ENUM_CAST(core_bind::Geometry2D::PolyJoinType);
diff --git a/core/debugger/local_debugger.cpp b/core/debugger/local_debugger.cpp
index 95798c3654..965dfa96c3 100644
--- a/core/debugger/local_debugger.cpp
+++ b/core/debugger/local_debugger.cpp
@@ -210,10 +210,10 @@ void LocalDebugger::debug(bool p_can_continue, bool p_is_error_breakpoint) {
print_variables(members, values, variable_prefix);
} else if (line.begins_with("p") || line.begins_with("print")) {
- if (line.get_slice_count(" ") <= 1) {
- print_line("Usage: print <expre>");
+ if (line.find_char(' ') < 0) {
+ print_line("Usage: print <expression>");
} else {
- String expr = line.get_slicec(' ', 2);
+ String expr = line.split(" ", true, 1)[1];
String res = script_lang->debug_parse_stack_level_expression(current_frame, expr);
print_line(res);
}
diff --git a/core/io/file_access_pack.cpp b/core/io/file_access_pack.cpp
index 7bc96c4644..a3039ca619 100644
--- a/core/io/file_access_pack.cpp
+++ b/core/io/file_access_pack.cpp
@@ -50,7 +50,7 @@ Error PackedData::add_pack(const String &p_path, bool p_replace_files, uint64_t
}
void PackedData::add_path(const String &p_pkg_path, const String &p_path, uint64_t p_ofs, uint64_t p_size, const uint8_t *p_md5, PackSource *p_src, bool p_replace_files, bool p_encrypted) {
- String simplified_path = p_path.simplify_path();
+ String simplified_path = p_path.simplify_path().trim_prefix("res://");
PathMD5 pmd5(simplified_path.md5_buffer());
bool exists = files.has(pmd5);
@@ -70,13 +70,11 @@ void PackedData::add_path(const String &p_pkg_path, const String &p_path, uint64
}
if (!exists) {
- //search for dir
- String p = simplified_path.replace_first("res://", "");
+ // Search for directory.
PackedDir *cd = root;
- if (p.contains("/")) { //in a subdir
-
- Vector<String> ds = p.get_base_dir().split("/");
+ if (simplified_path.contains("/")) { // In a subdirectory.
+ Vector<String> ds = simplified_path.get_base_dir().split("/");
for (int j = 0; j < ds.size(); j++) {
if (!cd->subdirs.has(ds[j])) {
@@ -91,13 +89,40 @@ void PackedData::add_path(const String &p_pkg_path, const String &p_path, uint64
}
}
String filename = simplified_path.get_file();
- // Don't add as a file if the path points to a directory
+ // Don't add as a file if the path points to a directory.
if (!filename.is_empty()) {
cd->files.insert(filename);
}
}
}
+void PackedData::remove_path(const String &p_path) {
+ String simplified_path = p_path.simplify_path().trim_prefix("res://");
+ PathMD5 pmd5(simplified_path.md5_buffer());
+ if (!files.has(pmd5)) {
+ return;
+ }
+
+ // Search for directory.
+ PackedDir *cd = root;
+
+ if (simplified_path.contains("/")) { // In a subdirectory.
+ Vector<String> ds = simplified_path.get_base_dir().split("/");
+
+ for (int j = 0; j < ds.size(); j++) {
+ if (!cd->subdirs.has(ds[j])) {
+ return; // Subdirectory does not exist, do not bother creating.
+ } else {
+ cd = cd->subdirs[ds[j]];
+ }
+ }
+ }
+
+ cd->files.erase(simplified_path.get_file());
+
+ files.erase(pmd5);
+}
+
void PackedData::add_pack_source(PackSource *p_source) {
if (p_source != nullptr) {
sources.push_back(p_source);
@@ -105,15 +130,32 @@ void PackedData::add_pack_source(PackSource *p_source) {
}
uint8_t *PackedData::get_file_hash(const String &p_path) {
- PathMD5 pmd5(p_path.md5_buffer());
+ String simplified_path = p_path.simplify_path().trim_prefix("res://");
+ PathMD5 pmd5(simplified_path.md5_buffer());
HashMap<PathMD5, PackedFile, PathMD5>::Iterator E = files.find(pmd5);
- if (!E || E->value.offset == 0) {
+ if (!E) {
return nullptr;
}
return E->value.md5;
}
+HashSet<String> PackedData::get_file_paths() const {
+ HashSet<String> file_paths;
+ _get_file_paths(root, root->name, file_paths);
+ return file_paths;
+}
+
+void PackedData::_get_file_paths(PackedDir *p_dir, const String &p_parent_dir, HashSet<String> &r_paths) const {
+ for (const String &E : p_dir->files) {
+ r_paths.insert(p_parent_dir.path_join(E));
+ }
+
+ for (const KeyValue<String, PackedDir *> &E : p_dir->subdirs) {
+ _get_file_paths(E.value, p_parent_dir.path_join(E.key), r_paths);
+ }
+}
+
void PackedData::clear() {
files.clear();
_free_packed_dirs(root);
@@ -271,13 +313,17 @@ bool PackedSourcePCK::try_open_pack(const String &p_path, bool p_replace_files,
String path;
path.parse_utf8(cs.ptr());
- uint64_t ofs = file_base + f->get_64();
+ uint64_t ofs = f->get_64();
uint64_t size = f->get_64();
uint8_t md5[16];
f->get_buffer(md5, 16);
uint32_t flags = f->get_32();
- PackedData::get_singleton()->add_path(p_path, path, ofs + p_offset, size, md5, this, p_replace_files, (flags & PACK_FILE_ENCRYPTED));
+ if (flags & PACK_FILE_REMOVAL) { // The file was removed.
+ PackedData::get_singleton()->remove_path(path);
+ } else {
+ PackedData::get_singleton()->add_path(p_path, path, file_base + ofs + p_offset, size, md5, this, p_replace_files, (flags & PACK_FILE_ENCRYPTED));
+ }
}
return true;
diff --git a/core/io/file_access_pack.h b/core/io/file_access_pack.h
index 34792d4018..124c08fe07 100644
--- a/core/io/file_access_pack.h
+++ b/core/io/file_access_pack.h
@@ -51,7 +51,8 @@ enum PackFlags {
};
enum PackFileFlags {
- PACK_FILE_ENCRYPTED = 1 << 0
+ PACK_FILE_ENCRYPTED = 1 << 0,
+ PACK_FILE_REMOVAL = 1 << 1,
};
class PackSource;
@@ -109,11 +110,14 @@ private:
bool disabled = false;
void _free_packed_dirs(PackedDir *p_dir);
+ void _get_file_paths(PackedDir *p_dir, const String &p_parent_dir, HashSet<String> &r_paths) const;
public:
void add_pack_source(PackSource *p_source);
void add_path(const String &p_pkg_path, const String &p_path, uint64_t p_ofs, uint64_t p_size, const uint8_t *p_md5, PackSource *p_src, bool p_replace_files, bool p_encrypted = false); // for PackSource
+ void remove_path(const String &p_path);
uint8_t *get_file_hash(const String &p_path);
+ HashSet<String> get_file_paths() const;
void set_disabled(bool p_disabled) { disabled = p_disabled; }
_FORCE_INLINE_ bool is_disabled() const { return disabled; }
@@ -192,14 +196,11 @@ public:
};
Ref<FileAccess> PackedData::try_open_path(const String &p_path) {
- String simplified_path = p_path.simplify_path();
+ String simplified_path = p_path.simplify_path().trim_prefix("res://");
PathMD5 pmd5(simplified_path.md5_buffer());
HashMap<PathMD5, PackedFile, PathMD5>::Iterator E = files.find(pmd5);
if (!E) {
- return nullptr; //not found
- }
- if (E->value.offset == 0) {
- return nullptr; //was erased
+ return nullptr; // Not found.
}
return E->value.src->get_file(p_path, &E->value);
diff --git a/core/io/http_client_tcp.cpp b/core/io/http_client_tcp.cpp
index e53f6ca2bb..75f4a8c398 100644
--- a/core/io/http_client_tcp.cpp
+++ b/core/io/http_client_tcp.cpp
@@ -664,15 +664,16 @@ PackedByteArray HTTPClientTCP::read_response_body_chunk() {
chunk_left -= rec;
if (chunk_left == 0) {
- if (chunk[chunk.size() - 2] != '\r' || chunk[chunk.size() - 1] != '\n') {
+ const int chunk_size = chunk.size();
+ if (chunk[chunk_size - 2] != '\r' || chunk[chunk_size - 1] != '\n') {
ERR_PRINT("HTTP Invalid chunk terminator (not \\r\\n)");
status = STATUS_CONNECTION_ERROR;
break;
}
- ret.resize(chunk.size() - 2);
+ ret.resize(chunk_size - 2);
uint8_t *w = ret.ptrw();
- memcpy(w, chunk.ptr(), chunk.size() - 2);
+ memcpy(w, chunk.ptr(), chunk_size - 2);
chunk.clear();
}
diff --git a/core/io/image.cpp b/core/io/image.cpp
index 613e740dd6..3e5ae837f5 100644
--- a/core/io/image.cpp
+++ b/core/io/image.cpp
@@ -46,24 +46,24 @@
#include <cmath>
const char *Image::format_names[Image::FORMAT_MAX] = {
- "Lum8", //luminance
- "LumAlpha8", //luminance-alpha
+ "Lum8",
+ "LumAlpha8",
"Red8",
"RedGreen",
"RGB8",
"RGBA8",
"RGBA4444",
- "RGBA5551",
- "RFloat", //float
+ "RGBA5551", // Actually RGB565, kept as RGBA5551 for compatibility.
+ "RFloat",
"RGFloat",
"RGBFloat",
"RGBAFloat",
- "RHalf", //half float
+ "RHalf",
"RGHalf",
"RGBHalf",
"RGBAHalf",
"RGBE9995",
- "DXT1 RGB8", //s3tc
+ "DXT1 RGB8",
"DXT3 RGBA8",
"DXT5 RGBA8",
"RGTC Red8",
@@ -71,9 +71,9 @@ const char *Image::format_names[Image::FORMAT_MAX] = {
"BPTC_RGBA",
"BPTC_RGBF",
"BPTC_RGBFU",
- "ETC", //etc1
- "ETC2_R11", //etc2
- "ETC2_R11S", //signed", NOT srgb.
+ "ETC",
+ "ETC2_R11",
+ "ETC2_R11S",
"ETC2_RG11",
"ETC2_RG11S",
"ETC2_RGB8",
@@ -87,17 +87,60 @@ const char *Image::format_names[Image::FORMAT_MAX] = {
"ASTC_8x8_HDR",
};
+// External saver function pointers.
+
SavePNGFunc Image::save_png_func = nullptr;
SaveJPGFunc Image::save_jpg_func = nullptr;
SaveEXRFunc Image::save_exr_func = nullptr;
+SaveWebPFunc Image::save_webp_func = nullptr;
SavePNGBufferFunc Image::save_png_buffer_func = nullptr;
-SaveEXRBufferFunc Image::save_exr_buffer_func = nullptr;
SaveJPGBufferFunc Image::save_jpg_buffer_func = nullptr;
-
-SaveWebPFunc Image::save_webp_func = nullptr;
+SaveEXRBufferFunc Image::save_exr_buffer_func = nullptr;
SaveWebPBufferFunc Image::save_webp_buffer_func = nullptr;
+// External loader function pointers.
+
+ImageMemLoadFunc Image::_png_mem_loader_func = nullptr;
+ImageMemLoadFunc Image::_png_mem_unpacker_func = nullptr;
+ImageMemLoadFunc Image::_jpg_mem_loader_func = nullptr;
+ImageMemLoadFunc Image::_webp_mem_loader_func = nullptr;
+ImageMemLoadFunc Image::_tga_mem_loader_func = nullptr;
+ImageMemLoadFunc Image::_bmp_mem_loader_func = nullptr;
+ScalableImageMemLoadFunc Image::_svg_scalable_mem_loader_func = nullptr;
+ImageMemLoadFunc Image::_ktx_mem_loader_func = nullptr;
+
+// External VRAM compression function pointers.
+
+void (*Image::_image_compress_bc_func)(Image *, Image::UsedChannels) = nullptr;
+void (*Image::_image_compress_bptc_func)(Image *, Image::UsedChannels) = nullptr;
+void (*Image::_image_compress_etc1_func)(Image *) = nullptr;
+void (*Image::_image_compress_etc2_func)(Image *, Image::UsedChannels) = nullptr;
+void (*Image::_image_compress_astc_func)(Image *, Image::ASTCFormat) = nullptr;
+
+Error (*Image::_image_compress_bptc_rd_func)(Image *, Image::UsedChannels) = nullptr;
+Error (*Image::_image_compress_bc_rd_func)(Image *, Image::UsedChannels) = nullptr;
+
+// External VRAM decompression function pointers.
+
+void (*Image::_image_decompress_bc)(Image *) = nullptr;
+void (*Image::_image_decompress_bptc)(Image *) = nullptr;
+void (*Image::_image_decompress_etc1)(Image *) = nullptr;
+void (*Image::_image_decompress_etc2)(Image *) = nullptr;
+void (*Image::_image_decompress_astc)(Image *) = nullptr;
+
+// External packer function pointers.
+
+Vector<uint8_t> (*Image::webp_lossy_packer)(const Ref<Image> &, float) = nullptr;
+Vector<uint8_t> (*Image::webp_lossless_packer)(const Ref<Image> &) = nullptr;
+Vector<uint8_t> (*Image::png_packer)(const Ref<Image> &) = nullptr;
+Vector<uint8_t> (*Image::basis_universal_packer)(const Ref<Image> &, Image::UsedChannels) = nullptr;
+
+Ref<Image> (*Image::webp_unpacker)(const Vector<uint8_t> &) = nullptr;
+Ref<Image> (*Image::png_unpacker)(const Vector<uint8_t> &) = nullptr;
+Ref<Image> (*Image::basis_universal_unpacker)(const Vector<uint8_t> &) = nullptr;
+Ref<Image> (*Image::basis_universal_unpacker_ptr)(const uint8_t *, int) = nullptr;
+
void Image::_put_pixelb(int p_x, int p_y, uint32_t p_pixel_size, uint8_t *p_data, const uint8_t *p_pixel) {
uint32_t ofs = (p_y * width + p_x) * p_pixel_size;
memcpy(p_data + ofs, p_pixel, p_pixel_size);
@@ -111,9 +154,9 @@ void Image::_get_pixelb(int p_x, int p_y, uint32_t p_pixel_size, const uint8_t *
int Image::get_format_pixel_size(Format p_format) {
switch (p_format) {
case FORMAT_L8:
- return 1; //luminance
+ return 1;
case FORMAT_LA8:
- return 2; //luminance-alpha
+ return 2;
case FORMAT_R8:
return 1;
case FORMAT_RG8:
@@ -127,7 +170,7 @@ int Image::get_format_pixel_size(Format p_format) {
case FORMAT_RGB565:
return 2;
case FORMAT_RF:
- return 4; //float
+ return 4;
case FORMAT_RGF:
return 8;
case FORMAT_RGBF:
@@ -135,7 +178,7 @@ int Image::get_format_pixel_size(Format p_format) {
case FORMAT_RGBAF:
return 16;
case FORMAT_RH:
- return 2; //half float
+ return 2;
case FORMAT_RGH:
return 4;
case FORMAT_RGBH:
@@ -145,27 +188,27 @@ int Image::get_format_pixel_size(Format p_format) {
case FORMAT_RGBE9995:
return 4;
case FORMAT_DXT1:
- return 1; //s3tc bc1
+ return 1;
case FORMAT_DXT3:
- return 1; //bc2
+ return 1;
case FORMAT_DXT5:
- return 1; //bc3
+ return 1;
case FORMAT_RGTC_R:
- return 1; //bc4
+ return 1;
case FORMAT_RGTC_RG:
- return 1; //bc5
+ return 1;
case FORMAT_BPTC_RGBA:
- return 1; //btpc bc6h
+ return 1;
case FORMAT_BPTC_RGBF:
- return 1; //float /
+ return 1;
case FORMAT_BPTC_RGBFU:
- return 1; //unsigned float
+ return 1;
case FORMAT_ETC:
- return 1; //etc1
+ return 1;
case FORMAT_ETC2_R11:
- return 1; //etc2
+ return 1;
case FORMAT_ETC2_R11S:
- return 1; //signed: return 1; NOT srgb.
+ return 1;
case FORMAT_ETC2_RG11:
return 1;
case FORMAT_ETC2_RG11S:
@@ -196,12 +239,11 @@ int Image::get_format_pixel_size(Format p_format) {
void Image::get_format_min_pixel_size(Format p_format, int &r_w, int &r_h) {
switch (p_format) {
- case FORMAT_DXT1: //s3tc bc1
- case FORMAT_DXT3: //bc2
- case FORMAT_DXT5: //bc3
- case FORMAT_RGTC_R: //bc4
- case FORMAT_RGTC_RG: { //bc5 case case FORMAT_DXT1:
-
+ case FORMAT_DXT1:
+ case FORMAT_DXT3:
+ case FORMAT_DXT5:
+ case FORMAT_RGTC_R:
+ case FORMAT_RGTC_RG: {
r_w = 4;
r_h = 4;
} break;
@@ -215,8 +257,8 @@ void Image::get_format_min_pixel_size(Format p_format, int &r_w, int &r_h) {
r_w = 4;
r_h = 4;
} break;
- case FORMAT_ETC2_R11: //etc2
- case FORMAT_ETC2_R11S: //signed: NOT srgb.
+ case FORMAT_ETC2_R11:
+ case FORMAT_ETC2_R11S:
case FORMAT_ETC2_RG11:
case FORMAT_ETC2_RG11S:
case FORMAT_ETC2_RGB8:
@@ -226,19 +268,16 @@ void Image::get_format_min_pixel_size(Format p_format, int &r_w, int &r_h) {
case FORMAT_DXT5_RA_AS_RG: {
r_w = 4;
r_h = 4;
-
} break;
case FORMAT_ASTC_4x4:
case FORMAT_ASTC_4x4_HDR: {
r_w = 4;
r_h = 4;
-
} break;
case FORMAT_ASTC_8x8:
case FORMAT_ASTC_8x8_HDR: {
r_w = 8;
r_h = 8;
-
} break;
default: {
r_w = 1;
@@ -259,12 +298,11 @@ int Image::get_format_pixel_rshift(Format p_format) {
int Image::get_format_block_size(Format p_format) {
switch (p_format) {
- case FORMAT_DXT1: //s3tc bc1
- case FORMAT_DXT3: //bc2
- case FORMAT_DXT5: //bc3
- case FORMAT_RGTC_R: //bc4
- case FORMAT_RGTC_RG: { //bc5 case case FORMAT_DXT1:
-
+ case FORMAT_DXT1:
+ case FORMAT_DXT3:
+ case FORMAT_DXT5:
+ case FORMAT_RGTC_R:
+ case FORMAT_RGTC_RG: {
return 4;
}
case FORMAT_ETC: {
@@ -275,17 +313,15 @@ int Image::get_format_block_size(Format p_format) {
case FORMAT_BPTC_RGBFU: {
return 4;
}
- case FORMAT_ETC2_R11: //etc2
- case FORMAT_ETC2_R11S: //signed: NOT srgb.
+ case FORMAT_ETC2_R11:
+ case FORMAT_ETC2_R11S:
case FORMAT_ETC2_RG11:
case FORMAT_ETC2_RG11S:
case FORMAT_ETC2_RGB8:
case FORMAT_ETC2_RGBA8:
case FORMAT_ETC2_RGB8A1:
- case FORMAT_ETC2_RA_AS_RG: //used to make basis universal happy
- case FORMAT_DXT5_RA_AS_RG: //used to make basis universal happy
-
- {
+ case FORMAT_ETC2_RA_AS_RG:
+ case FORMAT_DXT5_RA_AS_RG: {
return 4;
}
case FORMAT_ASTC_4x4:
@@ -461,7 +497,7 @@ int Image::get_mipmap_count() const {
}
}
-//using template generates perfectly optimized code due to constant expression reduction and unused variable removal present in all compilers
+// Using template generates perfectly optimized code due to constant expression reduction and unused variable removal present in all compilers.
template <uint32_t read_bytes, bool read_alpha, uint32_t write_bytes, bool write_alpha, bool read_gray, bool write_gray>
static void _convert(int p_width, int p_height, const uint8_t *p_src, uint8_t *p_dst) {
constexpr uint32_t max_bytes = MAX(read_bytes, write_bytes);
@@ -553,7 +589,7 @@ void Image::convert(Format p_new_format) {
ERR_FAIL_MSG("Cannot convert to <-> from compressed formats. Use compress() and decompress() instead.");
} else if (!_are_formats_compatible(format, p_new_format)) {
- //use put/set pixel which is slower but works with non byte formats
+ // Use put/set pixel which is slower but works with non-byte formats.
Image new_img(width, height, mipmaps, p_new_format);
for (int mip = 0; mip < mipmap_count; mip++) {
@@ -1696,7 +1732,7 @@ void Image::flip_x() {
}
}
-/// Get mipmap size and offset.
+// Get mipmap size and offset.
int64_t Image::_get_dst_image_size(int p_width, int p_height, Format p_format, int &r_mipmaps, int p_mipmaps, int *r_mm_width, int *r_mm_height) {
// Data offset in mipmaps (including the original texture).
int64_t size = 0;
@@ -3136,37 +3172,6 @@ void Image::fill_rect(const Rect2i &p_rect, const Color &p_color) {
}
}
-ImageMemLoadFunc Image::_png_mem_loader_func = nullptr;
-ImageMemLoadFunc Image::_png_mem_unpacker_func = nullptr;
-ImageMemLoadFunc Image::_jpg_mem_loader_func = nullptr;
-ImageMemLoadFunc Image::_webp_mem_loader_func = nullptr;
-ImageMemLoadFunc Image::_tga_mem_loader_func = nullptr;
-ImageMemLoadFunc Image::_bmp_mem_loader_func = nullptr;
-ScalableImageMemLoadFunc Image::_svg_scalable_mem_loader_func = nullptr;
-ImageMemLoadFunc Image::_ktx_mem_loader_func = nullptr;
-
-void (*Image::_image_compress_bc_func)(Image *, Image::UsedChannels) = nullptr;
-void (*Image::_image_compress_bptc_func)(Image *, Image::UsedChannels) = nullptr;
-void (*Image::_image_compress_etc1_func)(Image *) = nullptr;
-void (*Image::_image_compress_etc2_func)(Image *, Image::UsedChannels) = nullptr;
-void (*Image::_image_compress_astc_func)(Image *, Image::ASTCFormat) = nullptr;
-Error (*Image::_image_compress_bptc_rd_func)(Image *, Image::UsedChannels) = nullptr;
-Error (*Image::_image_compress_bc_rd_func)(Image *, Image::UsedChannels) = nullptr;
-void (*Image::_image_decompress_bc)(Image *) = nullptr;
-void (*Image::_image_decompress_bptc)(Image *) = nullptr;
-void (*Image::_image_decompress_etc1)(Image *) = nullptr;
-void (*Image::_image_decompress_etc2)(Image *) = nullptr;
-void (*Image::_image_decompress_astc)(Image *) = nullptr;
-
-Vector<uint8_t> (*Image::webp_lossy_packer)(const Ref<Image> &, float) = nullptr;
-Vector<uint8_t> (*Image::webp_lossless_packer)(const Ref<Image> &) = nullptr;
-Ref<Image> (*Image::webp_unpacker)(const Vector<uint8_t> &) = nullptr;
-Vector<uint8_t> (*Image::png_packer)(const Ref<Image> &) = nullptr;
-Ref<Image> (*Image::png_unpacker)(const Vector<uint8_t> &) = nullptr;
-Vector<uint8_t> (*Image::basis_universal_packer)(const Ref<Image> &, Image::UsedChannels) = nullptr;
-Ref<Image> (*Image::basis_universal_unpacker)(const Vector<uint8_t> &) = nullptr;
-Ref<Image> (*Image::basis_universal_unpacker_ptr)(const uint8_t *, int) = nullptr;
-
void Image::_set_data(const Dictionary &p_data) {
ERR_FAIL_COND(!p_data.has("width"));
ERR_FAIL_COND(!p_data.has("height"));
@@ -3206,6 +3211,14 @@ Color Image::get_pixelv(const Point2i &p_point) const {
return get_pixel(p_point.x, p_point.y);
}
+void Image::_copy_internals_from(const Image &p_image) {
+ format = p_image.format;
+ width = p_image.width;
+ height = p_image.height;
+ mipmaps = p_image.mipmaps;
+ data = p_image.data;
+}
+
Color Image::_get_color_at_ofs(const uint8_t *ptr, uint32_t ofs) const {
switch (format) {
case FORMAT_L8: {
@@ -3645,34 +3658,34 @@ void Image::_bind_methods() {
BIND_CONSTANT(MAX_WIDTH);
BIND_CONSTANT(MAX_HEIGHT);
- BIND_ENUM_CONSTANT(FORMAT_L8); //luminance
- BIND_ENUM_CONSTANT(FORMAT_LA8); //luminance-alpha
+ BIND_ENUM_CONSTANT(FORMAT_L8);
+ BIND_ENUM_CONSTANT(FORMAT_LA8);
BIND_ENUM_CONSTANT(FORMAT_R8);
BIND_ENUM_CONSTANT(FORMAT_RG8);
BIND_ENUM_CONSTANT(FORMAT_RGB8);
BIND_ENUM_CONSTANT(FORMAT_RGBA8);
BIND_ENUM_CONSTANT(FORMAT_RGBA4444);
BIND_ENUM_CONSTANT(FORMAT_RGB565);
- BIND_ENUM_CONSTANT(FORMAT_RF); //float
+ BIND_ENUM_CONSTANT(FORMAT_RF);
BIND_ENUM_CONSTANT(FORMAT_RGF);
BIND_ENUM_CONSTANT(FORMAT_RGBF);
BIND_ENUM_CONSTANT(FORMAT_RGBAF);
- BIND_ENUM_CONSTANT(FORMAT_RH); //half float
+ BIND_ENUM_CONSTANT(FORMAT_RH);
BIND_ENUM_CONSTANT(FORMAT_RGH);
BIND_ENUM_CONSTANT(FORMAT_RGBH);
BIND_ENUM_CONSTANT(FORMAT_RGBAH);
BIND_ENUM_CONSTANT(FORMAT_RGBE9995);
- BIND_ENUM_CONSTANT(FORMAT_DXT1); //s3tc bc1
- BIND_ENUM_CONSTANT(FORMAT_DXT3); //bc2
- BIND_ENUM_CONSTANT(FORMAT_DXT5); //bc3
+ BIND_ENUM_CONSTANT(FORMAT_DXT1);
+ BIND_ENUM_CONSTANT(FORMAT_DXT3);
+ BIND_ENUM_CONSTANT(FORMAT_DXT5);
BIND_ENUM_CONSTANT(FORMAT_RGTC_R);
BIND_ENUM_CONSTANT(FORMAT_RGTC_RG);
- BIND_ENUM_CONSTANT(FORMAT_BPTC_RGBA); //btpc bc6h
- BIND_ENUM_CONSTANT(FORMAT_BPTC_RGBF); //float /
- BIND_ENUM_CONSTANT(FORMAT_BPTC_RGBFU); //unsigned float
- BIND_ENUM_CONSTANT(FORMAT_ETC); //etc1
- BIND_ENUM_CONSTANT(FORMAT_ETC2_R11); //etc2
- BIND_ENUM_CONSTANT(FORMAT_ETC2_R11S); //signed ); NOT srgb.
+ BIND_ENUM_CONSTANT(FORMAT_BPTC_RGBA);
+ BIND_ENUM_CONSTANT(FORMAT_BPTC_RGBF);
+ BIND_ENUM_CONSTANT(FORMAT_BPTC_RGBFU);
+ BIND_ENUM_CONSTANT(FORMAT_ETC);
+ BIND_ENUM_CONSTANT(FORMAT_ETC2_R11);
+ BIND_ENUM_CONSTANT(FORMAT_ETC2_R11S);
BIND_ENUM_CONSTANT(FORMAT_ETC2_RG11);
BIND_ENUM_CONSTANT(FORMAT_ETC2_RG11S);
BIND_ENUM_CONSTANT(FORMAT_ETC2_RGB8);
@@ -4179,7 +4192,7 @@ void Image::renormalize_half(uint16_t *p_rgb) {
}
void Image::renormalize_rgbe9995(uint32_t *p_rgb) {
- // Never used
+ // Never used.
}
Image::Image(const uint8_t *p_mem_png_jpg, int p_len) {
@@ -4212,6 +4225,15 @@ void Image::set_as_black() {
memset(data.ptrw(), 0, data.size());
}
+void Image::copy_internals_from(const Ref<Image> &p_image) {
+ ERR_FAIL_COND_MSG(p_image.is_null(), "Cannot copy image internals: invalid Image object.");
+ format = p_image->format;
+ width = p_image->width;
+ height = p_image->height;
+ mipmaps = p_image->mipmaps;
+ data = p_image->data;
+}
+
Dictionary Image::compute_image_metrics(const Ref<Image> p_compared_image, bool p_luma_metric) {
// https://github.com/richgel999/bc7enc_rdo/blob/master/LICENSE
//
@@ -4252,8 +4274,6 @@ Dictionary Image::compute_image_metrics(const Ref<Image> p_compared_image, bool
}
ERR_FAIL_COND_V(err != OK, result);
- ERR_FAIL_COND_V(err != OK, result);
-
ERR_FAIL_COND_V_MSG((compared_image->get_format() >= Image::FORMAT_RH) && (compared_image->get_format() <= Image::FORMAT_RGBE9995), result, "Metrics on HDR images are not supported.");
ERR_FAIL_COND_V_MSG((source_image->get_format() >= Image::FORMAT_RH) && (source_image->get_format() <= Image::FORMAT_RGBE9995), result, "Metrics on HDR images are not supported.");
diff --git a/core/io/image.h b/core/io/image.h
index 6e149c7142..d11e9b9c9c 100644
--- a/core/io/image.h
+++ b/core/io/image.h
@@ -45,12 +45,17 @@
class Image;
+// Function pointer prototypes.
+
typedef Error (*SavePNGFunc)(const String &p_path, const Ref<Image> &p_img);
typedef Vector<uint8_t> (*SavePNGBufferFunc)(const Ref<Image> &p_img);
+
typedef Error (*SaveJPGFunc)(const String &p_path, const Ref<Image> &p_img, float p_quality);
typedef Vector<uint8_t> (*SaveJPGBufferFunc)(const Ref<Image> &p_img, float p_quality);
+
typedef Ref<Image> (*ImageMemLoadFunc)(const uint8_t *p_png, int p_size);
typedef Ref<Image> (*ScalableImageMemLoadFunc)(const uint8_t *p_data, int p_size, float p_scale);
+
typedef Error (*SaveWebPFunc)(const String &p_path, const Ref<Image> &p_img, const bool p_lossy, const float p_quality);
typedef Vector<uint8_t> (*SaveWebPBufferFunc)(const Ref<Image> &p_img, const bool p_lossy, const float p_quality);
@@ -61,57 +66,48 @@ class Image : public Resource {
GDCLASS(Image, Resource);
public:
- static SavePNGFunc save_png_func;
- static SaveJPGFunc save_jpg_func;
- static SaveEXRFunc save_exr_func;
- static SavePNGBufferFunc save_png_buffer_func;
- static SaveEXRBufferFunc save_exr_buffer_func;
- static SaveJPGBufferFunc save_jpg_buffer_func;
- static SaveWebPFunc save_webp_func;
- static SaveWebPBufferFunc save_webp_buffer_func;
-
enum {
- MAX_WIDTH = (1 << 24), // force a limit somehow
- MAX_HEIGHT = (1 << 24), // force a limit somehow
- MAX_PIXELS = 268435456
+ MAX_WIDTH = (1 << 24), // Force a limit somehow.
+ MAX_HEIGHT = (1 << 24), // Force a limit somehow.
+ MAX_PIXELS = 268435456 // 16384 ^ 2
};
enum Format {
- FORMAT_L8, //luminance
- FORMAT_LA8, //luminance-alpha
+ FORMAT_L8, // Luminance
+ FORMAT_LA8, // Luminance-Alpha
FORMAT_R8,
FORMAT_RG8,
FORMAT_RGB8,
FORMAT_RGBA8,
FORMAT_RGBA4444,
FORMAT_RGB565,
- FORMAT_RF, //float
+ FORMAT_RF, // Float
FORMAT_RGF,
FORMAT_RGBF,
FORMAT_RGBAF,
- FORMAT_RH, //half float
+ FORMAT_RH, // Half
FORMAT_RGH,
FORMAT_RGBH,
FORMAT_RGBAH,
FORMAT_RGBE9995,
- FORMAT_DXT1, //s3tc bc1
- FORMAT_DXT3, //bc2
- FORMAT_DXT5, //bc3
- FORMAT_RGTC_R,
- FORMAT_RGTC_RG,
- FORMAT_BPTC_RGBA, //btpc bc7
- FORMAT_BPTC_RGBF, //float bc6h
- FORMAT_BPTC_RGBFU, //unsigned float bc6hu
- FORMAT_ETC, //etc1
- FORMAT_ETC2_R11, //etc2
- FORMAT_ETC2_R11S, //signed, NOT srgb.
+ FORMAT_DXT1, // BC1
+ FORMAT_DXT3, // BC2
+ FORMAT_DXT5, // BC3
+ FORMAT_RGTC_R, // BC4
+ FORMAT_RGTC_RG, // BC5
+ FORMAT_BPTC_RGBA, // BC7
+ FORMAT_BPTC_RGBF, // BC6 Signed
+ FORMAT_BPTC_RGBFU, // BC6 Unsigned
+ FORMAT_ETC, // ETC1
+ FORMAT_ETC2_R11,
+ FORMAT_ETC2_R11S, // Signed, NOT srgb.
FORMAT_ETC2_RG11,
- FORMAT_ETC2_RG11S,
+ FORMAT_ETC2_RG11S, // Signed, NOT srgb.
FORMAT_ETC2_RGB8,
FORMAT_ETC2_RGBA8,
FORMAT_ETC2_RGB8A1,
- FORMAT_ETC2_RA_AS_RG, //used to make basis universal happy
- FORMAT_DXT5_RA_AS_RG, //used to make basis universal happy
+ FORMAT_ETC2_RA_AS_RG, // ETC2 RGBA with a RA-RG swizzle for normal maps.
+ FORMAT_DXT5_RA_AS_RG, // BC3 with a RA-RG swizzle for normal maps.
FORMAT_ASTC_4x4,
FORMAT_ASTC_4x4_HDR,
FORMAT_ASTC_8x8,
@@ -120,17 +116,18 @@ public:
};
static const char *format_names[FORMAT_MAX];
+
enum Interpolation {
INTERPOLATE_NEAREST,
INTERPOLATE_BILINEAR,
INTERPOLATE_CUBIC,
INTERPOLATE_TRILINEAR,
INTERPOLATE_LANCZOS,
- /* INTERPOLATE_TRICUBIC, */
- /* INTERPOLATE GAUSS */
+ // INTERPOLATE_TRICUBIC,
+ // INTERPOLATE_GAUSS
};
- //this is used for compression
+ // Used for obtaining optimal compression quality.
enum UsedChannels {
USED_CHANNELS_L,
USED_CHANNELS_LA,
@@ -139,13 +136,66 @@ public:
USED_CHANNELS_RGB,
USED_CHANNELS_RGBA,
};
- //some functions provided by something else
+ // ASTC supports block formats other than 4x4.
enum ASTCFormat {
ASTC_FORMAT_4x4,
ASTC_FORMAT_8x8,
};
+ enum RoughnessChannel {
+ ROUGHNESS_CHANNEL_R,
+ ROUGHNESS_CHANNEL_G,
+ ROUGHNESS_CHANNEL_B,
+ ROUGHNESS_CHANNEL_A,
+ ROUGHNESS_CHANNEL_L,
+ };
+
+ enum Image3DValidateError {
+ VALIDATE_3D_OK,
+ VALIDATE_3D_ERR_IMAGE_EMPTY,
+ VALIDATE_3D_ERR_MISSING_IMAGES,
+ VALIDATE_3D_ERR_EXTRA_IMAGES,
+ VALIDATE_3D_ERR_IMAGE_SIZE_MISMATCH,
+ VALIDATE_3D_ERR_IMAGE_FORMAT_MISMATCH,
+ VALIDATE_3D_ERR_IMAGE_HAS_MIPMAPS,
+ };
+
+ enum CompressMode {
+ COMPRESS_S3TC,
+ COMPRESS_ETC,
+ COMPRESS_ETC2,
+ COMPRESS_BPTC,
+ COMPRESS_ASTC,
+ COMPRESS_MAX,
+ };
+
+ enum CompressSource {
+ COMPRESS_SOURCE_GENERIC,
+ COMPRESS_SOURCE_SRGB,
+ COMPRESS_SOURCE_NORMAL,
+ COMPRESS_SOURCE_MAX,
+ };
+
+ enum AlphaMode {
+ ALPHA_NONE,
+ ALPHA_BIT,
+ ALPHA_BLEND
+ };
+
+ // External saver function pointers.
+
+ static SavePNGFunc save_png_func;
+ static SaveJPGFunc save_jpg_func;
+ static SaveEXRFunc save_exr_func;
+ static SaveWebPFunc save_webp_func;
+ static SavePNGBufferFunc save_png_buffer_func;
+ static SaveEXRBufferFunc save_exr_buffer_func;
+ static SaveJPGBufferFunc save_jpg_buffer_func;
+ static SaveWebPBufferFunc save_webp_buffer_func;
+
+ // External loader function pointers.
+
static ImageMemLoadFunc _png_mem_loader_func;
static ImageMemLoadFunc _png_mem_unpacker_func;
static ImageMemLoadFunc _jpg_mem_loader_func;
@@ -155,6 +205,8 @@ public:
static ScalableImageMemLoadFunc _svg_scalable_mem_loader_func;
static ImageMemLoadFunc _ktx_mem_loader_func;
+ // External VRAM compression function pointers.
+
static void (*_image_compress_bc_func)(Image *, UsedChannels p_channels);
static void (*_image_compress_bptc_func)(Image *, UsedChannels p_channels);
static void (*_image_compress_etc1_func)(Image *);
@@ -164,24 +216,26 @@ public:
static Error (*_image_compress_bptc_rd_func)(Image *, UsedChannels p_channels);
static Error (*_image_compress_bc_rd_func)(Image *, UsedChannels p_channels);
+ // External VRAM decompression function pointers.
+
static void (*_image_decompress_bc)(Image *);
static void (*_image_decompress_bptc)(Image *);
static void (*_image_decompress_etc1)(Image *);
static void (*_image_decompress_etc2)(Image *);
static void (*_image_decompress_astc)(Image *);
+ // External packer function pointers.
+
static Vector<uint8_t> (*webp_lossy_packer)(const Ref<Image> &p_image, float p_quality);
static Vector<uint8_t> (*webp_lossless_packer)(const Ref<Image> &p_image);
- static Ref<Image> (*webp_unpacker)(const Vector<uint8_t> &p_buffer);
static Vector<uint8_t> (*png_packer)(const Ref<Image> &p_image);
- static Ref<Image> (*png_unpacker)(const Vector<uint8_t> &p_buffer);
static Vector<uint8_t> (*basis_universal_packer)(const Ref<Image> &p_image, UsedChannels p_channels);
+
+ static Ref<Image> (*webp_unpacker)(const Vector<uint8_t> &p_buffer);
+ static Ref<Image> (*png_unpacker)(const Vector<uint8_t> &p_buffer);
static Ref<Image> (*basis_universal_unpacker)(const Vector<uint8_t> &p_buffer);
static Ref<Image> (*basis_universal_unpacker_ptr)(const uint8_t *p_data, int p_size);
- _FORCE_INLINE_ Color _get_color_at_ofs(const uint8_t *ptr, uint32_t ofs) const;
- _FORCE_INLINE_ void _set_color_at_ofs(uint8_t *ptr, uint32_t ofs, const Color &p_color);
-
protected:
static void _bind_methods();
@@ -192,15 +246,12 @@ private:
int height = 0;
bool mipmaps = false;
- void _copy_internals_from(const Image &p_image) {
- format = p_image.format;
- width = p_image.width;
- height = p_image.height;
- mipmaps = p_image.mipmaps;
- data = p_image.data;
- }
+ void _copy_internals_from(const Image &p_image);
+
+ _FORCE_INLINE_ Color _get_color_at_ofs(const uint8_t *ptr, uint32_t ofs) const;
+ _FORCE_INLINE_ void _set_color_at_ofs(uint8_t *ptr, uint32_t ofs, const Color &p_color);
- _FORCE_INLINE_ void _get_mipmap_offset_and_size(int p_mipmap, int64_t &r_offset, int &r_width, int &r_height) const; //get where the mipmap begins in data
+ _FORCE_INLINE_ void _get_mipmap_offset_and_size(int p_mipmap, int64_t &r_offset, int &r_width, int &r_height) const; // Get where the mipmap begins in data.
static int64_t _get_dst_image_size(int p_width, int p_height, Format p_format, int &r_mipmaps, int p_mipmaps = -1, int *r_mm_width = nullptr, int *r_mm_height = nullptr);
bool _can_modify(Format p_format) const;
@@ -227,52 +278,32 @@ private:
static void renormalize_rgbe9995(uint32_t *p_rgb);
public:
- int get_width() const; ///< Get image width
- int get_height() const; ///< Get image height
+ int get_width() const;
+ int get_height() const;
Size2i get_size() const;
bool has_mipmaps() const;
int get_mipmap_count() const;
- /**
- * Convert the image to another format, conversion only to raw byte format
- */
+ // Convert the image to another format, conversion only to raw byte format.
void convert(Format p_new_format);
- /**
- * Get the current image format.
- */
Format get_format() const;
- /**
- * Get where the mipmap begins in data.
- */
+ // Get where the mipmap begins in data.
int64_t get_mipmap_offset(int p_mipmap) const;
void get_mipmap_offset_and_size(int p_mipmap, int64_t &r_ofs, int64_t &r_size) const;
void get_mipmap_offset_size_and_dimensions(int p_mipmap, int64_t &r_ofs, int64_t &r_size, int &w, int &h) const;
- enum Image3DValidateError {
- VALIDATE_3D_OK,
- VALIDATE_3D_ERR_IMAGE_EMPTY,
- VALIDATE_3D_ERR_MISSING_IMAGES,
- VALIDATE_3D_ERR_EXTRA_IMAGES,
- VALIDATE_3D_ERR_IMAGE_SIZE_MISMATCH,
- VALIDATE_3D_ERR_IMAGE_FORMAT_MISMATCH,
- VALIDATE_3D_ERR_IMAGE_HAS_MIPMAPS,
- };
-
static Image3DValidateError validate_3d_image(Format p_format, int p_width, int p_height, int p_depth, bool p_mipmaps, const Vector<Ref<Image>> &p_images);
static String get_3d_image_validation_error_text(Image3DValidateError p_error);
- /**
- * Resize the image, using the preferred interpolation method.
- */
+ // Resize the image, using the preferred interpolation method.
void resize_to_po2(bool p_square = false, Interpolation p_interpolation = INTERPOLATE_BILINEAR);
void resize(int p_width, int p_height, Interpolation p_interpolation = INTERPOLATE_BILINEAR);
void shrink_x2();
bool is_size_po2() const;
- /**
- * Crop the image to a specific size, if larger, then the image is filled by black
- */
+
+ // Crop the image to a specific size, if larger, then the image is filled by black.
void crop_from_point(int p_x, int p_y, int p_width, int p_height);
void crop(int p_width, int p_height);
@@ -282,34 +313,20 @@ public:
void flip_x();
void flip_y();
- /**
- * Generate a mipmap to an image (creates an image 1/4 the size, with averaging of 4->1)
- */
+ // Generate a mipmap chain of an image (creates an image 1/4 the size, with averaging of 4->1).
Error generate_mipmaps(bool p_renormalize = false);
- enum RoughnessChannel {
- ROUGHNESS_CHANNEL_R,
- ROUGHNESS_CHANNEL_G,
- ROUGHNESS_CHANNEL_B,
- ROUGHNESS_CHANNEL_A,
- ROUGHNESS_CHANNEL_L,
- };
-
Error generate_mipmap_roughness(RoughnessChannel p_roughness_channel, const Ref<Image> &p_normal_map);
void clear_mipmaps();
- void normalize(); //for normal maps
+ void normalize();
- /**
- * Creates new internal image data of a given size and format. Current image will be lost.
- */
+ // Creates new internal image data of a given size and format. Current image will be lost.
void initialize_data(int p_width, int p_height, bool p_use_mipmaps, Format p_format);
void initialize_data(int p_width, int p_height, bool p_use_mipmaps, Format p_format, const Vector<uint8_t> &p_data);
void initialize_data(const char **p_xpm);
- /**
- * returns true when the image is empty (0,0) in size
- */
+ // Returns true when the image is empty (0,0) in size.
bool is_empty() const;
Vector<uint8_t> get_data() const;
@@ -329,27 +346,14 @@ public:
static Ref<Image> create_from_data(int p_width, int p_height, bool p_use_mipmaps, Format p_format, const Vector<uint8_t> &p_data);
void set_data(int p_width, int p_height, bool p_use_mipmaps, Format p_format, const Vector<uint8_t> &p_data);
- /**
- * create an empty image
- */
- Image() {}
- /**
- * create an empty image of a specific size and format
- */
- Image(int p_width, int p_height, bool p_use_mipmaps, Format p_format);
- /**
- * import an image of a specific size and format from a pointer
- */
- Image(int p_width, int p_height, bool p_mipmaps, Format p_format, const Vector<uint8_t> &p_data);
+ Image() = default; // Create an empty image.
+ Image(int p_width, int p_height, bool p_use_mipmaps, Format p_format); // Create an empty image of a specific size and format.
+ Image(int p_width, int p_height, bool p_mipmaps, Format p_format, const Vector<uint8_t> &p_data); // Import an image of a specific size and format from a byte vector.
+ Image(const uint8_t *p_mem_png_jpg, int p_len = -1); // Import either a png or jpg from a pointer.
+ Image(const char **p_xpm); // Import an XPM image.
~Image() {}
- enum AlphaMode {
- ALPHA_NONE,
- ALPHA_BIT,
- ALPHA_BLEND
- };
-
AlphaMode detect_alpha() const;
bool is_invisible() const;
@@ -364,21 +368,6 @@ public:
static int64_t get_image_mipmap_offset(int p_width, int p_height, Format p_format, int p_mipmap);
static int64_t get_image_mipmap_offset_and_dimensions(int p_width, int p_height, Format p_format, int p_mipmap, int &r_w, int &r_h);
- enum CompressMode {
- COMPRESS_S3TC,
- COMPRESS_ETC,
- COMPRESS_ETC2,
- COMPRESS_BPTC,
- COMPRESS_ASTC,
- COMPRESS_MAX,
- };
- enum CompressSource {
- COMPRESS_SOURCE_GENERIC,
- COMPRESS_SOURCE_SRGB,
- COMPRESS_SOURCE_NORMAL,
- COMPRESS_SOURCE_MAX,
- };
-
Error compress(CompressMode p_mode, CompressSource p_source = COMPRESS_SOURCE_GENERIC, ASTCFormat p_astc_format = ASTC_FORMAT_4x4);
Error compress_from_channels(CompressMode p_mode, UsedChannels p_channels, ASTCFormat p_astc_format = ASTC_FORMAT_4x4);
Error decompress();
@@ -424,9 +413,6 @@ public:
void convert_ra_rgba8_to_rg();
void convert_rgba8_to_bgra8();
- Image(const uint8_t *p_mem_png_jpg, int p_len = -1);
- Image(const char **p_xpm);
-
virtual Ref<Resource> duplicate(bool p_subresources = false) const override;
UsedChannels detect_used_channels(CompressSource p_source = COMPRESS_SOURCE_GENERIC) const;
@@ -445,14 +431,7 @@ public:
void set_as_black();
- void copy_internals_from(const Ref<Image> &p_image) {
- ERR_FAIL_COND_MSG(p_image.is_null(), "Cannot copy image internals: invalid Image object.");
- format = p_image->format;
- width = p_image->width;
- height = p_image->height;
- mipmaps = p_image->mipmaps;
- data = p_image->data;
- }
+ void copy_internals_from(const Ref<Image> &p_image);
Dictionary compute_image_metrics(const Ref<Image> p_compared_image, bool p_luma_metric = true);
};
diff --git a/core/io/marshalls.cpp b/core/io/marshalls.cpp
index d425af0afd..45b10b50bc 100644
--- a/core/io/marshalls.cpp
+++ b/core/io/marshalls.cpp
@@ -35,8 +35,6 @@
#include "core/io/resource_loader.h"
#include "core/object/ref_counted.h"
#include "core/object/script_language.h"
-#include "core/os/keyboard.h"
-#include "core/string/print_string.h"
#include <limits.h>
#include <stdio.h>
@@ -71,10 +69,31 @@ ObjectID EncodedObjectAsID::get_object_id() const {
// For `Variant::ARRAY`.
// Occupies bits 16 and 17.
#define HEADER_DATA_FIELD_TYPED_ARRAY_MASK (0b11 << 16)
-#define HEADER_DATA_FIELD_TYPED_ARRAY_NONE (0b00 << 16)
-#define HEADER_DATA_FIELD_TYPED_ARRAY_BUILTIN (0b01 << 16)
-#define HEADER_DATA_FIELD_TYPED_ARRAY_CLASS_NAME (0b10 << 16)
-#define HEADER_DATA_FIELD_TYPED_ARRAY_SCRIPT (0b11 << 16)
+#define HEADER_DATA_FIELD_TYPED_ARRAY_SHIFT 16
+
+// For `Variant::DICTIONARY`.
+// Occupies bits 16 and 17.
+#define HEADER_DATA_FIELD_TYPED_DICTIONARY_KEY_MASK (0b11 << 16)
+#define HEADER_DATA_FIELD_TYPED_DICTIONARY_KEY_SHIFT 16
+// Occupies bits 18 and 19.
+#define HEADER_DATA_FIELD_TYPED_DICTIONARY_VALUE_MASK (0b11 << 18)
+#define HEADER_DATA_FIELD_TYPED_DICTIONARY_VALUE_SHIFT 18
+
+enum ContainerTypeKind {
+ CONTAINER_TYPE_KIND_NONE = 0b00,
+ CONTAINER_TYPE_KIND_BUILTIN = 0b01,
+ CONTAINER_TYPE_KIND_CLASS_NAME = 0b10,
+ CONTAINER_TYPE_KIND_SCRIPT = 0b11,
+};
+
+struct ContainerType {
+ Variant::Type builtin_type = Variant::NIL;
+ StringName class_name;
+ Ref<Script> script;
+};
+
+#define GET_CONTAINER_TYPE_KIND(m_header, m_field) \
+ ((ContainerTypeKind)(((m_header) & HEADER_DATA_FIELD_##m_field##_MASK) >> HEADER_DATA_FIELD_##m_field##_SHIFT))
static Error _decode_string(const uint8_t *&buf, int &len, int *r_len, String &r_string) {
ERR_FAIL_COND_V(len < 4, ERR_INVALID_DATA);
@@ -82,7 +101,7 @@ static Error _decode_string(const uint8_t *&buf, int &len, int *r_len, String &r
int32_t strlen = decode_uint32(buf);
int32_t pad = 0;
- // Handle padding
+ // Handle padding.
if (strlen % 4) {
pad = 4 - strlen % 4;
}
@@ -90,7 +109,7 @@ static Error _decode_string(const uint8_t *&buf, int &len, int *r_len, String &r
buf += 4;
len -= 4;
- // Ensure buffer is big enough
+ // Ensure buffer is big enough.
ERR_FAIL_ADD_OF(strlen, pad, ERR_FILE_EOF);
ERR_FAIL_COND_V(strlen < 0 || strlen + pad > len, ERR_FILE_EOF);
@@ -98,10 +117,10 @@ static Error _decode_string(const uint8_t *&buf, int &len, int *r_len, String &r
ERR_FAIL_COND_V(str.parse_utf8((const char *)buf, strlen) != OK, ERR_INVALID_DATA);
r_string = str;
- // Add padding
+ // Add padding.
strlen += pad;
- // Update buffer pos, left data count, and return size
+ // Update buffer pos, left data count, and return size.
buf += strlen;
len -= strlen;
if (r_len) {
@@ -111,6 +130,65 @@ static Error _decode_string(const uint8_t *&buf, int &len, int *r_len, String &r
return OK;
}
+static Error _decode_container_type(const uint8_t *&buf, int &len, int *r_len, bool p_allow_objects, ContainerTypeKind p_type_kind, ContainerType &r_type) {
+ switch (p_type_kind) {
+ case CONTAINER_TYPE_KIND_NONE: {
+ return OK;
+ } break;
+ case CONTAINER_TYPE_KIND_BUILTIN: {
+ ERR_FAIL_COND_V(len < 4, ERR_INVALID_DATA);
+
+ int32_t bt = decode_uint32(buf);
+ buf += 4;
+ len -= 4;
+ if (r_len) {
+ (*r_len) += 4;
+ }
+
+ ERR_FAIL_INDEX_V(bt, Variant::VARIANT_MAX, ERR_INVALID_DATA);
+ r_type.builtin_type = (Variant::Type)bt;
+ if (!p_allow_objects && r_type.builtin_type == Variant::OBJECT) {
+ r_type.class_name = EncodedObjectAsID::get_class_static();
+ }
+ return OK;
+ } break;
+ case CONTAINER_TYPE_KIND_CLASS_NAME: {
+ String str;
+ Error err = _decode_string(buf, len, r_len, str);
+ if (err) {
+ return err;
+ }
+
+ r_type.builtin_type = Variant::OBJECT;
+ if (p_allow_objects) {
+ r_type.class_name = str;
+ } else {
+ r_type.class_name = EncodedObjectAsID::get_class_static();
+ }
+ return OK;
+ } break;
+ case CONTAINER_TYPE_KIND_SCRIPT: {
+ String path;
+ Error err = _decode_string(buf, len, r_len, path);
+ if (err) {
+ return err;
+ }
+
+ r_type.builtin_type = Variant::OBJECT;
+ if (p_allow_objects) {
+ ERR_FAIL_COND_V_MSG(path.is_empty() || !path.begins_with("res://") || !ResourceLoader::exists(path, "Script"), ERR_INVALID_DATA, vformat("Invalid script path \"%s\".", path));
+ r_type.script = ResourceLoader::load(path, "Script");
+ ERR_FAIL_COND_V_MSG(r_type.script.is_null(), ERR_INVALID_DATA, vformat("Can't load script at path \"%s\".", path));
+ r_type.class_name = r_type.script->get_instance_base_type();
+ } else {
+ r_type.class_name = EncodedObjectAsID::get_class_static();
+ }
+ return OK;
+ } break;
+ }
+ ERR_FAIL_V_MSG(ERR_INVALID_DATA, "Invalid container type kind."); // Future proofing.
+}
+
Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int *r_len, bool p_allow_objects, int p_depth) {
ERR_FAIL_COND_V_MSG(p_depth > Variant::MAX_RECURSION_DEPTH, ERR_OUT_OF_MEMORY, "Variant is too deep. Bailing.");
const uint8_t *buf = p_buffer;
@@ -128,7 +206,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int
*r_len = 4;
}
- // Note: We cannot use sizeof(real_t) for decoding, in case a different size is encoded.
+ // NOTE: We cannot use `sizeof(real_t)` for decoding, in case a different size is encoded.
// Decoding math types always checks for the encoded size, while encoding always uses compilation setting.
// This does lead to some code duplication for decoding, but compatibility is the priority.
switch (header & HEADER_TYPE_MASK) {
@@ -190,7 +268,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int
} break;
- // math types
+ // Math types.
case Variant::VECTOR2: {
Vector2 val;
if (header & HEADER_DATA_FLAG_64) {
@@ -541,7 +619,8 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int
r_variant = val;
} break;
- // misc types
+
+ // Misc types.
case Variant::COLOR: {
ERR_FAIL_COND_V(len < 4 * 4, ERR_INVALID_DATA);
Color val;
@@ -570,7 +649,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int
int32_t strlen = decode_uint32(buf);
if (strlen & 0x80000000) {
- //new format
+ // New format.
ERR_FAIL_COND_V(len < 12, ERR_INVALID_DATA);
Vector<StringName> names;
Vector<StringName> subnames;
@@ -609,8 +688,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int
r_variant = NodePath(names, subnames, np_flags & 1);
} else {
- //old format, just a string
-
+ // Old format, just a string.
ERR_FAIL_V(ERR_INVALID_DATA);
}
@@ -700,9 +778,9 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int
if (str == "script" && value.get_type() != Variant::NIL) {
ERR_FAIL_COND_V_MSG(value.get_type() != Variant::STRING, ERR_INVALID_DATA, "Invalid value for \"script\" property, expected script path as String.");
String path = value;
- ERR_FAIL_COND_V_MSG(path.is_empty() || !path.begins_with("res://") || !ResourceLoader::exists(path, "Script"), ERR_INVALID_DATA, vformat("Invalid script path: '%s'.", path));
+ ERR_FAIL_COND_V_MSG(path.is_empty() || !path.begins_with("res://") || !ResourceLoader::exists(path, "Script"), ERR_INVALID_DATA, vformat("Invalid script path \"%s\".", path));
Ref<Script> script = ResourceLoader::load(path, "Script");
- ERR_FAIL_COND_V_MSG(script.is_null(), ERR_INVALID_DATA, vformat("Can't load script at path: '%s'.", path));
+ ERR_FAIL_COND_V_MSG(script.is_null(), ERR_INVALID_DATA, vformat("Can't load script at path \"%s\".", path));
obj->set_script(script);
} else {
obj->set(str, value);
@@ -733,9 +811,30 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int
r_variant = Signal(id, StringName(name));
} break;
case Variant::DICTIONARY: {
+ ContainerType key_type;
+
+ {
+ ContainerTypeKind key_type_kind = GET_CONTAINER_TYPE_KIND(header, TYPED_DICTIONARY_KEY);
+ Error err = _decode_container_type(buf, len, r_len, p_allow_objects, key_type_kind, key_type);
+ if (err) {
+ return err;
+ }
+ }
+
+ ContainerType value_type;
+
+ {
+ ContainerTypeKind value_type_kind = GET_CONTAINER_TYPE_KIND(header, TYPED_DICTIONARY_VALUE);
+ Error err = _decode_container_type(buf, len, r_len, p_allow_objects, value_type_kind, value_type);
+ if (err) {
+ return err;
+ }
+ }
+
ERR_FAIL_COND_V(len < 4, ERR_INVALID_DATA);
+
int32_t count = decode_uint32(buf);
- // bool shared = count&0x80000000;
+ //bool shared = count & 0x80000000;
count &= 0x7FFFFFFF;
buf += 4;
@@ -745,7 +844,10 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int
(*r_len) += 4; // Size of count number.
}
- Dictionary d;
+ Dictionary dict;
+ if (key_type.builtin_type != Variant::NIL || value_type.builtin_type != Variant::NIL) {
+ dict.set_typed(key_type.builtin_type, key_type.class_name, key_type.script, value_type.builtin_type, value_type.class_name, value_type.script);
+ }
for (int i = 0; i < count; i++) {
Variant key, value;
@@ -769,75 +871,27 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int
(*r_len) += used;
}
- d[key] = value;
+ dict[key] = value;
}
- r_variant = d;
+ r_variant = dict;
} break;
case Variant::ARRAY: {
- Variant::Type builtin_type = Variant::VARIANT_MAX;
- StringName class_name;
- Ref<Script> script;
-
- switch (header & HEADER_DATA_FIELD_TYPED_ARRAY_MASK) {
- case HEADER_DATA_FIELD_TYPED_ARRAY_NONE:
- break; // Untyped array.
- case HEADER_DATA_FIELD_TYPED_ARRAY_BUILTIN: {
- ERR_FAIL_COND_V(len < 4, ERR_INVALID_DATA);
-
- int32_t bt = decode_uint32(buf);
- buf += 4;
- len -= 4;
- if (r_len) {
- (*r_len) += 4;
- }
+ ContainerType type;
- ERR_FAIL_INDEX_V(bt, Variant::VARIANT_MAX, ERR_INVALID_DATA);
- builtin_type = (Variant::Type)bt;
- if (!p_allow_objects && builtin_type == Variant::OBJECT) {
- class_name = EncodedObjectAsID::get_class_static();
- }
- } break;
- case HEADER_DATA_FIELD_TYPED_ARRAY_CLASS_NAME: {
- String str;
- Error err = _decode_string(buf, len, r_len, str);
- if (err) {
- return err;
- }
-
- builtin_type = Variant::OBJECT;
- if (p_allow_objects) {
- class_name = str;
- } else {
- class_name = EncodedObjectAsID::get_class_static();
- }
- } break;
- case HEADER_DATA_FIELD_TYPED_ARRAY_SCRIPT: {
- String path;
- Error err = _decode_string(buf, len, r_len, path);
- if (err) {
- return err;
- }
-
- builtin_type = Variant::OBJECT;
- if (p_allow_objects) {
- ERR_FAIL_COND_V_MSG(path.is_empty() || !path.begins_with("res://") || !ResourceLoader::exists(path, "Script"), ERR_INVALID_DATA, vformat("Invalid script path: '%s'.", path));
- script = ResourceLoader::load(path, "Script");
- ERR_FAIL_COND_V_MSG(script.is_null(), ERR_INVALID_DATA, vformat("Can't load script at path: '%s'.", path));
- class_name = script->get_instance_base_type();
- } else {
- class_name = EncodedObjectAsID::get_class_static();
- }
- } break;
- default:
- ERR_FAIL_V(ERR_INVALID_DATA); // Future proofing.
+ {
+ ContainerTypeKind type_kind = GET_CONTAINER_TYPE_KIND(header, TYPED_ARRAY);
+ Error err = _decode_container_type(buf, len, r_len, p_allow_objects, type_kind, type);
+ if (err) {
+ return err;
+ }
}
ERR_FAIL_COND_V(len < 4, ERR_INVALID_DATA);
int32_t count = decode_uint32(buf);
- // bool shared = count&0x80000000;
+ //bool shared = count & 0x80000000;
count &= 0x7FFFFFFF;
buf += 4;
@@ -847,29 +901,29 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int
(*r_len) += 4; // Size of count number.
}
- Array varr;
- if (builtin_type != Variant::VARIANT_MAX) {
- varr.set_typed(builtin_type, class_name, script);
+ Array array;
+ if (type.builtin_type != Variant::NIL) {
+ array.set_typed(type.builtin_type, type.class_name, type.script);
}
for (int i = 0; i < count; i++) {
int used = 0;
- Variant v;
- Error err = decode_variant(v, buf, len, &used, p_allow_objects, p_depth + 1);
+ Variant elem;
+ Error err = decode_variant(elem, buf, len, &used, p_allow_objects, p_depth + 1);
ERR_FAIL_COND_V_MSG(err != OK, err, "Error when trying to decode Variant.");
buf += used;
len -= used;
- varr.push_back(v);
+ array.push_back(elem);
if (r_len) {
(*r_len) += used;
}
}
- r_variant = varr;
+ r_variant = array;
} break;
- // arrays
+ // Packed arrays.
case Variant::PACKED_BYTE_ARRAY: {
ERR_FAIL_COND_V(len < 4, ERR_INVALID_DATA);
int32_t count = decode_uint32(buf);
@@ -908,7 +962,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int
Vector<int32_t> data;
if (count) {
- //const int*rbuf=(const int*)buf;
+ //const int *rbuf = (const int *)buf;
data.resize(count);
int32_t *w = data.ptrw();
for (int32_t i = 0; i < count; i++) {
@@ -932,7 +986,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int
Vector<int64_t> data;
if (count) {
- //const int*rbuf=(const int*)buf;
+ //const int *rbuf = (const int *)buf;
data.resize(count);
int64_t *w = data.ptrw();
for (int64_t i = 0; i < count; i++) {
@@ -956,7 +1010,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int
Vector<float> data;
if (count) {
- //const float*rbuf=(const float*)buf;
+ //const float *rbuf = (const float *)buf;
data.resize(count);
float *w = data.ptrw();
for (int32_t i = 0; i < count; i++) {
@@ -1267,13 +1321,50 @@ static void _encode_string(const String &p_string, uint8_t *&buf, int &r_len) {
r_len += 4 + utf8.length();
while (r_len % 4) {
- r_len++; //pad
+ r_len++; // Pad.
if (buf) {
*(buf++) = 0;
}
}
}
+static void _encode_container_type_header(const ContainerType &p_type, uint32_t &header, uint32_t p_shift, bool p_full_objects) {
+ if (p_type.builtin_type != Variant::NIL) {
+ if (p_type.script.is_valid()) {
+ header |= (p_full_objects ? CONTAINER_TYPE_KIND_SCRIPT : CONTAINER_TYPE_KIND_CLASS_NAME) << p_shift;
+ } else if (p_type.class_name != StringName()) {
+ header |= CONTAINER_TYPE_KIND_CLASS_NAME << p_shift;
+ } else {
+ // No need to check `p_full_objects` since `class_name` should be non-empty for `builtin_type == Variant::OBJECT`.
+ header |= CONTAINER_TYPE_KIND_BUILTIN << p_shift;
+ }
+ }
+}
+
+static Error _encode_container_type(const ContainerType &p_type, uint8_t *&buf, int &r_len, bool p_full_objects) {
+ if (p_type.builtin_type != Variant::NIL) {
+ if (p_type.script.is_valid()) {
+ if (p_full_objects) {
+ String path = p_type.script->get_path();
+ ERR_FAIL_COND_V_MSG(path.is_empty() || !path.begins_with("res://"), ERR_UNAVAILABLE, "Failed to encode a path to a custom script for a container type.");
+ _encode_string(path, buf, r_len);
+ } else {
+ _encode_string(EncodedObjectAsID::get_class_static(), buf, r_len);
+ }
+ } else if (p_type.class_name != StringName()) {
+ _encode_string(p_full_objects ? p_type.class_name.operator String() : EncodedObjectAsID::get_class_static(), buf, r_len);
+ } else {
+ // No need to check `p_full_objects` since `class_name` should be non-empty for `builtin_type == Variant::OBJECT`.
+ if (buf) {
+ encode_uint32(p_type.builtin_type, buf);
+ buf += 4;
+ }
+ r_len += 4;
+ }
+ }
+ return OK;
+}
+
Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bool p_full_objects, int p_depth) {
ERR_FAIL_COND_V_MSG(p_depth > Variant::MAX_RECURSION_DEPTH, ERR_OUT_OF_MEMORY, "Potential infinite recursion detected. Bailing.");
uint8_t *buf = r_buffer;
@@ -1312,20 +1403,32 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo
header |= HEADER_DATA_FLAG_OBJECT_AS_ID;
}
} break;
+ case Variant::DICTIONARY: {
+ Dictionary dict = p_variant;
+
+ ContainerType key_type;
+ key_type.builtin_type = (Variant::Type)dict.get_typed_key_builtin();
+ key_type.class_name = dict.get_typed_key_class_name();
+ key_type.script = dict.get_typed_key_script();
+
+ _encode_container_type_header(key_type, header, HEADER_DATA_FIELD_TYPED_DICTIONARY_KEY_SHIFT, p_full_objects);
+
+ ContainerType value_type;
+ value_type.builtin_type = (Variant::Type)dict.get_typed_value_builtin();
+ value_type.class_name = dict.get_typed_value_class_name();
+ value_type.script = dict.get_typed_value_script();
+
+ _encode_container_type_header(value_type, header, HEADER_DATA_FIELD_TYPED_DICTIONARY_VALUE_SHIFT, p_full_objects);
+ } break;
case Variant::ARRAY: {
Array array = p_variant;
- if (array.is_typed()) {
- Ref<Script> script = array.get_typed_script();
- if (script.is_valid()) {
- header |= p_full_objects ? HEADER_DATA_FIELD_TYPED_ARRAY_SCRIPT : HEADER_DATA_FIELD_TYPED_ARRAY_CLASS_NAME;
- } else if (array.get_typed_class_name() != StringName()) {
- header |= HEADER_DATA_FIELD_TYPED_ARRAY_CLASS_NAME;
- } else {
- // No need to check `p_full_objects` since for `Variant::OBJECT`
- // `array.get_typed_class_name()` should be non-empty.
- header |= HEADER_DATA_FIELD_TYPED_ARRAY_BUILTIN;
- }
- }
+
+ ContainerType type;
+ type.builtin_type = (Variant::Type)array.get_typed_builtin();
+ type.class_name = array.get_typed_class_name();
+ type.script = array.get_typed_script();
+
+ _encode_container_type_header(type, header, HEADER_DATA_FIELD_TYPED_ARRAY_SHIFT, p_full_objects);
} break;
#ifdef REAL_T_IS_DOUBLE
case Variant::VECTOR2:
@@ -1346,7 +1449,8 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo
} break;
#endif // REAL_T_IS_DOUBLE
default: {
- } // nothing to do at this stage
+ // Nothing to do at this stage.
+ } break;
}
if (buf) {
@@ -1357,7 +1461,7 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo
switch (p_variant.get_type()) {
case Variant::NIL: {
- //nothing to do
+ // Nothing to do.
} break;
case Variant::BOOL: {
if (buf) {
@@ -1369,7 +1473,7 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo
} break;
case Variant::INT: {
if (header & HEADER_DATA_FLAG_64) {
- //64 bits
+ // 64 bits.
if (buf) {
encode_uint64(p_variant.operator int64_t(), buf);
}
@@ -1403,7 +1507,7 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo
case Variant::NODE_PATH: {
NodePath np = p_variant;
if (buf) {
- encode_uint32(uint32_t(np.get_name_count()) | 0x80000000, buf); //for compatibility with the old format
+ encode_uint32(uint32_t(np.get_name_count()) | 0x80000000, buf); // For compatibility with the old format.
encode_uint32(np.get_subname_count(), buf + 4);
uint32_t np_flags = 0;
if (np.is_absolute()) {
@@ -1453,7 +1557,7 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo
} break;
- // math types
+ // Math types.
case Variant::VECTOR2: {
if (buf) {
Vector2 v2 = p_variant;
@@ -1637,7 +1741,7 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo
} break;
- // misc types
+ // Misc types.
case Variant::COLOR: {
if (buf) {
Color c = p_variant;
@@ -1748,29 +1852,53 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo
r_len += 8;
} break;
case Variant::DICTIONARY: {
- Dictionary d = p_variant;
+ Dictionary dict = p_variant;
+
+ {
+ ContainerType key_type;
+ key_type.builtin_type = (Variant::Type)dict.get_typed_key_builtin();
+ key_type.class_name = dict.get_typed_key_class_name();
+ key_type.script = dict.get_typed_key_script();
+
+ Error err = _encode_container_type(key_type, buf, r_len, p_full_objects);
+ if (err) {
+ return err;
+ }
+ }
+
+ {
+ ContainerType value_type;
+ value_type.builtin_type = (Variant::Type)dict.get_typed_value_builtin();
+ value_type.class_name = dict.get_typed_value_class_name();
+ value_type.script = dict.get_typed_value_script();
+
+ Error err = _encode_container_type(value_type, buf, r_len, p_full_objects);
+ if (err) {
+ return err;
+ }
+ }
if (buf) {
- encode_uint32(uint32_t(d.size()), buf);
+ encode_uint32(uint32_t(dict.size()), buf);
buf += 4;
}
r_len += 4;
List<Variant> keys;
- d.get_key_list(&keys);
+ dict.get_key_list(&keys);
- for (const Variant &E : keys) {
+ for (const Variant &key : keys) {
int len;
- Error err = encode_variant(E, buf, len, p_full_objects, p_depth + 1);
+ Error err = encode_variant(key, buf, len, p_full_objects, p_depth + 1);
ERR_FAIL_COND_V(err, err);
ERR_FAIL_COND_V(len % 4, ERR_BUG);
r_len += len;
if (buf) {
buf += len;
}
- Variant *v = d.getptr(E);
- ERR_FAIL_NULL_V(v, ERR_BUG);
- err = encode_variant(*v, buf, len, p_full_objects, p_depth + 1);
+ Variant *value = dict.getptr(key);
+ ERR_FAIL_NULL_V(value, ERR_BUG);
+ err = encode_variant(*value, buf, len, p_full_objects, p_depth + 1);
ERR_FAIL_COND_V(err, err);
ERR_FAIL_COND_V(len % 4, ERR_BUG);
r_len += len;
@@ -1783,27 +1911,15 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo
case Variant::ARRAY: {
Array array = p_variant;
- if (array.is_typed()) {
- Variant variant = array.get_typed_script();
- Ref<Script> script = variant;
- if (script.is_valid()) {
- if (p_full_objects) {
- String path = script->get_path();
- ERR_FAIL_COND_V_MSG(path.is_empty() || !path.begins_with("res://"), ERR_UNAVAILABLE, "Failed to encode a path to a custom script for an array type.");
- _encode_string(path, buf, r_len);
- } else {
- _encode_string(EncodedObjectAsID::get_class_static(), buf, r_len);
- }
- } else if (array.get_typed_class_name() != StringName()) {
- _encode_string(p_full_objects ? array.get_typed_class_name().operator String() : EncodedObjectAsID::get_class_static(), buf, r_len);
- } else {
- // No need to check `p_full_objects` since for `Variant::OBJECT`
- // `array.get_typed_class_name()` should be non-empty.
- if (buf) {
- encode_uint32(array.get_typed_builtin(), buf);
- buf += 4;
- }
- r_len += 4;
+ {
+ ContainerType type;
+ type.builtin_type = (Variant::Type)array.get_typed_builtin();
+ type.class_name = array.get_typed_class_name();
+ type.script = array.get_typed_script();
+
+ Error err = _encode_container_type(type, buf, r_len, p_full_objects);
+ if (err) {
+ return err;
}
}
@@ -1813,9 +1929,9 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo
}
r_len += 4;
- for (const Variant &var : array) {
+ for (const Variant &elem : array) {
int len;
- Error err = encode_variant(var, buf, len, p_full_objects, p_depth + 1);
+ Error err = encode_variant(elem, buf, len, p_full_objects, p_depth + 1);
ERR_FAIL_COND_V(err, err);
ERR_FAIL_COND_V(len % 4, ERR_BUG);
if (buf) {
@@ -1825,7 +1941,8 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo
}
} break;
- // arrays
+
+ // Packed arrays.
case Variant::PACKED_BYTE_ARRAY: {
Vector<uint8_t> data = p_variant;
int datalen = data.size();
@@ -1941,7 +2058,7 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo
r_len += 4 + utf8.length() + 1;
while (r_len % 4) {
- r_len++; //pad
+ r_len++; // Pad.
if (buf) {
*(buf++) = 0;
}
@@ -2059,9 +2176,9 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo
}
Vector<float> vector3_to_float32_array(const Vector3 *vecs, size_t count) {
- // We always allocate a new array, and we don't memcpy.
- // We also don't consider returning a pointer to the passed vectors when sizeof(real_t) == 4.
- // One reason is that we could decide to put a 4th component in Vector3 for SIMD/mobile performance,
+ // We always allocate a new array, and we don't `memcpy()`.
+ // We also don't consider returning a pointer to the passed vectors when `sizeof(real_t) == 4`.
+ // One reason is that we could decide to put a 4th component in `Vector3` for SIMD/mobile performance,
// which would cause trouble with these optimizations.
Vector<float> floats;
if (count == 0) {
diff --git a/core/io/net_socket.h b/core/io/net_socket.h
index 906218230e..c9cfb0a446 100644
--- a/core/io/net_socket.h
+++ b/core/io/net_socket.h
@@ -78,6 +78,8 @@ public:
virtual void set_reuse_address_enabled(bool p_enabled) = 0;
virtual Error join_multicast_group(const IPAddress &p_multi_address, const String &p_if_name) = 0;
virtual Error leave_multicast_group(const IPAddress &p_multi_address, const String &p_if_name) = 0;
+
+ virtual ~NetSocket() {}
};
#endif // NET_SOCKET_H
diff --git a/core/io/packet_peer_udp.cpp b/core/io/packet_peer_udp.cpp
index 198df4c016..db23bc070c 100644
--- a/core/io/packet_peer_udp.cpp
+++ b/core/io/packet_peer_udp.cpp
@@ -107,6 +107,19 @@ Error PacketPeerUDP::get_packet(const uint8_t **r_buffer, int &r_buffer_size) {
return ERR_UNAVAILABLE;
}
+/* Bogus GCC warning here:
+ * In member function 'int RingBuffer<T>::read(T*, int, bool) [with T = unsigned char]',
+ * inlined from 'virtual Error PacketPeerUDP::get_packet(const uint8_t**, int&)' at core/io/packet_peer_udp.cpp:112:9,
+ * inlined from 'virtual Error PacketPeerUDP::get_packet(const uint8_t**, int&)' at core/io/packet_peer_udp.cpp:99:7:
+ * Error: ./core/ring_buffer.h:68:46: error: writing 1 byte into a region of size 0 [-Werror=stringop-overflow=]
+ * 68 | p_buf[dst++] = read[pos + i];
+ * | ~~~~~~~~~~~~~^~~~~~~
+ */
+#if defined(__GNUC__) && !defined(__clang__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic warning "-Wstringop-overflow=0"
+#endif
+
uint32_t size = 0;
uint8_t ipv6[16] = {};
rb.read(ipv6, 16, true);
@@ -117,6 +130,11 @@ Error PacketPeerUDP::get_packet(const uint8_t **r_buffer, int &r_buffer_size) {
--queue_count;
*r_buffer = packet_buffer;
r_buffer_size = size;
+
+#if defined(__GNUC__) && !defined(__clang__)
+#pragma GCC diagnostic pop
+#endif
+
return OK;
}
diff --git a/core/io/pck_packer.cpp b/core/io/pck_packer.cpp
index 9127d65abd..877374c384 100644
--- a/core/io/pck_packer.cpp
+++ b/core/io/pck_packer.cpp
@@ -50,7 +50,8 @@ static int _get_pad(int p_alignment, int p_n) {
void PCKPacker::_bind_methods() {
ClassDB::bind_method(D_METHOD("pck_start", "pck_path", "alignment", "key", "encrypt_directory"), &PCKPacker::pck_start, DEFVAL(32), DEFVAL("0000000000000000000000000000000000000000000000000000000000000000"), DEFVAL(false));
- ClassDB::bind_method(D_METHOD("add_file", "pck_path", "source_path", "encrypt"), &PCKPacker::add_file, DEFVAL(false));
+ ClassDB::bind_method(D_METHOD("add_file", "target_path", "source_path", "encrypt"), &PCKPacker::add_file, DEFVAL(false));
+ ClassDB::bind_method(D_METHOD("add_file_removal", "target_path"), &PCKPacker::add_file_removal);
ClassDB::bind_method(D_METHOD("flush", "verbose"), &PCKPacker::flush, DEFVAL(false));
}
@@ -108,23 +109,42 @@ Error PCKPacker::pck_start(const String &p_pck_path, int p_alignment, const Stri
return OK;
}
-Error PCKPacker::add_file(const String &p_pck_path, const String &p_src, bool p_encrypt) {
+Error PCKPacker::add_file_removal(const String &p_target_path) {
ERR_FAIL_COND_V_MSG(file.is_null(), ERR_INVALID_PARAMETER, "File must be opened before use.");
- Ref<FileAccess> f = FileAccess::open(p_src, FileAccess::READ);
+ File pf;
+ // Simplify path here and on every 'files' access so that paths that have extra '/'
+ // symbols or 'res://' in them still match the MD5 hash for the saved path.
+ pf.path = p_target_path.simplify_path().trim_prefix("res://");
+ pf.ofs = ofs;
+ pf.size = 0;
+ pf.removal = true;
+
+ pf.md5.resize(16);
+ pf.md5.fill(0);
+
+ files.push_back(pf);
+
+ return OK;
+}
+
+Error PCKPacker::add_file(const String &p_target_path, const String &p_source_path, bool p_encrypt) {
+ ERR_FAIL_COND_V_MSG(file.is_null(), ERR_INVALID_PARAMETER, "File must be opened before use.");
+
+ Ref<FileAccess> f = FileAccess::open(p_source_path, FileAccess::READ);
if (f.is_null()) {
return ERR_FILE_CANT_OPEN;
}
File pf;
// Simplify path here and on every 'files' access so that paths that have extra '/'
- // symbols in them still match to the MD5 hash for the saved path.
- pf.path = p_pck_path.simplify_path();
- pf.src_path = p_src;
+ // symbols or 'res://' in them still match the MD5 hash for the saved path.
+ pf.path = p_target_path.simplify_path().trim_prefix("res://");
+ pf.src_path = p_source_path;
pf.ofs = ofs;
pf.size = f->get_length();
- Vector<uint8_t> data = FileAccess::get_file_as_bytes(p_src);
+ Vector<uint8_t> data = FileAccess::get_file_as_bytes(p_source_path);
{
unsigned char hash[16];
CryptoCore::md5(data.ptr(), data.size(), hash);
@@ -197,6 +217,9 @@ Error PCKPacker::flush(bool p_verbose) {
if (files[i].encrypted) {
flags |= PACK_FILE_ENCRYPTED;
}
+ if (files[i].removal) {
+ flags |= PACK_FILE_REMOVAL;
+ }
fhead->store_32(flags);
}
@@ -220,6 +243,10 @@ Error PCKPacker::flush(bool p_verbose) {
int count = 0;
for (int i = 0; i < files.size(); i++) {
+ if (files[i].removal) {
+ continue;
+ }
+
Ref<FileAccess> src = FileAccess::open(files[i].src_path, FileAccess::READ);
uint64_t to_write = files[i].size;
diff --git a/core/io/pck_packer.h b/core/io/pck_packer.h
index 3c5435784a..fb2acee45d 100644
--- a/core/io/pck_packer.h
+++ b/core/io/pck_packer.h
@@ -55,13 +55,15 @@ class PCKPacker : public RefCounted {
uint64_t ofs = 0;
uint64_t size = 0;
bool encrypted = false;
+ bool removal = false;
Vector<uint8_t> md5;
};
Vector<File> files;
public:
Error pck_start(const String &p_pck_path, int p_alignment = 32, const String &p_key = "0000000000000000000000000000000000000000000000000000000000000000", bool p_encrypt_directory = false);
- Error add_file(const String &p_pck_path, const String &p_src, bool p_encrypt = false);
+ Error add_file(const String &p_target_path, const String &p_source_path, bool p_encrypt = false);
+ Error add_file_removal(const String &p_target_path);
Error flush(bool p_verbose = false);
PCKPacker() {}
diff --git a/core/io/resource_format_binary.cpp b/core/io/resource_format_binary.cpp
index fe199653ec..e27873d024 100644
--- a/core/io/resource_format_binary.cpp
+++ b/core/io/resource_format_binary.cpp
@@ -1579,6 +1579,10 @@ ResourceUID::ID ResourceFormatLoaderBinary::get_resource_uid(const String &p_pat
return loader.uid;
}
+bool ResourceFormatLoaderBinary::has_custom_uid_support() const {
+ return true;
+}
+
///////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////
diff --git a/core/io/resource_format_binary.h b/core/io/resource_format_binary.h
index ef30de56fa..b4eef2c3e8 100644
--- a/core/io/resource_format_binary.h
+++ b/core/io/resource_format_binary.h
@@ -120,6 +120,7 @@ public:
virtual String get_resource_script_class(const String &p_path) const override;
virtual void get_classes_used(const String &p_path, HashSet<StringName> *r_classes) override;
virtual ResourceUID::ID get_resource_uid(const String &p_path) const override;
+ virtual bool has_custom_uid_support() const override;
virtual void get_dependencies(const String &p_path, List<String> *p_dependencies, bool p_add_types = false) override;
virtual Error rename_dependencies(const String &p_path, const HashMap<String, String> &p_map) override;
};
diff --git a/core/io/resource_importer.cpp b/core/io/resource_importer.cpp
index b72b19ae59..866a5aab1d 100644
--- a/core/io/resource_importer.cpp
+++ b/core/io/resource_importer.cpp
@@ -389,6 +389,10 @@ ResourceUID::ID ResourceFormatImporter::get_resource_uid(const String &p_path) c
return pat.uid;
}
+bool ResourceFormatImporter::has_custom_uid_support() const {
+ return true;
+}
+
Error ResourceFormatImporter::get_resource_import_info(const String &p_path, StringName &r_type, ResourceUID::ID &r_uid, String &r_import_group_file) const {
PathAndType pat;
Error err = _get_path_and_type(p_path, pat);
diff --git a/core/io/resource_importer.h b/core/io/resource_importer.h
index 3127097d8f..30195ee0a7 100644
--- a/core/io/resource_importer.h
+++ b/core/io/resource_importer.h
@@ -72,6 +72,7 @@ public:
virtual bool handles_type(const String &p_type) const override;
virtual String get_resource_type(const String &p_path) const override;
virtual ResourceUID::ID get_resource_uid(const String &p_path) const override;
+ virtual bool has_custom_uid_support() const override;
virtual Variant get_resource_metadata(const String &p_path) const;
virtual bool is_import_valid(const String &p_path) const override;
virtual void get_dependencies(const String &p_path, List<String> *p_dependencies, bool p_add_types = false) override;
@@ -149,7 +150,7 @@ public:
virtual void handle_compatibility_options(HashMap<StringName, Variant> &p_import_params) const {}
virtual String get_option_group_file() const { return String(); }
- virtual Error import(const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files = nullptr, Variant *r_metadata = nullptr) = 0;
+ virtual Error import(ResourceUID::ID p_source_id, const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files = nullptr, Variant *r_metadata = nullptr) = 0;
virtual bool can_import_threaded() const { return false; }
virtual void import_threaded_begin() {}
virtual void import_threaded_end() {}
diff --git a/core/io/resource_loader.cpp b/core/io/resource_loader.cpp
index 08b6b6e982..e33ebf1360 100644
--- a/core/io/resource_loader.cpp
+++ b/core/io/resource_loader.cpp
@@ -34,6 +34,7 @@
#include "core/config/project_settings.h"
#include "core/core_bind.h"
+#include "core/io/dir_access.h"
#include "core/io/file_access.h"
#include "core/io/resource_importer.h"
#include "core/object/script_language.h"
@@ -42,6 +43,7 @@
#include "core/os/safe_binary_mutex.h"
#include "core/string/print_string.h"
#include "core/string/translation_server.h"
+#include "core/templates/rb_set.h"
#include "core/variant/variant_parser.h"
#include "servers/rendering_server.h"
@@ -114,10 +116,21 @@ String ResourceFormatLoader::get_resource_script_class(const String &p_path) con
ResourceUID::ID ResourceFormatLoader::get_resource_uid(const String &p_path) const {
int64_t uid = ResourceUID::INVALID_ID;
- GDVIRTUAL_CALL(_get_resource_uid, p_path, uid);
+ if (has_custom_uid_support()) {
+ GDVIRTUAL_CALL(_get_resource_uid, p_path, uid);
+ } else {
+ Ref<FileAccess> file = FileAccess::open(p_path + ".uid", FileAccess::READ);
+ if (file.is_valid()) {
+ uid = ResourceUID::get_singleton()->text_to_id(file->get_line());
+ }
+ }
return uid;
}
+bool ResourceFormatLoader::has_custom_uid_support() const {
+ return GDVIRTUAL_IS_OVERRIDDEN(_get_resource_uid);
+}
+
void ResourceFormatLoader::get_recognized_extensions_for_type(const String &p_type, List<String> *p_extensions) const {
if (p_type.is_empty() || handles_type(p_type)) {
get_recognized_extensions(p_extensions);
@@ -1161,6 +1174,21 @@ ResourceUID::ID ResourceLoader::get_resource_uid(const String &p_path) {
return ResourceUID::INVALID_ID;
}
+bool ResourceLoader::has_custom_uid_support(const String &p_path) {
+ String local_path = _validate_local_path(p_path);
+
+ for (int i = 0; i < loader_count; i++) {
+ if (!loader[i]->recognize_path(local_path)) {
+ continue;
+ }
+ if (loader[i]->has_custom_uid_support()) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
String ResourceLoader::_path_remap(const String &p_path, bool *r_translation_remapped) {
String new_path = p_path;
@@ -1450,6 +1478,60 @@ bool ResourceLoader::is_cleaning_tasks() {
return cleaning_tasks;
}
+Vector<String> ResourceLoader::list_directory(const String &p_directory) {
+ RBSet<String> files_found;
+ Ref<DirAccess> dir = DirAccess::open(p_directory);
+ if (dir.is_null()) {
+ return Vector<String>();
+ }
+
+ Error err = dir->list_dir_begin();
+ if (err != OK) {
+ return Vector<String>();
+ }
+
+ String d = dir->get_next();
+ while (!d.is_empty()) {
+ bool recognized = false;
+ if (dir->current_is_dir()) {
+ if (d != "." && d != "..") {
+ d += "/";
+ recognized = true;
+ }
+ } else {
+ if (d.ends_with(".import") || d.ends_with(".remap") || d.ends_with(".uid")) {
+ d = d.substr(0, d.rfind("."));
+ }
+
+ if (d.ends_with(".gdc")) {
+ d = d.substr(0, d.rfind("."));
+ d += ".gd";
+ }
+
+ const String full_path = p_directory.path_join(d);
+ // Try all loaders and pick the first match for the type hint.
+ for (int i = 0; i < loader_count; i++) {
+ if (loader[i]->recognize_path(full_path)) {
+ recognized = true;
+ break;
+ }
+ }
+ }
+
+ if (recognized) {
+ files_found.insert(d);
+ }
+ d = dir->get_next();
+ }
+
+ Vector<String> ret;
+ for (const String &f : files_found) {
+ ret.push_back(f);
+ }
+
+ return ret;
+}
+
void ResourceLoader::initialize() {}
void ResourceLoader::finalize() {}
diff --git a/core/io/resource_loader.h b/core/io/resource_loader.h
index 21b688db9c..83729a9725 100644
--- a/core/io/resource_loader.h
+++ b/core/io/resource_loader.h
@@ -83,6 +83,7 @@ public:
virtual String get_resource_type(const String &p_path) const;
virtual String get_resource_script_class(const String &p_path) const;
virtual ResourceUID::ID get_resource_uid(const String &p_path) const;
+ virtual bool has_custom_uid_support() const;
virtual void get_dependencies(const String &p_path, List<String> *p_dependencies, bool p_add_types = false);
virtual Error rename_dependencies(const String &p_path, const HashMap<String, String> &p_map);
virtual bool is_import_valid(const String &p_path) const { return true; }
@@ -240,6 +241,7 @@ public:
static String get_resource_type(const String &p_path);
static String get_resource_script_class(const String &p_path);
static ResourceUID::ID get_resource_uid(const String &p_path);
+ static bool has_custom_uid_support(const String &p_path);
static void get_dependencies(const String &p_path, List<String> *p_dependencies, bool p_add_types = false);
static Error rename_dependencies(const String &p_path, const HashMap<String, String> &p_map);
static bool is_import_valid(const String &p_path);
@@ -304,6 +306,8 @@ public:
static bool is_cleaning_tasks();
+ static Vector<String> list_directory(const String &p_directory);
+
static void initialize();
static void finalize();
};
diff --git a/core/io/stream_peer.cpp b/core/io/stream_peer.cpp
index 7a40796c20..2b5a44a27c 100644
--- a/core/io/stream_peer.cpp
+++ b/core/io/stream_peer.cpp
@@ -225,13 +225,13 @@ void StreamPeer::put_var(const Variant &p_variant, bool p_full_objects) {
}
uint8_t StreamPeer::get_u8() {
- uint8_t buf[1];
+ uint8_t buf[1] = {};
get_data(buf, 1);
return buf[0];
}
int8_t StreamPeer::get_8() {
- uint8_t buf[1];
+ uint8_t buf[1] = {};
get_data(buf, 1);
return buf[0];
}
diff --git a/core/io/tcp_server.cpp b/core/io/tcp_server.cpp
index 39dc6d8897..182b7c0aa8 100644
--- a/core/io/tcp_server.cpp
+++ b/core/io/tcp_server.cpp
@@ -114,7 +114,7 @@ Ref<StreamPeerTCP> TCPServer::take_connection() {
return conn;
}
- conn = Ref<StreamPeerTCP>(memnew(StreamPeerTCP));
+ conn.instantiate();
conn->accept_socket(ns, ip, port);
return conn;
}
diff --git a/core/os/os.h b/core/os/os.h
index ddd4255d11..b5d247b313 100644
--- a/core/os/os.h
+++ b/core/os/os.h
@@ -96,7 +96,15 @@ public:
enum RenderThreadMode {
RENDER_THREAD_UNSAFE,
RENDER_THREAD_SAFE,
- RENDER_SEPARATE_THREAD
+ RENDER_SEPARATE_THREAD,
+ };
+
+ enum StdHandleType {
+ STD_HANDLE_INVALID,
+ STD_HANDLE_CONSOLE,
+ STD_HANDLE_FILE,
+ STD_HANDLE_PIPE,
+ STD_HANDLE_UNKNOWN,
};
protected:
@@ -148,7 +156,12 @@ public:
void print_rich(const char *p_format, ...) _PRINTF_FORMAT_ATTRIBUTE_2_3;
void printerr(const char *p_format, ...) _PRINTF_FORMAT_ATTRIBUTE_2_3;
- virtual String get_stdin_string() = 0;
+ virtual String get_stdin_string(int64_t p_buffer_size = 1024) = 0;
+ virtual PackedByteArray get_stdin_buffer(int64_t p_buffer_size = 1024) = 0;
+
+ virtual StdHandleType get_stdin_type() const { return STD_HANDLE_UNKNOWN; }
+ virtual StdHandleType get_stdout_type() const { return STD_HANDLE_UNKNOWN; }
+ virtual StdHandleType get_stderr_type() const { return STD_HANDLE_UNKNOWN; }
virtual Error get_entropy(uint8_t *r_buffer, int p_bytes) = 0; // Should return cryptographically-safe random bytes.
virtual String get_system_ca_certificates() { return ""; } // Concatenated certificates in PEM format.
diff --git a/core/string/translation_domain.cpp b/core/string/translation_domain.cpp
index 1fdf5b7e75..b621f9f698 100644
--- a/core/string/translation_domain.cpp
+++ b/core/string/translation_domain.cpp
@@ -249,7 +249,10 @@ PackedStringArray TranslationDomain::get_loaded_locales() const {
PackedStringArray locales;
for (const Ref<Translation> &E : translations) {
ERR_CONTINUE(E.is_null());
- locales.push_back(E->get_locale());
+ const String &locale = E->get_locale();
+ if (!locales.has(locale)) {
+ locales.push_back(locale);
+ }
}
return locales;
}
diff --git a/core/string/translation_server.cpp b/core/string/translation_server.cpp
index 47d10c5421..f67ffa3d6e 100644
--- a/core/string/translation_server.cpp
+++ b/core/string/translation_server.cpp
@@ -120,36 +120,45 @@ void TranslationServer::init_locale_info() {
}
}
-String TranslationServer::standardize_locale(const String &p_locale) const {
- return _standardize_locale(p_locale, false);
+TranslationServer::Locale::operator String() const {
+ String out = language;
+ if (!script.is_empty()) {
+ out = out + "_" + script;
+ }
+ if (!country.is_empty()) {
+ out = out + "_" + country;
+ }
+ if (!variant.is_empty()) {
+ out = out + "_" + variant;
+ }
+ return out;
}
-String TranslationServer::_standardize_locale(const String &p_locale, bool p_add_defaults) const {
+TranslationServer::Locale::Locale(const TranslationServer &p_server, const String &p_locale, bool p_add_defaults) {
// Replaces '-' with '_' for macOS style locales.
String univ_locale = p_locale.replace("-", "_");
// Extract locale elements.
- String lang_name, script_name, country_name, variant_name;
Vector<String> locale_elements = univ_locale.get_slice("@", 0).split("_");
- lang_name = locale_elements[0];
+ language = locale_elements[0];
if (locale_elements.size() >= 2) {
if (locale_elements[1].length() == 4 && is_ascii_upper_case(locale_elements[1][0]) && is_ascii_lower_case(locale_elements[1][1]) && is_ascii_lower_case(locale_elements[1][2]) && is_ascii_lower_case(locale_elements[1][3])) {
- script_name = locale_elements[1];
+ script = locale_elements[1];
}
if (locale_elements[1].length() == 2 && is_ascii_upper_case(locale_elements[1][0]) && is_ascii_upper_case(locale_elements[1][1])) {
- country_name = locale_elements[1];
+ country = locale_elements[1];
}
}
if (locale_elements.size() >= 3) {
if (locale_elements[2].length() == 2 && is_ascii_upper_case(locale_elements[2][0]) && is_ascii_upper_case(locale_elements[2][1])) {
- country_name = locale_elements[2];
- } else if (variant_map.has(locale_elements[2].to_lower()) && variant_map[locale_elements[2].to_lower()] == lang_name) {
- variant_name = locale_elements[2].to_lower();
+ country = locale_elements[2];
+ } else if (p_server.variant_map.has(locale_elements[2].to_lower()) && p_server.variant_map[locale_elements[2].to_lower()] == language) {
+ variant = locale_elements[2].to_lower();
}
}
if (locale_elements.size() >= 4) {
- if (variant_map.has(locale_elements[3].to_lower()) && variant_map[locale_elements[3].to_lower()] == lang_name) {
- variant_name = locale_elements[3].to_lower();
+ if (p_server.variant_map.has(locale_elements[3].to_lower()) && p_server.variant_map[locale_elements[3].to_lower()] == language) {
+ variant = locale_elements[3].to_lower();
}
}
@@ -157,71 +166,62 @@ String TranslationServer::_standardize_locale(const String &p_locale, bool p_add
Vector<String> script_extra = univ_locale.get_slice("@", 1).split(";");
for (int i = 0; i < script_extra.size(); i++) {
if (script_extra[i].to_lower() == "cyrillic") {
- script_name = "Cyrl";
+ script = "Cyrl";
break;
} else if (script_extra[i].to_lower() == "latin") {
- script_name = "Latn";
+ script = "Latn";
break;
} else if (script_extra[i].to_lower() == "devanagari") {
- script_name = "Deva";
+ script = "Deva";
break;
- } else if (variant_map.has(script_extra[i].to_lower()) && variant_map[script_extra[i].to_lower()] == lang_name) {
- variant_name = script_extra[i].to_lower();
+ } else if (p_server.variant_map.has(script_extra[i].to_lower()) && p_server.variant_map[script_extra[i].to_lower()] == language) {
+ variant = script_extra[i].to_lower();
}
}
// Handles known non-ISO language names used e.g. on Windows.
- if (locale_rename_map.has(lang_name)) {
- lang_name = locale_rename_map[lang_name];
+ if (p_server.locale_rename_map.has(language)) {
+ language = p_server.locale_rename_map[language];
}
// Handle country renames.
- if (country_rename_map.has(country_name)) {
- country_name = country_rename_map[country_name];
+ if (p_server.country_rename_map.has(country)) {
+ country = p_server.country_rename_map[country];
}
// Remove unsupported script codes.
- if (!script_map.has(script_name)) {
- script_name = "";
+ if (!p_server.script_map.has(script)) {
+ script = "";
}
// Add script code base on language and country codes for some ambiguous cases.
if (p_add_defaults) {
- if (script_name.is_empty()) {
- for (int i = 0; i < locale_script_info.size(); i++) {
- const LocaleScriptInfo &info = locale_script_info[i];
- if (info.name == lang_name) {
- if (country_name.is_empty() || info.supported_countries.has(country_name)) {
- script_name = info.script;
+ if (script.is_empty()) {
+ for (int i = 0; i < p_server.locale_script_info.size(); i++) {
+ const LocaleScriptInfo &info = p_server.locale_script_info[i];
+ if (info.name == language) {
+ if (country.is_empty() || info.supported_countries.has(country)) {
+ script = info.script;
break;
}
}
}
}
- if (!script_name.is_empty() && country_name.is_empty()) {
+ if (!script.is_empty() && country.is_empty()) {
// Add conntry code based on script for some ambiguous cases.
- for (int i = 0; i < locale_script_info.size(); i++) {
- const LocaleScriptInfo &info = locale_script_info[i];
- if (info.name == lang_name && info.script == script_name) {
- country_name = info.default_country;
+ for (int i = 0; i < p_server.locale_script_info.size(); i++) {
+ const LocaleScriptInfo &info = p_server.locale_script_info[i];
+ if (info.name == language && info.script == script) {
+ country = info.default_country;
break;
}
}
}
}
+}
- // Combine results.
- String out = lang_name;
- if (!script_name.is_empty()) {
- out = out + "_" + script_name;
- }
- if (!country_name.is_empty()) {
- out = out + "_" + country_name;
- }
- if (!variant_name.is_empty()) {
- out = out + "_" + variant_name;
- }
- return out;
+String TranslationServer::standardize_locale(const String &p_locale) const {
+ return Locale(*this, p_locale, false).operator String();
}
int TranslationServer::compare_locales(const String &p_locale_a, const String &p_locale_b) const {
@@ -236,8 +236,8 @@ int TranslationServer::compare_locales(const String &p_locale_a, const String &p
return *cached_result;
}
- String locale_a = _standardize_locale(p_locale_a, true);
- String locale_b = _standardize_locale(p_locale_b, true);
+ Locale locale_a = Locale(*this, p_locale_a, true);
+ Locale locale_b = Locale(*this, p_locale_b, true);
if (locale_a == locale_b) {
// Exact match.
@@ -245,26 +245,41 @@ int TranslationServer::compare_locales(const String &p_locale_a, const String &p
return 10;
}
- Vector<String> locale_a_elements = locale_a.split("_");
- Vector<String> locale_b_elements = locale_b.split("_");
- if (locale_a_elements[0] != locale_b_elements[0]) {
+ if (locale_a.language != locale_b.language) {
// No match.
locale_compare_cache.insert(cache_key, 0);
return 0;
}
- // Matching language, both locales have extra parts.
- // Return number of matching elements.
- int matching_elements = 1;
- for (int i = 1; i < locale_a_elements.size(); i++) {
- for (int j = 1; j < locale_b_elements.size(); j++) {
- if (locale_a_elements[i] == locale_b_elements[j]) {
- matching_elements++;
- }
+ // Matching language, both locales have extra parts. Compare the
+ // remaining elements. If both elements are non-empty, check the
+ // match to increase or decrease the score. If either element or
+ // both are empty, leave the score as is.
+ int score = 5;
+ if (!locale_a.script.is_empty() && !locale_b.script.is_empty()) {
+ if (locale_a.script == locale_b.script) {
+ score++;
+ } else {
+ score--;
}
}
- locale_compare_cache.insert(cache_key, matching_elements);
- return matching_elements;
+ if (!locale_a.country.is_empty() && !locale_b.country.is_empty()) {
+ if (locale_a.country == locale_b.country) {
+ score++;
+ } else {
+ score--;
+ }
+ }
+ if (!locale_a.variant.is_empty() && !locale_b.variant.is_empty()) {
+ if (locale_a.variant == locale_b.variant) {
+ score++;
+ } else {
+ score--;
+ }
+ }
+
+ locale_compare_cache.insert(cache_key, score);
+ return score;
}
String TranslationServer::get_locale_name(const String &p_locale) const {
@@ -398,8 +413,6 @@ StringName TranslationServer::translate_plural(const StringName &p_message, cons
return main_domain->translate_plural(p_message, p_message_plural, p_n, p_context);
}
-TranslationServer *TranslationServer::singleton = nullptr;
-
bool TranslationServer::_load_translations(const String &p_from) {
if (ProjectSettings::get_singleton()->has_setting(p_from)) {
const Vector<String> &translation_names = GLOBAL_GET(p_from);
diff --git a/core/string/translation_server.h b/core/string/translation_server.h
index e807cf9aa0..01974a4025 100644
--- a/core/string/translation_server.h
+++ b/core/string/translation_server.h
@@ -52,7 +52,7 @@ class TranslationServer : public Object {
bool enabled = true;
- static TranslationServer *singleton;
+ static inline TranslationServer *singleton = nullptr;
bool _load_translations(const String &p_from);
String _standardize_locale(const String &p_locale, bool p_add_defaults) const;
@@ -66,6 +66,24 @@ class TranslationServer : public Object {
};
static Vector<LocaleScriptInfo> locale_script_info;
+ struct Locale {
+ String language;
+ String script;
+ String country;
+ String variant;
+
+ bool operator==(const Locale &p_locale) const {
+ return (p_locale.language == language) &&
+ (p_locale.script == script) &&
+ (p_locale.country == country) &&
+ (p_locale.variant == variant);
+ }
+
+ operator String() const;
+
+ Locale(const TranslationServer &p_server, const String &p_locale, bool p_add_defaults);
+ };
+
static HashMap<String, String> language_map;
static HashMap<String, String> script_map;
static HashMap<String, String> locale_rename_map;
diff --git a/core/templates/hashfuncs.h b/core/templates/hashfuncs.h
index 1406ec327b..1f37035afe 100644
--- a/core/templates/hashfuncs.h
+++ b/core/templates/hashfuncs.h
@@ -112,6 +112,16 @@ static _FORCE_INLINE_ uint32_t hash_one_uint64(const uint64_t p_int) {
return uint32_t(v);
}
+static _FORCE_INLINE_ uint64_t hash64_murmur3_64(uint64_t key, uint64_t seed) {
+ key ^= seed;
+ key ^= key >> 33;
+ key *= 0xff51afd7ed558ccd;
+ key ^= key >> 33;
+ key *= 0xc4ceb9fe1a85ec53;
+ key ^= key >> 33;
+ return key;
+}
+
#define HASH_MURMUR3_SEED 0x7F07C65
// Murmurhash3 32-bit version.
// All MurmurHash versions are public domain software, and the author disclaims all copyright to their code.
diff --git a/core/variant/callable.cpp b/core/variant/callable.cpp
index dcc995bf1a..d124606082 100644
--- a/core/variant/callable.cpp
+++ b/core/variant/callable.cpp
@@ -208,19 +208,17 @@ int Callable::get_bound_arguments_count() const {
}
}
-void Callable::get_bound_arguments_ref(Vector<Variant> &r_arguments, int &r_argcount) const {
+void Callable::get_bound_arguments_ref(Vector<Variant> &r_arguments) const {
if (!is_null() && is_custom()) {
- custom->get_bound_arguments(r_arguments, r_argcount);
+ custom->get_bound_arguments(r_arguments);
} else {
r_arguments.clear();
- r_argcount = 0;
}
}
Array Callable::get_bound_arguments() const {
Vector<Variant> arr;
- int ac;
- get_bound_arguments_ref(arr, ac);
+ get_bound_arguments_ref(arr);
Array ret;
ret.resize(arr.size());
for (int i = 0; i < arr.size(); i++) {
@@ -229,6 +227,14 @@ Array Callable::get_bound_arguments() const {
return ret;
}
+int Callable::get_unbound_arguments_count() const {
+ if (!is_null() && is_custom()) {
+ return custom->get_unbound_arguments_count();
+ } else {
+ return 0;
+ }
+}
+
CallableCustom *Callable::get_custom() const {
ERR_FAIL_COND_V_MSG(!is_custom(), nullptr,
vformat("Can't get custom on non-CallableCustom \"%s\".", operator String()));
@@ -466,9 +472,12 @@ int CallableCustom::get_bound_arguments_count() const {
return 0;
}
-void CallableCustom::get_bound_arguments(Vector<Variant> &r_arguments, int &r_argcount) const {
- r_arguments = Vector<Variant>();
- r_argcount = 0;
+void CallableCustom::get_bound_arguments(Vector<Variant> &r_arguments) const {
+ r_arguments.clear();
+}
+
+int CallableCustom::get_unbound_arguments_count() const {
+ return 0;
}
CallableCustom::CallableCustom() {
diff --git a/core/variant/callable.h b/core/variant/callable.h
index 0dcf38790e..8821294ac1 100644
--- a/core/variant/callable.h
+++ b/core/variant/callable.h
@@ -113,8 +113,9 @@ public:
CallableCustom *get_custom() const;
int get_argument_count(bool *r_is_valid = nullptr) const;
int get_bound_arguments_count() const;
- void get_bound_arguments_ref(Vector<Variant> &r_arguments, int &r_argcount) const; // Internal engine use, the exposed one is below.
+ void get_bound_arguments_ref(Vector<Variant> &r_arguments) const; // Internal engine use, the exposed one is below.
Array get_bound_arguments() const;
+ int get_unbound_arguments_count() const;
uint32_t hash() const;
@@ -160,7 +161,8 @@ public:
virtual const Callable *get_base_comparator() const;
virtual int get_argument_count(bool &r_is_valid) const;
virtual int get_bound_arguments_count() const;
- virtual void get_bound_arguments(Vector<Variant> &r_arguments, int &r_argcount) const;
+ virtual void get_bound_arguments(Vector<Variant> &r_arguments) const;
+ virtual int get_unbound_arguments_count() const;
CallableCustom();
virtual ~CallableCustom() {}
diff --git a/core/variant/callable_bind.cpp b/core/variant/callable_bind.cpp
index 2b89636b69..0245e48533 100644
--- a/core/variant/callable_bind.cpp
+++ b/core/variant/callable_bind.cpp
@@ -45,7 +45,7 @@ bool CallableCustomBind::_equal_func(const CallableCustom *p_a, const CallableCu
const CallableCustomBind *a = static_cast<const CallableCustomBind *>(p_a);
const CallableCustomBind *b = static_cast<const CallableCustomBind *>(p_b);
- if (!(a->callable != b->callable)) {
+ if (a->callable != b->callable) {
return false;
}
@@ -102,44 +102,42 @@ int CallableCustomBind::get_argument_count(bool &r_is_valid) const {
}
int CallableCustomBind::get_bound_arguments_count() const {
- return callable.get_bound_arguments_count() + binds.size();
+ return callable.get_bound_arguments_count() + MAX(0, binds.size() - callable.get_unbound_arguments_count());
}
-void CallableCustomBind::get_bound_arguments(Vector<Variant> &r_arguments, int &r_argcount) const {
- Vector<Variant> sub_args;
- int sub_count;
- callable.get_bound_arguments_ref(sub_args, sub_count);
+void CallableCustomBind::get_bound_arguments(Vector<Variant> &r_arguments) const {
+ Vector<Variant> sub_bound_args;
+ callable.get_bound_arguments_ref(sub_bound_args);
+ int sub_bound_count = sub_bound_args.size();
- if (sub_count == 0) {
+ int sub_unbound_count = callable.get_unbound_arguments_count();
+
+ if (sub_bound_count == 0 && sub_unbound_count == 0) {
r_arguments = binds;
- r_argcount = binds.size();
return;
}
- int new_count = sub_count + binds.size();
- r_argcount = new_count;
+ int added_count = MAX(0, binds.size() - sub_unbound_count);
+ int new_count = sub_bound_count + added_count;
- if (new_count <= 0) {
- // Removed more arguments than it adds.
- r_arguments = Vector<Variant>();
+ if (added_count <= 0) {
+ // All added arguments are consumed by `sub_unbound_count`.
+ r_arguments = sub_bound_args;
return;
}
r_arguments.resize(new_count);
-
- if (sub_count > 0) {
- for (int i = 0; i < sub_count; i++) {
- r_arguments.write[i] = sub_args[i];
- }
- for (int i = 0; i < binds.size(); i++) {
- r_arguments.write[i + sub_count] = binds[i];
- }
- r_argcount = new_count;
- } else {
- for (int i = 0; i < binds.size() + sub_count; i++) {
- r_arguments.write[i] = binds[i - sub_count];
- }
+ Variant *args = r_arguments.ptrw();
+ for (int i = 0; i < added_count; i++) {
+ args[i] = binds[i];
}
+ for (int i = 0; i < sub_bound_count; i++) {
+ args[i + added_count] = sub_bound_args[i];
+ }
+}
+
+int CallableCustomBind::get_unbound_arguments_count() const {
+ return MAX(0, callable.get_unbound_arguments_count() - binds.size());
}
void CallableCustomBind::call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const {
@@ -187,7 +185,7 @@ bool CallableCustomUnbind::_equal_func(const CallableCustom *p_a, const Callable
const CallableCustomUnbind *a = static_cast<const CallableCustomUnbind *>(p_a);
const CallableCustomUnbind *b = static_cast<const CallableCustomUnbind *>(p_b);
- if (!(a->callable != b->callable)) {
+ if (a->callable != b->callable) {
return false;
}
@@ -244,22 +242,15 @@ int CallableCustomUnbind::get_argument_count(bool &r_is_valid) const {
}
int CallableCustomUnbind::get_bound_arguments_count() const {
- return callable.get_bound_arguments_count() - argcount;
+ return callable.get_bound_arguments_count();
}
-void CallableCustomUnbind::get_bound_arguments(Vector<Variant> &r_arguments, int &r_argcount) const {
- Vector<Variant> sub_args;
- int sub_count;
- callable.get_bound_arguments_ref(sub_args, sub_count);
-
- r_argcount = sub_args.size() - argcount;
+void CallableCustomUnbind::get_bound_arguments(Vector<Variant> &r_arguments) const {
+ callable.get_bound_arguments_ref(r_arguments);
+}
- if (argcount >= sub_args.size()) {
- r_arguments = Vector<Variant>();
- } else {
- sub_args.resize(sub_args.size() - argcount);
- r_arguments = sub_args;
- }
+int CallableCustomUnbind::get_unbound_arguments_count() const {
+ return callable.get_unbound_arguments_count() + argcount;
}
void CallableCustomUnbind::call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const {
diff --git a/core/variant/callable_bind.h b/core/variant/callable_bind.h
index 3a5e9bfabd..be98509ca1 100644
--- a/core/variant/callable_bind.h
+++ b/core/variant/callable_bind.h
@@ -57,7 +57,8 @@ public:
virtual const Callable *get_base_comparator() const override;
virtual int get_argument_count(bool &r_is_valid) const override;
virtual int get_bound_arguments_count() const override;
- virtual void get_bound_arguments(Vector<Variant> &r_arguments, int &r_argcount) const override;
+ virtual void get_bound_arguments(Vector<Variant> &r_arguments) const override;
+ virtual int get_unbound_arguments_count() const override;
Callable get_callable() { return callable; }
Vector<Variant> get_binds() { return binds; }
@@ -86,7 +87,8 @@ public:
virtual const Callable *get_base_comparator() const override;
virtual int get_argument_count(bool &r_is_valid) const override;
virtual int get_bound_arguments_count() const override;
- virtual void get_bound_arguments(Vector<Variant> &r_arguments, int &r_argcount) const override;
+ virtual void get_bound_arguments(Vector<Variant> &r_arguments) const override;
+ virtual int get_unbound_arguments_count() const override;
Callable get_callable() { return callable; }
int get_unbinds() { return argcount; }
diff --git a/core/variant/variant.cpp b/core/variant/variant.cpp
index 257b290e46..90cb722fd4 100644
--- a/core/variant/variant.cpp
+++ b/core/variant/variant.cpp
@@ -953,7 +953,7 @@ bool Variant::is_zero() const {
return *reinterpret_cast<const ::RID *>(_data._mem) == ::RID();
}
case OBJECT: {
- return _get_obj().obj == nullptr;
+ return get_validated_object() == nullptr;
}
case CALLABLE: {
return reinterpret_cast<const Callable *>(_data._mem)->is_null();
@@ -3666,18 +3666,20 @@ String Variant::get_call_error_text(Object *p_base, const StringName &p_method,
String Variant::get_callable_error_text(const Callable &p_callable, const Variant **p_argptrs, int p_argcount, const Callable::CallError &ce) {
Vector<Variant> binds;
- int args_bound;
- p_callable.get_bound_arguments_ref(binds, args_bound);
- if (args_bound <= 0) {
- return get_call_error_text(p_callable.get_object(), p_callable.get_method(), p_argptrs, MAX(0, p_argcount + args_bound), ce);
+ p_callable.get_bound_arguments_ref(binds);
+
+ int args_unbound = p_callable.get_unbound_arguments_count();
+
+ if (p_argcount - args_unbound < 0) {
+ return "Callable unbinds " + itos(args_unbound) + " arguments, but called with " + itos(p_argcount);
} else {
Vector<const Variant *> argptrs;
- argptrs.resize(p_argcount + binds.size());
- for (int i = 0; i < p_argcount; i++) {
+ argptrs.resize(p_argcount - args_unbound + binds.size());
+ for (int i = 0; i < p_argcount - args_unbound; i++) {
argptrs.write[i] = p_argptrs[i];
}
for (int i = 0; i < binds.size(); i++) {
- argptrs.write[i + p_argcount] = &binds[i];
+ argptrs.write[i + p_argcount - args_unbound] = &binds[i];
}
return get_call_error_text(p_callable.get_object(), p_callable.get_method(), (const Variant **)argptrs.ptr(), argptrs.size(), ce);
}
diff --git a/core/variant/variant_call.cpp b/core/variant/variant_call.cpp
index 9b599011a6..95c8550779 100644
--- a/core/variant/variant_call.cpp
+++ b/core/variant/variant_call.cpp
@@ -2118,6 +2118,7 @@ static void _register_variant_builtin_methods_misc() {
bind_function(Callable, get_argument_count, _VariantCall::func_Callable_get_argument_count, sarray(), varray());
bind_method(Callable, get_bound_arguments_count, sarray(), varray());
bind_method(Callable, get_bound_arguments, sarray(), varray());
+ bind_method(Callable, get_unbound_arguments_count, sarray(), varray());
bind_method(Callable, hash, sarray(), varray());
bind_method(Callable, bindv, sarray("arguments"), varray());
bind_method(Callable, unbind, sarray("argcount"), varray());
diff --git a/core/variant/variant_setget.cpp b/core/variant/variant_setget.cpp
index 348d2366e7..aa2e36b7f0 100644
--- a/core/variant/variant_setget.cpp
+++ b/core/variant/variant_setget.cpp
@@ -143,6 +143,10 @@ void register_named_setters_getters() {
REGISTER_MEMBER(Color, h);
REGISTER_MEMBER(Color, s);
REGISTER_MEMBER(Color, v);
+
+ REGISTER_MEMBER(Color, ok_hsl_h);
+ REGISTER_MEMBER(Color, ok_hsl_s);
+ REGISTER_MEMBER(Color, ok_hsl_l);
}
void unregister_named_setters_getters() {