diff options
Diffstat (limited to 'modules')
38 files changed, 581 insertions, 1440 deletions
diff --git a/modules/etc/SCsub b/modules/etc/SCsub index 8f5937017e..9c3e703f11 100644 --- a/modules/etc/SCsub +++ b/modules/etc/SCsub @@ -34,4 +34,8 @@ env_etc.Append(CPPPATH=[thirdparty_dir]) env_etc.add_source_files(env.modules_sources, "*.cpp") # upstream uses c++11 -env_etc.Append(CXXFLAGS="-std=gnu++11") +env_etc.Append(CCFLAGS="-std=gnu++11") +# -ffast-math seems to be incompatible with ec2comp on recent versions of +# GCC and Clang +if '-ffast-math' in env_etc['CCFLAGS']: + env_etc['CCFLAGS'].remove('-ffast-math') diff --git a/modules/gdnative/config.py b/modules/gdnative/config.py index 4f89ca0d4c..9f57b9bb74 100644 --- a/modules/gdnative/config.py +++ b/modules/gdnative/config.py @@ -1,7 +1,7 @@ def can_build(platform): - return False + return True def configure(env): diff --git a/modules/gdnative/gdnative.cpp b/modules/gdnative/gdnative.cpp index 7faf21c5a1..ded987557c 100644 --- a/modules/gdnative/gdnative.cpp +++ b/modules/gdnative/gdnative.cpp @@ -40,7 +40,7 @@ const String init_symbol = "godot_gdnative_init"; const String terminate_symbol = "godot_gdnative_terminate"; -String GDNativeLibrary::platform_names[NUM_PLATFORMS] = { +String GDNativeLibrary::platform_names[NUM_PLATFORMS + 1] = { "X11_32bit", "X11_64bit", "Windows_32bit", @@ -48,11 +48,15 @@ String GDNativeLibrary::platform_names[NUM_PLATFORMS] = { "OSX", "Android", - "iOS", - "WebAssembly" + "iOS_32bit", + "iOS_64bit", + + "WebAssembly", + + "" }; -String GDNativeLibrary::platform_lib_ext[NUM_PLATFORMS] = { +String GDNativeLibrary::platform_lib_ext[NUM_PLATFORMS + 1] = { "so", "so", "dll", @@ -60,21 +64,30 @@ String GDNativeLibrary::platform_lib_ext[NUM_PLATFORMS] = { "dylib", "so", + "dylib", + "dylib", + + "wasm", - "wasm" + "" }; -// TODO(karroffel): make this actually do the right thing. GDNativeLibrary::Platform GDNativeLibrary::current_platform = #if defined(X11_ENABLED) - X11_64BIT; + (sizeof(void *) == 8 ? X11_64BIT : X11_32BIT); #elif defined(WINDOWS_ENABLED) - WINDOWS_64BIT; + (sizeof(void *) == 8 ? WINDOWS_64BIT : WINDOWS_32BIT); #elif defined(OSX_ENABLED) OSX; +#elif defined(IPHONE_ENABLED) + (sizeof(void *) == 8 ? IOS_64BIT : IOS_32BIT); +#elif defined(ANDROID_ENABLED) + ANDROID; +#elif defined(JAVASCRIPT_ENABLED) + WASM; #else - X11_64BIT; // need a sensible default.. + NUM_PLATFORMS; #endif GDNativeLibrary::GDNativeLibrary() @@ -151,7 +164,10 @@ String GDNativeLibrary::get_library_path(StringName p_platform) const { } String GDNativeLibrary::get_active_library_path() const { - return library_paths[GDNativeLibrary::current_platform]; + if (GDNativeLibrary::current_platform != NUM_PLATFORMS) { + return library_paths[GDNativeLibrary::current_platform]; + } + return ""; } GDNative::GDNative() { diff --git a/modules/gdnative/gdnative.h b/modules/gdnative/gdnative.h index bec746a441..dc1c3507ec 100644 --- a/modules/gdnative/gdnative.h +++ b/modules/gdnative/gdnative.h @@ -48,11 +48,17 @@ class GDNativeLibrary : public Resource { // NOTE(karroffel): I heard OSX 32 bit is dead, so 64 only OSX, - // TODO(karroffel): all different android versions and archs + // Android .so files must be located in directories corresponding to Android ABI names: + // https://developer.android.com/ndk/guides/abis.html + // Android runtime will select the matching library depending on the device. + // The value here must simply point to the .so name, for example: + // "res://libmy_gdnative.so" or "libmy_gdnative.so", + // while in the project the actual paths can be "lib/android/armeabi-v7a/libmy_gdnative.so", + // "lib/android/arm64-v8a/libmy_gdnative.so". ANDROID, - // TODO(karroffe): all different iOS versions and archs - IOS, + IOS_32BIT, + IOS_64BIT, // TODO(karroffel): figure out how to deal with web stuff at all... WASM, @@ -64,10 +70,9 @@ class GDNativeLibrary : public Resource { }; - static String platform_names[NUM_PLATFORMS]; - static String platform_lib_ext[NUM_PLATFORMS]; + static String platform_names[NUM_PLATFORMS + 1]; + static String platform_lib_ext[NUM_PLATFORMS + 1]; - // TODO(karroffel): make this actually do something lol. static Platform current_platform; String library_paths[NUM_PLATFORMS]; diff --git a/modules/gdnative/godot/array.h b/modules/gdnative/godot/array.h index 434ce958c9..08f73c8785 100644 --- a/modules/gdnative/godot/array.h +++ b/modules/gdnative/godot/array.h @@ -37,7 +37,7 @@ extern "C" { #include <stdint.h> -#define GODOT_ARRAY_SIZE 8 +#define GODOT_ARRAY_SIZE sizeof(void *) #ifndef GODOT_CORE_API_GODOT_ARRAY_TYPE_DEFINED #define GODOT_CORE_API_GODOT_ARRAY_TYPE_DEFINED diff --git a/modules/gdnative/godot/basis.cpp b/modules/gdnative/godot/basis.cpp index 8433355c12..1d7aa18a70 100644 --- a/modules/gdnative/godot/basis.cpp +++ b/modules/gdnative/godot/basis.cpp @@ -107,24 +107,6 @@ godot_basis GDAPI godot_basis_scaled(const godot_basis *p_self, const godot_vect return dest; } -void GDAPI godot_basis_set_scale(godot_basis *p_self, const godot_vector3 *p_scale) { - Basis *self = (Basis *)p_self; - const Vector3 *scale = (const Vector3 *)p_scale; - self->set_scale(*scale); -} - -void GDAPI godot_basis_set_rotation_euler(godot_basis *p_self, const godot_vector3 *p_euler) { - Basis *self = (Basis *)p_self; - const Vector3 *euler = (const Vector3 *)p_euler; - self->set_rotation_euler(*euler); -} - -void GDAPI godot_basis_set_rotation_axis_angle(godot_basis *p_self, const godot_vector3 *p_axis, const godot_real p_angle) { - Basis *self = (Basis *)p_self; - const Vector3 *axis = (const Vector3 *)p_axis; - self->set_rotation_axis_angle(*axis, p_angle); -} - godot_vector3 GDAPI godot_basis_get_scale(const godot_basis *p_self) { godot_vector3 dest; const Basis *self = (const Basis *)p_self; diff --git a/modules/gdnative/godot/basis.h b/modules/gdnative/godot/basis.h index d336bb9bc1..f36d2199de 100644 --- a/modules/gdnative/godot/basis.h +++ b/modules/gdnative/godot/basis.h @@ -67,12 +67,6 @@ godot_basis GDAPI godot_basis_rotated(const godot_basis *p_self, const godot_vec godot_basis GDAPI godot_basis_scaled(const godot_basis *p_self, const godot_vector3 *p_scale); -void GDAPI godot_basis_set_scale(godot_basis *p_self, const godot_vector3 *p_scale); - -void GDAPI godot_basis_set_rotation_euler(godot_basis *p_self, const godot_vector3 *p_euler); - -void GDAPI godot_basis_set_rotation_axis_angle(godot_basis *p_self, const godot_vector3 *p_axis, const godot_real p_angle); - godot_vector3 GDAPI godot_basis_get_scale(const godot_basis *p_self); godot_vector3 GDAPI godot_basis_get_euler(const godot_basis *p_self); diff --git a/modules/gdnative/godot/dictionary.h b/modules/gdnative/godot/dictionary.h index bbe40f23c3..10d580af08 100644 --- a/modules/gdnative/godot/dictionary.h +++ b/modules/gdnative/godot/dictionary.h @@ -36,7 +36,7 @@ extern "C" { #include <stdint.h> -#define GODOT_DICTIONARY_SIZE 8 +#define GODOT_DICTIONARY_SIZE sizeof(void *) #ifndef GODOT_CORE_API_GODOT_DICTIONARY_TYPE_DEFINED #define GODOT_CORE_API_GODOT_DICTIONARY_TYPE_DEFINED diff --git a/modules/gdnative/godot/gdnative.h b/modules/gdnative/godot/gdnative.h index 8b289da1f5..c71a7ae1ef 100644 --- a/modules/gdnative/godot/gdnative.h +++ b/modules/gdnative/godot/gdnative.h @@ -56,7 +56,7 @@ extern "C" { #define GDAPI GDCALLINGCONV #endif #else -#define GDCALLINGCONV __attribute__((sysv_abi)) +#define GDCALLINGCONV __attribute__((sysv_abi, visibility("default"))) #define GDAPI GDCALLINGCONV #endif diff --git a/modules/gdnative/godot/node_path.h b/modules/gdnative/godot/node_path.h index 3e2a99e461..2f71ddd59d 100644 --- a/modules/gdnative/godot/node_path.h +++ b/modules/gdnative/godot/node_path.h @@ -36,7 +36,7 @@ extern "C" { #include <stdint.h> -#define GODOT_NODE_PATH_SIZE 8 +#define GODOT_NODE_PATH_SIZE sizeof(void *) #ifndef GODOT_CORE_API_GODOT_NODE_PATH_TYPE_DEFINED #define GODOT_CORE_API_GODOT_NODE_PATH_TYPE_DEFINED diff --git a/modules/gdnative/godot/pool_arrays.h b/modules/gdnative/godot/pool_arrays.h index ecd85ddfe8..1b51dca38c 100644 --- a/modules/gdnative/godot/pool_arrays.h +++ b/modules/gdnative/godot/pool_arrays.h @@ -38,7 +38,7 @@ extern "C" { /////// PoolByteArray -#define GODOT_POOL_BYTE_ARRAY_SIZE 8 +#define GODOT_POOL_BYTE_ARRAY_SIZE sizeof(void *) #ifndef GODOT_CORE_API_GODOT_POOL_BYTE_ARRAY_TYPE_DEFINED #define GODOT_CORE_API_GODOT_POOL_BYTE_ARRAY_TYPE_DEFINED @@ -49,7 +49,7 @@ typedef struct { /////// PoolIntArray -#define GODOT_POOL_INT_ARRAY_SIZE 8 +#define GODOT_POOL_INT_ARRAY_SIZE sizeof(void *) #ifndef GODOT_CORE_API_GODOT_POOL_INT_ARRAY_TYPE_DEFINED #define GODOT_CORE_API_GODOT_POOL_INT_ARRAY_TYPE_DEFINED @@ -60,7 +60,7 @@ typedef struct { /////// PoolRealArray -#define GODOT_POOL_REAL_ARRAY_SIZE 8 +#define GODOT_POOL_REAL_ARRAY_SIZE sizeof(void *) #ifndef GODOT_CORE_API_GODOT_POOL_REAL_ARRAY_TYPE_DEFINED #define GODOT_CORE_API_GODOT_POOL_REAL_ARRAY_TYPE_DEFINED @@ -71,7 +71,7 @@ typedef struct { /////// PoolStringArray -#define GODOT_POOL_STRING_ARRAY_SIZE 8 +#define GODOT_POOL_STRING_ARRAY_SIZE sizeof(void *) #ifndef GODOT_CORE_API_GODOT_POOL_STRING_ARRAY_TYPE_DEFINED #define GODOT_CORE_API_GODOT_POOL_STRING_ARRAY_TYPE_DEFINED @@ -82,7 +82,7 @@ typedef struct { /////// PoolVector2Array -#define GODOT_POOL_VECTOR2_ARRAY_SIZE 8 +#define GODOT_POOL_VECTOR2_ARRAY_SIZE sizeof(void *) #ifndef GODOT_CORE_API_GODOT_POOL_VECTOR2_ARRAY_TYPE_DEFINED #define GODOT_CORE_API_GODOT_POOL_VECTOR2_ARRAY_TYPE_DEFINED @@ -93,7 +93,7 @@ typedef struct { /////// PoolVector3Array -#define GODOT_POOL_VECTOR3_ARRAY_SIZE 8 +#define GODOT_POOL_VECTOR3_ARRAY_SIZE sizeof(void *) #ifndef GODOT_CORE_API_GODOT_POOL_VECTOR3_ARRAY_TYPE_DEFINED #define GODOT_CORE_API_GODOT_POOL_VECTOR3_ARRAY_TYPE_DEFINED @@ -104,7 +104,7 @@ typedef struct { /////// PoolColorArray -#define GODOT_POOL_COLOR_ARRAY_SIZE 8 +#define GODOT_POOL_COLOR_ARRAY_SIZE sizeof(void *) #ifndef GODOT_CORE_API_GODOT_POOL_COLOR_ARRAY_TYPE_DEFINED #define GODOT_CORE_API_GODOT_POOL_COLOR_ARRAY_TYPE_DEFINED diff --git a/modules/gdnative/godot/rid.h b/modules/gdnative/godot/rid.h index b685157cec..c56ff38735 100644 --- a/modules/gdnative/godot/rid.h +++ b/modules/gdnative/godot/rid.h @@ -36,7 +36,7 @@ extern "C" { #include <stdint.h> -#define GODOT_RID_SIZE 8 +#define GODOT_RID_SIZE sizeof(void *) #ifndef GODOT_CORE_API_GODOT_RID_TYPE_DEFINED #define GODOT_CORE_API_GODOT_RID_TYPE_DEFINED diff --git a/modules/gdnative/godot/string.cpp b/modules/gdnative/godot/string.cpp index 3790b6ea95..1282cf95e5 100644 --- a/modules/gdnative/godot/string.cpp +++ b/modules/gdnative/godot/string.cpp @@ -232,7 +232,7 @@ godot_int GDAPI godot_string_findn_from(const godot_string *p_self, godot_string return self->findn(*what, p_from); } -godot_int GDAPI find_last(const godot_string *p_self, godot_string p_what) { +godot_int GDAPI godot_string_find_last(const godot_string *p_self, godot_string p_what) { const String *self = (const String *)p_self; String *what = (String *)&p_what; diff --git a/modules/gdnative/godot/string.h b/modules/gdnative/godot/string.h index f41626faa1..128448e64e 100644 --- a/modules/gdnative/godot/string.h +++ b/modules/gdnative/godot/string.h @@ -37,7 +37,7 @@ extern "C" { #include <stdint.h> #include <wchar.h> -#define GODOT_STRING_SIZE 8 +#define GODOT_STRING_SIZE sizeof(void *) #ifndef GODOT_CORE_API_GODOT_STRING_TYPE_DEFINED #define GODOT_CORE_API_GODOT_STRING_TYPE_DEFINED @@ -82,7 +82,7 @@ godot_int GDAPI godot_string_findmk_from(const godot_string *p_self, const godot godot_int GDAPI godot_string_findmk_from_in_place(const godot_string *p_self, const godot_array *p_keys, godot_int p_from, godot_int *r_key); godot_int GDAPI godot_string_findn(const godot_string *p_self, godot_string p_what); godot_int GDAPI godot_string_findn_from(const godot_string *p_self, godot_string p_what, godot_int p_from); -godot_int GDAPI find_last(const godot_string *p_self, godot_string p_what); +godot_int GDAPI godot_string_find_last(const godot_string *p_self, godot_string p_what); godot_string GDAPI godot_string_format(const godot_string *p_self, const godot_variant *p_values); godot_string GDAPI godot_string_format_with_custom_placeholder(const godot_string *p_self, const godot_variant *p_values, const char *p_placeholder); godot_string GDAPI godot_string_hex_encode_buffer(const uint8_t *p_buffer, godot_int p_len); diff --git a/modules/gdnative/godot/variant.h b/modules/gdnative/godot/variant.h index fda24db8d4..849ba8bda6 100644 --- a/modules/gdnative/godot/variant.h +++ b/modules/gdnative/godot/variant.h @@ -36,7 +36,7 @@ extern "C" { #include <stdint.h> -#define GODOT_VARIANT_SIZE 24 +#define GODOT_VARIANT_SIZE (16 + sizeof(void *)) #ifndef GODOT_CORE_API_GODOT_VARIANT_TYPE_DEFINED #define GODOT_CORE_API_GODOT_VARIANT_TYPE_DEFINED diff --git a/modules/gdscript/gd_editor.cpp b/modules/gdscript/gd_editor.cpp index b10694ddfd..70e7da5748 100644 --- a/modules/gdscript/gd_editor.cpp +++ b/modules/gdscript/gd_editor.cpp @@ -297,23 +297,25 @@ void GDScriptLanguage::get_public_functions(List<MethodInfo> *p_functions) const //not really "functions", but.. { MethodInfo mi; - mi.name = "preload:Resource"; + mi.name = "preload"; mi.arguments.push_back(PropertyInfo(Variant::STRING, "path")); mi.return_val = PropertyInfo(Variant::OBJECT, "", PROPERTY_HINT_RESOURCE_TYPE, "Resource"); p_functions->push_back(mi); } { MethodInfo mi; - mi.name = "yield:GDFunctionState"; + mi.name = "yield"; mi.arguments.push_back(PropertyInfo(Variant::OBJECT, "object")); mi.arguments.push_back(PropertyInfo(Variant::STRING, "signal")); mi.default_arguments.push_back(Variant::NIL); mi.default_arguments.push_back(Variant::STRING); + mi.return_val = PropertyInfo(Variant::OBJECT, "", PROPERTY_HINT_RESOURCE_TYPE, "GDFunctionState"); p_functions->push_back(mi); } { MethodInfo mi; mi.name = "assert"; + mi.return_val.type = Variant::NIL; mi.arguments.push_back(PropertyInfo(Variant::BOOL, "condition")); p_functions->push_back(mi); } @@ -1901,11 +1903,11 @@ static void _find_call_arguments(GDCompletionContext &context, const GDParser::N arghint += ", "; else arghint += " "; - if (i == p_argidx) { + if (i == p_argidx || (mi.flags & METHOD_FLAG_VARARG && i > p_argidx)) { arghint += String::chr(0xFFFF); } arghint += _get_visual_datatype(mi.arguments[i]) + " " + mi.arguments[i].name; - if (i == p_argidx) { + if (i == p_argidx || (mi.flags & METHOD_FLAG_VARARG && i > p_argidx)) { arghint += String::chr(0xFFFF); } } diff --git a/modules/gdscript/gd_functions.cpp b/modules/gdscript/gd_functions.cpp index 3bd0ce3fab..f0cfdd6258 100644 --- a/modules/gdscript/gd_functions.cpp +++ b/modules/gdscript/gd_functions.cpp @@ -1572,43 +1572,49 @@ MethodInfo GDFunctions::get_info(Function p_func) { } break; case TEXT_STR: { - MethodInfo mi("str", PropertyInfo(Variant::NIL, "what"), PropertyInfo(Variant::NIL, "...")); + MethodInfo mi("str"); mi.return_val.type = Variant::STRING; + mi.flags |= METHOD_FLAG_VARARG; return mi; } break; case TEXT_PRINT: { - MethodInfo mi("print", PropertyInfo(Variant::NIL, "what"), PropertyInfo(Variant::NIL, "...")); + MethodInfo mi("print"); mi.return_val.type = Variant::NIL; + mi.flags |= METHOD_FLAG_VARARG; return mi; } break; case TEXT_PRINT_TABBED: { - MethodInfo mi("printt", PropertyInfo(Variant::NIL, "what"), PropertyInfo(Variant::NIL, "...")); + MethodInfo mi("printt"); mi.return_val.type = Variant::NIL; + mi.flags |= METHOD_FLAG_VARARG; return mi; } break; case TEXT_PRINT_SPACED: { - MethodInfo mi("prints", PropertyInfo(Variant::NIL, "what"), PropertyInfo(Variant::NIL, "...")); + MethodInfo mi("prints"); mi.return_val.type = Variant::NIL; + mi.flags |= METHOD_FLAG_VARARG; return mi; } break; case TEXT_PRINTERR: { - MethodInfo mi("printerr", PropertyInfo(Variant::NIL, "what"), PropertyInfo(Variant::NIL, "...")); + MethodInfo mi("printerr"); mi.return_val.type = Variant::NIL; + mi.flags |= METHOD_FLAG_VARARG; return mi; } break; case TEXT_PRINTRAW: { - MethodInfo mi("printraw", PropertyInfo(Variant::NIL, "what"), PropertyInfo(Variant::NIL, "...")); + MethodInfo mi("printraw"); mi.return_val.type = Variant::NIL; + mi.flags |= METHOD_FLAG_VARARG; return mi; } break; @@ -1620,8 +1626,9 @@ MethodInfo GDFunctions::get_info(Function p_func) { } break; case STR_TO_VAR: { - MethodInfo mi("str2var:Variant", PropertyInfo(Variant::STRING, "string")); + MethodInfo mi(Variant::NIL, "str2var", PropertyInfo(Variant::STRING, "string")); mi.return_val.type = Variant::NIL; + mi.return_val.usage |= PROPERTY_USAGE_NIL_IS_VARIANT; return mi; } break; case VAR_TO_BYTES: { @@ -1632,14 +1639,16 @@ MethodInfo GDFunctions::get_info(Function p_func) { } break; case BYTES_TO_VAR: { - MethodInfo mi("bytes2var:Variant", PropertyInfo(Variant::POOL_BYTE_ARRAY, "bytes")); + MethodInfo mi(Variant::NIL, "bytes2var", PropertyInfo(Variant::POOL_BYTE_ARRAY, "bytes")); mi.return_val.type = Variant::NIL; + mi.return_val.usage |= PROPERTY_USAGE_NIL_IS_VARIANT; return mi; } break; case GEN_RANGE: { - MethodInfo mi("range", PropertyInfo(Variant::NIL, "...")); + MethodInfo mi("range"); mi.return_val.type = Variant::ARRAY; + mi.flags |= METHOD_FLAG_VARARG; return mi; } break; case RESOURCE_LOAD: { @@ -1663,14 +1672,15 @@ MethodInfo GDFunctions::get_info(Function p_func) { } break; case VALIDATE_JSON: { - MethodInfo mi("validate_json:Variant", PropertyInfo(Variant::STRING, "json")); + MethodInfo mi("validate_json", PropertyInfo(Variant::STRING, "json")); mi.return_val.type = Variant::STRING; return mi; } break; case PARSE_JSON: { - MethodInfo mi("parse_json:Variant", PropertyInfo(Variant::STRING, "json")); + MethodInfo mi(Variant::NIL, "parse_json", PropertyInfo(Variant::STRING, "json")); mi.return_val.type = Variant::NIL; + mi.return_val.usage |= PROPERTY_USAGE_NIL_IS_VARIANT; return mi; } break; case TO_JSON: { diff --git a/modules/gridmap/grid_map_editor_plugin.cpp b/modules/gridmap/grid_map_editor_plugin.cpp index 6f0a13e07f..f6a76ad2a1 100644 --- a/modules/gridmap/grid_map_editor_plugin.cpp +++ b/modules/gridmap/grid_map_editor_plugin.cpp @@ -1159,14 +1159,14 @@ GridMapEditor::GridMapEditor(EditorNode *p_editor) { outer_mat.instance(); outer_mat->set_albedo(Color(0.7, 0.7, 1.0, 0.8)); - outer_mat->set_flag(SpatialMaterial::FLAG_ONTOP, true); + outer_mat->set_on_top_of_alpha(); outer_mat->set_flag(SpatialMaterial::FLAG_UNSHADED, true); outer_mat->set_line_width(3.0); outer_mat->set_feature(SpatialMaterial::FEATURE_TRANSPARENT, true); selection_floor_mat.instance(); selection_floor_mat->set_albedo(Color(0.80, 0.80, 1.0, 1)); - selection_floor_mat->set_flag(SpatialMaterial::FLAG_ONTOP, true); + selection_floor_mat->set_on_top_of_alpha(); selection_floor_mat->set_flag(SpatialMaterial::FLAG_UNSHADED, true); selection_floor_mat->set_line_width(3.0); //selection_floor_mat->set_feature(SpatialMaterial::FEATURE_TRANSPARENT, true); diff --git a/modules/hdr/image_loader_hdr.cpp b/modules/hdr/image_loader_hdr.cpp index d883b0f280..92d88207b3 100644 --- a/modules/hdr/image_loader_hdr.cpp +++ b/modules/hdr/image_loader_hdr.cpp @@ -1,5 +1,5 @@ /*************************************************************************/ -/* image_loader_jpegd.cpp */ +/* image_loader_hdr.cpp */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ diff --git a/modules/hdr/image_loader_hdr.h b/modules/hdr/image_loader_hdr.h index e6703dc142..569978d28d 100644 --- a/modules/hdr/image_loader_hdr.h +++ b/modules/hdr/image_loader_hdr.h @@ -1,5 +1,5 @@ /*************************************************************************/ -/* image_loader_jpegd.h */ +/* image_loader_hdr.h */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ diff --git a/modules/nativescript/api_generator.cpp b/modules/nativescript/api_generator.cpp index 4490197bdb..fdd5a2ea19 100644 --- a/modules/nativescript/api_generator.cpp +++ b/modules/nativescript/api_generator.cpp @@ -33,6 +33,7 @@ #include "class_db.h" #include "core/global_constants.h" +#include "core/pair.h" #include "core/project_settings.h" #include "os/file_access.h" @@ -93,6 +94,11 @@ struct SignalAPI { Map<int, Variant> default_arguments; }; +struct EnumAPI { + String name; + List<Pair<int, String> > values; +}; + struct ClassAPI { String class_name; String super_class_name; @@ -109,8 +115,28 @@ struct ClassAPI { List<PropertyAPI> properties; List<ConstantAPI> constants; List<SignalAPI> signals_; + List<EnumAPI> enums; }; +static String get_type_name(const PropertyInfo &info) { + if (info.type == Variant::INT && (info.usage & PROPERTY_USAGE_CLASS_IS_ENUM)) { + return String("enum.") + String(info.class_name).replace(".", "::"); + } + if (info.class_name != StringName()) { + return info.class_name; + } + if (info.hint == PROPERTY_HINT_RESOURCE_TYPE) { + return info.hint_string; + } + if (info.type == Variant::NIL && (info.usage & PROPERTY_USAGE_NIL_IS_VARIANT)) { + return "Variant"; + } + if (info.type == Variant::NIL) { + return "void"; + } + return Variant::get_type_name(info.type); +} + /* * Reads the entire Godot API to a list */ @@ -194,12 +220,8 @@ List<ClassAPI> generate_c_api_classes() { if (argument.name.find(":") != -1) { type = argument.name.get_slice(":", 1); name = argument.name.get_slice(":", 0); - } else if (argument.hint == PROPERTY_HINT_RESOURCE_TYPE) { - type = argument.hint_string; - } else if (argument.type == Variant::NIL) { - type = "Variant"; } else { - type = Variant::get_type_name(argument.type); + type = get_type_name(argument); } signal.argument_names.push_back(name); @@ -233,12 +255,8 @@ List<ClassAPI> generate_c_api_classes() { if (p->get().name.find(":") != -1) { property_api.type = p->get().name.get_slice(":", 1); property_api.name = p->get().name.get_slice(":", 0); - } else if (p->get().hint == PROPERTY_HINT_RESOURCE_TYPE) { - property_api.type = p->get().hint_string; - } else if (p->get().type == Variant::NIL) { - property_api.type = "Variant"; } else { - property_api.type = Variant::get_type_name(p->get().type); + property_api.type = get_type_name(p->get()); } if (!property_api.setter.empty() || !property_api.getter.empty()) { @@ -260,17 +278,11 @@ List<ClassAPI> generate_c_api_classes() { //method name method_api.method_name = m->get().name; //method return type - if (method_bind && method_bind->get_return_type() != StringName()) { - method_api.return_type = method_bind->get_return_type(); - } else if (method_api.method_name.find(":") != -1) { + if (method_api.method_name.find(":") != -1) { method_api.return_type = method_api.method_name.get_slice(":", 1); method_api.method_name = method_api.method_name.get_slice(":", 0); - } else if (m->get().return_val.type != Variant::NIL) { - method_api.return_type = m->get().return_val.hint == PROPERTY_HINT_RESOURCE_TYPE ? m->get().return_val.hint_string : Variant::get_type_name(m->get().return_val.type); - } else if (m->get().return_val.name != "") { - method_api.return_type = m->get().return_val.name; } else { - method_api.return_type = "void"; + method_api.return_type = get_type_name(m->get().return_val); } method_api.argument_count = method_info.arguments.size(); @@ -321,6 +333,25 @@ List<ClassAPI> generate_c_api_classes() { } } + // enums + { + List<EnumAPI> enums; + List<StringName> enum_names; + ClassDB::get_enum_list(class_name, &enum_names, true); + for (List<StringName>::Element *E = enum_names.front(); E; E = E->next()) { + List<StringName> value_names; + EnumAPI enum_api; + enum_api.name = E->get(); + ClassDB::get_enum_constants(class_name, E->get(), &value_names, true); + for (List<StringName>::Element *val_e = value_names.front(); val_e; val_e = val_e->next()) { + int int_val = ClassDB::get_integer_constant(class_name, val_e->get(), NULL); + enum_api.values.push_back(Pair<int, String>(int_val, val_e->get())); + } + enum_api.values.sort_custom<PairSort<int, String> >(); + class_api.enums.push_back(enum_api); + } + } + api.push_back(class_api); } @@ -410,11 +441,24 @@ static List<String> generate_c_api_json(const List<ClassAPI> &p_api) { source.push_back("\t\t\t\t]\n"); source.push_back(String("\t\t\t}") + (e->next() ? "," : "") + "\n"); } + source.push_back("\t\t],\n"); + + source.push_back("\t\t\"enums\": [\n"); + for (List<EnumAPI>::Element *e = api.enums.front(); e; e = e->next()) { + source.push_back("\t\t\t{\n"); + source.push_back("\t\t\t\t\"name\": \"" + e->get().name + "\",\n"); + source.push_back("\t\t\t\t\"values\": {\n"); + for (List<Pair<int, String> >::Element *val_e = e->get().values.front(); val_e; val_e = val_e->next()) { + source.push_back("\t\t\t\t\t\"" + val_e->get().second + "\": " + itos(val_e->get().first)); + source.push_back(String((val_e->next() ? "," : "")) + "\n"); + } + source.push_back("\t\t\t\t}\n"); + source.push_back(String("\t\t\t}") + (e->next() ? "," : "") + "\n"); + } source.push_back("\t\t]\n"); source.push_back(String("\t}") + (c->next() ? "," : "") + "\n"); } - source.push_back("]"); return source; diff --git a/modules/nativescript/config.py b/modules/nativescript/config.py index 4f89ca0d4c..9f57b9bb74 100644 --- a/modules/nativescript/config.py +++ b/modules/nativescript/config.py @@ -1,7 +1,7 @@ def can_build(platform): - return False + return True def configure(env): diff --git a/modules/nativescript/godot_nativescript.h b/modules/nativescript/godot_nativescript.h index 1eaf459570..d1cbf221ec 100644 --- a/modules/nativescript/godot_nativescript.h +++ b/modules/nativescript/godot_nativescript.h @@ -1,5 +1,5 @@ /*************************************************************************/ -/* godot.h */ +/* godot_nativescript.h */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -58,7 +58,7 @@ extern "C" { #define GDAPI GDCALLINGCONV #endif #else -#define GDCALLINGCONV __attribute__((sysv_abi)) +#define GDCALLINGCONV __attribute__((sysv_abi, visibility("default"))) #define GDAPI GDCALLINGCONV #endif diff --git a/modules/nativescript/nativescript.cpp b/modules/nativescript/nativescript.cpp index 3799ce31f8..e141080687 100644 --- a/modules/nativescript/nativescript.cpp +++ b/modules/nativescript/nativescript.cpp @@ -994,6 +994,8 @@ void NativeScriptLanguage::init_library(const Ref<GDNativeLibrary> &lib) { #endif // See if this library was "registered" already. const String &lib_path = lib->get_active_library_path(); + ERR_EXPLAIN(lib->get_name() + " does not have a library for the current platform"); + ERR_FAIL_COND(lib_path.length() == 0); Map<String, Ref<GDNative> >::Element *E = library_gdnatives.find(lib_path); if (!E) { diff --git a/modules/regex/SCsub b/modules/regex/SCsub index 0882406761..2dfc2739e9 100644 --- a/modules/regex/SCsub +++ b/modules/regex/SCsub @@ -1,7 +1,53 @@ #!/usr/bin/env python Import('env') +Import('env_modules') -env.add_source_files(env.modules_sources, "*.cpp") +env_regex = env_modules.Clone() +env_regex.Append(CPPFLAGS=["-DPCRE2_CODE_UNIT_WIDTH=0"]) +env_regex.add_source_files(env.modules_sources, "*.cpp") -Export('env') +if (env['builtin_pcre2'] != 'no'): + jit_blacklist = ['javascript'] + thirdparty_dir = '#thirdparty/pcre2/src/' + thirdparty_flags = ['-DPCRE2_STATIC', '-DHAVE_CONFIG_H'] + if 'platform' in env and env['platform'] not in jit_blacklist: + thirdparty_flags.append('-DSUPPORT_JIT') + thirdparty_sources = [ + "pcre2_auto_possess.c", + "pcre2_chartables.c", + "pcre2_compile.c", + "pcre2_config.c", + "pcre2_context.c", + "pcre2_dfa_match.c", + "pcre2_error.c", + "pcre2_find_bracket.c", + "pcre2_jit_compile.c", + "pcre2_maketables.c", + "pcre2_match.c", + "pcre2_match_data.c", + "pcre2_newline.c", + "pcre2_ord2utf.c", + "pcre2_pattern_info.c", + "pcre2_serialize.c", + "pcre2_string_utils.c", + "pcre2_study.c", + "pcre2_substitute.c", + "pcre2_substring.c", + "pcre2_tables.c", + "pcre2_ucd.c", + "pcre2_valid_utf.c", + "pcre2_xclass.c", + ] + thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources] + env_regex.Append(CPPPATH=[thirdparty_dir]) + env_regex.Append(CPPFLAGS=thirdparty_flags) + def pcre2_builtin(width): + env_pcre2 = env_modules.Clone() + env_pcre2["OBJSUFFIX"] = "_" + width + env_pcre2["OBJSUFFIX"] + env_pcre2.Append(CPPPATH=[thirdparty_dir]) + env_pcre2.add_source_files(env.modules_sources, thirdparty_sources) + env_pcre2.Append(CPPFLAGS=thirdparty_flags) + env_pcre2.Append(CPPFLAGS=["-DPCRE2_CODE_UNIT_WIDTH=" + width]) + pcre2_builtin("16") + pcre2_builtin("32") diff --git a/modules/regex/regex.cpp b/modules/regex/regex.cpp index de0a6b7e21..8afd01e20b 100644 --- a/modules/regex/regex.cpp +++ b/modules/regex/regex.cpp @@ -29,1479 +29,469 @@ /*************************************************************************/ #include "regex.h" -#include <wchar.h> -#include <wctype.h> - -static int RegEx_hex2int(const CharType c) { - if ('0' <= c && c <= '9') - return int(c - '0'); - else if ('a' <= c && c <= 'f') - return int(c - 'a') + 10; - else if ('A' <= c && c <= 'F') - return int(c - 'A') + 10; - return -1; -} - -struct RegExSearch { - - Ref<RegExMatch> match; - const CharType *str; - int end; - int eof; - - // For standard quantifier behaviour, test_parent is used to check the - // rest of the pattern. If the pattern matches, to prevent the parent - // from testing again, the complete flag is used as a shortcut out. - bool complete; - - // With lookahead, the position needs to rewind to its starting position - // when test_parent is used. Due to functional programming, this state - // has to be kept as a parameter. - Vector<int> lookahead_pos; - - CharType at(int p_pos) { - return str[p_pos]; - } - - RegExSearch(Ref<RegExMatch> &p_match, int p_end, int p_lookahead) - : match(p_match) { - - str = p_match->string.c_str(); - end = p_end; - eof = p_match->string.length(); - complete = false; - lookahead_pos.resize(p_lookahead); - } -}; - -struct RegExNode { - - RegExNode *next; - RegExNode *previous; - RegExNode *parent; - bool quantifiable; - int length; - - RegExNode() { - - next = NULL; - previous = NULL; - parent = NULL; - quantifiable = false; - length = -1; - } - - virtual ~RegExNode() { - - if (next) - memdelete(next); - } +#include "core/os/memory.h" - // For avoiding RTTI - virtual bool is_look_behind() { return false; } - - virtual int test(RegExSearch &s, int pos) const { - - return next ? next->test(s, pos) : -1; - } - - virtual int test_parent(RegExSearch &s, int pos) const { - - if (next) - pos = next->test(s, pos); - - if (pos >= 0) { - s.complete = true; - if (parent) - pos = parent->test_parent(s, pos); - } - - if (pos < 0) - s.complete = false; - - return pos; - } - - void increment_length(int amount, bool subtract = false) { - - if (amount >= 0 && length >= 0) { - if (!subtract) - length += amount; - else - length -= amount; - } else { - length = -1; - } +extern "C" { +#include <pcre2.h> +} - if (parent) - parent->increment_length(amount, subtract); - } -}; +static void *_regex_malloc(PCRE2_SIZE size, void *user) { -struct RegExNodeChar : public RegExNode { + return memalloc(size); +} - CharType ch; +static void _regex_free(void *ptr, void *user) { - RegExNodeChar(CharType p_char) { + memfree(ptr); +} - length = 1; - quantifiable = true; - ch = p_char; - } +int RegExMatch::_find(const Variant &p_name) const { - virtual int test(RegExSearch &s, int pos) const { + if (p_name.is_num()) { - if (s.end <= pos || 0 > pos || s.at(pos) != ch) + int i = (int)p_name; + if (i >= data.size()) return -1; + return i; - return next ? next->test(s, pos + 1) : pos + 1; - } + } else if (p_name.get_type() == Variant::STRING) { - static CharType parse_escape(const CharType *&c) { - - int point = 0; - switch (c[1]) { - case 'x': - for (int i = 2; i <= 3; ++i) { - int res = RegEx_hex2int(c[i]); - if (res == -1) - return '\0'; - point = (point << 4) + res; - } - c = &c[3]; - return CharType(point); - case 'u': - for (int i = 2; i <= 5; ++i) { - int res = RegEx_hex2int(c[i]); - if (res == -1) - return '\0'; - point = (point << 4) + res; - } - c = &c[5]; - return CharType(point); - case '0': ++c; return '\0'; - case 'a': ++c; return '\a'; - case 'e': ++c; return '\e'; - case 'f': ++c; return '\f'; - case 'n': ++c; return '\n'; - case 'r': ++c; return '\r'; - case 't': ++c; return '\t'; - case 'v': ++c; return '\v'; - case 'b': ++c; return '\b'; - default: break; - } - return (++c)[0]; + const Map<String, int>::Element *found = names.find((String)p_name); + if (found) + return found->value(); } -}; -struct RegExNodeRange : public RegExNode { + return -1; +} - CharType start; - CharType end; +String RegExMatch::get_subject() const { - RegExNodeRange(CharType p_start, CharType p_end) { + return subject; +} - length = 1; - quantifiable = true; - start = p_start; - end = p_end; - } +int RegExMatch::get_group_count() const { - virtual int test(RegExSearch &s, int pos) const { + if (data.size() == 0) + return 0; + return data.size() - 1; +} - if (s.end <= pos || 0 > pos) - return -1; +Dictionary RegExMatch::get_names() const { - CharType c = s.at(pos); - if (c < start || end < c) - return -1; + Dictionary result; - return next ? next->test(s, pos + 1) : pos + 1; + for (const Map<String, int>::Element *i = names.front(); i != NULL; i = i->next()) { + result[i->key()] = i->value(); } -}; - -struct RegExNodeShorthand : public RegExNode { - CharType repr; + return result; +} - RegExNodeShorthand(CharType p_repr) { +Array RegExMatch::get_strings() const { - length = 1; - quantifiable = true; - repr = p_repr; - } + Array result; - virtual int test(RegExSearch &s, int pos) const { + int size = data.size(); - if (s.end <= pos || 0 > pos) - return -1; + for (int i = 0; i < size; i++) { - bool found = false; - bool invert = false; - CharType c = s.at(pos); - switch (repr) { - case '.': - found = true; - break; - case 'W': - invert = true; - case 'w': - found = (c == '_' || iswalnum(c) != 0); - break; - case 'D': - invert = true; - case 'd': - found = ('0' <= c && c <= '9'); - break; - case 'S': - invert = true; - case 's': - found = (iswspace(c) != 0); - break; - default: - break; - } + int start = data[i].start; - if (found == invert) - return -1; - - return next ? next->test(s, pos + 1) : pos + 1; - } -}; - -struct RegExNodeClass : public RegExNode { - - enum Type { - Type_none, - Type_alnum, - Type_alpha, - Type_ascii, - Type_blank, - Type_cntrl, - Type_digit, - Type_graph, - Type_lower, - Type_print, - Type_punct, - Type_space, - Type_upper, - Type_xdigit, - Type_word - }; - - Type type; - - bool test_class(CharType c) const { - - static Vector<CharType> REGEX_NODE_SPACE = String(" \t\r\n\f"); - static Vector<CharType> REGEX_NODE_PUNCT = String("!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~"); - - switch (type) { - case Type_alnum: - if ('0' <= c && c <= '9') return true; - if ('a' <= c && c <= 'z') return true; - if ('A' <= c && c <= 'Z') return true; - return false; - case Type_alpha: - if ('a' <= c && c <= 'z') return true; - if ('A' <= c && c <= 'Z') return true; - return false; - case Type_ascii: - return (0x00 <= c && c <= 0x7F); - case Type_blank: - return (c == ' ' || c == '\t'); - case Type_cntrl: - return ((0x00 <= c && c <= 0x1F) || c == 0x7F); - case Type_digit: - return ('0' <= c && c <= '9'); - case Type_graph: - return (0x20 < c && c < 0x7F); - case Type_lower: - return ('a' <= c && c <= 'z'); - case Type_print: - return (0x20 < c && c < 0x7f); - case Type_punct: - return (REGEX_NODE_PUNCT.find(c) >= 0); - case Type_space: - return (REGEX_NODE_SPACE.find(c) >= 0); - case Type_upper: - return ('A' <= c && c <= 'Z'); - case Type_xdigit: - if ('0' <= c && c <= '9') return true; - if ('a' <= c && c <= 'f') return true; - if ('A' <= c && c <= 'F') return true; - return false; - case Type_word: - if ('0' <= c && c <= '9') return true; - if ('a' <= c && c <= 'z') return true; - if ('A' <= c && c <= 'Z') return true; - return (c == '_'); - default: - return false; + if (start == -1) { + result.append(String()); + continue; } - return false; - } - - RegExNodeClass(Type p_type) { - - length = 1; - quantifiable = true; - type = p_type; - } - - virtual int test(RegExSearch &s, int pos) const { - - if (s.end <= pos || 0 > pos) - return -1; - - if (!test_class(s.at(pos))) - return -1; - - return next ? next->test(s, pos + 1) : pos + 1; - } - -#define REGEX_CMP_CLASS(POS, NAME) \ - if (cmp_class(POS, #NAME)) return Type_##NAME - - static Type parse_type(const CharType *&p_pos) { - - REGEX_CMP_CLASS(p_pos, alnum); - REGEX_CMP_CLASS(p_pos, alpha); - REGEX_CMP_CLASS(p_pos, ascii); - REGEX_CMP_CLASS(p_pos, blank); - REGEX_CMP_CLASS(p_pos, cntrl); - REGEX_CMP_CLASS(p_pos, digit); - REGEX_CMP_CLASS(p_pos, graph); - REGEX_CMP_CLASS(p_pos, lower); - REGEX_CMP_CLASS(p_pos, print); - REGEX_CMP_CLASS(p_pos, punct); - REGEX_CMP_CLASS(p_pos, space); - REGEX_CMP_CLASS(p_pos, upper); - REGEX_CMP_CLASS(p_pos, xdigit); - REGEX_CMP_CLASS(p_pos, word); - return Type_none; - } - - static bool cmp_class(const CharType *&p_pos, const char *p_text) { - - unsigned int i = 0; - for (i = 0; p_text[i] != '\0'; ++i) - if (p_pos[i] != p_text[i]) - return false; - - if (p_pos[i++] != ':' || p_pos[i] != ']') - return false; - - p_pos = &p_pos[i]; - return true; - } -}; - -struct RegExNodeAnchorStart : public RegExNode { - RegExNodeAnchorStart() { + int length = data[i].end - start; - length = 0; + result.append(subject.substr(start, length)); } - virtual int test(RegExSearch &s, int pos) const { + return result; +} - if (pos != 0) - return -1; +String RegExMatch::get_string(const Variant &p_name) const { - return next ? next->test(s, pos) : pos; - } -}; + int id = _find(p_name); -struct RegExNodeAnchorEnd : public RegExNode { + if (id < 0) + return String(); - RegExNodeAnchorEnd() { + int start = data[id].start; - length = 0; - } + if (start == -1) + return String(); - virtual int test(RegExSearch &s, int pos) const { + int length = data[id].end - start; - if (pos != s.eof) - return -1; + return subject.substr(start, length); +} - return next ? next->test(s, pos) : pos; - } -}; +int RegExMatch::get_start(const Variant &p_name) const { -struct RegExNodeWordBoundary : public RegExNode { + int id = _find(p_name); - bool inverse; + if (id < 0) + return -1; - RegExNodeWordBoundary(bool p_inverse) { + return data[id].start; +} - length = 0; - inverse = p_inverse; - } +int RegExMatch::get_end(const Variant &p_name) const { - virtual int test(RegExSearch &s, int pos) const { + int id = _find(p_name); - bool left = false; - bool right = false; + if (id < 0) + return -1; - if (pos != 0) { - CharType c = s.at(pos - 1); - if (c == '_' || iswalnum(c)) - left = true; - } + return data[id].end; +} - if (pos != s.eof) { - CharType c = s.at(pos); - if (c == '_' || iswalnum(c)) - right = true; - } +void RegExMatch::_bind_methods() { - if ((left == right) != inverse) - return -1; + ClassDB::bind_method(D_METHOD("get_subject"), &RegExMatch::get_subject); + ClassDB::bind_method(D_METHOD("get_group_count"), &RegExMatch::get_group_count); + ClassDB::bind_method(D_METHOD("get_names"), &RegExMatch::get_names); + ClassDB::bind_method(D_METHOD("get_strings"), &RegExMatch::get_strings); + ClassDB::bind_method(D_METHOD("get_string", "name"), &RegExMatch::get_string, DEFVAL(0)); + ClassDB::bind_method(D_METHOD("get_start", "name"), &RegExMatch::get_start, DEFVAL(0)); + ClassDB::bind_method(D_METHOD("get_end", "name"), &RegExMatch::get_end, DEFVAL(0)); +} - return next ? next->test(s, pos) : pos; - } -}; +void RegEx::_pattern_info(uint32_t what, void *where) const { -struct RegExNodeQuantifier : public RegExNode { + if (sizeof(CharType) == 2) { - int min; - int max; - bool greedy; - RegExNode *child; + pcre2_pattern_info_16((pcre2_code_16 *)code, what, where); - RegExNodeQuantifier(int p_min, int p_max) { + } else { - min = p_min; - max = p_max; - greedy = true; - child = NULL; + pcre2_pattern_info_32((pcre2_code_32 *)code, what, where); } +} - ~RegExNodeQuantifier() { - - if (child) - memdelete(child); - } +void RegEx::clear() { - virtual int test(RegExSearch &s, int pos) const { + if (sizeof(CharType) == 2) { - return test_step(s, pos, 0, pos); - } + if (code) + pcre2_code_free_16((pcre2_code_16 *)code); - virtual int test_parent(RegExSearch &s, int pos) const { + } else { - s.complete = false; - return pos; + if (code) + pcre2_code_free_32((pcre2_code_32 *)code); } +} - int test_step(RegExSearch &s, int pos, int level, int start) const { - - if (pos > s.end) - return -1; - - if (!greedy && level > min) { - int res = next ? next->test(s, pos) : pos; - if (s.complete) - return res; - - if (res >= 0 && parent->test_parent(s, res) >= 0) - return res; - } - - if (max >= 0 && level > max) - return -1; +Error RegEx::compile(const String &p_pattern) { - int res = pos; - if (level >= 1) { - if (level > min + 1 && pos == start) - return -1; + pattern = p_pattern; + clear(); - res = child->test(s, pos); - if (s.complete) - return res; - } + int err; + PCRE2_SIZE offset; + uint32_t flags = PCRE2_DUPNAMES; - if (res >= 0) { + if (sizeof(CharType) == 2) { - int res_step = test_step(s, res, level + 1, start); - if (res_step >= 0) - return res_step; + pcre2_general_context_16 *gctx = (pcre2_general_context_16 *)general_ctx; + pcre2_compile_context_16 *cctx = pcre2_compile_context_create_16(gctx); + PCRE2_SPTR16 p = (PCRE2_SPTR16)pattern.c_str(); - if (greedy && level >= min) { - if (next) - res = next->test(s, res); - if (s.complete) - return res; + code = pcre2_compile_16(p, pattern.length(), flags, &err, &offset, cctx); - if (res >= 0 && parent->test_parent(s, res) >= 0) - return res; - } + if (!code) { + PCRE2_UCHAR16 buf[256]; + pcre2_get_error_message_16(err, buf, 256); + String message = String::num(offset) + ": " + String((const CharType *)buf); + ERR_PRINT(message.utf8()); + return FAILED; } - return -1; - } -}; - -struct RegExNodeBackReference : public RegExNode { - int id; + } else { - RegExNodeBackReference(int p_id) { - - length = -1; - quantifiable = true; - id = p_id; - } + pcre2_general_context_32 *gctx = (pcre2_general_context_32 *)general_ctx; + pcre2_compile_context_32 *cctx = pcre2_compile_context_create_32(gctx); + PCRE2_SPTR32 p = (PCRE2_SPTR32)pattern.c_str(); - virtual int test(RegExSearch &s, int pos) const { + code = pcre2_compile_32(p, pattern.length(), flags, &err, &offset, cctx); - RegExMatch::Group &ref = s.match->captures[id]; - for (int i = 0; i < ref.length; ++i) { - - if (pos + i >= s.end) - return -1; - - if (s.at(ref.start + i) != s.at(pos + i)) - return -1; + if (!code) { + PCRE2_UCHAR32 buf[256]; + pcre2_get_error_message_32(err, buf, 256); + String message = String::num(offset) + ": " + String((const CharType *)buf); + ERR_PRINT(message.utf8()); + return FAILED; } - return next ? next->test(s, pos + ref.length) : pos + ref.length; } -}; - -struct RegExNodeGroup : public RegExNode { - - bool inverse; - bool reset_pos; - Vector<RegExNode *> childset; - RegExNode *back; - - RegExNodeGroup() { - - length = 0; - quantifiable = true; - inverse = false; - reset_pos = false; - back = NULL; - } - - virtual ~RegExNodeGroup() { - - for (int i = 0; i < childset.size(); ++i) - memdelete(childset[i]); - } - - virtual void test_success(RegExSearch &s, int pos) const { + return OK; +} - return; - } +Ref<RegExMatch> RegEx::search(const String &p_subject, int p_offset, int p_end) const { - virtual int test(RegExSearch &s, int pos) const { + ERR_FAIL_COND_V(!is_valid(), NULL); - for (int i = 0; i < childset.size(); ++i) { + Ref<RegExMatch> result = memnew(RegExMatch); - s.complete = false; + int length = p_subject.length(); + if (p_end >= 0 && p_end < length) + length = p_end; - int res = childset[i]->test(s, pos); + if (sizeof(CharType) == 2) { - if (inverse) { - s.complete = false; - if (res < 0) - res = pos + 1; - else - return -1; + pcre2_code_16 *c = (pcre2_code_16 *)code; + pcre2_general_context_16 *gctx = (pcre2_general_context_16 *)general_ctx; + pcre2_match_context_16 *mctx = pcre2_match_context_create_16(gctx); + PCRE2_SPTR16 s = (PCRE2_SPTR16)p_subject.c_str(); - if (i + 1 < childset.size()) - continue; - } + pcre2_match_data_16 *match = pcre2_match_data_create_from_pattern_16(c, gctx); - if (s.complete) - return res; + int res = pcre2_match_16(c, s, length, p_offset, 0, match, mctx); - if (res >= 0) { - if (reset_pos) - res = pos; - this->test_success(s, res); - return next ? next->test(s, res) : res; - } + if (res < 0) { + pcre2_match_data_free_16(match); + return NULL; } - return -1; - } - - void add_child(RegExNode *node) { - node->parent = this; - node->previous = back; + uint32_t size = pcre2_get_ovector_count_16(match); + PCRE2_SIZE *ovector = pcre2_get_ovector_pointer_16(match); - if (back) - back->next = node; - else - childset.push_back(node); + result->data.resize(size); - increment_length(node->length); + for (uint32_t i = 0; i < size; i++) { - back = node; - } - - void add_childset() { - - if (childset.size() > 0) - length = -1; - back = NULL; - } - - RegExNode *swap_back(RegExNode *node) { - - RegExNode *old = back; - - if (old) { - if (!old->previous) - childset.remove(childset.size() - 1); - back = old->previous; - increment_length(old->length, true); + result->data[i].start = ovector[i * 2]; + result->data[i].end = ovector[i * 2 + 1]; } - add_child(node); + pcre2_match_data_free_16(match); + pcre2_match_context_free_16(mctx); - return old; - } -}; + } else { -struct RegExNodeCapturing : public RegExNodeGroup { + pcre2_code_32 *c = (pcre2_code_32 *)code; + pcre2_general_context_32 *gctx = (pcre2_general_context_32 *)general_ctx; + pcre2_match_context_32 *mctx = pcre2_match_context_create_32(gctx); + PCRE2_SPTR32 s = (PCRE2_SPTR32)p_subject.c_str(); - int id; + pcre2_match_data_32 *match = pcre2_match_data_create_from_pattern_32(c, gctx); - RegExNodeCapturing(int p_id = 0) { + int res = pcre2_match_32(c, s, length, p_offset, 0, match, mctx); - id = p_id; - } - - virtual void test_success(RegExSearch &s, int pos) const { - - RegExMatch::Group &ref = s.match->captures[id]; - ref.length = pos - ref.start; - } - - virtual int test(RegExSearch &s, int pos) const { - - RegExMatch::Group &ref = s.match->captures[id]; - int old_start = ref.start; - ref.start = pos; - - int res = RegExNodeGroup::test(s, pos); - - if (res < 0) - ref.start = old_start; - return res; - } - - virtual int test_parent(RegExSearch &s, int pos) const { - - RegExMatch::Group &ref = s.match->captures[id]; - ref.length = pos - ref.start; - return RegExNode::test_parent(s, pos); - } - - static Variant parse_name(const CharType *&c, bool p_allow_numeric) { - - if (c[1] == '0') { - return -1; - } else if ('1' <= c[1] && c[1] <= '9') { - if (!p_allow_numeric) - return -1; - int res = (++c)[0] - '0'; - while ('0' <= c[1] && c[1] <= '9') - res = res * 10 + int((++c)[0] - '0'); - if ((++c)[0] != '>') - return -1; - return res; - } else if (iswalnum(c[1])) { - String res(++c, 1); - while (iswalnum(c[1])) - res += String(++c, 1); - if ((++c)[0] != '>') - return -1; - return res; + if (res < 0) { + pcre2_match_data_free_32(match); + return NULL; } - return -1; - } -}; - -struct RegExNodeLookAhead : public RegExNodeGroup { - - int id; - RegExNodeLookAhead(bool p_inverse, int p_id = 0) { + uint32_t size = pcre2_get_ovector_count_32(match); + PCRE2_SIZE *ovector = pcre2_get_ovector_pointer_32(match); - quantifiable = false; - inverse = p_inverse; - reset_pos = true; - id = p_id; - } - - virtual int test(RegExSearch &s, int pos) const { + result->data.resize(size); - s.lookahead_pos[id] = pos; - return RegExNodeGroup::test(s, pos); - } + for (uint32_t i = 0; i < size; i++) { - virtual int test_parent(RegExSearch &s, int pos) const { + result->data[i].start = ovector[i * 2]; + result->data[i].end = ovector[i * 2 + 1]; + } - return RegExNode::test_parent(s, s.lookahead_pos[id]); + pcre2_match_data_free_32(match); + pcre2_match_context_free_32(mctx); } -}; -struct RegExNodeLookBehind : public RegExNodeGroup { + result->subject = p_subject; - RegExNodeLookBehind(bool p_inverse, int p_id = 0) { + uint32_t count; + const CharType *table; + uint32_t entry_size; - quantifiable = false; - inverse = p_inverse; - reset_pos = true; - } + _pattern_info(PCRE2_INFO_NAMECOUNT, &count); + _pattern_info(PCRE2_INFO_NAMETABLE, &table); + _pattern_info(PCRE2_INFO_NAMEENTRYSIZE, &entry_size); - virtual bool is_look_behind() { return true; } + for (int i = 0; i < count; i++) { - virtual int test(RegExSearch &s, int pos) const { + CharType id = table[i * entry_size]; + if (result->data[id].start == -1) + continue; + String name = &table[i * entry_size + 1]; + if (result->names.has(name)) + continue; - if (pos < length) - return -1; - return RegExNodeGroup::test(s, pos - length); + result->names.insert(name, id); } -}; -struct RegExNodeBracket : public RegExNode { + return result; +} - bool inverse; - Vector<RegExNode *> children; +String RegEx::sub(const String &p_subject, const String &p_replacement, bool p_all, int p_offset, int p_end) const { - RegExNodeBracket() { + ERR_FAIL_COND_V(!is_valid(), String()); - length = 1; - quantifiable = true; - inverse = false; - } + String output; + output.resize(p_subject.length()); - virtual ~RegExNodeBracket() { + uint32_t flags = PCRE2_SUBSTITUTE_OVERFLOW_LENGTH; + if (p_all) + flags |= PCRE2_SUBSTITUTE_GLOBAL; - for (int i = 0; i < children.size(); ++i) - memdelete(children[i]); - } + PCRE2_SIZE olength = output.length(); - virtual int test(RegExSearch &s, int pos) const { + PCRE2_SIZE length = p_subject.length(); + if (p_end >= 0 && p_end < length) + length = p_end; - for (int i = 0; i < children.size(); ++i) { + if (sizeof(CharType) == 2) { - int res = children[i]->test(s, pos); + pcre2_code_16 *c = (pcre2_code_16 *)code; + pcre2_general_context_16 *gctx = (pcre2_general_context_16 *)general_ctx; + pcre2_match_context_16 *mctx = pcre2_match_context_create_16(gctx); + PCRE2_SPTR16 s = (PCRE2_SPTR16)p_subject.c_str(); + PCRE2_SPTR16 r = (PCRE2_SPTR16)p_replacement.c_str(); + PCRE2_UCHAR16 *o = (PCRE2_UCHAR16 *)output.c_str(); - if (inverse) { - if (res < 0) - res = pos + 1; - else - return -1; + pcre2_match_data_16 *match = pcre2_match_data_create_from_pattern_16(c, gctx); - if (i + 1 < children.size()) - continue; - } + int res = pcre2_substitute_16(c, s, length, p_offset, flags, match, mctx, r, p_replacement.length(), o, &olength); - if (res >= 0) - return next ? next->test(s, res) : res; + if (res == PCRE2_ERROR_NOMEMORY) { + output.resize(olength); + o = (PCRE2_UCHAR16 *)output.c_str(); + res = pcre2_substitute_16(c, s, length, p_offset, flags, match, mctx, r, p_replacement.length(), o, &olength); } - return -1; - } - void add_child(RegExNode *node) { - - node->parent = this; - children.push_back(node); - } + pcre2_match_data_free_16(match); + pcre2_match_context_free_16(mctx); - void pop_back() { - - memdelete(children[children.size() - 1]); - children.remove(children.size() - 1); - } -}; - -#define REGEX_EXPAND_FAIL(MSG) \ - { \ - ERR_PRINT(MSG); \ - return String(); \ - } - -String RegExMatch::expand(const String &p_template) const { - - String res; - for (const CharType *c = p_template.c_str(); *c != '\0'; ++c) { - if (c[0] == '\\') { - if (('1' <= c[1] && c[1] <= '9') || (c[1] == 'g' && c[2] == '{')) { - - int ref = 0; - bool unclosed = false; - - if (c[1] == 'g') { - unclosed = true; - c = &c[2]; - } - - while ('0' <= c[1] && c[1] <= '9') { - ref = ref * 10 + int(c[1] - '0'); - ++c; - } - - if (unclosed) { - if (c[1] != '}') - REGEX_EXPAND_FAIL("unclosed backreference '{'"); - ++c; - } - - res += get_string(ref); - - } else if (c[1] == 'g' && c[2] == '<') { - - const CharType *d = &c[2]; + if (res < 0) + return String(); - Variant name = RegExNodeCapturing::parse_name(d, true); - if (name == Variant(-1)) - REGEX_EXPAND_FAIL("unrecognised character for group name"); + } else { - c = d; + pcre2_code_32 *c = (pcre2_code_32 *)code; + pcre2_general_context_32 *gctx = (pcre2_general_context_32 *)general_ctx; + pcre2_match_context_32 *mctx = pcre2_match_context_create_32(gctx); + PCRE2_SPTR32 s = (PCRE2_SPTR32)p_subject.c_str(); + PCRE2_SPTR32 r = (PCRE2_SPTR32)p_replacement.c_str(); + PCRE2_UCHAR32 *o = (PCRE2_UCHAR32 *)output.c_str(); - res += get_string(name); + pcre2_match_data_32 *match = pcre2_match_data_create_from_pattern_32(c, gctx); - } else { + int res = pcre2_substitute_32(c, s, length, p_offset, flags, match, mctx, r, p_replacement.length(), o, &olength); - const CharType *d = c; - CharType ch = RegExNodeChar::parse_escape(d); - if (c == d) - REGEX_EXPAND_FAIL("invalid escape token"); - res += String(&ch, 1); - c = d; - } - } else { - res += String(c, 1); + if (res == PCRE2_ERROR_NOMEMORY) { + output.resize(olength); + o = (PCRE2_UCHAR32 *)output.c_str(); + res = pcre2_substitute_32(c, s, length, p_offset, flags, match, mctx, r, p_replacement.length(), o, &olength); } - } - return res; -} -int RegExMatch::get_group_count() const { + pcre2_match_data_free_32(match); + pcre2_match_context_free_32(mctx); - int count = 0; - for (int i = 1; i < captures.size(); ++i) - if (captures[i].name.get_type() == Variant::INT) - ++count; - return count; -} - -Array RegExMatch::get_group_array() const { - - Array res; - for (int i = 1; i < captures.size(); ++i) { - const RegExMatch::Group &capture = captures[i]; - if (capture.name.get_type() != Variant::INT) - continue; - - if (capture.start >= 0) - res.push_back(string.substr(capture.start, capture.length)); - else - res.push_back(String()); - } - return res; -} - -Array RegExMatch::get_names() const { - - Array res; - for (int i = 1; i < captures.size(); ++i) - if (captures[i].name.get_type() == Variant::STRING) - res.push_back(captures[i].name); - return res; -} - -Dictionary RegExMatch::get_name_dict() const { - - Dictionary res; - for (int i = 1; i < captures.size(); ++i) { - const RegExMatch::Group &capture = captures[i]; - if (capture.name.get_type() != Variant::STRING) - continue; - - if (capture.start >= 0) - res[capture.name] = string.substr(capture.start, capture.length); - else - res[capture.name] = String(); - } - return res; -} - -String RegExMatch::get_string(const Variant &p_name) const { - - for (int i = 0; i < captures.size(); ++i) { - - const RegExMatch::Group &capture = captures[i]; - - if (capture.name != p_name) - continue; - - if (capture.start == -1) + if (res < 0) return String(); - - return string.substr(capture.start, capture.length); } - return String(); -} - -int RegExMatch::get_start(const Variant &p_name) const { - for (int i = 0; i < captures.size(); ++i) - if (captures[i].name == p_name) - return captures[i].start; - return -1; + return output; } -int RegExMatch::get_end(const Variant &p_name) const { +bool RegEx::is_valid() const { - for (int i = 0; i < captures.size(); ++i) - if (captures[i].name == p_name) - return captures[i].start + captures[i].length; - return -1; + return (code != NULL); } -RegExMatch::RegExMatch() { -} +String RegEx::get_pattern() const { -static bool RegEx_is_shorthand(CharType ch) { - - switch (ch) { - case 'w': - case 'W': - case 'd': - case 'D': - case 's': - case 'S': - return true; - default: - break; - } - return false; + return pattern; } -#define REGEX_COMPILE_FAIL(MSG) \ - { \ - ERR_PRINT(MSG); \ - clear(); \ - return FAILED; \ - } +int RegEx::get_group_count() const { -Error RegEx::compile(const String &p_pattern) { + ERR_FAIL_COND_V(!is_valid(), 0); - ERR_FAIL_COND_V(p_pattern.length() == 0, FAILED); + uint32_t count; - if (pattern == p_pattern && root) - return OK; + _pattern_info(PCRE2_INFO_CAPTURECOUNT, &count); - clear(); - pattern = p_pattern; - group_names.push_back(0); - RegExNodeGroup *root_group = memnew(RegExNodeCapturing(0)); - root = root_group; - Vector<RegExNodeGroup *> stack; - stack.push_back(root_group); - int lookahead_level = 0; - int numeric_groups = 0; - const int numeric_max = 9; - - for (const CharType *c = p_pattern.c_str(); *c != '\0'; ++c) { - - switch (c[0]) { - case '(': - if (c[1] == '?') { - - RegExNodeGroup *group = NULL; - switch (c[2]) { - case ':': - c = &c[2]; - group = memnew(RegExNodeGroup()); - break; - case '!': - case '=': - group = memnew(RegExNodeLookAhead((c[2] == '!'), lookahead_level++)); - if (lookahead_depth < lookahead_level) - lookahead_depth = lookahead_level; - c = &c[2]; - break; - case '<': - if (c[3] == '!' || c[3] == '=') { - group = memnew(RegExNodeLookBehind((c[3] == '!'), lookahead_level++)); - c = &c[3]; - } - break; - case 'P': - if (c[3] == '<') { - const CharType *d = &c[3]; - Variant name = RegExNodeCapturing::parse_name(d, false); - if (name == Variant(-1)) - REGEX_COMPILE_FAIL("unrecognised character for group name"); - group = memnew(RegExNodeCapturing(group_names.size())); - group_names.push_back(name); - c = d; - } - default: - break; - } - if (!group) - REGEX_COMPILE_FAIL("unrecognised qualifier for group"); - stack[0]->add_child(group); - stack.insert(0, group); - - } else if (numeric_groups < numeric_max) { - - RegExNodeCapturing *group = memnew(RegExNodeCapturing(group_names.size())); - group_names.push_back(++numeric_groups); - stack[0]->add_child(group); - stack.insert(0, group); - - } else { - - RegExNodeGroup *group = memnew(RegExNodeGroup()); - stack[0]->add_child(group); - stack.insert(0, group); - } - break; - case ')': - if (stack.size() == 1) - REGEX_COMPILE_FAIL("unexpected ')'"); - stack.remove(0); - break; - case '\\': - if (('1' <= c[1] && c[1] <= '9') || (c[1] == 'g' && c[2] == '{')) { - - int ref = 0; - bool unclosed = false; - - if (c[1] == 'g') { - unclosed = true; - c = &c[2]; - } - - while ('0' <= c[1] && c[1] <= '9') { - ref = ref * 10 + int(c[1] - '0'); - ++c; - } - - if (unclosed) { - if (c[1] != '}') - REGEX_COMPILE_FAIL("unclosed backreference '{'"); - ++c; - } - - if (ref > numeric_groups || ref <= 0) - REGEX_COMPILE_FAIL("backreference not found"); - - for (int i = 0; i < stack.size(); ++i) - if (stack[i]->is_look_behind()) - REGEX_COMPILE_FAIL("backreferences inside lookbehind not supported"); - - for (int i = 0; i < group_names.size(); ++i) { - if (group_names[i].get_type() == Variant::INT && int(group_names[i]) == ref) { - ref = group_names[i]; - break; - } - } - - stack[0]->add_child(memnew(RegExNodeBackReference(ref))); - } - if (c[1] == 'g' && c[2] == '<') { - - const CharType *d = &c[2]; - - Variant name = RegExNodeCapturing::parse_name(d, true); - if (name == Variant(-1)) - REGEX_COMPILE_FAIL("unrecognised character for group name"); - - c = d; - - for (int i = 0; i < stack.size(); ++i) - if (stack[i]->is_look_behind()) - REGEX_COMPILE_FAIL("backreferences inside lookbehind not supported"); - - int ref = -1; - - for (int i = 0; i < group_names.size(); ++i) { - if (group_names[i].get_type() == Variant::INT && int(group_names[i]) == ref) { - ref = group_names[i]; - break; - } - } - - if (ref == -1) - REGEX_COMPILE_FAIL("backreference not found"); - - stack[0]->add_child(memnew(RegExNodeBackReference(ref))); - - } else if (c[1] == 'b' || c[1] == 'B') { - - stack[0]->add_child(memnew(RegExNodeWordBoundary(*(++c) == 'B'))); - - } else if (RegEx_is_shorthand(c[1])) { - - stack[0]->add_child(memnew(RegExNodeShorthand(*(++c)))); - - } else { - - const CharType *d = c; - CharType ch = RegExNodeChar::parse_escape(d); - if (c == d) - REGEX_COMPILE_FAIL("invalid escape token"); - stack[0]->add_child(memnew(RegExNodeChar(ch))); - c = d; - } - break; - case '[': { - RegExNodeBracket *bracket = memnew(RegExNodeBracket()); - stack[0]->add_child(bracket); - if (c[1] == '^') { - bracket->inverse = true; - ++c; - } - bool first_child = true; - CharType previous_child; - bool previous_child_single = false; - while (true) { - ++c; - if (!first_child && c[0] == ']') { - - break; - - } else if (c[0] == '\0') { - - REGEX_COMPILE_FAIL("unclosed bracket expression '['"); - - } else if (c[0] == '\\') { - - if (RegEx_is_shorthand(c[1])) { - bracket->add_child(memnew(RegExNodeShorthand(*(++c)))); - } else { - const CharType *d = c; - CharType ch = RegExNodeChar::parse_escape(d); - if (c == d) - REGEX_COMPILE_FAIL("invalid escape token"); - bracket->add_child(memnew(RegExNodeChar(ch))); - c = d; - previous_child = ch; - previous_child_single = true; - } - - } else if (c[0] == ']' && c[1] == ':') { - - const CharType *d = &c[2]; - RegExNodeClass::Type type = RegExNodeClass::parse_type(d); - if (type != RegExNodeClass::Type_none) { - - c = d; - previous_child_single = false; - - } else { - - bracket->add_child(memnew(RegExNodeChar('['))); - previous_child = '['; - previous_child_single = true; - } - } else if (previous_child_single && c[0] == '-') { - - if (c[1] != '\0' && c[1] != ']') { - - CharType next; - - if (c[1] == '\\') { - const CharType *d = ++c; - next = RegExNodeChar::parse_escape(d); - if (c == d) - REGEX_COMPILE_FAIL("invalid escape token"); - } else { - next = *(++c); - } - - if (next < previous_child) - REGEX_COMPILE_FAIL("text range out of order"); - - bracket->pop_back(); - bracket->add_child(memnew(RegExNodeRange(previous_child, next))); - previous_child_single = false; - } else { - - bracket->add_child(memnew(RegExNodeChar('-'))); - previous_child = '-'; - previous_child_single = true; - } - } else { - - bracket->add_child(memnew(RegExNodeChar(c[0]))); - previous_child = c[0]; - previous_child_single = true; - } - first_child = false; - } - } break; - case '|': - for (int i = 0; i < stack.size(); ++i) - if (stack[i]->is_look_behind()) - REGEX_COMPILE_FAIL("alternations inside lookbehind not supported"); - stack[0]->add_childset(); - break; - case '^': - stack[0]->add_child(memnew(RegExNodeAnchorStart())); - break; - case '$': - stack[0]->add_child(memnew(RegExNodeAnchorEnd())); - break; - case '.': - stack[0]->add_child(memnew(RegExNodeShorthand('.'))); - break; - case '?': - case '*': - case '+': - case '{': { - int min_val = 0; - int max_val = -1; - bool valid = true; - const CharType *d = c; - bool max_set = true; - switch (c[0]) { - case '?': - min_val = 0; - max_val = 1; - break; - case '*': - min_val = 0; - max_val = -1; - break; - case '+': - min_val = 1; - max_val = -1; - break; - case '{': - max_set = false; - while (valid) { - ++d; - if (d[0] == '}') { - break; - } else if (d[0] == ',') { - max_set = true; - } else if ('0' <= d[0] && d[0] <= '9') { - if (max_set) { - if (max_val < 0) - max_val = int(d[0] - '0'); - else - max_val = max_val * 10 + int(d[0] - '0'); - } else { - min_val = min_val * 10 + int(d[0] - '0'); - } - } else { - valid = false; - } - } - break; - default: - break; - } - - if (!max_set) - max_val = min_val; - - if (valid) { - - c = d; - - if (stack[0]->back == NULL || !stack[0]->back->quantifiable) - REGEX_COMPILE_FAIL("element not quantifiable"); - - if (min_val != max_val) - for (int i = 0; i < stack.size(); ++i) - if (stack[i]->is_look_behind()) - REGEX_COMPILE_FAIL("variable length quantifiers inside lookbehind not supported"); - - RegExNodeQuantifier *quant = memnew(RegExNodeQuantifier(min_val, max_val)); - quant->child = stack[0]->swap_back(quant); - quant->child->previous = NULL; - quant->child->parent = quant; - - if (min_val == max_val && quant->child->length >= 0) - quant->length = max_val * quant->child->length; - - if (c[1] == '?') { - quant->greedy = false; - ++c; - } - break; - } - } - default: - stack[0]->add_child(memnew(RegExNodeChar(c[0]))); - break; - } - } - if (stack.size() > 1) - REGEX_COMPILE_FAIL("unclosed group '('"); - return OK; + return count; } -Ref<RegExMatch> RegEx::search(const String &p_text, int p_start, int p_end) const { - - ERR_FAIL_COND_V(!is_valid(), NULL); - ERR_FAIL_COND_V(p_start < 0, NULL); - ERR_FAIL_COND_V(p_start >= p_text.length(), NULL); - ERR_FAIL_COND_V(p_end > p_text.length(), NULL); - ERR_FAIL_COND_V(p_end != -1 && p_end < p_start, NULL); +Array RegEx::get_names() const { - Ref<RegExMatch> res = memnew(RegExMatch()); + Array result; - for (int i = 0; i < group_names.size(); ++i) { - RegExMatch::Group group; - group.name = group_names[i]; - res->captures.push_back(group); - } + ERR_FAIL_COND_V(!is_valid(), result); - res->string = p_text; + uint32_t count; + const CharType *table; + uint32_t entry_size; - if (p_end == -1) - p_end = p_text.length(); + _pattern_info(PCRE2_INFO_NAMECOUNT, &count); + _pattern_info(PCRE2_INFO_NAMETABLE, &table); + _pattern_info(PCRE2_INFO_NAMEENTRYSIZE, &entry_size); - RegExSearch s(res, p_end, lookahead_depth); + for (int i = 0; i < count; i++) { - for (int i = p_start; i <= s.end; ++i) { - for (int c = 0; c < group_names.size(); ++c) { - res->captures[c].start = -1; - res->captures[c].length = 0; + String name = &table[i * entry_size + 1]; + if (result.find(name) < 0) { + result.append(name); } - if (root->test(s, i) >= 0) - break; } - if (res->captures[0].start >= 0) - return res; - return NULL; + return result; } -String RegEx::sub(const String &p_text, const String &p_replacement, bool p_all, int p_start, int p_end) const { - - ERR_FAIL_COND_V(!is_valid(), p_text); - ERR_FAIL_COND_V(p_start < 0, p_text); - ERR_FAIL_COND_V(p_start >= p_text.length(), p_text); - ERR_FAIL_COND_V(p_end > p_text.length(), p_text); - ERR_FAIL_COND_V(p_end != -1 && p_end < p_start, p_text); - - String text = p_text; - int start = p_start; - - if (p_end == -1) - p_end = p_text.length(); - - while (start < text.length() && (p_all || start == p_start)) { - - Ref<RegExMatch> m = search(text, start, p_end); - - RegExMatch::Group &s = m->captures[0]; - - if (s.start < 0) - break; - - String res = text.substr(0, s.start) + m->expand(p_replacement); - - start = res.length(); +RegEx::RegEx() { - if (s.length == 0) - ++start; + if (sizeof(CharType) == 2) { - int sub_end = s.start + s.length; - if (sub_end < text.length()) - res += text.substr(sub_end, text.length() - sub_end); + general_ctx = pcre2_general_context_create_16(&_regex_malloc, &_regex_free, NULL); - p_end += res.length() - text.length(); + } else { - text = res; + general_ctx = pcre2_general_context_create_32(&_regex_malloc, &_regex_free, NULL); } - return text; + code = NULL; } -void RegEx::clear() { - - if (root) - memdelete(root); - - root = NULL; - group_names.clear(); - lookahead_depth = 0; -} - -bool RegEx::is_valid() const { - - return (root != NULL); -} - -String RegEx::get_pattern() const { - - return pattern; -} - -int RegEx::get_group_count() const { - - int count = 0; - for (int i = 1; i < group_names.size(); ++i) - if (group_names[i].get_type() == Variant::INT) - ++count; - return count; -} - -Array RegEx::get_names() const { +RegEx::RegEx(const String &p_pattern) { - Array res; - for (int i = 1; i < group_names.size(); ++i) - if (group_names[i].get_type() == Variant::STRING) - res.push_back(group_names[i]); - return res; -} + if (sizeof(CharType) == 2) { -RegEx::RegEx() { + general_ctx = pcre2_general_context_create_16(&_regex_malloc, &_regex_free, NULL); - root = NULL; - lookahead_depth = 0; -} + } else { -RegEx::RegEx(const String &p_pattern) { - - root = NULL; + general_ctx = pcre2_general_context_create_32(&_regex_malloc, &_regex_free, NULL); + } + code = NULL; compile(p_pattern); } RegEx::~RegEx() { - if (root) - memdelete(root); -} + if (sizeof(CharType) == 2) { -void RegExMatch::_bind_methods() { + if (code) + pcre2_code_free_16((pcre2_code_16 *)code); + pcre2_general_context_free_16((pcre2_general_context_16 *)general_ctx); - ClassDB::bind_method(D_METHOD("expand", "template"), &RegExMatch::expand); - ClassDB::bind_method(D_METHOD("get_group_count"), &RegExMatch::get_group_count); - ClassDB::bind_method(D_METHOD("get_group_array"), &RegExMatch::get_group_array); - ClassDB::bind_method(D_METHOD("get_names"), &RegExMatch::get_names); - ClassDB::bind_method(D_METHOD("get_name_dict"), &RegExMatch::get_name_dict); - ClassDB::bind_method(D_METHOD("get_string", "name"), &RegExMatch::get_string, DEFVAL(0)); - ClassDB::bind_method(D_METHOD("get_start", "name"), &RegExMatch::get_start, DEFVAL(0)); - ClassDB::bind_method(D_METHOD("get_end", "name"), &RegExMatch::get_end, DEFVAL(0)); + } else { + + if (code) + pcre2_code_free_32((pcre2_code_32 *)code); + pcre2_general_context_free_32((pcre2_general_context_32 *)general_ctx); + } } void RegEx::_bind_methods() { ClassDB::bind_method(D_METHOD("clear"), &RegEx::clear); ClassDB::bind_method(D_METHOD("compile", "pattern"), &RegEx::compile); - ClassDB::bind_method(D_METHOD("search", "text", "start", "end"), &RegEx::search, DEFVAL(0), DEFVAL(-1)); - ClassDB::bind_method(D_METHOD("sub", "text", "replacement", "all", "start", "end"), &RegEx::sub, DEFVAL(false), DEFVAL(0), DEFVAL(-1)); + ClassDB::bind_method(D_METHOD("search", "subject", "offset", "end"), &RegEx::search, DEFVAL(0), DEFVAL(-1)); + ClassDB::bind_method(D_METHOD("sub", "subject", "replacement", "all", "offset", "end"), &RegEx::sub, DEFVAL(false), DEFVAL(0), DEFVAL(-1)); ClassDB::bind_method(D_METHOD("is_valid"), &RegEx::is_valid); ClassDB::bind_method(D_METHOD("get_pattern"), &RegEx::get_pattern); ClassDB::bind_method(D_METHOD("get_group_count"), &RegEx::get_group_count); ClassDB::bind_method(D_METHOD("get_names"), &RegEx::get_names); - - ADD_PROPERTY(PropertyInfo(Variant::STRING, "pattern"), "compile", "get_pattern"); } diff --git a/modules/regex/regex.h b/modules/regex/regex.h index 8c76035b82..0d97bcce54 100644 --- a/modules/regex/regex.h +++ b/modules/regex/regex.h @@ -31,59 +31,53 @@ #ifndef REGEX_H #define REGEX_H +#include "core/array.h" #include "core/dictionary.h" +#include "core/map.h" #include "core/reference.h" -#include "core/resource.h" #include "core/ustring.h" #include "core/vector.h" -class RegExNode; - class RegExMatch : public Reference { GDCLASS(RegExMatch, Reference); - struct Group { - Variant name; + struct Range { int start; - int length; + int end; }; - Vector<Group> captures; - String string; + String subject; + Vector<Range> data; + Map<String, int> names; friend class RegEx; - friend class RegExSearch; - friend class RegExNodeCapturing; - friend class RegExNodeBackReference; protected: static void _bind_methods(); -public: - String expand(const String &p_template) const; + int _find(const Variant &p_name) const; +public: + String get_subject() const; int get_group_count() const; - Array get_group_array() const; - - Array get_names() const; - Dictionary get_name_dict() const; + Dictionary get_names() const; + Array get_strings() const; String get_string(const Variant &p_name) const; int get_start(const Variant &p_name) const; int get_end(const Variant &p_name) const; - - RegExMatch(); }; -class RegEx : public Resource { +class RegEx : public Reference { - GDCLASS(RegEx, Resource); + GDCLASS(RegEx, Reference); - RegExNode *root; - Vector<Variant> group_names; + void *general_ctx; + void *code; String pattern; - int lookahead_depth; + + void _pattern_info(uint32_t what, void *where) const; protected: static void _bind_methods(); @@ -91,9 +85,10 @@ protected: public: void clear(); Error compile(const String &p_pattern); + void _init(const String &p_pattern = ""); - Ref<RegExMatch> search(const String &p_text, int p_start = 0, int p_end = -1) const; - String sub(const String &p_text, const String &p_replacement, bool p_all = false, int p_start = 0, int p_end = -1) const; + Ref<RegExMatch> search(const String &p_subject, int offset = 0, int end = -1) const; + String sub(const String &p_subject, const String &p_replacement, bool p_all = false, int p_start = 0, int p_end = -1) const; bool is_valid() const; String get_pattern() const; diff --git a/modules/svg/image_loader_svg.cpp b/modules/svg/image_loader_svg.cpp index 086cc202f9..f5c379d32e 100644 --- a/modules/svg/image_loader_svg.cpp +++ b/modules/svg/image_loader_svg.cpp @@ -47,7 +47,49 @@ SVGRasterizer::~SVGRasterizer() { SVGRasterizer ImageLoaderSVG::rasterizer; -Error ImageLoaderSVG::_create_image(Ref<Image> p_image, const PoolVector<uint8_t> *p_data, float p_scale, bool upsample) { +inline void change_nsvg_paint_color(NSVGpaint *p_paint, const uint32_t p_old, const uint32_t p_new) { + + if (p_paint->type == NSVG_PAINT_COLOR) { + if (p_paint->color << 8 == p_old << 8) { + p_paint->color = p_new; + } + } + + if (p_paint->type == NSVG_PAINT_LINEAR_GRADIENT || p_paint->type == NSVG_PAINT_RADIAL_GRADIENT) { + for (int stop_index = 0; stop_index < p_paint->gradient->nstops; stop_index++) { + if (p_paint->gradient->stops[stop_index].color << 8 == p_old << 8) { + p_paint->gradient->stops[stop_index].color = p_new; + } + } + } +} +void ImageLoaderSVG::_convert_colors(NSVGimage *p_svg_image, const Dictionary p_colors) { + List<uint32_t> replace_colors_i; + List<uint32_t> new_colors_i; + List<Color> replace_colors; + List<Color> new_colors; + + for (int i = 0; i < p_colors.keys().size(); i++) { + Variant r_c = p_colors.keys()[i]; + Variant n_c = p_colors[p_colors.keys()[i]]; + if (r_c.get_type() == Variant::COLOR && n_c.get_type() == Variant::COLOR) { + Color replace_color = r_c; + Color new_color = n_c; + replace_colors_i.push_back(replace_color.to_ABGR32()); + new_colors_i.push_back(new_color.to_ABGR32()); + } + } + + for (NSVGshape *shape = p_svg_image->shapes; shape != NULL; shape = shape->next) { + + for (int i = 0; i < replace_colors_i.size(); i++) { + change_nsvg_paint_color(&(shape->stroke), replace_colors_i[i], new_colors_i[i]); + change_nsvg_paint_color(&(shape->fill), replace_colors_i[i], new_colors_i[i]); + } + } +} + +Error ImageLoaderSVG::_create_image(Ref<Image> p_image, const PoolVector<uint8_t> *p_data, float p_scale, bool upsample, const Dictionary *p_replace_colors) { NSVGimage *svg_image; PoolVector<uint8_t>::Read src_r = p_data->read(); svg_image = nsvgParse((char *)src_r.ptr(), "px", 96); @@ -56,6 +98,9 @@ Error ImageLoaderSVG::_create_image(Ref<Image> p_image, const PoolVector<uint8_t return ERR_FILE_CORRUPT; } + if (p_replace_colors != NULL) { + _convert_colors(svg_image, *p_replace_colors); + } float upscale = upsample ? 2.0 : 1.0; int w = (int)(svg_image->width * p_scale * upscale); @@ -78,7 +123,7 @@ Error ImageLoaderSVG::_create_image(Ref<Image> p_image, const PoolVector<uint8_t return OK; } -Error ImageLoaderSVG::create_image_from_string(Ref<Image> p_image, const char *svg_str, float p_scale, bool upsample) { +Error ImageLoaderSVG::create_image_from_string(Ref<Image> p_image, const char *svg_str, float p_scale, bool upsample, const Dictionary *p_replace_colors) { size_t str_len = strlen(svg_str); PoolVector<uint8_t> src_data; @@ -86,7 +131,7 @@ Error ImageLoaderSVG::create_image_from_string(Ref<Image> p_image, const char *s PoolVector<uint8_t>::Write src_w = src_data.write(); memcpy(src_w.ptr(), svg_str, str_len + 1); - return _create_image(p_image, &src_data, p_scale, upsample); + return _create_image(p_image, &src_data, p_scale, upsample, p_replace_colors); } Error ImageLoaderSVG::load_image(Ref<Image> p_image, FileAccess *f, bool p_force_linear, float p_scale) { diff --git a/modules/svg/image_loader_svg.h b/modules/svg/image_loader_svg.h index 1f2ec3c1c2..f692b1b28c 100644 --- a/modules/svg/image_loader_svg.h +++ b/modules/svg/image_loader_svg.h @@ -54,14 +54,15 @@ public: class ImageLoaderSVG : public ImageFormatLoader { static SVGRasterizer rasterizer; - static Error _create_image(Ref<Image> p_image, const PoolVector<uint8_t> *p_data, float p_scale, bool upsample); + static void _convert_colors(NSVGimage *p_svg_imge, const Dictionary p_colors); + static Error _create_image(Ref<Image> p_image, const PoolVector<uint8_t> *p_data, float p_scale, bool upsample, const Dictionary *p_replace_color = NULL); public: - static Error create_image_from_string(Ref<Image> p_image, const char *p_svg_str, float p_scale, bool upsample); + static Error create_image_from_string(Ref<Image> p_image, const char *p_svg_str, float p_scale, bool upsample, const Dictionary *p_replace_color = NULL); virtual Error load_image(Ref<Image> p_image, FileAccess *f, bool p_force_linear, float p_scale); virtual void get_recognized_extensions(List<String> *p_extensions) const; ImageLoaderSVG(); }; -#endif
\ No newline at end of file +#endif diff --git a/modules/tga/image_loader_tga.cpp b/modules/tga/image_loader_tga.cpp index 379c894550..84332ee2ff 100644 --- a/modules/tga/image_loader_tga.cpp +++ b/modules/tga/image_loader_tga.cpp @@ -1,5 +1,5 @@ /*************************************************************************/ -/* image_loader_jpegd.cpp */ +/* image_loader_tga.cpp */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ diff --git a/modules/tga/image_loader_tga.h b/modules/tga/image_loader_tga.h index 8689a1773b..7905ab37a7 100644 --- a/modules/tga/image_loader_tga.h +++ b/modules/tga/image_loader_tga.h @@ -1,5 +1,5 @@ /*************************************************************************/ -/* image_loader_jpegd.h */ +/* image_loader_tga.h */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ diff --git a/modules/tinyexr/image_loader_tinyexr.cpp b/modules/tinyexr/image_loader_tinyexr.cpp index 57826d69fb..49a4db237a 100644 --- a/modules/tinyexr/image_loader_tinyexr.cpp +++ b/modules/tinyexr/image_loader_tinyexr.cpp @@ -1,5 +1,5 @@ /*************************************************************************/ -/* image_loader_jpegd.cpp */ +/* image_loader_tinyexr.cpp */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ diff --git a/modules/tinyexr/image_loader_tinyexr.h b/modules/tinyexr/image_loader_tinyexr.h index adecba5d9d..53a81597af 100644 --- a/modules/tinyexr/image_loader_tinyexr.h +++ b/modules/tinyexr/image_loader_tinyexr.h @@ -1,5 +1,5 @@ /*************************************************************************/ -/* image_loader_jpegd.h */ +/* image_loader_tinyexr.h */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ diff --git a/modules/visual_script/visual_script_editor.cpp b/modules/visual_script/visual_script_editor.cpp index 7ab2a93b55..8386687c9f 100644 --- a/modules/visual_script/visual_script_editor.cpp +++ b/modules/visual_script/visual_script_editor.cpp @@ -1,5 +1,5 @@ /*************************************************************************/ -/* visual_script_editor.h */ +/* visual_script_editor.cpp */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -886,8 +886,8 @@ void VisualScriptEditor::_member_edited() { undo_redo->add_undo_method(this, "_update_members"); undo_redo->add_do_method(this, "_update_graph"); undo_redo->add_undo_method(this, "_update_graph"); - undo_redo->add_do_method(this, "emit_signal", "script_changed"); - undo_redo->add_undo_method(this, "emit_signal", "script_changed"); + undo_redo->add_do_method(this, "emit_signal", "edited_script_changed"); + undo_redo->add_undo_method(this, "emit_signal", "edited_script_changed"); undo_redo->commit_action(); // _update_graph(); @@ -903,8 +903,8 @@ void VisualScriptEditor::_member_edited() { undo_redo->add_undo_method(script.ptr(), "rename_variable", new_name, name); undo_redo->add_do_method(this, "_update_members"); undo_redo->add_undo_method(this, "_update_members"); - undo_redo->add_do_method(this, "emit_signal", "script_changed"); - undo_redo->add_undo_method(this, "emit_signal", "script_changed"); + undo_redo->add_do_method(this, "emit_signal", "edited_script_changed"); + undo_redo->add_undo_method(this, "emit_signal", "edited_script_changed"); undo_redo->commit_action(); return; //or crash because it will become invalid @@ -918,8 +918,8 @@ void VisualScriptEditor::_member_edited() { undo_redo->add_undo_method(script.ptr(), "rename_custom_signal", new_name, name); undo_redo->add_do_method(this, "_update_members"); undo_redo->add_undo_method(this, "_update_members"); - undo_redo->add_do_method(this, "emit_signal", "script_changed"); - undo_redo->add_undo_method(this, "emit_signal", "script_changed"); + undo_redo->add_do_method(this, "emit_signal", "edited_script_changed"); + undo_redo->add_undo_method(this, "emit_signal", "edited_script_changed"); undo_redo->commit_action(); return; //or crash because it will become invalid @@ -1057,8 +1057,8 @@ void VisualScriptEditor::_member_button(Object *p_item, int p_column, int p_butt undo_redo->add_undo_method(this, "_update_members"); undo_redo->add_do_method(this, "_update_graph"); undo_redo->add_undo_method(this, "_update_graph"); - undo_redo->add_do_method(this, "emit_signal", "script_changed"); - undo_redo->add_undo_method(this, "emit_signal", "script_changed"); + undo_redo->add_do_method(this, "emit_signal", "edited_script_changed"); + undo_redo->add_undo_method(this, "emit_signal", "edited_script_changed"); undo_redo->commit_action(); _update_graph(); @@ -1077,8 +1077,8 @@ void VisualScriptEditor::_member_button(Object *p_item, int p_column, int p_butt undo_redo->add_undo_method(script.ptr(), "remove_variable", name); undo_redo->add_do_method(this, "_update_members"); undo_redo->add_undo_method(this, "_update_members"); - undo_redo->add_do_method(this, "emit_signal", "script_changed"); - undo_redo->add_undo_method(this, "emit_signal", "script_changed"); + undo_redo->add_do_method(this, "emit_signal", "edited_script_changed"); + undo_redo->add_undo_method(this, "emit_signal", "edited_script_changed"); undo_redo->commit_action(); return; //or crash because it will become invalid } @@ -1093,8 +1093,8 @@ void VisualScriptEditor::_member_button(Object *p_item, int p_column, int p_butt undo_redo->add_undo_method(script.ptr(), "remove_custom_signal", name); undo_redo->add_do_method(this, "_update_members"); undo_redo->add_undo_method(this, "_update_members"); - undo_redo->add_do_method(this, "emit_signal", "script_changed"); - undo_redo->add_undo_method(this, "emit_signal", "script_changed"); + undo_redo->add_do_method(this, "emit_signal", "edited_script_changed"); + undo_redo->add_undo_method(this, "emit_signal", "edited_script_changed"); undo_redo->commit_action(); return; //or crash because it will become invalid } diff --git a/modules/visual_script/visual_script_nodes.cpp b/modules/visual_script/visual_script_nodes.cpp index af7b334e46..1decc004ab 100644 --- a/modules/visual_script/visual_script_nodes.cpp +++ b/modules/visual_script/visual_script_nodes.cpp @@ -2702,7 +2702,10 @@ void VisualScriptCustomNode::_bind_methods() { BIND_VMETHOD(MethodInfo(Variant::STRING, "_get_category")); BIND_VMETHOD(MethodInfo(Variant::INT, "_get_working_memory_size")); - BIND_VMETHOD(MethodInfo(Variant::NIL, "_step:Variant", PropertyInfo(Variant::ARRAY, "inputs"), PropertyInfo(Variant::ARRAY, "outputs"), PropertyInfo(Variant::INT, "start_mode"), PropertyInfo(Variant::ARRAY, "working_mem"))); + + MethodInfo stepmi(Variant::NIL, "_step", PropertyInfo(Variant::ARRAY, "inputs"), PropertyInfo(Variant::ARRAY, "outputs"), PropertyInfo(Variant::INT, "start_mode"), PropertyInfo(Variant::ARRAY, "working_mem")); + stepmi.return_val.usage |= PROPERTY_USAGE_NIL_IS_VARIANT; + BIND_VMETHOD(stepmi); ClassDB::bind_method(D_METHOD("_script_changed"), &VisualScriptCustomNode::_script_changed); @@ -2839,7 +2842,9 @@ VisualScriptNodeInstance *VisualScriptSubCall::instance(VisualScriptInstance *p_ void VisualScriptSubCall::_bind_methods() { - BIND_VMETHOD(MethodInfo(Variant::NIL, "_subcall:Variant", PropertyInfo(Variant::NIL, "arguments:Variant"))); + MethodInfo scmi(Variant::NIL, "_subcall", PropertyInfo(Variant::NIL, "arguments")); + scmi.return_val.usage |= PROPERTY_USAGE_NIL_IS_VARIANT; + BIND_VMETHOD(scmi); } VisualScriptSubCall::VisualScriptSubCall() { diff --git a/modules/visual_script/visual_script_nodes.h b/modules/visual_script/visual_script_nodes.h index 694fb96cfa..421409b265 100644 --- a/modules/visual_script/visual_script_nodes.h +++ b/modules/visual_script/visual_script_nodes.h @@ -1,5 +1,5 @@ /*************************************************************************/ -/* visual_script_nodes.h */ +/* visual_script_nodes.h */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ diff --git a/modules/webm/video_stream_webm.cpp b/modules/webm/video_stream_webm.cpp index 26344877de..0178ebab84 100644 --- a/modules/webm/video_stream_webm.cpp +++ b/modules/webm/video_stream_webm.cpp @@ -1,5 +1,5 @@ /*************************************************************************/ -/* av_stream_webm.cpp.cpp */ +/* video_stream_webm.cpp */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ diff --git a/modules/webm/video_stream_webm.h b/modules/webm/video_stream_webm.h index a48a6c6b9a..9a331849be 100644 --- a/modules/webm/video_stream_webm.h +++ b/modules/webm/video_stream_webm.h @@ -1,5 +1,5 @@ /*************************************************************************/ -/* av_stream_webm.cpp.cpp */ +/* video_stream_webm.h */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ |