diff options
author | George L. Albany <Megacake1234@gmail.com> | 2024-11-12 20:29:24 +0000 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-11-12 20:29:24 +0000 |
commit | ac1a49725fc038ae11ef9060fecb2b0f9c6333b2 (patch) | |
tree | c7341bd56c977259578b127886c9a88eeef11820 /core | |
parent | 5094c2a5f7d506b0e685120f14d1df42e1e9d495 (diff) | |
parent | 3a73c6ebd18bff0fa125be58d3ac9c7a63bab61d (diff) | |
download | redot-engine-ac1a49725fc038ae11ef9060fecb2b0f9c6333b2.tar.gz |
Merge pull request #855 from Spartan322/merge/cb411fa
Merge commit godotengine/godot@cb411fa
Diffstat (limited to 'core')
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() { |