summaryrefslogtreecommitdiffstats
path: root/core
diff options
context:
space:
mode:
Diffstat (limited to 'core')
-rw-r--r--core/SCsub18
-rw-r--r--core/config/engine.cpp42
-rw-r--r--core/config/engine.h19
-rw-r--r--core/config/project_settings.cpp65
-rw-r--r--core/config/project_settings.h8
-rw-r--r--core/core_bind.cpp15
-rw-r--r--core/core_bind.h1
-rw-r--r--core/core_constants.cpp8
-rw-r--r--core/crypto/crypto_core.cpp2
-rw-r--r--core/crypto/hashing_context.cpp6
-rw-r--r--core/debugger/engine_profiler.h4
-rw-r--r--core/debugger/remote_debugger.cpp4
-rw-r--r--core/extension/extension_api_dump.cpp236
-rw-r--r--core/extension/extension_api_dump.h4
-rw-r--r--core/extension/gdextension.cpp618
-rw-r--r--core/extension/gdextension.h63
-rw-r--r--core/extension/gdextension_compat_hashes.cpp843
-rw-r--r--core/extension/gdextension_compat_hashes.h57
-rw-r--r--core/extension/gdextension_interface.cpp321
-rw-r--r--core/extension/gdextension_interface.h316
-rw-r--r--core/extension/gdextension_manager.cpp173
-rw-r--r--core/extension/gdextension_manager.h15
-rw-r--r--core/input/gamecontrollerdb.txt199
-rw-r--r--core/input/godotcontrollerdb.txt3
-rw-r--r--core/input/input.cpp78
-rw-r--r--core/input/input.h3
-rw-r--r--core/input/input_builders.py1
-rw-r--r--core/input/input_event.cpp26
-rw-r--r--core/input/input_event.h2
-rw-r--r--core/io/compression.cpp2
-rw-r--r--core/io/dir_access.cpp6
-rw-r--r--core/io/dir_access.h2
-rw-r--r--core/io/file_access.cpp74
-rw-r--r--core/io/file_access.h38
-rw-r--r--core/io/file_access_compressed.cpp32
-rw-r--r--core/io/file_access_compressed.h9
-rw-r--r--core/io/file_access_encrypted.cpp41
-rw-r--r--core/io/file_access_encrypted.h9
-rw-r--r--core/io/file_access_memory.cpp16
-rw-r--r--core/io/file_access_memory.h9
-rw-r--r--core/io/file_access_pack.h9
-rw-r--r--core/io/file_access_zip.cpp24
-rw-r--r--core/io/file_access_zip.h9
-rw-r--r--core/io/image.cpp49
-rw-r--r--core/io/image.h4
-rw-r--r--core/io/ip.cpp2
-rw-r--r--core/io/marshalls.cpp6
-rw-r--r--core/io/packed_data_container.cpp6
-rw-r--r--core/io/packet_peer.h1
-rw-r--r--core/io/pck_packer.cpp4
-rw-r--r--core/io/remote_filesystem_client.cpp2
-rw-r--r--core/io/remote_filesystem_client.h4
-rw-r--r--core/io/resource.cpp8
-rw-r--r--core/io/resource.h4
-rw-r--r--core/io/resource_format_binary.cpp1
-rw-r--r--core/io/resource_loader.cpp3
-rw-r--r--core/io/resource_loader.h7
-rw-r--r--core/io/resource_saver.cpp2
-rw-r--r--core/io/resource_saver.h1
-rw-r--r--core/io/stream_peer.h1
-rw-r--r--core/io/stream_peer_gzip.cpp4
-rw-r--r--core/io/xml_parser.cpp4
-rw-r--r--core/io/zip_io.cpp14
-rw-r--r--core/math/a_star.h1
-rw-r--r--core/math/a_star_grid_2d.cpp80
-rw-r--r--core/math/a_star_grid_2d.h20
-rw-r--r--core/math/basis.cpp8
-rw-r--r--core/math/basis.h1
-rw-r--r--core/math/bvh_refit.inc2
-rw-r--r--core/math/bvh_split.inc18
-rw-r--r--core/math/bvh_structs.inc2
-rw-r--r--core/math/convex_hull.cpp2
-rw-r--r--core/math/delaunay_3d.h13
-rw-r--r--core/math/disjoint_set.h2
-rw-r--r--core/math/dynamic_bvh.h2
-rw-r--r--core/math/math_funcs.h42
-rw-r--r--core/math/projection.cpp1
-rw-r--r--core/math/transform_2d.cpp12
-rw-r--r--core/math/transform_2d.h1
-rw-r--r--core/object/callable_method_pointer.h6
-rw-r--r--core/object/class_db.cpp183
-rw-r--r--core/object/class_db.h37
-rw-r--r--core/object/make_virtuals.py43
-rw-r--r--core/object/message_queue.cpp4
-rw-r--r--core/object/method_bind.cpp8
-rw-r--r--core/object/method_bind.h4
-rw-r--r--core/object/object.cpp226
-rw-r--r--core/object/object.h41
-rw-r--r--core/object/ref_counted.h2
-rw-r--r--core/object/script_instance.cpp71
-rw-r--r--core/object/script_instance.h98
-rw-r--r--core/object/script_language.cpp37
-rw-r--r--core/object/script_language.h75
-rw-r--r--core/object/script_language_extension.cpp6
-rw-r--r--core/object/script_language_extension.h82
-rw-r--r--core/object/undo_redo.cpp73
-rw-r--r--core/object/undo_redo.h2
-rw-r--r--core/object/worker_thread_pool.cpp15
-rw-r--r--core/os/keyboard.cpp3
-rw-r--r--core/os/keyboard.h2
-rw-r--r--core/os/main_loop.h1
-rw-r--r--core/os/memory.cpp6
-rw-r--r--core/os/memory.h2
-rw-r--r--core/os/os.cpp2
-rw-r--r--core/os/os.h4
-rw-r--r--core/os/pool_allocator.cpp14
-rw-r--r--core/string/node_path.cpp8
-rw-r--r--core/string/print_string.cpp2
-rw-r--r--core/string/string_name.cpp4
-rw-r--r--core/string/translation.h1
-rw-r--r--core/string/ustring.cpp81
-rw-r--r--core/string/ustring.h1
-rw-r--r--core/templates/command_queue_mt.h2
-rw-r--r--core/templates/cowdata.h13
-rw-r--r--core/templates/list.h2
-rw-r--r--core/templates/paged_allocator.h2
-rw-r--r--core/templates/paged_array.h6
-rw-r--r--core/templates/rb_map.h2
-rw-r--r--core/templates/rid_owner.h6
-rw-r--r--core/templates/safe_refcount.h6
-rw-r--r--core/templates/self_list.h51
-rw-r--r--core/typedefs.h2
-rw-r--r--core/variant/array.cpp4
-rw-r--r--core/variant/binder_common.h56
-rw-r--r--core/variant/callable.cpp15
-rw-r--r--core/variant/callable.h2
-rw-r--r--core/variant/callable_bind.cpp10
-rw-r--r--core/variant/container_type_validate.h2
-rw-r--r--core/variant/dictionary.cpp4
-rw-r--r--core/variant/method_ptrcall.h44
-rw-r--r--core/variant/typed_array.h6
-rw-r--r--core/variant/variant.cpp29
-rw-r--r--core/variant/variant.h17
-rw-r--r--core/variant/variant_call.cpp49
-rw-r--r--core/variant/variant_internal.h2
-rw-r--r--core/variant/variant_op.h4
-rw-r--r--core/variant/variant_parser.cpp58
-rw-r--r--core/variant/variant_parser.h2
-rw-r--r--core/variant/variant_setget.cpp6
-rw-r--r--core/variant/variant_utility.cpp276
-rw-r--r--core/variant/variant_utility.h6
141 files changed, 4678 insertions, 914 deletions
diff --git a/core/SCsub b/core/SCsub
index ab78eeedc7..3b1a7ca79a 100644
--- a/core/SCsub
+++ b/core/SCsub
@@ -89,6 +89,24 @@ if env["brotli"] and env["builtin_brotli"]:
env_thirdparty.add_source_files(thirdparty_obj, thirdparty_brotli_sources)
+# Clipper2 Thirdparty source files used for polygon and polyline boolean operations.
+if env["builtin_clipper2"]:
+ thirdparty_clipper_dir = "#thirdparty/clipper2/"
+ thirdparty_clipper_sources = [
+ "src/clipper.engine.cpp",
+ "src/clipper.offset.cpp",
+ "src/clipper.rectclip.cpp",
+ ]
+ thirdparty_clipper_sources = [thirdparty_clipper_dir + file for file in thirdparty_clipper_sources]
+
+ env_thirdparty.Prepend(CPPPATH=[thirdparty_clipper_dir + "include"])
+ env.Prepend(CPPPATH=[thirdparty_clipper_dir + "include"])
+
+ env_thirdparty.Append(CPPDEFINES=["CLIPPER2_ENABLED"])
+ env.Append(CPPDEFINES=["CLIPPER2_ENABLED"])
+
+ env_thirdparty.add_source_files(thirdparty_obj, thirdparty_clipper_sources)
+
# Zlib library, can be unbundled
if env["builtin_zlib"]:
thirdparty_zlib_dir = "#thirdparty/zlib/"
diff --git a/core/config/engine.cpp b/core/config/engine.cpp
index 7fdea7d1aa..0e27d556ec 100644
--- a/core/config/engine.cpp
+++ b/core/config/engine.cpp
@@ -74,6 +74,14 @@ int Engine::get_max_fps() const {
return _max_fps;
}
+void Engine::set_audio_output_latency(int p_msec) {
+ _audio_output_latency = p_msec > 1 ? p_msec : 1;
+}
+
+int Engine::get_audio_output_latency() const {
+ return _audio_output_latency;
+}
+
uint64_t Engine::get_frames_drawn() {
return frames_drawn;
}
@@ -239,6 +247,10 @@ bool Engine::is_validation_layers_enabled() const {
return use_validation_layers;
}
+bool Engine::is_generate_spirv_debug_info_enabled() const {
+ return generate_spirv_debug_info;
+}
+
void Engine::set_print_error_messages(bool p_enabled) {
CoreGlobals::print_error_enabled = p_enabled;
}
@@ -248,14 +260,21 @@ bool Engine::is_printing_error_messages() const {
}
void Engine::add_singleton(const Singleton &p_singleton) {
- ERR_FAIL_COND_MSG(singleton_ptrs.has(p_singleton.name), "Can't register singleton that already exists: " + String(p_singleton.name));
+ ERR_FAIL_COND_MSG(singleton_ptrs.has(p_singleton.name), vformat("Can't register singleton '%s' because it already exists.", p_singleton.name));
singletons.push_back(p_singleton);
singleton_ptrs[p_singleton.name] = p_singleton.ptr;
}
Object *Engine::get_singleton_object(const StringName &p_name) const {
HashMap<StringName, Object *>::ConstIterator E = singleton_ptrs.find(p_name);
- ERR_FAIL_COND_V_MSG(!E, nullptr, "Failed to retrieve non-existent singleton '" + String(p_name) + "'.");
+ ERR_FAIL_COND_V_MSG(!E, nullptr, vformat("Failed to retrieve non-existent singleton '%s'.", p_name));
+
+#ifdef TOOLS_ENABLED
+ if (!is_editor_hint() && is_singleton_editor_only(p_name)) {
+ ERR_FAIL_V_MSG(nullptr, vformat("Can't retrieve singleton '%s' outside of editor.", p_name));
+ }
+#endif
+
return E->value;
}
@@ -270,6 +289,19 @@ bool Engine::is_singleton_user_created(const StringName &p_name) const {
return false;
}
+
+bool Engine::is_singleton_editor_only(const StringName &p_name) const {
+ ERR_FAIL_COND_V(!singleton_ptrs.has(p_name), false);
+
+ for (const Singleton &E : singletons) {
+ if (E.name == p_name && E.editor_only) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
void Engine::remove_singleton(const StringName &p_name) {
ERR_FAIL_COND(!singleton_ptrs.has(p_name));
@@ -288,6 +320,12 @@ bool Engine::has_singleton(const StringName &p_name) const {
void Engine::get_singletons(List<Singleton> *p_singletons) {
for (const Singleton &E : singletons) {
+#ifdef TOOLS_ENABLED
+ if (!is_editor_hint() && E.editor_only) {
+ continue;
+ }
+#endif
+
p_singletons->push_back(E);
}
}
diff --git a/core/config/engine.h b/core/config/engine.h
index 5ea653ba6c..b64309a9e8 100644
--- a/core/config/engine.h
+++ b/core/config/engine.h
@@ -44,8 +44,11 @@ public:
struct Singleton {
StringName name;
Object *ptr = nullptr;
- StringName class_name; //used for binding generation hinting
+ StringName class_name; // Used for binding generation hinting.
+ // Singleton scope flags.
bool user_created = false;
+ bool editor_only = false;
+
Singleton(const StringName &p_name = StringName(), Object *p_ptr = nullptr, const StringName &p_class_name = StringName());
};
@@ -61,12 +64,14 @@ private:
double physics_jitter_fix = 0.5;
double _fps = 1;
int _max_fps = 0;
+ int _audio_output_latency = 0;
double _time_scale = 1.0;
uint64_t _physics_frames = 0;
int max_physics_steps_per_frame = 8;
double _physics_interpolation_fraction = 0.0f;
bool abort_on_gpu_errors = false;
bool use_validation_layers = false;
+ bool generate_spirv_debug_info = false;
int32_t gpu_idx = -1;
uint64_t _process_frames = 0;
@@ -77,6 +82,7 @@ private:
bool editor_hint = false;
bool project_manager_hint = false;
+ bool extension_reloading = false;
static Engine *singleton;
@@ -98,6 +104,9 @@ public:
virtual void set_max_fps(int p_fps);
virtual int get_max_fps() const;
+ virtual void set_audio_output_latency(int p_msec);
+ virtual int get_audio_output_latency() const;
+
virtual double get_frames_per_second() const { return _fps; }
uint64_t get_frames_drawn();
@@ -124,6 +133,7 @@ public:
Object *get_singleton_object(const StringName &p_name) const;
void remove_singleton(const StringName &p_name);
bool is_singleton_user_created(const StringName &p_name) const;
+ bool is_singleton_editor_only(const StringName &p_name) const;
#ifdef TOOLS_ENABLED
_FORCE_INLINE_ void set_editor_hint(bool p_enabled) { editor_hint = p_enabled; }
@@ -131,12 +141,18 @@ public:
_FORCE_INLINE_ void set_project_manager_hint(bool p_enabled) { project_manager_hint = p_enabled; }
_FORCE_INLINE_ bool is_project_manager_hint() const { return project_manager_hint; }
+
+ _FORCE_INLINE_ void set_extension_reloading_enabled(bool p_enabled) { extension_reloading = p_enabled; }
+ _FORCE_INLINE_ bool is_extension_reloading_enabled() const { return extension_reloading; }
#else
_FORCE_INLINE_ void set_editor_hint(bool p_enabled) {}
_FORCE_INLINE_ bool is_editor_hint() const { return false; }
_FORCE_INLINE_ void set_project_manager_hint(bool p_enabled) {}
_FORCE_INLINE_ bool is_project_manager_hint() const { return false; }
+
+ _FORCE_INLINE_ void set_extension_reloading_enabled(bool p_enabled) {}
+ _FORCE_INLINE_ bool is_extension_reloading_enabled() const { return false; }
#endif
Dictionary get_version_info() const;
@@ -156,6 +172,7 @@ public:
bool is_abort_on_gpu_errors_enabled() const;
bool is_validation_layers_enabled() const;
+ bool is_generate_spirv_debug_info_enabled() const;
int32_t get_gpu_index() const;
Engine();
diff --git a/core/config/project_settings.cpp b/core/config/project_settings.cpp
index 973162a066..cbbfe3de75 100644
--- a/core/config/project_settings.cpp
+++ b/core/config/project_settings.cpp
@@ -146,30 +146,30 @@ const PackedStringArray ProjectSettings::_trim_to_supported_features(const Packe
#endif // TOOLS_ENABLED
String ProjectSettings::localize_path(const String &p_path) const {
- if (resource_path.is_empty() || (p_path.is_absolute_path() && !p_path.begins_with(resource_path))) {
- return p_path.simplify_path();
+ String path = p_path.simplify_path();
+
+ if (resource_path.is_empty() || (path.is_absolute_path() && !path.begins_with(resource_path))) {
+ return path;
}
// Check if we have a special path (like res://) or a protocol identifier.
- int p = p_path.find("://");
+ int p = path.find("://");
bool found = false;
if (p > 0) {
found = true;
for (int i = 0; i < p; i++) {
- if (!is_ascii_alphanumeric_char(p_path[i])) {
+ if (!is_ascii_alphanumeric_char(path[i])) {
found = false;
break;
}
}
}
if (found) {
- return p_path.simplify_path();
+ return path;
}
Ref<DirAccess> dir = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
- String path = p_path.replace("\\", "/").simplify_path();
-
if (dir->change_dir(path) == OK) {
String cwd = dir->get_current_dir();
cwd = cwd.replace("\\", "/");
@@ -187,7 +187,7 @@ String ProjectSettings::localize_path(const String &p_path) const {
cwd = cwd.path_join("");
if (!cwd.begins_with(res_path)) {
- return p_path;
+ return path;
}
return cwd.replace_first(res_path, "res://");
@@ -249,6 +249,11 @@ bool ProjectSettings::get_ignore_value_in_docs(const String &p_name) const {
#endif
}
+void ProjectSettings::add_hidden_prefix(const String &p_prefix) {
+ ERR_FAIL_COND_MSG(hidden_prefixes.find(p_prefix) > -1, vformat("Hidden prefix '%s' already exists.", p_prefix));
+ hidden_prefixes.push_back(p_prefix);
+}
+
String ProjectSettings::globalize_path(const String &p_path) const {
if (p_path.begins_with("res://")) {
if (!resource_path.is_empty()) {
@@ -283,6 +288,7 @@ bool ProjectSettings::_set(const StringName &p_name, const Variant &p_value) {
for (int i = 0; i < custom_feature_array.size(); i++) {
custom_features.insert(custom_feature_array[i]);
}
+ _queue_changed();
return true;
}
@@ -324,6 +330,7 @@ bool ProjectSettings::_set(const StringName &p_name, const Variant &p_value) {
}
}
+ _queue_changed();
return true;
}
@@ -386,7 +393,18 @@ void ProjectSettings::_get_property_list(List<PropertyInfo> *p_list) const {
vc.name = E.key;
vc.order = v->order;
vc.type = v->variant.get_type();
- if (v->internal || vc.name.begins_with("input/") || vc.name.begins_with("importer_defaults/") || vc.name.begins_with("import/") || vc.name.begins_with("autoload/") || vc.name.begins_with("editor_plugins/") || vc.name.begins_with("shader_globals/")) {
+
+ bool internal = v->internal;
+ if (!internal) {
+ for (const String &F : hidden_prefixes) {
+ if (vc.name.begins_with(F)) {
+ internal = true;
+ break;
+ }
+ }
+ }
+
+ if (internal) {
vc.flags = PROPERTY_USAGE_STORAGE;
} else {
vc.flags = PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_STORAGE;
@@ -424,6 +442,22 @@ void ProjectSettings::_get_property_list(List<PropertyInfo> *p_list) const {
}
}
+void ProjectSettings::_queue_changed() {
+ if (is_changed || !MessageQueue::get_singleton() || MessageQueue::get_singleton()->get_max_buffer_usage() == 0) {
+ return;
+ }
+ is_changed = true;
+ callable_mp(this, &ProjectSettings::_emit_changed).call_deferred();
+}
+
+void ProjectSettings::_emit_changed() {
+ if (!is_changed) {
+ return;
+ }
+ is_changed = false;
+ emit_signal("settings_changed");
+}
+
bool ProjectSettings::_load_resource_pack(const String &p_pack, bool p_replace_files, int p_offset) {
if (PackedData::get_singleton()->is_disabled()) {
return false;
@@ -928,6 +962,7 @@ Error ProjectSettings::_save_custom_bnd(const String &p_file) { // add other par
#ifdef TOOLS_ENABLED
bool _csproj_exists(String p_root_dir) {
Ref<DirAccess> dir = DirAccess::open(p_root_dir);
+ ERR_FAIL_COND_V(dir.is_null(), false);
dir->list_dir_begin();
String file_name = dir->_get_next();
@@ -1225,6 +1260,8 @@ void ProjectSettings::_bind_methods() {
ClassDB::bind_method(D_METHOD("load_resource_pack", "pack", "replace_files", "offset"), &ProjectSettings::_load_resource_pack, DEFVAL(true), DEFVAL(0));
ClassDB::bind_method(D_METHOD("save_custom", "file"), &ProjectSettings::_save_custom_bnd);
+
+ ADD_SIGNAL(MethodInfo("settings_changed"));
}
void ProjectSettings::_add_builtin_input_map() {
@@ -1306,6 +1343,9 @@ ProjectSettings::ProjectSettings() {
GLOBAL_DEF_RST(PropertyInfo(Variant::FLOAT, "audio/general/2d_panning_strength", PROPERTY_HINT_RANGE, "0,2,0.01"), 0.5f);
GLOBAL_DEF_RST(PropertyInfo(Variant::FLOAT, "audio/general/3d_panning_strength", PROPERTY_HINT_RANGE, "0,2,0.01"), 0.5f);
+ GLOBAL_DEF(PropertyInfo(Variant::INT, "audio/general/ios/session_category", PROPERTY_HINT_ENUM, "Ambient,Multi Route,Play and Record,Playback,Record,Solo Ambient"), 0);
+ GLOBAL_DEF("audio/general/ios/mix_with_others", false);
+
PackedStringArray extensions;
extensions.push_back("gd");
if (Engine::get_singleton()->has_singleton("GodotSharp")) {
@@ -1329,6 +1369,7 @@ ProjectSettings::ProjectSettings() {
GLOBAL_DEF_BASIC(PropertyInfo(Variant::STRING, "display/window/stretch/mode", PROPERTY_HINT_ENUM, "disabled,canvas_items,viewport"), "disabled");
GLOBAL_DEF_BASIC(PropertyInfo(Variant::STRING, "display/window/stretch/aspect", PROPERTY_HINT_ENUM, "ignore,keep,keep_width,keep_height,expand"), "keep");
GLOBAL_DEF_BASIC(PropertyInfo(Variant::FLOAT, "display/window/stretch/scale", PROPERTY_HINT_RANGE, "0.5,8.0,0.01"), 1.0);
+ GLOBAL_DEF_BASIC(PropertyInfo(Variant::STRING, "display/window/stretch/scale_mode", PROPERTY_HINT_ENUM, "fractional,integer"), "fractional");
GLOBAL_DEF(PropertyInfo(Variant::INT, "debug/settings/profiler/max_functions", PROPERTY_HINT_RANGE, "128,65535,1"), 16384);
@@ -1361,11 +1402,15 @@ ProjectSettings::ProjectSettings() {
GLOBAL_DEF_BASIC(PropertyInfo(Variant::INT, "rendering/textures/canvas_textures/default_texture_filter", PROPERTY_HINT_ENUM, "Nearest,Linear,Linear Mipmap,Nearest Mipmap"), 1);
GLOBAL_DEF_BASIC(PropertyInfo(Variant::INT, "rendering/textures/canvas_textures/default_texture_repeat", PROPERTY_HINT_ENUM, "Disable,Enable,Mirror"), 0);
- // These properties will not show up in the dialog nor in the documentation. If you want to exclude whole groups, see _get_property_list() method.
+ GLOBAL_DEF("collada/use_ambient", false);
+
+ // These properties will not show up in the dialog. If you want to exclude whole groups, use add_hidden_prefix().
GLOBAL_DEF_INTERNAL("application/config/features", PackedStringArray());
GLOBAL_DEF_INTERNAL("internationalization/locale/translation_remaps", PackedStringArray());
GLOBAL_DEF_INTERNAL("internationalization/locale/translations", PackedStringArray());
GLOBAL_DEF_INTERNAL("internationalization/locale/translations_pot_files", PackedStringArray());
+
+ ProjectSettings::get_singleton()->add_hidden_prefix("input/");
}
ProjectSettings::~ProjectSettings() {
diff --git a/core/config/project_settings.h b/core/config/project_settings.h
index b89e6694b0..302df7e8d0 100644
--- a/core/config/project_settings.h
+++ b/core/config/project_settings.h
@@ -43,6 +43,9 @@ class TypedArray;
class ProjectSettings : public Object {
GDCLASS(ProjectSettings, Object);
_THREAD_SAFE_CLASS_
+ friend class TestProjectSettingsInternalsAccessor;
+
+ bool is_changed = false;
public:
typedef HashMap<String, Variant> CustomMap;
@@ -101,6 +104,7 @@ protected:
HashSet<String> custom_features;
HashMap<StringName, LocalVector<Pair<StringName, StringName>>> feature_overrides;
+ LocalVector<String> hidden_prefixes;
HashMap<StringName, AutoloadInfo> autoloads;
Array global_class_list;
@@ -114,6 +118,9 @@ protected:
bool _property_can_revert(const StringName &p_name) const;
bool _property_get_revert(const StringName &p_name, Variant &r_property) const;
+ void _queue_changed();
+ void _emit_changed();
+
static ProjectSettings *singleton;
Error _load_settings_text(const String &p_path);
@@ -162,6 +169,7 @@ public:
void set_restart_if_changed(const String &p_name, bool p_restart);
void set_ignore_value_in_docs(const String &p_name, bool p_ignore);
bool get_ignore_value_in_docs(const String &p_name) const;
+ void add_hidden_prefix(const String &p_prefix);
String get_project_data_dir_name() const;
String get_project_data_path() const;
diff --git a/core/core_bind.cpp b/core/core_bind.cpp
index 4e220d0839..981d9b0025 100644
--- a/core/core_bind.cpp
+++ b/core/core_bind.cpp
@@ -929,6 +929,17 @@ Geometry3D *Geometry3D::get_singleton() {
return singleton;
}
+Vector<Vector3> Geometry3D::compute_convex_mesh_points(const TypedArray<Plane> &p_planes) {
+ Vector<Plane> planes_vec;
+ int size = p_planes.size();
+ planes_vec.resize(size);
+ for (int i = 0; i < size; ++i) {
+ planes_vec.set(i, p_planes[i]);
+ }
+ Variant ret = ::Geometry3D::compute_convex_mesh_points(planes_vec.ptr(), size);
+ return ret;
+}
+
TypedArray<Plane> Geometry3D::build_box_planes(const Vector3 &p_extents) {
Variant ret = ::Geometry3D::build_box_planes(p_extents);
return ret;
@@ -1029,6 +1040,7 @@ Vector<Vector3> Geometry3D::clip_polygon(const Vector<Vector3> &p_points, const
}
void Geometry3D::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("compute_convex_mesh_points", "planes"), &Geometry3D::compute_convex_mesh_points);
ClassDB::bind_method(D_METHOD("build_box_planes", "extents"), &Geometry3D::build_box_planes);
ClassDB::bind_method(D_METHOD("build_cylinder_planes", "radius", "height", "sides", "axis"), &Geometry3D::build_cylinder_planes, DEFVAL(Vector3::AXIS_Z));
ClassDB::bind_method(D_METHOD("build_capsule_planes", "radius", "height", "sides", "lats", "axis"), &Geometry3D::build_capsule_planes, DEFVAL(Vector3::AXIS_Z));
@@ -1199,8 +1211,7 @@ void Thread::_start_func(void *ud) {
Ref<Thread> t = *tud;
memdelete(tud);
- Object *target_instance = t->target_callable.get_object();
- if (!target_instance) {
+ if (!t->target_callable.is_valid()) {
t->running.clear();
ERR_FAIL_MSG(vformat("Could not call function '%s' on previously freed instance to start thread %s.", t->target_callable.get_method(), t->get_id()));
}
diff --git a/core/core_bind.h b/core/core_bind.h
index 1cbbcdd251..5f51b64eb7 100644
--- a/core/core_bind.h
+++ b/core/core_bind.h
@@ -320,6 +320,7 @@ protected:
public:
static Geometry3D *get_singleton();
+ Vector<Vector3> compute_convex_mesh_points(const TypedArray<Plane> &p_planes);
TypedArray<Plane> build_box_planes(const Vector3 &p_extents);
TypedArray<Plane> build_cylinder_planes(float p_radius, float p_height, int p_sides, Vector3::Axis p_axis = Vector3::AXIS_Z);
TypedArray<Plane> build_capsule_planes(float p_radius, float p_height, int p_sides, int p_lats, Vector3::Axis p_axis = Vector3::AXIS_Z);
diff --git a/core/core_constants.cpp b/core/core_constants.cpp
index 2332bc235b..33b3271495 100644
--- a/core/core_constants.cpp
+++ b/core/core_constants.cpp
@@ -420,6 +420,10 @@ void register_global_constants() {
BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, LAUNCHD);
BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, LAUNCHE);
BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, LAUNCHF);
+ BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, GLOBE);
+ BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, KEYBOARD);
+ BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, JIS_EISU);
+ BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, JIS_KANA);
BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, UNKNOWN);
BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, SPACE);
BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, EXCLAM);
@@ -492,10 +496,6 @@ void register_global_constants() {
BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, ASCIITILDE);
BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, YEN);
BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, SECTION);
- BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, GLOBE);
- BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, KEYBOARD);
- BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, JIS_EISU);
- BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, JIS_KANA);
BIND_CORE_BITFIELD_CLASS_FLAG_CUSTOM(KeyModifierMask, KEY_CODE_MASK, CODE_MASK);
BIND_CORE_BITFIELD_CLASS_FLAG_CUSTOM(KeyModifierMask, KEY_MODIFIER_MASK, MODIFIER_MASK);
diff --git a/core/crypto/crypto_core.cpp b/core/crypto/crypto_core.cpp
index 3ca2ec282f..17b34c08e2 100644
--- a/core/crypto/crypto_core.cpp
+++ b/core/crypto/crypto_core.cpp
@@ -73,7 +73,7 @@ Error CryptoCore::RandomGenerator::init() {
}
Error CryptoCore::RandomGenerator::get_random_bytes(uint8_t *r_buffer, size_t p_bytes) {
- ERR_FAIL_COND_V(!ctx, ERR_UNCONFIGURED);
+ ERR_FAIL_NULL_V(ctx, ERR_UNCONFIGURED);
int ret = mbedtls_ctr_drbg_random((mbedtls_ctr_drbg_context *)ctx, r_buffer, p_bytes);
ERR_FAIL_COND_V_MSG(ret, FAILED, " failed\n ! mbedtls_ctr_drbg_seed returned an error" + itos(ret));
return OK;
diff --git a/core/crypto/hashing_context.cpp b/core/crypto/hashing_context.cpp
index 35f491cd0c..157a0c091b 100644
--- a/core/crypto/hashing_context.cpp
+++ b/core/crypto/hashing_context.cpp
@@ -35,7 +35,7 @@
Error HashingContext::start(HashType p_type) {
ERR_FAIL_COND_V(ctx != nullptr, ERR_ALREADY_IN_USE);
_create_ctx(p_type);
- ERR_FAIL_COND_V(ctx == nullptr, ERR_UNAVAILABLE);
+ ERR_FAIL_NULL_V(ctx, ERR_UNAVAILABLE);
switch (type) {
case HASH_MD5:
return ((CryptoCore::MD5Context *)ctx)->start();
@@ -48,7 +48,7 @@ Error HashingContext::start(HashType p_type) {
}
Error HashingContext::update(PackedByteArray p_chunk) {
- ERR_FAIL_COND_V(ctx == nullptr, ERR_UNCONFIGURED);
+ ERR_FAIL_NULL_V(ctx, ERR_UNCONFIGURED);
size_t len = p_chunk.size();
ERR_FAIL_COND_V(len == 0, FAILED);
const uint8_t *r = p_chunk.ptr();
@@ -64,7 +64,7 @@ Error HashingContext::update(PackedByteArray p_chunk) {
}
PackedByteArray HashingContext::finish() {
- ERR_FAIL_COND_V(ctx == nullptr, PackedByteArray());
+ ERR_FAIL_NULL_V(ctx, PackedByteArray());
PackedByteArray out;
Error err = FAILED;
switch (type) {
diff --git a/core/debugger/engine_profiler.h b/core/debugger/engine_profiler.h
index f74481c84b..d3d0021e67 100644
--- a/core/debugger/engine_profiler.h
+++ b/core/debugger/engine_profiler.h
@@ -31,10 +31,8 @@
#ifndef ENGINE_PROFILER_H
#define ENGINE_PROFILER_H
-#include "core/object/ref_counted.h"
-
#include "core/object/gdvirtual.gen.inc"
-#include "core/object/script_language.h"
+#include "core/object/ref_counted.h"
class EngineProfiler : public RefCounted {
GDCLASS(EngineProfiler, RefCounted);
diff --git a/core/debugger/remote_debugger.cpp b/core/debugger/remote_debugger.cpp
index b4d6fa4174..a817ea871d 100644
--- a/core/debugger/remote_debugger.cpp
+++ b/core/debugger/remote_debugger.cpp
@@ -415,7 +415,7 @@ void RemoteDebugger::debug(bool p_can_continue, bool p_is_error_breakpoint) {
Array msg;
msg.push_back(p_can_continue);
msg.push_back(error_str);
- ERR_FAIL_COND(!script_lang);
+ ERR_FAIL_NULL(script_lang);
msg.push_back(script_lang->debug_get_stack_level_count() > 0);
msg.push_back(Thread::get_caller_id() == Thread::get_main_id() ? String(RTR("Main Thread")) : itos(Thread::get_caller_id()));
if (allow_focus_steal_fn) {
@@ -485,7 +485,7 @@ void RemoteDebugger::debug(bool p_can_continue, bool p_is_error_breakpoint) {
} else if (command == "get_stack_frame_vars") {
ERR_FAIL_COND(data.size() != 1);
- ERR_FAIL_COND(!script_lang);
+ ERR_FAIL_NULL(script_lang);
int lv = data[0];
List<String> members;
diff --git a/core/extension/extension_api_dump.cpp b/core/extension/extension_api_dump.cpp
index c67867f65d..f3e988633c 100644
--- a/core/extension/extension_api_dump.cpp
+++ b/core/extension/extension_api_dump.cpp
@@ -32,12 +32,14 @@
#include "core/config/engine.h"
#include "core/core_constants.h"
+#include "core/extension/gdextension_compat_hashes.h"
#include "core/io/file_access.h"
#include "core/io/json.h"
#include "core/templates/pair.h"
#include "core/version.h"
#ifdef TOOLS_ENABLED
+#include "editor/editor_help.h"
static String get_builtin_or_variant_type_name(const Variant::Type p_type) {
if (p_type == Variant::NIL) {
@@ -87,7 +89,16 @@ static String get_type_meta_name(const GodotTypeInfo::Metadata metadata) {
return argmeta[metadata];
}
-Dictionary GDExtensionAPIDump::generate_extension_api() {
+static String fix_doc_description(const String &p_bbcode) {
+ // Based on what EditorHelp does.
+
+ return p_bbcode.dedent()
+ .replace("\t", "")
+ .replace("\r", "")
+ .strip_edges();
+}
+
+Dictionary GDExtensionAPIDump::generate_extension_api(bool p_include_docs) {
Dictionary api_dump;
{
@@ -459,12 +470,22 @@ Dictionary GDExtensionAPIDump::generate_extension_api() {
api_dump["builtin_class_member_offsets"] = core_type_member_offsets;
}
+ if (p_include_docs) {
+ EditorHelp::generate_doc(false);
+ }
+
{
// Global enums and constants.
Array constants;
HashMap<String, List<Pair<String, int64_t>>> enum_list;
HashMap<String, bool> enum_is_bitfield;
+ const DocData::ClassDoc *global_scope_doc = nullptr;
+ if (p_include_docs) {
+ global_scope_doc = EditorHelp::get_doc_data()->class_list.getptr("@GlobalScope");
+ CRASH_COND_MSG(!global_scope_doc, "Could not find '@GlobalScope' in DocData.");
+ }
+
for (int i = 0; i < CoreConstants::get_global_constant_count(); i++) {
int64_t value = CoreConstants::get_global_constant_value(i);
String enum_name = CoreConstants::get_global_constant_enum(i);
@@ -478,6 +499,14 @@ Dictionary GDExtensionAPIDump::generate_extension_api() {
d["name"] = name;
d["value"] = value;
d["is_bitfield"] = bitfield;
+ if (p_include_docs) {
+ for (const DocData::ConstantDoc &constant_doc : global_scope_doc->constants) {
+ if (constant_doc.name == name) {
+ d["description"] = fix_doc_description(constant_doc.description);
+ break;
+ }
+ }
+ }
constants.push_back(d);
}
}
@@ -489,11 +518,25 @@ Dictionary GDExtensionAPIDump::generate_extension_api() {
Dictionary d1;
d1["name"] = E.key;
d1["is_bitfield"] = enum_is_bitfield[E.key];
+ if (p_include_docs) {
+ const DocData::EnumDoc *enum_doc = global_scope_doc->enums.getptr(E.key);
+ if (enum_doc) {
+ d1["description"] = fix_doc_description(enum_doc->description);
+ }
+ }
Array values;
for (const Pair<String, int64_t> &F : E.value) {
Dictionary d2;
d2["name"] = F.first;
d2["value"] = F.second;
+ if (p_include_docs) {
+ for (const DocData::ConstantDoc &constant_doc : global_scope_doc->constants) {
+ if (constant_doc.name == F.first) {
+ d2["description"] = fix_doc_description(constant_doc.description);
+ break;
+ }
+ }
+ }
values.push_back(d2);
}
d1["values"] = values;
@@ -508,6 +551,12 @@ Dictionary GDExtensionAPIDump::generate_extension_api() {
List<StringName> utility_func_names;
Variant::get_utility_function_list(&utility_func_names);
+ const DocData::ClassDoc *global_scope_doc = nullptr;
+ if (p_include_docs) {
+ global_scope_doc = EditorHelp::get_doc_data()->class_list.getptr("@GlobalScope");
+ CRASH_COND_MSG(!global_scope_doc, "Could not find '@GlobalScope' in DocData.");
+ }
+
for (const StringName &name : utility_func_names) {
Dictionary func;
func["name"] = String(name);
@@ -544,6 +593,15 @@ Dictionary GDExtensionAPIDump::generate_extension_api() {
func["arguments"] = arguments;
}
+ if (p_include_docs) {
+ for (const DocData::MethodDoc &method_doc : global_scope_doc->methods) {
+ if (method_doc.name == name) {
+ func["description"] = fix_doc_description(method_doc.description);
+ break;
+ }
+ }
+ }
+
utility_funcs.push_back(func);
}
@@ -570,6 +628,12 @@ Dictionary GDExtensionAPIDump::generate_extension_api() {
d["is_keyed"] = Variant::is_keyed(type);
+ DocData::ClassDoc *builtin_doc = nullptr;
+ if (p_include_docs && d["name"] != "Nil") {
+ builtin_doc = EditorHelp::get_doc_data()->class_list.getptr(d["name"]);
+ CRASH_COND_MSG(!builtin_doc, vformat("Could not find '%s' in DocData.", d["name"]));
+ }
+
{
//members
Array members;
@@ -580,6 +644,14 @@ Dictionary GDExtensionAPIDump::generate_extension_api() {
Dictionary d2;
d2["name"] = String(member_name);
d2["type"] = get_builtin_or_variant_type_name(Variant::get_member_type(type, member_name));
+ if (p_include_docs) {
+ for (const DocData::PropertyDoc &property_doc : builtin_doc->properties) {
+ if (property_doc.name == member_name) {
+ d2["description"] = fix_doc_description(property_doc.description);
+ break;
+ }
+ }
+ }
members.push_back(d2);
}
if (members.size()) {
@@ -598,6 +670,14 @@ Dictionary GDExtensionAPIDump::generate_extension_api() {
Variant constant = Variant::get_constant_value(type, constant_name);
d2["type"] = get_builtin_or_variant_type_name(constant.get_type());
d2["value"] = constant.get_construct_string();
+ if (p_include_docs) {
+ for (const DocData::ConstantDoc &constant_doc : builtin_doc->constants) {
+ if (constant_doc.name == constant_name) {
+ d2["description"] = fix_doc_description(constant_doc.description);
+ break;
+ }
+ }
+ }
constants.push_back(d2);
}
if (constants.size()) {
@@ -623,9 +703,24 @@ Dictionary GDExtensionAPIDump::generate_extension_api() {
Dictionary values_dict;
values_dict["name"] = String(enumeration);
values_dict["value"] = Variant::get_enum_value(type, enum_name, enumeration);
+ if (p_include_docs) {
+ for (const DocData::ConstantDoc &constant_doc : builtin_doc->constants) {
+ if (constant_doc.name == enumeration) {
+ values_dict["description"] = fix_doc_description(constant_doc.description);
+ break;
+ }
+ }
+ }
values.push_back(values_dict);
}
+ if (p_include_docs) {
+ const DocData::EnumDoc *enum_doc = builtin_doc->enums.getptr(enum_name);
+ if (enum_doc) {
+ enum_dict["description"] = fix_doc_description(enum_doc->description);
+ }
+ }
+
if (values.size()) {
enum_dict["values"] = values;
}
@@ -645,11 +740,22 @@ Dictionary GDExtensionAPIDump::generate_extension_api() {
Variant::Type rt = Variant::get_operator_return_type(Variant::Operator(k), type, Variant::Type(j));
if (rt != Variant::NIL) {
Dictionary d2;
- d2["name"] = Variant::get_operator_name(Variant::Operator(k));
+ String operator_name = Variant::get_operator_name(Variant::Operator(k));
+ d2["name"] = operator_name;
if (k != Variant::OP_NEGATE && k != Variant::OP_POSITIVE && k != Variant::OP_NOT && k != Variant::OP_BIT_NEGATE) {
d2["right_type"] = get_builtin_or_variant_type_name(Variant::Type(j));
}
d2["return_type"] = get_builtin_or_variant_type_name(Variant::get_operator_return_type(Variant::Operator(k), type, Variant::Type(j)));
+
+ if (p_include_docs && builtin_doc != nullptr) {
+ for (const DocData::MethodDoc &operator_doc : builtin_doc->operators) {
+ if (operator_doc.name == "operator " + operator_name) {
+ d2["description"] = fix_doc_description(operator_doc.description);
+ break;
+ }
+ }
+ }
+
operators.push_back(d2);
}
}
@@ -696,6 +802,15 @@ Dictionary GDExtensionAPIDump::generate_extension_api() {
d2["arguments"] = arguments;
}
+ if (p_include_docs) {
+ for (const DocData::MethodDoc &method_doc : builtin_doc->methods) {
+ if (method_doc.name == method_name) {
+ d2["description"] = fix_doc_description(method_doc.description);
+ break;
+ }
+ }
+ }
+
methods.push_back(d2);
}
if (methods.size()) {
@@ -721,6 +836,28 @@ Dictionary GDExtensionAPIDump::generate_extension_api() {
if (arguments.size()) {
d2["arguments"] = arguments;
}
+
+ if (p_include_docs && builtin_doc) {
+ for (const DocData::MethodDoc &constructor_doc : builtin_doc->constructors) {
+ if (constructor_doc.arguments.size() != argcount) {
+ continue;
+ }
+ bool constructor_found = true;
+ for (int k = 0; k < argcount; k++) {
+ const DocData::ArgumentDoc &argument_doc = constructor_doc.arguments[k];
+ const Dictionary &argument_dict = arguments[k];
+ const String &argument_string = argument_dict["type"];
+ if (argument_doc.type != argument_string) {
+ constructor_found = false;
+ break;
+ }
+ }
+ if (constructor_found) {
+ d2["description"] = fix_doc_description(constructor_doc.description);
+ }
+ }
+ }
+
constructors.push_back(d2);
}
@@ -733,6 +870,11 @@ Dictionary GDExtensionAPIDump::generate_extension_api() {
d["has_destructor"] = Variant::has_destructor(type);
}
+ if (p_include_docs && builtin_doc != nullptr) {
+ d["brief_description"] = fix_doc_description(builtin_doc->brief_description);
+ d["description"] = fix_doc_description(builtin_doc->description);
+ }
+
builtins.push_back(d);
}
@@ -750,6 +892,9 @@ Dictionary GDExtensionAPIDump::generate_extension_api() {
class_list.sort_custom<StringName::AlphCompare>();
for (const StringName &class_name : class_list) {
+ if (!ClassDB::is_class_exposed(class_name)) {
+ continue;
+ }
Dictionary d;
d["name"] = String(class_name);
d["is_refcounted"] = ClassDB::is_parent_class(class_name, "RefCounted");
@@ -759,6 +904,12 @@ Dictionary GDExtensionAPIDump::generate_extension_api() {
d["inherits"] = String(parent_class);
}
+ DocData::ClassDoc *class_doc = nullptr;
+ if (p_include_docs) {
+ class_doc = EditorHelp::get_doc_data()->class_list.getptr(class_name);
+ CRASH_COND_MSG(!class_doc, vformat("Could not find '%s' in DocData.", class_name));
+ }
+
{
ClassDB::APIType api = ClassDB::get_api_type(class_name);
static const char *api_type[5] = { "core", "editor", "extension", "editor_extension" };
@@ -780,6 +931,15 @@ Dictionary GDExtensionAPIDump::generate_extension_api() {
d2["name"] = String(F);
d2["value"] = ClassDB::get_integer_constant(class_name, F);
+ if (p_include_docs) {
+ for (const DocData::ConstantDoc &constant_doc : class_doc->constants) {
+ if (constant_doc.name == F) {
+ d2["description"] = fix_doc_description(constant_doc.description);
+ break;
+ }
+ }
+ }
+
constants.push_back(d2);
}
@@ -804,11 +964,28 @@ Dictionary GDExtensionAPIDump::generate_extension_api() {
Dictionary d3;
d3["name"] = String(G->get());
d3["value"] = ClassDB::get_integer_constant(class_name, G->get());
+
+ if (p_include_docs) {
+ for (const DocData::ConstantDoc &constant_doc : class_doc->constants) {
+ if (constant_doc.name == G->get()) {
+ d3["description"] = fix_doc_description(constant_doc.description);
+ break;
+ }
+ }
+ }
+
values.push_back(d3);
}
d2["values"] = values;
+ if (p_include_docs) {
+ const DocData::EnumDoc *enum_doc = class_doc->enums.getptr(F);
+ if (enum_doc) {
+ d2["description"] = fix_doc_description(enum_doc->description);
+ }
+ }
+
enums.push_back(d2);
}
@@ -860,6 +1037,15 @@ Dictionary GDExtensionAPIDump::generate_extension_api() {
d2["arguments"] = arguments;
}
+ if (p_include_docs) {
+ for (const DocData::MethodDoc &method_doc : class_doc->methods) {
+ if (method_doc.name == method_name) {
+ d2["description"] = fix_doc_description(method_doc.description);
+ break;
+ }
+ }
+ }
+
methods.push_back(d2);
} else if (F.name.begins_with("_")) {
@@ -881,11 +1067,18 @@ Dictionary GDExtensionAPIDump::generate_extension_api() {
d2["hash"] = method->get_hash();
Vector<uint32_t> compat_hashes = ClassDB::get_method_compatibility_hashes(class_name, method_name);
+ Array compatibility;
if (compat_hashes.size()) {
- Array compatibility;
for (int i = 0; i < compat_hashes.size(); i++) {
compatibility.push_back(compat_hashes[i]);
}
+ }
+
+#ifndef DISABLE_DEPRECATED
+ GDExtensionCompatHashes::get_legacy_hashes(class_name, method_name, compatibility);
+#endif
+
+ if (compatibility.size() > 0) {
d2["hash_compatibility"] = compatibility;
}
@@ -921,6 +1114,15 @@ Dictionary GDExtensionAPIDump::generate_extension_api() {
d2["arguments"] = arguments;
}
+ if (p_include_docs) {
+ for (const DocData::MethodDoc &method_doc : class_doc->methods) {
+ if (method_doc.name == method_name) {
+ d2["description"] = fix_doc_description(method_doc.description);
+ break;
+ }
+ }
+ }
+
methods.push_back(d2);
}
}
@@ -955,6 +1157,15 @@ Dictionary GDExtensionAPIDump::generate_extension_api() {
d2["arguments"] = arguments;
}
+ if (p_include_docs) {
+ for (const DocData::MethodDoc &signal_doc : class_doc->signals) {
+ if (signal_doc.name == signal_name) {
+ d2["description"] = fix_doc_description(signal_doc.description);
+ break;
+ }
+ }
+ }
+
signals.push_back(d2);
}
@@ -994,6 +1205,16 @@ Dictionary GDExtensionAPIDump::generate_extension_api() {
if (index != -1) {
d2["index"] = index;
}
+
+ if (p_include_docs) {
+ for (const DocData::PropertyDoc &property_doc : class_doc->properties) {
+ if (property_doc.name == property_name) {
+ d2["description"] = fix_doc_description(property_doc.description);
+ break;
+ }
+ }
+ }
+
properties.push_back(d2);
}
@@ -1002,6 +1223,11 @@ Dictionary GDExtensionAPIDump::generate_extension_api() {
}
}
+ if (p_include_docs && class_doc != nullptr) {
+ d["brief_description"] = fix_doc_description(class_doc->brief_description);
+ d["description"] = fix_doc_description(class_doc->description);
+ }
+
classes.push_back(d);
}
@@ -1054,8 +1280,8 @@ Dictionary GDExtensionAPIDump::generate_extension_api() {
return api_dump;
}
-void GDExtensionAPIDump::generate_extension_json_file(const String &p_path) {
- Dictionary api = generate_extension_api();
+void GDExtensionAPIDump::generate_extension_json_file(const String &p_path, bool p_include_docs) {
+ Dictionary api = generate_extension_api(p_include_docs);
Ref<JSON> json;
json.instantiate();
diff --git a/core/extension/extension_api_dump.h b/core/extension/extension_api_dump.h
index 11ea2cf923..204a115f84 100644
--- a/core/extension/extension_api_dump.h
+++ b/core/extension/extension_api_dump.h
@@ -37,8 +37,8 @@
class GDExtensionAPIDump {
public:
- static Dictionary generate_extension_api();
- static void generate_extension_json_file(const String &p_path);
+ static Dictionary generate_extension_api(bool p_include_docs = false);
+ static void generate_extension_json_file(const String &p_path, bool p_include_docs = false);
static Error validate_extension_json_file(const String &p_path);
};
#endif
diff --git a/core/extension/gdextension.cpp b/core/extension/gdextension.cpp
index 67b55db3db..8fb1aab6dc 100644
--- a/core/extension/gdextension.cpp
+++ b/core/extension/gdextension.cpp
@@ -35,6 +35,7 @@
#include "core/object/method_bind.h"
#include "core/os/os.h"
#include "core/version.h"
+#include "gdextension_manager.h"
extern void gdextension_setup_interface();
extern GDExtensionInterfaceFunctionPtr gdextension_get_proc_address(const char *p_name);
@@ -162,6 +163,14 @@ class GDExtensionMethodBind : public MethodBind {
List<PropertyInfo> arguments_info;
List<GodotTypeInfo::Metadata> arguments_metadata;
+#ifdef TOOLS_ENABLED
+ friend class GDExtension;
+
+ StringName name;
+ bool is_reloading = false;
+ bool valid = true;
+#endif
+
protected:
virtual Variant::Type _gen_argument_type(int p_arg) const override {
if (p_arg < 0) {
@@ -179,6 +188,10 @@ protected:
}
public:
+#ifdef TOOLS_ENABLED
+ virtual bool is_valid() const override { return valid; }
+#endif
+
#ifdef DEBUG_METHODS_ENABLED
virtual GodotTypeInfo::Metadata get_argument_meta(int p_arg) const override {
if (p_arg < 0) {
@@ -190,6 +203,9 @@ public:
#endif
virtual Variant call(Object *p_object, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) const override {
+#ifdef TOOLS_ENABLED
+ ERR_FAIL_COND_V_MSG(!valid, Variant(), vformat("Cannot call invalid GDExtension method bind '%s'. It's probably cached - you may need to restart Godot.", name));
+#endif
Variant ret;
GDExtensionClassInstancePtr extension_instance = is_static() ? nullptr : p_object->_get_extension_instance();
GDExtensionCallError ce{ GDEXTENSION_CALL_OK, 0, 0 };
@@ -200,40 +216,40 @@ public:
return ret;
}
virtual void validated_call(Object *p_object, const Variant **p_args, Variant *r_ret) const override {
- ERR_FAIL_COND_MSG(vararg, "Validated methods don't have ptrcall support. This is most likely an engine bug.");
+#ifdef TOOLS_ENABLED
+ ERR_FAIL_COND_MSG(!valid, vformat("Cannot call invalid GDExtension method bind '%s'. It's probably cached - you may need to restart Godot.", name));
+#endif
+ ERR_FAIL_COND_MSG(vararg, "Vararg methods don't have validated call support. This is most likely an engine bug.");
GDExtensionClassInstancePtr extension_instance = is_static() ? nullptr : p_object->_get_extension_instance();
if (validated_call_func) {
// This is added here, but it's unlikely to be provided by most extensions.
validated_call_func(method_userdata, extension_instance, reinterpret_cast<GDExtensionConstVariantPtr *>(p_args), (GDExtensionVariantPtr)r_ret);
} else {
-#if 1
- // Slow code-path, but works for the time being.
- Callable::CallError ce;
- call(p_object, p_args, argument_count, ce);
-#else
- // This is broken, because it needs more information to do the calling properly
-
// If not provided, go via ptrcall, which is faster than resorting to regular call.
const void **argptrs = (const void **)alloca(argument_count * sizeof(void *));
for (uint32_t i = 0; i < argument_count; i++) {
argptrs[i] = VariantInternal::get_opaque_pointer(p_args[i]);
}
- bool returns = true;
- void *ret_opaque;
- if (returns) {
- ret_opaque = VariantInternal::get_opaque_pointer(r_ret);
- } else {
- ret_opaque = nullptr; // May be unnecessary as this is ignored, but just in case.
+ void *ret_opaque = nullptr;
+ if (r_ret) {
+ VariantInternal::initialize(r_ret, return_value_info.type);
+ ret_opaque = r_ret->get_type() == Variant::NIL ? r_ret : VariantInternal::get_opaque_pointer(r_ret);
}
ptrcall(p_object, argptrs, ret_opaque);
-#endif
+
+ if (r_ret && r_ret->get_type() == Variant::OBJECT) {
+ VariantInternal::update_object_id(r_ret);
+ }
}
}
virtual void ptrcall(Object *p_object, const void **p_args, void *r_ret) const override {
+#ifdef TOOLS_ENABLED
+ ERR_FAIL_COND_MSG(!valid, vformat("Cannot call invalid GDExtension method bind '%s'. It's probably cached - you may need to restart Godot.", name));
+#endif
ERR_FAIL_COND_MSG(vararg, "Vararg methods don't have ptrcall support. This is most likely an engine bug.");
GDExtensionClassInstancePtr extension_instance = p_object->_get_extension_instance();
ptrcall_func(method_userdata, extension_instance, reinterpret_cast<GDExtensionConstTypePtr *>(p_args), (GDExtensionTypePtr)r_ret);
@@ -243,7 +259,43 @@ public:
return false;
}
- explicit GDExtensionMethodBind(const GDExtensionClassMethodInfo *p_method_info) {
+#ifdef TOOLS_ENABLED
+ bool try_update(const GDExtensionClassMethodInfo *p_method_info) {
+ if (is_static() != (bool)(p_method_info->method_flags & GDEXTENSION_METHOD_FLAG_STATIC)) {
+ return false;
+ }
+
+ if (vararg != (bool)(p_method_info->method_flags & GDEXTENSION_METHOD_FLAG_VARARG)) {
+ return false;
+ }
+
+ if (has_return() != (bool)p_method_info->has_return_value) {
+ return false;
+ }
+
+ if (has_return() && return_value_info.type != (Variant::Type)p_method_info->return_value_info->type) {
+ return false;
+ }
+
+ if (argument_count != p_method_info->argument_count) {
+ return false;
+ }
+
+ for (uint32_t i = 0; i < p_method_info->argument_count; i++) {
+ if (arguments_info[i].type != (Variant::Type)p_method_info->arguments_info[i].type) {
+ return false;
+ }
+ }
+
+ update(p_method_info);
+ return true;
+ }
+#endif
+
+ void update(const GDExtensionClassMethodInfo *p_method_info) {
+#ifdef TOOLS_ENABLED
+ name = *reinterpret_cast<StringName *>(p_method_info->name);
+#endif
method_userdata = p_method_info->method_userdata;
call_func = p_method_info->call_func;
validated_call_func = nullptr;
@@ -255,6 +307,8 @@ public:
return_value_metadata = GodotTypeInfo::Metadata(p_method_info->return_value_metadata);
}
+ arguments_info.clear();
+ arguments_metadata.clear();
for (uint32_t i = 0; i < p_method_info->argument_count; i++) {
arguments_info.push_back(PropertyInfo(p_method_info->arguments_info[i]));
arguments_metadata.push_back(GodotTypeInfo::Metadata(p_method_info->arguments_metadata[i]));
@@ -279,9 +333,51 @@ public:
set_default_arguments(defargs);
}
+
+ explicit GDExtensionMethodBind(const GDExtensionClassMethodInfo *p_method_info) {
+ update(p_method_info);
+ }
};
+#ifndef DISABLE_DEPRECATED
void GDExtension::_register_extension_class(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_parent_class_name, const GDExtensionClassCreationInfo *p_extension_funcs) {
+ const GDExtensionClassCreationInfo2 class_info2 = {
+ p_extension_funcs->is_virtual, // GDExtensionBool is_virtual;
+ p_extension_funcs->is_abstract, // GDExtensionBool is_abstract;
+ true, // GDExtensionBool is_exposed;
+ p_extension_funcs->set_func, // GDExtensionClassSet set_func;
+ p_extension_funcs->get_func, // GDExtensionClassGet get_func;
+ p_extension_funcs->get_property_list_func, // GDExtensionClassGetPropertyList get_property_list_func;
+ p_extension_funcs->free_property_list_func, // GDExtensionClassFreePropertyList free_property_list_func;
+ p_extension_funcs->property_can_revert_func, // GDExtensionClassPropertyCanRevert property_can_revert_func;
+ p_extension_funcs->property_get_revert_func, // GDExtensionClassPropertyGetRevert property_get_revert_func;
+ nullptr, // GDExtensionClassValidateProperty validate_property_func;
+ nullptr, // GDExtensionClassNotification2 notification_func;
+ p_extension_funcs->to_string_func, // GDExtensionClassToString to_string_func;
+ p_extension_funcs->reference_func, // GDExtensionClassReference reference_func;
+ p_extension_funcs->unreference_func, // GDExtensionClassUnreference unreference_func;
+ p_extension_funcs->create_instance_func, // GDExtensionClassCreateInstance create_instance_func; /* this one is mandatory */
+ p_extension_funcs->free_instance_func, // GDExtensionClassFreeInstance free_instance_func; /* this one is mandatory */
+ nullptr, // GDExtensionClassRecreateInstance recreate_instance_func;
+ p_extension_funcs->get_virtual_func, // GDExtensionClassGetVirtual get_virtual_func;
+ nullptr, // GDExtensionClassGetVirtualCallData get_virtual_call_data_func;
+ nullptr, // GDExtensionClassCallVirtualWithData call_virtual_func;
+ p_extension_funcs->get_rid_func, // GDExtensionClassGetRID get_rid;
+ p_extension_funcs->class_userdata, // void *class_userdata;
+ };
+
+ const ClassCreationDeprecatedInfo legacy = {
+ p_extension_funcs->notification_func,
+ };
+ _register_extension_class_internal(p_library, p_class_name, p_parent_class_name, &class_info2, &legacy);
+}
+#endif // DISABLE_DEPRECATED
+
+void GDExtension::_register_extension_class2(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_parent_class_name, const GDExtensionClassCreationInfo2 *p_extension_funcs) {
+ _register_extension_class_internal(p_library, p_class_name, p_parent_class_name, p_extension_funcs);
+}
+
+void GDExtension::_register_extension_class_internal(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_parent_class_name, const GDExtensionClassCreationInfo2 *p_extension_funcs, const ClassCreationDeprecatedInfo *p_deprecated_funcs) {
GDExtension *self = reinterpret_cast<GDExtension *>(p_library);
StringName class_name = *reinterpret_cast<const StringName *>(p_class_name);
@@ -304,39 +400,81 @@ void GDExtension::_register_extension_class(GDExtensionClassLibraryPtr p_library
ERR_FAIL_MSG("Attempt to register an extension class '" + String(class_name) + "' using non-existing parent class '" + String(parent_class_name) + "'");
}
+#ifdef TOOLS_ENABLED
+ Extension *extension = nullptr;
+ if (self->is_reloading && self->extension_classes.has(class_name)) {
+ extension = &self->extension_classes[class_name];
+ if (!parent_extension && parent_class_name != extension->gdextension.parent_class_name) {
+ ERR_FAIL_MSG(vformat("GDExtension class '%s' attempt to change parent type from '%s' to '%s' on hot reload. Restart Godot for this change to take effect.", class_name, extension->gdextension.parent_class_name, parent_class_name));
+ }
+ extension->is_reloading = false;
+ } else {
+ self->extension_classes[class_name] = Extension();
+ extension = &self->extension_classes[class_name];
+ }
+#else
self->extension_classes[class_name] = Extension();
-
Extension *extension = &self->extension_classes[class_name];
+#endif
if (parent_extension) {
extension->gdextension.parent = &parent_extension->gdextension;
parent_extension->gdextension.children.push_back(&extension->gdextension);
}
+ if (self->reloadable && p_extension_funcs->recreate_instance_func == nullptr) {
+ ERR_PRINT(vformat("Extension marked as reloadable, but attempted to register class '%s' which doesn't support reloading. Perhaps your language binding don't support it? Reloading disabled for this extension.", class_name));
+ self->reloadable = false;
+ }
+
extension->gdextension.library = self;
extension->gdextension.parent_class_name = parent_class_name;
extension->gdextension.class_name = class_name;
extension->gdextension.editor_class = self->level_initialized == INITIALIZATION_LEVEL_EDITOR;
extension->gdextension.is_virtual = p_extension_funcs->is_virtual;
extension->gdextension.is_abstract = p_extension_funcs->is_abstract;
+ extension->gdextension.is_exposed = p_extension_funcs->is_exposed;
extension->gdextension.set = p_extension_funcs->set_func;
extension->gdextension.get = p_extension_funcs->get_func;
extension->gdextension.get_property_list = p_extension_funcs->get_property_list_func;
extension->gdextension.free_property_list = p_extension_funcs->free_property_list_func;
extension->gdextension.property_can_revert = p_extension_funcs->property_can_revert_func;
extension->gdextension.property_get_revert = p_extension_funcs->property_get_revert_func;
- extension->gdextension.notification = p_extension_funcs->notification_func;
+ extension->gdextension.validate_property = p_extension_funcs->validate_property_func;
+#ifndef DISABLE_DEPRECATED
+ if (p_deprecated_funcs) {
+ extension->gdextension.notification = p_deprecated_funcs->notification_func;
+ }
+#endif // DISABLE_DEPRECATED
+ extension->gdextension.notification2 = p_extension_funcs->notification_func;
extension->gdextension.to_string = p_extension_funcs->to_string_func;
extension->gdextension.reference = p_extension_funcs->reference_func;
extension->gdextension.unreference = p_extension_funcs->unreference_func;
extension->gdextension.class_userdata = p_extension_funcs->class_userdata;
extension->gdextension.create_instance = p_extension_funcs->create_instance_func;
extension->gdextension.free_instance = p_extension_funcs->free_instance_func;
+ extension->gdextension.recreate_instance = p_extension_funcs->recreate_instance_func;
extension->gdextension.get_virtual = p_extension_funcs->get_virtual_func;
+ extension->gdextension.get_virtual_call_data = p_extension_funcs->get_virtual_call_data_func;
+ extension->gdextension.call_virtual_with_data = p_extension_funcs->call_virtual_with_data_func;
extension->gdextension.get_rid = p_extension_funcs->get_rid_func;
+ extension->gdextension.reloadable = self->reloadable;
+#ifdef TOOLS_ENABLED
+ if (extension->gdextension.reloadable) {
+ extension->gdextension.tracking_userdata = extension;
+ extension->gdextension.track_instance = &GDExtension::_track_instance;
+ extension->gdextension.untrack_instance = &GDExtension::_untrack_instance;
+ } else {
+ extension->gdextension.tracking_userdata = nullptr;
+ extension->gdextension.track_instance = nullptr;
+ extension->gdextension.untrack_instance = nullptr;
+ }
+#endif
+
ClassDB::register_extension_class(&extension->gdextension);
}
+
void GDExtension::_register_extension_class_method(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, const GDExtensionClassMethodInfo *p_method_info) {
GDExtension *self = reinterpret_cast<GDExtension *>(p_library);
@@ -344,10 +482,39 @@ void GDExtension::_register_extension_class_method(GDExtensionClassLibraryPtr p_
StringName method_name = *reinterpret_cast<const StringName *>(p_method_info->name);
ERR_FAIL_COND_MSG(!self->extension_classes.has(class_name), "Attempt to register extension method '" + String(method_name) + "' for unexisting class '" + class_name + "'.");
- //Extension *extension = &self->extension_classes[class_name];
+#ifdef TOOLS_ENABLED
+ Extension *extension = &self->extension_classes[class_name];
+ GDExtensionMethodBind *method = nullptr;
+
+ // If the extension is still marked as reloading, that means it failed to register again.
+ if (extension->is_reloading) {
+ return;
+ }
+ if (self->is_reloading && extension->methods.has(method_name)) {
+ method = extension->methods[method_name];
+
+ // Try to update the method bind. If it doesn't work (because it's incompatible) then
+ // mark as invalid and create a new one.
+ if (!method->is_reloading || !method->try_update(p_method_info)) {
+ method->valid = false;
+ self->invalid_methods.push_back(method);
+
+ method = nullptr;
+ }
+ }
+
+ if (method == nullptr) {
+ method = memnew(GDExtensionMethodBind(p_method_info));
+ method->set_instance_class(class_name);
+ extension->methods[method_name] = method;
+ } else {
+ method->is_reloading = false;
+ }
+#else
GDExtensionMethodBind *method = memnew(GDExtensionMethodBind(p_method_info));
method->set_instance_class(class_name);
+#endif
ClassDB::bind_method_custom(class_name, method);
}
@@ -359,6 +526,14 @@ void GDExtension::_register_extension_class_integer_constant(GDExtensionClassLib
StringName constant_name = *reinterpret_cast<const StringName *>(p_constant_name);
ERR_FAIL_COND_MSG(!self->extension_classes.has(class_name), "Attempt to register extension constant '" + constant_name + "' for unexisting class '" + class_name + "'.");
+#ifdef TOOLS_ENABLED
+ // If the extension is still marked as reloading, that means it failed to register again.
+ Extension *extension = &self->extension_classes[class_name];
+ if (extension->is_reloading) {
+ return;
+ }
+#endif
+
ClassDB::bind_integer_constant(class_name, enum_name, constant_name, p_constant_value, p_is_bitfield);
}
@@ -375,6 +550,14 @@ void GDExtension::_register_extension_class_property_indexed(GDExtensionClassLib
String property_name = *reinterpret_cast<const StringName *>(p_info->name);
ERR_FAIL_COND_MSG(!self->extension_classes.has(class_name), "Attempt to register extension class property '" + property_name + "' for unexisting class '" + class_name + "'.");
+#ifdef TOOLS_ENABLED
+ // If the extension is still marked as reloading, that means it failed to register again.
+ Extension *extension = &self->extension_classes[class_name];
+ if (extension->is_reloading) {
+ return;
+ }
+#endif
+
PropertyInfo pinfo(*p_info);
ClassDB::add_property(class_name, pinfo, setter, getter, p_index);
@@ -388,6 +571,14 @@ void GDExtension::_register_extension_class_property_group(GDExtensionClassLibra
String prefix = *reinterpret_cast<const String *>(p_prefix);
ERR_FAIL_COND_MSG(!self->extension_classes.has(class_name), "Attempt to register extension class property group '" + group_name + "' for unexisting class '" + class_name + "'.");
+#ifdef TOOLS_ENABLED
+ // If the extension is still marked as reloading, that means it failed to register again.
+ Extension *extension = &self->extension_classes[class_name];
+ if (extension->is_reloading) {
+ return;
+ }
+#endif
+
ClassDB::add_property_group(class_name, group_name, prefix);
}
@@ -399,6 +590,14 @@ void GDExtension::_register_extension_class_property_subgroup(GDExtensionClassLi
String prefix = *reinterpret_cast<const String *>(p_prefix);
ERR_FAIL_COND_MSG(!self->extension_classes.has(class_name), "Attempt to register extension class property subgroup '" + subgroup_name + "' for unexisting class '" + class_name + "'.");
+#ifdef TOOLS_ENABLED
+ // If the extension is still marked as reloading, that means it failed to register again.
+ Extension *extension = &self->extension_classes[class_name];
+ if (extension->is_reloading) {
+ return;
+ }
+#endif
+
ClassDB::add_property_subgroup(class_name, subgroup_name, prefix);
}
@@ -409,6 +608,14 @@ void GDExtension::_register_extension_class_signal(GDExtensionClassLibraryPtr p_
StringName signal_name = *reinterpret_cast<const StringName *>(p_signal_name);
ERR_FAIL_COND_MSG(!self->extension_classes.has(class_name), "Attempt to register extension class signal '" + signal_name + "' for unexisting class '" + class_name + "'.");
+#ifdef TOOLS_ENABLED
+ // If the extension is still marked as reloading, that means it failed to register again.
+ Extension *extension = &self->extension_classes[class_name];
+ if (extension->is_reloading) {
+ return;
+ }
+#endif
+
MethodInfo s;
s.name = signal_name;
for (int i = 0; i < p_argument_count; i++) {
@@ -423,14 +630,32 @@ void GDExtension::_unregister_extension_class(GDExtensionClassLibraryPtr p_libra
StringName class_name = *reinterpret_cast<const StringName *>(p_class_name);
ERR_FAIL_COND_MSG(!self->extension_classes.has(class_name), "Attempt to unregister unexisting extension class '" + class_name + "'.");
+
Extension *ext = &self->extension_classes[class_name];
+#ifdef TOOLS_ENABLED
+ if (ext->is_reloading) {
+ self->_clear_extension(ext);
+ }
+#endif
ERR_FAIL_COND_MSG(ext->gdextension.children.size(), "Attempt to unregister class '" + class_name + "' while other extension classes inherit from it.");
+#ifdef TOOLS_ENABLED
+ ClassDB::unregister_extension_class(class_name, !ext->is_reloading);
+#else
ClassDB::unregister_extension_class(class_name);
+#endif
+
if (ext->gdextension.parent != nullptr) {
ext->gdextension.parent->children.erase(&ext->gdextension);
}
+
+#ifdef TOOLS_ENABLED
+ if (!ext->is_reloading) {
+ self->extension_classes.erase(class_name);
+ }
+#else
self->extension_classes.erase(class_name);
+#endif
}
void GDExtension::_get_library_path(GDExtensionClassLibraryPtr p_library, GDExtensionUninitializedStringPtr r_path) {
@@ -448,14 +673,50 @@ void GDExtension::register_interface_function(StringName p_function_name, GDExte
GDExtensionInterfaceFunctionPtr GDExtension::get_interface_function(StringName p_function_name) {
GDExtensionInterfaceFunctionPtr *function = gdextension_interface_functions.getptr(p_function_name);
- ERR_FAIL_COND_V_MSG(function == nullptr, nullptr, "Attempt to get non-existent interface function: " + p_function_name);
+ ERR_FAIL_NULL_V_MSG(function, nullptr, "Attempt to get non-existent interface function: " + String(p_function_name) + ".");
return *function;
}
Error GDExtension::open_library(const String &p_path, const String &p_entry_symbol) {
- Error err = OS::get_singleton()->open_dynamic_library(p_path, library, true, &library_path);
+ library_path = p_path;
+
+ String abs_path = ProjectSettings::get_singleton()->globalize_path(p_path);
+#if defined(WINDOWS_ENABLED) && defined(TOOLS_ENABLED)
+ // If running on the editor on Windows, we copy the library and open the copy.
+ // This is so the original file isn't locked and can be updated by a compiler.
+ if (Engine::get_singleton()->is_editor_hint()) {
+ if (!FileAccess::exists(abs_path)) {
+ ERR_PRINT("GDExtension library not found: " + library_path);
+ return ERR_FILE_NOT_FOUND;
+ }
+
+ // Copy the file to the same directory as the original with a prefix in the name.
+ // This is so relative path to dependencies are satisfied.
+ String copy_path = abs_path.get_base_dir().path_join("~" + abs_path.get_file());
+
+ // If there's a left-over copy (possibly from a crash) then delete it first.
+ if (FileAccess::exists(copy_path)) {
+ DirAccess::remove_absolute(copy_path);
+ }
+
+ Error copy_err = DirAccess::copy_absolute(abs_path, copy_path);
+ if (copy_err) {
+ ERR_PRINT("Error copying GDExtension library: " + library_path);
+ return ERR_CANT_CREATE;
+ }
+ FileAccess::set_hidden_attribute(copy_path, true);
+
+ // Save the copied path so it can be deleted later.
+ temp_lib_path = copy_path;
+
+ // Use the copy to open the library.
+ abs_path = copy_path;
+ }
+#endif
+
+ Error err = OS::get_singleton()->open_dynamic_library(abs_path, library, true);
if (err != OK) {
- ERR_PRINT("GDExtension dynamic library not found: " + p_path);
+ ERR_PRINT("GDExtension dynamic library not found: " + abs_path);
return err;
}
@@ -464,7 +725,7 @@ Error GDExtension::open_library(const String &p_path, const String &p_entry_symb
err = OS::get_singleton()->get_dynamic_library_symbol_handle(library, p_entry_symbol, entry_funcptr, false);
if (err != OK) {
- ERR_PRINT("GDExtension entry point '" + p_entry_symbol + "' not found in library " + p_path);
+ ERR_PRINT("GDExtension entry point '" + p_entry_symbol + "' not found in library " + abs_path);
OS::get_singleton()->close_dynamic_library(library);
return err;
}
@@ -477,15 +738,28 @@ Error GDExtension::open_library(const String &p_path, const String &p_entry_symb
return OK;
} else {
ERR_PRINT("GDExtension initialization function '" + p_entry_symbol + "' returned an error.");
+ OS::get_singleton()->close_dynamic_library(library);
return FAILED;
}
}
void GDExtension::close_library() {
- ERR_FAIL_COND(library == nullptr);
+ ERR_FAIL_NULL(library);
OS::get_singleton()->close_dynamic_library(library);
+#if defined(TOOLS_ENABLED) && defined(WINDOWS_ENABLED)
+ // Delete temporary copy of library if it exists.
+ if (!temp_lib_path.is_empty() && Engine::get_singleton()->is_editor_hint()) {
+ DirAccess::remove_absolute(temp_lib_path);
+ }
+#endif
+
library = nullptr;
+ class_icon_paths.clear();
+
+#ifdef TOOLS_ENABLED
+ instance_bindings.clear();
+#endif
}
bool GDExtension::is_library_open() const {
@@ -493,22 +767,22 @@ bool GDExtension::is_library_open() const {
}
GDExtension::InitializationLevel GDExtension::get_minimum_library_initialization_level() const {
- ERR_FAIL_COND_V(library == nullptr, INITIALIZATION_LEVEL_CORE);
+ ERR_FAIL_NULL_V(library, INITIALIZATION_LEVEL_CORE);
return InitializationLevel(initialization.minimum_initialization_level);
}
void GDExtension::initialize_library(InitializationLevel p_level) {
- ERR_FAIL_COND(library == nullptr);
+ ERR_FAIL_NULL(library);
ERR_FAIL_COND_MSG(p_level <= int32_t(level_initialized), vformat("Level '%d' must be higher than the current level '%d'", p_level, level_initialized));
level_initialized = int32_t(p_level);
- ERR_FAIL_COND(initialization.initialize == nullptr);
+ ERR_FAIL_NULL(initialization.initialize);
initialization.initialize(initialization.userdata, GDExtensionInitializationLevel(p_level));
}
void GDExtension::deinitialize_library(InitializationLevel p_level) {
- ERR_FAIL_COND(library == nullptr);
+ ERR_FAIL_NULL(library);
ERR_FAIL_COND(p_level > int32_t(level_initialized));
level_initialized = int32_t(p_level) - 1;
@@ -536,12 +810,21 @@ GDExtension::~GDExtension() {
if (library != nullptr) {
close_library();
}
+#ifdef TOOLS_ENABLED
+ // If we have any invalid method binds still laying around, we can finally free them!
+ for (GDExtensionMethodBind *E : invalid_methods) {
+ memdelete(E);
+ }
+#endif
}
void GDExtension::initialize_gdextensions() {
gdextension_setup_interface();
+#ifndef DISABLE_DEPRECATED
register_interface_function("classdb_register_extension_class", (GDExtensionInterfaceFunctionPtr)&GDExtension::_register_extension_class);
+#endif // DISABLE_DEPRECATED
+ register_interface_function("classdb_register_extension_class2", (GDExtensionInterfaceFunctionPtr)&GDExtension::_register_extension_class2);
register_interface_function("classdb_register_extension_class_method", (GDExtensionInterfaceFunctionPtr)&GDExtension::_register_extension_class_method);
register_interface_function("classdb_register_extension_class_integer_constant", (GDExtensionInterfaceFunctionPtr)&GDExtension::_register_extension_class_integer_constant);
register_interface_function("classdb_register_extension_class_property", (GDExtensionInterfaceFunctionPtr)&GDExtension::_register_extension_class_property);
@@ -553,27 +836,22 @@ void GDExtension::initialize_gdextensions() {
register_interface_function("get_library_path", (GDExtensionInterfaceFunctionPtr)&GDExtension::_get_library_path);
}
-Ref<Resource> GDExtensionResourceLoader::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress, CacheMode p_cache_mode) {
+Error GDExtensionResourceLoader::load_gdextension_resource(const String &p_path, Ref<GDExtension> &p_extension) {
+ ERR_FAIL_COND_V_MSG(p_extension.is_valid() && p_extension->is_library_open(), ERR_ALREADY_IN_USE, "Cannot load GDExtension resource into already opened library.");
+
Ref<ConfigFile> config;
config.instantiate();
Error err = config->load(p_path);
- if (r_error) {
- *r_error = err;
- }
-
if (err != OK) {
ERR_PRINT("Error loading GDExtension configuration file: " + p_path);
- return Ref<Resource>();
+ return err;
}
if (!config->has_section_key("configuration", "entry_symbol")) {
- if (r_error) {
- *r_error = ERR_INVALID_DATA;
- }
ERR_PRINT("GDExtension configuration file must contain a \"configuration/entry_symbol\" key: " + p_path);
- return Ref<Resource>();
+ return ERR_INVALID_DATA;
}
String entry_symbol = config->get_value("configuration", "entry_symbol");
@@ -591,64 +869,67 @@ Ref<Resource> GDExtensionResourceLoader::load(const String &p_path, const String
}
}
} else {
- if (r_error) {
- *r_error = ERR_INVALID_DATA;
- }
ERR_PRINT("GDExtension configuration file must contain a \"configuration/compatibility_minimum\" key: " + p_path);
- return Ref<Resource>();
+ return ERR_INVALID_DATA;
}
if (compatibility_minimum[0] < 4 || (compatibility_minimum[0] == 4 && compatibility_minimum[1] == 0)) {
- if (r_error) {
- *r_error = ERR_INVALID_DATA;
- }
ERR_PRINT(vformat("GDExtension's compatibility_minimum (%d.%d.%d) must be at least 4.1.0: %s", compatibility_minimum[0], compatibility_minimum[1], compatibility_minimum[2], p_path));
- return Ref<Resource>();
+ return ERR_INVALID_DATA;
}
bool compatible = true;
- if (VERSION_MAJOR < compatibility_minimum[0]) {
- compatible = false;
- } else if (VERSION_MINOR < compatibility_minimum[1]) {
- compatible = false;
- } else if (VERSION_PATCH < compatibility_minimum[2]) {
- compatible = false;
+ // Check version lexicographically.
+ if (VERSION_MAJOR != compatibility_minimum[0]) {
+ compatible = VERSION_MAJOR > compatibility_minimum[0];
+ } else if (VERSION_MINOR != compatibility_minimum[1]) {
+ compatible = VERSION_MINOR > compatibility_minimum[1];
+ } else {
+ compatible = VERSION_PATCH >= compatibility_minimum[2];
}
if (!compatible) {
- if (r_error) {
- *r_error = ERR_INVALID_DATA;
- }
ERR_PRINT(vformat("GDExtension only compatible with Godot version %d.%d.%d or later: %s", compatibility_minimum[0], compatibility_minimum[1], compatibility_minimum[2], p_path));
- return Ref<Resource>();
+ return ERR_INVALID_DATA;
}
String library_path = GDExtension::find_extension_library(p_path, config, [](String p_feature) { return OS::get_singleton()->has_feature(p_feature); });
if (library_path.is_empty()) {
- if (r_error) {
- *r_error = ERR_FILE_NOT_FOUND;
- }
const String os_arch = OS::get_singleton()->get_name().to_lower() + "." + Engine::get_singleton()->get_architecture_name();
ERR_PRINT(vformat("No GDExtension library found for current OS and architecture (%s) in configuration file: %s", os_arch, p_path));
- return Ref<Resource>();
+ return ERR_FILE_NOT_FOUND;
}
if (!library_path.is_resource_file() && !library_path.is_absolute_path()) {
library_path = p_path.get_base_dir().path_join(library_path);
}
- Ref<GDExtension> lib;
- lib.instantiate();
- String abs_path = ProjectSettings::get_singleton()->globalize_path(library_path);
- err = lib->open_library(abs_path, entry_symbol);
-
- if (r_error) {
- *r_error = err;
+ if (p_extension.is_null()) {
+ p_extension.instantiate();
}
+#ifdef TOOLS_ENABLED
+ p_extension->set_reloadable(config->get_value("configuration", "reloadable", false) && Engine::get_singleton()->is_extension_reloading_enabled());
+
+ p_extension->update_last_modified_time(MAX(
+ FileAccess::get_modified_time(library_path),
+ FileAccess::get_modified_time(p_path)));
+#endif
+
+ err = p_extension->open_library(library_path, entry_symbol);
if (err != OK) {
+#if defined(WINDOWS_ENABLED) && defined(TOOLS_ENABLED)
+ // If the DLL fails to load, make sure that temporary DLL copies are cleaned up.
+ if (Engine::get_singleton()->is_editor_hint()) {
+ DirAccess::remove_absolute(p_extension->get_temp_library_path());
+ }
+#endif
+
+ // Unreference the extension so that this loading can be considered a failure.
+ p_extension.unref();
+
// Errors already logged in open_library()
- return Ref<Resource>();
+ return err;
}
// Handle icons if any are specified.
@@ -656,10 +937,32 @@ Ref<Resource> GDExtensionResourceLoader::load(const String &p_path, const String
List<String> keys;
config->get_section_keys("icons", &keys);
for (const String &key : keys) {
- lib->class_icon_paths[key] = config->get_value("icons", key);
+ String icon_path = config->get_value("icons", key);
+ if (icon_path.is_relative_path()) {
+ icon_path = p_path.get_base_dir().path_join(icon_path);
+ }
+
+ p_extension->class_icon_paths[key] = icon_path;
}
}
+ return OK;
+}
+
+Ref<Resource> GDExtensionResourceLoader::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress, CacheMode p_cache_mode) {
+ // We can't have two GDExtension resource object representing the same library, because
+ // loading (or unloading) a GDExtension affects global data. So, we need reuse the same
+ // object if one has already been loaded (even if caching is disabled at the resource
+ // loader level).
+ GDExtensionManager *manager = GDExtensionManager::get_singleton();
+ Ref<GDExtension> lib = manager->get_extension(p_path);
+ if (lib.is_null()) {
+ Error err = load_gdextension_resource(p_path, lib);
+ if (err != OK && r_error) {
+ // Errors already logged in load_gdextension_resource().
+ *r_error = err;
+ }
+ }
return lib;
}
@@ -680,6 +983,183 @@ String GDExtensionResourceLoader::get_resource_type(const String &p_path) const
}
#ifdef TOOLS_ENABLED
+bool GDExtension::has_library_changed() const {
+ if (FileAccess::get_modified_time(get_path()) > last_modified_time) {
+ return true;
+ }
+ if (FileAccess::get_modified_time(library_path) > last_modified_time) {
+ return true;
+ }
+ return false;
+}
+
+void GDExtension::prepare_reload() {
+ is_reloading = true;
+
+ for (KeyValue<StringName, Extension> &E : extension_classes) {
+ E.value.is_reloading = true;
+
+ for (KeyValue<StringName, GDExtensionMethodBind *> &M : E.value.methods) {
+ M.value->is_reloading = true;
+ }
+
+ for (const ObjectID &obj_id : E.value.instances) {
+ Object *obj = ObjectDB::get_instance(obj_id);
+ if (!obj) {
+ continue;
+ }
+
+ // Store instance state so it can be restored after reload.
+ List<Pair<String, Variant>> state;
+ List<PropertyInfo> prop_list;
+ obj->get_property_list(&prop_list);
+ for (const PropertyInfo &P : prop_list) {
+ if (!(P.usage & PROPERTY_USAGE_STORAGE)) {
+ continue;
+ }
+
+ Variant value = obj->get(P.name);
+ Variant default_value = ClassDB::class_get_default_property_value(obj->get_class_name(), P.name);
+
+ if (default_value.get_type() != Variant::NIL && bool(Variant::evaluate(Variant::OP_EQUAL, value, default_value))) {
+ continue;
+ }
+
+ if (P.type == Variant::OBJECT && value.is_zero() && !(P.usage & PROPERTY_USAGE_STORE_IF_NULL)) {
+ continue;
+ }
+
+ state.push_back(Pair<String, Variant>(P.name, value));
+ }
+ E.value.instance_state[obj_id] = state;
+ }
+ }
+}
+
+void GDExtension::_clear_extension(Extension *p_extension) {
+ // Clear out hierarchy information because it may change.
+ p_extension->gdextension.parent = nullptr;
+ p_extension->gdextension.children.clear();
+
+ // Clear all objects of any GDExtension data. It will become its native parent class
+ // until the reload can reset the object with the new GDExtension data.
+ for (const ObjectID &obj_id : p_extension->instances) {
+ Object *obj = ObjectDB::get_instance(obj_id);
+ if (!obj) {
+ continue;
+ }
+
+ obj->clear_internal_extension();
+ }
+}
+
+void GDExtension::track_instance_binding(Object *p_object) {
+ instance_bindings.push_back(p_object->get_instance_id());
+}
+
+void GDExtension::untrack_instance_binding(Object *p_object) {
+ instance_bindings.erase(p_object->get_instance_id());
+}
+
+void GDExtension::clear_instance_bindings() {
+ for (ObjectID obj_id : instance_bindings) {
+ Object *obj = ObjectDB::get_instance(obj_id);
+ if (!obj) {
+ continue;
+ }
+
+ obj->free_instance_binding(this);
+ }
+ instance_bindings.clear();
+}
+
+void GDExtension::finish_reload() {
+ is_reloading = false;
+
+ // Clean up any classes or methods that didn't get re-added.
+ Vector<StringName> classes_to_remove;
+ for (KeyValue<StringName, Extension> &E : extension_classes) {
+ if (E.value.is_reloading) {
+ E.value.is_reloading = false;
+ classes_to_remove.push_back(E.key);
+ }
+
+ Vector<StringName> methods_to_remove;
+ for (KeyValue<StringName, GDExtensionMethodBind *> &M : E.value.methods) {
+ if (M.value->is_reloading) {
+ M.value->valid = false;
+ invalid_methods.push_back(M.value);
+
+ M.value->is_reloading = false;
+ methods_to_remove.push_back(M.key);
+ }
+ }
+ for (const StringName &method_name : methods_to_remove) {
+ E.value.methods.erase(method_name);
+ }
+ }
+ for (const StringName &class_name : classes_to_remove) {
+ extension_classes.erase(class_name);
+ }
+
+ // Reset any the extension on instances made from the classes that remain.
+ for (KeyValue<StringName, Extension> &E : extension_classes) {
+ // Loop over 'instance_state' rather than 'instance' because new instances
+ // may have been created when re-initializing the extension.
+ for (const KeyValue<ObjectID, List<Pair<String, Variant>>> &S : E.value.instance_state) {
+ Object *obj = ObjectDB::get_instance(S.key);
+ if (!obj) {
+ continue;
+ }
+
+ obj->reset_internal_extension(&E.value.gdextension);
+ }
+ }
+
+ // Now that all the classes are back, restore the state.
+ for (KeyValue<StringName, Extension> &E : extension_classes) {
+ for (const KeyValue<ObjectID, List<Pair<String, Variant>>> &S : E.value.instance_state) {
+ Object *obj = ObjectDB::get_instance(S.key);
+ if (!obj) {
+ continue;
+ }
+
+ for (const Pair<String, Variant> &state : S.value) {
+ obj->set(state.first, state.second);
+ }
+ }
+ }
+
+ // Finally, let the objects know that we are done reloading them.
+ for (KeyValue<StringName, Extension> &E : extension_classes) {
+ for (const KeyValue<ObjectID, List<Pair<String, Variant>>> &S : E.value.instance_state) {
+ Object *obj = ObjectDB::get_instance(S.key);
+ if (!obj) {
+ continue;
+ }
+
+ obj->notification(NOTIFICATION_EXTENSION_RELOADED);
+ }
+
+ // Clear the instance state, we're done looping.
+ E.value.instance_state.clear();
+ }
+}
+
+void GDExtension::_track_instance(void *p_user_data, void *p_instance) {
+ Extension *extension = reinterpret_cast<Extension *>(p_user_data);
+ Object *obj = reinterpret_cast<Object *>(p_instance);
+
+ extension->instances.insert(obj->get_instance_id());
+}
+
+void GDExtension::_untrack_instance(void *p_user_data, void *p_instance) {
+ Extension *extension = reinterpret_cast<Extension *>(p_user_data);
+ Object *obj = reinterpret_cast<Object *>(p_instance);
+
+ extension->instances.erase(obj->get_instance_id());
+}
+
Vector<StringName> GDExtensionEditorPlugins::extension_classes;
GDExtensionEditorPlugins::EditorPluginRegisterFunc GDExtensionEditorPlugins::editor_node_add_plugin = nullptr;
GDExtensionEditorPlugins::EditorPluginRegisterFunc GDExtensionEditorPlugins::editor_node_remove_plugin = nullptr;
diff --git a/core/extension/gdextension.h b/core/extension/gdextension.h
index b935f8706f..d71b1f9704 100644
--- a/core/extension/gdextension.h
+++ b/core/extension/gdextension.h
@@ -38,19 +38,44 @@
#include "core/io/resource_loader.h"
#include "core/object/ref_counted.h"
+class GDExtensionMethodBind;
+
class GDExtension : public Resource {
GDCLASS(GDExtension, Resource)
+ friend class GDExtensionManager;
+
void *library = nullptr; // pointer if valid,
String library_path;
+#if defined(WINDOWS_ENABLED) && defined(TOOLS_ENABLED)
+ String temp_lib_path;
+#endif
+ bool reloadable = false;
struct Extension {
ObjectGDExtension gdextension;
+
+#ifdef TOOLS_ENABLED
+ bool is_reloading = false;
+ HashMap<StringName, GDExtensionMethodBind *> methods;
+ HashSet<ObjectID> instances;
+ HashMap<ObjectID, List<Pair<String, Variant>>> instance_state;
+#endif
};
HashMap<StringName, Extension> extension_classes;
+ struct ClassCreationDeprecatedInfo {
+#ifndef DISABLE_DEPRECATED
+ GDExtensionClassNotification notification_func = nullptr;
+#endif // DISABLE_DEPRECATED
+ };
+
+#ifndef DISABLE_DEPRECATED
static void _register_extension_class(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_parent_class_name, const GDExtensionClassCreationInfo *p_extension_funcs);
+#endif // DISABLE_DEPRECATED
+ static void _register_extension_class2(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_parent_class_name, const GDExtensionClassCreationInfo2 *p_extension_funcs);
+ static void _register_extension_class_internal(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_parent_class_name, const GDExtensionClassCreationInfo2 *p_extension_funcs, const ClassCreationDeprecatedInfo *p_deprecated_funcs = nullptr);
static void _register_extension_class_method(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, const GDExtensionClassMethodInfo *p_method_info);
static void _register_extension_class_integer_constant(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_enum_name, GDExtensionConstStringNamePtr p_constant_name, GDExtensionInt p_constant_value, GDExtensionBool p_is_bitfield);
static void _register_extension_class_property(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, const GDExtensionPropertyInfo *p_info, GDExtensionConstStringNamePtr p_setter, GDExtensionConstStringNamePtr p_getter);
@@ -64,18 +89,41 @@ class GDExtension : public Resource {
GDExtensionInitialization initialization;
int32_t level_initialized = -1;
+#ifdef TOOLS_ENABLED
+ uint64_t last_modified_time = 0;
+ bool is_reloading = false;
+ Vector<GDExtensionMethodBind *> invalid_methods;
+ Vector<ObjectID> instance_bindings;
+
+ static void _track_instance(void *p_user_data, void *p_instance);
+ static void _untrack_instance(void *p_user_data, void *p_instance);
+
+ void _clear_extension(Extension *p_extension);
+
+ // Only called by GDExtensionManager during the reload process.
+ void prepare_reload();
+ void finish_reload();
+ void clear_instance_bindings();
+#endif
+
protected:
static void _bind_methods();
public:
HashMap<String, String> class_icon_paths;
+ virtual bool editor_can_reload_from_file() override { return false; } // Reloading is handled in a special way.
+
static String get_extension_list_config_file();
static String find_extension_library(const String &p_path, Ref<ConfigFile> p_config, std::function<bool(String)> p_has_feature, PackedStringArray *r_tags = nullptr);
Error open_library(const String &p_path, const String &p_entry_symbol);
void close_library();
+#if defined(WINDOWS_ENABLED) && defined(TOOLS_ENABLED)
+ String get_temp_library_path() const { return temp_lib_path; }
+#endif
+
enum InitializationLevel {
INITIALIZATION_LEVEL_CORE = GDEXTENSION_INITIALIZATION_CORE,
INITIALIZATION_LEVEL_SERVERS = GDEXTENSION_INITIALIZATION_SERVERS,
@@ -85,6 +133,19 @@ public:
bool is_library_open() const;
+#ifdef TOOLS_ENABLED
+ bool is_reloadable() const { return reloadable; }
+ void set_reloadable(bool p_reloadable) { reloadable = p_reloadable; }
+
+ bool has_library_changed() const;
+ void update_last_modified_time(uint64_t p_last_modified_time) {
+ last_modified_time = MAX(last_modified_time, p_last_modified_time);
+ }
+
+ void track_instance_binding(Object *p_object);
+ void untrack_instance_binding(Object *p_object);
+#endif
+
InitializationLevel get_minimum_library_initialization_level() const;
void initialize_library(InitializationLevel p_level);
void deinitialize_library(InitializationLevel p_level);
@@ -101,6 +162,8 @@ VARIANT_ENUM_CAST(GDExtension::InitializationLevel)
class GDExtensionResourceLoader : public ResourceFormatLoader {
public:
+ static Error load_gdextension_resource(const String &p_path, Ref<GDExtension> &p_extension);
+
virtual Ref<Resource> load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads = false, float *r_progress = nullptr, CacheMode p_cache_mode = CACHE_MODE_REUSE);
virtual void get_recognized_extensions(List<String> *p_extensions) const;
virtual bool handles_type(const String &p_type) const;
diff --git a/core/extension/gdextension_compat_hashes.cpp b/core/extension/gdextension_compat_hashes.cpp
new file mode 100644
index 0000000000..9c8d6b3e7f
--- /dev/null
+++ b/core/extension/gdextension_compat_hashes.cpp
@@ -0,0 +1,843 @@
+/**************************************************************************/
+/* gdextension_compat_hashes.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#include "gdextension_compat_hashes.h"
+
+#ifndef DISABLE_DEPRECATED
+
+#include "core/variant/variant.h"
+
+HashMap<StringName, LocalVector<GDExtensionCompatHashes::Mapping>> GDExtensionCompatHashes::mappings;
+
+bool GDExtensionCompatHashes::lookup_current_hash(const StringName &p_class, const StringName &p_method, uint32_t p_legacy_hash, uint32_t *r_current_hash) {
+ LocalVector<Mapping> *methods = mappings.getptr(p_class);
+ if (!methods) {
+ return false;
+ }
+
+ for (const Mapping &mapping : *methods) {
+ if (mapping.method == p_method && mapping.legacy_hash == p_legacy_hash) {
+ *r_current_hash = mapping.current_hash;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool GDExtensionCompatHashes::get_legacy_hashes(const StringName &p_class, const StringName &p_method, Array &r_hashes) {
+ LocalVector<Mapping> *methods = mappings.getptr(p_class);
+ if (!methods) {
+ return false;
+ }
+
+ bool found = false;
+ for (const Mapping &mapping : *methods) {
+ if (mapping.method == p_method) {
+ r_hashes.push_back(mapping.legacy_hash);
+ found = true;
+ }
+ }
+
+ return found;
+}
+
+void GDExtensionCompatHashes::initialize() {
+ // clang-format off
+ mappings.insert("AESContext", {
+ { "start", 3167574919, 3122411423 },
+ });
+ mappings.insert("AStar2D", {
+ { "add_point", 3370185124, 4074201818 },
+ { "set_point_disabled", 4023243586, 972357352 },
+ { "connect_points", 3785370599, 3710494224 },
+ { "disconnect_points", 3785370599, 3710494224 },
+ { "are_points_connected", 4063588998, 2288175859 },
+ });
+ mappings.insert("AStar3D", {
+ { "add_point", 2920922839, 1038703438 },
+ { "set_point_disabled", 4023243586, 972357352 },
+ { "connect_points", 3785370599, 3710494224 },
+ { "disconnect_points", 3785370599, 3710494224 },
+ { "are_points_connected", 4063588998, 2288175859 },
+ });
+ mappings.insert("AStarGrid2D", {
+ { "set_point_solid", 2825551965, 1765703753 },
+ { "fill_solid_region", 1152863744, 2261970063 },
+ });
+ mappings.insert("AcceptDialog", {
+ { "add_button", 4158837846, 3328440682 },
+ });
+ mappings.insert("Animation", {
+ { "add_track", 2393815928, 3843682357 },
+ { "track_insert_key", 1985425300, 808952278 },
+ { "track_find_key", 3898229885, 3245197284 },
+ { "bezier_track_insert_key", 1057544502, 3656773645 },
+ { "bezier_track_set_key_in_handle", 1028302688, 1719223284 },
+ { "bezier_track_set_key_out_handle", 1028302688, 1719223284 },
+ { "audio_track_insert_key", 3489962123, 4021027286 },
+ });
+ mappings.insert("AnimationNode", {
+ { "blend_animation", 11797022, 1630801826 },
+ { "blend_node", 263389446, 1746075988 },
+ { "blend_input", 2709059328, 1361527350 },
+ });
+ mappings.insert("AnimationNodeBlendSpace1D", {
+ { "add_blend_point", 4069484420, 285050433 },
+ });
+ mappings.insert("AnimationNodeBlendSpace2D", {
+ { "add_blend_point", 1533588937, 402261981 },
+ { "add_triangle", 642454959, 753017335 },
+ });
+ mappings.insert("AnimationNodeBlendTree", {
+ { "add_node", 2055804584, 1980270704 },
+ });
+ mappings.insert("AnimationNodeStateMachine", {
+ { "add_node", 2055804584, 1980270704 },
+ });
+ mappings.insert("AnimationNodeStateMachinePlayback", {
+ { "travel", 3683006648, 3823612587 },
+ { "start", 3683006648, 3823612587 },
+ });
+ mappings.insert("ArrayMesh", {
+ { "add_surface_from_arrays", 172284304, 1796411378 },
+ });
+ mappings.insert("AudioEffectSpectrumAnalyzerInstance", {
+ { "get_magnitude_for_frequency_range", 2693213071, 797993915 },
+ });
+ mappings.insert("AudioServer", {
+ { "add_bus_effect", 4147765248, 4068819785 },
+ { "get_bus_effect_instance", 2887144608, 1829771234 },
+ });
+ mappings.insert("AudioStreamPlaybackPolyphonic", {
+ { "play_stream", 3792189967, 604492179 },
+ });
+ mappings.insert("AudioStreamRandomizer", {
+ { "add_stream", 3197802065, 1892018854 },
+ });
+ mappings.insert("BitMap", {
+ { "create_from_image_alpha", 505265891, 106271684 },
+ { "opaque_to_polygons", 876132484, 48478126 },
+ });
+ mappings.insert("CanvasItem", {
+ { "draw_line", 2516941890, 1562330099 },
+ { "draw_dashed_line", 2175215884, 684651049 },
+ { "draw_polyline", 4175878946, 3797364428 },
+ { "draw_polyline_colors", 2239164197, 2311979562 },
+ { "draw_arc", 3486841771, 4140652635 },
+ { "draw_multiline", 4230657331, 2239075205 },
+ { "draw_multiline_colors", 235933050, 4072951537 },
+ { "draw_rect", 84391229, 2417231121 },
+ { "draw_texture", 1695860435, 520200117 },
+ { "draw_texture_rect", 3204081724, 3832805018 },
+ { "draw_texture_rect_region", 3196597532, 3883821411 },
+ { "draw_msdf_texture_rect_region", 2672026175, 4219163252 },
+ { "draw_lcd_texture_rect_region", 169610548, 3212350954 },
+ { "draw_primitive", 2248678295, 3288481815 },
+ { "draw_polygon", 2683625537, 974537912 },
+ { "draw_colored_polygon", 1659099617, 15245644 },
+ { "draw_string", 2552080639, 728290553 },
+ { "draw_multiline_string", 4002645436, 1927038192 },
+ { "draw_string_outline", 850005221, 340562381 },
+ { "draw_multiline_string_outline", 3717870722, 1912318525 },
+ { "draw_char", 2329089032, 3339793283 },
+ { "draw_char_outline", 419453826, 3302344391 },
+ { "draw_mesh", 1634855856, 153818295 },
+ { "draw_set_transform", 3283884939, 288975085 },
+ { "draw_animation_slice", 2295343543, 3112831842 },
+ });
+ mappings.insert("CodeEdit", {
+ { "is_in_string", 3294126239, 688195400 },
+ { "is_in_comment", 3294126239, 688195400 },
+ { "add_code_completion_option", 1629240608, 947964390 },
+ });
+ mappings.insert("Control", {
+ { "set_offsets_preset", 3651818904, 3724524307 },
+ { "set_anchors_and_offsets_preset", 3651818904, 3724524307 },
+ { "set_anchor", 2589937826, 2302782885 },
+ { "get_theme_icon", 2336455395, 3163973443 },
+ { "get_theme_stylebox", 2759935355, 604739069 },
+ { "get_theme_font", 387378635, 2826986490 },
+ { "get_theme_font_size", 229578101, 1327056374 },
+ { "get_theme_color", 2377051548, 2798751242 },
+ { "get_theme_constant", 229578101, 1327056374 },
+ { "has_theme_icon", 1187511791, 866386512 },
+ { "has_theme_stylebox", 1187511791, 866386512 },
+ { "has_theme_font", 1187511791, 866386512 },
+ { "has_theme_font_size", 1187511791, 866386512 },
+ { "has_theme_color", 1187511791, 866386512 },
+ { "has_theme_constant", 1187511791, 866386512 },
+ });
+ mappings.insert("Crypto", {
+ { "generate_self_signed_certificate", 947314696, 492266173 },
+ });
+ mappings.insert("Curve", {
+ { "add_point", 2766148617, 434072736 },
+ });
+ mappings.insert("Curve2D", {
+ { "add_point", 2437345566, 4175465202 },
+ });
+ mappings.insert("Curve3D", {
+ { "add_point", 3836314258, 2931053748 },
+ });
+ mappings.insert("DirAccess", {
+ { "list_dir_begin", 2018049411, 2610976713 },
+ { "copy", 198434953, 1063198817 },
+ { "copy_absolute", 198434953, 1063198817 },
+ });
+ mappings.insert("DisplayServer", {
+ { "global_menu_add_submenu_item", 3806306913, 2828985934 },
+ { "global_menu_add_item", 3415468211, 3401266716 },
+ { "global_menu_add_check_item", 3415468211, 3401266716 },
+ { "global_menu_add_icon_item", 1700867534, 4245856523 },
+ { "global_menu_add_icon_check_item", 1700867534, 4245856523 },
+ { "global_menu_add_radio_check_item", 3415468211, 3401266716 },
+ { "global_menu_add_icon_radio_check_item", 1700867534, 4245856523 },
+ { "global_menu_add_multistate_item", 635750054, 3431222859 },
+ { "global_menu_add_separator", 1041533178, 3214812433 },
+ { "tts_speak", 3741216677, 903992738 },
+ { "is_touchscreen_available", 4162880507, 3323674545 },
+ { "screen_set_orientation", 2629526904, 2211511631 },
+ { "window_get_native_handle", 2709193271, 1096425680 },
+ { "window_set_title", 3043792800, 441246282 },
+ { "window_set_mouse_passthrough", 3958815166, 1993637420 },
+ { "window_set_current_screen", 3023605688, 2230941749 },
+ { "window_set_position", 3614040015, 2019273902 },
+ { "window_set_size", 3614040015, 2019273902 },
+ { "window_set_rect_changed_callback", 3653650673, 1091192925 },
+ { "window_set_window_event_callback", 3653650673, 1091192925 },
+ { "window_set_input_event_callback", 3653650673, 1091192925 },
+ { "window_set_input_text_callback", 3653650673, 1091192925 },
+ { "window_set_drop_files_callback", 3653650673, 1091192925 },
+ { "window_set_max_size", 3614040015, 2019273902 },
+ { "window_set_min_size", 3614040015, 2019273902 },
+ { "window_set_mode", 2942569511, 1319965401 },
+ { "window_set_flag", 3971592565, 254894155 },
+ { "window_get_flag", 2662949986, 802816991 },
+ { "window_set_window_buttons_offset", 3614040015, 2019273902 },
+ { "window_set_ime_active", 450484987, 1661950165 },
+ { "window_set_ime_position", 3614040015, 2019273902 },
+ { "window_set_vsync_mode", 1708924624, 2179333492 },
+ { "virtual_keyboard_show", 860410478, 3042891259 },
+ { "cursor_set_custom_image", 1358907026, 1816663697 },
+ });
+ mappings.insert("ENetConnection", {
+ { "create_host_bound", 866250949, 1515002313 },
+ { "connect_to_host", 385984708, 2171300490 },
+ { "dtls_client_setup", 3097527179, 1966198364 },
+ });
+ mappings.insert("ENetMultiplayerPeer", {
+ { "create_server", 1616151701, 2917761309 },
+ { "create_client", 920217784, 2327163476 },
+ });
+ mappings.insert("EditorCommandPalette", {
+ { "add_command", 3664614892, 864043298 },
+ });
+ mappings.insert("EditorDebuggerSession", {
+ { "send_message", 3780025912, 85656714 },
+ { "toggle_profiler", 35674246, 1198443697 },
+ });
+ mappings.insert("EditorFileDialog", {
+ { "add_filter", 233059325, 3388804757 },
+ });
+ mappings.insert("EditorImportPlugin", {
+ { "append_import_external_resource", 3645925746, 320493106 },
+ });
+ mappings.insert("EditorInterface", {
+ { "popup_dialog", 2478844058, 2015770942 },
+ { "popup_dialog_centered", 1723337679, 346557367 },
+ { "popup_dialog_centered_ratio", 1310934579, 2093669136 },
+ { "popup_dialog_centered_clamped", 3433759678, 3763385571 },
+ { "inspect_object", 2564140749, 127962172 },
+ { "edit_script", 3664508569, 219829402 },
+ { "save_scene_as", 1168363258, 3647332257 },
+ });
+ mappings.insert("EditorNode3DGizmo", {
+ { "add_lines", 302451090, 2910971437 },
+ { "add_mesh", 1868867708, 1579955111 },
+ { "add_unscaled_billboard", 3719733075, 520007164 },
+ });
+ mappings.insert("EditorNode3DGizmoPlugin", {
+ { "create_icon_material", 2976007329, 3804976916 },
+ { "get_material", 3501703615, 974464017 },
+ });
+ mappings.insert("EditorScenePostImportPlugin", {
+ { "add_import_option_advanced", 3774155785, 3674075649 },
+ });
+ mappings.insert("EditorUndoRedoManager", {
+ { "create_action", 3577985681, 2107025470 },
+ });
+ mappings.insert("EngineDebugger", {
+ { "profiler_enable", 438160728, 3192561009 },
+ });
+ mappings.insert("Expression", {
+ { "parse", 3658149758, 3069722906 },
+ });
+ mappings.insert("FileAccess", {
+ { "open_compressed", 2874458257, 3686439335 },
+ { "store_csv_line", 2217842308, 2173791505 },
+ });
+ mappings.insert("FileDialog", {
+ { "add_filter", 233059325, 3388804757 },
+ });
+ mappings.insert("Font", {
+ { "find_variation", 1222433716, 3344325384 },
+ { "get_string_size", 3678918099, 1868866121 },
+ { "get_multiline_string_size", 2427690650, 519636710 },
+ { "draw_string", 2565402639, 1983721962 },
+ { "draw_multiline_string", 348869189, 1171506176 },
+ { "draw_string_outline", 657875837, 623754045 },
+ { "draw_multiline_string_outline", 1649790182, 3206388178 },
+ { "draw_char", 1462476057, 3815617597 },
+ { "draw_char_outline", 4161008124, 209525354 },
+ // Pre-existing compatibility hash.
+ { "find_variation", 1149405976, 1851767612 },
+ });
+ mappings.insert("GLTFDocument", {
+ { "append_from_file", 1862991421, 866380864 },
+ { "append_from_buffer", 2818062664, 1616081266 },
+ { "append_from_scene", 374125375, 1622574258 },
+ { "generate_scene", 2770277081, 596118388 },
+ });
+ mappings.insert("Geometry2D", {
+ { "offset_polygon", 3837618924, 1275354010 },
+ { "offset_polyline", 328033063, 2328231778 },
+ });
+ mappings.insert("Geometry3D", {
+ { "build_cylinder_planes", 3142160516, 449920067 },
+ { "build_capsule_planes", 410870045, 2113592876 },
+ });
+ mappings.insert("GraphNode", {
+ { "set_slot", 902131739, 2873310869 },
+ });
+ mappings.insert("GridMap", {
+ { "set_cell_item", 4177201334, 3449088946 },
+ });
+ mappings.insert("HTTPClient", {
+ { "connect_to_host", 1970282951, 504540374 },
+ { "request", 3249905507, 3778990155 },
+ });
+ mappings.insert("HTTPRequest", {
+ { "request", 2720304520, 3215244323 },
+ { "request_raw", 4282724657, 2714829993 },
+ });
+ mappings.insert("IP", {
+ { "resolve_hostname", 396864159, 4283295457 },
+ { "resolve_hostname_addresses", 3462780090, 773767525 },
+ { "resolve_hostname_queue_item", 3936392508, 1749894742 },
+ });
+ mappings.insert("Image", {
+ { "resize", 2461393748, 994498151 },
+ { "save_jpg", 578836491, 2800019068 },
+ { "save_webp", 3594949219, 2781156876 },
+ { "compress", 4094210332, 2975424957 },
+ { "compress_from_channels", 279105990, 4212890953 },
+ { "load_svg_from_buffer", 1822513750, 311853421 },
+ { "load_svg_from_string", 1461766635, 3254053600 },
+ });
+ mappings.insert("ImmediateMesh", {
+ { "surface_begin", 3716480242, 2794442543 },
+ });
+ mappings.insert("ImporterMesh", {
+ { "add_surface", 4122361985, 1740448849 },
+ });
+ mappings.insert("Input", {
+ { "get_vector", 1517139831, 2479607902 },
+ { "start_joy_vibration", 1890603622, 2576575033 },
+ { "action_press", 573731101, 1713091165 },
+ { "set_custom_mouse_cursor", 3489634142, 703945977 },
+ });
+ mappings.insert("InputEvent", {
+ { "is_match", 3392494811, 1754951977 },
+ { "xformed_by", 2747409789, 1282766827 },
+ });
+ mappings.insert("InputMap", {
+ { "add_action", 573731101, 4100757082 },
+ });
+ mappings.insert("ItemList", {
+ { "add_item", 4086250691, 359861678 },
+ { "add_icon_item", 3332687421, 4256579627 },
+ { "get_item_rect", 1501513492, 159227807 },
+ { "select", 4023243586, 972357352 },
+ });
+ mappings.insert("JSON", {
+ { "stringify", 2656701787, 462733549 },
+ });
+ mappings.insert("JavaScriptBridge", {
+ { "download_buffer", 4123979296, 3352272093 },
+ });
+ mappings.insert("Line2D", {
+ { "add_point", 468506575, 2654014372 },
+ });
+ mappings.insert("MultiplayerAPI", {
+ { "rpc", 1833408346, 2077486355 },
+ });
+ mappings.insert("NavigationMeshGenerator", {
+ { "parse_source_geometry_data", 3703028813, 685862123 },
+ { "bake_from_source_geometry_data", 3669016597, 2469318639 },
+ });
+ mappings.insert("NavigationServer2D", {
+ { "map_get_path", 56240621, 3146466012 },
+ });
+ mappings.insert("NavigationServer3D", {
+ { "map_get_path", 2121045993, 1187418690 },
+ { "parse_source_geometry_data", 3703028813, 685862123 },
+ { "bake_from_source_geometry_data", 3669016597, 2469318639 },
+ { "bake_from_source_geometry_data_async", 3669016597, 2469318639 },
+ });
+ mappings.insert("Node", {
+ { "add_child", 3070154285, 3863233950 },
+ { "reparent", 2570952461, 3685795103 },
+ { "find_child", 4253159453, 2008217037 },
+ { "find_children", 1585018254, 2560337219 },
+ { "propagate_call", 1667910434, 1871007965 },
+ { "set_multiplayer_authority", 4023243586, 972357352 },
+ });
+ mappings.insert("Node3D", {
+ { "look_at", 3123400617, 2882425029 },
+ { "look_at_from_position", 4067663783, 2086826090 },
+ });
+ mappings.insert("Noise", {
+ { "get_image", 2569233413, 3180683109 },
+ { "get_seamless_image", 2210827790, 2770743602 },
+ { "get_image_3d", 2358868431, 3977814329 },
+ { "get_seamless_image_3d", 3328694319, 451006340 },
+ });
+ mappings.insert("OS", {
+ { "alert", 233059325, 1783970740 },
+ { "get_system_font_path", 2262142305, 626580860 },
+ { "get_system_font_path_for_text", 3824042574, 197317981 },
+ { "execute", 2881709059, 1488299882 },
+ { "shell_show_in_file_manager", 885841341, 3565188097 },
+ { "set_restart_on_exit", 611198603, 3331453935 },
+ { "get_system_dir", 1965199849, 3073895123 },
+ });
+ mappings.insert("Object", {
+ { "add_user_signal", 3780025912, 85656714 },
+ { "connect", 1469446357, 1518946055 },
+ { "tr", 2475554935, 1195764410 },
+ { "tr_n", 4021311862, 162698058 },
+ });
+ mappings.insert("OptionButton", {
+ { "add_item", 3043792800, 2697778442 },
+ { "add_icon_item", 3944051090, 3781678508 },
+ });
+ mappings.insert("PCKPacker", {
+ { "pck_start", 3232891339, 508410629 },
+ });
+ mappings.insert("PacketPeerDTLS", {
+ { "connect_to_peer", 1801538152, 2880188099 },
+ });
+ mappings.insert("PacketPeerUDP", {
+ { "bind", 4290438434, 4051239242 },
+ });
+ mappings.insert("Performance", {
+ { "add_custom_monitor", 2865980031, 4099036814 },
+ });
+ mappings.insert("PhysicalBone3D", {
+ { "apply_impulse", 1002852006, 2754756483 },
+ });
+ mappings.insert("PhysicsBody2D", {
+ { "move_and_collide", 1529961754, 3681923724 },
+ { "test_move", 1369208982, 3324464701 },
+ });
+ mappings.insert("PhysicsBody3D", {
+ { "move_and_collide", 2825704414, 3208792678 },
+ { "test_move", 680299713, 2481691619 },
+ });
+ mappings.insert("PhysicsDirectBodyState2D", {
+ { "apply_impulse", 496058220, 4288681949 },
+ { "apply_force", 496058220, 4288681949 },
+ { "add_constant_force", 496058220, 4288681949 },
+ });
+ mappings.insert("PhysicsDirectBodyState3D", {
+ { "apply_impulse", 1002852006, 2754756483 },
+ { "apply_force", 1002852006, 2754756483 },
+ { "add_constant_force", 1002852006, 2754756483 },
+ });
+ mappings.insert("PhysicsDirectSpaceState2D", {
+ { "intersect_point", 3278207904, 2118456068 },
+ { "intersect_shape", 3803848594, 2488867228 },
+ { "collide_shape", 3803848594, 2488867228 },
+ });
+ mappings.insert("PhysicsDirectSpaceState3D", {
+ { "intersect_point", 45993382, 975173756 },
+ { "intersect_shape", 550215980, 3762137681 },
+ { "collide_shape", 550215980, 3762137681 },
+ });
+ mappings.insert("PhysicsRayQueryParameters2D", {
+ { "create", 1118143851, 3196569324 },
+ });
+ mappings.insert("PhysicsRayQueryParameters3D", {
+ { "create", 680321959, 3110599579 },
+ });
+ mappings.insert("PhysicsServer2D", {
+ { "area_add_shape", 754862190, 339056240 },
+ { "body_add_shape", 754862190, 339056240 },
+ { "body_apply_impulse", 34330743, 205485391 },
+ { "body_apply_force", 34330743, 205485391 },
+ { "body_add_constant_force", 34330743, 205485391 },
+ { "joint_make_pin", 2288600450, 1612646186 },
+ { "joint_make_groove", 3573265764, 481430435 },
+ { "joint_make_damped_spring", 206603952, 1994657646 },
+ });
+ mappings.insert("PhysicsServer3D", {
+ { "area_add_shape", 4040559639, 3711419014 },
+ { "body_add_shape", 4040559639, 3711419014 },
+ { "body_apply_impulse", 110375048, 390416203 },
+ { "body_apply_force", 110375048, 390416203 },
+ { "body_add_constant_force", 110375048, 390416203 },
+ });
+ mappings.insert("PopupMenu", {
+ { "add_item", 3224536192, 3674230041 },
+ { "add_icon_item", 1200674553, 1086190128 },
+ { "add_check_item", 3224536192, 3674230041 },
+ { "add_icon_check_item", 1200674553, 1086190128 },
+ { "add_radio_check_item", 3224536192, 3674230041 },
+ { "add_icon_radio_check_item", 1200674553, 1086190128 },
+ { "add_multistate_item", 1585218420, 150780458 },
+ { "add_shortcut", 2482211467, 3451850107 },
+ { "add_icon_shortcut", 3060251822, 2997871092 },
+ { "add_check_shortcut", 2168272394, 1642193386 },
+ { "add_icon_check_shortcut", 68101841, 3856247530 },
+ { "add_radio_check_shortcut", 2168272394, 1642193386 },
+ { "add_icon_radio_check_shortcut", 68101841, 3856247530 },
+ { "add_submenu_item", 3728518296, 2979222410 },
+ // Pre-existing compatibility hashes.
+ { "add_icon_shortcut", 68101841, 3856247530 },
+ { "add_shortcut", 2168272394, 1642193386 },
+ });
+ mappings.insert("PortableCompressedTexture2D", {
+ { "create_from_image", 97251393, 3679243433 },
+ });
+ mappings.insert("ProjectSettings", {
+ { "load_resource_pack", 3001721055, 708980503 },
+ });
+ mappings.insert("RegEx", {
+ { "search", 4087180739, 3365977994 },
+ { "search_all", 3354100289, 849021363 },
+ { "sub", 758293621, 54019702 },
+ });
+ mappings.insert("RenderingDevice", {
+ { "texture_create", 3011278298, 3709173589 },
+ { "texture_create_shared_from_slice", 864132525, 1808971279 },
+ { "texture_update", 2736912341, 2096463824 },
+ { "texture_copy", 3741367532, 2339493201 },
+ { "texture_clear", 3423681478, 3396867530 },
+ { "texture_resolve_multisample", 2126834943, 594679454 },
+ { "framebuffer_format_create", 2635475316, 697032759 },
+ { "framebuffer_format_create_multipass", 1992489524, 2647479094 },
+ { "framebuffer_format_get_texture_samples", 1036806638, 4223391010 },
+ { "framebuffer_create", 1884747791, 3284231055 },
+ { "framebuffer_create_multipass", 452534725, 1750306695 },
+ { "framebuffer_create_empty", 382373098, 3058360618 },
+ { "vertex_buffer_create", 3491282828, 3410049843 },
+ { "vertex_array_create", 3137892244, 3799816279 },
+ { "index_buffer_create", 975915977, 3935920523 },
+ { "shader_compile_spirv_from_source", 3459523685, 1178973306 },
+ { "shader_compile_binary_from_spirv", 1395027180, 134910450 },
+ { "shader_create_from_spirv", 3297482566, 342949005 },
+ { "shader_create_from_bytecode", 2078349841, 1687031350 },
+ { "uniform_buffer_create", 1453158401, 34556762 },
+ { "storage_buffer_create", 1173156076, 2316365934 },
+ { "texture_buffer_create", 2344087557, 1470338698 },
+ { "buffer_update", 652628289, 3793150683 },
+ { "buffer_clear", 1645170096, 2797041220 },
+ { "buffer_get_data", 125363422, 3101830688 },
+ { "render_pipeline_create", 2911419500, 2385451958 },
+ { "compute_pipeline_create", 403593840, 1448838280 },
+ { "draw_list_begin", 4252992020, 2468082605 },
+ { "draw_list_begin_split", 832527510, 2406300660 },
+ { "draw_list_draw", 3710874499, 4230067973 },
+ { "draw_list_enable_scissor", 338791288, 244650101 },
+ });
+ mappings.insert("RenderingServer", {
+ { "texture_rd_create", 3291180269, 1434128712 },
+ { "shader_set_default_texture_parameter", 3864903085, 4094001817 },
+ { "shader_get_default_texture_parameter", 2523186822, 1464608890 },
+ { "mesh_create_from_surfaces", 4007581507, 4291747531 },
+ { "mesh_add_surface_from_arrays", 1247008646, 2342446560 },
+ { "viewport_attach_to_screen", 1278520651, 1062245816 },
+ { "environment_set_ambient_light", 491659071, 1214961493 },
+ { "instances_cull_aabb", 2031554939, 2570105777 },
+ { "instances_cull_ray", 3388524336, 2208759584 },
+ { "instances_cull_convex", 3690700105, 2488539944 },
+ { "canvas_item_set_custom_rect", 2180266943, 1333997032 },
+ { "canvas_item_add_line", 2843922985, 1819681853 },
+ { "canvas_item_add_polyline", 3438017257, 3098767073 },
+ { "canvas_item_add_multiline", 3176074788, 2088642721 },
+ { "canvas_item_add_texture_rect", 3205360868, 324864032 },
+ { "canvas_item_add_msdf_texture_rect_region", 349157222, 97408773 },
+ { "canvas_item_add_texture_rect_region", 2782979504, 485157892 },
+ { "canvas_item_add_nine_patch", 904428547, 389957886 },
+ { "canvas_item_add_polygon", 2907936855, 3580000528 },
+ { "canvas_item_add_triangle_array", 749685193, 660261329 },
+ { "canvas_item_add_mesh", 3548053052, 316450961 },
+ { "canvas_item_add_multimesh", 1541595251, 2131855138 },
+ { "canvas_item_add_animation_slice", 4107531031, 2646834499 },
+ { "canvas_item_set_canvas_group_mode", 41973386, 3973586316 },
+ { "set_boot_image", 2244367877, 3759744527 },
+ });
+ mappings.insert("ResourceLoader", {
+ { "load_threaded_request", 1939848623, 3614384323 },
+ { "load_threaded_get_status", 3931021148, 4137685479 },
+ { "load", 2622212233, 3358495409 },
+ { "exists", 2220807150, 4185558881 },
+ });
+ mappings.insert("ResourceSaver", {
+ { "save", 2303056517, 2983274697 },
+ });
+ mappings.insert("RichTextLabel", {
+ { "add_image", 3346058748, 3580801207 },
+ { "push_font", 814287596, 2347424842 },
+ { "push_paragraph", 3218895358, 3089306873 },
+ { "push_list", 4036303897, 3017143144 },
+ { "push_table", 1125058220, 2623499273 },
+ { "push_dropcap", 311501835, 4061635501 },
+ { "set_table_column_expand", 4132157579, 2185176273 },
+ });
+ mappings.insert("RigidBody2D", {
+ { "apply_impulse", 496058220, 4288681949 },
+ { "apply_force", 496058220, 4288681949 },
+ { "add_constant_force", 496058220, 4288681949 },
+ });
+ mappings.insert("RigidBody3D", {
+ { "apply_impulse", 1002852006, 2754756483 },
+ { "apply_force", 1002852006, 2754756483 },
+ { "add_constant_force", 1002852006, 2754756483 },
+ });
+ mappings.insert("SceneMultiplayer", {
+ { "send_bytes", 2742700601, 1307428718 },
+ });
+ mappings.insert("SceneReplicationConfig", {
+ { "add_property", 3818401521, 4094619021 },
+ });
+ mappings.insert("SceneTree", {
+ { "create_timer", 1780978058, 2709170273 },
+ });
+ mappings.insert("ScriptCreateDialog", {
+ { "config", 4210001628, 869314288 },
+ });
+ mappings.insert("Shader", {
+ { "set_default_texture_parameter", 1628453603, 2750740428 },
+ { "get_default_texture_parameter", 3823812009, 3090538643 },
+ });
+ mappings.insert("Skeleton3D", {
+ { "set_bone_enabled", 4023243586, 972357352 },
+ });
+ mappings.insert("SpriteFrames", {
+ { "add_frame", 407562921, 1351332740 },
+ { "set_frame", 3155743884, 56804795 },
+ });
+ mappings.insert("StreamPeerTCP", {
+ { "bind", 4025329869, 3167955072 },
+ });
+ mappings.insert("StreamPeerTLS", {
+ { "connect_to_stream", 1325480781, 57169517 },
+ });
+ mappings.insert("SurfaceTool", {
+ { "add_triangle_fan", 297960074, 2235017613 },
+ { "generate_lod", 1894448909, 1938056459 },
+ });
+ mappings.insert("TCPServer", {
+ { "listen", 4025329869, 3167955072 },
+ });
+ mappings.insert("TextEdit", {
+ { "get_line_width", 3294126239, 688195400 },
+ { "insert_text_at_caret", 3043792800, 2697778442 },
+ { "get_line_column_at_pos", 850652858, 239517838 },
+ { "is_mouse_over_selection", 1099474134, 1840282309 },
+ { "set_caret_line", 1413195636, 1302582944 },
+ { "set_caret_column", 1071284433, 3796796178 },
+ { "set_selection_mode", 2920622473, 1443345937 },
+ { "select", 4269665324, 2560984452 },
+ { "get_scroll_pos_for_line", 3274652423, 3929084198 },
+ { "set_line_as_first_visible", 3023605688, 2230941749 },
+ { "set_line_as_center_visible", 3023605688, 2230941749 },
+ { "set_line_as_last_visible", 3023605688, 2230941749 },
+ });
+ mappings.insert("TextLine", {
+ { "add_string", 867188035, 621426851 },
+ { "add_object", 735420116, 1316529304 },
+ { "resize_object", 960819067, 2095776372 },
+ { "draw", 1164457837, 856975658 },
+ { "draw_outline", 1364491366, 1343401456 },
+ });
+ mappings.insert("TextParagraph", {
+ { "set_dropcap", 2613124475, 2498990330 },
+ { "add_string", 867188035, 621426851 },
+ { "add_object", 735420116, 1316529304 },
+ { "resize_object", 960819067, 2095776372 },
+ { "draw", 367324453, 1567802413 },
+ { "draw_outline", 2159523405, 1893131224 },
+ { "draw_line", 3963848920, 1242169894 },
+ { "draw_line_outline", 1814903311, 2664926980 },
+ { "draw_dropcap", 1164457837, 856975658 },
+ { "draw_dropcap_outline", 1364491366, 1343401456 },
+ });
+ mappings.insert("TextServer", {
+ { "font_draw_glyph", 1821196351, 1339057948 },
+ { "font_draw_glyph_outline", 1124898203, 2626165733 },
+ { "shaped_text_set_direction", 2616949700, 1551430183 },
+ { "shaped_text_set_orientation", 104095128, 3019609126 },
+ { "shaped_text_add_string", 2621279422, 623473029 },
+ { "shaped_text_add_object", 2838446185, 3664424789 },
+ { "shaped_text_resize_object", 2353789835, 790361552 },
+ { "shaped_set_span_update_font", 1578983057, 2022725822 },
+ { "shaped_text_fit_to_width", 603718830, 530670926 },
+ { "shaped_text_get_line_breaks_adv", 4206849830, 2376991424 },
+ { "shaped_text_get_line_breaks", 303410369, 2651359741 },
+ { "shaped_text_get_word_breaks", 3299477123, 185957063 },
+ { "shaped_text_overrun_trim_to_width", 1572579718, 2723146520 },
+ { "shaped_text_draw", 70679950, 880389142 },
+ { "shaped_text_draw_outline", 2673671346, 2559184194 },
+ { "format_number", 2305636099, 2664628024 },
+ { "parse_number", 2305636099, 2664628024 },
+ { "string_get_word_breaks", 1398910359, 581857818 },
+ { "string_get_character_breaks", 1586579831, 2333794773 },
+ { "string_to_upper", 2305636099, 2664628024 },
+ { "string_to_lower", 2305636099, 2664628024 },
+ });
+ mappings.insert("Texture2D", {
+ { "draw", 1115460088, 2729649137 },
+ { "draw_rect", 575156982, 3499451691 },
+ { "draw_rect_region", 1066564656, 2963678660 },
+ });
+ mappings.insert("Thread", {
+ { "start", 2779832528, 1327203254 },
+ });
+ mappings.insert("TileMap", {
+ { "set_cell", 1732664643, 966713560 },
+ { "set_cells_terrain_connect", 3072115677, 3578627656 },
+ { "set_cells_terrain_path", 3072115677, 3578627656 },
+ { "get_used_cells_by_id", 4152068407, 2931012785 },
+ });
+ mappings.insert("TileMapPattern", {
+ { "set_cell", 634000503, 2224802556 },
+ });
+ mappings.insert("TileSet", {
+ { "add_source", 276991387, 1059186179 },
+ { "add_terrain", 3023605688, 1230568737 },
+ { "add_pattern", 3009264082, 763712015 },
+ });
+ mappings.insert("TileSetAtlasSource", {
+ { "create_tile", 1583819816, 190528769 },
+ { "move_tile_in_atlas", 1375626516, 3870111920 },
+ { "has_room_for_tile", 4182444377, 3018597268 },
+ { "create_alternative_tile", 3531100812, 2226298068 },
+ { "get_tile_texture_region", 1321423751, 241857547 },
+ });
+ mappings.insert("TileSetScenesCollectionSource", {
+ { "create_scene_tile", 2633389122, 1117465415 },
+ });
+ mappings.insert("Translation", {
+ { "add_message", 971803314, 3898530326 },
+ { "add_plural_message", 360316719, 2356982266 },
+ { "get_message", 58037827, 1829228469 },
+ { "get_plural_message", 1333931916, 229954002 },
+ { "erase_message", 3919944288, 3959009644 },
+ });
+ mappings.insert("TranslationServer", {
+ { "translate", 58037827, 1829228469 },
+ { "translate_plural", 1333931916, 229954002 },
+ });
+ mappings.insert("Tree", {
+ { "get_item_area_rect", 1235226180, 47968679 },
+ });
+ mappings.insert("TreeItem", {
+ { "propagate_check", 4023243586, 972357352 },
+ { "add_button", 1507727907, 1688223362 },
+ });
+ mappings.insert("UDPServer", {
+ { "listen", 4025329869, 3167955072 },
+ });
+ mappings.insert("UPNP", {
+ { "add_port_mapping", 3358934458, 818314583 },
+ { "delete_port_mapping", 760296170, 3444187325 },
+ });
+ mappings.insert("UPNPDevice", {
+ { "add_port_mapping", 3358934458, 818314583 },
+ { "delete_port_mapping", 760296170, 3444187325 },
+ });
+ mappings.insert("UndoRedo", {
+ { "create_action", 3900135403, 3171901514 },
+ });
+ mappings.insert("VideoStreamPlayback", {
+ { "mix_audio", 1369271885, 93876830 },
+ });
+ mappings.insert("WebRTCMultiplayerPeer", {
+ { "create_client", 1777354631, 2641732907 },
+ { "create_mesh", 1777354631, 2641732907 },
+ { "add_peer", 2555866323, 4078953270 },
+ });
+ mappings.insert("WebRTCPeerConnection", {
+ { "create_data_channel", 3997447457, 1288557393 },
+ });
+ mappings.insert("WebSocketMultiplayerPeer", {
+ { "create_client", 3097527179, 1966198364 },
+ { "create_server", 337374795, 2400822951 },
+ });
+ mappings.insert("WebSocketPeer", {
+ { "connect_to_url", 3097527179, 1966198364 },
+ { "send", 3440492527, 2780360567 },
+ });
+ mappings.insert("Window", {
+ { "get_theme_icon", 2336455395, 3163973443 },
+ { "get_theme_stylebox", 2759935355, 604739069 },
+ { "get_theme_font", 387378635, 2826986490 },
+ { "get_theme_font_size", 229578101, 1327056374 },
+ { "get_theme_color", 2377051548, 2798751242 },
+ { "get_theme_constant", 229578101, 1327056374 },
+ { "has_theme_icon", 1187511791, 866386512 },
+ { "has_theme_stylebox", 1187511791, 866386512 },
+ { "has_theme_font", 1187511791, 866386512 },
+ { "has_theme_font_size", 1187511791, 866386512 },
+ { "has_theme_color", 1187511791, 866386512 },
+ { "has_theme_constant", 1187511791, 866386512 },
+ { "popup_exclusive", 1728044812, 2134721627 },
+ { "popup_exclusive_centered", 2561668109, 3357594017 },
+ { "popup_exclusive_centered_ratio", 4257659513, 2284776287 },
+ { "popup_exclusive_centered_clamped", 224798062, 2612708785 },
+ });
+ mappings.insert("WorkerThreadPool", {
+ { "add_task", 3976347598, 3745067146 },
+ { "add_group_task", 2377228549, 1801953219 },
+ });
+ mappings.insert("ZIPPacker", {
+ { "open", 3715508516, 1936816515 },
+ });
+ mappings.insert("ZIPReader", {
+ { "read_file", 156385007, 740857591 },
+ { "file_exists", 1676256, 35364943 },
+ });
+ // clang-format on
+}
+
+#endif // DISABLE_DEPRECATED
diff --git a/core/extension/gdextension_compat_hashes.h b/core/extension/gdextension_compat_hashes.h
new file mode 100644
index 0000000000..3a66ef0b97
--- /dev/null
+++ b/core/extension/gdextension_compat_hashes.h
@@ -0,0 +1,57 @@
+/**************************************************************************/
+/* gdextension_compat_hashes.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#ifndef GDEXTENSION_COMPAT_HASHES_H
+#define GDEXTENSION_COMPAT_HASHES_H
+
+#ifndef DISABLE_DEPRECATED
+
+#include "core/string/string_name.h"
+#include "core/templates/hash_map.h"
+#include "core/templates/local_vector.h"
+
+class GDExtensionCompatHashes {
+ struct Mapping {
+ StringName method;
+ uint32_t legacy_hash;
+ uint32_t current_hash;
+ };
+
+ static HashMap<StringName, LocalVector<Mapping>> mappings;
+
+public:
+ static void initialize();
+ static bool lookup_current_hash(const StringName &p_class, const StringName &p_method, uint32_t p_legacy_hash, uint32_t *r_current_hash);
+ static bool get_legacy_hashes(const StringName &p_class, const StringName &p_method, Array &r_hashes);
+};
+
+#endif // DISABLE_DEPRECATED
+
+#endif // GDEXTENSION_COMPAT_HASHES_H
diff --git a/core/extension/gdextension_interface.cpp b/core/extension/gdextension_interface.cpp
index 7ef956a470..843f9ceecf 100644
--- a/core/extension/gdextension_interface.cpp
+++ b/core/extension/gdextension_interface.cpp
@@ -32,6 +32,7 @@
#include "core/config/engine.h"
#include "core/extension/gdextension.h"
+#include "core/extension/gdextension_compat_hashes.h"
#include "core/io/file_access.h"
#include "core/io/xml_parser.h"
#include "core/object/class_db.h"
@@ -41,6 +42,152 @@
#include "core/variant/variant.h"
#include "core/version.h"
+class CallableCustomExtension : public CallableCustom {
+ void *userdata;
+ void *token;
+
+ ObjectID object;
+
+ GDExtensionCallableCustomCall call_func;
+ GDExtensionCallableCustomIsValid is_valid_func;
+ GDExtensionCallableCustomFree free_func;
+
+ GDExtensionCallableCustomEqual equal_func;
+ GDExtensionCallableCustomLessThan less_than_func;
+
+ GDExtensionCallableCustomToString to_string_func;
+
+ uint32_t _hash;
+
+ static bool default_compare_equal(const CallableCustom *p_a, const CallableCustom *p_b) {
+ const CallableCustomExtension *a = static_cast<const CallableCustomExtension *>(p_a);
+ const CallableCustomExtension *b = static_cast<const CallableCustomExtension *>(p_b);
+
+ if (a->call_func != b->call_func || a->userdata != b->userdata) {
+ return false;
+ }
+ return true;
+ }
+
+ static bool default_compare_less(const CallableCustom *p_a, const CallableCustom *p_b) {
+ const CallableCustomExtension *a = static_cast<const CallableCustomExtension *>(p_a);
+ const CallableCustomExtension *b = static_cast<const CallableCustomExtension *>(p_b);
+
+ if (a->call_func != b->call_func) {
+ return a->call_func < b->call_func;
+ }
+ return a->userdata < b->userdata;
+ }
+
+ static bool custom_compare_equal(const CallableCustom *p_a, const CallableCustom *p_b) {
+ const CallableCustomExtension *a = static_cast<const CallableCustomExtension *>(p_a);
+ const CallableCustomExtension *b = static_cast<const CallableCustomExtension *>(p_b);
+
+ if (a->equal_func != b->equal_func) {
+ return false;
+ }
+ return a->equal_func(a->userdata, b->userdata);
+ }
+
+ static bool custom_compare_less(const CallableCustom *p_a, const CallableCustom *p_b) {
+ const CallableCustomExtension *a = static_cast<const CallableCustomExtension *>(p_a);
+ const CallableCustomExtension *b = static_cast<const CallableCustomExtension *>(p_b);
+
+ if (a->less_than_func != b->less_than_func) {
+ return default_compare_less(p_a, p_b);
+ }
+ return a->less_than_func(a->userdata, b->userdata);
+ }
+
+public:
+ uint32_t hash() const override {
+ return _hash;
+ }
+
+ String get_as_text() const override {
+ if (to_string_func != nullptr) {
+ String out;
+ GDExtensionBool is_valid = false;
+
+ to_string_func(userdata, &is_valid, (GDExtensionStringPtr)&out);
+
+ if (is_valid) {
+ return out;
+ }
+ }
+ return "<CallableCustom>";
+ }
+
+ CompareEqualFunc get_compare_equal_func() const override {
+ return (equal_func != nullptr) ? custom_compare_equal : default_compare_equal;
+ }
+
+ CompareLessFunc get_compare_less_func() const override {
+ return (less_than_func != nullptr) ? custom_compare_less : default_compare_less;
+ }
+
+ bool is_valid() const override {
+ if (is_valid_func != nullptr && !is_valid_func(userdata)) {
+ return false;
+ }
+ return call_func != nullptr;
+ }
+
+ StringName get_method() const override {
+ return StringName();
+ }
+
+ ObjectID get_object() const override {
+ return object;
+ }
+
+ void *get_userdata(void *p_token) const {
+ return (p_token == token) ? userdata : nullptr;
+ }
+
+ void call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const override {
+ GDExtensionCallError error;
+
+ call_func(userdata, (GDExtensionConstVariantPtr *)p_arguments, p_argcount, (GDExtensionVariantPtr)&r_return_value, &error);
+
+ r_call_error.error = (Callable::CallError::Error)error.error;
+ r_call_error.argument = error.argument;
+ r_call_error.expected = error.expected;
+ }
+
+ CallableCustomExtension(GDExtensionCallableCustomInfo *p_info) {
+ userdata = p_info->callable_userdata;
+ token = p_info->token;
+
+ if (p_info->object != nullptr) {
+ object = ((Object *)p_info->object)->get_instance_id();
+ }
+
+ call_func = p_info->call_func;
+ is_valid_func = p_info->is_valid_func;
+ free_func = p_info->free_func;
+
+ equal_func = p_info->equal_func;
+ less_than_func = p_info->less_than_func;
+
+ to_string_func = p_info->to_string_func;
+
+ // Pre-calculate the hash.
+ if (p_info->hash_func != nullptr) {
+ _hash = p_info->hash_func(userdata);
+ } else {
+ _hash = hash_murmur3_one_64((uint64_t)call_func);
+ _hash = hash_murmur3_one_64((uint64_t)userdata, _hash);
+ }
+ }
+
+ ~CallableCustomExtension() {
+ if (free_func != nullptr) {
+ free_func(userdata);
+ }
+ }
+};
+
// Core interface functions.
GDExtensionInterfaceFunctionPtr gdextension_get_proc_address(const char *p_name) {
return GDExtension::get_interface_function(p_name);
@@ -607,25 +754,25 @@ static void gdextension_string_new_with_utf8_chars_and_len(GDExtensionUninitiali
dest->parse_utf8(p_contents, p_size);
}
-static void gdextension_string_new_with_utf16_chars_and_len(GDExtensionUninitializedStringPtr r_dest, const char16_t *p_contents, GDExtensionInt p_size) {
+static void gdextension_string_new_with_utf16_chars_and_len(GDExtensionUninitializedStringPtr r_dest, const char16_t *p_contents, GDExtensionInt p_char_count) {
memnew_placement(r_dest, String);
String *dest = reinterpret_cast<String *>(r_dest);
- dest->parse_utf16(p_contents, p_size);
+ dest->parse_utf16(p_contents, p_char_count);
}
-static void gdextension_string_new_with_utf32_chars_and_len(GDExtensionUninitializedStringPtr r_dest, const char32_t *p_contents, GDExtensionInt p_size) {
- memnew_placement(r_dest, String((const char32_t *)p_contents, p_size));
+static void gdextension_string_new_with_utf32_chars_and_len(GDExtensionUninitializedStringPtr r_dest, const char32_t *p_contents, GDExtensionInt p_char_count) {
+ memnew_placement(r_dest, String((const char32_t *)p_contents, p_char_count));
}
-static void gdextension_string_new_with_wide_chars_and_len(GDExtensionUninitializedStringPtr r_dest, const wchar_t *p_contents, GDExtensionInt p_size) {
+static void gdextension_string_new_with_wide_chars_and_len(GDExtensionUninitializedStringPtr r_dest, const wchar_t *p_contents, GDExtensionInt p_char_count) {
if constexpr (sizeof(wchar_t) == 2) {
// wchar_t is 16 bit, parse.
memnew_placement(r_dest, String);
String *dest = reinterpret_cast<String *>(r_dest);
- dest->parse_utf16((const char16_t *)p_contents, p_size);
+ dest->parse_utf16((const char16_t *)p_contents, p_char_count);
} else {
// wchar_t is 32 bit, copy.
- memnew_placement(r_dest, String((const char32_t *)p_contents, p_size));
+ memnew_placement(r_dest, String((const char32_t *)p_contents, p_char_count));
}
}
@@ -731,6 +878,24 @@ static GDExtensionInt gdextension_string_resize(GDExtensionStringPtr p_self, GDE
return (*self).resize(p_length);
}
+static void gdextension_string_name_new_with_latin1_chars(GDExtensionUninitializedStringNamePtr r_dest, const char *p_contents, GDExtensionBool p_is_static) {
+ memnew_placement(r_dest, StringName(p_contents, static_cast<bool>(p_is_static)));
+}
+
+static void gdextension_string_name_new_with_utf8_chars(GDExtensionUninitializedStringNamePtr r_dest, const char *p_contents) {
+ String tmp;
+ tmp.parse_utf8(p_contents);
+
+ memnew_placement(r_dest, StringName(tmp));
+}
+
+static void gdextension_string_name_new_with_utf8_chars_and_len(GDExtensionUninitializedStringNamePtr r_dest, const char *p_contents, GDExtensionInt p_size) {
+ String tmp;
+ tmp.parse_utf8(p_contents, p_size);
+
+ memnew_placement(r_dest, StringName(tmp));
+}
+
static GDExtensionInt gdextension_xml_parser_open_buffer(GDExtensionObjectPtr p_instance, const uint8_t *p_buffer, size_t p_size) {
XMLParser *xml = (XMLParser *)p_instance;
return (GDExtensionInt)xml->_open_buffer(p_buffer, p_size);
@@ -987,6 +1152,11 @@ static void gdextension_object_set_instance_binding(GDExtensionObjectPtr p_objec
o->set_instance_binding(p_token, p_binding, p_callbacks);
}
+static void gdextension_object_free_instance_binding(GDExtensionObjectPtr p_object, void *p_token) {
+ Object *o = (Object *)p_object;
+ o->free_instance_binding(p_token);
+}
+
static void gdextension_object_set_instance(GDExtensionObjectPtr p_object, GDExtensionConstStringNamePtr p_classname, GDExtensionClassInstancePtr p_instance) {
const StringName classname = *reinterpret_cast<const StringName *>(p_classname);
Object *o = (Object *)p_object;
@@ -1041,27 +1211,142 @@ static void gdextension_ref_set_object(GDExtensionRefPtr p_ref, GDExtensionObjec
ref->reference_ptr(o);
}
+#ifndef DISABLE_DEPRECATED
static GDExtensionScriptInstancePtr gdextension_script_instance_create(const GDExtensionScriptInstanceInfo *p_info, GDExtensionScriptInstanceDataPtr p_instance_data) {
+ GDExtensionScriptInstanceInfo2 *info_2 = memnew(GDExtensionScriptInstanceInfo2);
+ info_2->set_func = p_info->set_func;
+ info_2->get_func = p_info->get_func;
+ info_2->get_property_list_func = p_info->get_property_list_func;
+ info_2->free_property_list_func = p_info->free_property_list_func;
+ info_2->get_class_category_func = nullptr;
+ info_2->property_can_revert_func = p_info->property_can_revert_func;
+ info_2->property_get_revert_func = p_info->property_get_revert_func;
+ info_2->get_owner_func = p_info->get_owner_func;
+ info_2->get_property_state_func = p_info->get_property_state_func;
+ info_2->get_method_list_func = p_info->get_method_list_func;
+ info_2->free_method_list_func = p_info->free_method_list_func;
+ info_2->get_property_type_func = p_info->get_property_type_func;
+ info_2->validate_property_func = nullptr;
+ info_2->has_method_func = p_info->has_method_func;
+ info_2->call_func = p_info->call_func;
+ info_2->notification_func = nullptr;
+ info_2->to_string_func = p_info->to_string_func;
+ info_2->refcount_incremented_func = p_info->refcount_incremented_func;
+ info_2->refcount_decremented_func = p_info->refcount_decremented_func;
+ info_2->get_script_func = p_info->get_script_func;
+ info_2->is_placeholder_func = p_info->is_placeholder_func;
+ info_2->set_fallback_func = p_info->set_fallback_func;
+ info_2->get_fallback_func = p_info->get_fallback_func;
+ info_2->get_language_func = p_info->get_language_func;
+ info_2->free_func = p_info->free_func;
+
+ ScriptInstanceExtension *script_instance_extension = memnew(ScriptInstanceExtension);
+ script_instance_extension->instance = p_instance_data;
+ script_instance_extension->native_info = info_2;
+ script_instance_extension->free_native_info = true;
+ script_instance_extension->deprecated_native_info.notification_func = p_info->notification_func;
+ return reinterpret_cast<GDExtensionScriptInstancePtr>(script_instance_extension);
+}
+#endif // DISABLE_DEPRECATED
+
+static GDExtensionScriptInstancePtr gdextension_script_instance_create2(const GDExtensionScriptInstanceInfo2 *p_info, GDExtensionScriptInstanceDataPtr p_instance_data) {
ScriptInstanceExtension *script_instance_extension = memnew(ScriptInstanceExtension);
script_instance_extension->instance = p_instance_data;
script_instance_extension->native_info = p_info;
return reinterpret_cast<GDExtensionScriptInstancePtr>(script_instance_extension);
}
+static GDExtensionScriptInstancePtr gdextension_placeholder_script_instance_create(GDExtensionObjectPtr p_language, GDExtensionObjectPtr p_script, GDExtensionObjectPtr p_owner) {
+ ScriptLanguage *language = (ScriptLanguage *)p_language;
+ Ref<Script> script;
+ script.reference_ptr((Script *)p_script);
+ Object *owner = (Object *)p_owner;
+
+ PlaceHolderScriptInstance *placeholder = memnew(PlaceHolderScriptInstance(language, script, owner));
+ return reinterpret_cast<GDExtensionScriptInstancePtr>(placeholder);
+}
+
+static void gdextension_placeholder_script_instance_update(GDExtensionScriptInstancePtr p_placeholder, GDExtensionConstTypePtr p_properties, GDExtensionConstTypePtr p_values) {
+ PlaceHolderScriptInstance *placeholder = dynamic_cast<PlaceHolderScriptInstance *>(reinterpret_cast<ScriptInstance *>(p_placeholder));
+ ERR_FAIL_NULL_MSG(placeholder, "Unable to update placeholder, expected a PlaceHolderScriptInstance but received an invalid type.");
+
+ const Array &properties = *reinterpret_cast<const Array *>(p_properties);
+ const Dictionary &values = *reinterpret_cast<const Dictionary *>(p_values);
+
+ List<PropertyInfo> properties_list;
+ HashMap<StringName, Variant> values_map;
+
+ for (int i = 0; i < properties.size(); i++) {
+ Dictionary d = properties[i];
+ properties_list.push_back(PropertyInfo::from_dict(d));
+ }
+
+ List<Variant> keys;
+ values.get_key_list(&keys);
+
+ for (const Variant &E : keys) {
+ values_map.insert(E, values[E]);
+ }
+
+ placeholder->update(properties_list, values_map);
+}
+
+static GDExtensionScriptInstancePtr gdextension_object_get_script_instance(GDExtensionConstObjectPtr p_object, GDExtensionConstObjectPtr p_language) {
+ if (!p_object || !p_language) {
+ return nullptr;
+ }
+
+ const Object *o = (const Object *)p_object;
+ ScriptInstanceExtension *script_instance_extension = reinterpret_cast<ScriptInstanceExtension *>(o->get_script_instance());
+ if (!script_instance_extension) {
+ return nullptr;
+ }
+
+ const ScriptLanguage *language = script_instance_extension->get_language();
+ if (language != p_language) {
+ return nullptr;
+ }
+
+ return script_instance_extension->instance;
+}
+
+static void gdextension_callable_custom_create(GDExtensionUninitializedTypePtr r_callable, GDExtensionCallableCustomInfo *p_custom_callable_info) {
+ memnew_placement(r_callable, Callable(memnew(CallableCustomExtension(p_custom_callable_info))));
+}
+
+static void *gdextension_callable_custom_get_userdata(GDExtensionTypePtr p_callable, void *p_token) {
+ const Callable &callable = *reinterpret_cast<const Callable *>(p_callable);
+ if (!callable.is_custom()) {
+ return nullptr;
+ }
+ const CallableCustomExtension *custom_callable = dynamic_cast<const CallableCustomExtension *>(callable.get_custom());
+ if (!custom_callable) {
+ return nullptr;
+ }
+ return custom_callable->get_userdata(p_token);
+}
+
static GDExtensionMethodBindPtr gdextension_classdb_get_method_bind(GDExtensionConstStringNamePtr p_classname, GDExtensionConstStringNamePtr p_methodname, GDExtensionInt p_hash) {
const StringName classname = *reinterpret_cast<const StringName *>(p_classname);
const StringName methodname = *reinterpret_cast<const StringName *>(p_methodname);
bool exists = false;
MethodBind *mb = ClassDB::get_method_with_compatibility(classname, methodname, p_hash, &exists);
+
+#ifndef DISABLE_DEPRECATED
+ // If lookup failed, see if this is one of the broken hashes from issue #81386.
if (!mb && exists) {
- ERR_PRINT("Method '" + classname + "." + methodname + "' has changed and no compatibility fallback has been provided. Please open an issue.");
- return nullptr;
+ uint32_t mapped_hash;
+ if (GDExtensionCompatHashes::lookup_current_hash(classname, methodname, p_hash, &mapped_hash)) {
+ mb = ClassDB::get_method_with_compatibility(classname, methodname, mapped_hash, &exists);
+ }
}
- ERR_FAIL_COND_V(!mb, nullptr);
- if (mb->get_hash() != p_hash) {
- ERR_PRINT("Hash mismatch for method '" + classname + "." + methodname + "'.");
+#endif
+
+ if (!mb && exists) {
+ ERR_PRINT("Method '" + classname + "." + methodname + "' has changed and no compatibility fallback has been provided. Please open an issue.");
return nullptr;
}
+ ERR_FAIL_NULL_V(mb, nullptr);
return (GDExtensionMethodBindPtr)mb;
}
@@ -1173,6 +1458,9 @@ void gdextension_setup_interface() {
REGISTER_INTERFACE_FUNC(string_operator_plus_eq_wcstr);
REGISTER_INTERFACE_FUNC(string_operator_plus_eq_c32str);
REGISTER_INTERFACE_FUNC(string_resize);
+ REGISTER_INTERFACE_FUNC(string_name_new_with_latin1_chars);
+ REGISTER_INTERFACE_FUNC(string_name_new_with_utf8_chars);
+ REGISTER_INTERFACE_FUNC(string_name_new_with_utf8_chars_and_len);
REGISTER_INTERFACE_FUNC(xml_parser_open_buffer);
REGISTER_INTERFACE_FUNC(file_access_store_buffer);
REGISTER_INTERFACE_FUNC(file_access_get_buffer);
@@ -1208,6 +1496,7 @@ void gdextension_setup_interface() {
REGISTER_INTERFACE_FUNC(global_get_singleton);
REGISTER_INTERFACE_FUNC(object_get_instance_binding);
REGISTER_INTERFACE_FUNC(object_set_instance_binding);
+ REGISTER_INTERFACE_FUNC(object_free_instance_binding);
REGISTER_INTERFACE_FUNC(object_set_instance);
REGISTER_INTERFACE_FUNC(object_get_class_name);
REGISTER_INTERFACE_FUNC(object_cast_to);
@@ -1215,7 +1504,15 @@ void gdextension_setup_interface() {
REGISTER_INTERFACE_FUNC(object_get_instance_id);
REGISTER_INTERFACE_FUNC(ref_get_object);
REGISTER_INTERFACE_FUNC(ref_set_object);
+#ifndef DISABLE_DEPRECATED
REGISTER_INTERFACE_FUNC(script_instance_create);
+#endif // DISABLE_DEPRECATED
+ REGISTER_INTERFACE_FUNC(script_instance_create2);
+ REGISTER_INTERFACE_FUNC(placeholder_script_instance_create);
+ REGISTER_INTERFACE_FUNC(placeholder_script_instance_update);
+ REGISTER_INTERFACE_FUNC(object_get_script_instance);
+ REGISTER_INTERFACE_FUNC(callable_custom_create);
+ REGISTER_INTERFACE_FUNC(callable_custom_get_userdata);
REGISTER_INTERFACE_FUNC(classdb_construct_object);
REGISTER_INTERFACE_FUNC(classdb_get_method_bind);
REGISTER_INTERFACE_FUNC(classdb_get_class_tag);
diff --git a/core/extension/gdextension_interface.h b/core/extension/gdextension_interface.h
index 6c05f3988b..240da6e073 100644
--- a/core/extension/gdextension_interface.h
+++ b/core/extension/gdextension_interface.h
@@ -258,14 +258,19 @@ typedef const GDExtensionPropertyInfo *(*GDExtensionClassGetPropertyList)(GDExte
typedef void (*GDExtensionClassFreePropertyList)(GDExtensionClassInstancePtr p_instance, const GDExtensionPropertyInfo *p_list);
typedef GDExtensionBool (*GDExtensionClassPropertyCanRevert)(GDExtensionClassInstancePtr p_instance, GDExtensionConstStringNamePtr p_name);
typedef GDExtensionBool (*GDExtensionClassPropertyGetRevert)(GDExtensionClassInstancePtr p_instance, GDExtensionConstStringNamePtr p_name, GDExtensionVariantPtr r_ret);
-typedef void (*GDExtensionClassNotification)(GDExtensionClassInstancePtr p_instance, int32_t p_what);
+typedef GDExtensionBool (*GDExtensionClassValidateProperty)(GDExtensionClassInstancePtr p_instance, GDExtensionPropertyInfo *p_property);
+typedef void (*GDExtensionClassNotification)(GDExtensionClassInstancePtr p_instance, int32_t p_what); // Deprecated. Use GDExtensionClassNotification2 instead.
+typedef void (*GDExtensionClassNotification2)(GDExtensionClassInstancePtr p_instance, int32_t p_what, GDExtensionBool p_reversed);
typedef void (*GDExtensionClassToString)(GDExtensionClassInstancePtr p_instance, GDExtensionBool *r_is_valid, GDExtensionStringPtr p_out);
typedef void (*GDExtensionClassReference)(GDExtensionClassInstancePtr p_instance);
typedef void (*GDExtensionClassUnreference)(GDExtensionClassInstancePtr p_instance);
typedef void (*GDExtensionClassCallVirtual)(GDExtensionClassInstancePtr p_instance, const GDExtensionConstTypePtr *p_args, GDExtensionTypePtr r_ret);
-typedef GDExtensionObjectPtr (*GDExtensionClassCreateInstance)(void *p_userdata);
-typedef void (*GDExtensionClassFreeInstance)(void *p_userdata, GDExtensionClassInstancePtr p_instance);
-typedef GDExtensionClassCallVirtual (*GDExtensionClassGetVirtual)(void *p_userdata, GDExtensionConstStringNamePtr p_name);
+typedef GDExtensionObjectPtr (*GDExtensionClassCreateInstance)(void *p_class_userdata);
+typedef void (*GDExtensionClassFreeInstance)(void *p_class_userdata, GDExtensionClassInstancePtr p_instance);
+typedef GDExtensionClassInstancePtr (*GDExtensionClassRecreateInstance)(void *p_class_userdata, GDExtensionObjectPtr p_object);
+typedef GDExtensionClassCallVirtual (*GDExtensionClassGetVirtual)(void *p_class_userdata, GDExtensionConstStringNamePtr p_name);
+typedef void *(*GDExtensionClassGetVirtualCallData)(void *p_class_userdata, GDExtensionConstStringNamePtr p_name);
+typedef void (*GDExtensionClassCallVirtualWithData)(GDExtensionClassInstancePtr p_instance, GDExtensionConstStringNamePtr p_name, void *p_virtual_call_userdata, const GDExtensionConstTypePtr *p_args, GDExtensionTypePtr r_ret);
typedef struct {
GDExtensionBool is_virtual;
@@ -285,7 +290,40 @@ typedef struct {
GDExtensionClassGetVirtual get_virtual_func; // Queries a virtual function by name and returns a callback to invoke the requested virtual function.
GDExtensionClassGetRID get_rid_func;
void *class_userdata; // Per-class user data, later accessible in instance bindings.
-} GDExtensionClassCreationInfo;
+} GDExtensionClassCreationInfo; // Deprecated. Use GDExtensionClassCreationInfo2 instead.
+
+typedef struct {
+ GDExtensionBool is_virtual;
+ GDExtensionBool is_abstract;
+ GDExtensionBool is_exposed;
+ GDExtensionClassSet set_func;
+ GDExtensionClassGet get_func;
+ GDExtensionClassGetPropertyList get_property_list_func;
+ GDExtensionClassFreePropertyList free_property_list_func;
+ GDExtensionClassPropertyCanRevert property_can_revert_func;
+ GDExtensionClassPropertyGetRevert property_get_revert_func;
+ GDExtensionClassValidateProperty validate_property_func;
+ GDExtensionClassNotification2 notification_func;
+ GDExtensionClassToString to_string_func;
+ GDExtensionClassReference reference_func;
+ GDExtensionClassUnreference unreference_func;
+ GDExtensionClassCreateInstance create_instance_func; // (Default) constructor; mandatory. If the class is not instantiable, consider making it virtual or abstract.
+ GDExtensionClassFreeInstance free_instance_func; // Destructor; mandatory.
+ GDExtensionClassRecreateInstance recreate_instance_func;
+ // Queries a virtual function by name and returns a callback to invoke the requested virtual function.
+ GDExtensionClassGetVirtual get_virtual_func;
+ // Paired with `call_virtual_with_data_func`, this is an alternative to `get_virtual_func` for extensions that
+ // need or benefit from extra data when calling virtual functions.
+ // Returns user data that will be passed to `call_virtual_with_data_func`.
+ // Returning `NULL` from this function signals to Godot that the virtual function is not overridden.
+ // Data returned from this function should be managed by the extension and must be valid until the extension is deinitialized.
+ // You should supply either `get_virtual_func`, or `get_virtual_call_data_func` with `call_virtual_with_data_func`.
+ GDExtensionClassGetVirtualCallData get_virtual_call_data_func;
+ // Used to call virtual functions when `get_virtual_call_data_func` is not null.
+ GDExtensionClassCallVirtualWithData call_virtual_with_data_func;
+ GDExtensionClassGetRID get_rid_func;
+ void *class_userdata; // Per-class user data, later accessible in instance bindings.
+} GDExtensionClassCreationInfo2;
typedef void *GDExtensionClassLibraryPtr;
@@ -343,6 +381,47 @@ typedef struct {
GDExtensionVariantPtr *default_arguments;
} GDExtensionClassMethodInfo;
+typedef void (*GDExtensionCallableCustomCall)(void *callable_userdata, const GDExtensionConstVariantPtr *p_args, GDExtensionInt p_argument_count, GDExtensionVariantPtr r_return, GDExtensionCallError *r_error);
+typedef GDExtensionBool (*GDExtensionCallableCustomIsValid)(void *callable_userdata);
+typedef void (*GDExtensionCallableCustomFree)(void *callable_userdata);
+
+typedef uint32_t (*GDExtensionCallableCustomHash)(void *callable_userdata);
+typedef GDExtensionBool (*GDExtensionCallableCustomEqual)(void *callable_userdata_a, void *callable_userdata_b);
+typedef GDExtensionBool (*GDExtensionCallableCustomLessThan)(void *callable_userdata_a, void *callable_userdata_b);
+
+typedef void (*GDExtensionCallableCustomToString)(void *callable_userdata, GDExtensionBool *r_is_valid, GDExtensionStringPtr r_out);
+
+typedef struct {
+ /* Only `call_func` and `token` are strictly required, however, `object` should be passed if its not a static method.
+ *
+ * `token` should point to an address that uniquely identifies the GDExtension (for example, the
+ * `GDExtensionClassLibraryPtr` passed to the entry symbol function.
+ *
+ * `hash_func`, `equal_func`, and `less_than_func` are optional. If not provided both `call_func` and
+ * `callable_userdata` together are used as the identity of the callable for hashing and comparison purposes.
+ *
+ * The hash returned by `hash_func` is cached, `hash_func` will not be called more than once per callable.
+ *
+ * `is_valid_func` is necessary if the validity of the callable can change before destruction.
+ *
+ * `free_func` is necessary if `callable_userdata` needs to be cleaned up when the callable is freed.
+ */
+ void *callable_userdata;
+ void *token;
+
+ GDExtensionObjectPtr object;
+
+ GDExtensionCallableCustomCall call_func;
+ GDExtensionCallableCustomIsValid is_valid_func;
+ GDExtensionCallableCustomFree free_func;
+
+ GDExtensionCallableCustomHash hash_func;
+ GDExtensionCallableCustomEqual equal_func;
+ GDExtensionCallableCustomLessThan less_than_func;
+
+ GDExtensionCallableCustomToString to_string_func;
+} GDExtensionCallableCustomInfo;
+
/* SCRIPT INSTANCE EXTENSION */
typedef void *GDExtensionScriptInstanceDataPtr; // Pointer to custom ScriptInstance native implementation.
@@ -351,7 +430,10 @@ typedef GDExtensionBool (*GDExtensionScriptInstanceSet)(GDExtensionScriptInstanc
typedef GDExtensionBool (*GDExtensionScriptInstanceGet)(GDExtensionScriptInstanceDataPtr p_instance, GDExtensionConstStringNamePtr p_name, GDExtensionVariantPtr r_ret);
typedef const GDExtensionPropertyInfo *(*GDExtensionScriptInstanceGetPropertyList)(GDExtensionScriptInstanceDataPtr p_instance, uint32_t *r_count);
typedef void (*GDExtensionScriptInstanceFreePropertyList)(GDExtensionScriptInstanceDataPtr p_instance, const GDExtensionPropertyInfo *p_list);
+typedef GDExtensionBool (*GDExtensionScriptInstanceGetClassCategory)(GDExtensionScriptInstanceDataPtr p_instance, GDExtensionPropertyInfo *p_class_category);
+
typedef GDExtensionVariantType (*GDExtensionScriptInstanceGetPropertyType)(GDExtensionScriptInstanceDataPtr p_instance, GDExtensionConstStringNamePtr p_name, GDExtensionBool *r_is_valid);
+typedef GDExtensionBool (*GDExtensionScriptInstanceValidateProperty)(GDExtensionScriptInstanceDataPtr p_instance, GDExtensionPropertyInfo *p_property);
typedef GDExtensionBool (*GDExtensionScriptInstancePropertyCanRevert)(GDExtensionScriptInstanceDataPtr p_instance, GDExtensionConstStringNamePtr p_name);
typedef GDExtensionBool (*GDExtensionScriptInstancePropertyGetRevert)(GDExtensionScriptInstanceDataPtr p_instance, GDExtensionConstStringNamePtr p_name, GDExtensionVariantPtr r_ret);
@@ -366,7 +448,8 @@ typedef void (*GDExtensionScriptInstanceFreeMethodList)(GDExtensionScriptInstanc
typedef GDExtensionBool (*GDExtensionScriptInstanceHasMethod)(GDExtensionScriptInstanceDataPtr p_instance, GDExtensionConstStringNamePtr p_name);
typedef void (*GDExtensionScriptInstanceCall)(GDExtensionScriptInstanceDataPtr p_self, GDExtensionConstStringNamePtr p_method, const GDExtensionConstVariantPtr *p_args, GDExtensionInt p_argument_count, GDExtensionVariantPtr r_return, GDExtensionCallError *r_error);
-typedef void (*GDExtensionScriptInstanceNotification)(GDExtensionScriptInstanceDataPtr p_instance, int32_t p_what);
+typedef void (*GDExtensionScriptInstanceNotification)(GDExtensionScriptInstanceDataPtr p_instance, int32_t p_what); // Deprecated. Use GDExtensionScriptInstanceNotification2 instead.
+typedef void (*GDExtensionScriptInstanceNotification2)(GDExtensionScriptInstanceDataPtr p_instance, int32_t p_what, GDExtensionBool p_reversed);
typedef void (*GDExtensionScriptInstanceToString)(GDExtensionScriptInstanceDataPtr p_instance, GDExtensionBool *r_is_valid, GDExtensionStringPtr r_out);
typedef void (*GDExtensionScriptInstanceRefCountIncremented)(GDExtensionScriptInstanceDataPtr p_instance);
@@ -420,7 +503,48 @@ typedef struct {
GDExtensionScriptInstanceFree free_func;
-} GDExtensionScriptInstanceInfo;
+} GDExtensionScriptInstanceInfo; // Deprecated. Use GDExtensionScriptInstanceInfo2 instead.
+
+typedef struct {
+ GDExtensionScriptInstanceSet set_func;
+ GDExtensionScriptInstanceGet get_func;
+ GDExtensionScriptInstanceGetPropertyList get_property_list_func;
+ GDExtensionScriptInstanceFreePropertyList free_property_list_func;
+ GDExtensionScriptInstanceGetClassCategory get_class_category_func; // Optional. Set to NULL for the default behavior.
+
+ GDExtensionScriptInstancePropertyCanRevert property_can_revert_func;
+ GDExtensionScriptInstancePropertyGetRevert property_get_revert_func;
+
+ GDExtensionScriptInstanceGetOwner get_owner_func;
+ GDExtensionScriptInstanceGetPropertyState get_property_state_func;
+
+ GDExtensionScriptInstanceGetMethodList get_method_list_func;
+ GDExtensionScriptInstanceFreeMethodList free_method_list_func;
+ GDExtensionScriptInstanceGetPropertyType get_property_type_func;
+ GDExtensionScriptInstanceValidateProperty validate_property_func;
+
+ GDExtensionScriptInstanceHasMethod has_method_func;
+
+ GDExtensionScriptInstanceCall call_func;
+ GDExtensionScriptInstanceNotification2 notification_func;
+
+ GDExtensionScriptInstanceToString to_string_func;
+
+ GDExtensionScriptInstanceRefCountIncremented refcount_incremented_func;
+ GDExtensionScriptInstanceRefCountDecremented refcount_decremented_func;
+
+ GDExtensionScriptInstanceGetScript get_script_func;
+
+ GDExtensionScriptInstanceIsPlaceholder is_placeholder_func;
+
+ GDExtensionScriptInstanceSet set_fallback_func;
+ GDExtensionScriptInstanceGet get_fallback_func;
+
+ GDExtensionScriptInstanceGetLanguage get_language_func;
+
+ GDExtensionScriptInstanceFree free_func;
+
+} GDExtensionScriptInstanceInfo2;
/* INITIALIZATION */
@@ -466,7 +590,10 @@ typedef GDExtensionInterfaceFunctionPtr (*GDExtensionInterfaceGetProcAddress)(co
*
* For example:
*
- * GDExtensionInterfaceGetGodotVersion *get_godot_version = (GDExtensionInterfaceGetGodotVersion)p_get_proc_address("get_godot_version");
+ * GDExtensionInterfaceGetGodotVersion get_godot_version = (GDExtensionInterfaceGetGodotVersion)p_get_proc_address("get_godot_version");
+ *
+ * (Note that snippet may cause "cast between incompatible function types" on some compilers, you can
+ * silence this by adding an intermediary `void*` cast.)
*
* You can then call it like a normal function:
*
@@ -1313,7 +1440,7 @@ typedef void (*GDExtensionInterfaceStringNewWithWideChars)(GDExtensionUninitiali
*
* @param r_dest A pointer to a Variant to hold the newly created String.
* @param p_contents A pointer to a Latin-1 encoded C string.
- * @param p_size The number of characters.
+ * @param p_size The number of characters (= number of bytes).
*/
typedef void (*GDExtensionInterfaceStringNewWithLatin1CharsAndLen)(GDExtensionUninitializedStringPtr r_dest, const char *p_contents, GDExtensionInt p_size);
@@ -1325,7 +1452,7 @@ typedef void (*GDExtensionInterfaceStringNewWithLatin1CharsAndLen)(GDExtensionUn
*
* @param r_dest A pointer to a Variant to hold the newly created String.
* @param p_contents A pointer to a UTF-8 encoded C string.
- * @param p_size The number of characters.
+ * @param p_size The number of bytes (not code units).
*/
typedef void (*GDExtensionInterfaceStringNewWithUtf8CharsAndLen)(GDExtensionUninitializedStringPtr r_dest, const char *p_contents, GDExtensionInt p_size);
@@ -1337,9 +1464,9 @@ typedef void (*GDExtensionInterfaceStringNewWithUtf8CharsAndLen)(GDExtensionUnin
*
* @param r_dest A pointer to a Variant to hold the newly created String.
* @param p_contents A pointer to a UTF-16 encoded C string.
- * @param p_size The number of characters.
+ * @param p_size The number of characters (not bytes).
*/
-typedef void (*GDExtensionInterfaceStringNewWithUtf16CharsAndLen)(GDExtensionUninitializedStringPtr r_dest, const char16_t *p_contents, GDExtensionInt p_size);
+typedef void (*GDExtensionInterfaceStringNewWithUtf16CharsAndLen)(GDExtensionUninitializedStringPtr r_dest, const char16_t *p_contents, GDExtensionInt p_char_count);
/**
* @name string_new_with_utf32_chars_and_len
@@ -1349,9 +1476,9 @@ typedef void (*GDExtensionInterfaceStringNewWithUtf16CharsAndLen)(GDExtensionUni
*
* @param r_dest A pointer to a Variant to hold the newly created String.
* @param p_contents A pointer to a UTF-32 encoded C string.
- * @param p_size The number of characters.
+ * @param p_size The number of characters (not bytes).
*/
-typedef void (*GDExtensionInterfaceStringNewWithUtf32CharsAndLen)(GDExtensionUninitializedStringPtr r_dest, const char32_t *p_contents, GDExtensionInt p_size);
+typedef void (*GDExtensionInterfaceStringNewWithUtf32CharsAndLen)(GDExtensionUninitializedStringPtr r_dest, const char32_t *p_contents, GDExtensionInt p_char_count);
/**
* @name string_new_with_wide_chars_and_len
@@ -1361,9 +1488,9 @@ typedef void (*GDExtensionInterfaceStringNewWithUtf32CharsAndLen)(GDExtensionUni
*
* @param r_dest A pointer to a Variant to hold the newly created String.
* @param p_contents A pointer to a wide C string.
- * @param p_size The number of characters.
+ * @param p_size The number of characters (not bytes).
*/
-typedef void (*GDExtensionInterfaceStringNewWithWideCharsAndLen)(GDExtensionUninitializedStringPtr r_dest, const wchar_t *p_contents, GDExtensionInt p_size);
+typedef void (*GDExtensionInterfaceStringNewWithWideCharsAndLen)(GDExtensionUninitializedStringPtr r_dest, const wchar_t *p_contents, GDExtensionInt p_char_count);
/**
* @name string_to_latin1_chars
@@ -1545,6 +1672,50 @@ typedef void (*GDExtensionInterfaceStringOperatorPlusEqC32str)(GDExtensionString
*/
typedef GDExtensionInt (*GDExtensionInterfaceStringResize)(GDExtensionStringPtr p_self, GDExtensionInt p_resize);
+/* INTERFACE: StringName Utilities */
+
+/**
+ * @name string_name_new_with_latin1_chars
+ * @since 4.2
+ *
+ * Creates a StringName from a Latin-1 encoded C string.
+ *
+ * If `p_is_static` is true, then:
+ * - The StringName will reuse the `p_contents` buffer instead of copying it.
+ * You must guarantee that the buffer remains valid for the duration of the application (e.g. string literal).
+ * - You must not call a destructor for this StringName. Incrementing the initial reference once should achieve this.
+ *
+ * `p_is_static` is purely an optimization and can easily introduce undefined behavior if used wrong. In case of doubt, set it to false.
+ *
+ * @param r_dest A pointer to uninitialized storage, into which the newly created StringName is constructed.
+ * @param p_contents A pointer to a C string (null terminated and Latin-1 or ASCII encoded).
+ * @param p_is_static Whether the StringName reuses the buffer directly (see above).
+ */
+typedef void (*GDExtensionInterfaceStringNameNewWithLatin1Chars)(GDExtensionUninitializedStringNamePtr r_dest, const char *p_contents, GDExtensionBool p_is_static);
+
+/**
+ * @name string_name_new_with_utf8_chars
+ * @since 4.2
+ *
+ * Creates a StringName from a UTF-8 encoded C string.
+ *
+ * @param r_dest A pointer to uninitialized storage, into which the newly created StringName is constructed.
+ * @param p_contents A pointer to a C string (null terminated and UTF-8 encoded).
+ */
+typedef void (*GDExtensionInterfaceStringNameNewWithUtf8Chars)(GDExtensionUninitializedStringNamePtr r_dest, const char *p_contents);
+
+/**
+ * @name string_name_new_with_utf8_chars_and_len
+ * @since 4.2
+ *
+ * Creates a StringName from a UTF-8 encoded string with a given number of characters.
+ *
+ * @param r_dest A pointer to uninitialized storage, into which the newly created StringName is constructed.
+ * @param p_contents A pointer to a C string (null terminated and UTF-8 encoded).
+ * @param p_size The number of bytes (not UTF-8 code points).
+ */
+typedef void (*GDExtensionInterfaceStringNameNewWithUtf8CharsAndLen)(GDExtensionUninitializedStringNamePtr r_dest, const char *p_contents, GDExtensionInt p_size);
+
/* INTERFACE: XMLParser Utilities */
/**
@@ -2024,6 +2195,17 @@ typedef void *(*GDExtensionInterfaceObjectGetInstanceBinding)(GDExtensionObjectP
typedef void (*GDExtensionInterfaceObjectSetInstanceBinding)(GDExtensionObjectPtr p_o, void *p_token, void *p_binding, const GDExtensionInstanceBindingCallbacks *p_callbacks);
/**
+ * @name object_free_instance_binding
+ * @since 4.2
+ *
+ * Free an Object's instance binding.
+ *
+ * @param p_o A pointer to the Object.
+ * @param p_library A token the library received by the GDExtension's entry point function.
+ */
+typedef void (*GDExtensionInterfaceObjectFreeInstanceBinding)(GDExtensionObjectPtr p_o, void *p_token);
+
+/**
* @name object_set_instance
* @since 4.1
*
@@ -2116,6 +2298,7 @@ typedef void (*GDExtensionInterfaceRefSetObject)(GDExtensionRefPtr p_ref, GDExte
/**
* @name script_instance_create
* @since 4.1
+ * @deprecated in Godot 4.2. Use `script_instance_create2` instead.
*
* Creates a script instance that contains the given info and instance data.
*
@@ -2126,6 +2309,91 @@ typedef void (*GDExtensionInterfaceRefSetObject)(GDExtensionRefPtr p_ref, GDExte
*/
typedef GDExtensionScriptInstancePtr (*GDExtensionInterfaceScriptInstanceCreate)(const GDExtensionScriptInstanceInfo *p_info, GDExtensionScriptInstanceDataPtr p_instance_data);
+/**
+ * @name script_instance_create2
+ * @since 4.2
+ *
+ * Creates a script instance that contains the given info and instance data.
+ *
+ * @param p_info A pointer to a GDExtensionScriptInstanceInfo2 struct.
+ * @param p_instance_data A pointer to a data representing the script instance in the GDExtension. This will be passed to all the function pointers on p_info.
+ *
+ * @return A pointer to a ScriptInstanceExtension object.
+ */
+typedef GDExtensionScriptInstancePtr (*GDExtensionInterfaceScriptInstanceCreate2)(const GDExtensionScriptInstanceInfo2 *p_info, GDExtensionScriptInstanceDataPtr p_instance_data);
+
+/**
+ * @name placeholder_script_instance_create
+ * @since 4.2
+ *
+ * Creates a placeholder script instance for a given script and instance.
+ *
+ * This interface is optional as a custom placeholder could also be created with script_instance_create().
+ *
+ * @param p_language A pointer to a ScriptLanguage.
+ * @param p_script A pointer to a Script.
+ * @param p_owner A pointer to an Object.
+ *
+ * @return A pointer to a PlaceHolderScriptInstance object.
+ */
+typedef GDExtensionScriptInstancePtr (*GDExtensionInterfacePlaceHolderScriptInstanceCreate)(GDExtensionObjectPtr p_language, GDExtensionObjectPtr p_script, GDExtensionObjectPtr p_owner);
+
+/**
+ * @name placeholder_script_instance_update
+ * @since 4.2
+ *
+ * Updates a placeholder script instance with the given properties and values.
+ *
+ * The passed in placeholder must be an instance of PlaceHolderScriptInstance
+ * such as the one returned by placeholder_script_instance_create().
+ *
+ * @param p_placeholder A pointer to a PlaceHolderScriptInstance.
+ * @param p_properties A pointer to an Array of Dictionary representing PropertyInfo.
+ * @param p_values A pointer to a Dictionary mapping StringName to Variant values.
+ */
+typedef void (*GDExtensionInterfacePlaceHolderScriptInstanceUpdate)(GDExtensionScriptInstancePtr p_placeholder, GDExtensionConstTypePtr p_properties, GDExtensionConstTypePtr p_values);
+
+/**
+ * @name object_get_script_instance
+ * @since 4.2
+ *
+ * Get the script instance data attached to this object.
+ *
+ * @param p_object A pointer to the Object.
+ * @param p_language A pointer to the language expected for this script instance.
+ *
+ * @return A GDExtensionScriptInstanceDataPtr that was attached to this object as part of script_instance_create.
+ */
+typedef GDExtensionScriptInstanceDataPtr (*GDExtensionInterfaceObjectGetScriptInstance)(GDExtensionConstObjectPtr p_object, GDExtensionObjectPtr p_language);
+
+/* INTERFACE: Callable */
+
+/**
+ * @name callable_custom_create
+ * @since 4.2
+ *
+ * Creates a custom Callable object from a function pointer.
+ *
+ * Provided struct can be safely freed once the function returns.
+ *
+ * @param r_callable A pointer that will receive the new Callable.
+ * @param p_callable_custom_info The info required to construct a Callable.
+ */
+typedef void (*GDExtensionInterfaceCallableCustomCreate)(GDExtensionUninitializedTypePtr r_callable, GDExtensionCallableCustomInfo *p_callable_custom_info);
+
+/**
+ * @name callable_custom_get_userdata
+ * @since 4.2
+ *
+ * Retrieves the userdata pointer from a custom Callable.
+ *
+ * If the Callable is not a custom Callable or the token does not match the one provided to callable_custom_create() via GDExtensionCallableCustomInfo then NULL will be returned.
+ *
+ * @param p_callable A pointer to a Callable.
+ * @param p_token A pointer to an address that uniquely identifies the GDExtension.
+ */
+typedef void *(*GDExtensionInterfaceCallableCustomGetUserData)(GDExtensionConstTypePtr p_callable, void *p_token);
+
/* INTERFACE: ClassDB */
/**
@@ -2173,6 +2441,7 @@ typedef void *(*GDExtensionInterfaceClassdbGetClassTag)(GDExtensionConstStringNa
/**
* @name classdb_register_extension_class
* @since 4.1
+ * @deprecated in Godot 4.2. Use `classdb_register_extension_class2` instead.
*
* Registers an extension class in the ClassDB.
*
@@ -2186,6 +2455,21 @@ typedef void *(*GDExtensionInterfaceClassdbGetClassTag)(GDExtensionConstStringNa
typedef void (*GDExtensionInterfaceClassdbRegisterExtensionClass)(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_parent_class_name, const GDExtensionClassCreationInfo *p_extension_funcs);
/**
+ * @name classdb_register_extension_class2
+ * @since 4.2
+ *
+ * Registers an extension class in the ClassDB.
+ *
+ * Provided struct can be safely freed once the function returns.
+ *
+ * @param p_library A pointer the library received by the GDExtension's entry point function.
+ * @param p_class_name A pointer to a StringName with the class name.
+ * @param p_parent_class_name A pointer to a StringName with the parent class name.
+ * @param p_extension_funcs A pointer to a GDExtensionClassCreationInfo2 struct.
+ */
+typedef void (*GDExtensionInterfaceClassdbRegisterExtensionClass2)(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_parent_class_name, const GDExtensionClassCreationInfo2 *p_extension_funcs);
+
+/**
* @name classdb_register_extension_class_method
* @since 4.1
*
diff --git a/core/extension/gdextension_manager.cpp b/core/extension/gdextension_manager.cpp
index 63e809bc7c..0dc84f685f 100644
--- a/core/extension/gdextension_manager.cpp
+++ b/core/extension/gdextension_manager.cpp
@@ -29,59 +29,120 @@
/**************************************************************************/
#include "gdextension_manager.h"
-#include "core/io/file_access.h"
-GDExtensionManager::LoadStatus GDExtensionManager::load_extension(const String &p_path) {
- if (gdextension_map.has(p_path)) {
- return LOAD_STATUS_ALREADY_LOADED;
- }
- Ref<GDExtension> extension = ResourceLoader::load(p_path);
- if (extension.is_null()) {
- return LOAD_STATUS_FAILED;
- }
+#include "core/extension/gdextension_compat_hashes.h"
+#include "core/io/file_access.h"
+#include "core/object/script_language.h"
+GDExtensionManager::LoadStatus GDExtensionManager::_load_extension_internal(const Ref<GDExtension> &p_extension) {
if (level >= 0) { // Already initialized up to some level.
- int32_t minimum_level = extension->get_minimum_library_initialization_level();
+ int32_t minimum_level = p_extension->get_minimum_library_initialization_level();
if (minimum_level < MIN(level, GDExtension::INITIALIZATION_LEVEL_SCENE)) {
return LOAD_STATUS_NEEDS_RESTART;
}
// Initialize up to current level.
for (int32_t i = minimum_level; i <= level; i++) {
- extension->initialize_library(GDExtension::InitializationLevel(i));
+ p_extension->initialize_library(GDExtension::InitializationLevel(i));
}
}
- for (const KeyValue<String, String> &kv : extension->class_icon_paths) {
+ for (const KeyValue<String, String> &kv : p_extension->class_icon_paths) {
gdextension_class_icon_paths[kv.key] = kv.value;
}
+ return LOAD_STATUS_OK;
+}
+
+GDExtensionManager::LoadStatus GDExtensionManager::_unload_extension_internal(const Ref<GDExtension> &p_extension) {
+ if (level >= 0) { // Already initialized up to some level.
+ // Deinitialize down from current level.
+ for (int32_t i = level; i >= GDExtension::INITIALIZATION_LEVEL_CORE; i--) {
+ p_extension->deinitialize_library(GDExtension::InitializationLevel(i));
+ }
+ }
+
+ for (const KeyValue<String, String> &kv : p_extension->class_icon_paths) {
+ gdextension_class_icon_paths.erase(kv.key);
+ }
+
+ return LOAD_STATUS_OK;
+}
+
+GDExtensionManager::LoadStatus GDExtensionManager::load_extension(const String &p_path) {
+ if (gdextension_map.has(p_path)) {
+ return LOAD_STATUS_ALREADY_LOADED;
+ }
+ Ref<GDExtension> extension = ResourceLoader::load(p_path);
+ if (extension.is_null()) {
+ return LOAD_STATUS_FAILED;
+ }
+
+ LoadStatus status = _load_extension_internal(extension);
+ if (status != LOAD_STATUS_OK) {
+ return status;
+ }
+
gdextension_map[p_path] = extension;
return LOAD_STATUS_OK;
}
GDExtensionManager::LoadStatus GDExtensionManager::reload_extension(const String &p_path) {
- return LOAD_STATUS_OK; //TODO
-}
-GDExtensionManager::LoadStatus GDExtensionManager::unload_extension(const String &p_path) {
+#ifndef TOOLS_ENABLED
+ ERR_FAIL_V_MSG(LOAD_STATUS_FAILED, "GDExtensions can only be reloaded in an editor build.");
+#else
+ ERR_FAIL_COND_V_MSG(!Engine::get_singleton()->is_extension_reloading_enabled(), LOAD_STATUS_FAILED, "GDExtension reloading is disabled.");
+
if (!gdextension_map.has(p_path)) {
return LOAD_STATUS_NOT_LOADED;
}
Ref<GDExtension> extension = gdextension_map[p_path];
+ ERR_FAIL_COND_V_MSG(!extension->is_reloadable(), LOAD_STATUS_FAILED, vformat("This GDExtension is not marked as 'reloadable' or doesn't support reloading: %s.", p_path));
- if (level >= 0) { // Already initialized up to some level.
- int32_t minimum_level = extension->get_minimum_library_initialization_level();
- if (minimum_level < MIN(level, GDExtension::INITIALIZATION_LEVEL_SCENE)) {
- return LOAD_STATUS_NEEDS_RESTART;
- }
- // Deinitialize down to current level.
- for (int32_t i = level; i >= minimum_level; i--) {
- extension->deinitialize_library(GDExtension::InitializationLevel(i));
+ LoadStatus status;
+
+ extension->prepare_reload();
+
+ // Unload library if it's open. It may not be open if the developer made a
+ // change that broke loading in a previous hot-reload attempt.
+ if (extension->is_library_open()) {
+ status = _unload_extension_internal(extension);
+ if (status != LOAD_STATUS_OK) {
+ // We need to clear these no matter what.
+ extension->clear_instance_bindings();
+ return status;
}
+
+ extension->clear_instance_bindings();
+ extension->close_library();
}
- for (const KeyValue<String, String> &kv : extension->class_icon_paths) {
- gdextension_class_icon_paths.erase(kv.key);
+ Error err = GDExtensionResourceLoader::load_gdextension_resource(p_path, extension);
+ if (err != OK) {
+ return LOAD_STATUS_FAILED;
+ }
+
+ status = _load_extension_internal(extension);
+ if (status != LOAD_STATUS_OK) {
+ return status;
+ }
+
+ extension->finish_reload();
+
+ return LOAD_STATUS_OK;
+#endif
+}
+
+GDExtensionManager::LoadStatus GDExtensionManager::unload_extension(const String &p_path) {
+ if (!gdextension_map.has(p_path)) {
+ return LOAD_STATUS_NOT_LOADED;
+ }
+
+ Ref<GDExtension> extension = gdextension_map[p_path];
+
+ LoadStatus status = _unload_extension_internal(extension);
+ if (status != LOAD_STATUS_OK) {
+ return status;
}
gdextension_map.erase(p_path);
@@ -134,6 +195,36 @@ void GDExtensionManager::deinitialize_extensions(GDExtension::InitializationLeve
level = int32_t(p_level) - 1;
}
+#ifdef TOOLS_ENABLED
+void GDExtensionManager::track_instance_binding(void *p_token, Object *p_object) {
+ for (KeyValue<String, Ref<GDExtension>> &E : gdextension_map) {
+ if (E.value.ptr() == p_token) {
+ if (E.value->is_reloadable()) {
+ E.value->track_instance_binding(p_object);
+ return;
+ }
+ }
+ }
+}
+
+void GDExtensionManager::untrack_instance_binding(void *p_token, Object *p_object) {
+ for (KeyValue<String, Ref<GDExtension>> &E : gdextension_map) {
+ if (E.value.ptr() == p_token) {
+ if (E.value->is_reloadable()) {
+ E.value->untrack_instance_binding(p_object);
+ return;
+ }
+ }
+ }
+}
+
+void GDExtensionManager::_reload_all_scripts() {
+ for (int i = 0; i < ScriptServer::get_language_count(); i++) {
+ ScriptServer::get_language(i)->reload_all_scripts();
+ }
+}
+#endif // TOOLS_ENABLED
+
void GDExtensionManager::load_extensions() {
Ref<FileAccess> f = FileAccess::open(GDExtension::get_extension_list_config_file(), FileAccess::READ);
while (f.is_valid() && !f->eof_reached()) {
@@ -143,11 +234,37 @@ void GDExtensionManager::load_extensions() {
ERR_CONTINUE_MSG(err == LOAD_STATUS_FAILED, "Error loading extension: " + s);
}
}
+
+ OS::get_singleton()->load_platform_gdextensions();
+}
+
+void GDExtensionManager::reload_extensions() {
+#ifdef TOOLS_ENABLED
+ bool reloaded = false;
+ for (const KeyValue<String, Ref<GDExtension>> &E : gdextension_map) {
+ if (!E.value->is_reloadable()) {
+ continue;
+ }
+
+ if (E.value->has_library_changed()) {
+ reloaded = true;
+ reload_extension(E.value->get_path());
+ }
+ }
+
+ if (reloaded) {
+ emit_signal("extensions_reloaded");
+
+ // Reload all scripts to clear out old references.
+ callable_mp_static(&GDExtensionManager::_reload_all_scripts).call_deferred();
+ }
+#endif
}
GDExtensionManager *GDExtensionManager::get_singleton() {
return singleton;
}
+
void GDExtensionManager::_bind_methods() {
ClassDB::bind_method(D_METHOD("load_extension", "path"), &GDExtensionManager::load_extension);
ClassDB::bind_method(D_METHOD("reload_extension", "path"), &GDExtensionManager::reload_extension);
@@ -162,6 +279,8 @@ void GDExtensionManager::_bind_methods() {
BIND_ENUM_CONSTANT(LOAD_STATUS_ALREADY_LOADED);
BIND_ENUM_CONSTANT(LOAD_STATUS_NOT_LOADED);
BIND_ENUM_CONSTANT(LOAD_STATUS_NEEDS_RESTART);
+
+ ADD_SIGNAL(MethodInfo("extensions_reloaded"));
}
GDExtensionManager *GDExtensionManager::singleton = nullptr;
@@ -169,4 +288,8 @@ GDExtensionManager *GDExtensionManager::singleton = nullptr;
GDExtensionManager::GDExtensionManager() {
ERR_FAIL_COND(singleton != nullptr);
singleton = this;
+
+#ifndef DISABLE_DEPRECATED
+ GDExtensionCompatHashes::initialize();
+#endif
}
diff --git a/core/extension/gdextension_manager.h b/core/extension/gdextension_manager.h
index 3643f043d8..8cd6d5a3e2 100644
--- a/core/extension/gdextension_manager.h
+++ b/core/extension/gdextension_manager.h
@@ -53,6 +53,15 @@ public:
LOAD_STATUS_NEEDS_RESTART,
};
+private:
+ LoadStatus _load_extension_internal(const Ref<GDExtension> &p_extension);
+ LoadStatus _unload_extension_internal(const Ref<GDExtension> &p_extension);
+
+#ifdef TOOLS_ENABLED
+ static void _reload_all_scripts();
+#endif
+
+public:
LoadStatus load_extension(const String &p_path);
LoadStatus reload_extension(const String &p_path);
LoadStatus unload_extension(const String &p_path);
@@ -66,9 +75,15 @@ public:
void initialize_extensions(GDExtension::InitializationLevel p_level);
void deinitialize_extensions(GDExtension::InitializationLevel p_level);
+#ifdef TOOLS_ENABLED
+ void track_instance_binding(void *p_token, Object *p_object);
+ void untrack_instance_binding(void *p_token, Object *p_object);
+#endif
+
static GDExtensionManager *get_singleton();
void load_extensions();
+ void reload_extensions();
GDExtensionManager();
};
diff --git a/core/input/gamecontrollerdb.txt b/core/input/gamecontrollerdb.txt
index 8c0deb7f74..9a43c4e35d 100644
--- a/core/input/gamecontrollerdb.txt
+++ b/core/input/gamecontrollerdb.txt
@@ -25,6 +25,7 @@
03000000c82d00000151000000000000,8BitDo M30,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftx:a0,lefty:a2,rightshoulder:b6,righttrigger:b7,rightx:a3,righty:a5,start:b11,x:b3,y:b4,platform:Windows,
03000000c82d00000650000000000000,8BitDo M30,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b8,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b7,start:b11,x:b3,y:b4,platform:Windows,
03000000c82d00005106000000000000,8BitDo M30,a:b0,b:b1,back:b10,dpdown:+a2,dpleft:-a0,dpright:+a0,dpup:-a2,guide:b2,leftshoulder:b8,lefttrigger:b9,rightshoulder:b6,righttrigger:b7,start:b11,x:b3,y:b4,platform:Windows,
+03000000c82d00002090000000000000,8BitDo Micro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Windows,
03000000c82d00000310000000000000,8BitDo N30,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Windows,
03000000c82d00000451000000000000,8BitDo N30,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftx:a0,lefty:a2,rightx:a3,righty:a5,start:b11,platform:Windows,
03000000c82d00002028000000000000,8BitDo N30,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b4,y:b3,platform:Windows,
@@ -35,6 +36,8 @@
03000000c82d00006528000000000000,8BitDo N30 Pro 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Windows,
03000000c82d00000290000000000000,8BitDo N64,+rightx:b9,+righty:b3,-rightx:b4,-righty:b8,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,platform:Windows,
03000000c82d00003038000000000000,8BitDo N64,+rightx:b9,+righty:b3,-rightx:b4,-righty:b8,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,platform:Windows,
+03000000c82d00006928000000000000,8BitDo N64,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a3,righty:a4,start:b11,platform:Windows,
+03000000c82d00002590000000000000,8BitDo NEOGEO,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,
030000003512000012ab000000000000,8BitDo NES30,a:b2,b:b1,back:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b7,x:b3,y:b0,platform:Windows,
03000000c82d000012ab000000000000,8BitDo NES30,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Windows,
03000000022000000090000000000000,8BitDo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Windows,
@@ -66,12 +69,15 @@
03000000c82d00000021000000000000,8BitDo SN30 Pro,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b3,y:b4,platform:Windows,
03000000c82d00000160000000000000,8BitDo SN30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Windows,
03000000c82d00000161000000000000,8BitDo SN30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Windows,
-03000000c82d00000121000000000000,8BitDo Xbox One SN30 Pro,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,
03000000c82d00000260000000000000,8BitDo SN30 Pro Plus,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Windows,
03000000c82d00000261000000000000,8BitDo SN30 Pro Plus,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Windows,
+03000000c82d00001230000000000000,8BitDo Ultimate,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,paddle1:b2,paddle2:b5,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,
+03000000c82d00001530000000000000,8BitDo Ultimate C,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,
+03000000c82d00001630000000000000,8BitDo Ultimate C,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,
+03000000c82d00001730000000000000,8BitDo Ultimate C,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,
03000000c82d00001130000000000000,8BitDo Ultimate Wired,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,misc1:b26,paddle1:b24,paddle2:b25,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,
-03000000c82d00001230000000000000,8BitDo Ultimate Wireless,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,
03000000c82d00001330000000000000,8BitDo Ultimate Wireless,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b26,paddle1:b23,paddle2:b19,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Windows,
+03000000c82d00000121000000000000,8BitDo Xbox One SN30 Pro,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,
03000000a00500003232000000000000,8BitDo Zero,a:b0,b:b1,back:b10,dpdown:+a2,dpleft:-a0,dpright:+a0,dpup:-a2,leftshoulder:b6,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Windows,
03000000c82d00001890000000000000,8BitDo Zero 2,a:b1,b:b0,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Windows,
03000000c82d00003032000000000000,8BitDo Zero 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Windows,
@@ -79,6 +85,7 @@
03000000c01100000355000000000000,Acrux,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,
03000000fa190000f0ff000000000000,Acteck AGJ 3200,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,
03000000d1180000402c000000000000,ADT1,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a3,rightx:a2,righty:a5,x:b3,y:b4,platform:Windows,
+030000006f0e00008801000000000000,Afterglow Deluxe Nintendo Switch Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,
03000000341a00003608000000000000,Afterglow PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,
030000006f0e00000263000000000000,Afterglow PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,
030000006f0e00001101000000000000,Afterglow PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,
@@ -105,6 +112,8 @@
03000000a30c00002800000000000000,Astro City Mini,a:b2,b:b1,back:b8,leftx:a3,lefty:a4,rightshoulder:b4,righttrigger:b5,start:b9,x:b3,y:b0,platform:Windows,
03000000050b00000579000000000000,ASUS ROG Kunai 3,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,
03000000050b00000679000000000000,ASUS ROG Kunai 3,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,misc1:b15,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,
+03000000503200000110000000000000,Atari VCS Classic Controller,a:b0,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,start:b3,platform:Windows,
+03000000503200000210000000000000,Atari VCS Modern Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,rightx:a3,righty:a4,start:b9,x:b2,y:b3,platform:Windows,
03000000e4150000103f000000000000,Batarang,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b11,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,
03000000d6200000e557000000000000,Batarang PlayStation Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,
03000000c01100001352000000000000,Battalife Joystick,a:b6,b:b7,back:b2,leftshoulder:b0,leftx:a0,lefty:a1,rightshoulder:b1,start:b3,x:b4,y:b5,platform:Windows,
@@ -176,21 +185,23 @@
03000000101c0000181c000000000000,Essential,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b4,leftx:a1,lefty:a0,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Windows,
030000008f0e00000f31000000000000,EXEQ,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b2,platform:Windows,
03000000341a00000108000000000000,EXEQ RF Gamepad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,
-030000006f0e00008401000000000000,Faceoff Deluxe Nintendo Switch Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,
-030000006f0e00008001000000000000,Faceoff Pro Nintendo Switch Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,
+030000006f0e00008401000000000000,Faceoff Deluxe Nintendo Switch Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,
+030000006f0e00008101000000000000,Faceoff Deluxe Pro Nintendo Switch Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,
+030000006f0e00008001000000000000,Faceoff Pro Nintendo Switch Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,
03000000021000000090000000000000,FC30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b8,leftstick:b13,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b9,rightstick:b14,righttrigger:b7,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Windows,
0300000011040000c600000000000000,FC801,a:b0,b:b1,back:b6,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,start:b7,x:b2,y:b3,platform:Windows,
03000000852100000201000000000000,FF GP1,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,
03000000ad1b000028f0000000000000,Fightpad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b11,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,
03000000ad1b00002ef0000000000000,Fightpad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,
03000000ad1b000038f0000000000000,Fightpad TE,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b8,rightshoulder:b5,righttrigger:b9,start:b7,x:b2,y:b3,platform:Windows,
+03005036852100000000000000000000,Final Fantasy XIV Online Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,
03000000f806000001a3000000000000,Firestorm,a:b9,b:b7,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b0,leftstick:b10,lefttrigger:b1,leftx:a0,lefty:a1,rightshoulder:b2,rightstick:b11,righttrigger:b3,start:b12,x:b8,y:b4,platform:Windows,
03000000b50700000399000000000000,Firestorm 2,a:b2,b:b4,back:b10,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b8,righttrigger:b9,start:b11,x:b3,y:b5,platform:Windows,
03000000b50700001302000000000000,Firestorm D3,a:b0,b:b2,leftshoulder:b4,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b7,x:b1,y:b3,platform:Windows,
03000000b40400001024000000000000,Flydigi Apex,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b3,y:b4,platform:Windows,
-03000000151900004000000000000000,Flydigi Vader 2,a:b11,b:b10,back:b3,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b7,leftstick:b1,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b0,righttrigger:b4,rightx:a3,righty:a4,start:b2,x:b9,y:b8,platform:Windows,
-03000000b40400001124000000000000,Flydigi Vader 2,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b12,lefttrigger:b8,leftx:a0,lefty:a1,paddle1:b4,paddle2:b5,paddle4:b17,rightshoulder:b7,rightstick:b13,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b2,y:b3,platform:Windows,
-03000000b40400001224000000000000,Flydigi Vader 2 Pro,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b12,lefttrigger:a5,leftx:a0,lefty:a1,paddle1:b15,paddle2:b16,paddle3:b17,paddle4:b18,rightshoulder:b7,rightstick:b13,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Windows,
+03000000151900004000000000000000,Flydigi Vader 2,a:b27,b:b26,back:b19,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b6,leftshoulder:b23,leftstick:b17,lefttrigger:b21,leftx:a0,lefty:a1,misc1:b15,paddle1:b11,paddle2:b10,paddle3:b13,paddle4:b12,rightshoulder:b22,rightstick:b16,righttrigger:b20,rightx:a3,righty:a4,start:b18,x:b25,y:b24,platform:Windows,
+03000000b40400001124000000000000,Flydigi Vader 2,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b12,lefttrigger:b8,leftx:a0,lefty:a1,misc1:b14,paddle1:b4,paddle2:b5,paddle3:b16,paddle4:b17,rightshoulder:b7,rightstick:b13,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b2,y:b3,platform:Windows,
+03000000b40400001224000000000000,Flydigi Vader 2,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b12,lefttrigger:b8,leftx:a0,lefty:a1,misc1:b2,paddle1:b16,paddle2:b17,paddle3:b14,paddle4:b15,rightshoulder:b7,rightstick:b13,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,
030000008305000000a0000000000000,G08XU,a:b0,b:b1,back:b4,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b5,x:b2,y:b3,platform:Windows,
0300000066f700000100000000000000,Game VIB Joystick,a:b2,b:b3,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a3,righty:a2,start:b11,x:b0,y:b1,platform:Windows,
03000000260900002625000000000000,GameCube Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b6,lefttrigger:a4,leftx:a0,lefty:a1,righttrigger:a5,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Windows,
@@ -288,7 +299,6 @@
03000000ad1b000002f5000000000000,Hori Real Arcade Pro VX,a:b0,b:b1,back:b7,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b11,rightx:a2,righty:a5,start:b6,x:b2,y:b3,platform:Windows,
030000000d0f00009c00000000000000,Hori TAC Pro,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,
030000000d0f0000c900000000000000,Hori Taiko Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,
-030000000d0f0000c100000000000000,Horipad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,
030000000d0f00006400000000000000,Horipad 3TP,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,
030000000d0f00001300000000000000,Horipad 3W,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,
030000000d0f00005500000000000000,Horipad 4 FPS,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,
@@ -297,16 +307,20 @@
030000000d0f00004200000000000000,Horipad A,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,
03000000ad1b000001f5000000000000,Horipad EXT2,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b11,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,
030000000d0f0000ee00000000000000,Horipad Mini 4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,
+030000000d0f0000c100000000000000,Horipad Nintendo Switch Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,
+030000000d0f0000f600000000000000,Horipad Nintendo Switch Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,
030000000d0f00006700000000000000,Horipad One,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,
030000000d0f0000dc00000000000000,Horipad Switch,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,
03000000242e00000b20000000000000,Hyperkin Admiral N64 Controller,+rightx:b11,+righty:b13,-rightx:b8,-righty:b12,a:b1,b:b0,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b14,leftx:a0,lefty:a1,rightshoulder:b5,start:b9,platform:Windows,
03000000242e0000ff0b000000000000,Hyperkin N64 Adapter,a:b1,b:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightx:a2,righty:a3,start:b9,platform:Windows,
03000000790000004e95000000000000,Hyperkin N64 Controller Adapter,a:b1,b:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b7,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b5,rightx:a5,righty:a2,start:b9,platform:Windows,
+03000000242e00006a38000000000000,Hyperkin Trooper 2,a:b0,b:b1,back:b4,leftshoulder:b2,leftx:a0,lefty:a1,rightshoulder:b3,start:b5,platform:Windows,
03000000d81d00000e00000000000000,iBuffalo AC02 Arcade Joystick,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b2,rightstick:b11,righttrigger:b3,rightx:a2,righty:a5,start:b8,x:b4,y:b5,platform:Windows,
03000000d81d00000f00000000000000,iBuffalo BSGP1204 Series,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,
03000000d81d00001000000000000000,iBuffalo BSGP1204P Series,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,
030000005c0a00000285000000000000,iDroidCon,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b4,y:b6,platform:Windows,
03000000696400006964000000000000,iDroidCon Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,
+03000000511d00000230000000000000,iGUGU Gamecore,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b1,leftstick:b4,lefttrigger:b3,leftx:a0,lefty:a1,rightshoulder:b0,righttrigger:b2,platform:Windows,
03000000b50700001403000000000000,Impact Black,a:b2,b:b3,back:b8,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Windows,
030000006f0e00002401000000000000,Injustice Fightstick PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,
03000000830500005130000000000000,InterAct ActionPad,a:b0,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,lefttrigger:b7,rightshoulder:b5,righttrigger:b2,start:b9,x:b3,y:b4,platform:Windows,
@@ -374,7 +388,7 @@
030000009f000000adbb000000000000,MaxJoypad Virtual Controller,a:b1,b:b2,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b8,x:b3,y:b0,platform:Windows,
03000000250900000128000000000000,Mayflash Arcade Stick,a:b1,b:b2,back:b8,leftshoulder:b0,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b3,righttrigger:b7,start:b9,x:b5,y:b6,platform:Windows,
030000008f0e00001330000000000000,Mayflash Controller Adapter,a:b1,b:b2,back:b8,dpdown:h0.8,dpleft:h0.2,dpright:h0.1,dpup:h0.4,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightx:a3~,righty:a2,start:b9,x:b0,y:b3,platform:Windows,
-03000000242f00003700000000000000,Mayflash F101,a:b1,b:b2,back:b8,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,
+03000000242f00003700000000000000,Mayflash F101,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,
03000000790000003018000000000000,Mayflash F300 Arcade Joystick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,
03000000242f00003900000000000000,Mayflash F300 Elite Arcade Joystick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,
03000000790000004418000000000000,Mayflash GameCube Controller,a:b1,b:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b0,y:b3,platform:Windows,
@@ -382,6 +396,7 @@
03000000242f00007300000000000000,Mayflash Magic NS,a:b1,b:b4,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b0,y:b3,platform:Windows,
0300000079000000d218000000000000,Mayflash Magic NS,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,
03000000d620000010a7000000000000,Mayflash Magic NS,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,
+03000000242f0000f400000000000000,Mayflash N64 Controller Adapter,a:b1,b:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightx:a2,righty:a5,start:b9,platform:Windows,
03000000790000007918000000000000,Mayflash N64 Controller Adapter,a:b1,b:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b9,leftx:a0,lefty:a1,righttrigger:b7,rightx:a3,righty:a2,start:b8,platform:Windows,
030000008f0e00001030000000000000,Mayflash Saturn Adapter,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b5,lefttrigger:b7,rightshoulder:b6,righttrigger:b2,start:b9,x:b3,y:b4,platform:Windows,
0300000025090000e803000000000000,Mayflash Wii Classic Adapter,a:b1,b:b0,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:a4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:a5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b2,platform:Windows,
@@ -445,7 +460,7 @@
03000000362800000100000000000000,OUYA Controller,a:b0,b:b3,dpdown:b9,dpleft:b10,dpright:b11,dpup:b8,guide:b14,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:b13,rightx:a3,righty:a4,x:b1,y:b2,platform:Windows,
03000000120c0000f60e000000000000,P4 Gamepad,a:b1,b:b2,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b5,lefttrigger:b7,rightshoulder:b4,righttrigger:b6,start:b9,x:b0,y:b3,platform:Windows,
03000000790000002201000000000000,PC Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,
-030000006f0e00008501000000000000,PDP Fightpad Pro,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b0,platform:Windows,
+030000006f0e00008501000000000000,PDP Fightpad Pro GameCube Controller,a:b1,b:b0,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,
030000006f0e00000901000000000000,PDP PS3 Versus Fighting,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,
030000008f0e00004100000000000000,PlaySega,a:b1,b:b0,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,lefttrigger:b7,rightshoulder:b5,righttrigger:b2,start:b8,x:b4,y:b3,platform:Windows,
03000000666600006706000000000000,PlayStation Adapter,a:b2,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,leftshoulder:b6,leftstick:b9,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b10,righttrigger:b5,rightx:a2,righty:a3,start:b11,x:b3,y:b0,platform:Windows,
@@ -455,9 +470,14 @@
03000000f0250000c183000000000000,PlayStation Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,
03000000d9040000160f000000000000,PlayStation Controller Adapter,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Windows,
030000004c0500003713000000000000,PlayStation Vita,a:b1,b:b2,back:b8,dpdown:b13,dpleft:b15,dpright:b14,dpup:b12,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,rightx:a3,righty:a4,start:b9,x:b0,y:b3,platform:Windows,
+03000000d620000011a7000000000000,PowerA Core Plus GameCube Controller,a:b1,b:b0,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,
+03000000dd62000015a7000000000000,PowerA Fusion Nintendo Switch Arcade Stick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,
+03000000d620000012a7000000000000,PowerA Fusion Nintendo Switch Fight Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,
+03000000dd62000016a7000000000000,PowerA Fusion Pro Nintendo Switch Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,
+03000000d620000013a7000000000000,PowerA Nintendo Switch Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,
03000000d62000006dca000000000000,PowerA Pro Ex,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,
0300000062060000d570000000000000,PowerA PS3 Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,
-03000000d620000013a7000000000000,PowerA Switch Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,
+03000000d620000014a7000000000000,PowerA Spectra Nintendo Switch Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,
030000006d04000084ca000000000000,Precision,a:b0,b:b1,back:b7,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b5,leftstick:b8,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b4,rightstick:b9,righttrigger:b11,rightx:a3,righty:a4,start:b6,x:b2,y:b3,platform:Windows,
03000000d62000009557000000000000,Pro Elite PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,
03000000c62400001a53000000000000,Pro Ex Mini,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b11,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,
@@ -530,9 +550,11 @@
030000009b2800002300000000000000,Raphnet 3DO Adapter,a:b0,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b2,start:b3,platform:Windows,
030000009b2800006900000000000000,Raphnet 3DO Adapter,a:b0,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b2,start:b3,platform:Windows,
030000009b2800000800000000000000,Raphnet Dreamcast Adapter,a:b2,b:b1,dpdown:b5,dpleft:b6,dpright:b7,dpup:b4,lefttrigger:a2,leftx:a0,righttrigger:a3,righty:a1,start:b3,x:b10,y:b9,platform:Windows,
+030000009b2800006200000000000000,Raphnet GameCube Adapter,a:b0,b:b7,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:b5,rightx:a3,righty:a4,start:b3,x:b1,y:b8,platform:Windows,
030000009b2800003200000000000000,Raphnet GC and N64 Adapter,a:b0,b:b7,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,lefttrigger:+a5,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:+a2,rightx:a3,righty:a4,start:b3,x:b1,y:b8,platform:Windows,
030000009b2800006000000000000000,Raphnet GC and N64 Adapter,a:b0,b:b7,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,lefttrigger:+a5,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:+a2,rightx:a3,righty:a4,start:b3,x:b1,y:b8,platform:Windows,
030000009b2800001800000000000000,Raphnet Jaguar Adapter,a:b2,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b0,righttrigger:b10,start:b3,x:b11,y:b12,platform:Windows,
+030000009b2800006300000000000000,Raphnet N64 Adapter,+rightx:b9,+righty:b7,-rightx:b8,-righty:b6,a:b0,b:b1,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,leftshoulder:b4,lefttrigger:b2,leftx:a0,lefty:a1,rightshoulder:b5,start:b3,platform:Windows,
030000009b2800000200000000000000,Raphnet NES Adapter,a:b7,b:b6,back:b5,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftx:a0,lefty:a1,start:b4,platform:Windows,
030000009b2800004400000000000000,Raphnet PS1 and PS2 Adapter,a:b1,b:b2,back:b5,dpdown:b13,dpleft:b14,dpright:b15,dpup:b12,leftshoulder:b6,leftstick:b10,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b9,rightx:a3,righty:a4,start:b4,x:b0,y:b3,platform:Windows,
030000009b2800004300000000000000,Raphnet Saturn,a:b0,b:b1,dpdown:b13,dpleft:b14,dpright:b15,dpup:b12,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b2,start:b8,x:b3,y:b4,platform:Windows,
@@ -545,6 +567,7 @@
030000009b2800002c00000000000000,Raphnet Wii Classic Adapter,a:b1,b:b4,back:b2,dpdown:b13,dpleft:b14,dpright:b15,dpup:b12,guide:b10,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a3,righty:a4,start:b3,x:b0,y:b5,platform:Windows,
030000009b2800008000000000000000,Raphnet Wii Classic Adapter,a:b1,b:b4,back:b2,dpdown:b13,dpleft:b14,dpright:b15,dpup:b12,guide:b10,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a3,righty:a4,start:b3,x:b0,y:b5,platform:Windows,
03000000321500000003000000000000,Razer Hydra,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a2,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,
+03000000f8270000bf0b000000000000,Razer Kishi,a:b6,b:b7,back:b16,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b18,leftshoulder:b12,leftstick:b19,lefttrigger:b14,leftx:a0,lefty:a1,rightshoulder:b13,rightstick:b20,righttrigger:b15,rightx:a3,righty:a4,start:b17,x:b9,y:b10,platform:Windows,
03000000321500000204000000000000,Razer Panthera PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,
03000000321500000104000000000000,Razer Panthera PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,
03000000321500000010000000000000,Razer Raiju,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,
@@ -571,10 +594,10 @@
030000006f0e00001f01000000000000,Rock Candy,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a2,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,
030000006f0e00004601000000000000,Rock Candy,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,
03000000c6240000fefa000000000000,Rock Candy Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,
+030000006f0e00008701000000000000,Rock Candy Nintendo Switch Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,
030000006f0e00001e01000000000000,Rock Candy PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,
030000006f0e00002801000000000000,Rock Candy PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,
030000006f0e00002f01000000000000,Rock Candy PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,
-030000006f0e00008701000000000000,Rock Candy Switch Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b13,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,
03000000050b0000e318000000000000,ROG Chakram,a:b1,b:b0,leftx:a0,lefty:a1,x:b2,y:b3,platform:Windows,
03000000050b0000e518000000000000,ROG Chakram,a:b1,b:b0,leftx:a0,lefty:a1,x:b2,y:b3,platform:Windows,
03000000050b00005819000000000000,ROG Chakram Core,a:b1,b:b0,leftx:a0,lefty:a1,x:b2,y:b3,platform:Windows,
@@ -612,6 +635,7 @@
03000000952e00002577000000000000,Scuf PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,
03000000a30c00002500000000000000,Sega Genesis Mini 3B Controller,a:b2,b:b1,dpdown:+a4,dpleft:-a3,dpright:+a3,dpup:-a4,righttrigger:b5,start:b9,platform:Windows,
03000000a30c00002400000000000000,Sega Mega Drive Mini 6B Controller,a:b2,b:b1,dpdown:+a4,dpleft:-a3,dpright:+a3,dpup:-a4,rightshoulder:b4,righttrigger:b5,start:b9,x:b3,y:b0,platform:Windows,
+03000000d804000086e6000000000000,Sega Multi Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b7,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b2,start:b8,x:b3,y:b4,platform:Windows,
0300000000050000289b000000000000,Sega Saturn Adapter,a:b1,b:b2,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,lefttrigger:b4,rightshoulder:b7,righttrigger:b5,start:b9,x:b0,y:b3,platform:Windows,
0300000000f000000800000000000000,Sega Saturn Controller,a:b1,b:b2,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,rightshoulder:b7,righttrigger:b3,start:b0,x:b5,y:b6,platform:Windows,
03000000730700000601000000000000,Sega Saturn Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b2,start:b9,x:b3,y:b4,platform:Windows,
@@ -645,8 +669,6 @@
030000001f08000001e4000000000000,Super Famicom Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b9,x:b3,y:b0,platform:Windows,
03000000790000000418000000000000,Super Famicom Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b33,rightshoulder:b5,start:b7,x:b2,y:b3,platform:Windows,
03000000341200001300000000000000,Super Racer,a:b2,b:b3,back:b8,leftshoulder:b5,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b4,righttrigger:b7,x:b0,y:b1,platform:Windows,
-03000000d620000011a7000000000000,Switch Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,
-030000000d0f0000f600000000000000,Switch Hori Pad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,
03000000457500002211000000000000,Szmy Power PC Gamepad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,
030000004f0400000ab1000000000000,T16000M,a:b0,b:b1,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b11,leftshoulder:b4,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b7,start:b10,x:b2,y:b3,platform:Windows,
030000000d0f00007b00000000000000,TAC GEAR,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,
@@ -657,6 +679,8 @@
03000000ba2200000701000000000000,Technology Innovation PS2 Adapter,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b3,y:b2,platform:Windows,
03000000c61100001000000000000000,Tencent Xianyou Gamepad,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,x:b3,y:b4,platform:Windows,
03000000790000002601000000000000,TGZ,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b3,y:b0,platform:Windows,
+03000000591c00002400000000000000,THEC64 Joystick,a:b0,b:b1,back:b6,leftshoulder:b4,leftx:a0,lefty:a4,rightshoulder:b5,start:b7,x:b2,y:b3,platform:Windows,
+03000000591c00002600000000000000,THEGamepad,a:b2,b:b1,back:b6,leftx:a0,lefty:a1,start:b7,x:b3,y:b0,platform:Windows,
030000004f04000015b3000000000000,Thrustmaster Dual Analog 4,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b3,platform:Windows,
030000004f04000023b3000000000000,Thrustmaster Dual Trigger PlayStation Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,
030000004f0400000ed0000000000000,ThrustMaster eSwap Pro Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,
@@ -772,9 +796,14 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
03000000c82d00000151000000010000,8BitDo M30,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b7,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,
03000000c82d00000650000001000000,8BitDo M30,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b8,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b7,start:b11,x:b3,y:b4,platform:Mac OS X,
03000000c82d00005106000000010000,8BitDo M30,a:b1,b:b0,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,guide:b2,leftshoulder:b6,lefttrigger:a5,rightshoulder:b7,righttrigger:a4,start:b11,x:b4,y:b3,platform:Mac OS X,
+03000000c82d00002090000000010000,8BitDo Micro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X,
03000000c82d00000451000000010000,8BitDo N30,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftx:a0,lefty:a1,rightx:a2,righty:a3,start:b11,platform:Mac OS X,
03000000c82d00001590000001000000,8BitDo N30 Pro 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a5,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X,
03000000c82d00006528000000010000,8BitDo N30 Pro 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X,
+03000000c82d00006928000000010000,8BitDo N64,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a2,righty:a3,start:b11,platform:Mac OS X,
+03000000c82d00002590000000010000,8BitDo NEOGEO,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,
+03000000c82d00002590000001000000,8BitDo NEOGEO,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,
+03000000c82d00002690000000010000,8BitDo NEOGEOa:b0,+leftx:h0.2,+lefty:h0.4,-leftx:h0.8,-lefty:h0.1,b:b1,back:b10,leftshoulder:b6,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Mac OS X,
030000003512000012ab000001000000,8BitDo NES30,a:b1,b:b0,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Mac OS X,
03000000c82d000012ab000001000000,8BitDo NES30,a:b0,b:b1,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Mac OS X,
03000000c82d00002028000000010000,8BitDo NES30,a:b0,b:b1,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Mac OS X,
@@ -798,6 +827,10 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
03000000c82d00000161000000010000,8BitDo SN30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b4,y:b3,platform:Mac OS X,
03000000c82d00000260000001000000,8BitDo SN30 Pro Plus,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X,
03000000c82d00000261000000010000,8BitDo SN30 Pro Plus,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X,
+03000000c82d00001230000000010000,8BitDo Ultimate,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,paddle1:b2,paddle2:b5,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,
+03000000c82d00001530000001000000,8BitDo Ultimate C,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,
+03000000c82d00001630000001000000,8BitDo Ultimate C,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,
+03000000c82d00001730000001000000,8BitDo Ultimate C,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,
03000000c82d00001130000000020000,8BitDo Ultimate Wired,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b26,paddle1:b24,paddle2:b25,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,
03000000c82d00001330000001000000,8BitDo Ultimate Wireless,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b26,paddle1:b23,paddle2:b19,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,
03000000c82d00001330000000020000,8BitDo Ultimate Wireless Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b26,paddle1:b23,paddle2:b19,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,
@@ -812,6 +845,8 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
03000000050b00000045000031000000,ASUS Gamepad,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Mac OS X,
03000000050b00000579000000010000,ASUS ROG Kunai 3,a:b0,b:b1,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b14,leftshoulder:b6,leftstick:b15,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b42,paddle1:b9,paddle2:b11,rightshoulder:b7,rightstick:b16,righttrigger:a4,rightx:a2,righty:a3,start:b13,x:b3,y:b4,platform:Mac OS X,
03000000050b00000679000000010000,ASUS ROG Kunai 3,a:b0,b:b1,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b14,leftshoulder:b6,leftstick:b15,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b23,rightshoulder:b7,rightstick:b16,righttrigger:a4,rightx:a2,righty:a3,start:b13,x:b3,y:b4,platform:Mac OS X,
+03000000503200000110000047010000,Atari VCS Classic Controller,a:b0,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b3,start:b2,platform:Mac OS X,
+03000000503200000210000047010000,Atari VCS Modern Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b9,leftshoulder:b4,leftstick:b6,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a4,rightx:a2,righty:a3,start:b8,x:b2,y:b3,platform:Mac OS X,
03000000c62400001a89000000010000,BDA MOGA XP5-X Plus,a:b0,b:b1,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b14,leftshoulder:b6,leftstick:b15,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b16,righttrigger:a4,rightx:a2,righty:a3,start:b13,x:b3,y:b4,platform:Mac OS X,
03000000c62400001b89000000010000,BDA MOGA XP5-X Plus,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,
03000000d62000002a79000000010000,BDA PS4 Fightpad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,
@@ -823,9 +858,10 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
03000000a306000022f6000001030000,Cyborg V3 Rumble Pad PlayStation Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:+a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:-a3,rightx:a2,righty:a4,start:b9,x:b0,y:b3,platform:Mac OS X,
03000000791d00000103000009010000,Dual Box Wii Classic Adapter,a:b2,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,guide:b10,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Mac OS X,
030000006e0500000720000010020000,Elecom JC-W01U,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b1,platform:Mac OS X,
-030000006f0e00008401000003010000,Faceoff Premiere Wired Pro Controller for Nintendo Switch,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b13,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,
-03000000151900004000000001000000,Flydigi Vader 2,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Mac OS X,
-03000000b40400001124000000000000,Flydigi Vader 2,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b12,lefttrigger:b8,leftx:a0,lefty:a1,paddle1:b4,paddle2:b5,paddle3:b17,rightshoulder:b7,rightstick:b13,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b2,y:b3,platform:Mac OS X,
+030000006f0e00008401000003010000,Faceoff Deluxe Nintendo Switch Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b13,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,
+03000000151900004000000001000000,Flydigi Vader 2,a:b14,b:b15,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b21,leftshoulder:b6,leftstick:b12,lefttrigger:a5,leftx:a0,lefty:a1,paddle1:b2,paddle2:b5,paddle3:b16,paddle4:b17,rightshoulder:b7,rightstick:b13,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b0,y:b1,platform:Mac OS X,
+03000000b40400001124000001040000,Flydigi Vader 2,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b12,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b14,paddle1:b2,paddle2:b5,paddle3:b16,paddle4:b17,rightshoulder:b7,rightstick:b13,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,
+03000000b40400001224000003030000,Flydigi Vader 2,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b12,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b2,paddle1:b16,paddle2:b17,paddle3:b14,paddle4:b15,rightshoulder:b7,rightstick:b13,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,
03000000790000004618000000010000,GameCube Controller Adapter,a:b4,b:b0,dpdown:b56,dpleft:b60,dpright:b52,dpup:b48,lefttrigger:a12,leftx:a0,lefty:a4,rightshoulder:b28,righttrigger:a16,rightx:a20,righty:a8,start:b36,x:b8,y:b12,platform:Mac OS X,
03000000ac0500001a06000002020000,GameSir-T3 2.02,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b15,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,
03000000ad1b000001f9000000000000,Gamestop BB070 X360 Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,
@@ -849,7 +885,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
030000000d0f00004d00000000000000,Hori Gem Pad 3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,
030000000d0f00003801000008010000,Hori PC Engine Mini Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,start:b9,platform:Mac OS X,
030000000d0f00009200000000010000,Hori Pokken Tournament DX Pro,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Mac OS X,
-030000000d0f0000aa00000072050000,Hori Real Arcade Pro,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Mac OS X,
+030000000d0f0000aa00000072050000,Hori Real Arcade Pro for Nintendo Switch,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Mac OS X,
030000000d0f00000002000015010000,Hori Switch Split Pad Pro,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,
030000000d0f00006e00000000010000,Horipad 4 PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,
030000000d0f00006600000000010000,Horipad 4 PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,
@@ -857,7 +893,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
030000000d0f0000ee00000000010000,Horipad Mini 4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,
03000000242e0000ff0b000000010000,Hyperkin N64 Adapter,a:b1,b:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightx:a2,righty:a3,start:b9,platform:Mac OS X,
03000000790000004e95000000010000,Hyperkin N64 Controller Adapter,a:b1,b:b2,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,leftshoulder:b7,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightx:a5,righty:a2,start:b9,platform:Mac OS X,
-03000000830500006020000000000000,iBuffalo Gamepad,a:b1,b:b0,back:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b7,x:b3,y:b2,platform:Mac OS X,
+03000000830500006020000000000000,iBuffalo Super Famicom Controller,a:b1,b:b0,back:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b7,x:b3,y:b2,platform:Mac OS X,
03000000ef0500000300000000020000,InterAct AxisPad,a:b2,b:b3,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a3,righty:a2,start:b11,x:b0,y:b1,platform:Mac OS X,
03000000fd0500000030000010010000,Interact GoPad,a:b3,b:b4,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:b5,x:b0,y:b1,platform:Mac OS X,
030000007e0500000620000001000000,Joy-Con (L),+leftx:h0.2,+lefty:h0.4,-leftx:h0.8,-lefty:h0.1,a:b0,b:b1,back:b13,leftshoulder:b4,leftstick:b10,rightshoulder:b5,start:b8,x:b2,y:b3,platform:Mac OS X,
@@ -889,6 +925,8 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
03000000790000000018000000000000,Mayflash Wii U Pro Adapter,a:b4,b:b8,back:b32,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b16,leftstick:b40,lefttrigger:b24,leftx:a0,lefty:a4,rightshoulder:b20,rightstick:b44,righttrigger:b28,rightx:a8,righty:a12,start:b36,x:b0,y:b12,platform:Mac OS X,
03000000790000000018000000010000,Mayflash Wii U Pro Adapter,a:b4,b:b8,back:b32,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b16,leftstick:b40,lefttrigger:b24,leftx:a0,lefty:a4,rightshoulder:b20,rightstick:b44,righttrigger:b28,rightx:a8,righty:a12,start:b36,x:b0,y:b12,platform:Mac OS X,
030000005e0400002800000002010000,Microsoft Dual Strike,a:b3,b:b2,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b8,rightshoulder:b7,rightx:a0,righty:a1~,start:b5,x:b1,y:b0,platform:Mac OS X,
+030000005e0400000300000006010000,Microsoft SideWinder,a:b0,b:b1,back:b9,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b2,start:b8,x:b3,y:b4,platform:Mac OS X,
+030000005e0400000700000006010000,Microsoft SideWinder,a:b0,b:b1,back:b8,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b2,start:b9,x:b3,y:b4,platform:Mac OS X,
030000005e0400002700000001010000,Microsoft SideWinder Plug and Play,a:b0,b:b1,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,lefttrigger:b4,righttrigger:b5,x:b2,y:b3,platform:Mac OS X,
03000000d62000007162000001000000,Moga Pro 2,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Mac OS X,
03000000c62400002a89000000010000,MOGA XP5A Plus,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b21,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,
@@ -896,8 +934,6 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
03000000632500007505000000020000,NeoGeo mini PAD Controller,a:b1,b:b0,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,start:b9,x:b2,y:b3,platform:Mac OS X,
03000000921200004b46000003020000,NES 2-port Adapter,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,start:b11,platform:Mac OS X,
030000001008000001e5000006010000,NEXT SNES Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,righttrigger:b6,start:b9,x:b3,y:b0,platform:Mac OS X,
-03000000d620000011a7000000020000,Nintendo Switch Core Plus Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,
-03000000d620000011a7000010050000,Nintendo Switch PowerA Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,
030000007e0500000920000000000000,Nintendo Switch Pro Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Mac OS X,
030000007e0500000920000001000000,Nintendo Switch Pro Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Mac OS X,
050000007e05000009200000ff070000,Nintendo Switch Pro Controller,a:b1,b:b0,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b9,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b10,x:b3,y:b2,platform:Mac OS X,
@@ -910,6 +946,8 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
03000000666600006706000088020000,PlayStation Adapter,a:b2,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,rightx:a2,righty:a3,start:b11,x:b3,y:b0,platform:Mac OS X,
030000004c050000da0c000000010000,PlayStation Classic Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,lefttrigger:b4,rightshoulder:b7,righttrigger:b5,start:b9,x:b3,y:b0,platform:Mac OS X,
030000004c0500003713000000010000,PlayStation Vita,a:b1,b:b2,back:b8,dpdown:b13,dpleft:b15,dpright:b14,dpup:b12,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,rightx:a3,righty:a4,start:b9,x:b0,y:b3,platform:Mac OS X,
+03000000d620000011a7000000020000,PowerA Core Plus Gamecube Controller,a:b1,b:b0,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Mac OS X,
+03000000d620000011a7000010050000,PowerA Nintendo Switch Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,
03000000d62000006dca000000010000,PowerA Pro Ex,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,
03000000100800000300000006010000,PS2 Adapter,a:b2,b:b1,back:b8,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a4,righty:a3,start:b9,x:b3,y:b0,platform:Mac OS X,
030000004c0500006802000000000000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Mac OS X,
@@ -946,8 +984,8 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
030000006b140000010d000000010000,Revolution Pro Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,
030000006b140000130d000000010000,Revolution Pro Controller 3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,
030000004c0500006802000002100000,Rii RK707,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b2,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b3,righttrigger:b9,rightx:a2,righty:a3,start:b1,x:b15,y:b12,platform:Mac OS X,
+030000006f0e00008701000005010000,Rock Candy Nintendo Switch Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,
03000000c6240000fefa000000000000,Rock Candy PS3,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,
-030000006f0e00008701000005010000,Rock Candy Switch Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,
03000000e804000000a000001b010000,Samsung EIGP20,a:b1,b:b3,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b20,leftshoulder:b11,leftx:a1,lefty:a3,rightshoulder:b12,rightx:a4,righty:a5,start:b16,x:b7,y:b9,platform:Mac OS X,
03000000730700000401000000010000,Sanwa PlayOnline Mobile,a:b0,b:b1,back:b2,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,start:b3,platform:Mac OS X,
03000000a30c00002500000006020000,Sega Genesis Mini 3B Controller,a:b2,b:b1,dpdown:+a4,dpleft:-a3,dpright:+a3,dpup:-a4,righttrigger:b5,start:b9,platform:Mac OS X,
@@ -971,6 +1009,8 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
030000000d0f0000f600000000010000,Switch Hori Pad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Mac OS X,
03000000457500002211000000010000,SZMY Power PC Gamepad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,
03000000790000001c18000003100000,TGZ Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,
+03000000591c00002400000021000000,THEC64 Joystick,a:b0,b:b1,back:b6,leftshoulder:b4,leftx:a0,lefty:a4,rightshoulder:b5,start:b7,x:b2,y:b3,platform:Mac OS X,
+03000000591c00002600000021000000,THEGamepad,a:b2,b:b1,back:b6,dpdown:+a4,dpleft:-a0,dpright:+a0,dpup:-a4,leftshoulder:b4,rightshoulder:b5,start:b7,x:b3,y:b0,platform:Mac OS X,
030000004f04000015b3000000000000,Thrustmaster Dual Analog 3.2,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b3,platform:Mac OS X,
030000004f0400000ed0000000020000,ThrustMaster eSwap Pro Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,
030000004f04000000b3000000000000,Thrustmaster Firestorm Dual Power,a:b0,b:b2,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b11,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b7,rightx:a2,righty:a3,start:b10,x:b1,y:b3,platform:Mac OS X,
@@ -1026,9 +1066,13 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
03000000c82d00000151000000010000,8BitDo M30,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b7,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
03000000c82d00000650000011010000,8BitDo M30,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,start:b11,x:b3,y:b4,platform:Linux,
05000000c82d00005106000000010000,8BitDo M30,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b8,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b7,start:b11,x:b3,y:b4,platform:Linux,
+03000000c82d00002090000011010000,8BitDo Micro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,
+05000000c82d00002090000000010000,8BitDo Micro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,
03000000c82d00000451000000010000,8BitDo N30,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftx:a0,lefty:a1,rightx:a2,righty:a3,start:b11,platform:Linux,
03000000c82d00001590000011010000,8BitDo N30 Pro 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,
05000000c82d00006528000000010000,8BitDo N30 Pro 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,
+05000000c82d00006928000000010000,8BitDo N64,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a2,righty:a3,start:b11,platform:Linux,
+05000000c82d00002590000001000000,8BitDo NEOGEO,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
03000000008000000210000011010000,8BitDo NES30,a:b1,b:b2,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux,
03000000c82d00000310000011010000,8BitDo NES30,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b7,lefttrigger:b6,rightshoulder:b9,righttrigger:b8,start:b11,x:b3,y:b4,platform:Linux,
05000000c82d00008010000000010000,8BitDo NES30,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b7,lefttrigger:b6,rightshoulder:b9,righttrigger:b8,start:b11,x:b3,y:b4,platform:Linux,
@@ -1039,11 +1083,10 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
03000000c82d00000751000000010000,8BitDo P30,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:a8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
05000000c82d00000851000000010000,8BitDo P30,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:a8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
03000000c82d00000660000011010000,8BitDo Pro 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,
+03000000c82d00001030000011010000,8BitDo Pro 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,
05000000c82d00000660000000010000,8BitDo Pro 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,
-03000000c82d00000631000014010000,8BitDo Pro 2 Wired Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
-03000000c82d00001030000011010000,8BitDo Pro 2 Wired Controller,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,
-03000000c82d00000020000000000000,8BitDo Pro 2 Wired Controller for Xbox,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
-06000000c82d00000020000006010000,8BitDo Pro 2 Wired Controller for Xbox,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
+03000000c82d00000020000000000000,8BitDo Pro 2 for Xbox,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
+06000000c82d00000020000006010000,8BitDo Pro 2 for Xbox,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
03000000c82d00000131000011010000,8BitDo Receiver,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,
03000000c82d00000231000011010000,8BitDo Receiver,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,
03000000c82d00000331000011010000,8BitDo Receiver,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,
@@ -1063,21 +1106,27 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
03000000c82d00001290000011010000,8BitDo SN30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Linux,
05000000c82d00000161000000010000,8BitDo SN30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,
05000000c82d00006228000000010000,8BitDo SN30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,
-03000000c82d00000121000011010000,8BitDo Xbox One SN30 Pro,a:b0,b:b1,x:b3,y:b4,back:b10,guide:b12,start:b11,leftstick:b13,rightstick:b14,leftshoulder:b6,rightshoulder:b7,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a5,righttrigger:a4,platform:Linux,
-05000000c82d00000121000000010000,8BitDo Xbox One SN30 Pro,a:b0,b:b1,x:b3,y:b4,back:b10,guide:b12,start:b11,leftstick:b13,rightstick:b14,leftshoulder:b6,rightshoulder:b7,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a5,righttrigger:a4,platform:Linux,
03000000c82d00000260000011010000,8BitDo SN30 Pro Plus,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,
05000000c82d00000261000000010000,8BitDo SN30 Pro Plus,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,
05000000202800000900000000010000,8BitDo SNES30,a:b1,b:b0,back:b10,dpdown:b122,dpleft:b119,dpright:b120,dpup:b117,leftshoulder:b6,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Linux,
+05000000c82d00001230000000010000,8BitDo Ultimate,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
+03000000c82d00001530000011010000,8BitDo Ultimate C,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
+03000000c82d00001630000011010000,8BitDo Ultimate C,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
+03000000c82d00001730000011010000,8BitDo Ultimate C,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
03000000c82d00001130000011010000,8BitDo Ultimate Wired,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b26,paddle1:b24,paddle2:b25,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
03000000c82d00000760000011010000,8BitDo Ultimate Wireless,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,
03000000c82d00001230000011010000,8BitDo Ultimate Wireless,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
03000000c82d00001330000011010000,8BitDo Ultimate Wireless,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b26,paddle1:b23,paddle2:b19,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
+03000000c82d00000631000014010000,8BitDo Ultimate Wireless Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
+03000000c82d00000121000011010000,8BitDo Xbox One SN30 Pro,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
+05000000c82d00000121000000010000,8BitDo Xbox One SN30 Pro,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
05000000a00500003232000001000000,8BitDo Zero,a:b0,b:b1,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Linux,
05000000a00500003232000008010000,8BitDo Zero,a:b0,b:b1,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Linux,
03000000c82d00001890000011010000,8BitDo Zero 2,a:b1,b:b0,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Linux,
050000005e040000e002000030110000,8BitDo Zero 2,a:b0,b:b1,back:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b7,x:b2,y:b3,platform:Linux,
05000000c82d00003032000000010000,8BitDo Zero 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,
03000000c01100000355000011010000,Acrux Gamepad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,
+030000006f0e00008801000011010000,Afterglow Deluxe Nintendo Switch Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,
030000006f0e00003901000000430000,Afterglow Prismatic Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
030000006f0e00003901000013020000,Afterglow Prismatic Controller 048-007-NA,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
030000006f0e00001302000000010000,Afterglow Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
@@ -1129,14 +1178,20 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
030000006e0500000720000010010000,Elecom W01U,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b1,platform:Linux,
030000007d0400000640000010010000,Eliminator AfterShock,a:b1,b:b2,back:b9,dpdown:+a3,dpleft:-a5,dpright:+a5,dpup:-a3,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a4,righty:a2,start:b8,x:b0,y:b3,platform:Linux,
03000000430b00000300000000010000,EMS Production PS2 Adapter,a:b2,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a5,righty:a2,start:b9,x:b3,y:b0,platform:Linux,
-03000000b40400001124000011010000,Flydigi Vader 2,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b12,lefttrigger:a5,leftx:a0,lefty:a1,paddle1:b2,paddle2:b5,paddle4:b17,rightshoulder:b7,rightstick:b13,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
-05000000151900004000000001000000,Flydigi Vader 2,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,
+030000006f0e00008401000011010000,Faceoff Deluxe Nintendo Switch Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,
+030000006f0e00008101000011010000,Faceoff Deluxe Pro Nintendo Switch Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,
+030000006f0e00008001000011010000,Faceoff Pro Nintendo Switch Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,
+03005036852100000201000010010000,Final Fantasy XIV Online Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,
+03000000b40400001124000011010000,Flydigi Vader 2,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b12,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b14,paddle1:b2,paddle2:b5,paddle3:b16,paddle4:b17,rightshoulder:b7,rightstick:b13,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
+03000000b40400001224000011010000,Flydigi Vader 2,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b12,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b2,paddle1:b16,paddle2:b17,paddle3:b14,paddle4:b15,rightshoulder:b7,rightstick:b13,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
+05000000151900004000000001000000,Flydigi Vader 2,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b21,leftshoulder:b6,leftstick:b12,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b14,paddle1:b2,paddle2:b5,paddle3:b16,paddle4:b17,rightshoulder:b7,rightstick:b13,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
030000007e0500003703000000000000,GameCube Adapter,a:b0,b:b1,dpdown:b9,dpleft:b10,dpright:b11,dpup:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b3,y:b2,platform:Linux,
19000000030000000300000002030000,GameForce Controller,a:b1,b:b0,back:b8,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,guide:b16,leftshoulder:b4,leftstick:b14,lefttrigger:b6,leftx:a1,lefty:a0,rightshoulder:b5,rightstick:b15,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b2,y:b3,platform:Linux,
03000000ac0500005b05000010010000,GameSir G3w,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,
03000000bc2000000055000011010000,GameSir G3w,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
03000000558500001b06000010010000,GameSir G4 Pro,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
05000000ac0500002d0200001b010000,GameSir G4s,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b33,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
+03000000ac0500007a05000011010000,GameSir G5,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b16,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
03000000bc2000005656000011010000,GameSir T4w,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,
03000000ac0500001a06000011010000,GameSir-T3 2.02,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b15,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
0500000047532047616d657061640000,GameStop Gamepad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,
@@ -1148,6 +1203,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
190000004b4800000011000000010000,GO-Super Controller,a:b1,b:b0,back:b12,dpdown:b9,dpleft:b10,dpright:b11,dpup:b8,guide:b16,leftshoulder:b4,leftstick:b14,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b15,righttrigger:b7,rightx:a2,righty:a3,start:b13,x:b2,y:b3,platform:Linux,
03000000f0250000c183000010010000,Goodbetterbest Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,
0300000079000000d418000000010000,GPD Win 2 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
+030000005e0400008e02000001010000,GPD Win Max 2 (6800U) Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
030000007d0400000540000000010000,Gravis Eliminator Pro,a:b1,b:b2,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux,
03000000280400000140000000010000,Gravis GamePad Pro,a:b1,b:b2,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux,
030000008f0e00000610000000010000,GreenAsia Electronics Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b9,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b10,righttrigger:b5,rightx:a3,righty:a2,start:b11,x:b3,y:b0,platform:Linux,
@@ -1175,25 +1231,26 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
03000000ad1b000001f5000033050000,Hori Pad EX Turbo 2,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
030000000d0f00003801000011010000,Hori PC Engine Mini Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,start:b9,platform:Linux,
030000000d0f00009200000011010000,Hori Pokken Tournament DX Pro,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux,
-030000000d0f0000aa00000011010000,Hori Real Arcade Pro,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,
030000000d0f00001100000011010000,Hori Real Arcade Pro 3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,
030000000d0f00002200000011010000,Hori Real Arcade Pro 3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux,
030000000d0f00006a00000011010000,Hori Real Arcade Pro 4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,
030000000d0f00006b00000011010000,Hori Real Arcade Pro 4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,
030000000d0f00001600000000010000,Hori Real Arcade Pro EXSE,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b2,y:b3,platform:Linux,
+030000000d0f0000aa00000011010000,Hori Real Arcade Pro for Nintendo Switch,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,
030000000d0f00008501000015010000,Hori Switch Split Pad Pro,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
030000000d0f00006e00000011010000,Horipad 4 PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,
030000000d0f00006600000011010000,Horipad 4 PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,
030000000d0f0000ee00000011010000,Horipad Mini 4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,
+030000000d0f0000c100000011010000,Horipad Nintendo Switch Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,
030000000d0f00006700000001010000,Horipad One,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
-030000000d0f0000c100000011010000,Horipad S,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b13,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,
050000000d0f0000f600000001000000,Horipad Switch Pro Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,
03000000341a000005f7000010010000,HuiJia GameCube Controller Adapter,a:b1,b:b2,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b0,y:b3,platform:Linux,
05000000242e00000b20000001000000,Hyperkin Admiral N64 Controller,+rightx:b11,+righty:b13,-rightx:b8,-righty:b12,a:b1,b:b0,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b14,leftx:a0,lefty:a1,rightshoulder:b5,start:b9,platform:Linux,
03000000242e0000ff0b000011010000,Hyperkin N64 Adapter,a:b1,b:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightx:a2,righty:a3,start:b9,platform:Linux,
+03000000242e00006a38000010010000,Hyperkin Trooper 2,a:b0,b:b1,back:b4,leftshoulder:b2,leftx:a0,lefty:a1,rightshoulder:b3,start:b5,platform:Linux,
03000000242e00008816000001010000,Hyperkin X91,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
03000000f00300008d03000011010000,HyperX Clutch,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
-03000000830500006020000010010000,iBuffalo SNES Controller,a:b1,b:b0,back:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b7,x:b3,y:b2,platform:Linux,
+03000000830500006020000010010000,iBuffalo Super Famicom Controller,a:b1,b:b0,back:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b7,x:b3,y:b2,platform:Linux,
050000006964726f69643a636f6e0000,idroidcon Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,
03000000b50700001503000010010000,Impact,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Linux,
03000000d80400008200000003000000,IMS PCU0,a:b1,b:b0,back:b4,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,start:b5,x:b3,y:b2,platform:Linux,
@@ -1217,6 +1274,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
03000000242f00008a00000011010000,JYS Adapter,a:b1,b:b4,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b0,y:b3,platform:Linux,
030000006f0e00000103000000020000,Logic3 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
030000006d040000d1ca000000000000,Logitech Chillstream,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,
+030000006d040000d1ca000011010000,Logitech Chillstream,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,
030000006d04000019c2000010010000,Logitech Cordless RumblePad 2,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,
030000006d04000016c2000010010000,Logitech Dual Action,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,
030000006d04000016c2000011010000,Logitech Dual Action,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,
@@ -1255,10 +1313,12 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
03000000790000000318000011010000,Mayflash Wii DolphinBar,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b11,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b1,platform:Linux,
03000000790000000018000011010000,Mayflash Wii U Pro Adapter,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,
03000000b50700001203000010010000,Mega World Logic 3 Controller,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Linux,
+03000000b50700004f00000000010000,Mega World Logic 3 Controller,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b1,platform:Linux,
03000000780000000600000010010000,Microntek Joystick,a:b2,b:b1,back:b8,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,start:b9,x:b3,y:b0,platform:Linux,
030000005e0400002800000000010000,Microsoft Dual Strike,a:b3,b:b2,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b8,rightshoulder:b7,rightx:a0,righty:a1~,start:b5,x:b1,y:b0,platform:Linux,
-030000005e0400000e00000000010000,Microsoft SideWinder,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,rightshoulder:b7,start:b8,x:b3,y:b4,platform:Linux,
-030000005e0400000700000000010000,Microsoft SideWinder Gamepad,a:b0,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,lefttrigger:b7,rightshoulder:b5,righttrigger:b2,start:b9,x:b3,y:b4,platform:Linux,
+030000005e0400000300000000010000,Microsoft SideWinder,a:b0,b:b1,back:b9,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b2,start:b8,x:b3,y:b4,platform:Linux,
+030000005e0400000700000000010000,Microsoft SideWinder,a:b0,b:b1,back:b8,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b2,start:b9,x:b3,y:b4,platform:Linux,
+030000005e0400000e00000000010000,Microsoft SideWinder Freestyle Pro,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,rightshoulder:b7,start:b8,x:b3,y:b4,platform:Linux,
030000005e0400002700000000010000,Microsoft SideWinder Plug and Play,a:b0,b:b1,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,lefttrigger:b4,righttrigger:b5,x:b2,y:b3,platform:Linux,
030000005e0400008502000000010000,Microsoft Xbox,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b5,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b2,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b3,y:b4,platform:Linux,
030000005e0400008902000021010000,Microsoft Xbox,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b5,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b2,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b3,y:b4,platform:Linux,
@@ -1272,7 +1332,8 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
030000005e040000ea02000008040000,Microsoft Xbox One,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
060000005e040000120b000009050000,Microsoft Xbox One,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,misc1:b11,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
030000005e040000e302000003020000,Microsoft Xbox One Elite,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
-030000005e040000000b000008040000,Microsoft Xbox One Elite 2,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
+030000005e040000000b000007040000,Microsoft Xbox One Elite 2,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,paddle1:b12,paddle2:b14,paddle3:b13,paddle4:b15,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
+030000005e040000000b000008040000,Microsoft Xbox One Elite 2,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,paddle1:b12,paddle2:b14,paddle3:b13,paddle4:b15,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
050000005e040000050b000003090000,Microsoft Xbox One Elite 2,a:b0,b:b1,back:b17,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a6,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a5,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
030000005e040000120b00000b050000,Microsoft Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
03000000030000000300000002000000,Miroof,a:b1,b:b0,back:b6,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,start:b7,x:b3,y:b2,platform:Linux,
@@ -1304,8 +1365,6 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
060000007e0500000820000000000000,Nintendo Switch Combined Joy-Cons,a:b0,b:b1,back:b9,dpdown:b15,dpleft:b16,dpright:b17,dpup:b14,guide:b11,leftshoulder:b5,leftstick:b12,lefttrigger:b7,leftx:a0,lefty:a1,misc1:b4,rightshoulder:b6,rightstick:b13,righttrigger:b8,rightx:a2,righty:a3,start:b10,x:b3,y:b2,platform:Linux,
050000004c69632050726f20436f6e00,Nintendo Switch Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,
050000007e0500000620000001800000,Nintendo Switch Left Joy-Con,a:b16,b:b15,back:b4,leftshoulder:b6,leftstick:b12,leftx:a1,lefty:a0~,rightshoulder:b8,start:b9,x:b14,y:b17,platform:Linux,
-03000000d620000013a7000011010000,Nintendo Switch PowerA Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,
-03000000d620000011a7000011010000,Nintendo Switch PowerA Core Plus Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,
030000007e0500000920000000026803,Nintendo Switch Pro Controller,a:b1,b:b0,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Linux,
030000007e0500000920000011810000,Nintendo Switch Pro Controller,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b11,leftshoulder:b5,leftstick:b12,lefttrigger:b7,leftx:a0,lefty:a1,misc1:b4,rightshoulder:b6,rightstick:b13,righttrigger:b8,rightx:a2,righty:a3,start:b10,x:b3,y:b2,platform:Linux,
050000007e0500000920000001000000,Nintendo Switch Pro Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,
@@ -1314,8 +1373,12 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
05000000010000000100000003000000,Nintendo Wii Remote,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,
050000007e0500003003000001000000,Nintendo Wii U Pro Controller,a:b0,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b16,dpup:b13,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b2,platform:Linux,
030000000d0500000308000010010000,Nostromo n45 Dual Analog,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b9,leftshoulder:b4,leftstick:b12,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b10,x:b2,y:b3,platform:Linux,
+030000007e0500001920000011810000,NSO N64 Controller,+rightx:b10,+righty:b8,-rightx:b9,-righty:b7,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b11,leftshoulder:b3,lefttrigger:b2,leftx:a0,lefty:a1,misc1:b12,rightshoulder:b4,righttrigger:b5,start:b6,platform:Linux,
050000007e0500001920000001000000,NSO N64 Controller,+rightx:b8,+righty:b7,-rightx:b3,-righty:b2,a:b1,b:b0,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,righttrigger:b10,start:b9,platform:Linux,
-050000007e0500001720000001000000,NSO SNES Controller,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b11,leftshoulder:b5,leftstick:b12,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b13,righttrigger:b8,rightx:a2,righty:a3,start:b10,x:b3,y:b2,platform:Linux,
+050000007e0500001920000001800000,NSO N64 Controller,+rightx:b10,+righty:b8,-rightx:b9,-righty:b7,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b11,leftshoulder:b3,lefttrigger:b2,leftx:a0,lefty:a1,misc1:b12,rightshoulder:b4,righttrigger:b5,start:b6,platform:Linux,
+030000007e0500001720000011810000,NSO SNES Controller,a:b1,b:b0,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b3,y:b2,platform:Linux,
+050000007e0500001720000001000000,NSO SNES Controller,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b5,lefttrigger:b7,rightshoulder:b6,righttrigger:b8,start:b10,x:b3,y:b2,platform:Linux,
+050000007e0500001720000001800000,NSO SNES Controller,a:b1,b:b0,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b3,y:b2,platform:Linux,
03000000550900001072000011010000,NVIDIA Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b13,leftshoulder:b4,leftstick:b8,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Linux,
03000000550900001472000011010000,NVIDIA Controller v01.04,a:b0,b:b1,back:b14,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b16,leftshoulder:b4,leftstick:b7,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a5,start:b6,x:b2,y:b3,platform:Linux,
05000000550900001472000001000000,NVIDIA Controller v01.04,a:b0,b:b1,back:b14,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b16,leftshoulder:b4,leftstick:b7,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a5,start:b6,x:b2,y:b3,platform:Linux,
@@ -1333,24 +1396,30 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
030000006f0e00006401000001010000,PDP Battlefield One,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
030000006f0e0000d702000006640000,PDP Black Camo Wired Xbox Series X Controller,a:b0,b:b1,back:b6,dpdown:b13,dpleft:b14,dpright:b13,dpup:b14,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
030000006f0e00003101000000010000,PDP EA Sports Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
-030000006f0e00008001000011010000,PDP Faceoff Nintendo Switch Pro Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,
+030000006f0e00008501000011010000,PDP Fightpad Pro Gamecube Controller,a:b1,b:b0,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,
030000006f0e0000c802000012010000,PDP Kingdom Hearts Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
-030000006f0e00008501000011010000,PDP Nintendo Switch Fightpad Pro,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,
030000006f0e00002801000011010000,PDP PS3 Rock Candy Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,
030000006f0e00000901000011010000,PDP PS3 Versus Fighting,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux,
03000000ad1b000004f9000000010000,PDP Xbox 360 Versus Fighting,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,lefttrigger:a2,rightshoulder:b5,righttrigger:a5,start:b7,x:b2,y:b3,platform:Linux,
030000006f0e0000a802000023020000,PDP Xbox One Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,
030000006f0e0000a702000023020000,PDP Xbox One Raven Black,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
030000006f0e0000d802000006640000,PDP Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
+030000006f0e0000ef02000007640000,PDP Xbox Series Kinetic Wired Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
03000000666600006706000000010000,PlayStation Adapter,a:b2,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,leftshoulder:b6,leftstick:b9,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b10,righttrigger:b5,rightx:a2,righty:a3,start:b11,x:b3,y:b0,platform:Linux,
030000004c050000da0c000011010000,PlayStation Controller,a:b2,b:b1,back:b8,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,start:b9,x:b3,y:b0,platform:Linux,
03000000d9040000160f000000010000,PlayStation Controller Adapter,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Linux,
030000004c0500003713000011010000,PlayStation Vita,a:b1,b:b2,back:b8,dpdown:b13,dpleft:b15,dpright:b14,dpup:b12,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,rightx:a3,righty:a4,start:b9,x:b0,y:b3,platform:Linux,
03000000c62400000053000000010000,PowerA,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
03000000c62400003a54000001010000,PowerA 1428124-01,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
+03000000d620000011a7000011010000,PowerA Core Plus Gamecube Controller,a:b1,b:b0,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,
+03000000dd62000015a7000011010000,PowerA Fusion Nintendo Switch Arcade Stick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,
+03000000d620000012a7000011010000,PowerA Fusion Nintendo Switch Fight Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,
03000000d62000000140000001010000,PowerA Fusion Pro 2 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
+03000000dd62000016a7000000000000,PowerA Fusion Pro Nintendo Switch Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,
03000000c62400001a53000000010000,PowerA Mini Pro Ex,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
+03000000d620000013a7000011010000,PowerA Nintendo Switch Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,
03000000d62000006dca000011010000,PowerA Pro Ex,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,
+03000000d620000014a7000011010000,PowerA Spectra Nintendo Switch Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,
03000000c62400001a58000001010000,PowerA Xbox One,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
03000000d62000000220000001010000,PowerA Xbox One Controller,a:b0,b:b1,back:b7,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b6,x:b2,y:b3,platform:Linux,
03000000d62000000228000001010000,PowerA Xbox One Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
@@ -1404,6 +1473,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
030000009b2800004200000001010000,Raphnet Dual NES Adapter,a:b0,b:b1,back:b2,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,start:b3,platform:Linux,
030000009b2800003200000001010000,Raphnet GC and N64 Adapter,a:b0,b:b7,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:b5,rightx:a3,righty:a4,start:b3,x:b1,y:b8,platform:Linux,
030000009b2800006000000001010000,Raphnet GC and N64 Adapter,a:b0,b:b7,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:b5,rightx:a3,righty:a4,start:b3,x:b1,y:b8,platform:Linux,
+03000000f8270000bf0b000011010000,Razer Kishi,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
030000008916000001fd000024010000,Razer Onza Classic Edition,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b11,dpright:b12,dpup:b13,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
03000000321500000204000011010000,Razer Panthera PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,
03000000321500000104000011010000,Razer Panthera PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,
@@ -1418,6 +1488,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
03000000321500000009000011010000,Razer Serval,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Linux,
050000003215000000090000163a0000,Razer Serval,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Linux,
0300000032150000030a000001010000,Razer Wildcat,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
+03000000321500000b10000011010000,Razer Wolverine PS5 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux,
03000000790000001100000010010000,Retro Controller,a:b1,b:b2,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,lefttrigger:b7,rightshoulder:b4,righttrigger:b5,start:b9,x:b0,y:b3,platform:Linux,
190000004b4800000111000000010000,RetroGame Joypad,a:b1,b:b0,back:b8,dpdown:b14,dpleft:b15,dpright:b16,dpup:b13,leftshoulder:b4,leftstick:b11,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,
0300000081170000990a000001010000,Retronic Adapter,a:b0,leftx:a0,lefty:a1,platform:Linux,
@@ -1427,8 +1498,8 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
030000006b140000010d000011010000,Revolution Pro Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,
030000006b140000130d000011010000,Revolution Pro Controller 3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,
030000006f0e00001f01000000010000,Rock Candy,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
+030000006f0e00008701000011010000,Rock Candy Nintendo Switch Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,
030000006f0e00001e01000011010000,Rock Candy PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,
-030000006f0e00008701000011010000,Rock Candy Switch Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b13,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,
03000000c6240000fefa000000010000,Rock Candy Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
030000006f0e00004601000001010000,Rock Candy Xbox One Controller,a:b0,b:b1,back:b6,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
03000000a306000023f6000011010000,Saitek Cyborg V1 PlayStation Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a4,start:b9,x:b0,y:b3,platform:Linux,
@@ -1487,6 +1558,8 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
030000008f0e00001431000010010000,SZMY Power PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,
03000000ba2200000701000001010000,Technology Innovation PS2 Adapter,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a5,righty:a2,start:b9,x:b3,y:b2,platform:Linux,
03000000790000001c18000011010000,TGZ Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
+03000000591c00002400000010010000,THEC64 Joystick,a:b0,b:b1,back:b6,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,start:b7,x:b2,y:b3,platform:Linux,
+03000000591c00002600000010010000,THEGamepad,a:b2,b:b1,back:b6,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,start:b7,x:b3,y:b0,platform:Linux,
030000004f04000015b3000001010000,Thrustmaster Dual Analog 3.2,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b3,platform:Linux,
030000004f04000015b3000010010000,Thrustmaster Dual Analog 4,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b3,platform:Linux,
030000004f04000020b3000010010000,Thrustmaster Dual Trigger,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b3,platform:Linux,
@@ -1512,6 +1585,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
03000000e00d00000300000003000000,TRBot Virtual Joypad,a:b11,b:b12,back:b15,dpdown:b6,dpleft:b3,dpright:b4,dpup:b5,leftshoulder:b17,leftstick:b21,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b22,righttrigger:a2,rightx:a3,righty:a4,start:b16,x:b13,y:b14,platform:Linux,
03000000f00600000300000003000000,TRBot Virtual Joypad,a:b11,b:b12,back:b15,dpdown:b6,dpleft:b3,dpright:b4,dpup:b5,leftshoulder:b17,leftstick:b21,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b22,righttrigger:a2,rightx:a3,righty:a4,start:b16,x:b13,y:b14,platform:Linux,
030000005f140000c501000010010000,Trust Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,
+06000000f51000000870000003010000,Turtle Beach Recon,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
03000000100800000100000010010000,Twin PS2 Adapter,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Linux,
03000000151900005678000010010000,Uniplay U6,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
03000000100800000300000010010000,USB Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Linux,
@@ -1528,6 +1602,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
030000005e0400009102000007010000,Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b11,dpright:b12,dpup:b13,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
030000005e040000a102000000010000,Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b11,dpright:b12,dpup:b13,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
030000005e040000a102000007010000,Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
+030000005e040000a102000030060000,Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
030000005e0400008e02000000010000,Xbox 360 EasySMX,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
030000005e040000a102000014010000,Xbox 360 Receiver,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b11,dpright:b12,dpup:b13,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
030000005e0400000202000000010000,Xbox Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b5,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b2,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b3,y:b4,platform:Linux,
@@ -1544,6 +1619,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
050000005e040000e302000002090000,Xbox One Elite,a:b0,b:b1,back:b136,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a6,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a5,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
050000005e040000220b000013050000,Xbox One Elite 2 Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
050000005e040000050b000002090000,Xbox One Elite Series 2,a:b0,b:b1,back:b136,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a6,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a5,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
+030000005e040000ea02000011050000,Xbox One S Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
060000005e040000ea0200000b050000,Xbox One S Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
060000005e040000ea0200000d050000,Xbox One S Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
030000005e040000120b000001050000,Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
@@ -1559,10 +1635,11 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
050000005e040000130b000009050000,Xbox Series Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b15,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
050000005e040000130b000011050000,Xbox Series Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b15,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
050000005e040000130b000013050000,Xbox Series Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b15,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
+050000005e040000130b000015050000,Xbox Series Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b15,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
060000005e040000120b000007050000,Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
060000005e040000120b00000b050000,Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
+060000005e040000120b00000f050000,Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,misc1:b11,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
050000005e040000200b000013050000,Xbox Wireless Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
-050000005e040000130b000015050000,Xbox Series Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b15,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
03000000450c00002043000010010000,XEOX SL6556 BK,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,
05000000172700004431000029010000,XiaoMi Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b20,leftshoulder:b6,leftstick:b13,lefttrigger:a7,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a6,rightx:a2,righty:a5,start:b11,x:b3,y:b4,platform:Linux,
03000000c0160000e105000001010000,XinMo Dual Arcade,a:b4,b:b3,back:b6,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b9,leftshoulder:b2,leftx:a0,lefty:a1,rightshoulder:b5,start:b7,x:b1,y:b0,platform:Linux,
@@ -1596,10 +1673,19 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
64653533313537373934323436343563,8BitDo M30,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:a4,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b9,righttrigger:b10,start:b6,x:b2,y:b3,platform:Android,
66356438346136366337386437653934,8BitDo M30,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b3,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,righttrigger:b10,start:b18,x:b19,y:b2,platform:Android,
66393064393162303732356665666366,8BitDo M30,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,righttrigger:a5,start:b6,x:b2,y:b3,platform:Android,
+38426974446f204d6963726f2067616d,8BitDo Micro,a:b1,b:b0,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b9,lefttrigger:a4,leftx:b0,lefty:b1,rightshoulder:b10,righttrigger:a5,rightx:b2,righty:b3,start:b6,x:b3,y:b2,platform:Android,
+61653365323561356263373333643266,8BitDo Micro,a:b1,b:b0,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b9,lefttrigger:a4,leftx:b0,lefty:b1,rightshoulder:b10,righttrigger:a5,rightx:b2,righty:b3,start:b6,x:b3,y:b2,platform:Android,
+62613137616239666338343866326336,8BitDo Micro,a:b1,b:b0,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b9,lefttrigger:a4,leftx:b0,lefty:b1,rightshoulder:b10,righttrigger:a5,rightx:b2,righty:b3,start:b6,x:b3,y:b2,platform:Android,
33663431326134333366393233616633,8BitDo N30,a:b1,b:b0,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftx:a0,lefty:a1,rightx:a2,righty:a3,start:b6,platform:Android,
38426974446f204e3330204d6f646b69,8BitDo N30,a:b1,b:b0,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftx:a0,lefty:a1,rightx:a2,righty:a3,start:b6,platform:Android,
05000000c82d000015900000ffff3f00,8BitDo N30 Pro 2,a:b1,b:b0,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android,
05000000c82d000065280000ffff3f00,8BitDo N30 Pro 2,a:b1,b:b0,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b17,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android,
+38323035343766666239373834336637,8BitDo N64,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b9,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,righttrigger:b18,rightx:a2,righty:a3,start:b6,platform:Android,
+38426974446f204e3634204d6f646b69,8BitDo N64,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b9,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,righttrigger:b18,rightx:a2,righty:a3,start:b6,platform:Android,
+32363135613966656338666638666237,8BitDo NEOGEO,a:b0,b:b1,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftx:a0,lefty:a1,rightshoulder:b10,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android,
+35363534633333373639386466346631,8BitDo NEOGEO,a:b0,b:b1,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftx:a0,lefty:a1,rightshoulder:b10,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android,
+38426974446f204e454f47454f204750,8BitDo NEOGEO,a:b0,b:b1,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftx:a0,lefty:a1,rightshoulder:b10,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android,
+39383963623932353561633733306334,8BitDo NEOGEO,a:b0,b:b1,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftx:a0,lefty:a1,rightshoulder:b10,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android,
050000000220000000900000ffff3f00,8BitDo NES30 Pro,a:b1,b:b0,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android,
050000002038000009000000ffff3f00,8BitDo NES30 Pro,a:b1,b:b0,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android,
38313433643131656262306631373166,8BitDo P30,a:b0,b:b1,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android,
@@ -1644,6 +1730,8 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
63633435623263373466343461646430,8BitDo Zero 2,a:b1,b:b0,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftx:a0,lefty:a1,rightshoulder:b10,start:b6,x:b2,y:b3,platform:Android,
32333634613735616163326165323731,Amazon Luna Controller,a:b0,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,x:b2,y:b3,platform:Android,
417374726f2063697479206d696e6920,Astro City Mini,a:b23,b:b22,back:b29,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,rightshoulder:b25,righttrigger:b26,start:b30,x:b24,y:b21,platform:Android,
+35643263313264386134376362363435,Atari VCS Classic Controller,a:b0,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,start:b6,platform:Android,
+32353831643566306563643065356239,Atari VCS Modern Controller,a:b0,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android,
32303165626138343962363666346165,Brook Mars PS4 Controller,a:b1,b:b19,back:b17,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b3,leftstick:b15,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b6,righttrigger:b10,rightx:a2,righty:a3,start:b18,x:b0,y:b2,platform:Android,
38383337343564366131323064613561,Brook Mars PS4 Controller,a:b1,b:b19,back:b17,leftshoulder:b3,leftstick:b15,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b6,righttrigger:b10,rightx:a2,righty:a3,start:b18,x:b0,y:b2,platform:Android,
34313430343161653665353737323365,Elecom JC-W01U,a:b23,b:b24,back:b29,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b25,lefttrigger:b27,leftx:a0,lefty:a1,rightshoulder:b26,righttrigger:b28,rightx:a2,righty:a3,start:b30,x:b21,y:b22,platform:Android,
@@ -1654,6 +1742,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
34323662653333636330306631326233,Google Nexus,a:b0,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android,
35383633353935396534393230616564,Google Stadia Controller,a:b0,b:b1,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android,
05000000d6020000e5890000dfff3f00,GPD XD Plus,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a4,rightx:a2,righty:a5,start:b6,x:b2,y:b3,platform:Android,
+05000000d6020000e5890000dfff3f80,GPD XD Plus,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a3,rightx:a4,righty:a5,start:b6,x:b2,y:b3,platform:Android,
66633030656131663837396562323935,Hori Battle,a:b0,b:b1,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a3,righty:a4,start:b6,x:b2,y:b3,platform:Android,
35623466343433653739346434636330,Hori Fighting Commander 3 Pro,a:b1,b:b19,back:b17,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b3,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,righttrigger:b10,rightx:a2,righty:a3,start:b18,x:b0,y:b2,platform:Android,
484f524920434f2e2c4c54442e203130,Hori Fighting Commander 3 Pro,a:b1,b:b19,back:b17,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b20,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b3,righttrigger:b9,rightx:a2,righty:a3,start:b18,x:b0,y:b2,platform:Android,
@@ -1664,9 +1753,10 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
30306539356238653637313730656134,HORIPAD Switch Pro Controller,a:b0,b:b1,back:b17,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b3,leftstick:b15,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b6,righttrigger:b10,rightx:a2,righty:a3,start:b18,x:b19,y:b2,platform:Android,
48797065726b696e2050616400000000,Hyperkin Admiral N64 Controller,+rightx:b6,+righty:b7,-rightx:b17,-righty:b5,a:b1,b:b0,leftshoulder:b3,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b20,start:b18,platform:Android,
62333331353131353034386136626636,Hyperkin Admiral N64 Controller,+rightx:b6,+righty:b7,-rightx:b17,-righty:b5,a:b1,b:b0,leftshoulder:b3,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b20,start:b18,platform:Android,
-31306635363562663834633739396333,Hyperkin N64 Adapter,a:b1,b:b19,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b3,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,rightx:a2,righty:a3,start:b18,platform:Android,
-5368616e57616e202020202048797065,Hyperkin N64 Adapter,a:b1,b:b19,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b3,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,rightx:a2,righty:a3,start:b18,platform:Android,
+31306635363562663834633739396333,Hyperkin N64 Adapter,a:b1,b:b19,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b3,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,rightx:a2,righty:a3,start:b18,platform:Android,
+5368616e57616e202020202048797065,Hyperkin N64 Adapter,a:b1,b:b19,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b3,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,rightx:a2,righty:a3,start:b18,platform:Android,
0500000083050000602000000ffe0000,iBuffalo SNES Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b15,rightshoulder:b16,start:b10,x:b2,y:b3,platform:Android,
+5553422c322d6178697320382d627574,iBuffalo Super Famicom Controller,a:b1,b:b0,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b17,rightshoulder:b18,start:b10,x:b3,y:b2,platform:Android,
64306137363261396266353433303531,InterAct GoPad,a:b24,b:b25,leftshoulder:b23,lefttrigger:b27,leftx:a0,lefty:a1,rightshoulder:b26,righttrigger:b28,x:b21,y:b22,platform:Android,
532e542e442e20496e74657261637420,InterAct HammerHead FX,a:b23,b:b24,back:b30,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b26,leftstick:b22,lefttrigger:b28,leftx:a0,lefty:a1,rightshoulder:b27,rightstick:b25,righttrigger:b29,rightx:a2,righty:a3,start:b31,x:b20,y:b21,platform:Android,
65346535636333663931613264643164,Joy-Con,a:b21,b:b22,back:b29,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b25,lefttrigger:b27,leftx:a0,lefty:a1,rightshoulder:b26,righttrigger:b28,rightx:a2,righty:a3,start:b30,x:b23,y:b24,platform:Android,
@@ -1682,7 +1772,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
35623364393661626231343866613337,Logitech F710,a:b0,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android,
4c6f6769746563682047616d65706164,Logitech F710,a:b0,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android,
64396331333230326333313330336533,Logitech F710,a:b0,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android,
-39653365373864633935383236363438,Logitech G Cloud,a:b0,b:b1,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4~,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5~,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android,
+39653365373864633935383236363438,Logitech G Cloud,a:b0,b:b1,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android,
416d617a6f6e2047616d6520436f6e74,Luna Controller,a:b0,b:b1,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a3,righty:a4,start:b6,x:b2,y:b3,platform:Android,
4c756e612047616d6570616400000000,Luna Controller,a:b0,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android,
30363066623539323534363639323363,Magic NS,a:b1,b:b19,back:b17,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b3,leftstick:b15,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b6,righttrigger:b10,rightx:a2,righty:a3,start:b18,x:b0,y:b2,platform:Android,
@@ -1716,6 +1806,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
050000005509000003720000cf7f3f00,NVIDIA Controller,a:b0,b:b1,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android,
050000005509000010720000ffff3f00,NVIDIA Controller,a:b0,b:b1,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android,
050000005509000014720000df7f3f00,NVIDIA Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a4,rightx:a2,righty:a5,start:b6,x:b2,y:b3,platform:Android,
+050000005509000014720000df7f3f80,NVIDIA Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a3,rightx:a4,righty:a5,start:b6,x:b2,y:b3,platform:Android,
37336435666338653565313731303834,NVIDIA Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android,
4e564944494120436f72706f72617469,NVIDIA Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android,
61363931656135336130663561616264,NVIDIA Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android,
@@ -1734,6 +1825,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
66383132326164626636313737373037,PS3 Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android,
050000004c050000c405000000783f00,PS4 Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android,
050000004c050000c4050000fffe3f00,PS4 Controller,a:b1,b:b17,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b3,leftstick:b4,lefttrigger:+a3,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b6,righttrigger:+a4,rightx:a2,righty:a5,start:b16,x:b0,y:b2,platform:Android,
+050000004c050000c4050000fffe3f80,PS4 Controller,a:b1,b:b17,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b3,leftstick:b4,lefttrigger:+a2,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b6,righttrigger:+a3,rightx:a4,righty:a5,start:b16,x:b0,y:b2,platform:Android,
050000004c050000c4050000ffff3f00,PS4 Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android,
050000004c050000cc090000fffe3f00,PS4 Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android,
050000004c050000cc090000ffff3f00,PS4 Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android,
@@ -1751,6 +1843,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
65366465656364636137653363376531,PS4 Controller,a:b1,b:b19,back:b17,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b3,leftstick:b15,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b6,righttrigger:b10,rightx:a2,righty:a3,start:b18,x:b0,y:b2,platform:Android,
66613532303965383534396638613230,PS4 Controller,a:b1,b:b19,back:b17,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b3,leftstick:b15,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b6,righttrigger:b10,rightx:a2,righty:a5,start:b18,x:b0,y:b2,platform:Android,
050000004c050000e60c0000fffe3f00,PS5 Controller,a:b1,b:b17,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b3,leftstick:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b6,righttrigger:a4,rightx:a2,righty:a5,start:b16,x:b0,y:b2,platform:Android,
+050000004c050000e60c0000fffe3f80,PS5 Controller,a:b1,b:b17,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b3,leftstick:b4,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b6,righttrigger:a3,rightx:a4,righty:a5,start:b16,x:b0,y:b2,platform:Android,
050000004c050000e60c0000ffff3f00,PS5 Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android,
32346465346533616263386539323932,PS5 Controller,a:b0,b:b1,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android,
32633532643734376632656664383733,PS5 Controller,a:b1,b:b19,back:b17,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b3,leftstick:b15,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b6,righttrigger:b10,rightx:a2,righty:a5,start:b18,x:b0,y:b2,platform:Android,
@@ -1769,6 +1862,8 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
526574726f466c616720576972656420,Retro Controller,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b17,rightshoulder:b18,start:b10,x:b2,y:b3,platform:Android,
61343739353764363165343237303336,Retro Controller,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b17,lefttrigger:b18,leftx:a0,lefty:a1,start:b10,x:b2,y:b3,platform:Android,
38653130373365613538333235303036,Retroid Pocket 2,a:b0,b:b1,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android,
+526574726f696420506f636b65742043,Retroid Pocket Flip,a:b1,b:b0,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,paddle1:b17,paddle2:b18,rightshoulder:b10,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android,
+582d426f7820436f6e74726f6c6c6572,Retroid Pocket Flip,a:b0,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,paddle1:b17,paddle2:b18,rightshoulder:b10,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android,
64363363336633363736393038313463,Retrolink,a:b1,b:b0,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,start:b6,platform:Android,
37393234373533633333323633646531,RetroUSB N64 RetroPort,+rightx:b17,+righty:b15,-rightx:b18,-righty:b6,a:b10,b:b9,dpdown:b19,dpleft:b1,dpright:b0,dpup:b2,leftshoulder:b7,lefttrigger:b20,leftx:a0,lefty:a1,rightshoulder:b5,start:b3,platform:Android,
5365616c6965436f6d707574696e6720,RetroUSB N64 RetroPort,+rightx:b17,+righty:b15,-rightx:b18,-righty:b6,a:b10,b:b9,dpdown:b19,dpleft:b1,dpright:b0,dpup:b2,leftshoulder:b7,lefttrigger:b20,leftx:a0,lefty:a1,rightshoulder:b5,start:b3,platform:Android,
@@ -1790,7 +1885,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
5346432f555342205061640000000000,SNES Adapter,a:b0,b:b1,back:b9,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b3,rightshoulder:b20,start:b10,x:b19,y:b2,platform:Android,
5553422047616d657061642000000000,SNES Controller,a:b1,b:b0,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,rightshoulder:b10,start:b6,x:b3,y:b2,platform:Android,
63303964303462366136616266653561,Sony PSP,a:b21,b:b22,back:b27,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b25,leftx:a0,lefty:a1,rightshoulder:b26,start:b28,x:b23,y:b24,platform:Android,
-63376637643462343766333462383235,Sony Vita,a:b1,b:b19,back:b17,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftshoulder:b3,leftx:a0,lefty:a1,rightshoulder:b20,rightx:a3,righty:a4,start:b18,x:b0,y:b2,platform:Android,
+63376637643462343766333462383235,Sony Vita,a:b1,b:b19,back:b17,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b3,leftx:a0,lefty:a1,rightshoulder:b20,rightx:a3,righty:a4,start:b18,x:b0,y:b2,platform:Android,
476f6f676c65204c4c43205374616469,Stadia Controller,a:b0,b:b1,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android,
5374616469614e3848532d6532633400,Stadia Controller,a:b0,b:b1,back:b15,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android,
05000000de2800000511000001000000,Steam Controller,a:b0,b:b1,back:b6,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,platform:Android,
@@ -1799,6 +1894,8 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
35306436396437373135383665646464,SteelSeries Nimbus Plus,a:b0,b:b1,leftshoulder:b3,leftstick:b17,lefttrigger:b9,leftx:a0,rightshoulder:b20,rightstick:b18,righttrigger:b10,rightx:a2,x:b19,y:b2,platform:Android,
54475a20436f6e74726f6c6c65720000,TGZ Controller,a:b0,b:b1,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android,
62363434353532386238336663643836,TGZ Controller,a:b0,b:b1,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android,
+37323236633763666465316365313236,THEC64 Joystick,a:b21,b:b22,back:b27,leftshoulder:b25,leftx:a0,lefty:a1,rightshoulder:b26,start:b27,x:b23,y:b24,platform:Android,
+38346162326232346533316164363336,THEGamepad,a:b23,b:b22,back:b27,leftshoulder:b25,leftx:a0,lefty:a1,rightshoulder:b26,start:b28,x:b24,y:b21,platform:Android,
050000004f0400000ed00000fffe3f00,ThrustMaster eSwap Pro Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android,
5477696e20555342204a6f7973746963,Twin Joystick,a:b22,b:b21,back:b28,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b26,leftstick:b30,lefttrigger:b24,leftx:a0,lefty:a1,rightshoulder:b27,rightstick:b31,righttrigger:b25,rightx:a3,righty:a2,start:b29,x:b23,y:b20,platform:Android,
30623739343039643830333266346439,Valve Steam Controller,a:b0,b:b1,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b9,leftstick:b7,leftx:a0,lefty:a1,paddle1:b24,paddle2:b23,rightshoulder:b10,rightstick:b8,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android,
@@ -1815,8 +1912,11 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
4d6963726f736f667420582d426f7820,Xbox Controller,a:b0,b:b1,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android,
64633436313965656664373634323364,Xbox Controller,a:b0,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b19,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android,
050000005e04000091020000ff073f00,Xbox One Controller,a:b0,b:b1,back:b4,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a3,righty:a4,start:b6,x:b2,y:b3,platform:Android,
+050000005e04000091020000ff073f80,Xbox One Controller,a:b0,b:b1,back:b4,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android,
050000005e040000e00200000ffe3f00,Xbox One Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,leftstick:b15,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b16,righttrigger:a5,rightx:a3,righty:a4,start:b10,x:b17,y:b2,platform:Android,
+050000005e040000e00200000ffe3f80,Xbox One Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,leftstick:b15,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b16,righttrigger:a5,rightx:a2,righty:a3,start:b10,x:b17,y:b2,platform:Android,
050000005e040000e0020000ffff3f00,Xbox One Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b4,leftshoulder:b3,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b10,x:b17,y:b2,platform:Android,
+050000005e040000e0020000ffff3f80,Xbox One Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b4,leftshoulder:b3,leftstick:b8,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b7,righttrigger:a5,rightx:a2,righty:a3,start:b10,x:b17,y:b2,platform:Android,
050000005e040000fd020000ffff3f00,Xbox One Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android,
33356661323266333733373865656366,Xbox One Controller,a:b0,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android,
34356136633366613530316338376136,Xbox One Controller,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b3,leftstick:b15,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b16,righttrigger:a5,rightx:a3,righty:a4,x:b17,y:b2,platform:Android,
@@ -1824,10 +1924,13 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
36616131643361333337396261666433,Xbox One Controller,a:b0,b:b1,back:b15,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android,
58626f7820576972656c65737320436f,Xbox One Controller,a:b0,b:b1,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android,
050000005e040000000b000000783f00,Xbox One Elite 2 Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a3,righty:a4,start:b6,x:b2,y:b3,platform:Android,
+050000005e040000000b000000783f80,Xbox One Elite 2 Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android,
+050000005e040000050b0000ffff3f00,Xbox One Elite 2 Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a6,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android,
050000005e040000e002000000783f00,Xbox One S Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android,
050000005e040000ea02000000783f00,Xbox One S Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android,
050000005e040000fd020000ff7f3f00,Xbox One S Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android,
050000005e040000120b000000783f00,Xbox Series Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a3,righty:a4,start:b6,x:b2,y:b3,platform:Android,
+050000005e040000120b000000783f80,Xbox Series Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android,
050000005e040000130b0000ffff3f00,Xbox Series Controller,a:b0,b:b1,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android,
65633038363832353634653836396239,Xbox Series Controller,a:b0,b:b1,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android,
050000001727000044310000ffff3f00,XiaoMi Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a7,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a6,rightx:a2,righty:a5,start:b6,x:b2,y:b3,platform:Android,
@@ -1840,6 +1943,8 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
05000000ac05000001000000ff076d01,*,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b9,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b10,x:b2,y:b3,platform:iOS,
05000000ac0500000200000000006d02,*,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b6,leftshoulder:b4,rightshoulder:b5,x:b2,y:b3,platform:iOS,
05000000ac050000020000004f066d02,*,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b6,leftshoulder:b4,rightshoulder:b5,x:b2,y:b3,platform:iOS,
+050000008a35000003010000ff070000,Backbone One,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b10,x:b2,y:b3,platform:iOS,
+050000008a35000004010000ff070000,Backbone One,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b10,x:b2,y:b3,platform:iOS,
4d466947616d65706164010000000000,MFi Extended Gamepad,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a5,rightx:a3,righty:a4,start:b6,x:b2,y:b3,platform:iOS,
4d466947616d65706164020000000000,MFi Gamepad,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,rightshoulder:b5,start:b6,x:b2,y:b3,platform:iOS,
050000007e050000062000000f060000,Nintendo Switch Joy-Con (L),+leftx:h0.2,+lefty:h0.4,-leftx:h0.8,-lefty:h0.1,a:b0,b:b2,leftshoulder:b4,rightshoulder:b5,x:b1,y:b3,platform:iOS,
diff --git a/core/input/godotcontrollerdb.txt b/core/input/godotcontrollerdb.txt
index 7c51e20b4c..f5158bfabb 100644
--- a/core/input/godotcontrollerdb.txt
+++ b/core/input/godotcontrollerdb.txt
@@ -33,6 +33,3 @@ Linux20d6a713,Bensussen Deutsch & Associates Inc.(BDA) NSW Wired controller,a:b1
Linux054c05c4,Sony Computer Entertainment Wireless Controller,a:b0,b:b1,y:b2,x:b3,start:b9,back:b8,leftstick:b11,rightstick:b12,leftshoulder:b4,rightshoulder:b5,dpup:-a7,dpleft:-a6,dpdown:+a7,dpright:+a6,leftx:a0,lefty:a1,rightx:a3,righty:a4,lefttrigger:a2,righttrigger:a5,platform:Web
Linux18d19400,18d1-9400-Google LLC Stadia Controller rev. A,a:b0,b:b1,y:b3,x:b2,start:b7,back:b6,leftstick:b9,rightstick:b10,leftshoulder:b4,rightshoulder:b5,dpup:-a7,dpleft:-a6,dpdown:+a7,dpright:+a6,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a5,righttrigger:a4,platform:Web
Linux054c0268,054c-0268-Sony PLAYSTATION(R)3 Controller,a:b0,b:b1,y:b2,x:b3,start:b9,guide:b10,back:b8,leftstick:b11,rightstick:b12,leftshoulder:b4,rightshoulder:b5,dpup:b13,dpleft:b15,dpdown:b14,dpright:b16,leftx:a0,lefty:a1,rightx:a3,righty:a4,lefttrigger:a2,righttrigger:a5,platform:Web
-
-# UWP
-__UWP_GAMEPAD__,Xbox Controller,a:b2,b:b3,x:b4,y:b5,start:b0,back:b1,leftstick:b12,rightstick:b13,leftshoulder:b10,rightshoulder:b11,dpup:b6,dpdown:b7,dpleft:b8,dpright:b9,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a4,righttrigger:a5,platform:UWP,
diff --git a/core/input/input.cpp b/core/input/input.cpp
index 4a32abfafa..f3f5940118 100644
--- a/core/input/input.cpp
+++ b/core/input/input.cpp
@@ -285,7 +285,7 @@ bool Input::is_joy_button_pressed(int p_device, JoyButton p_button) const {
bool Input::is_action_pressed(const StringName &p_action, bool p_exact) const {
ERR_FAIL_COND_V_MSG(!InputMap::get_singleton()->has_action(p_action), false, InputMap::get_singleton()->suggest_actions(p_action));
- return action_state.has(p_action) && action_state[p_action].pressed && (p_exact ? action_state[p_action].exact : true);
+ return action_state.has(p_action) && action_state[p_action].pressed > 0 && (p_exact ? action_state[p_action].exact : true);
}
bool Input::is_action_just_pressed(const StringName &p_action, bool p_exact) const {
@@ -475,7 +475,8 @@ void Input::joy_connection_changed(int p_idx, bool p_connected, String p_name, S
}
joy_names[p_idx] = js;
- emit_signal(SNAME("joy_connection_changed"), p_idx, p_connected);
+ // Ensure this signal is emitted on the main thread, as some platforms (e.g. Linux) call this from a different thread.
+ call_deferred("emit_signal", SNAME("joy_connection_changed"), p_idx, p_connected);
}
Vector3 Input::get_gravity() const {
@@ -696,23 +697,50 @@ void Input::_parse_input_event_impl(const Ref<InputEvent> &p_event, bool p_is_em
for (const KeyValue<StringName, InputMap::Action> &E : InputMap::get_singleton()->get_action_map()) {
if (InputMap::get_singleton()->event_is_action(p_event, E.key)) {
Action &action = action_state[E.key];
- // If not echo and action pressed state has changed
- if (!p_event->is_echo() && is_action_pressed(E.key, false) != p_event->is_action_pressed(E.key)) {
+ bool is_joypad_axis = jm.is_valid();
+ bool is_pressed = false;
+ if (!p_event->is_echo()) {
if (p_event->is_action_pressed(E.key)) {
- action.pressed = true;
- action.pressed_physics_frame = Engine::get_singleton()->get_physics_frames();
- action.pressed_process_frame = Engine::get_singleton()->get_process_frames();
+ bool is_joypad_axis_valid_zone_enter = false;
+ if (is_joypad_axis) {
+ if (!action.axis_pressed) {
+ is_joypad_axis_valid_zone_enter = true;
+ action.pressed++;
+ action.axis_pressed = true;
+ }
+ } else {
+ action.pressed++;
+ }
+ if (action.pressed == 1 && (is_joypad_axis_valid_zone_enter || !is_joypad_axis)) {
+ action.pressed_physics_frame = Engine::get_singleton()->get_physics_frames();
+ action.pressed_process_frame = Engine::get_singleton()->get_process_frames();
+ }
+ is_pressed = true;
} else {
- action.pressed = false;
- action.released_physics_frame = Engine::get_singleton()->get_physics_frames();
- action.released_process_frame = Engine::get_singleton()->get_process_frames();
+ bool is_released = true;
+ if (is_joypad_axis) {
+ if (action.axis_pressed) {
+ action.axis_pressed = false;
+ } else {
+ is_released = false;
+ }
+ }
+
+ if (is_released) {
+ if (action.pressed == 1) {
+ action.released_physics_frame = Engine::get_singleton()->get_physics_frames();
+ action.released_process_frame = Engine::get_singleton()->get_process_frames();
+ }
+ action.pressed = MAX(action.pressed - 1, 0);
+ }
}
- action.strength = 0.0f;
- action.raw_strength = 0.0f;
action.exact = InputMap::get_singleton()->event_is_action(p_event, E.key, true);
}
- action.strength = p_event->get_action_strength(E.key);
- action.raw_strength = p_event->get_action_raw_strength(E.key);
+
+ if (is_pressed || action.pressed == 0) {
+ action.strength = p_event->get_action_strength(E.key);
+ action.raw_strength = p_event->get_action_raw_strength(E.key);
+ }
}
}
@@ -830,9 +858,11 @@ void Input::action_press(const StringName &p_action, float p_strength) {
// Create or retrieve existing action.
Action &action = action_state[p_action];
- action.pressed_physics_frame = Engine::get_singleton()->get_physics_frames();
- action.pressed_process_frame = Engine::get_singleton()->get_process_frames();
- action.pressed = true;
+ action.pressed++;
+ if (action.pressed == 1) {
+ action.pressed_physics_frame = Engine::get_singleton()->get_physics_frames();
+ action.pressed_process_frame = Engine::get_singleton()->get_process_frames();
+ }
action.strength = p_strength;
action.raw_strength = p_strength;
action.exact = true;
@@ -842,9 +872,11 @@ void Input::action_release(const StringName &p_action) {
// Create or retrieve existing action.
Action &action = action_state[p_action];
- action.released_physics_frame = Engine::get_singleton()->get_physics_frames();
- action.released_process_frame = Engine::get_singleton()->get_process_frames();
- action.pressed = false;
+ action.pressed--;
+ if (action.pressed == 0) {
+ action.released_physics_frame = Engine::get_singleton()->get_physics_frames();
+ action.released_process_frame = Engine::get_singleton()->get_process_frames();
+ }
action.strength = 0.0f;
action.raw_strength = 0.0f;
action.exact = true;
@@ -1005,8 +1037,10 @@ void Input::release_pressed_events() {
joy_buttons_pressed.clear();
_joy_axis.clear();
- for (const KeyValue<StringName, Input::Action> &E : action_state) {
- if (E.value.pressed) {
+ for (KeyValue<StringName, Input::Action> &E : action_state) {
+ if (E.value.pressed > 0) {
+ // Make sure the action is really released.
+ E.value.pressed = 1;
action_release(E.key);
}
}
diff --git a/core/input/input.h b/core/input/input.h
index c63a4e52e3..8ce5f64a6a 100644
--- a/core/input/input.h
+++ b/core/input/input.h
@@ -103,7 +103,8 @@ private:
uint64_t pressed_process_frame = UINT64_MAX;
uint64_t released_physics_frame = UINT64_MAX;
uint64_t released_process_frame = UINT64_MAX;
- bool pressed = false;
+ int pressed = 0;
+ bool axis_pressed = false;
bool exact = true;
float strength = 0.0f;
float raw_strength = 0.0f;
diff --git a/core/input/input_builders.py b/core/input/input_builders.py
index 76e199e0ff..e98e2441e2 100644
--- a/core/input/input_builders.py
+++ b/core/input/input_builders.py
@@ -51,7 +51,6 @@ def make_default_controller_mappings(target, source, env):
"Android": "#if defined(__ANDROID__)",
"iOS": "#ifdef IOS_ENABLED",
"Web": "#ifdef WEB_ENABLED",
- "UWP": "#ifdef UWP_ENABLED",
}
g.write("const char* DefaultControllerMappings::mappings[] = {\n")
diff --git a/core/input/input_event.cpp b/core/input/input_event.cpp
index e37886cbe9..e99dd04599 100644
--- a/core/input/input_event.cpp
+++ b/core/input/input_event.cpp
@@ -1722,7 +1722,27 @@ String InputEventMIDI::as_text() const {
}
String InputEventMIDI::to_string() {
- return vformat("InputEventMIDI: channel=%d, message=%d, pitch=%d, velocity=%d, pressure=%d, controller_number=%d, controller_value=%d", channel, message, pitch, velocity, pressure, controller_number, controller_value);
+ String ret;
+ switch (message) {
+ case MIDIMessage::NOTE_ON:
+ ret = vformat("Note On: channel=%d, pitch=%d, velocity=%d", channel, pitch, velocity);
+ break;
+ case MIDIMessage::NOTE_OFF:
+ ret = vformat("Note Off: channel=%d, pitch=%d, velocity=%d", channel, pitch, velocity);
+ break;
+ case MIDIMessage::PITCH_BEND:
+ ret = vformat("Pitch Bend: channel=%d, pitch=%d", channel, pitch);
+ break;
+ case MIDIMessage::CHANNEL_PRESSURE:
+ ret = vformat("Channel Pressure: channel=%d, pressure=%d", channel, pressure);
+ break;
+ case MIDIMessage::CONTROL_CHANGE:
+ ret = vformat("Control Change: channel=%d, controller_number=%d, controller_value=%d", channel, controller_number, controller_value);
+ break;
+ default:
+ ret = vformat("channel=%d, message=%d, pitch=%d, velocity=%d, pressure=%d, controller_number=%d, controller_value=%d, instrument=%d", channel, message, pitch, velocity, pressure, controller_number, controller_value, instrument);
+ }
+ return "InputEventMIDI: " + ret;
}
void InputEventMIDI::_bind_methods() {
@@ -1782,3 +1802,7 @@ String InputEventShortcut::to_string() {
return vformat("InputEventShortcut: shortcut=%s", shortcut->get_as_text());
}
+
+InputEventShortcut::InputEventShortcut() {
+ pressed = true;
+}
diff --git a/core/input/input_event.h b/core/input/input_event.h
index e9d4fb8325..ed7ccf0a9f 100644
--- a/core/input/input_event.h
+++ b/core/input/input_event.h
@@ -567,6 +567,8 @@ public:
virtual String as_text() const override;
virtual String to_string() override;
+
+ InputEventShortcut();
};
#endif // INPUT_EVENT_H
diff --git a/core/io/compression.cpp b/core/io/compression.cpp
index e36fb0afa4..4e4627c40b 100644
--- a/core/io/compression.cpp
+++ b/core/io/compression.cpp
@@ -215,7 +215,7 @@ int Compression::decompress_dynamic(Vector<uint8_t> *p_dst_vect, int p_max_dst_s
#ifdef BROTLI_ENABLED
BrotliDecoderResult ret;
BrotliDecoderState *state = BrotliDecoderCreateInstance(nullptr, nullptr, nullptr);
- ERR_FAIL_COND_V(state == nullptr, Z_DATA_ERROR);
+ ERR_FAIL_NULL_V(state, Z_DATA_ERROR);
// Setup the stream inputs.
const uint8_t *next_in = p_src;
diff --git a/core/io/dir_access.cpp b/core/io/dir_access.cpp
index 22b63a0929..40c1a53958 100644
--- a/core/io/dir_access.cpp
+++ b/core/io/dir_access.cpp
@@ -546,6 +546,10 @@ bool DirAccess::get_include_hidden() const {
return include_hidden;
}
+bool DirAccess::is_case_sensitive(const String &p_path) const {
+ return true;
+}
+
void DirAccess::_bind_methods() {
ClassDB::bind_static_method("DirAccess", D_METHOD("open", "path"), &DirAccess::_open);
ClassDB::bind_static_method("DirAccess", D_METHOD("get_open_error"), &DirAccess::get_open_error);
@@ -583,6 +587,8 @@ void DirAccess::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_include_hidden", "enable"), &DirAccess::set_include_hidden);
ClassDB::bind_method(D_METHOD("get_include_hidden"), &DirAccess::get_include_hidden);
+ ClassDB::bind_method(D_METHOD("is_case_sensitive", "path"), &DirAccess::is_case_sensitive);
+
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "include_navigational"), "set_include_navigational", "get_include_navigational");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "include_hidden"), "set_include_hidden", "get_include_hidden");
}
diff --git a/core/io/dir_access.h b/core/io/dir_access.h
index 52ed688deb..4ee69571f2 100644
--- a/core/io/dir_access.h
+++ b/core/io/dir_access.h
@@ -159,6 +159,8 @@ public:
void set_include_hidden(bool p_enable);
bool get_include_hidden() const;
+ virtual bool is_case_sensitive(const String &p_path) const;
+
DirAccess() {}
virtual ~DirAccess() {}
};
diff --git a/core/io/file_access.cpp b/core/io/file_access.cpp
index b669afdc99..6026dbf896 100644
--- a/core/io/file_access.cpp
+++ b/core/io/file_access.cpp
@@ -583,7 +583,7 @@ uint64_t FileAccess::get_modified_time(const String &p_file) {
return mt;
}
-uint32_t FileAccess::get_unix_permissions(const String &p_file) {
+BitField<FileAccess::UnixPermissionFlags> FileAccess::get_unix_permissions(const String &p_file) {
if (PackedData::get_singleton() && !PackedData::get_singleton()->is_disabled() && (PackedData::get_singleton()->has_path(p_file) || PackedData::get_singleton()->has_directory(p_file))) {
return 0;
}
@@ -591,11 +591,10 @@ uint32_t FileAccess::get_unix_permissions(const String &p_file) {
Ref<FileAccess> fa = create_for_path(p_file);
ERR_FAIL_COND_V_MSG(fa.is_null(), 0, "Cannot create FileAccess for path '" + p_file + "'.");
- uint32_t mt = fa->_get_unix_permissions(p_file);
- return mt;
+ return fa->_get_unix_permissions(p_file);
}
-Error FileAccess::set_unix_permissions(const String &p_file, uint32_t p_permissions) {
+Error FileAccess::set_unix_permissions(const String &p_file, BitField<FileAccess::UnixPermissionFlags> p_permissions) {
if (PackedData::get_singleton() && !PackedData::get_singleton()->is_disabled() && (PackedData::get_singleton()->has_path(p_file) || PackedData::get_singleton()->has_directory(p_file))) {
return ERR_UNAVAILABLE;
}
@@ -607,6 +606,52 @@ Error FileAccess::set_unix_permissions(const String &p_file, uint32_t p_permissi
return err;
}
+bool FileAccess::get_hidden_attribute(const String &p_file) {
+ if (PackedData::get_singleton() && !PackedData::get_singleton()->is_disabled() && (PackedData::get_singleton()->has_path(p_file) || PackedData::get_singleton()->has_directory(p_file))) {
+ return false;
+ }
+
+ Ref<FileAccess> fa = create_for_path(p_file);
+ ERR_FAIL_COND_V_MSG(fa.is_null(), false, "Cannot create FileAccess for path '" + p_file + "'.");
+
+ return fa->_get_hidden_attribute(p_file);
+}
+
+Error FileAccess::set_hidden_attribute(const String &p_file, bool p_hidden) {
+ if (PackedData::get_singleton() && !PackedData::get_singleton()->is_disabled() && (PackedData::get_singleton()->has_path(p_file) || PackedData::get_singleton()->has_directory(p_file))) {
+ return ERR_UNAVAILABLE;
+ }
+
+ Ref<FileAccess> fa = create_for_path(p_file);
+ ERR_FAIL_COND_V_MSG(fa.is_null(), ERR_CANT_CREATE, "Cannot create FileAccess for path '" + p_file + "'.");
+
+ Error err = fa->_set_hidden_attribute(p_file, p_hidden);
+ return err;
+}
+
+bool FileAccess::get_read_only_attribute(const String &p_file) {
+ if (PackedData::get_singleton() && !PackedData::get_singleton()->is_disabled() && (PackedData::get_singleton()->has_path(p_file) || PackedData::get_singleton()->has_directory(p_file))) {
+ return false;
+ }
+
+ Ref<FileAccess> fa = create_for_path(p_file);
+ ERR_FAIL_COND_V_MSG(fa.is_null(), false, "Cannot create FileAccess for path '" + p_file + "'.");
+
+ return fa->_get_read_only_attribute(p_file);
+}
+
+Error FileAccess::set_read_only_attribute(const String &p_file, bool p_ro) {
+ if (PackedData::get_singleton() && !PackedData::get_singleton()->is_disabled() && (PackedData::get_singleton()->has_path(p_file) || PackedData::get_singleton()->has_directory(p_file))) {
+ return ERR_UNAVAILABLE;
+ }
+
+ Ref<FileAccess> fa = create_for_path(p_file);
+ ERR_FAIL_COND_V_MSG(fa.is_null(), ERR_CANT_CREATE, "Cannot create FileAccess for path '" + p_file + "'.");
+
+ Error err = fa->_set_read_only_attribute(p_file, p_ro);
+ return err;
+}
+
void FileAccess::store_string(const String &p_string) {
if (p_string.length() == 0) {
return;
@@ -865,6 +910,14 @@ void FileAccess::_bind_methods() {
ClassDB::bind_static_method("FileAccess", D_METHOD("file_exists", "path"), &FileAccess::exists);
ClassDB::bind_static_method("FileAccess", D_METHOD("get_modified_time", "file"), &FileAccess::get_modified_time);
+ ClassDB::bind_static_method("FileAccess", D_METHOD("get_unix_permissions", "file"), &FileAccess::get_unix_permissions);
+ ClassDB::bind_static_method("FileAccess", D_METHOD("set_unix_permissions", "file", "permissions"), &FileAccess::set_unix_permissions);
+
+ ClassDB::bind_static_method("FileAccess", D_METHOD("get_hidden_attribute", "file"), &FileAccess::get_hidden_attribute);
+ ClassDB::bind_static_method("FileAccess", D_METHOD("set_hidden_attribute", "file", "hidden"), &FileAccess::set_hidden_attribute);
+ ClassDB::bind_static_method("FileAccess", D_METHOD("set_read_only_attribute", "file", "ro"), &FileAccess::set_read_only_attribute);
+ ClassDB::bind_static_method("FileAccess", D_METHOD("get_read_only_attribute", "file"), &FileAccess::get_read_only_attribute);
+
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "big_endian"), "set_big_endian", "is_big_endian");
BIND_ENUM_CONSTANT(READ);
@@ -877,4 +930,17 @@ void FileAccess::_bind_methods() {
BIND_ENUM_CONSTANT(COMPRESSION_ZSTD);
BIND_ENUM_CONSTANT(COMPRESSION_GZIP);
BIND_ENUM_CONSTANT(COMPRESSION_BROTLI);
+
+ BIND_BITFIELD_FLAG(UNIX_READ_OWNER);
+ BIND_BITFIELD_FLAG(UNIX_WRITE_OWNER);
+ BIND_BITFIELD_FLAG(UNIX_EXECUTE_OWNER);
+ BIND_BITFIELD_FLAG(UNIX_READ_GROUP);
+ BIND_BITFIELD_FLAG(UNIX_WRITE_GROUP);
+ BIND_BITFIELD_FLAG(UNIX_EXECUTE_GROUP);
+ BIND_BITFIELD_FLAG(UNIX_READ_OTHER);
+ BIND_BITFIELD_FLAG(UNIX_WRITE_OTHER);
+ BIND_BITFIELD_FLAG(UNIX_EXECUTE_OTHER);
+ BIND_BITFIELD_FLAG(UNIX_SET_USER_ID);
+ BIND_BITFIELD_FLAG(UNIX_SET_GROUP_ID);
+ BIND_BITFIELD_FLAG(UNIX_RESTRICTED_DELETE);
}
diff --git a/core/io/file_access.h b/core/io/file_access.h
index ad1ac665f3..7d346ca2f4 100644
--- a/core/io/file_access.h
+++ b/core/io/file_access.h
@@ -60,6 +60,21 @@ public:
WRITE_READ = 7,
};
+ enum UnixPermissionFlags {
+ UNIX_EXECUTE_OTHER = 0x001,
+ UNIX_WRITE_OTHER = 0x002,
+ UNIX_READ_OTHER = 0x004,
+ UNIX_EXECUTE_GROUP = 0x008,
+ UNIX_WRITE_GROUP = 0x010,
+ UNIX_READ_GROUP = 0x020,
+ UNIX_EXECUTE_OWNER = 0x040,
+ UNIX_WRITE_OWNER = 0x080,
+ UNIX_READ_OWNER = 0x100,
+ UNIX_RESTRICTED_DELETE = 0x200,
+ UNIX_SET_GROUP_ID = 0x400,
+ UNIX_SET_USER_ID = 0x800,
+ };
+
enum CompressionMode {
COMPRESSION_FASTLZ = Compression::MODE_FASTLZ,
COMPRESSION_DEFLATE = Compression::MODE_DEFLATE,
@@ -74,8 +89,13 @@ public:
bool big_endian = false;
bool real_is_double = false;
- virtual uint32_t _get_unix_permissions(const String &p_file) = 0;
- virtual Error _set_unix_permissions(const String &p_file, uint32_t p_permissions) = 0;
+ virtual BitField<UnixPermissionFlags> _get_unix_permissions(const String &p_file) = 0;
+ virtual Error _set_unix_permissions(const String &p_file, BitField<UnixPermissionFlags> p_permissions) = 0;
+
+ virtual bool _get_hidden_attribute(const String &p_file) = 0;
+ virtual Error _set_hidden_attribute(const String &p_file, bool p_hidden) = 0;
+ virtual bool _get_read_only_attribute(const String &p_file) = 0;
+ virtual Error _set_read_only_attribute(const String &p_file, bool p_ro) = 0;
protected:
static void _bind_methods();
@@ -185,8 +205,13 @@ public:
static CreateFunc get_create_func(AccessType p_access);
static bool exists(const String &p_name); ///< return true if a file exists
static uint64_t get_modified_time(const String &p_file);
- static uint32_t get_unix_permissions(const String &p_file);
- static Error set_unix_permissions(const String &p_file, uint32_t p_permissions);
+ static BitField<FileAccess::UnixPermissionFlags> get_unix_permissions(const String &p_file);
+ static Error set_unix_permissions(const String &p_file, BitField<FileAccess::UnixPermissionFlags> p_permissions);
+
+ static bool get_hidden_attribute(const String &p_file);
+ static Error set_hidden_attribute(const String &p_file, bool p_hidden);
+ static bool get_read_only_attribute(const String &p_file);
+ static Error set_read_only_attribute(const String &p_file, bool p_ro);
static void set_backup_save(bool p_enable) { backup_save = p_enable; };
static bool is_backup_save_enabled() { return backup_save; };
@@ -198,8 +223,8 @@ public:
static Vector<uint8_t> get_file_as_bytes(const String &p_path, Error *r_error = nullptr);
static String get_file_as_string(const String &p_path, Error *r_error = nullptr);
- static PackedByteArray _get_file_as_bytes(const String &p_path) { return get_file_as_bytes(p_path); }
- static String _get_file_as_string(const String &p_path) { return get_file_as_string(p_path); };
+ static PackedByteArray _get_file_as_bytes(const String &p_path) { return get_file_as_bytes(p_path, &last_file_open_error); }
+ static String _get_file_as_string(const String &p_path) { return get_file_as_string(p_path, &last_file_open_error); }
template <class T>
static void make_default(AccessType p_access) {
@@ -212,5 +237,6 @@ public:
VARIANT_ENUM_CAST(FileAccess::CompressionMode);
VARIANT_ENUM_CAST(FileAccess::ModeFlags);
+VARIANT_BITFIELD_CAST(FileAccess::UnixPermissionFlags);
#endif // FILE_ACCESS_H
diff --git a/core/io/file_access_compressed.cpp b/core/io/file_access_compressed.cpp
index 3e5a1217dd..0f00bd292c 100644
--- a/core/io/file_access_compressed.cpp
+++ b/core/io/file_access_compressed.cpp
@@ -365,20 +365,48 @@ uint64_t FileAccessCompressed::_get_modified_time(const String &p_file) {
}
}
-uint32_t FileAccessCompressed::_get_unix_permissions(const String &p_file) {
+BitField<FileAccess::UnixPermissionFlags> FileAccessCompressed::_get_unix_permissions(const String &p_file) {
if (f.is_valid()) {
return f->_get_unix_permissions(p_file);
}
return 0;
}
-Error FileAccessCompressed::_set_unix_permissions(const String &p_file, uint32_t p_permissions) {
+Error FileAccessCompressed::_set_unix_permissions(const String &p_file, BitField<FileAccess::UnixPermissionFlags> p_permissions) {
if (f.is_valid()) {
return f->_set_unix_permissions(p_file, p_permissions);
}
return FAILED;
}
+bool FileAccessCompressed::_get_hidden_attribute(const String &p_file) {
+ if (f.is_valid()) {
+ return f->_get_hidden_attribute(p_file);
+ }
+ return false;
+}
+
+Error FileAccessCompressed::_set_hidden_attribute(const String &p_file, bool p_hidden) {
+ if (f.is_valid()) {
+ return f->_set_hidden_attribute(p_file, p_hidden);
+ }
+ return FAILED;
+}
+
+bool FileAccessCompressed::_get_read_only_attribute(const String &p_file) {
+ if (f.is_valid()) {
+ return f->_get_read_only_attribute(p_file);
+ }
+ return false;
+}
+
+Error FileAccessCompressed::_set_read_only_attribute(const String &p_file, bool p_ro) {
+ if (f.is_valid()) {
+ return f->_set_read_only_attribute(p_file, p_ro);
+ }
+ return FAILED;
+}
+
void FileAccessCompressed::close() {
_close();
}
diff --git a/core/io/file_access_compressed.h b/core/io/file_access_compressed.h
index 601b74a9c1..bf57eaa07c 100644
--- a/core/io/file_access_compressed.h
+++ b/core/io/file_access_compressed.h
@@ -94,8 +94,13 @@ public:
virtual bool file_exists(const String &p_name) override; ///< return true if a file exists
virtual uint64_t _get_modified_time(const String &p_file) override;
- virtual uint32_t _get_unix_permissions(const String &p_file) override;
- virtual Error _set_unix_permissions(const String &p_file, uint32_t p_permissions) override;
+ virtual BitField<FileAccess::UnixPermissionFlags> _get_unix_permissions(const String &p_file) override;
+ virtual Error _set_unix_permissions(const String &p_file, BitField<FileAccess::UnixPermissionFlags> p_permissions) override;
+
+ virtual bool _get_hidden_attribute(const String &p_file) override;
+ virtual Error _set_hidden_attribute(const String &p_file, bool p_hidden) override;
+ virtual bool _get_read_only_attribute(const String &p_file) override;
+ virtual Error _set_read_only_attribute(const String &p_file, bool p_ro) override;
virtual void close() override;
diff --git a/core/io/file_access_encrypted.cpp b/core/io/file_access_encrypted.cpp
index c39d19d52b..b689f5b628 100644
--- a/core/io/file_access_encrypted.cpp
+++ b/core/io/file_access_encrypted.cpp
@@ -285,13 +285,46 @@ uint64_t FileAccessEncrypted::_get_modified_time(const String &p_file) {
return 0;
}
-uint32_t FileAccessEncrypted::_get_unix_permissions(const String &p_file) {
+BitField<FileAccess::UnixPermissionFlags> FileAccessEncrypted::_get_unix_permissions(const String &p_file) {
+ if (file.is_valid()) {
+ return file->_get_unix_permissions(p_file);
+ }
return 0;
}
-Error FileAccessEncrypted::_set_unix_permissions(const String &p_file, uint32_t p_permissions) {
- ERR_PRINT("Setting UNIX permissions on encrypted files is not implemented yet.");
- return ERR_UNAVAILABLE;
+Error FileAccessEncrypted::_set_unix_permissions(const String &p_file, BitField<FileAccess::UnixPermissionFlags> p_permissions) {
+ if (file.is_valid()) {
+ return file->_set_unix_permissions(p_file, p_permissions);
+ }
+ return FAILED;
+}
+
+bool FileAccessEncrypted::_get_hidden_attribute(const String &p_file) {
+ if (file.is_valid()) {
+ return file->_get_hidden_attribute(p_file);
+ }
+ return false;
+}
+
+Error FileAccessEncrypted::_set_hidden_attribute(const String &p_file, bool p_hidden) {
+ if (file.is_valid()) {
+ return file->_set_hidden_attribute(p_file, p_hidden);
+ }
+ return FAILED;
+}
+
+bool FileAccessEncrypted::_get_read_only_attribute(const String &p_file) {
+ if (file.is_valid()) {
+ return file->_get_read_only_attribute(p_file);
+ }
+ return false;
+}
+
+Error FileAccessEncrypted::_set_read_only_attribute(const String &p_file, bool p_ro) {
+ if (file.is_valid()) {
+ return file->_set_read_only_attribute(p_file, p_ro);
+ }
+ return FAILED;
}
void FileAccessEncrypted::close() {
diff --git a/core/io/file_access_encrypted.h b/core/io/file_access_encrypted.h
index 9702b5a517..489d213b8f 100644
--- a/core/io/file_access_encrypted.h
+++ b/core/io/file_access_encrypted.h
@@ -85,8 +85,13 @@ public:
virtual bool file_exists(const String &p_name) override; ///< return true if a file exists
virtual uint64_t _get_modified_time(const String &p_file) override;
- virtual uint32_t _get_unix_permissions(const String &p_file) override;
- virtual Error _set_unix_permissions(const String &p_file, uint32_t p_permissions) override;
+ virtual BitField<FileAccess::UnixPermissionFlags> _get_unix_permissions(const String &p_file) override;
+ virtual Error _set_unix_permissions(const String &p_file, BitField<FileAccess::UnixPermissionFlags> p_permissions) override;
+
+ virtual bool _get_hidden_attribute(const String &p_file) override;
+ virtual Error _set_hidden_attribute(const String &p_file, bool p_hidden) override;
+ virtual bool _get_read_only_attribute(const String &p_file) override;
+ virtual Error _set_read_only_attribute(const String &p_file, bool p_ro) override;
virtual void close() override;
diff --git a/core/io/file_access_memory.cpp b/core/io/file_access_memory.cpp
index 14ec0be092..0983920b94 100644
--- a/core/io/file_access_memory.cpp
+++ b/core/io/file_access_memory.cpp
@@ -79,7 +79,7 @@ Error FileAccessMemory::open_custom(const uint8_t *p_data, uint64_t p_len) {
}
Error FileAccessMemory::open_internal(const String &p_path, int p_mode_flags) {
- ERR_FAIL_COND_V(!files, ERR_FILE_NOT_FOUND);
+ ERR_FAIL_NULL_V(files, ERR_FILE_NOT_FOUND);
String name = fix_path(p_path);
//name = DirAccess::normalize_path(name);
@@ -99,22 +99,22 @@ bool FileAccessMemory::is_open() const {
}
void FileAccessMemory::seek(uint64_t p_position) {
- ERR_FAIL_COND(!data);
+ ERR_FAIL_NULL(data);
pos = p_position;
}
void FileAccessMemory::seek_end(int64_t p_position) {
- ERR_FAIL_COND(!data);
+ ERR_FAIL_NULL(data);
pos = length + p_position;
}
uint64_t FileAccessMemory::get_position() const {
- ERR_FAIL_COND_V(!data, 0);
+ ERR_FAIL_NULL_V(data, 0);
return pos;
}
uint64_t FileAccessMemory::get_length() const {
- ERR_FAIL_COND_V(!data, 0);
+ ERR_FAIL_NULL_V(data, 0);
return length;
}
@@ -134,7 +134,7 @@ uint8_t FileAccessMemory::get_8() const {
uint64_t FileAccessMemory::get_buffer(uint8_t *p_dst, uint64_t p_length) const {
ERR_FAIL_COND_V(!p_dst && p_length > 0, -1);
- ERR_FAIL_COND_V(!data, -1);
+ ERR_FAIL_NULL_V(data, -1);
uint64_t left = length - pos;
uint64_t read = MIN(p_length, left);
@@ -154,11 +154,11 @@ Error FileAccessMemory::get_error() const {
}
void FileAccessMemory::flush() {
- ERR_FAIL_COND(!data);
+ ERR_FAIL_NULL(data);
}
void FileAccessMemory::store_8(uint8_t p_byte) {
- ERR_FAIL_COND(!data);
+ ERR_FAIL_NULL(data);
ERR_FAIL_COND(pos >= length);
data[pos++] = p_byte;
}
diff --git a/core/io/file_access_memory.h b/core/io/file_access_memory.h
index 43fe6ab658..ac08e5406f 100644
--- a/core/io/file_access_memory.h
+++ b/core/io/file_access_memory.h
@@ -68,8 +68,13 @@ public:
virtual bool file_exists(const String &p_name) override; ///< return true if a file exists
virtual uint64_t _get_modified_time(const String &p_file) override { return 0; }
- virtual uint32_t _get_unix_permissions(const String &p_file) override { return 0; }
- virtual Error _set_unix_permissions(const String &p_file, uint32_t p_permissions) override { return FAILED; }
+ virtual BitField<FileAccess::UnixPermissionFlags> _get_unix_permissions(const String &p_file) override { return 0; }
+ virtual Error _set_unix_permissions(const String &p_file, BitField<FileAccess::UnixPermissionFlags> p_permissions) override { return FAILED; }
+
+ virtual bool _get_hidden_attribute(const String &p_file) override { return false; }
+ virtual Error _set_hidden_attribute(const String &p_file, bool p_hidden) override { return ERR_UNAVAILABLE; }
+ virtual bool _get_read_only_attribute(const String &p_file) override { return false; }
+ virtual Error _set_read_only_attribute(const String &p_file, bool p_ro) override { return ERR_UNAVAILABLE; }
virtual void close() override {}
diff --git a/core/io/file_access_pack.h b/core/io/file_access_pack.h
index 1538b302c2..97391a5611 100644
--- a/core/io/file_access_pack.h
+++ b/core/io/file_access_pack.h
@@ -150,8 +150,13 @@ class FileAccessPack : public FileAccess {
Ref<FileAccess> f;
virtual Error open_internal(const String &p_path, int p_mode_flags) override;
virtual uint64_t _get_modified_time(const String &p_file) override { return 0; }
- virtual uint32_t _get_unix_permissions(const String &p_file) override { return 0; }
- virtual Error _set_unix_permissions(const String &p_file, uint32_t p_permissions) override { return FAILED; }
+ virtual BitField<FileAccess::UnixPermissionFlags> _get_unix_permissions(const String &p_file) override { return 0; }
+ virtual Error _set_unix_permissions(const String &p_file, BitField<FileAccess::UnixPermissionFlags> p_permissions) override { return FAILED; }
+
+ virtual bool _get_hidden_attribute(const String &p_file) override { return false; }
+ virtual Error _set_hidden_attribute(const String &p_file, bool p_hidden) override { return ERR_UNAVAILABLE; }
+ virtual bool _get_read_only_attribute(const String &p_file) override { return false; }
+ virtual Error _set_read_only_attribute(const String &p_file, bool p_ro) override { return ERR_UNAVAILABLE; }
public:
virtual bool is_open() const override;
diff --git a/core/io/file_access_zip.cpp b/core/io/file_access_zip.cpp
index c7f1a73f97..dd45332412 100644
--- a/core/io/file_access_zip.cpp
+++ b/core/io/file_access_zip.cpp
@@ -110,7 +110,7 @@ static void godot_free(voidpf opaque, voidpf address) {
} // extern "C"
void ZipArchive::close_handle(unzFile p_file) const {
- ERR_FAIL_COND_MSG(!p_file, "Cannot close a file if none is open.");
+ ERR_FAIL_NULL_MSG(p_file, "Cannot close a file if none is open.");
unzCloseCurrentFile(p_file);
unzClose(p_file);
}
@@ -136,7 +136,7 @@ unzFile ZipArchive::get_file_handle(String p_file) const {
io.free_mem = godot_free;
unzFile pkg = unzOpen2(packages[file.package].filename.utf8().get_data(), &io);
- ERR_FAIL_COND_V_MSG(!pkg, nullptr, "Cannot open file '" + packages[file.package].filename + "'.");
+ ERR_FAIL_NULL_V_MSG(pkg, nullptr, "Cannot open file '" + packages[file.package].filename + "'.");
int unz_err = unzGoToFilePos(pkg, &file.file_pos);
if (unz_err != UNZ_OK || unzOpenCurrentFile(pkg) != UNZ_OK) {
unzClose(pkg);
@@ -168,7 +168,7 @@ bool ZipArchive::try_open_pack(const String &p_path, bool p_replace_files, uint6
io.zerror_file = godot_testerror;
unzFile zfile = unzOpen2(p_path.utf8().get_data(), &io);
- ERR_FAIL_COND_V(!zfile, false);
+ ERR_FAIL_NULL_V(zfile, false);
unz_global_info64 gi;
int err = unzGetGlobalInfo64(zfile, &gi);
@@ -239,9 +239,9 @@ Error FileAccessZip::open_internal(const String &p_path, int p_mode_flags) {
ERR_FAIL_COND_V(p_mode_flags & FileAccess::WRITE, FAILED);
ZipArchive *arch = ZipArchive::get_singleton();
- ERR_FAIL_COND_V(!arch, FAILED);
+ ERR_FAIL_NULL_V(arch, FAILED);
zfile = arch->get_file_handle(p_path);
- ERR_FAIL_COND_V(!zfile, FAILED);
+ ERR_FAIL_NULL_V(zfile, FAILED);
int err = unzGetCurrentFileInfo64(zfile, &file_info, nullptr, 0, nullptr, 0, nullptr, 0);
ERR_FAIL_COND_V(err != UNZ_OK, FAILED);
@@ -255,7 +255,7 @@ void FileAccessZip::_close() {
}
ZipArchive *arch = ZipArchive::get_singleton();
- ERR_FAIL_COND(!arch);
+ ERR_FAIL_NULL(arch);
arch->close_handle(zfile);
zfile = nullptr;
}
@@ -265,28 +265,28 @@ bool FileAccessZip::is_open() const {
}
void FileAccessZip::seek(uint64_t p_position) {
- ERR_FAIL_COND(!zfile);
+ ERR_FAIL_NULL(zfile);
unzSeekCurrentFile(zfile, p_position);
}
void FileAccessZip::seek_end(int64_t p_position) {
- ERR_FAIL_COND(!zfile);
+ ERR_FAIL_NULL(zfile);
unzSeekCurrentFile(zfile, get_length() + p_position);
}
uint64_t FileAccessZip::get_position() const {
- ERR_FAIL_COND_V(!zfile, 0);
+ ERR_FAIL_NULL_V(zfile, 0);
return unztell(zfile);
}
uint64_t FileAccessZip::get_length() const {
- ERR_FAIL_COND_V(!zfile, 0);
+ ERR_FAIL_NULL_V(zfile, 0);
return file_info.uncompressed_size;
}
bool FileAccessZip::eof_reached() const {
- ERR_FAIL_COND_V(!zfile, true);
+ ERR_FAIL_NULL_V(zfile, true);
return at_eof;
}
@@ -299,7 +299,7 @@ uint8_t FileAccessZip::get_8() const {
uint64_t FileAccessZip::get_buffer(uint8_t *p_dst, uint64_t p_length) const {
ERR_FAIL_COND_V(!p_dst && p_length > 0, -1);
- ERR_FAIL_COND_V(!zfile, -1);
+ ERR_FAIL_NULL_V(zfile, -1);
at_eof = unzeof(zfile);
if (at_eof) {
diff --git a/core/io/file_access_zip.h b/core/io/file_access_zip.h
index f8b640946c..1062a06238 100644
--- a/core/io/file_access_zip.h
+++ b/core/io/file_access_zip.h
@@ -106,8 +106,13 @@ public:
virtual bool file_exists(const String &p_name) override; ///< return true if a file exists
virtual uint64_t _get_modified_time(const String &p_file) override { return 0; } // todo
- virtual uint32_t _get_unix_permissions(const String &p_file) override { return 0; }
- virtual Error _set_unix_permissions(const String &p_file, uint32_t p_permissions) override { return FAILED; }
+ virtual BitField<FileAccess::UnixPermissionFlags> _get_unix_permissions(const String &p_file) override { return 0; }
+ virtual Error _set_unix_permissions(const String &p_file, BitField<FileAccess::UnixPermissionFlags> p_permissions) override { return FAILED; }
+
+ virtual bool _get_hidden_attribute(const String &p_file) override { return false; }
+ virtual Error _set_hidden_attribute(const String &p_file, bool p_hidden) override { return ERR_UNAVAILABLE; }
+ virtual bool _get_read_only_attribute(const String &p_file) override { return false; }
+ virtual Error _set_read_only_attribute(const String &p_file, bool p_ro) override { return ERR_UNAVAILABLE; }
virtual void close() override;
diff --git a/core/io/image.cpp b/core/io/image.cpp
index a5fea09113..15d0182dfc 100644
--- a/core/io/image.cpp
+++ b/core/io/image.cpp
@@ -1930,8 +1930,7 @@ Error Image::generate_mipmaps(bool p_renormalize) {
}
Error Image::generate_mipmap_roughness(RoughnessChannel p_roughness_channel, const Ref<Image> &p_normal_map) {
- Vector<double> normal_sat_vec; //summed area table
- double *normal_sat = nullptr; //summed area table for normal map
+ LocalVector<double> normal_sat_vec; //summed area table
int normal_w = 0, normal_h = 0;
ERR_FAIL_COND_V_MSG(p_normal_map.is_null() || p_normal_map->is_empty(), ERR_INVALID_PARAMETER, "Must provide a valid normal map for roughness mipmaps");
@@ -1945,8 +1944,7 @@ Error Image::generate_mipmap_roughness(RoughnessChannel p_roughness_channel, con
normal_h = nm->get_height();
normal_sat_vec.resize(normal_w * normal_h * 3);
-
- normal_sat = normal_sat_vec.ptrw();
+ double *normal_sat = normal_sat_vec.ptr();
//create summed area table
@@ -2021,24 +2019,26 @@ Error Image::generate_mipmap_roughness(RoughnessChannel p_roughness_channel, con
avg[2] += normal_sat[tofs + 2];
}
- if (from_y > 0) {
+ if (from_y > 0 && to_x > 0) {
uint32_t tofs = ((from_y - 1) * normal_w + to_x) * 3;
avg[0] -= normal_sat[tofs + 0];
avg[1] -= normal_sat[tofs + 1];
avg[2] -= normal_sat[tofs + 2];
}
- if (from_x > 0) {
+ if (from_x > 0 && to_y > 0) {
uint32_t tofs = (to_y * normal_w + (from_x - 1)) * 3;
avg[0] -= normal_sat[tofs + 0];
avg[1] -= normal_sat[tofs + 1];
avg[2] -= normal_sat[tofs + 2];
}
- uint32_t tofs = (to_y * normal_w + to_x) * 3;
- avg[0] += normal_sat[tofs + 0];
- avg[1] += normal_sat[tofs + 1];
- avg[2] += normal_sat[tofs + 2];
+ if (to_y > 0 && to_x > 0) {
+ uint32_t tofs = (to_y * normal_w + to_x) * 3;
+ avg[0] += normal_sat[tofs + 0];
+ avg[1] += normal_sat[tofs + 1];
+ avg[2] += normal_sat[tofs + 2];
+ }
double div = double(size_x * size_y);
Vector3 vec(avg[0] / div, avg[1] / div, avg[2] / div);
@@ -2356,7 +2356,7 @@ void Image::initialize_data(const char **p_xpm) {
}
Color *colorptr = colormap.getptr(pixelstr);
- ERR_FAIL_COND(!colorptr);
+ ERR_FAIL_NULL(colorptr);
uint8_t pixel[4];
for (uint32_t i = 0; i < pixel_size; i++) {
pixel[i] = CLAMP((*colorptr)[i] * 255, 0, 255);
@@ -2646,23 +2646,23 @@ Error Image::compress_from_channels(CompressMode p_mode, UsedChannels p_channels
switch (p_mode) {
case COMPRESS_S3TC: {
- ERR_FAIL_COND_V(!_image_compress_bc_func, ERR_UNAVAILABLE);
+ ERR_FAIL_NULL_V(_image_compress_bc_func, ERR_UNAVAILABLE);
_image_compress_bc_func(this, p_channels);
} break;
case COMPRESS_ETC: {
- ERR_FAIL_COND_V(!_image_compress_etc1_func, ERR_UNAVAILABLE);
+ ERR_FAIL_NULL_V(_image_compress_etc1_func, ERR_UNAVAILABLE);
_image_compress_etc1_func(this);
} break;
case COMPRESS_ETC2: {
- ERR_FAIL_COND_V(!_image_compress_etc2_func, ERR_UNAVAILABLE);
+ ERR_FAIL_NULL_V(_image_compress_etc2_func, ERR_UNAVAILABLE);
_image_compress_etc2_func(this, p_channels);
} break;
case COMPRESS_BPTC: {
- ERR_FAIL_COND_V(!_image_compress_bptc_func, ERR_UNAVAILABLE);
+ ERR_FAIL_NULL_V(_image_compress_bptc_func, ERR_UNAVAILABLE);
_image_compress_bptc_func(this, p_channels);
} break;
case COMPRESS_ASTC: {
- ERR_FAIL_COND_V(!_image_compress_astc_func, ERR_UNAVAILABLE);
+ ERR_FAIL_NULL_V(_image_compress_astc_func, ERR_UNAVAILABLE);
_image_compress_astc_func(this, p_astc_format);
} break;
case COMPRESS_MAX: {
@@ -3017,7 +3017,7 @@ 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::_dds_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;
@@ -3412,6 +3412,7 @@ void Image::_bind_methods() {
ClassDB::bind_method(D_METHOD("convert", "format"), &Image::convert);
+ ClassDB::bind_method(D_METHOD("get_mipmap_count"), &Image::get_mipmap_count);
ClassDB::bind_method(D_METHOD("get_mipmap_offset", "mipmap"), &Image::get_mipmap_offset);
ClassDB::bind_method(D_METHOD("resize_to_po2", "square", "interpolation"), &Image::resize_to_po2, DEFVAL(false), DEFVAL(INTERPOLATE_BILINEAR));
@@ -3489,7 +3490,7 @@ void Image::_bind_methods() {
ClassDB::bind_method(D_METHOD("load_webp_from_buffer", "buffer"), &Image::load_webp_from_buffer);
ClassDB::bind_method(D_METHOD("load_tga_from_buffer", "buffer"), &Image::load_tga_from_buffer);
ClassDB::bind_method(D_METHOD("load_bmp_from_buffer", "buffer"), &Image::load_bmp_from_buffer);
- ClassDB::bind_method(D_METHOD("load_dds_from_buffer", "buffer"), &Image::load_dds_from_buffer);
+ ClassDB::bind_method(D_METHOD("load_ktx_from_buffer", "buffer"), &Image::load_ktx_from_buffer);
ClassDB::bind_method(D_METHOD("load_svg_from_buffer", "buffer", "scale"), &Image::load_svg_from_buffer, DEFVAL(1.0));
ClassDB::bind_method(D_METHOD("load_svg_from_string", "svg_str", "scale"), &Image::load_svg_from_string, DEFVAL(1.0));
@@ -3655,7 +3656,7 @@ void Image::bump_map_to_normal_map(float bump_scale) {
const uint8_t *rp = data.ptr();
uint8_t *wp = result_image.ptrw();
- ERR_FAIL_COND(!rp);
+ ERR_FAIL_NULL(rp);
unsigned char *write_ptr = wp;
float *read_ptr = (float *)rp;
@@ -3865,12 +3866,12 @@ Error Image::load_svg_from_string(const String &p_svg_str, float scale) {
return load_svg_from_buffer(p_svg_str.to_utf8_buffer(), scale);
}
-Error Image::load_dds_from_buffer(const Vector<uint8_t> &p_array) {
+Error Image::load_ktx_from_buffer(const Vector<uint8_t> &p_array) {
ERR_FAIL_NULL_V_MSG(
- _dds_mem_loader_func,
+ _ktx_mem_loader_func,
ERR_UNAVAILABLE,
- "The DDS module isn't enabled. Recompile the Godot editor or export template binary with the `module_dds_enabled=yes` SCons option.");
- return _load_from_buffer(p_array, _dds_mem_loader_func);
+ "The KTX module isn't enabled. Recompile the Godot editor or export template binary with the `module_ktx_enabled=yes` SCons option.");
+ return _load_from_buffer(p_array, _ktx_mem_loader_func);
}
void Image::convert_rg_to_ra_rgba8() {
@@ -3916,7 +3917,7 @@ Error Image::_load_from_buffer(const Vector<uint8_t> &p_array, ImageMemLoadFunc
int buffer_size = p_array.size();
ERR_FAIL_COND_V(buffer_size == 0, ERR_INVALID_PARAMETER);
- ERR_FAIL_COND_V(!p_loader, ERR_INVALID_PARAMETER);
+ ERR_FAIL_NULL_V(p_loader, ERR_INVALID_PARAMETER);
const uint8_t *r = p_array.ptr();
diff --git a/core/io/image.h b/core/io/image.h
index f68543ba24..a21d05187b 100644
--- a/core/io/image.h
+++ b/core/io/image.h
@@ -150,7 +150,7 @@ public:
static ImageMemLoadFunc _tga_mem_loader_func;
static ImageMemLoadFunc _bmp_mem_loader_func;
static ScalableImageMemLoadFunc _svg_scalable_mem_loader_func;
- static ImageMemLoadFunc _dds_mem_loader_func;
+ static ImageMemLoadFunc _ktx_mem_loader_func;
static void (*_image_compress_bc_func)(Image *, UsedChannels p_channels);
static void (*_image_compress_bptc_func)(Image *, UsedChannels p_channels);
@@ -403,7 +403,7 @@ public:
Error load_webp_from_buffer(const Vector<uint8_t> &p_array);
Error load_tga_from_buffer(const Vector<uint8_t> &p_array);
Error load_bmp_from_buffer(const Vector<uint8_t> &p_array);
- Error load_dds_from_buffer(const Vector<uint8_t> &p_array);
+ Error load_ktx_from_buffer(const Vector<uint8_t> &p_array);
Error load_svg_from_buffer(const Vector<uint8_t> &p_array, float scale = 1.0);
Error load_svg_from_string(const String &p_svg_str, float scale = 1.0);
diff --git a/core/io/ip.cpp b/core/io/ip.cpp
index 772f700916..254351897d 100644
--- a/core/io/ip.cpp
+++ b/core/io/ip.cpp
@@ -333,7 +333,7 @@ IP *(*IP::_create)() = nullptr;
IP *IP::create() {
ERR_FAIL_COND_V_MSG(singleton, nullptr, "IP singleton already exist.");
- ERR_FAIL_COND_V(!_create, nullptr);
+ ERR_FAIL_NULL_V(_create, nullptr);
return _create();
}
diff --git a/core/io/marshalls.cpp b/core/io/marshalls.cpp
index 6ca7b0f831..79e4b207d4 100644
--- a/core/io/marshalls.cpp
+++ b/core/io/marshalls.cpp
@@ -640,7 +640,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int
} else {
Object *obj = ClassDB::instantiate(str);
- ERR_FAIL_COND_V(!obj, ERR_UNAVAILABLE);
+ ERR_FAIL_NULL_V(obj, ERR_UNAVAILABLE);
ERR_FAIL_COND_V(len < 4, ERR_INVALID_DATA);
int32_t count = decode_uint32(buf);
@@ -1155,10 +1155,12 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo
#ifdef REAL_T_IS_DOUBLE
case Variant::VECTOR2:
case Variant::VECTOR3:
+ case Variant::VECTOR4:
case Variant::PACKED_VECTOR2_ARRAY:
case Variant::PACKED_VECTOR3_ARRAY:
case Variant::TRANSFORM2D:
case Variant::TRANSFORM3D:
+ case Variant::PROJECTION:
case Variant::QUATERNION:
case Variant::PLANE:
case Variant::BASIS:
@@ -1576,7 +1578,7 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo
buf += len;
}
Variant *v = d.getptr(E);
- ERR_FAIL_COND_V(!v, ERR_BUG);
+ ERR_FAIL_NULL_V(v, ERR_BUG);
err = encode_variant(*v, buf, len, p_full_objects, p_depth + 1);
ERR_FAIL_COND_V(err, err);
ERR_FAIL_COND_V(len % 4, ERR_BUG);
diff --git a/core/io/packed_data_container.cpp b/core/io/packed_data_container.cpp
index ce4edb18fe..11b0c69774 100644
--- a/core/io/packed_data_container.cpp
+++ b/core/io/packed_data_container.cpp
@@ -125,7 +125,7 @@ Variant PackedDataContainer::_get_at_ofs(uint32_t p_ofs, const uint8_t *p_buf, b
uint32_t PackedDataContainer::_type_at_ofs(uint32_t p_ofs) const {
ERR_FAIL_COND_V(p_ofs + 4 > (uint32_t)data.size(), 0);
const uint8_t *rd = data.ptr();
- ERR_FAIL_COND_V(!rd, 0);
+ ERR_FAIL_NULL_V(rd, 0);
const uint8_t *r = &rd[p_ofs];
uint32_t type = decode_uint32(r);
@@ -135,7 +135,7 @@ uint32_t PackedDataContainer::_type_at_ofs(uint32_t p_ofs) const {
int PackedDataContainer::_size(uint32_t p_ofs) const {
ERR_FAIL_COND_V(p_ofs + 4 > (uint32_t)data.size(), 0);
const uint8_t *rd = data.ptr();
- ERR_FAIL_COND_V(!rd, 0);
+ ERR_FAIL_NULL_V(rd, 0);
const uint8_t *r = &rd[p_ofs];
uint32_t type = decode_uint32(r);
@@ -156,7 +156,7 @@ Variant PackedDataContainer::_key_at_ofs(uint32_t p_ofs, const Variant &p_key, b
const uint8_t *rd = data.ptr();
if (!rd) {
err = true;
- ERR_FAIL_COND_V(!rd, Variant());
+ ERR_FAIL_NULL_V(rd, Variant());
}
const uint8_t *r = &rd[p_ofs];
uint32_t type = decode_uint32(r);
diff --git a/core/io/packet_peer.h b/core/io/packet_peer.h
index 7383ab84df..86ebe32cb6 100644
--- a/core/io/packet_peer.h
+++ b/core/io/packet_peer.h
@@ -37,7 +37,6 @@
#include "core/extension/ext_wrappers.gen.inc"
#include "core/object/gdvirtual.gen.inc"
-#include "core/object/script_language.h"
#include "core/variant/native_ptr.h"
class PacketPeer : public RefCounted {
diff --git a/core/io/pck_packer.cpp b/core/io/pck_packer.cpp
index 9b49cc3d8c..a7c715c318 100644
--- a/core/io/pck_packer.cpp
+++ b/core/io/pck_packer.cpp
@@ -205,7 +205,7 @@ Error PCKPacker::flush(bool p_verbose) {
int header_padding = _get_pad(alignment, file->get_position());
for (int i = 0; i < header_padding; i++) {
- file->store_8(Math::rand() % 256);
+ file->store_8(0);
}
int64_t file_base = file->get_position();
@@ -244,7 +244,7 @@ Error PCKPacker::flush(bool p_verbose) {
int pad = _get_pad(alignment, file->get_position());
for (int j = 0; j < pad; j++) {
- file->store_8(Math::rand() % 256);
+ file->store_8(0);
}
count += 1;
diff --git a/core/io/remote_filesystem_client.cpp b/core/io/remote_filesystem_client.cpp
index f22e442a34..1198810441 100644
--- a/core/io/remote_filesystem_client.cpp
+++ b/core/io/remote_filesystem_client.cpp
@@ -270,7 +270,7 @@ Error RemoteFilesystemClient::_synchronize_with_server(const String &p_host, int
String file = temp_file_cache[i].path;
if (temp_file_cache[i].server_modified_time == 0 || server_disconnected) {
- // File was removed, or server disconnected before tranferring it. Since it's no longer valid, remove anyway.
+ // File was removed, or server disconnected before transferring it. Since it's no longer valid, remove anyway.
_remove_file(file);
continue;
}
diff --git a/core/io/remote_filesystem_client.h b/core/io/remote_filesystem_client.h
index 42eba98eb1..fcb5c1cfc3 100644
--- a/core/io/remote_filesystem_client.h
+++ b/core/io/remote_filesystem_client.h
@@ -44,8 +44,8 @@ protected:
String _get_cache_path() { return cache_path; }
struct FileCache {
String path; // Local path (as in "folder/to/file.png")
- uint64_t server_modified_time; // MD5 checksum.
- uint64_t modified_time;
+ uint64_t server_modified_time = 0; // MD5 checksum.
+ uint64_t modified_time = 0;
};
virtual bool _is_configured() { return !cache_path.is_empty(); }
// Can be re-implemented per platform. If so, feel free to ignore get_cache_path()
diff --git a/core/io/resource.cpp b/core/io/resource.cpp
index 07677337b4..e0d42a274a 100644
--- a/core/io/resource.cpp
+++ b/core/io/resource.cpp
@@ -239,6 +239,7 @@ void Resource::configure_for_local_scene(Node *p_for_scene, HashMap<Ref<Resource
List<PropertyInfo> plist;
get_property_list(&plist);
+ reset_local_to_scene();
local_scene = p_for_scene;
for (const PropertyInfo &E : plist) {
@@ -378,8 +379,12 @@ Node *Resource::get_local_scene() const {
}
void Resource::setup_local_to_scene() {
- // Can't use GDVIRTUAL in Resource, so this will have to be done with a signal
emit_signal(SNAME("setup_local_to_scene_requested"));
+ GDVIRTUAL_CALL(_setup_local_to_scene);
+}
+
+void Resource::reset_local_to_scene() {
+ // Restores the state as if setup_local_to_scene() hadn't been called.
}
Node *(*Resource::_get_local_scene_func)() = nullptr;
@@ -455,6 +460,7 @@ void Resource::_bind_methods() {
get_rid_bind.return_val.type = Variant::RID;
::ClassDB::add_virtual_method(get_class_static(), get_rid_bind, true, Vector<String>(), true);
+ GDVIRTUAL_BIND(_setup_local_to_scene);
}
Resource::Resource() :
diff --git a/core/io/resource.h b/core/io/resource.h
index af8c275a1c..a9b1a88f6b 100644
--- a/core/io/resource.h
+++ b/core/io/resource.h
@@ -33,6 +33,7 @@
#include "core/io/resource_uid.h"
#include "core/object/class_db.h"
+#include "core/object/gdvirtual.gen.inc"
#include "core/object/ref_counted.h"
#include "core/templates/safe_refcount.h"
#include "core/templates/self_list.h"
@@ -80,6 +81,9 @@ protected:
void _set_path(const String &p_path);
void _take_over_path(const String &p_path);
+ virtual void reset_local_to_scene();
+ GDVIRTUAL0(_setup_local_to_scene);
+
public:
static Node *(*_get_local_scene_func)(); //used by editor
static void (*_update_configuration_warning)(); //used by editor
diff --git a/core/io/resource_format_binary.cpp b/core/io/resource_format_binary.cpp
index 551d3268b8..ea97e5ecce 100644
--- a/core/io/resource_format_binary.cpp
+++ b/core/io/resource_format_binary.cpp
@@ -36,6 +36,7 @@
#include "core/io/image.h"
#include "core/io/marshalls.h"
#include "core/io/missing_resource.h"
+#include "core/object/script_language.h"
#include "core/version.h"
//#define print_bl(m_what) print_line(m_what)
diff --git a/core/io/resource_loader.cpp b/core/io/resource_loader.cpp
index df0253349c..f7915261af 100644
--- a/core/io/resource_loader.cpp
+++ b/core/io/resource_loader.cpp
@@ -33,6 +33,7 @@
#include "core/config/project_settings.h"
#include "core/io/file_access.h"
#include "core/io/resource_importer.h"
+#include "core/object/script_language.h"
#include "core/os/condition_variable.h"
#include "core/os/os.h"
#include "core/string/print_string.h"
@@ -1113,7 +1114,7 @@ bool ResourceLoader::add_custom_resource_format_loader(String script_path) {
Object *obj = ClassDB::instantiate(ibt);
- ERR_FAIL_COND_V_MSG(obj == nullptr, false, "Cannot instance script as custom resource loader, expected 'ResourceFormatLoader' inheritance, got: " + String(ibt) + ".");
+ ERR_FAIL_NULL_V_MSG(obj, false, "Cannot instance script as custom resource loader, expected 'ResourceFormatLoader' inheritance, got: " + String(ibt) + ".");
Ref<ResourceFormatLoader> crl = Object::cast_to<ResourceFormatLoader>(obj);
crl->set_script(s);
diff --git a/core/io/resource_loader.h b/core/io/resource_loader.h
index 2701caa3f4..3c32a19066 100644
--- a/core/io/resource_loader.h
+++ b/core/io/resource_loader.h
@@ -33,7 +33,6 @@
#include "core/io/resource.h"
#include "core/object/gdvirtual.gen.inc"
-#include "core/object/script_language.h"
#include "core/object/worker_thread_pool.h"
#include "core/os/semaphore.h"
#include "core/os/thread.h"
@@ -231,7 +230,11 @@ public:
// Loaders can safely use this regardless which thread they are running on.
static void notify_dependency_error(const String &p_path, const String &p_dependency, const String &p_type) {
if (dep_err_notify) {
- callable_mp_static(dep_err_notify).bind(p_path, p_dependency, p_type).call_deferred();
+ if (Thread::get_caller_id() == Thread::get_main_id()) {
+ dep_err_notify(p_path, p_dependency, p_type);
+ } else {
+ callable_mp_static(dep_err_notify).bind(p_path, p_dependency, p_type).call_deferred();
+ }
}
}
static void set_dependency_error_notify_func(DependencyErrorNotify p_err_notify) {
diff --git a/core/io/resource_saver.cpp b/core/io/resource_saver.cpp
index 6e377847a8..564a54b913 100644
--- a/core/io/resource_saver.cpp
+++ b/core/io/resource_saver.cpp
@@ -241,7 +241,7 @@ bool ResourceSaver::add_custom_resource_format_saver(String script_path) {
Object *obj = ClassDB::instantiate(ibt);
- ERR_FAIL_COND_V_MSG(obj == nullptr, false, "Cannot instance script as custom resource saver, expected 'ResourceFormatSaver' inheritance, got: " + String(ibt) + ".");
+ ERR_FAIL_NULL_V_MSG(obj, false, "Cannot instance script as custom resource saver, expected 'ResourceFormatSaver' inheritance, got: " + String(ibt) + ".");
Ref<ResourceFormatSaver> crl = Object::cast_to<ResourceFormatSaver>(obj);
crl->set_script(s);
diff --git a/core/io/resource_saver.h b/core/io/resource_saver.h
index 572742d129..4828df297a 100644
--- a/core/io/resource_saver.h
+++ b/core/io/resource_saver.h
@@ -33,7 +33,6 @@
#include "core/io/resource.h"
#include "core/object/gdvirtual.gen.inc"
-#include "core/object/script_language.h"
class ResourceFormatSaver : public RefCounted {
GDCLASS(ResourceFormatSaver, RefCounted);
diff --git a/core/io/stream_peer.h b/core/io/stream_peer.h
index ba11144e33..29cdb82615 100644
--- a/core/io/stream_peer.h
+++ b/core/io/stream_peer.h
@@ -35,7 +35,6 @@
#include "core/extension/ext_wrappers.gen.inc"
#include "core/object/gdvirtual.gen.inc"
-#include "core/object/script_language.h"
#include "core/variant/native_ptr.h"
class StreamPeer : public RefCounted {
diff --git a/core/io/stream_peer_gzip.cpp b/core/io/stream_peer_gzip.cpp
index 74216f3cff..4daa71a22a 100644
--- a/core/io/stream_peer_gzip.cpp
+++ b/core/io/stream_peer_gzip.cpp
@@ -102,7 +102,7 @@ Error StreamPeerGZIP::_start(bool p_compress, bool p_is_deflate, int buffer_size
}
Error StreamPeerGZIP::_process(uint8_t *p_dst, int p_dst_size, const uint8_t *p_src, int p_src_size, int &r_consumed, int &r_out, bool p_close) {
- ERR_FAIL_COND_V(!ctx, ERR_UNCONFIGURED);
+ ERR_FAIL_NULL_V(ctx, ERR_UNCONFIGURED);
z_stream &strm = *(z_stream *)ctx;
strm.avail_in = p_src_size;
strm.avail_out = p_dst_size;
@@ -132,7 +132,7 @@ Error StreamPeerGZIP::put_data(const uint8_t *p_data, int p_bytes) {
}
Error StreamPeerGZIP::put_partial_data(const uint8_t *p_data, int p_bytes, int &r_sent) {
- ERR_FAIL_COND_V(!ctx, ERR_UNCONFIGURED);
+ ERR_FAIL_NULL_V(ctx, ERR_UNCONFIGURED);
ERR_FAIL_COND_V(p_bytes < 0, ERR_INVALID_PARAMETER);
// Ensure we have enough space in temporary buffer.
diff --git a/core/io/xml_parser.cpp b/core/io/xml_parser.cpp
index 958734addf..faf7d75172 100644
--- a/core/io/xml_parser.cpp
+++ b/core/io/xml_parser.cpp
@@ -336,7 +336,7 @@ uint64_t XMLParser::get_node_offset() const {
}
Error XMLParser::seek(uint64_t p_pos) {
- ERR_FAIL_COND_V(!data, ERR_FILE_EOF);
+ ERR_FAIL_NULL_V(data, ERR_FILE_EOF);
ERR_FAIL_COND_V(p_pos >= length, ERR_FILE_EOF);
P = data + p_pos;
@@ -474,7 +474,7 @@ Error XMLParser::open_buffer(const Vector<uint8_t> &p_buffer) {
Error XMLParser::_open_buffer(const uint8_t *p_buffer, size_t p_size) {
ERR_FAIL_COND_V(p_size == 0, ERR_INVALID_DATA);
- ERR_FAIL_COND_V(!p_buffer, ERR_INVALID_DATA);
+ ERR_FAIL_NULL_V(p_buffer, ERR_INVALID_DATA);
if (data_copy) {
memdelete_arr(data_copy);
diff --git a/core/io/zip_io.cpp b/core/io/zip_io.cpp
index a0e6bd62de..a89c6253f1 100644
--- a/core/io/zip_io.cpp
+++ b/core/io/zip_io.cpp
@@ -74,7 +74,7 @@ int godot_unzip_locate_file(unzFile p_zip_file, String p_filepath, bool p_case_s
void *zipio_open(voidpf opaque, const char *p_fname, int mode) {
Ref<FileAccess> *fa = reinterpret_cast<Ref<FileAccess> *>(opaque);
- ERR_FAIL_COND_V(fa == nullptr, nullptr);
+ ERR_FAIL_NULL_V(fa, nullptr);
String fname;
fname.parse_utf8(p_fname);
@@ -100,7 +100,7 @@ void *zipio_open(voidpf opaque, const char *p_fname, int mode) {
uLong zipio_read(voidpf opaque, voidpf stream, void *buf, uLong size) {
Ref<FileAccess> *fa = reinterpret_cast<Ref<FileAccess> *>(opaque);
- ERR_FAIL_COND_V(fa == nullptr, 0);
+ ERR_FAIL_NULL_V(fa, 0);
ERR_FAIL_COND_V(fa->is_null(), 0);
return (*fa)->get_buffer((uint8_t *)buf, size);
@@ -108,7 +108,7 @@ uLong zipio_read(voidpf opaque, voidpf stream, void *buf, uLong size) {
uLong zipio_write(voidpf opaque, voidpf stream, const void *buf, uLong size) {
Ref<FileAccess> *fa = reinterpret_cast<Ref<FileAccess> *>(opaque);
- ERR_FAIL_COND_V(fa == nullptr, 0);
+ ERR_FAIL_NULL_V(fa, 0);
ERR_FAIL_COND_V(fa->is_null(), 0);
(*fa)->store_buffer((uint8_t *)buf, size);
@@ -117,7 +117,7 @@ uLong zipio_write(voidpf opaque, voidpf stream, const void *buf, uLong size) {
long zipio_tell(voidpf opaque, voidpf stream) {
Ref<FileAccess> *fa = reinterpret_cast<Ref<FileAccess> *>(opaque);
- ERR_FAIL_COND_V(fa == nullptr, 0);
+ ERR_FAIL_NULL_V(fa, 0);
ERR_FAIL_COND_V(fa->is_null(), 0);
return (*fa)->get_position();
@@ -125,7 +125,7 @@ long zipio_tell(voidpf opaque, voidpf stream) {
long zipio_seek(voidpf opaque, voidpf stream, uLong offset, int origin) {
Ref<FileAccess> *fa = reinterpret_cast<Ref<FileAccess> *>(opaque);
- ERR_FAIL_COND_V(fa == nullptr, 0);
+ ERR_FAIL_NULL_V(fa, 0);
ERR_FAIL_COND_V(fa->is_null(), 0);
uint64_t pos = offset;
@@ -146,7 +146,7 @@ long zipio_seek(voidpf opaque, voidpf stream, uLong offset, int origin) {
int zipio_close(voidpf opaque, voidpf stream) {
Ref<FileAccess> *fa = reinterpret_cast<Ref<FileAccess> *>(opaque);
- ERR_FAIL_COND_V(fa == nullptr, 0);
+ ERR_FAIL_NULL_V(fa, 0);
ERR_FAIL_COND_V(fa->is_null(), 0);
fa->unref();
@@ -155,7 +155,7 @@ int zipio_close(voidpf opaque, voidpf stream) {
int zipio_testerror(voidpf opaque, voidpf stream) {
Ref<FileAccess> *fa = reinterpret_cast<Ref<FileAccess> *>(opaque);
- ERR_FAIL_COND_V(fa == nullptr, 1);
+ ERR_FAIL_NULL_V(fa, 1);
ERR_FAIL_COND_V(fa->is_null(), 0);
return (fa->is_valid() && (*fa)->get_error() != OK) ? 1 : 0;
diff --git a/core/math/a_star.h b/core/math/a_star.h
index fc4bb09f03..0758500c8a 100644
--- a/core/math/a_star.h
+++ b/core/math/a_star.h
@@ -33,7 +33,6 @@
#include "core/object/gdvirtual.gen.inc"
#include "core/object/ref_counted.h"
-#include "core/object/script_language.h"
#include "core/templates/oa_hash_map.h"
/**
diff --git a/core/math/a_star_grid_2d.cpp b/core/math/a_star_grid_2d.cpp
index 9ba4c2ff9a..379d34aa2a 100644
--- a/core/math/a_star_grid_2d.cpp
+++ b/core/math/a_star_grid_2d.cpp
@@ -32,9 +32,7 @@
#include "core/variant/typed_array.h"
-#define GET_POINT_UNCHECKED(m_id) points[m_id.y - region.position.y][m_id.x - region.position.x]
-
-static real_t heuristic_euclidian(const Vector2i &p_from, const Vector2i &p_to) {
+static real_t heuristic_euclidean(const Vector2i &p_from, const Vector2i &p_to) {
real_t dx = (real_t)ABS(p_to.x - p_from.x);
real_t dy = (real_t)ABS(p_to.y - p_from.y);
return (real_t)Math::sqrt(dx * dx + dy * dy);
@@ -59,7 +57,7 @@ static real_t heuristic_chebyshev(const Vector2i &p_from, const Vector2i &p_to)
return MAX(dx, dy);
}
-static real_t (*heuristics[AStarGrid2D::HEURISTIC_MAX])(const Vector2i &, const Vector2i &) = { heuristic_euclidian, heuristic_manhattan, heuristic_octile, heuristic_chebyshev };
+static real_t (*heuristics[AStarGrid2D::HEURISTIC_MAX])(const Vector2i &, const Vector2i &) = { heuristic_euclidean, heuristic_manhattan, heuristic_octile, heuristic_chebyshev };
void AStarGrid2D::set_region(const Rect2i &p_region) {
ERR_FAIL_COND(p_region.size.x < 0 || p_region.size.y < 0);
@@ -110,19 +108,22 @@ Size2 AStarGrid2D::get_cell_size() const {
void AStarGrid2D::update() {
points.clear();
- const int64_t end_x = region.position.x + region.size.width;
- const int64_t end_y = region.position.y + region.size.height;
- for (int64_t y = region.position.y; y < end_y; y++) {
+
+ const int32_t end_x = region.get_end().x;
+ const int32_t end_y = region.get_end().y;
+
+ for (int32_t y = region.position.y; y < end_y; y++) {
LocalVector<Point> line;
- for (int64_t x = region.position.x; x < end_x; x++) {
+ for (int32_t x = region.position.x; x < end_x; x++) {
line.push_back(Point(Vector2i(x, y), offset + Vector2(x, y) * cell_size));
}
points.push_back(line);
}
+
dirty = false;
}
-bool AStarGrid2D::is_in_bounds(int p_x, int p_y) const {
+bool AStarGrid2D::is_in_bounds(int32_t p_x, int32_t p_y) const {
return region.has_point(Vector2i(p_x, p_y));
}
@@ -172,40 +173,38 @@ AStarGrid2D::Heuristic AStarGrid2D::get_default_estimate_heuristic() const {
void AStarGrid2D::set_point_solid(const Vector2i &p_id, bool p_solid) {
ERR_FAIL_COND_MSG(dirty, "Grid is not initialized. Call the update method.");
ERR_FAIL_COND_MSG(!is_in_boundsv(p_id), vformat("Can't set if point is disabled. Point %s out of bounds %s.", p_id, region));
- GET_POINT_UNCHECKED(p_id).solid = p_solid;
+ _get_point_unchecked(p_id)->solid = p_solid;
}
bool AStarGrid2D::is_point_solid(const Vector2i &p_id) const {
ERR_FAIL_COND_V_MSG(dirty, false, "Grid is not initialized. Call the update method.");
ERR_FAIL_COND_V_MSG(!is_in_boundsv(p_id), false, vformat("Can't get if point is disabled. Point %s out of bounds %s.", p_id, region));
- return GET_POINT_UNCHECKED(p_id).solid;
+ return _get_point_unchecked(p_id)->solid;
}
void AStarGrid2D::set_point_weight_scale(const Vector2i &p_id, real_t p_weight_scale) {
ERR_FAIL_COND_MSG(dirty, "Grid is not initialized. Call the update method.");
ERR_FAIL_COND_MSG(!is_in_boundsv(p_id), vformat("Can't set point's weight scale. Point %s out of bounds %s.", p_id, region));
ERR_FAIL_COND_MSG(p_weight_scale < 0.0, vformat("Can't set point's weight scale less than 0.0: %f.", p_weight_scale));
- GET_POINT_UNCHECKED(p_id).weight_scale = p_weight_scale;
+ _get_point_unchecked(p_id)->weight_scale = p_weight_scale;
}
real_t AStarGrid2D::get_point_weight_scale(const Vector2i &p_id) const {
ERR_FAIL_COND_V_MSG(dirty, 0, "Grid is not initialized. Call the update method.");
ERR_FAIL_COND_V_MSG(!is_in_boundsv(p_id), 0, vformat("Can't get point's weight scale. Point %s out of bounds %s.", p_id, region));
- return GET_POINT_UNCHECKED(p_id).weight_scale;
+ return _get_point_unchecked(p_id)->weight_scale;
}
void AStarGrid2D::fill_solid_region(const Rect2i &p_region, bool p_solid) {
ERR_FAIL_COND_MSG(dirty, "Grid is not initialized. Call the update method.");
- Rect2i safe_region = p_region.intersection(region);
- int from_x = safe_region.get_position().x;
- int from_y = safe_region.get_position().y;
- int end_x = safe_region.get_end().x;
- int end_y = safe_region.get_end().y;
+ const Rect2i safe_region = p_region.intersection(region);
+ const int32_t end_x = safe_region.get_end().x;
+ const int32_t end_y = safe_region.get_end().y;
- for (int x = from_x; x < end_x; x++) {
- for (int y = from_y; y < end_y; y++) {
- GET_POINT_UNCHECKED(Vector2i(x, y)).solid = p_solid;
+ for (int32_t y = safe_region.position.y; y < end_y; y++) {
+ for (int32_t x = safe_region.position.x; x < end_x; x++) {
+ _get_point_unchecked(x, y)->solid = p_solid;
}
}
}
@@ -214,14 +213,13 @@ void AStarGrid2D::fill_weight_scale_region(const Rect2i &p_region, real_t p_weig
ERR_FAIL_COND_MSG(dirty, "Grid is not initialized. Call the update method.");
ERR_FAIL_COND_MSG(p_weight_scale < 0.0, vformat("Can't set point's weight scale less than 0.0: %f.", p_weight_scale));
- Rect2i safe_region = p_region.intersection(region);
- int from_x = safe_region.get_position().x;
- int from_y = safe_region.get_position().y;
- int end_x = safe_region.get_end().x;
- int end_y = safe_region.get_end().y;
- for (int x = from_x; x < end_x; x++) {
- for (int y = from_y; y < end_y; y++) {
- GET_POINT_UNCHECKED(Vector2i(x, y)).weight_scale = p_weight_scale;
+ const Rect2i safe_region = p_region.intersection(region);
+ const int32_t end_x = safe_region.get_end().x;
+ const int32_t end_y = safe_region.get_end().y;
+
+ for (int32_t y = safe_region.position.y; y < end_y; y++) {
+ for (int32_t x = safe_region.position.x; x < end_x; x++) {
+ _get_point_unchecked(x, y)->weight_scale = p_weight_scale;
}
}
}
@@ -234,14 +232,14 @@ AStarGrid2D::Point *AStarGrid2D::_jump(Point *p_from, Point *p_to) {
return p_to;
}
- int64_t from_x = p_from->id.x;
- int64_t from_y = p_from->id.y;
+ int32_t from_x = p_from->id.x;
+ int32_t from_y = p_from->id.y;
- int64_t to_x = p_to->id.x;
- int64_t to_y = p_to->id.y;
+ int32_t to_x = p_to->id.x;
+ int32_t to_y = p_to->id.y;
- int64_t dx = to_x - from_x;
- int64_t dy = to_y - from_y;
+ int32_t dx = to_x - from_x;
+ int32_t dy = to_y - from_y;
if (diagonal_mode == DIAGONAL_MODE_ALWAYS || diagonal_mode == DIAGONAL_MODE_AT_LEAST_ONE_WALKABLE) {
if (dx != 0 && dy != 0) {
@@ -516,7 +514,7 @@ void AStarGrid2D::clear() {
Vector2 AStarGrid2D::get_point_position(const Vector2i &p_id) const {
ERR_FAIL_COND_V_MSG(dirty, Vector2(), "Grid is not initialized. Call the update method.");
ERR_FAIL_COND_V_MSG(!is_in_boundsv(p_id), Vector2(), vformat("Can't get point's position. Point %s out of bounds %s.", p_id, region));
- return GET_POINT_UNCHECKED(p_id).pos;
+ return _get_point_unchecked(p_id)->pos;
}
Vector<Vector2> AStarGrid2D::get_point_path(const Vector2i &p_from_id, const Vector2i &p_to_id) {
@@ -542,7 +540,7 @@ Vector<Vector2> AStarGrid2D::get_point_path(const Vector2i &p_from_id, const Vec
}
Point *p = end_point;
- int64_t pc = 1;
+ int32_t pc = 1;
while (p != begin_point) {
pc++;
p = p->prev_point;
@@ -555,7 +553,7 @@ Vector<Vector2> AStarGrid2D::get_point_path(const Vector2i &p_from_id, const Vec
Vector2 *w = path.ptrw();
p = end_point;
- int64_t idx = pc - 1;
+ int32_t idx = pc - 1;
while (p != begin_point) {
w[idx--] = p->pos;
p = p->prev_point;
@@ -590,7 +588,7 @@ TypedArray<Vector2i> AStarGrid2D::get_id_path(const Vector2i &p_from_id, const V
}
Point *p = end_point;
- int64_t pc = 1;
+ int32_t pc = 1;
while (p != begin_point) {
pc++;
p = p->prev_point;
@@ -601,7 +599,7 @@ TypedArray<Vector2i> AStarGrid2D::get_id_path(const Vector2i &p_from_id, const V
{
p = end_point;
- int64_t idx = pc - 1;
+ int32_t idx = pc - 1;
while (p != begin_point) {
path[idx--] = p->id;
p = p->prev_point;
@@ -671,5 +669,3 @@ void AStarGrid2D::_bind_methods() {
BIND_ENUM_CONSTANT(DIAGONAL_MODE_ONLY_IF_NO_OBSTACLES);
BIND_ENUM_CONSTANT(DIAGONAL_MODE_MAX);
}
-
-#undef GET_POINT_UNCHECKED
diff --git a/core/math/a_star_grid_2d.h b/core/math/a_star_grid_2d.h
index dd5f9d0575..619551b754 100644
--- a/core/math/a_star_grid_2d.h
+++ b/core/math/a_star_grid_2d.h
@@ -33,7 +33,6 @@
#include "core/object/gdvirtual.gen.inc"
#include "core/object/ref_counted.h"
-#include "core/object/script_language.h"
#include "core/templates/list.h"
#include "core/templates/local_vector.h"
@@ -106,24 +105,32 @@ private:
uint64_t pass = 1;
private: // Internal routines.
- _FORCE_INLINE_ bool _is_walkable(int64_t p_x, int64_t p_y) const {
+ _FORCE_INLINE_ bool _is_walkable(int32_t p_x, int32_t p_y) const {
if (region.has_point(Vector2i(p_x, p_y))) {
return !points[p_y - region.position.y][p_x - region.position.x].solid;
}
return false;
}
- _FORCE_INLINE_ Point *_get_point(int64_t p_x, int64_t p_y) {
+ _FORCE_INLINE_ Point *_get_point(int32_t p_x, int32_t p_y) {
if (region.has_point(Vector2i(p_x, p_y))) {
return &points[p_y - region.position.y][p_x - region.position.x];
}
return nullptr;
}
- _FORCE_INLINE_ Point *_get_point_unchecked(int64_t p_x, int64_t p_y) {
+ _FORCE_INLINE_ Point *_get_point_unchecked(int32_t p_x, int32_t p_y) {
return &points[p_y - region.position.y][p_x - region.position.x];
}
+ _FORCE_INLINE_ Point *_get_point_unchecked(const Vector2i &p_id) {
+ return &points[p_id.y - region.position.y][p_id.x - region.position.x];
+ }
+
+ _FORCE_INLINE_ const Point *_get_point_unchecked(const Vector2i &p_id) const {
+ return &points[p_id.y - region.position.y][p_id.x - region.position.x];
+ }
+
void _get_nbors(Point *p_point, LocalVector<Point *> &r_nbors);
Point *_jump(Point *p_from, Point *p_to);
bool _solve(Point *p_begin_point, Point *p_end_point);
@@ -152,10 +159,7 @@ public:
void update();
- int get_width() const;
- int get_height() const;
-
- bool is_in_bounds(int p_x, int p_y) const;
+ bool is_in_bounds(int32_t p_x, int32_t p_y) const;
bool is_in_boundsv(const Vector2i &p_id) const;
bool is_dirty() const;
diff --git a/core/math/basis.cpp b/core/math/basis.cpp
index 6b0ecadc7f..9796ac59c2 100644
--- a/core/math/basis.cpp
+++ b/core/math/basis.cpp
@@ -96,6 +96,14 @@ bool Basis::is_orthogonal() const {
return m.is_equal_approx(identity);
}
+bool Basis::is_conformal() const {
+ const Vector3 x = get_column(0);
+ const Vector3 y = get_column(1);
+ const Vector3 z = get_column(2);
+ const real_t x_len_sq = x.length_squared();
+ return Math::is_equal_approx(x_len_sq, y.length_squared()) && Math::is_equal_approx(x_len_sq, z.length_squared()) && Math::is_zero_approx(x.dot(y)) && Math::is_zero_approx(x.dot(z)) && Math::is_zero_approx(y.dot(z));
+}
+
bool Basis::is_diagonal() const {
return (
Math::is_zero_approx(rows[0][1]) && Math::is_zero_approx(rows[0][2]) &&
diff --git a/core/math/basis.h b/core/math/basis.h
index 1a68bee686..adacd1c216 100644
--- a/core/math/basis.h
+++ b/core/math/basis.h
@@ -138,6 +138,7 @@ struct _NO_DISCARD_ Basis {
_FORCE_INLINE_ Basis operator*(const real_t p_val) const;
bool is_orthogonal() const;
+ bool is_conformal() const;
bool is_diagonal() const;
bool is_rotation() const;
diff --git a/core/math/bvh_refit.inc b/core/math/bvh_refit.inc
index 717a3438c7..b20b805bb0 100644
--- a/core/math/bvh_refit.inc
+++ b/core/math/bvh_refit.inc
@@ -134,7 +134,7 @@ void refit_branch(uint32_t p_node_id) {
TLeaf &leaf = _node_get_leaf(tnode);
if (leaf.is_dirty()) {
leaf.set_dirty(false);
- refit_upward(p_node_id);
+ refit_upward(rp.node_id);
}
}
} // while more nodes to pop
diff --git a/core/math/bvh_split.inc b/core/math/bvh_split.inc
index 875abedb70..2c85a63575 100644
--- a/core/math/bvh_split.inc
+++ b/core/math/bvh_split.inc
@@ -20,8 +20,8 @@ void _split_leaf_sort_groups_simple(int &num_a, int &num_b, uint16_t *group_a, u
group_b[num_b++] = ind;
// remove from a
- group_a[0] = group_a[num_a - 1];
num_a--;
+ group_a[0] = group_a[num_a];
return;
}
@@ -30,15 +30,15 @@ void _split_leaf_sort_groups_simple(int &num_a, int &num_b, uint16_t *group_a, u
int order[POINT::AXIS_COUNT];
- order[0] = size.min_axis_index();
- order[POINT::AXIS_COUNT - 1] = size.max_axis_index();
+ order[0] = size.max_axis_index(); // The longest axis.
+ order[POINT::AXIS_COUNT - 1] = size.min_axis_index(); // The shortest axis.
static_assert(POINT::AXIS_COUNT <= 3, "BVH POINT::AXIS_COUNT has unexpected size");
if constexpr (POINT::AXIS_COUNT == 3) {
order[1] = 3 - (order[0] + order[2]);
}
- // simplest case, split on the longest axis
+ // Simplest case, split on the longest axis.
int split_axis = order[0];
for (int a = 0; a < num_a; a++) {
uint32_t ind = group_a[a];
@@ -48,8 +48,8 @@ void _split_leaf_sort_groups_simple(int &num_a, int &num_b, uint16_t *group_a, u
group_b[num_b++] = ind;
// remove from a
- group_a[a] = group_a[num_a - 1];
num_a--;
+ group_a[a] = group_a[num_a];
// do this one again, as it has been replaced
a--;
@@ -67,7 +67,7 @@ void _split_leaf_sort_groups_simple(int &num_a, int &num_b, uint16_t *group_a, u
}
num_b = 0;
- // now calculate the best split
+ // Now calculate the best split.
for (int axis = 1; axis < POINT::AXIS_COUNT; axis++) {
split_axis = order[axis];
int count = 0;
@@ -105,8 +105,8 @@ void _split_leaf_sort_groups_simple(int &num_a, int &num_b, uint16_t *group_a, u
group_b[num_b++] = ind;
// remove from a
- group_a[a] = group_a[num_a - 1];
num_a--;
+ group_a[a] = group_a[num_a];
// do this one again, as it has been replaced
a--;
@@ -123,8 +123,8 @@ void _split_leaf_sort_groups_simple(int &num_a, int &num_b, uint16_t *group_a, u
group_b[num_b++] = ind;
// remove from a
- group_a[0] = group_a[num_a - 1];
num_a--;
+ group_a[0] = group_a[num_a];
}
// opposite problem! :)
if (!num_a) {
@@ -134,8 +134,8 @@ void _split_leaf_sort_groups_simple(int &num_a, int &num_b, uint16_t *group_a, u
group_a[num_a++] = ind;
// remove from b
- group_b[0] = group_b[num_b - 1];
num_b--;
+ group_b[0] = group_b[num_b];
}
}
diff --git a/core/math/bvh_structs.inc b/core/math/bvh_structs.inc
index 06f6e5d05d..d40c631ce2 100644
--- a/core/math/bvh_structs.inc
+++ b/core/math/bvh_structs.inc
@@ -83,7 +83,7 @@ public:
void clear() {
num_items = 0;
- set_dirty(true);
+ set_dirty(false);
}
bool is_full() const { return num_items >= MAX_ITEMS; }
diff --git a/core/math/convex_hull.cpp b/core/math/convex_hull.cpp
index f8456ec998..68d995fe67 100644
--- a/core/math/convex_hull.cpp
+++ b/core/math/convex_hull.cpp
@@ -2278,7 +2278,7 @@ Error ConvexHullComputer::convex_hull(const Vector<Vector3> &p_points, Geometry3
uint32_t edges_copied = 0;
for (uint32_t i = 0; i < ch.edges.size(); i++) {
- ERR_CONTINUE(edge_faces[i] == -1); // Sanity check
+ ERR_CONTINUE(edge_faces[i] == -1); // Safety check.
uint32_t a = (&ch.edges[i])->get_source_vertex();
uint32_t b = (&ch.edges[i])->get_target_vertex();
diff --git a/core/math/delaunay_3d.h b/core/math/delaunay_3d.h
index 55923e0133..7df8c37e3c 100644
--- a/core/math/delaunay_3d.h
+++ b/core/math/delaunay_3d.h
@@ -105,8 +105,8 @@ class Delaunay3D {
};
_FORCE_INLINE_ static void circum_sphere_compute(const Vector3 *p_points, Simplex *p_simplex) {
- // the only part in the algorithm where there may be precision errors is this one, so ensure that
- // we do it as maximum precision as possible
+ // The only part in the algorithm where there may be precision errors is this one,
+ // so ensure that we do it with the maximum precision possible.
R128 v0_x = p_points[p_simplex->points[0]].x;
R128 v0_y = p_points[p_simplex->points[0]].y;
@@ -121,7 +121,7 @@ class Delaunay3D {
R128 v3_y = p_points[p_simplex->points[3]].y;
R128 v3_z = p_points[p_simplex->points[3]].z;
- //Create the rows of our "unrolled" 3x3 matrix
+ // Create the rows of our "unrolled" 3x3 matrix.
R128 row1_x = v1_x - v0_x;
R128 row1_y = v1_y - v0_y;
R128 row1_z = v1_z - v0_z;
@@ -138,10 +138,10 @@ class Delaunay3D {
R128 sq_lenght2 = row2_x * row2_x + row2_y * row2_y + row2_z * row2_z;
R128 sq_lenght3 = row3_x * row3_x + row3_y * row3_y + row3_z * row3_z;
- //Compute the determinant of said matrix
+ // Compute the determinant of said matrix.
R128 determinant = row1_x * (row2_y * row3_z - row3_y * row2_z) - row2_x * (row1_y * row3_z - row3_y * row1_z) + row3_x * (row1_y * row2_z - row2_y * row1_z);
- // Compute the volume of the tetrahedron, and precompute a scalar quantity for re-use in the formula
+ // Compute the volume of the tetrahedron, and precompute a scalar quantity for reuse in the formula.
R128 volume = determinant / R128(6.f);
R128 i12volume = R128(1.f) / (volume * R128(12.f));
@@ -149,8 +149,7 @@ class Delaunay3D {
R128 center_y = v0_y + i12volume * (-(row2_x * row3_z - row3_x * row2_z) * sq_lenght1 + (row1_x * row3_z - row3_x * row1_z) * sq_lenght2 - (row1_x * row2_z - row2_x * row1_z) * sq_lenght3);
R128 center_z = v0_z + i12volume * ((row2_x * row3_y - row3_x * row2_y) * sq_lenght1 - (row1_x * row3_y - row3_x * row1_y) * sq_lenght2 + (row1_x * row2_y - row2_x * row1_y) * sq_lenght3);
- //Once we know the center, the radius is clearly the distance to any vertex
-
+ // Once we know the center, the radius is clearly the distance to any vertex.
R128 rel1_x = center_x - v0_x;
R128 rel1_y = center_y - v0_y;
R128 rel1_z = center_z - v0_z;
diff --git a/core/math/disjoint_set.h b/core/math/disjoint_set.h
index 78ae77e72c..2ece991fea 100644
--- a/core/math/disjoint_set.h
+++ b/core/math/disjoint_set.h
@@ -134,7 +134,7 @@ void DisjointSet<T, H, C, AL>::get_representatives(Vector<T> &out_representative
template <typename T, class H, class C, class AL>
void DisjointSet<T, H, C, AL>::get_members(Vector<T> &out_members, T representative) {
typename MapT::Iterator rep_itr = elements.find(representative);
- ERR_FAIL_COND(rep_itr == nullptr);
+ ERR_FAIL_NULL(rep_itr);
Element *rep_element = rep_itr->value;
ERR_FAIL_COND(rep_element->parent != rep_element);
diff --git a/core/math/dynamic_bvh.h b/core/math/dynamic_bvh.h
index 21b5340aaa..dbc1cb31de 100644
--- a/core/math/dynamic_bvh.h
+++ b/core/math/dynamic_bvh.h
@@ -190,7 +190,7 @@ private:
_FORCE_INLINE_ bool is_internal() const { return (!is_leaf()); }
_FORCE_INLINE_ int get_index_in_parent() const {
- ERR_FAIL_COND_V(!parent, 0);
+ ERR_FAIL_NULL_V(parent, 0);
return (parent->children[1] == this) ? 1 : 0;
}
void get_max_depth(int depth, int &maxdepth) {
diff --git a/core/math/math_funcs.h b/core/math/math_funcs.h
index f96d3a909f..366ccca4cb 100644
--- a/core/math/math_funcs.h
+++ b/core/math/math_funcs.h
@@ -88,6 +88,17 @@ public:
static _ALWAYS_INLINE_ double atan2(double p_y, double p_x) { return ::atan2(p_y, p_x); }
static _ALWAYS_INLINE_ float atan2(float p_y, float p_x) { return ::atan2f(p_y, p_x); }
+ static _ALWAYS_INLINE_ double asinh(double p_x) { return ::asinh(p_x); }
+ static _ALWAYS_INLINE_ float asinh(float p_x) { return ::asinhf(p_x); }
+
+ // Always does clamping so always safe to use.
+ static _ALWAYS_INLINE_ double acosh(double p_x) { return p_x < 1 ? 0 : ::acosh(p_x); }
+ static _ALWAYS_INLINE_ float acosh(float p_x) { return p_x < 1 ? 0 : ::acoshf(p_x); }
+
+ // Always does clamping so always safe to use.
+ static _ALWAYS_INLINE_ double atanh(double p_x) { return p_x <= -1 ? -INFINITY : (p_x >= 1 ? INFINITY : ::atanh(p_x)); }
+ static _ALWAYS_INLINE_ float atanh(float p_x) { return p_x <= -1 ? -INFINITY : (p_x >= 1 ? INFINITY : ::atanhf(p_x)); }
+
static _ALWAYS_INLINE_ double sqrt(double p_x) { return ::sqrt(p_x); }
static _ALWAYS_INLINE_ float sqrt(float p_x) { return ::sqrtf(p_x); }
@@ -388,15 +399,20 @@ public:
return d;
}
- static _ALWAYS_INLINE_ double lerp_angle(double p_from, double p_to, double p_weight) {
+ static _ALWAYS_INLINE_ double angle_difference(double p_from, double p_to) {
double difference = fmod(p_to - p_from, Math_TAU);
- double distance = fmod(2.0 * difference, Math_TAU) - difference;
- return p_from + distance * p_weight;
+ return fmod(2.0 * difference, Math_TAU) - difference;
}
- static _ALWAYS_INLINE_ float lerp_angle(float p_from, float p_to, float p_weight) {
+ static _ALWAYS_INLINE_ float angle_difference(float p_from, float p_to) {
float difference = fmod(p_to - p_from, (float)Math_TAU);
- float distance = fmod(2.0f * difference, (float)Math_TAU) - difference;
- return p_from + distance * p_weight;
+ return fmod(2.0f * difference, (float)Math_TAU) - difference;
+ }
+
+ static _ALWAYS_INLINE_ double lerp_angle(double p_from, double p_to, double p_weight) {
+ return p_from + Math::angle_difference(p_from, p_to) * p_weight;
+ }
+ static _ALWAYS_INLINE_ float lerp_angle(float p_from, float p_to, float p_weight) {
+ return p_from + Math::angle_difference(p_from, p_to) * p_weight;
}
static _ALWAYS_INLINE_ double inverse_lerp(double p_from, double p_to, double p_value) {
@@ -427,6 +443,7 @@ public:
float s = CLAMP((p_s - p_from) / (p_to - p_from), 0.0f, 1.0f);
return s * s * (3.0f - 2.0f * s);
}
+
static _ALWAYS_INLINE_ double move_toward(double p_from, double p_to, double p_delta) {
return abs(p_to - p_from) <= p_delta ? p_to : p_from + SIGN(p_to - p_from) * p_delta;
}
@@ -434,6 +451,19 @@ public:
return abs(p_to - p_from) <= p_delta ? p_to : p_from + SIGN(p_to - p_from) * p_delta;
}
+ static _ALWAYS_INLINE_ double rotate_toward(double p_from, double p_to, double p_delta) {
+ double difference = Math::angle_difference(p_from, p_to);
+ double abs_difference = Math::abs(difference);
+ // When `p_delta < 0` move no further than to PI radians away from `p_to` (as PI is the max possible angle distance).
+ return p_from + CLAMP(p_delta, abs_difference - Math_PI, abs_difference) * (difference >= 0.0 ? 1.0 : -1.0);
+ }
+ static _ALWAYS_INLINE_ float rotate_toward(float p_from, float p_to, float p_delta) {
+ float difference = Math::angle_difference(p_from, p_to);
+ float abs_difference = Math::abs(difference);
+ // When `p_delta < 0` move no further than to PI radians away from `p_to` (as PI is the max possible angle distance).
+ return p_from + CLAMP(p_delta, abs_difference - (float)Math_PI, abs_difference) * (difference >= 0.0f ? 1.0f : -1.0f);
+ }
+
static _ALWAYS_INLINE_ double linear_to_db(double p_linear) {
return Math::log(p_linear) * 8.6858896380650365530225783783321;
}
diff --git a/core/math/projection.cpp b/core/math/projection.cpp
index a304318e2e..9d5dc8b4d6 100644
--- a/core/math/projection.cpp
+++ b/core/math/projection.cpp
@@ -408,7 +408,6 @@ real_t Projection::get_z_far() const {
matrix[11] - matrix[10],
matrix[15] - matrix[14]);
- new_plane.normal = -new_plane.normal;
new_plane.normalize();
return new_plane.d;
diff --git a/core/math/transform_2d.cpp b/core/math/transform_2d.cpp
index a0187e00b1..bc4682fd90 100644
--- a/core/math/transform_2d.cpp
+++ b/core/math/transform_2d.cpp
@@ -164,6 +164,18 @@ Transform2D Transform2D::orthonormalized() const {
return ortho;
}
+bool Transform2D::is_conformal() const {
+ // Non-flipped case.
+ if (Math::is_equal_approx(columns[0][0], columns[1][1]) && Math::is_equal_approx(columns[0][1], -columns[1][0])) {
+ return true;
+ }
+ // Flipped case.
+ if (Math::is_equal_approx(columns[0][0], -columns[1][1]) && Math::is_equal_approx(columns[0][1], columns[1][0])) {
+ return true;
+ }
+ return false;
+}
+
bool Transform2D::is_equal_approx(const Transform2D &p_transform) const {
return columns[0].is_equal_approx(p_transform.columns[0]) && columns[1].is_equal_approx(p_transform.columns[1]) && columns[2].is_equal_approx(p_transform.columns[2]);
}
diff --git a/core/math/transform_2d.h b/core/math/transform_2d.h
index c511034669..dd1a33c5d5 100644
--- a/core/math/transform_2d.h
+++ b/core/math/transform_2d.h
@@ -96,6 +96,7 @@ struct _NO_DISCARD_ Transform2D {
void orthonormalize();
Transform2D orthonormalized() const;
+ bool is_conformal() const;
bool is_equal_approx(const Transform2D &p_transform) const;
bool is_finite() const;
diff --git a/core/object/callable_method_pointer.h b/core/object/callable_method_pointer.h
index 2dbb7e468e..db78b982e4 100644
--- a/core/object/callable_method_pointer.h
+++ b/core/object/callable_method_pointer.h
@@ -99,7 +99,7 @@ public:
virtual void call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const {
#ifdef DEBUG_ENABLED
- ERR_FAIL_COND_MSG(ObjectDB::get_instance(ObjectID(data.object_id)) == nullptr, "Invalid Object id '" + uitos(data.object_id) + "', can't call method.");
+ ERR_FAIL_NULL_MSG(ObjectDB::get_instance(ObjectID(data.object_id)), "Invalid Object id '" + uitos(data.object_id) + "', can't call method.");
#endif
call_with_variant_args(data.instance, data.method, p_arguments, p_argcount, r_call_error);
}
@@ -154,7 +154,7 @@ public:
virtual void call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const {
#ifdef DEBUG_ENABLED
- ERR_FAIL_COND_MSG(ObjectDB::get_instance(ObjectID(data.object_id)) == nullptr, "Invalid Object id '" + uitos(data.object_id) + "', can't call method.");
+ ERR_FAIL_NULL_MSG(ObjectDB::get_instance(ObjectID(data.object_id)), "Invalid Object id '" + uitos(data.object_id) + "', can't call method.");
#endif
call_with_variant_args_ret(data.instance, data.method, p_arguments, p_argcount, r_return_value, r_call_error);
}
@@ -209,7 +209,7 @@ public:
virtual void call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const override {
#ifdef DEBUG_ENABLED
- ERR_FAIL_COND_MSG(ObjectDB::get_instance(ObjectID(data.object_id)) == nullptr, "Invalid Object id '" + uitos(data.object_id) + "', can't call method.");
+ ERR_FAIL_NULL_MSG(ObjectDB::get_instance(ObjectID(data.object_id)), "Invalid Object id '" + uitos(data.object_id) + "', can't call method.");
#endif
call_with_variant_args_retc(data.instance, data.method, p_arguments, p_argcount, r_return_value, r_call_error);
}
diff --git a/core/object/class_db.cpp b/core/object/class_db.cpp
index c8c50fb957..c594f4a9b4 100644
--- a/core/object/class_db.cpp
+++ b/core/object/class_db.cpp
@@ -31,6 +31,7 @@
#include "class_db.h"
#include "core/config/engine.h"
+#include "core/io/resource_loader.h"
#include "core/object/script_language.h"
#include "core/os/mutex.h"
#include "core/version.h"
@@ -144,7 +145,7 @@ StringName ClassDB::get_compatibility_remapped_class(const StringName &p_class)
StringName ClassDB::_get_parent_class(const StringName &p_class) {
ClassInfo *ti = classes.getptr(p_class);
- ERR_FAIL_COND_V_MSG(!ti, StringName(), "Cannot get class '" + String(p_class) + "'.");
+ ERR_FAIL_NULL_V_MSG(ti, StringName(), "Cannot get class '" + String(p_class) + "'.");
return ti->inherits;
}
@@ -159,7 +160,7 @@ ClassDB::APIType ClassDB::get_api_type(const StringName &p_class) {
ClassInfo *ti = classes.getptr(p_class);
- ERR_FAIL_COND_V_MSG(!ti, API_NONE, "Cannot get class '" + String(p_class) + "'.");
+ ERR_FAIL_NULL_V_MSG(ti, API_NONE, "Cannot get class '" + String(p_class) + "'.");
return ti->api;
}
@@ -180,7 +181,7 @@ uint32_t ClassDB::get_api_hash(APIType p_api) {
for (const StringName &E : class_list) {
ClassInfo *t = classes.getptr(E);
- ERR_FAIL_COND_V_MSG(!t, 0, "Cannot get class '" + String(E) + "'.");
+ ERR_FAIL_NULL_V_MSG(t, 0, "Cannot get class '" + String(E) + "'.");
if (t->api != p_api || !t->exposed) {
continue;
}
@@ -221,10 +222,11 @@ uint32_t ClassDB::get_api_hash(APIType p_api) {
hash = hash_murmur3_one_64(mb->get_default_argument_count(), hash);
- for (int i = 0; i < mb->get_default_argument_count(); i++) {
- //hash should not change, i hope for tis
- Variant da = mb->get_default_argument(i);
- hash = hash_murmur3_one_64(da.hash(), hash);
+ for (int i = 0; i < mb->get_argument_count(); i++) {
+ if (mb->has_default_argument(i)) {
+ Variant da = mb->get_default_argument(i);
+ hash = hash_murmur3_one_64(da.hash(), hash);
+ }
}
hash = hash_murmur3_one_64(mb->get_hint_flags(), hash);
@@ -278,7 +280,7 @@ uint32_t ClassDB::get_api_hash(APIType p_api) {
for (const StringName &F : snames) {
PropertySetGet *psg = t->property_setget.getptr(F);
- ERR_FAIL_COND_V(!psg, 0);
+ ERR_FAIL_NULL_V(psg, 0);
hash = hash_murmur3_one_64(F.hash(), hash);
hash = hash_murmur3_one_64(psg->setter.hash(), hash);
@@ -336,9 +338,9 @@ Object *ClassDB::instantiate(const StringName &p_class) {
ti = classes.getptr(compat_classes[p_class]);
}
}
- ERR_FAIL_COND_V_MSG(!ti, nullptr, "Cannot get class '" + String(p_class) + "'.");
+ ERR_FAIL_NULL_V_MSG(ti, nullptr, "Cannot get class '" + String(p_class) + "'.");
ERR_FAIL_COND_V_MSG(ti->disabled, nullptr, "Class '" + String(p_class) + "' is disabled.");
- ERR_FAIL_COND_V_MSG(!ti->creation_func, nullptr, "Class '" + String(p_class) + "' or its base class cannot be instantiated.");
+ ERR_FAIL_NULL_V_MSG(ti->creation_func, nullptr, "Class '" + String(p_class) + "' or its base class cannot be instantiated.");
}
#ifdef TOOLS_ENABLED
if (ti->api == API_EDITOR && !Engine::get_singleton()->is_editor_hint()) {
@@ -347,14 +349,20 @@ Object *ClassDB::instantiate(const StringName &p_class) {
}
#endif
if (ti->gdextension && ti->gdextension->create_instance) {
- return (Object *)ti->gdextension->create_instance(ti->gdextension->class_userdata);
+ Object *obj = (Object *)ti->gdextension->create_instance(ti->gdextension->class_userdata);
+#ifdef TOOLS_ENABLED
+ if (ti->gdextension->track_instance) {
+ ti->gdextension->track_instance(ti->gdextension->tracking_userdata, obj);
+ }
+#endif
+ return obj;
} else {
return ti->creation_func();
}
}
void ClassDB::set_object_extension_instance(Object *p_object, const StringName &p_class, GDExtensionClassInstancePtr p_instance) {
- ERR_FAIL_COND(!p_object);
+ ERR_FAIL_NULL(p_object);
ClassInfo *ti;
{
OBJTYPE_RLOCK;
@@ -364,9 +372,9 @@ void ClassDB::set_object_extension_instance(Object *p_object, const StringName &
ti = classes.getptr(compat_classes[p_class]);
}
}
- ERR_FAIL_COND_MSG(!ti, "Cannot get class '" + String(p_class) + "'.");
+ ERR_FAIL_NULL_MSG(ti, "Cannot get class '" + String(p_class) + "'.");
ERR_FAIL_COND_MSG(ti->disabled, "Class '" + String(p_class) + "' is disabled.");
- ERR_FAIL_COND_MSG(!ti->gdextension, "Class '" + String(p_class) + "' has no native extension.");
+ ERR_FAIL_NULL_MSG(ti->gdextension, "Class '" + String(p_class) + "' has no native extension.");
}
p_object->_extension = ti->gdextension;
@@ -377,7 +385,14 @@ bool ClassDB::can_instantiate(const StringName &p_class) {
OBJTYPE_RLOCK;
ClassInfo *ti = classes.getptr(p_class);
- ERR_FAIL_COND_V_MSG(!ti, false, "Cannot get class '" + String(p_class) + "'.");
+ if (!ti) {
+ if (!ScriptServer::is_global_class(p_class)) {
+ ERR_FAIL_V_MSG(false, "Cannot get class '" + String(p_class) + "'.");
+ }
+ String path = ScriptServer::get_global_class_path(p_class);
+ Ref<Script> scr = ResourceLoader::load(path);
+ return scr.is_valid() && scr->is_valid() && !scr->is_abstract();
+ }
#ifdef TOOLS_ENABLED
if (ti->api == API_EDITOR && !Engine::get_singleton()->is_editor_hint()) {
return false;
@@ -394,7 +409,9 @@ bool ClassDB::is_virtual(const StringName &p_class) {
if (!ScriptServer::is_global_class(p_class)) {
ERR_FAIL_V_MSG(false, "Cannot get class '" + String(p_class) + "'.");
}
- return false;
+ String path = ScriptServer::get_global_class_path(p_class);
+ Ref<Script> scr = ResourceLoader::load(path);
+ return scr.is_valid() && scr->is_valid() && scr->is_abstract();
}
#ifdef TOOLS_ENABLED
if (ti->api == API_EDITOR && !Engine::get_singleton()->is_editor_hint()) {
@@ -463,7 +480,6 @@ void ClassDB::get_method_list(const StringName &p_class, List<MethodInfo> *p_met
}
#ifdef DEBUG_METHODS_ENABLED
-
for (const MethodInfo &E : type->virtual_methods) {
p_methods->push_back(E);
}
@@ -478,17 +494,74 @@ void ClassDB::get_method_list(const StringName &p_class, List<MethodInfo> *p_met
p_methods->push_back(minfo);
}
-
#else
-
for (KeyValue<StringName, MethodBind *> &E : type->method_map) {
MethodBind *m = E.value;
MethodInfo minfo = info_from_bind(m);
p_methods->push_back(minfo);
}
+#endif
+ if (p_no_inheritance) {
+ break;
+ }
+
+ type = type->inherits_ptr;
+ }
+}
+
+void ClassDB::get_method_list_with_compatibility(const StringName &p_class, List<Pair<MethodInfo, uint32_t>> *p_methods, bool p_no_inheritance, bool p_exclude_from_properties) {
+ OBJTYPE_RLOCK;
+
+ ClassInfo *type = classes.getptr(p_class);
+
+ while (type) {
+ if (type->disabled) {
+ if (p_no_inheritance) {
+ break;
+ }
+
+ type = type->inherits_ptr;
+ continue;
+ }
+
+#ifdef DEBUG_METHODS_ENABLED
+ for (const MethodInfo &E : type->virtual_methods) {
+ Pair<MethodInfo, uint32_t> pair(E, 0);
+ p_methods->push_back(pair);
+ }
+
+ for (const StringName &E : type->method_order) {
+ if (p_exclude_from_properties && type->methods_in_properties.has(E)) {
+ continue;
+ }
+
+ MethodBind *method = type->method_map.get(E);
+ MethodInfo minfo = info_from_bind(method);
+
+ Pair<MethodInfo, uint32_t> pair(minfo, method->get_hash());
+ p_methods->push_back(pair);
+ }
+#else
+ for (KeyValue<StringName, MethodBind *> &E : type->method_map) {
+ MethodBind *method = E.value;
+ MethodInfo minfo = info_from_bind(method);
+
+ Pair<MethodInfo, uint32_t> pair(minfo, method->get_hash());
+ p_methods->push_back(pair);
+ }
#endif
+ for (const KeyValue<StringName, LocalVector<MethodBind *, unsigned int, false, false>> &E : type->method_map_compatibility) {
+ LocalVector<MethodBind *> compat = E.value;
+ for (MethodBind *method : compat) {
+ MethodInfo minfo = info_from_bind(method);
+
+ Pair<MethodInfo, uint32_t> pair(minfo, method->get_hash());
+ p_methods->push_back(pair);
+ }
+ }
+
if (p_no_inheritance) {
break;
}
@@ -621,7 +694,7 @@ void ClassDB::bind_integer_constant(const StringName &p_class, const StringName
ClassInfo *type = classes.getptr(p_class);
- ERR_FAIL_COND(!type);
+ ERR_FAIL_NULL(type);
if (type->constant_map.has(p_name)) {
ERR_FAIL();
@@ -790,7 +863,7 @@ void ClassDB::set_method_error_return_values(const StringName &p_class, const St
#ifdef DEBUG_METHODS_ENABLED
ClassInfo *type = classes.getptr(p_class);
- ERR_FAIL_COND(!type);
+ ERR_FAIL_NULL(type);
type->method_error_values[p_method] = p_values;
#endif
@@ -800,7 +873,7 @@ Vector<Error> ClassDB::get_method_error_return_values(const StringName &p_class,
#ifdef DEBUG_METHODS_ENABLED
ClassInfo *type = classes.getptr(p_class);
- ERR_FAIL_COND_V(!type, Vector<Error>());
+ ERR_FAIL_NULL_V(type, Vector<Error>());
if (!type->method_error_values.has(p_method)) {
return Vector<Error>();
@@ -853,7 +926,7 @@ void ClassDB::add_signal(const StringName &p_class, const MethodInfo &p_signal)
OBJTYPE_WLOCK;
ClassInfo *type = classes.getptr(p_class);
- ERR_FAIL_COND(!type);
+ ERR_FAIL_NULL(type);
StringName sname = p_signal.name;
@@ -872,7 +945,7 @@ void ClassDB::get_signal_list(const StringName &p_class, List<MethodInfo> *p_sig
OBJTYPE_RLOCK;
ClassInfo *type = classes.getptr(p_class);
- ERR_FAIL_COND(!type);
+ ERR_FAIL_NULL(type);
ClassInfo *check = type;
@@ -926,7 +999,7 @@ bool ClassDB::get_signal(const StringName &p_class, const StringName &p_signal,
void ClassDB::add_property_group(const StringName &p_class, const String &p_name, const String &p_prefix, int p_indent_depth) {
OBJTYPE_WLOCK;
ClassInfo *type = classes.getptr(p_class);
- ERR_FAIL_COND(!type);
+ ERR_FAIL_NULL(type);
String prefix = p_prefix;
if (p_indent_depth > 0) {
@@ -939,7 +1012,7 @@ void ClassDB::add_property_group(const StringName &p_class, const String &p_name
void ClassDB::add_property_subgroup(const StringName &p_class, const String &p_name, const String &p_prefix, int p_indent_depth) {
OBJTYPE_WLOCK;
ClassInfo *type = classes.getptr(p_class);
- ERR_FAIL_COND(!type);
+ ERR_FAIL_NULL(type);
String prefix = p_prefix;
if (p_indent_depth > 0) {
@@ -956,7 +1029,7 @@ void ClassDB::add_property_array_count(const StringName &p_class, const String &
void ClassDB::add_property_array(const StringName &p_class, const StringName &p_path, const String &p_array_element_prefix) {
OBJTYPE_WLOCK;
ClassInfo *type = classes.getptr(p_class);
- ERR_FAIL_COND(!type);
+ ERR_FAIL_NULL(type);
type->property_list.push_back(PropertyInfo(Variant::NIL, p_path, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_ARRAY, p_array_element_prefix));
}
@@ -967,14 +1040,14 @@ void ClassDB::add_property(const StringName &p_class, const PropertyInfo &p_pinf
ClassInfo *type = classes.getptr(p_class);
lock.read_unlock();
- ERR_FAIL_COND(!type);
+ ERR_FAIL_NULL(type);
MethodBind *mb_set = nullptr;
if (p_setter) {
mb_set = get_method(p_class, p_setter);
#ifdef DEBUG_METHODS_ENABLED
- ERR_FAIL_COND_MSG(!mb_set, "Invalid setter '" + p_class + "::" + p_setter + "' for property '" + p_pinfo.name + "'.");
+ ERR_FAIL_NULL_MSG(mb_set, "Invalid setter '" + p_class + "::" + p_setter + "' for property '" + p_pinfo.name + "'.");
int exp_args = 1 + (p_index >= 0 ? 1 : 0);
ERR_FAIL_COND_MSG(mb_set->get_argument_count() != exp_args, "Invalid function for setter '" + p_class + "::" + p_setter + " for property '" + p_pinfo.name + "'.");
@@ -986,7 +1059,7 @@ void ClassDB::add_property(const StringName &p_class, const PropertyInfo &p_pinf
mb_get = get_method(p_class, p_getter);
#ifdef DEBUG_METHODS_ENABLED
- ERR_FAIL_COND_MSG(!mb_get, "Invalid getter '" + p_class + "::" + p_getter + "' for property '" + p_pinfo.name + "'.");
+ ERR_FAIL_NULL_MSG(mb_get, "Invalid getter '" + p_class + "::" + p_getter + "' for property '" + p_pinfo.name + "'.");
int exp_args = 0 + (p_index >= 0 ? 1 : 0);
ERR_FAIL_COND_MSG(mb_get->get_argument_count() != exp_args, "Invalid function for getter '" + p_class + "::" + p_getter + "' for property: '" + p_pinfo.name + "'.");
@@ -1031,7 +1104,7 @@ void ClassDB::add_linked_property(const StringName &p_class, const String &p_pro
#ifdef TOOLS_ENABLED
OBJTYPE_WLOCK;
ClassInfo *type = classes.getptr(p_class);
- ERR_FAIL_COND(!type);
+ ERR_FAIL_NULL(type);
ERR_FAIL_COND(!type->property_map.has(p_property));
ERR_FAIL_COND(!type->property_map.has(p_linked_property));
@@ -1306,7 +1379,7 @@ void ClassDB::set_method_flags(const StringName &p_class, const StringName &p_me
OBJTYPE_WLOCK;
ClassInfo *type = classes.getptr(p_class);
ClassInfo *check = type;
- ERR_FAIL_COND(!check);
+ ERR_FAIL_NULL(check);
ERR_FAIL_COND(!check->method_map.has(p_method));
check->method_map[p_method]->set_hint_flags(p_flags);
}
@@ -1374,7 +1447,7 @@ MethodBind *ClassDB::_bind_vararg_method(MethodBind *p_bind, const StringName &p
ClassInfo *type = classes.getptr(instance_type);
if (!type) {
memdelete(bind);
- ERR_FAIL_COND_V(!type, nullptr);
+ ERR_FAIL_NULL_V(type, nullptr);
}
if (p_compatibility) {
@@ -1406,7 +1479,7 @@ MethodBind *ClassDB::bind_methodfi(uint32_t p_flags, MethodBind *p_bind, bool p_
#endif
OBJTYPE_WLOCK;
- ERR_FAIL_COND_V(!p_bind, nullptr);
+ ERR_FAIL_NULL_V(p_bind, nullptr);
p_bind->set_name(mdname);
String instance_type = p_bind->get_instance_class();
@@ -1532,7 +1605,7 @@ bool ClassDB::is_class_enabled(const StringName &p_class) {
}
}
- ERR_FAIL_COND_V_MSG(!ti, false, "Cannot get class '" + String(p_class) + "'.");
+ ERR_FAIL_NULL_V_MSG(ti, false, "Cannot get class '" + String(p_class) + "'.");
return !ti->disabled;
}
@@ -1540,10 +1613,18 @@ bool ClassDB::is_class_exposed(const StringName &p_class) {
OBJTYPE_RLOCK;
ClassInfo *ti = classes.getptr(p_class);
- ERR_FAIL_COND_V_MSG(!ti, false, "Cannot get class '" + String(p_class) + "'.");
+ ERR_FAIL_NULL_V_MSG(ti, false, "Cannot get class '" + String(p_class) + "'.");
return ti->exposed;
}
+bool ClassDB::is_class_reloadable(const StringName &p_class) {
+ OBJTYPE_RLOCK;
+
+ ClassInfo *ti = classes.getptr(p_class);
+ ERR_FAIL_NULL_V_MSG(ti, false, "Cannot get class '" + String(p_class) + "'.");
+ return ti->reloadable;
+}
+
void ClassDB::add_resource_base_extension(const StringName &p_extension, const StringName &p_class) {
if (resource_base_extensions.has(p_extension)) {
return;
@@ -1661,21 +1742,39 @@ void ClassDB::register_extension_class(ObjectGDExtension *p_extension) {
c.name = p_extension->class_name;
c.is_virtual = p_extension->is_virtual;
if (!p_extension->is_abstract) {
- c.creation_func = parent->creation_func;
+ // Find the closest ancestor which is either non-abstract or native (or both).
+ ClassInfo *concrete_ancestor = parent;
+ while (concrete_ancestor->creation_func == nullptr &&
+ concrete_ancestor->inherits_ptr != nullptr &&
+ concrete_ancestor->gdextension != nullptr) {
+ concrete_ancestor = concrete_ancestor->inherits_ptr;
+ }
+ ERR_FAIL_NULL_MSG(concrete_ancestor->creation_func, "Extension class " + String(p_extension->class_name) + " cannot extend native abstract class " + String(concrete_ancestor->name));
+ c.creation_func = concrete_ancestor->creation_func;
}
c.inherits = parent->name;
c.class_ptr = parent->class_ptr;
c.inherits_ptr = parent;
- c.exposed = true;
+ c.exposed = p_extension->is_exposed;
+ if (c.exposed) {
+ // The parent classes should be exposed if it has an exposed child class.
+ while (parent && !parent->exposed) {
+ parent->exposed = true;
+ parent = classes.getptr(parent->name);
+ }
+ }
+ c.reloadable = p_extension->reloadable;
classes[p_extension->class_name] = c;
}
-void ClassDB::unregister_extension_class(const StringName &p_class) {
+void ClassDB::unregister_extension_class(const StringName &p_class, bool p_free_method_binds) {
ClassInfo *c = classes.getptr(p_class);
- ERR_FAIL_COND_MSG(!c, "Class " + p_class + "does not exist");
- for (KeyValue<StringName, MethodBind *> &F : c->method_map) {
- memdelete(F.value);
+ ERR_FAIL_NULL_MSG(c, "Class '" + String(p_class) + "' does not exist.");
+ if (p_free_method_binds) {
+ for (KeyValue<StringName, MethodBind *> &F : c->method_map) {
+ memdelete(F.value);
+ }
}
classes.erase(p_class);
}
diff --git a/core/object/class_db.h b/core/object/class_db.h
index 3aae3b452e..5c2c59d508 100644
--- a/core/object/class_db.h
+++ b/core/object/class_db.h
@@ -131,6 +131,7 @@ public:
StringName name;
bool disabled = false;
bool exposed = false;
+ bool reloadable = false;
bool is_virtual = false;
Object *(*creation_func)() = nullptr;
@@ -187,9 +188,10 @@ public:
template <class T>
static void register_class(bool p_virtual = false) {
GLOBAL_LOCK_FUNCTION;
+ static_assert(TypesAreSame<typename T::self_type, T>::value, "Class not declared properly, please use GDCLASS.");
T::initialize_class();
ClassInfo *t = classes.getptr(T::get_class_static());
- ERR_FAIL_COND(!t);
+ ERR_FAIL_NULL(t);
t->creation_func = &creator<T>;
t->exposed = true;
t->is_virtual = p_virtual;
@@ -201,17 +203,33 @@ public:
template <class T>
static void register_abstract_class() {
GLOBAL_LOCK_FUNCTION;
+ static_assert(TypesAreSame<typename T::self_type, T>::value, "Class not declared properly, please use GDCLASS.");
T::initialize_class();
ClassInfo *t = classes.getptr(T::get_class_static());
- ERR_FAIL_COND(!t);
+ ERR_FAIL_NULL(t);
t->exposed = true;
t->class_ptr = T::get_class_ptr_static();
t->api = current_api;
//nothing
}
+ template <class T>
+ static void register_internal_class() {
+ GLOBAL_LOCK_FUNCTION;
+ static_assert(TypesAreSame<typename T::self_type, T>::value, "Class not declared properly, please use GDCLASS.");
+ T::initialize_class();
+ ClassInfo *t = classes.getptr(T::get_class_static());
+ ERR_FAIL_NULL(t);
+ t->creation_func = &creator<T>;
+ t->exposed = false;
+ t->is_virtual = false;
+ t->class_ptr = T::get_class_ptr_static();
+ t->api = current_api;
+ T::register_custom_data_to_otdb();
+ }
+
static void register_extension_class(ObjectGDExtension *p_extension);
- static void unregister_extension_class(const StringName &p_class);
+ static void unregister_extension_class(const StringName &p_class, bool p_free_method_binds = true);
template <class T>
static Object *_create_ptr_func() {
@@ -221,9 +239,10 @@ public:
template <class T>
static void register_custom_instance_class() {
GLOBAL_LOCK_FUNCTION;
+ static_assert(TypesAreSame<typename T::self_type, T>::value, "Class not declared properly, please use GDCLASS.");
T::initialize_class();
ClassInfo *t = classes.getptr(T::get_class_static());
- ERR_FAIL_COND(!t);
+ ERR_FAIL_NULL(t);
t->creation_func = &_create_ptr_func<T>;
t->exposed = true;
t->class_ptr = T::get_class_ptr_static();
@@ -329,7 +348,7 @@ public:
GLOBAL_LOCK_FUNCTION;
MethodBind *bind = create_vararg_method_bind(p_method, p_info, p_return_nil_is_variant);
- ERR_FAIL_COND_V(!bind, nullptr);
+ ERR_FAIL_NULL_V(bind, nullptr);
if constexpr (std::is_same<typename member_function_traits<M>::return_type, Object *>::value) {
bind->set_return_type_is_raw_object_ptr(true);
@@ -342,7 +361,7 @@ public:
GLOBAL_LOCK_FUNCTION;
MethodBind *bind = create_vararg_method_bind(p_method, p_info, p_return_nil_is_variant);
- ERR_FAIL_COND_V(!bind, nullptr);
+ ERR_FAIL_NULL_V(bind, nullptr);
if constexpr (std::is_same<typename member_function_traits<M>::return_type, Object *>::value) {
bind->set_return_type_is_raw_object_ptr(true);
@@ -380,6 +399,7 @@ public:
static void set_method_flags(const StringName &p_class, const StringName &p_method, int p_flags);
static void get_method_list(const StringName &p_class, List<MethodInfo> *p_methods, bool p_no_inheritance = false, bool p_exclude_from_properties = false);
+ static void get_method_list_with_compatibility(const StringName &p_class, List<Pair<MethodInfo, uint32_t>> *p_methods_with_hash, bool p_no_inheritance = false, bool p_exclude_from_properties = false);
static bool get_method_info(const StringName &p_class, const StringName &p_method, MethodInfo *r_info, bool p_no_inheritance = false, bool p_exclude_from_properties = false);
static MethodBind *get_method(const StringName &p_class, const StringName &p_name);
static MethodBind *get_method_with_compatibility(const StringName &p_class, const StringName &p_name, uint64_t p_hash, bool *r_method_exists = nullptr, bool *r_is_deprecated = nullptr);
@@ -408,6 +428,7 @@ public:
static bool is_class_enabled(const StringName &p_class);
static bool is_class_exposed(const StringName &p_class);
+ static bool is_class_reloadable(const StringName &p_class);
static void add_resource_base_extension(const StringName &p_extension, const StringName &p_class);
static void get_resource_base_extensions(List<String> *p_extensions);
@@ -480,6 +501,10 @@ _FORCE_INLINE_ Vector<Error> errarray(P... p_args) {
if (m_class::_class_is_enabled) { \
::ClassDB::register_abstract_class<m_class>(); \
}
+#define GDREGISTER_INTERNAL_CLASS(m_class) \
+ if (m_class::_class_is_enabled) { \
+ ::ClassDB::register_internal_class<m_class>(); \
+ }
#define GDREGISTER_NATIVE_STRUCT(m_class, m_code) ClassDB::register_native_struct(#m_class, m_code, sizeof(m_class))
diff --git a/core/object/make_virtuals.py b/core/object/make_virtuals.py
index 5be9650b32..0f3cf3916a 100644
--- a/core/object/make_virtuals.py
+++ b/core/object/make_virtuals.py
@@ -2,7 +2,7 @@ proto = """
#define GDVIRTUAL$VER($RET m_name $ARG) \\
StringName _gdvirtual_##m_name##_sn = #m_name;\\
mutable bool _gdvirtual_##m_name##_initialized = false;\\
-mutable GDExtensionClassCallVirtual _gdvirtual_##m_name = nullptr;\\
+mutable void* _gdvirtual_##m_name = nullptr;\\
template<bool required>\\
_FORCE_INLINE_ bool _gdvirtual_##m_name##_call($CALLARGS) $CONST { \\
ScriptInstance *_script_instance = ((Object*)(this))->get_script_instance();\\
@@ -16,15 +16,25 @@ _FORCE_INLINE_ bool _gdvirtual_##m_name##_call($CALLARGS) $CONST { \\
} \\
}\\
if (unlikely(_get_extension() && !_gdvirtual_##m_name##_initialized)) {\\
- /* TODO: C-style cast because GDExtensionStringNamePtr's const qualifier is broken (see https://github.com/godotengine/godot/pull/67751) */\\
- _gdvirtual_##m_name = (_get_extension() && _get_extension()->get_virtual) ? _get_extension()->get_virtual(_get_extension()->class_userdata, (GDExtensionStringNamePtr)&_gdvirtual_##m_name##_sn) : (GDExtensionClassCallVirtual) nullptr;\\
+ _gdvirtual_##m_name = nullptr;\\
+ if (_get_extension()->get_virtual_call_data && _get_extension()->call_virtual_with_data) {\\
+ _gdvirtual_##m_name = _get_extension()->get_virtual_call_data(_get_extension()->class_userdata, &_gdvirtual_##m_name##_sn);\\
+ } else if (_get_extension()->get_virtual) {\\
+ _gdvirtual_##m_name = (void *)_get_extension()->get_virtual(_get_extension()->class_userdata, &_gdvirtual_##m_name##_sn);\\
+ }\\
+ GDVIRTUAL_TRACK(_gdvirtual_##m_name, _gdvirtual_##m_name##_initialized); \\
_gdvirtual_##m_name##_initialized = true;\\
}\\
if (_gdvirtual_##m_name) {\\
$CALLPTRARGS\\
$CALLPTRRETDEF\\
- _gdvirtual_##m_name(_get_extension_instance(),$CALLPTRARGPASS,$CALLPTRRETPASS);\\
- $CALLPTRRET\\
+ if (_get_extension()->get_virtual_call_data && _get_extension()->call_virtual_with_data) {\\
+ _get_extension()->call_virtual_with_data(_get_extension_instance(), &_gdvirtual_##m_name##_sn, _gdvirtual_##m_name, $CALLPTRARGPASS,$CALLPTRRETPASS);\\
+ $CALLPTRRET\\
+ } else {\\
+ ((GDExtensionClassCallVirtual)_gdvirtual_##m_name)(_get_extension_instance(),$CALLPTRARGPASS,$CALLPTRRETPASS);\\
+ $CALLPTRRET\\
+ }\\
return true;\\
}\\
\\
@@ -41,8 +51,13 @@ _FORCE_INLINE_ bool _gdvirtual_##m_name##_overridden() const { \\
return _script_instance->has_method(_gdvirtual_##m_name##_sn);\\
}\\
if (unlikely(_get_extension() && !_gdvirtual_##m_name##_initialized)) {\\
- /* TODO: C-style cast because GDExtensionStringNamePtr's const qualifier is broken (see https://github.com/godotengine/godot/pull/67751) */\\
- _gdvirtual_##m_name = (_get_extension() && _get_extension()->get_virtual) ? _get_extension()->get_virtual(_get_extension()->class_userdata, (GDExtensionStringNamePtr)&_gdvirtual_##m_name##_sn) : (GDExtensionClassCallVirtual) nullptr;\\
+ _gdvirtual_##m_name = nullptr;\\
+ if (_get_extension()->get_virtual_call_data && _get_extension()->call_virtual_with_data) {\\
+ _gdvirtual_##m_name = _get_extension()->get_virtual_call_data(_get_extension()->class_userdata, &_gdvirtual_##m_name##_sn);\\
+ } else if (_get_extension()->get_virtual) {\\
+ _gdvirtual_##m_name = (void *)_get_extension()->get_virtual(_get_extension()->class_userdata, &_gdvirtual_##m_name##_sn);\\
+ }\\
+ GDVIRTUAL_TRACK(_gdvirtual_##m_name, _gdvirtual_##m_name##_initialized); \\
_gdvirtual_##m_name##_initialized = true;\\
}\\
if (_gdvirtual_##m_name) {\\
@@ -160,6 +175,20 @@ def run(target, source, env):
#ifndef GDVIRTUAL_GEN_H
#define GDVIRTUAL_GEN_H
+#include "core/object/script_instance.h"
+
+#ifdef TOOLS_ENABLED
+#define GDVIRTUAL_TRACK(m_virtual, m_initialized) \\
+ if (_get_extension()->reloadable) {\\
+ VirtualMethodTracker *tracker = memnew(VirtualMethodTracker);\\
+ tracker->method = (void **)&m_virtual;\\
+ tracker->initialized = &m_initialized;\\
+ tracker->next = virtual_method_list;\\
+ virtual_method_list = tracker;\\
+ }
+#else
+#define GDVIRTUAL_TRACK(m_virtual, m_initialized)
+#endif
"""
diff --git a/core/object/message_queue.cpp b/core/object/message_queue.cpp
index 506f8291eb..de71295ee5 100644
--- a/core/object/message_queue.cpp
+++ b/core/object/message_queue.cpp
@@ -36,7 +36,7 @@
#include "core/object/script_language.h"
#ifdef DEV_ENABLED
-// Includes sanity checks to ensure that a queue set as a thread singleton override
+// Includes safety checks to ensure that a queue set as a thread singleton override
// is only ever called from the thread it was set for.
#define LOCK_MUTEX \
if (this != MessageQueue::thread_singleton) { \
@@ -537,7 +537,7 @@ CallQueue::~CallQueue() {
if (!allocator_is_custom) {
memdelete(allocator);
}
- // This is done here to avoid a circular dependency between the sanity checks and the thread singleton pointer.
+ // This is done here to avoid a circular dependency between the safety checks and the thread singleton pointer.
if (this == MessageQueue::thread_singleton) {
MessageQueue::thread_singleton = nullptr;
}
diff --git a/core/object/method_bind.cpp b/core/object/method_bind.cpp
index aa0cdedafc..b530101058 100644
--- a/core/object/method_bind.cpp
+++ b/core/object/method_bind.cpp
@@ -47,9 +47,11 @@ uint32_t MethodBind::get_hash() const {
}
hash = hash_murmur3_one_32(get_default_argument_count(), hash);
- for (int i = 0; i < get_default_argument_count(); i++) {
- Variant v = get_default_argument(i);
- hash = hash_murmur3_one_32(v.hash(), hash);
+ for (int i = 0; i < get_argument_count(); i++) {
+ if (has_default_argument(i)) {
+ Variant v = get_default_argument(i);
+ hash = hash_murmur3_one_32(v.hash(), hash);
+ }
}
hash = hash_murmur3_one_32(is_const(), hash);
diff --git a/core/object/method_bind.h b/core/object/method_bind.h
index 84f0941b94..d67fd003c8 100644
--- a/core/object/method_bind.h
+++ b/core/object/method_bind.h
@@ -111,6 +111,10 @@ public:
_FORCE_INLINE_ int get_argument_count() const { return argument_count; };
+#ifdef TOOLS_ENABLED
+ virtual bool is_valid() const { return true; }
+#endif
+
virtual Variant call(Object *p_object, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) const = 0;
virtual void validated_call(Object *p_object, const Variant **p_args, Variant *r_ret) const = 0;
diff --git a/core/object/object.cpp b/core/object/object.cpp
index 4ae0ecdefd..2e5b897bce 100644
--- a/core/object/object.cpp
+++ b/core/object/object.cpp
@@ -31,6 +31,7 @@
#include "object.h"
#include "core/core_string_names.h"
+#include "core/extension/gdextension_manager.h"
#include "core/io/resource.h"
#include "core/object/class_db.h"
#include "core/object/message_queue.h"
@@ -485,7 +486,7 @@ void Object::get_property_list(List<PropertyInfo> *p_list, bool p_reversed) cons
if (_extension) {
const ObjectGDExtension *current_extension = _extension;
while (current_extension) {
- p_list->push_back(PropertyInfo(Variant::NIL, current_extension->class_name, PROPERTY_HINT_NONE, String(), PROPERTY_USAGE_CATEGORY));
+ p_list->push_back(PropertyInfo(Variant::NIL, current_extension->class_name, PROPERTY_HINT_NONE, current_extension->class_name, PROPERTY_USAGE_CATEGORY));
ClassDB::get_property_list(current_extension->class_name, p_list, true, this);
@@ -526,6 +527,31 @@ void Object::get_property_list(List<PropertyInfo> *p_list, bool p_reversed) cons
void Object::validate_property(PropertyInfo &p_property) const {
_validate_propertyv(p_property);
+
+ if (_extension && _extension->validate_property) {
+ // GDExtension uses a StringName rather than a String for property name.
+ StringName prop_name = p_property.name;
+ GDExtensionPropertyInfo gdext_prop = {
+ (GDExtensionVariantType)p_property.type,
+ &prop_name,
+ &p_property.class_name,
+ (uint32_t)p_property.hint,
+ &p_property.hint_string,
+ p_property.usage,
+ };
+ if (_extension->validate_property(_extension_instance, &gdext_prop)) {
+ p_property.type = (Variant::Type)gdext_prop.type;
+ p_property.name = *reinterpret_cast<StringName *>(gdext_prop.name);
+ p_property.class_name = *reinterpret_cast<StringName *>(gdext_prop.class_name);
+ p_property.hint = (PropertyHint)gdext_prop.hint;
+ p_property.hint_string = *reinterpret_cast<String *>(gdext_prop.hint_string);
+ p_property.usage = gdext_prop.usage;
+ };
+ }
+
+ if (script_instance) { // Call it last to allow user altering already validated properties.
+ script_instance->validate_property(p_property);
+ }
}
bool Object::property_can_revert(const StringName &p_name) const {
@@ -591,7 +617,7 @@ void Object::get_method_list(List<MethodInfo> *p_list) const {
Variant Object::_call_bind(const Variant **p_args, int p_argcount, Callable::CallError &r_error) {
if (p_argcount < 1) {
r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;
- r_error.argument = 0;
+ r_error.expected = 1;
return Variant();
}
@@ -610,7 +636,7 @@ Variant Object::_call_bind(const Variant **p_args, int p_argcount, Callable::Cal
Variant Object::_call_deferred_bind(const Variant **p_args, int p_argcount, Callable::CallError &r_error) {
if (p_argcount < 1) {
r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;
- r_error.argument = 0;
+ r_error.expected = 1;
return Variant();
}
@@ -640,8 +666,16 @@ bool Object::has_method(const StringName &p_method) const {
}
MethodBind *method = ClassDB::get_method(get_class_name(), p_method);
+ if (method != nullptr) {
+ return true;
+ }
- return method != nullptr;
+ const Script *scr = Object::cast_to<Script>(this);
+ if (scr != nullptr) {
+ return scr->has_static_method(p_method);
+ }
+
+ return false;
}
Variant Object::getvar(const Variant &p_key, bool *r_valid) const {
@@ -689,12 +723,11 @@ Variant Object::callp(const StringName &p_method, const Variant **p_args, int p_
//free must be here, before anything, always ready
#ifdef DEBUG_ENABLED
if (p_argcount != 0) {
- r_error.argument = 0;
r_error.error = Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS;
+ r_error.expected = 0;
return Variant();
}
if (Object::cast_to<RefCounted>(this)) {
- r_error.argument = 0;
r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
ERR_FAIL_V_MSG(Variant(), "Can't 'free' a reference.");
}
@@ -795,14 +828,30 @@ Variant Object::call_const(const StringName &p_method, const Variant **p_args, i
}
void Object::notification(int p_notification, bool p_reversed) {
- _notificationv(p_notification, p_reversed);
+ if (p_reversed) {
+ if (script_instance) {
+ script_instance->notification(p_notification, p_reversed);
+ }
+ } else {
+ _notificationv(p_notification, p_reversed);
+ }
- if (script_instance) {
- script_instance->notification(p_notification);
+ if (_extension) {
+ if (_extension->notification2) {
+ _extension->notification2(_extension_instance, p_notification, static_cast<GDExtensionBool>(p_reversed));
+#ifndef DISABLE_DEPRECATED
+ } else if (_extension->notification) {
+ _extension->notification(_extension_instance, p_notification);
+#endif // DISABLE_DEPRECATED
+ }
}
- if (_extension && _extension->notification) {
- _extension->notification(_extension_instance, p_notification);
+ if (p_reversed) {
+ _notificationv(p_notification, p_reversed);
+ } else {
+ if (script_instance) {
+ script_instance->notification(p_notification, p_reversed);
+ }
}
}
@@ -826,7 +875,7 @@ String Object::to_string() {
void Object::set_script_and_instance(const Variant &p_script, ScriptInstance *p_instance) {
//this function is not meant to be used in any of these ways
ERR_FAIL_COND(p_script.is_null());
- ERR_FAIL_COND(!p_instance);
+ ERR_FAIL_NULL(p_instance);
ERR_FAIL_COND(script_instance != nullptr || !script.is_null());
script = p_script;
@@ -839,7 +888,10 @@ void Object::set_script(const Variant &p_script) {
}
Ref<Script> s = p_script;
- ERR_FAIL_COND_MSG(s.is_null() && !p_script.is_null(), "Invalid parameter, it should be a reference to a valid script (or null).");
+ if (!p_script.is_null()) {
+ ERR_FAIL_COND_MSG(s.is_null(), "Cannot set object script. Parameter should be null or a reference to a valid script.");
+ ERR_FAIL_COND_MSG(s->is_abstract(), vformat("Cannot set object script. Script '%s' should not be abstract.", s->get_path()));
+ }
script = p_script;
@@ -991,14 +1043,17 @@ struct _ObjectSignalDisconnectData {
};
Error Object::_emit_signal(const Variant **p_args, int p_argcount, Callable::CallError &r_error) {
- r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;
+ if (unlikely(p_argcount < 1)) {
+ r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;
+ r_error.expected = 1;
+ ERR_FAIL_V(Error::ERR_INVALID_PARAMETER);
+ }
- ERR_FAIL_COND_V(p_argcount < 1, Error::ERR_INVALID_PARAMETER);
- if (p_args[0]->get_type() != Variant::STRING_NAME && p_args[0]->get_type() != Variant::STRING) {
+ if (unlikely(p_args[0]->get_type() != Variant::STRING_NAME && p_args[0]->get_type() != Variant::STRING)) {
r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
r_error.argument = 0;
r_error.expected = Variant::STRING_NAME;
- ERR_FAIL_COND_V(p_args[0]->get_type() != Variant::STRING_NAME && p_args[0]->get_type() != Variant::STRING, Error::ERR_INVALID_PARAMETER);
+ ERR_FAIL_V(Error::ERR_INVALID_PARAMETER);
}
r_error.error = Callable::CallError::CALL_OK;
@@ -1054,8 +1109,7 @@ Error Object::emit_signalp(const StringName &p_name, const Variant **p_args, int
Error err = OK;
for (const Connection &c : slot_conns) {
- Object *target = c.callable.get_object();
- if (!target) {
+ if (!c.callable.is_valid()) {
// Target might have been deleted during signal callback, this is expected and OK.
continue;
}
@@ -1078,7 +1132,8 @@ Error Object::emit_signalp(const StringName &p_name, const Variant **p_args, int
continue;
}
#endif
- if (ce.error == Callable::CallError::CALL_ERROR_INVALID_METHOD && !ClassDB::class_exists(target->get_class_name())) {
+ Object *target = c.callable.get_object();
+ if (ce.error == Callable::CallError::CALL_ERROR_INVALID_METHOD && target && !ClassDB::class_exists(target->get_class_name())) {
//most likely object is not initialized yet, do not throw error.
} else {
ERR_PRINT("Error calling from signal '" + String(p_name) + "' to callable: " + Variant::get_callable_error_text(c.callable, args, argc, ce) + ".");
@@ -1258,8 +1313,14 @@ void Object::get_signals_connected_to_this(List<Connection> *p_connections) cons
Error Object::connect(const StringName &p_signal, const Callable &p_callable, uint32_t p_flags) {
ERR_FAIL_COND_V_MSG(p_callable.is_null(), ERR_INVALID_PARAMETER, "Cannot connect to '" + p_signal + "': the provided callable is null.");
- Object *target_object = p_callable.get_object();
- ERR_FAIL_COND_V_MSG(!target_object, ERR_INVALID_PARAMETER, "Cannot connect to '" + p_signal + "' to callable '" + p_callable + "': the callable object is null.");
+ if (p_callable.is_standard()) {
+ // FIXME: This branch should probably removed in favor of the `is_valid()` branch, but there exist some classes
+ // that call `connect()` before they are fully registered with ClassDB. Until all such classes can be found
+ // and registered soon enough this branch is needed to allow `connect()` to succeed.
+ ERR_FAIL_NULL_V_MSG(p_callable.get_object(), ERR_INVALID_PARAMETER, "Cannot connect to '" + p_signal + "' to callable '" + p_callable + "': the callable object is null.");
+ } else {
+ ERR_FAIL_COND_V_MSG(!p_callable.is_valid(), ERR_INVALID_PARAMETER, "Cannot connect to '" + p_signal + "': the provided callable is not valid: " + p_callable);
+ }
SignalData *s = signal_map.getptr(p_signal);
if (!s) {
@@ -1297,6 +1358,8 @@ Error Object::connect(const StringName &p_signal, const Callable &p_callable, ui
}
}
+ Object *target_object = p_callable.get_object();
+
SignalData::Slot slot;
Connection conn;
@@ -1304,7 +1367,9 @@ Error Object::connect(const StringName &p_signal, const Callable &p_callable, ui
conn.signal = ::Signal(this, p_signal);
conn.flags = p_flags;
slot.conn = conn;
- slot.cE = target_object->connections.push_back(conn);
+ if (target_object) {
+ slot.cE = target_object->connections.push_back(conn);
+ }
if (p_flags & CONNECT_REFERENCE_COUNTED) {
slot.reference_count = 1;
}
@@ -1343,16 +1408,13 @@ void Object::disconnect(const StringName &p_signal, const Callable &p_callable)
bool Object::_disconnect(const StringName &p_signal, const Callable &p_callable, bool p_force) {
ERR_FAIL_COND_V_MSG(p_callable.is_null(), false, "Cannot disconnect from '" + p_signal + "': the provided callable is null.");
- Object *target_object = p_callable.get_object();
- ERR_FAIL_COND_V_MSG(!target_object, false, "Cannot disconnect '" + p_signal + "' from callable '" + p_callable + "': the callable object is null.");
-
SignalData *s = signal_map.getptr(p_signal);
if (!s) {
bool signal_is_valid = ClassDB::has_signal(get_class_name(), p_signal) ||
(!script.is_null() && Ref<Script>(script)->has_script_signal(p_signal));
ERR_FAIL_COND_V_MSG(signal_is_valid, false, "Attempt to disconnect a nonexistent connection from '" + to_string() + "'. Signal: '" + p_signal + "', callable: '" + p_callable + "'.");
}
- ERR_FAIL_COND_V_MSG(!s, false, vformat("Disconnecting nonexistent signal '%s' in %s.", p_signal, to_string()));
+ ERR_FAIL_NULL_V_MSG(s, false, vformat("Disconnecting nonexistent signal '%s' in %s.", p_signal, to_string()));
ERR_FAIL_COND_V_MSG(!s->slot_map.has(*p_callable.get_base_comparator()), false, "Attempt to disconnect a nonexistent connection from '" + to_string() + "'. Signal: '" + p_signal + "', callable: '" + p_callable + "'.");
@@ -1365,7 +1427,13 @@ bool Object::_disconnect(const StringName &p_signal, const Callable &p_callable,
}
}
- target_object->connections.erase(slot->cE);
+ if (slot->cE) {
+ Object *target_object = p_callable.get_object();
+ if (target_object) {
+ target_object->connections.erase(slot->cE);
+ }
+ }
+
s->slot_map.erase(*p_callable.get_base_comparator());
if (s->slot_map.is_empty() && ClassDB::has_signal(get_class_name(), p_signal)) {
@@ -1604,6 +1672,8 @@ void Object::_bind_methods() {
plget.return_val.hint_string = "Dictionary";
BIND_OBJ_CORE_METHOD(plget);
+ BIND_OBJ_CORE_METHOD(MethodInfo(Variant::NIL, "_validate_property", PropertyInfo(Variant::DICTIONARY, "property")));
+
BIND_OBJ_CORE_METHOD(MethodInfo(Variant::BOOL, "_property_can_revert", PropertyInfo(Variant::STRING_NAME, "property")));
MethodInfo mipgr("_property_get_revert", PropertyInfo(Variant::STRING_NAME, "property"));
mipgr.return_val.name = "Variant";
@@ -1748,14 +1818,17 @@ StringName Object::get_class_name_for_extension(const GDExtension *p_library) co
}
void Object::set_instance_binding(void *p_token, void *p_binding, const GDExtensionInstanceBindingCallbacks *p_callbacks) {
- // This is only meant to be used on creation by the binder.
- ERR_FAIL_COND(_instance_bindings != nullptr);
- _instance_bindings = (InstanceBinding *)memalloc(sizeof(InstanceBinding));
+ // This is only meant to be used on creation by the binder, but we also
+ // need to account for reloading (where the 'binding' will be cleared).
+ ERR_FAIL_COND(_instance_bindings != nullptr && _instance_bindings[0].binding != nullptr);
+ if (_instance_bindings == nullptr) {
+ _instance_bindings = (InstanceBinding *)memalloc(sizeof(InstanceBinding));
+ _instance_binding_count = 1;
+ }
_instance_bindings[0].binding = p_binding;
_instance_bindings[0].free_callback = p_callbacks->free_callback;
_instance_bindings[0].reference_callback = p_callbacks->reference_callback;
_instance_bindings[0].token = p_token;
- _instance_binding_count = 1;
}
void *Object::get_instance_binding(void *p_token, const GDExtensionInstanceBindingCallbacks *p_callbacks) {
@@ -1782,6 +1855,12 @@ void *Object::get_instance_binding(void *p_token, const GDExtensionInstanceBindi
binding = p_callbacks->create_callback(p_token, this);
_instance_bindings[_instance_binding_count].binding = binding;
+#ifdef TOOLS_ENABLED
+ if (!_extension && Engine::get_singleton()->is_extension_reloading_enabled()) {
+ GDExtensionManager::get_singleton()->track_instance_binding(p_token, this);
+ }
+#endif
+
_instance_binding_count++;
}
@@ -1805,6 +1884,71 @@ bool Object::has_instance_binding(void *p_token) {
return found;
}
+void Object::free_instance_binding(void *p_token) {
+ bool found = false;
+ _instance_binding_mutex.lock();
+ for (uint32_t i = 0; i < _instance_binding_count; i++) {
+ if (!found && _instance_bindings[i].token == p_token) {
+ if (_instance_bindings[i].free_callback) {
+ _instance_bindings[i].free_callback(_instance_bindings[i].token, this, _instance_bindings[i].binding);
+ }
+ found = true;
+ }
+ if (found) {
+ if (i + 1 < _instance_binding_count) {
+ _instance_bindings[i] = _instance_bindings[i + 1];
+ } else {
+ _instance_bindings[i] = { nullptr };
+ }
+ }
+ }
+ if (found) {
+ _instance_binding_count--;
+ }
+ _instance_binding_mutex.unlock();
+}
+
+#ifdef TOOLS_ENABLED
+void Object::clear_internal_extension() {
+ ERR_FAIL_NULL(_extension);
+
+ // Free the instance inside the GDExtension.
+ if (_extension->free_instance) {
+ _extension->free_instance(_extension->class_userdata, _extension_instance);
+ }
+ _extension = nullptr;
+ _extension_instance = nullptr;
+
+ // Clear the instance bindings.
+ _instance_binding_mutex.lock();
+ if (_instance_bindings[0].free_callback) {
+ _instance_bindings[0].free_callback(_instance_bindings[0].token, this, _instance_bindings[0].binding);
+ }
+ _instance_bindings[0].binding = nullptr;
+ _instance_bindings[0].token = nullptr;
+ _instance_bindings[0].free_callback = nullptr;
+ _instance_bindings[0].reference_callback = nullptr;
+ _instance_binding_mutex.unlock();
+
+ // Clear the virtual methods.
+ while (virtual_method_list) {
+ (*virtual_method_list->method) = nullptr;
+ (*virtual_method_list->initialized) = false;
+ virtual_method_list = virtual_method_list->next;
+ }
+}
+
+void Object::reset_internal_extension(ObjectGDExtension *p_extension) {
+ ERR_FAIL_COND(_extension != nullptr);
+
+ if (p_extension) {
+ _extension_instance = p_extension->recreate_instance ? p_extension->recreate_instance(p_extension->class_userdata, (GDExtensionObjectPtr)this) : nullptr;
+ ERR_FAIL_NULL_MSG(_extension_instance, "Unable to recreate GDExtension instance - does this extension support hot reloading?");
+ _extension = p_extension;
+ }
+}
+#endif
+
void Object::_construct_object(bool p_reference) {
type_is_reference = p_reference;
_instance_id = ObjectDB::add_instance(this);
@@ -1835,11 +1979,25 @@ Object::~Object() {
}
script_instance = nullptr;
- if (_extension && _extension->free_instance) {
- _extension->free_instance(_extension->class_userdata, _extension_instance);
+ if (_extension) {
+#ifdef TOOLS_ENABLED
+ if (_extension->untrack_instance) {
+ _extension->untrack_instance(_extension->tracking_userdata, this);
+ }
+#endif
+ if (_extension->free_instance) {
+ _extension->free_instance(_extension->class_userdata, _extension_instance);
+ }
_extension = nullptr;
_extension_instance = nullptr;
}
+#ifdef TOOLS_ENABLED
+ else if (_instance_bindings != nullptr && Engine::get_singleton()->is_extension_reloading_enabled()) {
+ for (uint32_t i = 0; i < _instance_binding_count; i++) {
+ GDExtensionManager::get_singleton()->untrack_instance_binding(_instance_bindings[i].token, this);
+ }
+ }
+#endif
if (_emitting) {
//@todo this may need to actually reach the debugger prioritarily somehow because it may crash before
diff --git a/core/object/object.h b/core/object/object.h
index 318dbf98de..a444db0f70 100644
--- a/core/object/object.h
+++ b/core/object/object.h
@@ -49,7 +49,7 @@ class TypedArray;
enum PropertyHint {
PROPERTY_HINT_NONE, ///< no hint provided.
- PROPERTY_HINT_RANGE, ///< hint_text = "min,max[,step][,or_greater][,or_less][,hide_slider][,radians][,degrees][,exp][,suffix:<keyword>] range.
+ PROPERTY_HINT_RANGE, ///< hint_text = "min,max[,step][,or_greater][,or_less][,hide_slider][,radians_as_degrees][,degrees][,exp][,suffix:<keyword>] range.
PROPERTY_HINT_ENUM, ///< hint_text= "val1,val2,val3,etc"
PROPERTY_HINT_ENUM_SUGGESTION, ///< hint_text= "val1,val2,val3,etc"
PROPERTY_HINT_EXP_EASING, /// exponential easing function (Math::ease) use "attenuation" hint string to revert (flip h), "positive_only" to exclude in-out and out-in. (ie: "attenuation,positive_only")
@@ -313,15 +313,21 @@ struct ObjectGDExtension {
StringName parent_class_name;
StringName class_name;
bool editor_class = false;
+ bool reloadable = false;
bool is_virtual = false;
bool is_abstract = false;
+ bool is_exposed = true;
GDExtensionClassSet set;
GDExtensionClassGet get;
GDExtensionClassGetPropertyList get_property_list;
GDExtensionClassFreePropertyList free_property_list;
GDExtensionClassPropertyCanRevert property_can_revert;
GDExtensionClassPropertyGetRevert property_get_revert;
+ GDExtensionClassValidateProperty validate_property;
+#ifndef DISABLE_DEPRECATED
GDExtensionClassNotification notification;
+#endif // DISABLE_DEPRECATED
+ GDExtensionClassNotification2 notification2;
GDExtensionClassToString to_string;
GDExtensionClassReference reference;
GDExtensionClassReference unreference;
@@ -342,6 +348,15 @@ struct ObjectGDExtension {
GDExtensionClassCreateInstance create_instance;
GDExtensionClassFreeInstance free_instance;
GDExtensionClassGetVirtual get_virtual;
+ GDExtensionClassGetVirtualCallData get_virtual_call_data;
+ GDExtensionClassCallVirtualWithData call_virtual_with_data;
+ GDExtensionClassRecreateInstance recreate_instance;
+
+#ifdef TOOLS_ENABLED
+ void *tracking_userdata = nullptr;
+ void (*track_instance)(void *p_userdata, void *p_instance);
+ void (*untrack_instance)(void *p_userdata, void *p_instance);
+#endif
};
#define GDVIRTUAL_CALL(m_name, ...) _gdvirtual_##m_name##_call<false>(__VA_ARGS__)
@@ -382,6 +397,7 @@ private:
friend class ::ClassDB; \
\
public: \
+ typedef m_class self_type; \
static constexpr bool _class_is_enabled = !bool(GD_IS_DEFINED(ClassDB_Disable_##m_class)) && m_inherits::_class_is_enabled; \
virtual String get_class() const override { \
if (_get_extension()) { \
@@ -485,7 +501,7 @@ protected:
if (!p_reversed) { \
m_inherits::_get_property_listv(p_list, p_reversed); \
} \
- p_list->push_back(PropertyInfo(Variant::NIL, get_class_static(), PROPERTY_HINT_NONE, String(), PROPERTY_USAGE_CATEGORY)); \
+ p_list->push_back(PropertyInfo(Variant::NIL, get_class_static(), PROPERTY_HINT_NONE, get_class_static(), PROPERTY_USAGE_CATEGORY)); \
if (!_is_gpl_reversed()) { \
::ClassDB::get_property_list(#m_class, p_list, true, this); \
} \
@@ -557,6 +573,8 @@ class ScriptInstance;
class Object {
public:
+ typedef Object self_type;
+
enum ConnectFlags {
CONNECT_DEFERRED = 1,
CONNECT_PERSIST = 2, // hint for scene to save this connection
@@ -740,6 +758,16 @@ protected:
bool _disconnect(const StringName &p_signal, const Callable &p_callable, bool p_force = false);
+#ifdef TOOLS_ENABLED
+ struct VirtualMethodTracker {
+ void **method;
+ bool *initialized;
+ VirtualMethodTracker *next;
+ };
+
+ mutable VirtualMethodTracker *virtual_method_list = nullptr;
+#endif
+
public: // Should be protected, but bug in clang++.
static void initialize_class();
_FORCE_INLINE_ static void register_custom_data_to_otdb() {}
@@ -771,7 +799,8 @@ public:
enum {
NOTIFICATION_POSTINITIALIZE = 0,
- NOTIFICATION_PREDELETE = 1
+ NOTIFICATION_PREDELETE = 1,
+ NOTIFICATION_EXTENSION_RELOADED = 2,
};
/* TYPE API */
@@ -941,6 +970,12 @@ public:
// Used on creation by binding only.
void set_instance_binding(void *p_token, void *p_binding, const GDExtensionInstanceBindingCallbacks *p_callbacks);
bool has_instance_binding(void *p_token);
+ void free_instance_binding(void *p_token);
+
+#ifdef TOOLS_ENABLED
+ void clear_internal_extension();
+ void reset_internal_extension(ObjectGDExtension *p_extension);
+#endif
void clear_internal_resource_paths();
diff --git a/core/object/ref_counted.h b/core/object/ref_counted.h
index 3386514706..228373d662 100644
--- a/core/object/ref_counted.h
+++ b/core/object/ref_counted.h
@@ -71,7 +71,7 @@ class Ref {
}
void ref_pointer(T *p_ref) {
- ERR_FAIL_COND(!p_ref);
+ ERR_FAIL_NULL(p_ref);
if (p_ref->init_ref()) {
reference = p_ref;
diff --git a/core/object/script_instance.cpp b/core/object/script_instance.cpp
new file mode 100644
index 0000000000..303b127db1
--- /dev/null
+++ b/core/object/script_instance.cpp
@@ -0,0 +1,71 @@
+/**************************************************************************/
+/* script_instance.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#include "script_instance.h"
+
+#include "core/object/script_language.h"
+
+Variant ScriptInstance::call_const(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) {
+ return callp(p_method, p_args, p_argcount, r_error);
+}
+
+void ScriptInstance::get_property_state(List<Pair<StringName, Variant>> &state) {
+ List<PropertyInfo> pinfo;
+ get_property_list(&pinfo);
+ for (const PropertyInfo &E : pinfo) {
+ if (E.usage & PROPERTY_USAGE_STORAGE) {
+ Pair<StringName, Variant> p;
+ p.first = E.name;
+ if (get(p.first, p.second)) {
+ state.push_back(p);
+ }
+ }
+ }
+}
+
+void ScriptInstance::property_set_fallback(const StringName &, const Variant &, bool *r_valid) {
+ if (r_valid) {
+ *r_valid = false;
+ }
+}
+
+Variant ScriptInstance::property_get_fallback(const StringName &, bool *r_valid) {
+ if (r_valid) {
+ *r_valid = false;
+ }
+ return Variant();
+}
+
+const Variant ScriptInstance::get_rpc_config() const {
+ return get_script()->get_rpc_config();
+}
+
+ScriptInstance::~ScriptInstance() {
+}
diff --git a/core/object/script_instance.h b/core/object/script_instance.h
new file mode 100644
index 0000000000..df978a25ea
--- /dev/null
+++ b/core/object/script_instance.h
@@ -0,0 +1,98 @@
+/**************************************************************************/
+/* script_instance.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#ifndef SCRIPT_INSTANCE_H
+#define SCRIPT_INSTANCE_H
+
+#include "core/object/ref_counted.h"
+
+class Script;
+class ScriptLanguage;
+
+class ScriptInstance {
+public:
+ virtual bool set(const StringName &p_name, const Variant &p_value) = 0;
+ virtual bool get(const StringName &p_name, Variant &r_ret) const = 0;
+ virtual void get_property_list(List<PropertyInfo> *p_properties) const = 0;
+ virtual Variant::Type get_property_type(const StringName &p_name, bool *r_is_valid = nullptr) const = 0;
+ virtual void validate_property(PropertyInfo &p_property) const = 0;
+
+ virtual bool property_can_revert(const StringName &p_name) const = 0;
+ virtual bool property_get_revert(const StringName &p_name, Variant &r_ret) const = 0;
+
+ virtual Object *get_owner() { return nullptr; }
+ virtual void get_property_state(List<Pair<StringName, Variant>> &state);
+
+ virtual void get_method_list(List<MethodInfo> *p_list) const = 0;
+ virtual bool has_method(const StringName &p_method) const = 0;
+
+ virtual Variant callp(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) = 0;
+
+ template <typename... VarArgs>
+ Variant call(const StringName &p_method, VarArgs... p_args) {
+ Variant args[sizeof...(p_args) + 1] = { p_args..., Variant() }; // +1 makes sure zero sized arrays are also supported.
+ const Variant *argptrs[sizeof...(p_args) + 1];
+ for (uint32_t i = 0; i < sizeof...(p_args); i++) {
+ argptrs[i] = &args[i];
+ }
+ Callable::CallError cerr;
+ return callp(p_method, sizeof...(p_args) == 0 ? nullptr : (const Variant **)argptrs, sizeof...(p_args), cerr);
+ }
+
+ virtual Variant call_const(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error); // implement if language supports const functions
+ virtual void notification(int p_notification, bool p_reversed = false) = 0;
+ virtual String to_string(bool *r_valid) {
+ if (r_valid) {
+ *r_valid = false;
+ }
+ return String();
+ }
+
+ //this is used by script languages that keep a reference counter of their own
+ //you can make make Ref<> not die when it reaches zero, so deleting the reference
+ //depends entirely from the script
+
+ virtual void refcount_incremented() {}
+ virtual bool refcount_decremented() { return true; } //return true if it can die
+
+ virtual Ref<Script> get_script() const = 0;
+
+ virtual bool is_placeholder() const { return false; }
+
+ virtual void property_set_fallback(const StringName &p_name, const Variant &p_value, bool *r_valid);
+ virtual Variant property_get_fallback(const StringName &p_name, bool *r_valid);
+
+ virtual const Variant get_rpc_config() const;
+
+ virtual ScriptLanguage *get_language() = 0;
+ virtual ~ScriptInstance();
+};
+
+#endif // SCRIPT_INSTANCE_H
diff --git a/core/object/script_language.cpp b/core/object/script_language.cpp
index a8b0e426ae..011f4203ea 100644
--- a/core/object/script_language.cpp
+++ b/core/object/script_language.cpp
@@ -146,6 +146,7 @@ void Script::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_property_default_value", "property"), &Script::_get_property_default_value);
ClassDB::bind_method(D_METHOD("is_tool"), &Script::is_tool);
+ ClassDB::bind_method(D_METHOD("is_abstract"), &Script::is_abstract);
ADD_PROPERTY(PropertyInfo(Variant::STRING, "source_code", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "set_source_code", "get_source_code");
}
@@ -388,40 +389,6 @@ String ScriptServer::get_global_class_cache_file_path() {
////////////////////
-Variant ScriptInstance::call_const(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) {
- return callp(p_method, p_args, p_argcount, r_error);
-}
-
-void ScriptInstance::get_property_state(List<Pair<StringName, Variant>> &state) {
- List<PropertyInfo> pinfo;
- get_property_list(&pinfo);
- for (const PropertyInfo &E : pinfo) {
- if (E.usage & PROPERTY_USAGE_STORAGE) {
- Pair<StringName, Variant> p;
- p.first = E.name;
- if (get(p.first, p.second)) {
- state.push_back(p);
- }
- }
- }
-}
-
-void ScriptInstance::property_set_fallback(const StringName &, const Variant &, bool *r_valid) {
- if (r_valid) {
- *r_valid = false;
- }
-}
-
-Variant ScriptInstance::property_get_fallback(const StringName &, bool *r_valid) {
- if (r_valid) {
- *r_valid = false;
- }
- return Variant();
-}
-
-ScriptInstance::~ScriptInstance() {
-}
-
ScriptCodeCompletionCache *ScriptCodeCompletionCache::singleton = nullptr;
ScriptCodeCompletionCache::ScriptCodeCompletionCache() {
singleton = this;
@@ -482,7 +449,6 @@ TypedArray<int> ScriptLanguage::CodeCompletionOption::get_option_characteristics
}
charac.push_back(matches.size());
charac.push_back((matches[0].first == 0) ? 0 : 1);
- charac.push_back(location);
const char32_t *target_char = &p_base[0];
int bad_case = 0;
for (const Pair<int, int> &match_segment : matches) {
@@ -494,6 +460,7 @@ TypedArray<int> ScriptLanguage::CodeCompletionOption::get_option_characteristics
}
}
charac.push_back(bad_case);
+ charac.push_back(location);
charac.push_back(matches[0].first);
last_matches = matches;
return charac;
diff --git a/core/object/script_language.h b/core/object/script_language.h
index 3ea6a6e4c3..3e4041d173 100644
--- a/core/object/script_language.h
+++ b/core/object/script_language.h
@@ -33,6 +33,7 @@
#include "core/doc_data.h"
#include "core/io/resource.h"
+#include "core/object/script_instance.h"
#include "core/templates/pair.h"
#include "core/templates/rb_map.h"
#include "core/templates/safe_refcount.h"
@@ -101,7 +102,6 @@ public:
static bool are_languages_finished() { return languages_finished.is_set(); }
};
-class ScriptInstance;
class PlaceHolderScriptInstance;
class Script : public Resource {
@@ -141,14 +141,19 @@ public:
#ifdef TOOLS_ENABLED
virtual Vector<DocData::ClassDoc> get_documentation() const = 0;
+ virtual String get_class_icon_path() const = 0;
virtual PropertyInfo get_class_category() const;
#endif // TOOLS_ENABLED
+ // TODO: In the next compat breakage rename to `*_script_*` to disambiguate from `Object::has_method()`.
virtual bool has_method(const StringName &p_method) const = 0;
+ virtual bool has_static_method(const StringName &p_method) const { return false; }
+
virtual MethodInfo get_method_info(const StringName &p_method) const = 0;
virtual bool is_tool() const = 0;
virtual bool is_valid() const = 0;
+ virtual bool is_abstract() const = 0;
virtual ScriptLanguage *get_language() const = 0;
@@ -173,64 +178,6 @@ public:
Script() {}
};
-class ScriptInstance {
-public:
- virtual bool set(const StringName &p_name, const Variant &p_value) = 0;
- virtual bool get(const StringName &p_name, Variant &r_ret) const = 0;
- virtual void get_property_list(List<PropertyInfo> *p_properties) const = 0;
- virtual Variant::Type get_property_type(const StringName &p_name, bool *r_is_valid = nullptr) const = 0;
-
- virtual bool property_can_revert(const StringName &p_name) const = 0;
- virtual bool property_get_revert(const StringName &p_name, Variant &r_ret) const = 0;
-
- virtual Object *get_owner() { return nullptr; }
- virtual void get_property_state(List<Pair<StringName, Variant>> &state);
-
- virtual void get_method_list(List<MethodInfo> *p_list) const = 0;
- virtual bool has_method(const StringName &p_method) const = 0;
-
- virtual Variant callp(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) = 0;
-
- template <typename... VarArgs>
- Variant call(const StringName &p_method, VarArgs... p_args) {
- Variant args[sizeof...(p_args) + 1] = { p_args..., Variant() }; // +1 makes sure zero sized arrays are also supported.
- const Variant *argptrs[sizeof...(p_args) + 1];
- for (uint32_t i = 0; i < sizeof...(p_args); i++) {
- argptrs[i] = &args[i];
- }
- Callable::CallError cerr;
- return callp(p_method, sizeof...(p_args) == 0 ? nullptr : (const Variant **)argptrs, sizeof...(p_args), cerr);
- }
-
- virtual Variant call_const(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error); // implement if language supports const functions
- virtual void notification(int p_notification) = 0;
- virtual String to_string(bool *r_valid) {
- if (r_valid) {
- *r_valid = false;
- }
- return String();
- }
-
- //this is used by script languages that keep a reference counter of their own
- //you can make make Ref<> not die when it reaches zero, so deleting the reference
- //depends entirely from the script
-
- virtual void refcount_incremented() {}
- virtual bool refcount_decremented() { return true; } //return true if it can die
-
- virtual Ref<Script> get_script() const = 0;
-
- virtual bool is_placeholder() const { return false; }
-
- virtual void property_set_fallback(const StringName &p_name, const Variant &p_value, bool *r_valid);
- virtual Variant property_get_fallback(const StringName &p_name, bool *r_valid);
-
- virtual const Variant get_rpc_config() const { return get_script()->get_rpc_config(); }
-
- virtual ScriptLanguage *get_language() = 0;
- virtual ~ScriptInstance();
-};
-
class ScriptCodeCompletionCache {
static ScriptCodeCompletionCache *singleton;
@@ -292,6 +239,7 @@ public:
virtual void get_reserved_words(List<String> *p_words) const = 0;
virtual bool is_control_flow_keyword(String p_string) const = 0;
virtual void get_comment_delimiters(List<String> *p_delimiters) const = 0;
+ virtual void get_doc_comment_delimiters(List<String> *p_delimiters) const = 0;
virtual void get_string_delimiters(List<String> *p_delimiters) const = 0;
virtual Ref<Script> make_template(const String &p_template, const String &p_class_name, const String &p_base_class_name) const { return Ref<Script>(); }
virtual Vector<ScriptTemplate> get_built_in_templates(StringName p_object) { return Vector<ScriptTemplate>(); }
@@ -299,7 +247,9 @@ public:
virtual bool validate(const String &p_script, const String &p_path = "", List<String> *r_functions = nullptr, List<ScriptError> *r_errors = nullptr, List<Warning> *r_warnings = nullptr, HashSet<int> *r_safe_lines = nullptr) const = 0;
virtual String validate_path(const String &p_path) const { return ""; }
virtual Script *create_script() const = 0;
+#ifndef DISABLE_DEPRECATED
virtual bool has_named_classes() const = 0;
+#endif
virtual bool supports_builtin_mode() const = 0;
virtual bool supports_documentation() const { return false; }
virtual bool can_inherit_from_file() const { return false; }
@@ -342,14 +292,16 @@ public:
Vector<Pair<int, int>> matches;
Vector<Pair<int, int>> last_matches = { { -1, -1 } }; // This value correspond to an impossible match
int location = LOCATION_OTHER;
+ String theme_color_name;
CodeCompletionOption() {}
- CodeCompletionOption(const String &p_text, CodeCompletionKind p_kind, int p_location = LOCATION_OTHER) {
+ CodeCompletionOption(const String &p_text, CodeCompletionKind p_kind, int p_location = LOCATION_OTHER, const String &p_theme_color_name = "") {
display = p_text;
insert_text = p_text;
kind = p_kind;
location = p_location;
+ theme_color_name = p_theme_color_name;
}
TypedArray<int> get_option_characteristics(const String &p_base);
@@ -462,6 +414,7 @@ public:
virtual bool get(const StringName &p_name, Variant &r_ret) const override;
virtual void get_property_list(List<PropertyInfo> *p_properties) const override;
virtual Variant::Type get_property_type(const StringName &p_name, bool *r_is_valid = nullptr) const override;
+ virtual void validate_property(PropertyInfo &p_property) const override {}
virtual bool property_can_revert(const StringName &p_name) const override { return false; };
virtual bool property_get_revert(const StringName &p_name, Variant &r_ret) const override { return false; };
@@ -473,7 +426,7 @@ public:
r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
return Variant();
}
- virtual void notification(int p_notification) override {}
+ virtual void notification(int p_notification, bool p_reversed = false) override {}
virtual Ref<Script> get_script() const override { return script; }
diff --git a/core/object/script_language_extension.cpp b/core/object/script_language_extension.cpp
index 0df9d58334..e326baf7eb 100644
--- a/core/object/script_language_extension.cpp
+++ b/core/object/script_language_extension.cpp
@@ -52,12 +52,15 @@ void ScriptExtension::_bind_methods() {
GDVIRTUAL_BIND(_reload, "keep_state");
GDVIRTUAL_BIND(_get_documentation);
+ GDVIRTUAL_BIND(_get_class_icon_path);
GDVIRTUAL_BIND(_has_method, "method");
+ GDVIRTUAL_BIND(_has_static_method, "method");
GDVIRTUAL_BIND(_get_method_info, "method");
GDVIRTUAL_BIND(_is_tool);
GDVIRTUAL_BIND(_is_valid);
+ GDVIRTUAL_BIND(_is_abstract);
GDVIRTUAL_BIND(_get_language);
GDVIRTUAL_BIND(_has_script_signal, "signal");
@@ -89,6 +92,7 @@ void ScriptLanguageExtension::_bind_methods() {
GDVIRTUAL_BIND(_get_reserved_words);
GDVIRTUAL_BIND(_is_control_flow_keyword, "keyword");
GDVIRTUAL_BIND(_get_comment_delimiters);
+ GDVIRTUAL_BIND(_get_doc_comment_delimiters);
GDVIRTUAL_BIND(_get_string_delimiters);
GDVIRTUAL_BIND(_make_template, "template", "class_name", "base_class_name");
GDVIRTUAL_BIND(_get_built_in_templates, "object");
@@ -97,7 +101,9 @@ void ScriptLanguageExtension::_bind_methods() {
GDVIRTUAL_BIND(_validate_path, "path");
GDVIRTUAL_BIND(_create_script);
+#ifndef DISABLE_DEPRECATED
GDVIRTUAL_BIND(_has_named_classes);
+#endif
GDVIRTUAL_BIND(_supports_builtin_mode);
GDVIRTUAL_BIND(_supports_documentation);
GDVIRTUAL_BIND(_can_inherit_from_file);
diff --git a/core/object/script_language_extension.h b/core/object/script_language_extension.h
index 1a0ec29479..5f3a70cad8 100644
--- a/core/object/script_language_extension.h
+++ b/core/object/script_language_extension.h
@@ -77,6 +77,7 @@ public:
EXBIND1R(Error, reload, bool)
GDVIRTUAL0RC(TypedArray<Dictionary>, _get_documentation)
+ GDVIRTUAL0RC(String, _get_class_icon_path)
#ifdef TOOLS_ENABLED
virtual Vector<DocData::ClassDoc> get_documentation() const override {
TypedArray<Dictionary> doc;
@@ -89,9 +90,16 @@ public:
return class_doc;
}
+
+ virtual String get_class_icon_path() const override {
+ String ret;
+ GDVIRTUAL_CALL(_get_class_icon_path, ret);
+ return ret;
+ }
#endif // TOOLS_ENABLED
EXBIND1RC(bool, has_method, const StringName &)
+ EXBIND1RC(bool, has_static_method, const StringName &)
GDVIRTUAL1RC(Dictionary, _get_method_info, const StringName &)
virtual MethodInfo get_method_info(const StringName &p_method) const override {
@@ -103,6 +111,12 @@ public:
EXBIND0RC(bool, is_tool)
EXBIND0RC(bool, is_valid)
+ virtual bool is_abstract() const override {
+ bool abst;
+ return GDVIRTUAL_CALL(_is_abstract, abst) && abst;
+ }
+ GDVIRTUAL0RC(bool, _is_abstract)
+
EXBIND0RC(ScriptLanguage *, get_language)
EXBIND1RC(bool, has_script_signal, const StringName &)
@@ -227,6 +241,16 @@ public:
}
}
+ GDVIRTUAL0RC(Vector<String>, _get_doc_comment_delimiters)
+
+ virtual void get_doc_comment_delimiters(List<String> *p_words) const override {
+ Vector<String> ret;
+ GDVIRTUAL_CALL(_get_doc_comment_delimiters, ret);
+ for (int i = 0; i < ret.size(); i++) {
+ p_words->push_back(ret[i]);
+ }
+ }
+
GDVIRTUAL0RC(Vector<String>, _get_string_delimiters)
virtual void get_string_delimiters(List<String> *p_words) const override {
@@ -337,7 +361,9 @@ public:
GDVIRTUAL_REQUIRED_CALL(_create_script, ret);
return Object::cast_to<Script>(ret);
}
+#ifndef DISABLE_DEPRECATED
EXBIND0RC(bool, has_named_classes)
+#endif
EXBIND0RC(bool, supports_builtin_mode)
EXBIND0RC(bool, supports_documentation)
EXBIND0RC(bool, can_inherit_from_file)
@@ -623,7 +649,12 @@ VARIANT_ENUM_CAST(ScriptLanguageExtension::CodeCompletionLocation)
class ScriptInstanceExtension : public ScriptInstance {
public:
- const GDExtensionScriptInstanceInfo *native_info;
+ const GDExtensionScriptInstanceInfo2 *native_info;
+ bool free_native_info = false;
+ struct {
+ GDExtensionScriptInstanceNotification notification_func = nullptr;
+ } deprecated_native_info;
+
GDExtensionScriptInstanceDataPtr instance = nullptr;
// There should not be warnings on explicit casts.
@@ -650,9 +681,18 @@ public:
const GDExtensionPropertyInfo *pinfo = native_info->get_property_list_func(instance, &pcount);
#ifdef TOOLS_ENABLED
- Ref<Script> script = get_script();
- if (script.is_valid() && pcount > 0) {
- p_list->push_back(script->get_class_category());
+ if (pcount > 0) {
+ if (native_info->get_class_category_func) {
+ GDExtensionPropertyInfo gdext_class_category;
+ if (native_info->get_class_category_func(instance, &gdext_class_category)) {
+ p_list->push_back(PropertyInfo(gdext_class_category));
+ }
+ } else {
+ Ref<Script> script = get_script();
+ if (script.is_valid()) {
+ p_list->push_back(script->get_class_category());
+ }
+ }
}
#endif // TOOLS_ENABLED
@@ -675,6 +715,28 @@ public:
}
return Variant::NIL;
}
+ virtual void validate_property(PropertyInfo &p_property) const override {
+ if (native_info->validate_property_func) {
+ // GDExtension uses a StringName rather than a String for property name.
+ StringName prop_name = p_property.name;
+ GDExtensionPropertyInfo gdext_prop = {
+ (GDExtensionVariantType)p_property.type,
+ &prop_name,
+ &p_property.class_name,
+ (uint32_t)p_property.hint,
+ &p_property.hint_string,
+ p_property.usage,
+ };
+ if (native_info->validate_property_func(instance, &gdext_prop)) {
+ p_property.type = (Variant::Type)gdext_prop.type;
+ p_property.name = *reinterpret_cast<StringName *>(gdext_prop.name);
+ p_property.class_name = *reinterpret_cast<StringName *>(gdext_prop.class_name);
+ p_property.hint = (PropertyHint)gdext_prop.hint;
+ p_property.hint_string = *reinterpret_cast<String *>(gdext_prop.hint_string);
+ p_property.usage = gdext_prop.usage;
+ }
+ }
+ }
virtual bool property_can_revert(const StringName &p_name) const override {
if (native_info->property_can_revert_func) {
@@ -736,11 +798,16 @@ public:
return ret;
}
- virtual void notification(int p_notification) override {
+ virtual void notification(int p_notification, bool p_reversed = false) override {
if (native_info->notification_func) {
- native_info->notification_func(instance, p_notification);
+ native_info->notification_func(instance, p_notification, p_reversed);
+#ifndef DISABLE_DEPRECATED
+ } else if (deprecated_native_info.notification_func) {
+ deprecated_native_info.notification_func(instance, p_notification);
+#endif // DISABLE_DEPRECATED
}
}
+
virtual String to_string(bool *r_valid) override {
if (native_info->to_string_func) {
GDExtensionBool valid;
@@ -812,6 +879,9 @@ public:
if (native_info->free_func) {
native_info->free_func(instance);
}
+ if (free_native_info) {
+ memfree(const_cast<GDExtensionScriptInstanceInfo2 *>(native_info));
+ }
}
#if defined(__GNUC__) && !defined(__clang__)
diff --git a/core/object/undo_redo.cpp b/core/object/undo_redo.cpp
index f04961c760..a8f2ac5bfe 100644
--- a/core/object/undo_redo.cpp
+++ b/core/object/undo_redo.cpp
@@ -71,9 +71,7 @@ bool UndoRedo::_redo(bool p_execute) {
}
current_action++;
- if (p_execute) {
- _process_operation_list(actions.write[current_action].do_ops.front());
- }
+ _process_operation_list(actions.write[current_action].do_ops.front(), p_execute);
version++;
emit_signal(SNAME("version_changed"));
@@ -136,17 +134,22 @@ void UndoRedo::add_do_method(const Callable &p_callable) {
ERR_FAIL_COND(action_level <= 0);
ERR_FAIL_COND((current_action + 1) >= actions.size());
- Object *object = p_callable.get_object();
- ERR_FAIL_NULL(object);
+ ObjectID object_id = p_callable.get_object_id();
+ Object *object = ObjectDB::get_instance(object_id);
+ ERR_FAIL_COND(object_id.is_valid() && object == nullptr);
Operation do_op;
do_op.callable = p_callable;
- do_op.object = p_callable.get_object_id();
+ do_op.object = object_id;
if (Object::cast_to<RefCounted>(object)) {
do_op.ref = Ref<RefCounted>(Object::cast_to<RefCounted>(object));
}
do_op.type = Operation::TYPE_METHOD;
do_op.name = p_callable.get_method();
+ if (do_op.name == StringName()) {
+ // There's no `get_method()` for custom callables, so use `operator String()` instead.
+ do_op.name = static_cast<String>(p_callable);
+ }
actions.write[current_action + 1].do_ops.push_back(do_op);
}
@@ -161,24 +164,29 @@ void UndoRedo::add_undo_method(const Callable &p_callable) {
return;
}
- Object *object = p_callable.get_object();
- ERR_FAIL_NULL(object);
+ ObjectID object_id = p_callable.get_object_id();
+ Object *object = ObjectDB::get_instance(object_id);
+ ERR_FAIL_COND(object_id.is_valid() && object == nullptr);
Operation undo_op;
undo_op.callable = p_callable;
- undo_op.object = p_callable.get_object_id();
+ undo_op.object = object_id;
if (Object::cast_to<RefCounted>(object)) {
undo_op.ref = Ref<RefCounted>(Object::cast_to<RefCounted>(object));
}
undo_op.type = Operation::TYPE_METHOD;
undo_op.force_keep_in_merge_ends = force_keep_in_merge_ends;
undo_op.name = p_callable.get_method();
+ if (undo_op.name == StringName()) {
+ // There's no `get_method()` for custom callables, so use `operator String()` instead.
+ undo_op.name = static_cast<String>(p_callable);
+ }
actions.write[current_action + 1].undo_ops.push_back(undo_op);
}
void UndoRedo::add_do_property(Object *p_object, const StringName &p_property, const Variant &p_value) {
- ERR_FAIL_COND(p_object == nullptr);
+ ERR_FAIL_NULL(p_object);
ERR_FAIL_COND(action_level <= 0);
ERR_FAIL_COND((current_action + 1) >= actions.size());
Operation do_op;
@@ -194,7 +202,7 @@ void UndoRedo::add_do_property(Object *p_object, const StringName &p_property, c
}
void UndoRedo::add_undo_property(Object *p_object, const StringName &p_property, const Variant &p_value) {
- ERR_FAIL_COND(p_object == nullptr);
+ ERR_FAIL_NULL(p_object);
ERR_FAIL_COND(action_level <= 0);
ERR_FAIL_COND((current_action + 1) >= actions.size());
@@ -217,7 +225,7 @@ void UndoRedo::add_undo_property(Object *p_object, const StringName &p_property,
}
void UndoRedo::add_do_reference(Object *p_object) {
- ERR_FAIL_COND(p_object == nullptr);
+ ERR_FAIL_NULL(p_object);
ERR_FAIL_COND(action_level <= 0);
ERR_FAIL_COND((current_action + 1) >= actions.size());
Operation do_op;
@@ -231,7 +239,7 @@ void UndoRedo::add_do_reference(Object *p_object) {
}
void UndoRedo::add_undo_reference(Object *p_object) {
- ERR_FAIL_COND(p_object == nullptr);
+ ERR_FAIL_NULL(p_object);
ERR_FAIL_COND(action_level <= 0);
ERR_FAIL_COND((current_action + 1) >= actions.size());
@@ -311,7 +319,7 @@ void UndoRedo::commit_action(bool p_execute) {
}
}
-void UndoRedo::_process_operation_list(List<Operation>::Element *E) {
+void UndoRedo::_process_operation_list(List<Operation>::Element *E, bool p_execute) {
const int PREALLOCATE_ARGS_COUNT = 16;
LocalVector<const Variant *> args;
@@ -327,18 +335,20 @@ void UndoRedo::_process_operation_list(List<Operation>::Element *E) {
switch (op.type) {
case Operation::TYPE_METHOD: {
- Callable::CallError ce;
- Variant ret;
- op.callable.callp(nullptr, 0, ret, ce);
- if (ce.error != Callable::CallError::CALL_OK) {
- ERR_PRINT("Error calling UndoRedo method operation '" + String(op.name) + "': " + Variant::get_call_error_text(obj, op.name, nullptr, 0, ce));
- }
+ if (p_execute) {
+ Callable::CallError ce;
+ Variant ret;
+ op.callable.callp(nullptr, 0, ret, ce);
+ if (ce.error != Callable::CallError::CALL_OK) {
+ ERR_PRINT("Error calling UndoRedo method operation '" + String(op.name) + "': " + Variant::get_call_error_text(obj, op.name, nullptr, 0, ce));
+ }
#ifdef TOOLS_ENABLED
- Resource *res = Object::cast_to<Resource>(obj);
- if (res) {
- res->set_edited(true);
- }
+ Resource *res = Object::cast_to<Resource>(obj);
+ if (res) {
+ res->set_edited(true);
+ }
#endif
+ }
if (method_callback) {
Vector<Variant> binds;
@@ -363,13 +373,16 @@ void UndoRedo::_process_operation_list(List<Operation>::Element *E) {
}
} break;
case Operation::TYPE_PROPERTY: {
- obj->set(op.name, op.value);
+ if (p_execute) {
+ obj->set(op.name, op.value);
#ifdef TOOLS_ENABLED
- Resource *res = Object::cast_to<Resource>(obj);
- if (res) {
- res->set_edited(true);
- }
+ Resource *res = Object::cast_to<Resource>(obj);
+ if (res) {
+ res->set_edited(true);
+ }
#endif
+ }
+
if (property_callback) {
property_callback(prop_callback_ud, obj, op.name, op.value);
}
@@ -390,7 +403,7 @@ bool UndoRedo::undo() {
if (current_action < 0) {
return false; //nothing to redo
}
- _process_operation_list(actions.write[current_action].undo_ops.front());
+ _process_operation_list(actions.write[current_action].undo_ops.front(), true);
current_action--;
version--;
emit_signal(SNAME("version_changed"));
diff --git a/core/object/undo_redo.h b/core/object/undo_redo.h
index 389d8714f7..74a6bea732 100644
--- a/core/object/undo_redo.h
+++ b/core/object/undo_redo.h
@@ -85,7 +85,7 @@ private:
uint64_t version = 1;
void _pop_history_tail();
- void _process_operation_list(List<Operation>::Element *E);
+ void _process_operation_list(List<Operation>::Element *E, bool p_execute);
void _discard_redo();
bool _redo(bool p_execute);
diff --git a/core/object/worker_thread_pool.cpp b/core/object/worker_thread_pool.cpp
index 5ec3e1a1a8..2fcd0867e6 100644
--- a/core/object/worker_thread_pool.cpp
+++ b/core/object/worker_thread_pool.cpp
@@ -34,8 +34,8 @@
#include "core/os/thread_safe.h"
void WorkerThreadPool::Task::free_template_userdata() {
- ERR_FAIL_COND(!template_userdata);
- ERR_FAIL_COND(native_func_userdata == nullptr);
+ ERR_FAIL_NULL(template_userdata);
+ ERR_FAIL_NULL(native_func_userdata);
BaseTemplateUserdata *btu = (BaseTemplateUserdata *)native_func_userdata;
memdelete(btu);
}
@@ -75,10 +75,6 @@ void WorkerThreadPool::_process_task(Task *p_task) {
if (p_task->group) {
// Handling a group
bool do_post = false;
- Callable::CallError ce;
- Variant ret;
- Variant arg;
- Variant *argptr = &arg;
while (true) {
uint32_t work_index = p_task->group->index.postincrement();
@@ -91,8 +87,7 @@ void WorkerThreadPool::_process_task(Task *p_task) {
} else if (p_task->template_userdata) {
p_task->template_userdata->callback_indexed(work_index);
} else {
- arg = work_index;
- p_task->callable.callp((const Variant **)&argptr, 1, ret, ce);
+ p_task->callable.call(work_index);
}
// This is the only way to ensure posting is done when all tasks are really complete.
@@ -141,9 +136,7 @@ void WorkerThreadPool::_process_task(Task *p_task) {
p_task->template_userdata->callback();
memdelete(p_task->template_userdata);
} else {
- Callable::CallError ce;
- Variant ret;
- p_task->callable.callp(nullptr, 0, ret, ce);
+ p_task->callable.call();
}
task_mutex.lock();
diff --git a/core/os/keyboard.cpp b/core/os/keyboard.cpp
index 1a51624030..6078882839 100644
--- a/core/os/keyboard.cpp
+++ b/core/os/keyboard.cpp
@@ -146,6 +146,7 @@ static const _KeyCodeText _keycodes[] = {
{Key::FAVORITES ,"Favorites"},
{Key::SEARCH ,"Search"},
{Key::STANDBY ,"StandBy"},
+ {Key::OPENURL ,"OpenURL"},
{Key::LAUNCHMAIL ,"LaunchMail"},
{Key::LAUNCHMEDIA ,"LaunchMedia"},
{Key::LAUNCH0 ,"Launch0"},
@@ -238,6 +239,8 @@ static const _KeyCodeText _keycodes[] = {
{Key::BAR ,"Bar"},
{Key::BRACERIGHT ,"BraceRight"},
{Key::ASCIITILDE ,"AsciiTilde"},
+ {Key::YEN ,"Yen"},
+ {Key::SECTION ,"Section"},
{Key::NONE ,nullptr}
/* clang-format on */
};
diff --git a/core/os/keyboard.h b/core/os/keyboard.h
index cf276dc49f..785972d31d 100644
--- a/core/os/keyboard.h
+++ b/core/os/keyboard.h
@@ -33,6 +33,8 @@
#include "core/string/ustring.h"
+// Keep the values in this enum in sync with `_keycodes` in `keyboard.cpp`,
+// and the bindings in `core_constants.cpp`.
enum class Key {
NONE = 0,
// Special key: The strategy here is similar to the one used by toolkits,
diff --git a/core/os/main_loop.h b/core/os/main_loop.h
index 90cad009b1..b45eb38aeb 100644
--- a/core/os/main_loop.h
+++ b/core/os/main_loop.h
@@ -34,7 +34,6 @@
#include "core/input/input_event.h"
#include "core/object/gdvirtual.gen.inc"
#include "core/object/ref_counted.h"
-#include "core/object/script_language.h"
class MainLoop : public Object {
GDCLASS(MainLoop, Object);
diff --git a/core/os/memory.cpp b/core/os/memory.cpp
index 0d15b8dcf5..5f6216a5f1 100644
--- a/core/os/memory.cpp
+++ b/core/os/memory.cpp
@@ -74,7 +74,7 @@ void *Memory::alloc_static(size_t p_bytes, bool p_pad_align) {
void *mem = malloc(p_bytes + (prepad ? PAD_ALIGN : 0));
- ERR_FAIL_COND_V(!mem, nullptr);
+ ERR_FAIL_NULL_V(mem, nullptr);
alloc_count.increment();
@@ -127,7 +127,7 @@ void *Memory::realloc_static(void *p_memory, size_t p_bytes, bool p_pad_align) {
*s = p_bytes;
mem = (uint8_t *)realloc(mem, p_bytes + PAD_ALIGN);
- ERR_FAIL_COND_V(!mem, nullptr);
+ ERR_FAIL_NULL_V(mem, nullptr);
s = (uint64_t *)mem;
@@ -145,7 +145,7 @@ void *Memory::realloc_static(void *p_memory, size_t p_bytes, bool p_pad_align) {
}
void Memory::free_static(void *p_ptr, bool p_pad_align) {
- ERR_FAIL_COND(p_ptr == nullptr);
+ ERR_FAIL_NULL(p_ptr);
uint8_t *mem = (uint8_t *)p_ptr;
diff --git a/core/os/memory.h b/core/os/memory.h
index 45019894b1..a0524b0ea2 100644
--- a/core/os/memory.h
+++ b/core/os/memory.h
@@ -144,7 +144,7 @@ T *memnew_arr_template(size_t p_elements) {
size_t len = sizeof(T) * p_elements;
uint64_t *mem = (uint64_t *)Memory::alloc_static(len, true);
T *failptr = nullptr; //get rid of a warning
- ERR_FAIL_COND_V(!mem, failptr);
+ ERR_FAIL_NULL_V(mem, failptr);
*(mem - 1) = p_elements;
if (!std::is_trivially_constructible<T>::value) {
diff --git a/core/os/os.cpp b/core/os/os.cpp
index 38ea4a0fdd..991b179e1f 100644
--- a/core/os/os.cpp
+++ b/core/os/os.cpp
@@ -355,7 +355,7 @@ void OS::set_cmdline(const char *p_execpath, const List<String> &p_args, const L
}
String OS::get_unique_id() const {
- ERR_FAIL_V("");
+ return "";
}
int OS::get_processor_count() const {
diff --git a/core/os/os.h b/core/os/os.h
index 965dc1f912..cc5ebe1bc8 100644
--- a/core/os/os.h
+++ b/core/os/os.h
@@ -328,6 +328,10 @@ public:
virtual PreferredTextureFormat get_preferred_texture_format() const;
+ // Load GDExtensions specific to this platform.
+ // This is invoked by the GDExtensionManager after loading GDExtensions specified by the project.
+ virtual void load_platform_gdextensions() const {}
+
OS();
virtual ~OS();
};
diff --git a/core/os/pool_allocator.cpp b/core/os/pool_allocator.cpp
index c23196efdb..acbaed4ce8 100644
--- a/core/os/pool_allocator.cpp
+++ b/core/os/pool_allocator.cpp
@@ -305,7 +305,7 @@ Error PoolAllocator::resize(ID p_mem, int p_new_size) {
if (!e) {
mt_unlock();
- ERR_FAIL_COND_V(!e, ERR_INVALID_PARAMETER);
+ ERR_FAIL_NULL_V(e, ERR_INVALID_PARAMETER);
}
if (needs_locking && e->lock) {
@@ -431,7 +431,7 @@ bool PoolAllocator::is_locked(ID p_mem) const {
const void *PoolAllocator::get(ID p_mem) const {
if (!needs_locking) {
const Entry *e = get_entry(p_mem);
- ERR_FAIL_COND_V(!e, nullptr);
+ ERR_FAIL_NULL_V(e, nullptr);
return &pool[e->pos];
}
@@ -440,7 +440,7 @@ const void *PoolAllocator::get(ID p_mem) const {
if (!e) {
mt_unlock();
- ERR_FAIL_COND_V(!e, nullptr);
+ ERR_FAIL_NULL_V(e, nullptr);
}
if (e->lock == 0) {
mt_unlock();
@@ -463,7 +463,7 @@ const void *PoolAllocator::get(ID p_mem) const {
void *PoolAllocator::get(ID p_mem) {
if (!needs_locking) {
Entry *e = get_entry(p_mem);
- ERR_FAIL_COND_V(!e, nullptr);
+ ERR_FAIL_NULL_V(e, nullptr);
return &pool[e->pos];
}
@@ -472,7 +472,7 @@ void *PoolAllocator::get(ID p_mem) {
if (!e) {
mt_unlock();
- ERR_FAIL_COND_V(!e, nullptr);
+ ERR_FAIL_NULL_V(e, nullptr);
}
if (e->lock == 0) {
mt_unlock();
@@ -500,7 +500,7 @@ void PoolAllocator::unlock(ID p_mem) {
Entry *e = get_entry(p_mem);
if (!e) {
mt_unlock();
- ERR_FAIL_COND(!e);
+ ERR_FAIL_NULL(e);
}
if (e->lock == 0) {
mt_unlock();
@@ -540,7 +540,7 @@ void PoolAllocator::create_pool(void *p_mem, int p_size, int p_max_entries) {
PoolAllocator::PoolAllocator(int p_size, bool p_needs_locking, int p_max_entries) {
mem_ptr = memalloc(p_size);
- ERR_FAIL_COND(!mem_ptr);
+ ERR_FAIL_NULL(mem_ptr);
align = 1;
create_pool(mem_ptr, p_size, p_max_entries);
needs_locking = p_needs_locking;
diff --git a/core/string/node_path.cpp b/core/string/node_path.cpp
index af7c18741d..32e4564c5e 100644
--- a/core/string/node_path.cpp
+++ b/core/string/node_path.cpp
@@ -73,7 +73,7 @@ int NodePath::get_name_count() const {
}
StringName NodePath::get_name(int p_idx) const {
- ERR_FAIL_COND_V(!data, StringName());
+ ERR_FAIL_NULL_V(data, StringName());
ERR_FAIL_INDEX_V(p_idx, data->path.size(), StringName());
return data->path[p_idx];
}
@@ -87,7 +87,7 @@ int NodePath::get_subname_count() const {
}
StringName NodePath::get_subname(int p_idx) const {
- ERR_FAIL_COND_V(!data, StringName());
+ ERR_FAIL_NULL_V(data, StringName());
ERR_FAIL_INDEX_V(p_idx, data->subpath.size(), StringName());
return data->subpath[p_idx];
}
@@ -200,7 +200,7 @@ Vector<StringName> NodePath::get_subnames() const {
}
StringName NodePath::get_concatenated_names() const {
- ERR_FAIL_COND_V(!data, StringName());
+ ERR_FAIL_NULL_V(data, StringName());
if (!data->concatenated_path) {
int pc = data->path.size();
@@ -215,7 +215,7 @@ StringName NodePath::get_concatenated_names() const {
}
StringName NodePath::get_concatenated_subnames() const {
- ERR_FAIL_COND_V(!data, StringName());
+ ERR_FAIL_NULL_V(data, StringName());
if (!data->concatenated_subpath) {
int spc = data->subpath.size();
diff --git a/core/string/print_string.cpp b/core/string/print_string.cpp
index dcdde3c175..e3614be359 100644
--- a/core/string/print_string.cpp
+++ b/core/string/print_string.cpp
@@ -65,7 +65,7 @@ void remove_print_handler(const PrintHandlerList *p_handler) {
//OS::get_singleton()->print("print handler list is %p\n",print_handler_list);
_global_unlock();
- ERR_FAIL_COND(l == nullptr);
+ ERR_FAIL_NULL(l);
}
void __print_line(String p_string) {
diff --git a/core/string/string_name.cpp b/core/string/string_name.cpp
index 6099fea13f..4402e44ad4 100644
--- a/core/string/string_name.cpp
+++ b/core/string/string_name.cpp
@@ -390,7 +390,7 @@ StringName::StringName(const String &p_name, bool p_static) {
StringName StringName::search(const char *p_name) {
ERR_FAIL_COND_V(!configured, StringName());
- ERR_FAIL_COND_V(!p_name, StringName());
+ ERR_FAIL_NULL_V(p_name, StringName());
if (!p_name[0]) {
return StringName();
}
@@ -426,7 +426,7 @@ StringName StringName::search(const char *p_name) {
StringName StringName::search(const char32_t *p_name) {
ERR_FAIL_COND_V(!configured, StringName());
- ERR_FAIL_COND_V(!p_name, StringName());
+ ERR_FAIL_NULL_V(p_name, StringName());
if (!p_name[0]) {
return StringName();
}
diff --git a/core/string/translation.h b/core/string/translation.h
index ca8b460312..3f9dbcc476 100644
--- a/core/string/translation.h
+++ b/core/string/translation.h
@@ -33,7 +33,6 @@
#include "core/io/resource.h"
#include "core/object/gdvirtual.gen.inc"
-#include "core/object/script_language.h"
class Translation : public Resource {
GDCLASS(Translation, Resource);
diff --git a/core/string/ustring.cpp b/core/string/ustring.cpp
index 12e6423724..9be7c04158 100644
--- a/core/string/ustring.cpp
+++ b/core/string/ustring.cpp
@@ -1493,9 +1493,9 @@ String String::num(double p_num, int p_decimals) {
if (p_decimals < 0) {
p_decimals = 14;
- const double abs_num = ABS(p_num);
+ const double abs_num = Math::abs(p_num);
if (abs_num > 10) {
- // We want to align the digits to the above sane default, so we only
+ // We want to align the digits to the above reasonable default, so we only
// need to subtract log10 for numbers with a positive power of ten.
p_decimals -= (int)floor(log10(abs_num));
}
@@ -1750,7 +1750,7 @@ Vector<uint8_t> String::hex_decode() const {
void String::print_unicode_error(const String &p_message, bool p_critical) const {
if (p_critical) {
- print_error(vformat("Unicode parsing error, some characters were replaced with � (U+FFFD): %s", p_message));
+ print_error(vformat(U"Unicode parsing error, some characters were replaced with � (U+FFFD): %s", p_message));
} else {
print_error(vformat("Unicode parsing error: %s", p_message));
}
@@ -2763,12 +2763,13 @@ double String::to_float() const {
}
uint32_t String::hash(const char *p_cstr) {
+ // static_cast: avoid negative values on platforms where char is signed.
uint32_t hashv = 5381;
- uint32_t c = *p_cstr++;
+ uint32_t c = static_cast<uint8_t>(*p_cstr++);
while (c) {
hashv = ((hashv << 5) + hashv) + c; /* hash * 33 + c */
- c = *p_cstr++;
+ c = static_cast<uint8_t>(*p_cstr++);
}
return hashv;
@@ -2777,28 +2778,35 @@ uint32_t String::hash(const char *p_cstr) {
uint32_t String::hash(const char *p_cstr, int p_len) {
uint32_t hashv = 5381;
for (int i = 0; i < p_len; i++) {
- hashv = ((hashv << 5) + hashv) + p_cstr[i]; /* hash * 33 + c */
+ // static_cast: avoid negative values on platforms where char is signed.
+ hashv = ((hashv << 5) + hashv) + static_cast<uint8_t>(p_cstr[i]); /* hash * 33 + c */
}
return hashv;
}
uint32_t String::hash(const wchar_t *p_cstr, int p_len) {
+ // Avoid negative values on platforms where wchar_t is signed. Account for different sizes.
+ using wide_unsigned = std::conditional<sizeof(wchar_t) == 2, uint16_t, uint32_t>::type;
+
uint32_t hashv = 5381;
for (int i = 0; i < p_len; i++) {
- hashv = ((hashv << 5) + hashv) + p_cstr[i]; /* hash * 33 + c */
+ hashv = ((hashv << 5) + hashv) + static_cast<wide_unsigned>(p_cstr[i]); /* hash * 33 + c */
}
return hashv;
}
uint32_t String::hash(const wchar_t *p_cstr) {
+ // Avoid negative values on platforms where wchar_t is signed. Account for different sizes.
+ using wide_unsigned = std::conditional<sizeof(wchar_t) == 2, uint16_t, uint32_t>::type;
+
uint32_t hashv = 5381;
- uint32_t c = *p_cstr++;
+ uint32_t c = static_cast<wide_unsigned>(*p_cstr++);
while (c) {
hashv = ((hashv << 5) + hashv) + c; /* hash * 33 + c */
- c = *p_cstr++;
+ c = static_cast<wide_unsigned>(*p_cstr++);
}
return hashv;
@@ -3635,6 +3643,23 @@ String String::repeat(int p_count) const {
return new_string;
}
+String String::reverse() const {
+ int len = length();
+ if (len <= 1) {
+ return *this;
+ }
+ String new_string;
+ new_string.resize(len + 1);
+
+ const char32_t *src = ptr();
+ char32_t *dst = new_string.ptrw();
+ for (int i = 0; i < len; i++) {
+ dst[i] = src[len - i - 1];
+ }
+ dst[len] = _null;
+ return new_string;
+}
+
String String::left(int p_len) const {
if (p_len < 0) {
p_len = length() + p_len;
@@ -3648,7 +3673,9 @@ String String::left(int p_len) const {
return *this;
}
- return substr(0, p_len);
+ String s;
+ s.copy_from_unchecked(&get_data()[0], p_len);
+ return s;
}
String String::right(int p_len) const {
@@ -3664,7 +3691,9 @@ String String::right(int p_len) const {
return *this;
}
- return substr(length() - p_len);
+ String s;
+ s.copy_from_unchecked(&get_data()[length() - p_len], p_len);
+ return s;
}
char32_t String::unicode_at(int p_idx) const {
@@ -3945,24 +3974,22 @@ bool String::is_absolute_path() const {
}
}
-static _FORCE_INLINE_ bool _is_valid_identifier_bit(int p_index, char32_t p_char) {
- if (p_index == 0 && is_digit(p_char)) {
- return false; // No start with number plz.
- }
- return is_ascii_identifier_char(p_char);
-}
-
String String::validate_identifier() const {
if (is_empty()) {
return "_"; // Empty string is not a valid identifier;
}
- String result = *this;
+ String result;
+ if (is_digit(operator[](0))) {
+ result = "_" + *this;
+ } else {
+ result = *this;
+ }
+
int len = result.length();
char32_t *buffer = result.ptrw();
-
for (int i = 0; i < len; i++) {
- if (!_is_valid_identifier_bit(i, buffer[i])) {
+ if (!is_ascii_identifier_char(buffer[i])) {
buffer[i] = '_';
}
}
@@ -3977,10 +4004,14 @@ bool String::is_valid_identifier() const {
return false;
}
+ if (is_digit(operator[](0))) {
+ return false;
+ }
+
const char32_t *str = &operator[](0);
for (int i = 0; i < len; i++) {
- if (!_is_valid_identifier_bit(i, str[i])) {
+ if (!is_ascii_identifier_char(str[i])) {
return false;
}
}
@@ -4869,8 +4900,8 @@ String String::sprintf(const Array &values, bool *error) const {
}
double value = values[value_index];
- bool is_negative = (value < 0);
- String str = String::num(ABS(value), min_decimals);
+ bool is_negative = signbit(value);
+ String str = String::num(Math::abs(value), min_decimals);
const bool is_finite = Math::is_finite(value);
// Pad decimals out.
@@ -4932,7 +4963,7 @@ String String::sprintf(const Array &values, bool *error) const {
String str = "(";
for (int i = 0; i < count; i++) {
double val = vec[i];
- String number_str = String::num(ABS(val), min_decimals);
+ String number_str = String::num(Math::abs(val), min_decimals);
const bool is_finite = Math::is_finite(val);
// Pad decimals out.
diff --git a/core/string/ustring.h b/core/string/ustring.h
index 295625395d..f45392eee1 100644
--- a/core/string/ustring.h
+++ b/core/string/ustring.h
@@ -305,6 +305,7 @@ public:
String replace(const char *p_key, const char *p_with) const;
String replacen(const String &p_key, const String &p_with) const;
String repeat(int p_count) const;
+ String reverse() const;
String insert(int p_at_pos, const String &p_string) const;
String erase(int p_pos, int p_chars = 1) const;
String pad_decimals(int p_digits) const;
diff --git a/core/templates/command_queue_mt.h b/core/templates/command_queue_mt.h
index b7dd1bae34..7e480653ac 100644
--- a/core/templates/command_queue_mt.h
+++ b/core/templates/command_queue_mt.h
@@ -409,7 +409,7 @@ public:
}
void wait_and_flush() {
- ERR_FAIL_COND(!sync);
+ ERR_FAIL_NULL(sync);
sync->wait();
_flush();
}
diff --git a/core/templates/cowdata.h b/core/templates/cowdata.h
index e33822fedf..46d9797d6c 100644
--- a/core/templates/cowdata.h
+++ b/core/templates/cowdata.h
@@ -90,6 +90,10 @@ private:
}
_FORCE_INLINE_ bool _get_alloc_size_checked(size_t p_elements, size_t *out) const {
+ if (unlikely(p_elements == 0)) {
+ *out = 0;
+ return true;
+ }
#if defined(__GNUC__)
size_t o;
size_t p;
@@ -101,13 +105,12 @@ private:
if (__builtin_add_overflow(o, static_cast<size_t>(32), &p)) {
return false; // No longer allocated here.
}
- return true;
#else
// Speed is more important than correctness here, do the operations unchecked
// and hope for the best.
*out = _get_alloc_size(p_elements);
- return true;
#endif
+ return *out;
}
void _unref(void *p_data);
@@ -286,7 +289,7 @@ Error CowData<T>::resize(int p_size) {
if (current_size == 0) {
// alloc from scratch
uint32_t *ptr = (uint32_t *)Memory::alloc_static(alloc_size, true);
- ERR_FAIL_COND_V(!ptr, ERR_OUT_OF_MEMORY);
+ ERR_FAIL_NULL_V(ptr, ERR_OUT_OF_MEMORY);
*(ptr - 1) = 0; //size, currently none
new (ptr - 2) SafeNumeric<uint32_t>(1); //refcount
@@ -294,7 +297,7 @@ Error CowData<T>::resize(int p_size) {
} else {
uint32_t *_ptrnew = (uint32_t *)Memory::realloc_static(_ptr, alloc_size, true);
- ERR_FAIL_COND_V(!_ptrnew, ERR_OUT_OF_MEMORY);
+ ERR_FAIL_NULL_V(_ptrnew, ERR_OUT_OF_MEMORY);
new (_ptrnew - 2) SafeNumeric<uint32_t>(rc); //refcount
_ptr = (T *)(_ptrnew);
@@ -324,7 +327,7 @@ Error CowData<T>::resize(int p_size) {
if (alloc_size != current_alloc_size) {
uint32_t *_ptrnew = (uint32_t *)Memory::realloc_static(_ptr, alloc_size, true);
- ERR_FAIL_COND_V(!_ptrnew, ERR_OUT_OF_MEMORY);
+ ERR_FAIL_NULL_V(_ptrnew, ERR_OUT_OF_MEMORY);
new (_ptrnew - 2) SafeNumeric<uint32_t>(rc); //refcount
_ptr = (T *)(_ptrnew);
diff --git a/core/templates/list.h b/core/templates/list.h
index 809d7e1667..6393b942ff 100644
--- a/core/templates/list.h
+++ b/core/templates/list.h
@@ -219,7 +219,7 @@ private:
int size_cache = 0;
bool erase(const Element *p_I) {
- ERR_FAIL_COND_V(!p_I, false);
+ ERR_FAIL_NULL_V(p_I, false);
ERR_FAIL_COND_V(p_I->data != this, false);
if (first == p_I) {
diff --git a/core/templates/paged_allocator.h b/core/templates/paged_allocator.h
index deb2937771..72425a8c3d 100644
--- a/core/templates/paged_allocator.h
+++ b/core/templates/paged_allocator.h
@@ -144,7 +144,7 @@ public:
if (thread_safe) {
spin_lock.lock();
}
- ERR_FAIL_COND(page_pool != nullptr); //sanity check
+ ERR_FAIL_COND(page_pool != nullptr); // Safety check.
ERR_FAIL_COND(p_page_size == 0);
page_size = nearest_power_of_2_templated(p_page_size);
page_mask = page_size - 1;
diff --git a/core/templates/paged_array.h b/core/templates/paged_array.h
index 45b90869b9..863e3eef11 100644
--- a/core/templates/paged_array.h
+++ b/core/templates/paged_array.h
@@ -112,7 +112,7 @@ public:
}
void configure(uint32_t p_page_size) {
- ERR_FAIL_COND(page_pool != nullptr); //sanity check
+ ERR_FAIL_COND(page_pool != nullptr); // Safety check.
ERR_FAIL_COND(p_page_size == 0);
page_size = nearest_power_of_2_templated(p_page_size);
}
@@ -185,7 +185,7 @@ public:
uint32_t new_page_count = page_count + 1;
if (unlikely(new_page_count > max_pages_used)) {
- ERR_FAIL_COND(page_pool == nullptr); //sanity check
+ ERR_FAIL_NULL(page_pool); // Safety check.
_grow_page_array(); //keep out of inline
}
@@ -352,7 +352,7 @@ public:
}
void set_page_pool(PagedArrayPool<T> *p_page_pool) {
- ERR_FAIL_COND(max_pages_used > 0); //sanity check
+ ERR_FAIL_COND(max_pages_used > 0); // Safety check.
page_pool = p_page_pool;
page_size_mask = page_pool->get_page_size_mask();
diff --git a/core/templates/rb_map.h b/core/templates/rb_map.h
index 0a33809bb2..d373713669 100644
--- a/core/templates/rb_map.h
+++ b/core/templates/rb_map.h
@@ -96,6 +96,8 @@ public:
typedef KeyValue<K, V> ValueType;
struct Iterator {
+ friend class RBMap<K, V, C, A>;
+
_FORCE_INLINE_ KeyValue<K, V> &operator*() const {
return E->key_value();
}
diff --git a/core/templates/rid_owner.h b/core/templates/rid_owner.h
index e2ffabdaf0..e6c62ebf43 100644
--- a/core/templates/rid_owner.h
+++ b/core/templates/rid_owner.h
@@ -211,12 +211,12 @@ public:
}
void initialize_rid(RID p_rid) {
T *mem = get_or_null(p_rid, true);
- ERR_FAIL_COND(!mem);
+ ERR_FAIL_NULL(mem);
memnew_placement(mem, T);
}
void initialize_rid(RID p_rid, const T &p_value) {
T *mem = get_or_null(p_rid, true);
- ERR_FAIL_COND(!mem);
+ ERR_FAIL_NULL(mem);
memnew_placement(mem, T(p_value));
}
@@ -391,7 +391,7 @@ public:
_FORCE_INLINE_ void replace(const RID &p_rid, T *p_new_ptr) {
T **ptr = alloc.get_or_null(p_rid);
- ERR_FAIL_COND(!ptr);
+ ERR_FAIL_NULL(ptr);
*ptr = p_new_ptr;
}
diff --git a/core/templates/safe_refcount.h b/core/templates/safe_refcount.h
index bfc9f6fc9a..20fb0c6501 100644
--- a/core/templates/safe_refcount.h
+++ b/core/templates/safe_refcount.h
@@ -182,7 +182,7 @@ class SafeRefCount {
SafeNumeric<uint32_t> count;
#ifdef DEV_ENABLED
- _ALWAYS_INLINE_ void _check_unref_sanity() {
+ _ALWAYS_INLINE_ void _check_unref_safety() {
// This won't catch every misuse, but it's better than nothing.
CRASH_COND_MSG(count.get() == 0,
"Trying to unreference a SafeRefCount which is already zero is wrong and a symptom of it being misused.\n"
@@ -202,14 +202,14 @@ public:
_ALWAYS_INLINE_ bool unref() { // true if must be disposed of
#ifdef DEV_ENABLED
- _check_unref_sanity();
+ _check_unref_safety();
#endif
return count.decrement() == 0;
}
_ALWAYS_INLINE_ uint32_t unrefval() { // 0 if must be disposed of
#ifdef DEV_ENABLED
- _check_unref_sanity();
+ _check_unref_safety();
#endif
return count.decrement();
}
diff --git a/core/templates/self_list.h b/core/templates/self_list.h
index ff6fa953ae..fdf91beacc 100644
--- a/core/templates/self_list.h
+++ b/core/templates/self_list.h
@@ -105,6 +105,57 @@ public:
}
}
+ void sort() {
+ sort_custom<Comparator<T>>();
+ }
+
+ template <class C>
+ void sort_custom() {
+ if (_first == _last) {
+ return;
+ }
+
+ SelfList<T> *from = _first;
+ SelfList<T> *current = from;
+ SelfList<T> *to = from;
+
+ while (current) {
+ SelfList<T> *next = current->_next;
+
+ if (from != current) {
+ current->_prev = nullptr;
+ current->_next = from;
+
+ SelfList<T> *find = from;
+ C less;
+ while (find && less(*find->_self, *current->_self)) {
+ current->_prev = find;
+ current->_next = find->_next;
+ find = find->_next;
+ }
+
+ if (current->_prev) {
+ current->_prev->_next = current;
+ } else {
+ from = current;
+ }
+
+ if (current->_next) {
+ current->_next->_prev = current;
+ } else {
+ to = current;
+ }
+ } else {
+ current->_prev = nullptr;
+ current->_next = nullptr;
+ }
+
+ current = next;
+ }
+ _first = from;
+ _last = to;
+ }
+
_FORCE_INLINE_ SelfList<T> *first() { return _first; }
_FORCE_INLINE_ const SelfList<T> *first() const { return _first; }
diff --git a/core/typedefs.h b/core/typedefs.h
index 1dcba58188..24c247fd38 100644
--- a/core/typedefs.h
+++ b/core/typedefs.h
@@ -109,7 +109,7 @@ constexpr T ABS(T m_v) {
template <typename T>
constexpr const T SIGN(const T m_v) {
- return m_v == 0 ? 0.0f : (m_v < 0 ? -1.0f : +1.0f);
+ return m_v > 0 ? +1.0f : (m_v < 0 ? -1.0f : 0.0f);
}
template <typename T, typename T2>
diff --git a/core/variant/array.cpp b/core/variant/array.cpp
index 5a0ded6c01..ab0315ae34 100644
--- a/core/variant/array.cpp
+++ b/core/variant/array.cpp
@@ -52,7 +52,7 @@ public:
void Array::_ref(const Array &p_from) const {
ArrayPrivate *_fp = p_from._p;
- ERR_FAIL_COND(!_fp); // should NOT happen.
+ ERR_FAIL_NULL(_fp); // Should NOT happen.
if (_fp == _p) {
return; // whatever it is, nothing to do here move along
@@ -137,7 +137,7 @@ bool Array::recursive_equal(const Array &p_array, int recursion_count) const {
}
recursion_count++;
for (int i = 0; i < size; i++) {
- if (!a1[i].hash_compare(a2[i], recursion_count)) {
+ if (!a1[i].hash_compare(a2[i], recursion_count, false)) {
return false;
}
}
diff --git a/core/variant/binder_common.h b/core/variant/binder_common.h
index 9f8fb7e95e..34b54f1d00 100644
--- a/core/variant/binder_common.h
+++ b/core/variant/binder_common.h
@@ -404,13 +404,13 @@ void call_with_variant_args(T *p_instance, void (T::*p_method)(P...), const Vari
#ifdef DEBUG_METHODS_ENABLED
if ((size_t)p_argcount > sizeof...(P)) {
r_error.error = Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS;
- r_error.argument = sizeof...(P);
+ r_error.expected = sizeof...(P);
return;
}
if ((size_t)p_argcount < sizeof...(P)) {
r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;
- r_error.argument = sizeof...(P);
+ r_error.expected = sizeof...(P);
return;
}
#endif
@@ -422,7 +422,7 @@ void call_with_variant_args_dv(T *p_instance, void (T::*p_method)(P...), const V
#ifdef DEBUG_ENABLED
if ((size_t)p_argcount > sizeof...(P)) {
r_error.error = Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS;
- r_error.argument = sizeof...(P);
+ r_error.expected = sizeof...(P);
return;
}
#endif
@@ -433,7 +433,7 @@ void call_with_variant_args_dv(T *p_instance, void (T::*p_method)(P...), const V
#ifdef DEBUG_ENABLED
if (missing > dvs) {
r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;
- r_error.argument = sizeof...(P);
+ r_error.expected = sizeof...(P);
return;
}
#endif
@@ -455,13 +455,13 @@ void call_with_variant_argsc(T *p_instance, void (T::*p_method)(P...) const, con
#ifdef DEBUG_METHODS_ENABLED
if ((size_t)p_argcount > sizeof...(P)) {
r_error.error = Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS;
- r_error.argument = sizeof...(P);
+ r_error.expected = sizeof...(P);
return;
}
if ((size_t)p_argcount < sizeof...(P)) {
r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;
- r_error.argument = sizeof...(P);
+ r_error.expected = sizeof...(P);
return;
}
#endif
@@ -473,7 +473,7 @@ void call_with_variant_argsc_dv(T *p_instance, void (T::*p_method)(P...) const,
#ifdef DEBUG_ENABLED
if ((size_t)p_argcount > sizeof...(P)) {
r_error.error = Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS;
- r_error.argument = sizeof...(P);
+ r_error.expected = sizeof...(P);
return;
}
#endif
@@ -484,7 +484,7 @@ void call_with_variant_argsc_dv(T *p_instance, void (T::*p_method)(P...) const,
#ifdef DEBUG_ENABLED
if (missing > dvs) {
r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;
- r_error.argument = sizeof...(P);
+ r_error.expected = sizeof...(P);
return;
}
#endif
@@ -506,7 +506,7 @@ void call_with_variant_args_ret_dv(T *p_instance, R (T::*p_method)(P...), const
#ifdef DEBUG_ENABLED
if ((size_t)p_argcount > sizeof...(P)) {
r_error.error = Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS;
- r_error.argument = sizeof...(P);
+ r_error.expected = sizeof...(P);
return;
}
#endif
@@ -517,7 +517,7 @@ void call_with_variant_args_ret_dv(T *p_instance, R (T::*p_method)(P...), const
#ifdef DEBUG_ENABLED
if (missing > dvs) {
r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;
- r_error.argument = sizeof...(P);
+ r_error.expected = sizeof...(P);
return;
}
#endif
@@ -539,7 +539,7 @@ void call_with_variant_args_retc_dv(T *p_instance, R (T::*p_method)(P...) const,
#ifdef DEBUG_ENABLED
if ((size_t)p_argcount > sizeof...(P)) {
r_error.error = Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS;
- r_error.argument = sizeof...(P);
+ r_error.expected = sizeof...(P);
return;
}
#endif
@@ -550,7 +550,7 @@ void call_with_variant_args_retc_dv(T *p_instance, R (T::*p_method)(P...) const,
#ifdef DEBUG_ENABLED
if (missing > dvs) {
r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;
- r_error.argument = sizeof...(P);
+ r_error.expected = sizeof...(P);
return;
}
#endif
@@ -785,13 +785,13 @@ void call_with_variant_args_ret(T *p_instance, R (T::*p_method)(P...), const Var
#ifdef DEBUG_METHODS_ENABLED
if ((size_t)p_argcount > sizeof...(P)) {
r_error.error = Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS;
- r_error.argument = sizeof...(P);
+ r_error.expected = sizeof...(P);
return;
}
if ((size_t)p_argcount < sizeof...(P)) {
r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;
- r_error.argument = sizeof...(P);
+ r_error.expected = sizeof...(P);
return;
}
#endif
@@ -815,13 +815,13 @@ void call_with_variant_args_static_ret(R (*p_method)(P...), const Variant **p_ar
#ifdef DEBUG_METHODS_ENABLED
if ((size_t)p_argcount > sizeof...(P)) {
r_error.error = Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS;
- r_error.argument = sizeof...(P);
+ r_error.expected = sizeof...(P);
return;
}
if ((size_t)p_argcount < sizeof...(P)) {
r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;
- r_error.argument = sizeof...(P);
+ r_error.expected = sizeof...(P);
return;
}
#endif
@@ -833,13 +833,13 @@ void call_with_variant_args_static_ret(void (*p_method)(P...), const Variant **p
#ifdef DEBUG_METHODS_ENABLED
if ((size_t)p_argcount > sizeof...(P)) {
r_error.error = Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS;
- r_error.argument = sizeof...(P);
+ r_error.expected = sizeof...(P);
return;
}
if ((size_t)p_argcount < sizeof...(P)) {
r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;
- r_error.argument = sizeof...(P);
+ r_error.expected = sizeof...(P);
return;
}
#endif
@@ -851,13 +851,13 @@ void call_with_variant_args_retc(T *p_instance, R (T::*p_method)(P...) const, co
#ifdef DEBUG_METHODS_ENABLED
if ((size_t)p_argcount > sizeof...(P)) {
r_error.error = Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS;
- r_error.argument = sizeof...(P);
+ r_error.expected = sizeof...(P);
return;
}
if ((size_t)p_argcount < sizeof...(P)) {
r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;
- r_error.argument = sizeof...(P);
+ r_error.expected = sizeof...(P);
return;
}
#endif
@@ -882,7 +882,7 @@ void call_with_variant_args_retc_static_helper_dv(T *p_instance, R (*p_method)(T
#ifdef DEBUG_ENABLED
if ((size_t)p_argcount > sizeof...(P)) {
r_error.error = Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS;
- r_error.argument = sizeof...(P);
+ r_error.expected = sizeof...(P);
return;
}
#endif
@@ -893,7 +893,7 @@ void call_with_variant_args_retc_static_helper_dv(T *p_instance, R (*p_method)(T
#ifdef DEBUG_ENABLED
if (missing > dvs) {
r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;
- r_error.argument = sizeof...(P);
+ r_error.expected = sizeof...(P);
return;
}
#endif
@@ -928,7 +928,7 @@ void call_with_variant_args_static_helper_dv(T *p_instance, void (*p_method)(T *
#ifdef DEBUG_ENABLED
if ((size_t)p_argcount > sizeof...(P)) {
r_error.error = Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS;
- r_error.argument = sizeof...(P);
+ r_error.expected = sizeof...(P);
return;
}
#endif
@@ -939,7 +939,7 @@ void call_with_variant_args_static_helper_dv(T *p_instance, void (*p_method)(T *
#ifdef DEBUG_ENABLED
if (missing > dvs) {
r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;
- r_error.argument = sizeof...(P);
+ r_error.expected = sizeof...(P);
return;
}
#endif
@@ -961,7 +961,7 @@ void call_with_variant_args_static_ret_dv(R (*p_method)(P...), const Variant **p
#ifdef DEBUG_ENABLED
if ((size_t)p_argcount > sizeof...(P)) {
r_error.error = Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS;
- r_error.argument = sizeof...(P);
+ r_error.expected = sizeof...(P);
return;
}
#endif
@@ -972,7 +972,7 @@ void call_with_variant_args_static_ret_dv(R (*p_method)(P...), const Variant **p
#ifdef DEBUG_ENABLED
if (missing > dvs) {
r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;
- r_error.argument = sizeof...(P);
+ r_error.expected = sizeof...(P);
return;
}
#endif
@@ -994,7 +994,7 @@ void call_with_variant_args_static_dv(void (*p_method)(P...), const Variant **p_
#ifdef DEBUG_ENABLED
if ((size_t)p_argcount > sizeof...(P)) {
r_error.error = Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS;
- r_error.argument = sizeof...(P);
+ r_error.expected = sizeof...(P);
return;
}
#endif
@@ -1005,7 +1005,7 @@ void call_with_variant_args_static_dv(void (*p_method)(P...), const Variant **p_
#ifdef DEBUG_ENABLED
if (missing > dvs) {
r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;
- r_error.argument = sizeof...(P);
+ r_error.expected = sizeof...(P);
return;
}
#endif
diff --git a/core/variant/callable.cpp b/core/variant/callable.cpp
index 630873ec2e..0b1174c873 100644
--- a/core/variant/callable.cpp
+++ b/core/variant/callable.cpp
@@ -47,6 +47,13 @@ void Callable::callp(const Variant **p_arguments, int p_argcount, Variant &r_ret
r_call_error.expected = 0;
r_return_value = Variant();
} else if (is_custom()) {
+ if (!is_valid()) {
+ r_call_error.error = CallError::CALL_ERROR_INSTANCE_IS_NULL;
+ r_call_error.argument = 0;
+ r_call_error.expected = 0;
+ r_return_value = Variant();
+ return;
+ }
custom->call(p_arguments, p_argcount, r_return_value, r_call_error);
} else {
Object *obj = ObjectDB::get_instance(ObjectID(object));
@@ -465,20 +472,20 @@ Error Signal::emit(const Variant **p_arguments, int p_argcount) const {
Error Signal::connect(const Callable &p_callable, uint32_t p_flags) {
Object *obj = get_object();
- ERR_FAIL_COND_V(!obj, ERR_UNCONFIGURED);
+ ERR_FAIL_NULL_V(obj, ERR_UNCONFIGURED);
return obj->connect(name, p_callable, p_flags);
}
void Signal::disconnect(const Callable &p_callable) {
Object *obj = get_object();
- ERR_FAIL_COND(!obj);
+ ERR_FAIL_NULL(obj);
obj->disconnect(name, p_callable);
}
bool Signal::is_connected(const Callable &p_callable) const {
Object *obj = get_object();
- ERR_FAIL_COND_V(!obj, false);
+ ERR_FAIL_NULL_V(obj, false);
return obj->is_connected(name, p_callable);
}
@@ -500,7 +507,7 @@ Array Signal::get_connections() const {
}
Signal::Signal(const Object *p_object, const StringName &p_name) {
- ERR_FAIL_COND_MSG(p_object == nullptr, "Object argument to Signal constructor must be non-null");
+ ERR_FAIL_NULL_MSG(p_object, "Object argument to Signal constructor must be non-null.");
object = p_object->get_instance_id();
name = p_name;
diff --git a/core/variant/callable.h b/core/variant/callable.h
index 086e5d2a00..3ae424e9bf 100644
--- a/core/variant/callable.h
+++ b/core/variant/callable.h
@@ -69,6 +69,8 @@ public:
int expected = 0;
};
+ template <typename... VarArgs>
+ Variant call(VarArgs... p_args) const;
void callp(const Variant **p_arguments, int p_argcount, Variant &r_return_value, CallError &r_call_error) const;
void call_deferredp(const Variant **p_arguments, int p_argcount) const;
Variant callv(const Array &p_arguments) const;
diff --git a/core/variant/callable_bind.cpp b/core/variant/callable_bind.cpp
index e493e50467..9a6380a55f 100644
--- a/core/variant/callable_bind.cpp
+++ b/core/variant/callable_bind.cpp
@@ -88,7 +88,7 @@ ObjectID CallableCustomBind::get_object() const {
}
const Callable *CallableCustomBind::get_base_comparator() const {
- return &callable;
+ return callable.get_base_comparator();
}
int CallableCustomBind::get_bound_arguments_count() const {
@@ -222,7 +222,7 @@ ObjectID CallableCustomUnbind::get_object() const {
}
const Callable *CallableCustomUnbind::get_base_comparator() const {
- return &callable;
+ return callable.get_base_comparator();
}
int CallableCustomUnbind::get_bound_arguments_count() const {
@@ -245,9 +245,8 @@ void CallableCustomUnbind::get_bound_arguments(Vector<Variant> &r_arguments, int
}
void CallableCustomUnbind::call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const {
- if (argcount > p_argcount) {
+ if (p_argcount < argcount) {
r_call_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;
- r_call_error.argument = 0;
r_call_error.expected = argcount;
return;
}
@@ -255,9 +254,8 @@ void CallableCustomUnbind::call(const Variant **p_arguments, int p_argcount, Var
}
Error CallableCustomUnbind::rpc(int p_peer_id, const Variant **p_arguments, int p_argcount, Callable::CallError &r_call_error) const {
- if (argcount > p_argcount) {
+ if (p_argcount < argcount) {
r_call_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;
- r_call_error.argument = 0;
r_call_error.expected = argcount;
return ERR_UNCONFIGURED;
}
diff --git a/core/variant/container_type_validate.h b/core/variant/container_type_validate.h
index ffe1dc90a3..0a23c69cb4 100644
--- a/core/variant/container_type_validate.h
+++ b/core/variant/container_type_validate.h
@@ -113,7 +113,7 @@ struct ContainerTypeValidate {
return true; // This is fine, it's null.
}
Object *object = ObjectDB::get_instance(object_id);
- ERR_FAIL_COND_V_MSG(object == nullptr, false, "Attempted to " + String(p_operation) + " an invalid (previously freed?) object instance into a '" + String(where) + ".");
+ ERR_FAIL_NULL_V_MSG(object, false, "Attempted to " + String(p_operation) + " an invalid (previously freed?) object instance into a '" + String(where) + ".");
#else
Object *object = p_variant;
if (object == nullptr) {
diff --git a/core/variant/dictionary.cpp b/core/variant/dictionary.cpp
index f019273735..141ce25fa6 100644
--- a/core/variant/dictionary.cpp
+++ b/core/variant/dictionary.cpp
@@ -210,7 +210,7 @@ bool Dictionary::recursive_equal(const Dictionary &p_dictionary, int recursion_c
recursion_count++;
for (const KeyValue<Variant, Variant> &this_E : _p->variant_map) {
HashMap<Variant, Variant, VariantHasher, StringLikeVariantComparator>::ConstIterator other_E(p_dictionary._p->variant_map.find(this_E.key));
- if (!other_E || !this_E.value.hash_compare(other_E->value, recursion_count)) {
+ if (!other_E || !this_E.value.hash_compare(other_E->value, recursion_count, false)) {
return false;
}
}
@@ -248,7 +248,7 @@ void Dictionary::merge(const Dictionary &p_dictionary, bool p_overwrite) {
}
void Dictionary::_unref() const {
- ERR_FAIL_COND(!_p);
+ ERR_FAIL_NULL(_p);
if (_p->refcount.unref()) {
if (_p->read_only) {
memdelete(_p->read_only);
diff --git a/core/variant/method_ptrcall.h b/core/variant/method_ptrcall.h
index cbfb9cc257..79be85cae6 100644
--- a/core/variant/method_ptrcall.h
+++ b/core/variant/method_ptrcall.h
@@ -38,26 +38,26 @@
template <class T>
struct PtrToArg {};
-#define MAKE_PTRARG(m_type) \
- template <> \
- struct PtrToArg<m_type> { \
- _FORCE_INLINE_ static m_type convert(const void *p_ptr) { \
- return *reinterpret_cast<const m_type *>(p_ptr); \
- } \
- typedef m_type EncodeT; \
- _FORCE_INLINE_ static void encode(m_type p_val, void *p_ptr) { \
- *((m_type *)p_ptr) = p_val; \
- } \
- }; \
- template <> \
- struct PtrToArg<const m_type &> { \
- _FORCE_INLINE_ static m_type convert(const void *p_ptr) { \
- return *reinterpret_cast<const m_type *>(p_ptr); \
- } \
- typedef m_type EncodeT; \
- _FORCE_INLINE_ static void encode(m_type p_val, void *p_ptr) { \
- *((m_type *)p_ptr) = p_val; \
- } \
+#define MAKE_PTRARG(m_type) \
+ template <> \
+ struct PtrToArg<m_type> { \
+ _FORCE_INLINE_ static const m_type &convert(const void *p_ptr) { \
+ return *reinterpret_cast<const m_type *>(p_ptr); \
+ } \
+ typedef m_type EncodeT; \
+ _FORCE_INLINE_ static void encode(m_type p_val, void *p_ptr) { \
+ *((m_type *)p_ptr) = p_val; \
+ } \
+ }; \
+ template <> \
+ struct PtrToArg<const m_type &> { \
+ _FORCE_INLINE_ static const m_type &convert(const void *p_ptr) { \
+ return *reinterpret_cast<const m_type *>(p_ptr); \
+ } \
+ typedef m_type EncodeT; \
+ _FORCE_INLINE_ static void encode(m_type p_val, void *p_ptr) { \
+ *((m_type *)p_ptr) = p_val; \
+ } \
}
#define MAKE_PTRARGCONV(m_type, m_conv) \
@@ -85,7 +85,7 @@ struct PtrToArg {};
#define MAKE_PTRARG_BY_REFERENCE(m_type) \
template <> \
struct PtrToArg<m_type> { \
- _FORCE_INLINE_ static m_type convert(const void *p_ptr) { \
+ _FORCE_INLINE_ static const m_type &convert(const void *p_ptr) { \
return *reinterpret_cast<const m_type *>(p_ptr); \
} \
typedef m_type EncodeT; \
@@ -95,7 +95,7 @@ struct PtrToArg {};
}; \
template <> \
struct PtrToArg<const m_type &> { \
- _FORCE_INLINE_ static m_type convert(const void *p_ptr) { \
+ _FORCE_INLINE_ static const m_type &convert(const void *p_ptr) { \
return *reinterpret_cast<const m_type *>(p_ptr); \
} \
typedef m_type EncodeT; \
diff --git a/core/variant/typed_array.h b/core/variant/typed_array.h
index 98afc7e717..037c5c7c2e 100644
--- a/core/variant/typed_array.h
+++ b/core/variant/typed_array.h
@@ -145,8 +145,7 @@ struct PtrToArg<TypedArray<T>> {
template <class T>
struct PtrToArg<const TypedArray<T> &> {
typedef Array EncodeT;
- _FORCE_INLINE_ static TypedArray<T>
- convert(const void *p_ptr) {
+ _FORCE_INLINE_ static TypedArray<T> convert(const void *p_ptr) {
return TypedArray<T>(*reinterpret_cast<const Array *>(p_ptr));
}
};
@@ -230,4 +229,7 @@ MAKE_TYPED_ARRAY_INFO(Vector<Vector3>, Variant::PACKED_VECTOR3_ARRAY)
MAKE_TYPED_ARRAY_INFO(Vector<Color>, Variant::PACKED_COLOR_ARRAY)
MAKE_TYPED_ARRAY_INFO(IPAddress, Variant::STRING)
+#undef MAKE_TYPED_ARRAY
+#undef MAKE_TYPED_ARRAY_INFO
+
#endif // TYPED_ARRAY_H
diff --git a/core/variant/variant.cpp b/core/variant/variant.cpp
index 10a267e5a9..09fb34e7c1 100644
--- a/core/variant/variant.cpp
+++ b/core/variant/variant.cpp
@@ -1754,11 +1754,10 @@ String Variant::stringify(int recursion_count) const {
case COLOR:
return operator Color();
case DICTIONARY: {
+ ERR_FAIL_COND_V_MSG(recursion_count > MAX_RECURSION, "{ ... }", "Maximum dictionary recursion reached!");
+ recursion_count++;
+
const Dictionary &d = *reinterpret_cast<const Dictionary *>(_data._mem);
- if (recursion_count > MAX_RECURSION) {
- ERR_PRINT("Maximum dictionary recursion reached!");
- return "{ ... }";
- }
// Add leading and trailing space to Dictionary printing. This distinguishes it
// from array printing on fonts that have similar-looking {} and [] characters.
@@ -1768,7 +1767,6 @@ String Variant::stringify(int recursion_count) const {
Vector<_VariantStrPair> pairs;
- recursion_count++;
for (List<Variant>::Element *E = keys.front(); E; E = E->next()) {
_VariantStrPair sp;
sp.key = stringify_variant_clean(E->get(), recursion_count);
@@ -1787,6 +1785,7 @@ String Variant::stringify(int recursion_count) const {
return str;
}
+ // Packed arrays cannot contain recursive structures, the recursion_count increment is not needed.
case PACKED_VECTOR2_ARRAY: {
return stringify_vector(operator Vector<Vector2>(), recursion_count);
}
@@ -1815,13 +1814,10 @@ String Variant::stringify(int recursion_count) const {
return stringify_vector(operator Vector<double>(), recursion_count);
}
case ARRAY: {
- Array arr = operator Array();
- if (recursion_count > MAX_RECURSION) {
- ERR_PRINT("Maximum array recursion reached!");
- return "[...]";
- }
+ ERR_FAIL_COND_V_MSG(recursion_count > MAX_RECURSION, "[...]", "Maximum array recursion reached!");
+ recursion_count++;
- return stringify_vector(arr, recursion_count);
+ return stringify_vector(operator Array(), recursion_count);
}
case OBJECT: {
if (_get_obj().obj) {
@@ -2121,7 +2117,7 @@ Variant::operator ::RID() const {
} else if (type == OBJECT && _get_obj().obj) {
#ifdef DEBUG_ENABLED
if (EngineDebugger::is_active()) {
- ERR_FAIL_COND_V_MSG(ObjectDB::get_instance(_get_obj().id) == nullptr, ::RID(), "Invalid pointer (object was freed).");
+ ERR_FAIL_NULL_V_MSG(ObjectDB::get_instance(_get_obj().id), ::RID(), "Invalid pointer (object was freed).");
}
#endif
Callable::CallError ce;
@@ -3239,8 +3235,11 @@ uint32_t Variant::recursive_hash(int recursion_count) const {
return 0;
}
+#define hash_compare_scalar_base(p_lhs, p_rhs, semantic_comparison) \
+ (((p_lhs) == (p_rhs)) || (semantic_comparison && Math::is_nan(p_lhs) && Math::is_nan(p_rhs)))
+
#define hash_compare_scalar(p_lhs, p_rhs) \
- (((p_lhs) == (p_rhs)) || (Math::is_nan(p_lhs) && Math::is_nan(p_rhs)))
+ (hash_compare_scalar_base(p_lhs, p_rhs, true))
#define hash_compare_vector2(p_lhs, p_rhs) \
(hash_compare_scalar((p_lhs).x, (p_rhs).x) && \
@@ -3286,7 +3285,7 @@ uint32_t Variant::recursive_hash(int recursion_count) const {
\
return true
-bool Variant::hash_compare(const Variant &p_variant, int recursion_count) const {
+bool Variant::hash_compare(const Variant &p_variant, int recursion_count, bool semantic_comparison) const {
if (type != p_variant.type) {
return false;
}
@@ -3297,7 +3296,7 @@ bool Variant::hash_compare(const Variant &p_variant, int recursion_count) const
} break;
case FLOAT: {
- return hash_compare_scalar(_data._float, p_variant._data._float);
+ return hash_compare_scalar_base(_data._float, p_variant._data._float, semantic_comparison);
} break;
case STRING: {
diff --git a/core/variant/variant.h b/core/variant/variant.h
index 04c2fe2012..21342ae6ef 100644
--- a/core/variant/variant.h
+++ b/core/variant/variant.h
@@ -751,7 +751,8 @@ public:
uint32_t hash() const;
uint32_t recursive_hash(int recursion_count) const;
- bool hash_compare(const Variant &p_variant, int recursion_count = 0) const;
+ // By default, performs a semantic comparison. Otherwise, numeric/binary comparison (if appropriate).
+ bool hash_compare(const Variant &p_variant, int recursion_count = 0, bool semantic_comparison = true) const;
bool identity_compare(const Variant &p_variant) const;
bool booleanize() const;
String stringify(int recursion_count = 0) const;
@@ -833,6 +834,20 @@ String vformat(const String &p_text, const VarArgs... p_args) {
}
template <typename... VarArgs>
+Variant Callable::call(VarArgs... p_args) const {
+ Variant args[sizeof...(p_args) + 1] = { p_args..., 0 }; // +1 makes sure zero sized arrays are also supported.
+ const Variant *argptrs[sizeof...(p_args) + 1];
+ for (uint32_t i = 0; i < sizeof...(p_args); i++) {
+ argptrs[i] = &args[i];
+ }
+
+ Variant ret;
+ CallError ce;
+ callp(sizeof...(p_args) == 0 ? nullptr : (const Variant **)argptrs, sizeof...(p_args), ret, ce);
+ return ret;
+}
+
+template <typename... VarArgs>
Callable Callable::bind(VarArgs... p_args) {
Variant args[sizeof...(p_args) + 1] = { p_args..., Variant() }; // +1 makes sure zero sized arrays are also supported.
const Variant *argptrs[sizeof...(p_args) + 1];
diff --git a/core/variant/variant_call.cpp b/core/variant/variant_call.cpp
index dad9183216..f041d2c95e 100644
--- a/core/variant/variant_call.cpp
+++ b/core/variant/variant_call.cpp
@@ -561,8 +561,8 @@ static _FORCE_INLINE_ void vc_ptrcall(void (*method)(T *, P...), void *p_base, c
} \
static void ptrcall(void *p_base, const void **p_args, void *r_ret, int p_argcount) { \
LocalVector<Variant> vars; \
- vars.resize(p_argcount); \
LocalVector<const Variant *> vars_ptrs; \
+ vars.resize(p_argcount); \
vars_ptrs.resize(p_argcount); \
for (int i = 0; i < p_argcount; i++) { \
vars[i] = PtrToArg<Variant>::convert(p_args[i]); \
@@ -571,7 +571,7 @@ static _FORCE_INLINE_ void vc_ptrcall(void (*method)(T *, P...), void *p_base, c
Variant base = PtrToArg<m_class>::convert(p_base); \
Variant ret; \
Callable::CallError ce; \
- m_method_ptr(&base, (const Variant **)&vars_ptrs[0], p_argcount, ret, ce); \
+ m_method_ptr(&base, vars_ptrs.ptr(), p_argcount, ret, ce); \
if (m_has_return) { \
m_return_type r = ret; \
PtrToArg<m_return_type>::encode(ret, r_ret); \
@@ -617,8 +617,8 @@ static _FORCE_INLINE_ void vc_ptrcall(void (*method)(T *, P...), void *p_base, c
} \
static void ptrcall(void *p_base, const void **p_args, void *r_ret, int p_argcount) { \
LocalVector<Variant> vars; \
- vars.resize(p_argcount); \
LocalVector<const Variant *> vars_ptrs; \
+ vars.resize(p_argcount); \
vars_ptrs.resize(p_argcount); \
for (int i = 0; i < p_argcount; i++) { \
vars[i] = PtrToArg<Variant>::convert(p_args[i]); \
@@ -627,7 +627,7 @@ static _FORCE_INLINE_ void vc_ptrcall(void (*method)(T *, P...), void *p_base, c
Variant base = PtrToArg<m_class>::convert(p_base); \
Variant ret; \
Callable::CallError ce; \
- m_method_ptr(&base, (const Variant **)&vars_ptrs[0], p_argcount, ret, ce); \
+ m_method_ptr(&base, vars_ptrs.ptr(), p_argcount, ret, ce); \
} \
static int get_argument_count() { \
return 1; \
@@ -1037,9 +1037,7 @@ struct _VariantCall {
static void func_Callable_rpc_id(Variant *v, const Variant **p_args, int p_argcount, Variant &r_ret, Callable::CallError &r_error) {
if (p_argcount == 0) {
r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;
- r_error.argument = 0;
r_error.expected = 1;
-
} else if (p_args[0]->get_type() != Variant::INT) {
r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
r_error.argument = 0;
@@ -1132,11 +1130,7 @@ static void register_builtin_method(const Vector<String> &p_argnames, const Vect
imi.call = T::call;
imi.validated_call = T::validated_call;
- if (T::is_vararg()) {
- imi.ptrcall = nullptr;
- } else {
- imi.ptrcall = T::ptrcall;
- }
+ imi.ptrcall = T::ptrcall;
imi.default_arguments = p_def_args;
imi.argument_names = p_argnames;
@@ -1263,28 +1257,28 @@ bool Variant::has_builtin_method(Variant::Type p_type, const StringName &p_metho
Variant::ValidatedBuiltInMethod Variant::get_validated_builtin_method(Variant::Type p_type, const StringName &p_method) {
ERR_FAIL_INDEX_V(p_type, Variant::VARIANT_MAX, nullptr);
const VariantBuiltInMethodInfo *method = builtin_method_info[p_type].lookup_ptr(p_method);
- ERR_FAIL_COND_V(!method, nullptr);
+ ERR_FAIL_NULL_V(method, nullptr);
return method->validated_call;
}
Variant::PTRBuiltInMethod Variant::get_ptr_builtin_method(Variant::Type p_type, const StringName &p_method) {
ERR_FAIL_INDEX_V(p_type, Variant::VARIANT_MAX, nullptr);
const VariantBuiltInMethodInfo *method = builtin_method_info[p_type].lookup_ptr(p_method);
- ERR_FAIL_COND_V(!method, nullptr);
+ ERR_FAIL_NULL_V(method, nullptr);
return method->ptrcall;
}
int Variant::get_builtin_method_argument_count(Variant::Type p_type, const StringName &p_method) {
ERR_FAIL_INDEX_V(p_type, Variant::VARIANT_MAX, 0);
const VariantBuiltInMethodInfo *method = builtin_method_info[p_type].lookup_ptr(p_method);
- ERR_FAIL_COND_V(!method, 0);
+ ERR_FAIL_NULL_V(method, 0);
return method->argument_count;
}
Variant::Type Variant::get_builtin_method_argument_type(Variant::Type p_type, const StringName &p_method, int p_argument) {
ERR_FAIL_INDEX_V(p_type, Variant::VARIANT_MAX, Variant::NIL);
const VariantBuiltInMethodInfo *method = builtin_method_info[p_type].lookup_ptr(p_method);
- ERR_FAIL_COND_V(!method, Variant::NIL);
+ ERR_FAIL_NULL_V(method, Variant::NIL);
ERR_FAIL_INDEX_V(p_argument, method->argument_count, Variant::NIL);
return method->get_argument_type(p_argument);
}
@@ -1292,7 +1286,7 @@ Variant::Type Variant::get_builtin_method_argument_type(Variant::Type p_type, co
String Variant::get_builtin_method_argument_name(Variant::Type p_type, const StringName &p_method, int p_argument) {
ERR_FAIL_INDEX_V(p_type, Variant::VARIANT_MAX, String());
const VariantBuiltInMethodInfo *method = builtin_method_info[p_type].lookup_ptr(p_method);
- ERR_FAIL_COND_V(!method, String());
+ ERR_FAIL_NULL_V(method, String());
#ifdef DEBUG_METHODS_ENABLED
ERR_FAIL_INDEX_V(p_argument, method->argument_count, String());
return method->argument_names[p_argument];
@@ -1304,14 +1298,14 @@ String Variant::get_builtin_method_argument_name(Variant::Type p_type, const Str
Vector<Variant> Variant::get_builtin_method_default_arguments(Variant::Type p_type, const StringName &p_method) {
ERR_FAIL_INDEX_V(p_type, Variant::VARIANT_MAX, Vector<Variant>());
const VariantBuiltInMethodInfo *method = builtin_method_info[p_type].lookup_ptr(p_method);
- ERR_FAIL_COND_V(!method, Vector<Variant>());
+ ERR_FAIL_NULL_V(method, Vector<Variant>());
return method->default_arguments;
}
bool Variant::has_builtin_method_return_value(Variant::Type p_type, const StringName &p_method) {
ERR_FAIL_INDEX_V(p_type, Variant::VARIANT_MAX, false);
const VariantBuiltInMethodInfo *method = builtin_method_info[p_type].lookup_ptr(p_method);
- ERR_FAIL_COND_V(!method, false);
+ ERR_FAIL_NULL_V(method, false);
return method->has_return_type;
}
@@ -1330,35 +1324,35 @@ int Variant::get_builtin_method_count(Variant::Type p_type) {
Variant::Type Variant::get_builtin_method_return_type(Variant::Type p_type, const StringName &p_method) {
ERR_FAIL_INDEX_V(p_type, Variant::VARIANT_MAX, Variant::NIL);
const VariantBuiltInMethodInfo *method = builtin_method_info[p_type].lookup_ptr(p_method);
- ERR_FAIL_COND_V(!method, Variant::NIL);
+ ERR_FAIL_NULL_V(method, Variant::NIL);
return method->return_type;
}
bool Variant::is_builtin_method_const(Variant::Type p_type, const StringName &p_method) {
ERR_FAIL_INDEX_V(p_type, Variant::VARIANT_MAX, false);
const VariantBuiltInMethodInfo *method = builtin_method_info[p_type].lookup_ptr(p_method);
- ERR_FAIL_COND_V(!method, false);
+ ERR_FAIL_NULL_V(method, false);
return method->is_const;
}
bool Variant::is_builtin_method_static(Variant::Type p_type, const StringName &p_method) {
ERR_FAIL_INDEX_V(p_type, Variant::VARIANT_MAX, false);
const VariantBuiltInMethodInfo *method = builtin_method_info[p_type].lookup_ptr(p_method);
- ERR_FAIL_COND_V(!method, false);
+ ERR_FAIL_NULL_V(method, false);
return method->is_static;
}
bool Variant::is_builtin_method_vararg(Variant::Type p_type, const StringName &p_method) {
ERR_FAIL_INDEX_V(p_type, Variant::VARIANT_MAX, false);
const VariantBuiltInMethodInfo *method = builtin_method_info[p_type].lookup_ptr(p_method);
- ERR_FAIL_COND_V(!method, false);
+ ERR_FAIL_NULL_V(method, false);
return method->is_vararg;
}
uint32_t Variant::get_builtin_method_hash(Variant::Type p_type, const StringName &p_method) {
ERR_FAIL_INDEX_V(p_type, Variant::VARIANT_MAX, 0);
const VariantBuiltInMethodInfo *method = builtin_method_info[p_type].lookup_ptr(p_method);
- ERR_FAIL_COND_V(!method, 0);
+ ERR_FAIL_NULL_V(method, 0);
uint32_t hash = hash_murmur3_one_32(method->is_const);
hash = hash_murmur3_one_32(method->is_static, hash);
hash = hash_murmur3_one_32(method->is_vararg, hash);
@@ -1659,6 +1653,7 @@ static void _register_variant_builtin_methods() {
bind_string_methodv(replace, static_cast<String (String::*)(const String &, const String &) const>(&String::replace), sarray("what", "forwhat"), varray());
bind_string_method(replacen, sarray("what", "forwhat"), varray());
bind_string_method(repeat, sarray("count"), varray());
+ bind_string_method(reverse, sarray(), varray());
bind_string_method(insert, sarray("position", "what"), varray());
bind_string_method(erase, sarray("position", "chars"), varray(1));
bind_string_method(capitalize, sarray(), varray());
@@ -2079,6 +2074,7 @@ static void _register_variant_builtin_methods() {
bind_method(Transform2D, basis_xform, sarray("v"), varray());
bind_method(Transform2D, basis_xform_inv, sarray("v"), varray());
bind_method(Transform2D, interpolate_with, sarray("xform", "weight"), varray());
+ bind_method(Transform2D, is_conformal, sarray(), varray());
bind_method(Transform2D, is_equal_approx, sarray("xform"), varray());
bind_method(Transform2D, is_finite, sarray(), varray());
// Do not bind functions like set_rotation, set_scale, set_skew, etc because this type is immutable and can't be modified.
@@ -2098,6 +2094,7 @@ static void _register_variant_builtin_methods() {
bind_method(Basis, tdoty, sarray("with"), varray());
bind_method(Basis, tdotz, sarray("with"), varray());
bind_method(Basis, slerp, sarray("to", "weight"), varray());
+ bind_method(Basis, is_conformal, sarray(), varray());
bind_method(Basis, is_equal_approx, sarray("b"), varray());
bind_method(Basis, is_finite, sarray(), varray());
bind_method(Basis, get_rotation_quaternion, sarray(), varray());
@@ -2572,9 +2569,13 @@ static void _register_variant_builtin_methods() {
_VariantCall::add_variant_constant(Variant::VECTOR4I, "ZERO", Vector4i(0, 0, 0, 0));
_VariantCall::add_variant_constant(Variant::VECTOR4I, "ONE", Vector4i(1, 1, 1, 1));
+ _VariantCall::add_variant_constant(Variant::VECTOR4I, "MIN", Vector4i(INT32_MIN, INT32_MIN, INT32_MIN, INT32_MIN));
+ _VariantCall::add_variant_constant(Variant::VECTOR4I, "MAX", Vector4i(INT32_MAX, INT32_MAX, INT32_MAX, INT32_MAX));
_VariantCall::add_variant_constant(Variant::VECTOR3I, "ZERO", Vector3i(0, 0, 0));
_VariantCall::add_variant_constant(Variant::VECTOR3I, "ONE", Vector3i(1, 1, 1));
+ _VariantCall::add_variant_constant(Variant::VECTOR3I, "MIN", Vector3i(INT32_MIN, INT32_MIN, INT32_MIN));
+ _VariantCall::add_variant_constant(Variant::VECTOR3I, "MAX", Vector3i(INT32_MAX, INT32_MAX, INT32_MAX));
_VariantCall::add_variant_constant(Variant::VECTOR3I, "LEFT", Vector3i(-1, 0, 0));
_VariantCall::add_variant_constant(Variant::VECTOR3I, "RIGHT", Vector3i(1, 0, 0));
_VariantCall::add_variant_constant(Variant::VECTOR3I, "UP", Vector3i(0, 1, 0));
@@ -2604,6 +2605,8 @@ static void _register_variant_builtin_methods() {
_VariantCall::add_variant_constant(Variant::VECTOR2I, "ZERO", Vector2i(0, 0));
_VariantCall::add_variant_constant(Variant::VECTOR2I, "ONE", Vector2i(1, 1));
+ _VariantCall::add_variant_constant(Variant::VECTOR2I, "MIN", Vector2i(INT32_MIN, INT32_MIN));
+ _VariantCall::add_variant_constant(Variant::VECTOR2I, "MAX", Vector2i(INT32_MAX, INT32_MAX));
_VariantCall::add_variant_constant(Variant::VECTOR2I, "LEFT", Vector2i(-1, 0));
_VariantCall::add_variant_constant(Variant::VECTOR2I, "RIGHT", Vector2i(1, 0));
_VariantCall::add_variant_constant(Variant::VECTOR2I, "UP", Vector2i(0, -1));
diff --git a/core/variant/variant_internal.h b/core/variant/variant_internal.h
index 782053b613..116210e8de 100644
--- a/core/variant/variant_internal.h
+++ b/core/variant/variant_internal.h
@@ -418,7 +418,7 @@ public:
case Variant::PACKED_COLOR_ARRAY:
return get_color_array(v);
case Variant::OBJECT:
- return v->_get_obj().obj;
+ return get_object(v);
case Variant::VARIANT_MAX:
ERR_FAIL_V(nullptr);
}
diff --git a/core/variant/variant_op.h b/core/variant/variant_op.h
index fc1f7828a2..9e6367ab6d 100644
--- a/core/variant/variant_op.h
+++ b/core/variant/variant_op.h
@@ -1493,7 +1493,7 @@ public:
}
static inline void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) {
Object *l = right->get_validated_object();
- ERR_FAIL_COND(l == nullptr);
+ ERR_FAIL_NULL(l);
const String &a = *VariantGetInternalPtr<String>::get_ptr(left);
bool valid;
@@ -1527,7 +1527,7 @@ public:
}
static inline void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) {
Object *l = right->get_validated_object();
- ERR_FAIL_COND(l == nullptr);
+ ERR_FAIL_NULL(l);
const StringName &a = *VariantGetInternalPtr<StringName>::get_ptr(left);
bool valid;
diff --git a/core/variant/variant_parser.cpp b/core/variant/variant_parser.cpp
index 3320750994..fea1622222 100644
--- a/core/variant/variant_parser.cpp
+++ b/core/variant/variant_parser.cpp
@@ -32,6 +32,7 @@
#include "core/input/input_event.h"
#include "core/io/resource_loader.h"
+#include "core/object/script_language.h"
#include "core/os/keyboard.h"
#include "core/string/string_buffer.h"
@@ -1708,7 +1709,7 @@ static String rtos_fix(double p_value) {
}
}
-Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_string_func, void *p_store_string_ud, EncodeResourceFunc p_encode_res_func, void *p_encode_res_ud, int recursion_count) {
+Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_string_func, void *p_store_string_ud, EncodeResourceFunc p_encode_res_func, void *p_encode_res_ud, int p_recursion_count) {
switch (p_variant.get_type()) {
case Variant::NIL: {
p_store_string_func(p_store_string_ud, "null");
@@ -1730,10 +1731,11 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str
} break;
case Variant::STRING: {
String str = p_variant;
-
str = "\"" + str.c_escape_multiline() + "\"";
p_store_string_func(p_store_string_ud, str);
} break;
+
+ // Math types.
case Variant::VECTOR2: {
Vector2 v = p_variant;
p_store_string_func(p_store_string_ud, "Vector2(" + rtos_fix(v.x) + ", " + rtos_fix(v.y) + ")");
@@ -1745,12 +1747,10 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str
case Variant::RECT2: {
Rect2 aabb = p_variant;
p_store_string_func(p_store_string_ud, "Rect2(" + rtos_fix(aabb.position.x) + ", " + rtos_fix(aabb.position.y) + ", " + rtos_fix(aabb.size.x) + ", " + rtos_fix(aabb.size.y) + ")");
-
} break;
case Variant::RECT2I: {
Rect2i aabb = p_variant;
p_store_string_func(p_store_string_ud, "Rect2i(" + itos(aabb.position.x) + ", " + itos(aabb.position.y) + ", " + itos(aabb.size.x) + ", " + itos(aabb.size.y) + ")");
-
} break;
case Variant::VECTOR3: {
Vector3 v = p_variant;
@@ -1771,17 +1771,14 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str
case Variant::PLANE: {
Plane p = p_variant;
p_store_string_func(p_store_string_ud, "Plane(" + rtos_fix(p.normal.x) + ", " + rtos_fix(p.normal.y) + ", " + rtos_fix(p.normal.z) + ", " + rtos_fix(p.d) + ")");
-
} break;
case Variant::AABB: {
AABB aabb = p_variant;
p_store_string_func(p_store_string_ud, "AABB(" + rtos_fix(aabb.position.x) + ", " + rtos_fix(aabb.position.y) + ", " + rtos_fix(aabb.position.z) + ", " + rtos_fix(aabb.size.x) + ", " + rtos_fix(aabb.size.y) + ", " + rtos_fix(aabb.size.z) + ")");
-
} break;
case Variant::QUATERNION: {
Quaternion quaternion = p_variant;
p_store_string_func(p_store_string_ud, "Quaternion(" + rtos_fix(quaternion.x) + ", " + rtos_fix(quaternion.y) + ", " + rtos_fix(quaternion.z) + ", " + rtos_fix(quaternion.w) + ")");
-
} break;
case Variant::TRANSFORM2D: {
String s = "Transform2D(";
@@ -1796,7 +1793,6 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str
}
p_store_string_func(p_store_string_ud, s + ")");
-
} break;
case Variant::BASIS: {
String s = "Basis(";
@@ -1811,7 +1807,6 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str
}
p_store_string_func(p_store_string_ud, s + ")");
-
} break;
case Variant::TRANSFORM3D: {
String s = "Transform3D(";
@@ -1845,30 +1840,23 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str
p_store_string_func(p_store_string_ud, s + ")");
} break;
- // misc types
+ // Misc types.
case Variant::COLOR: {
Color c = p_variant;
p_store_string_func(p_store_string_ud, "Color(" + rtos_fix(c.r) + ", " + rtos_fix(c.g) + ", " + rtos_fix(c.b) + ", " + rtos_fix(c.a) + ")");
-
} break;
case Variant::STRING_NAME: {
String str = p_variant;
-
str = "&\"" + str.c_escape() + "\"";
p_store_string_func(p_store_string_ud, str);
-
} break;
case Variant::NODE_PATH: {
String str = p_variant;
-
str = "NodePath(\"" + str.c_escape() + "\")";
p_store_string_func(p_store_string_ud, str);
-
} break;
-
case Variant::RID: {
RID rid = p_variant;
-
if (rid == RID()) {
p_store_string_func(p_store_string_ud, "RID()");
} else {
@@ -1885,6 +1873,13 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str
} break;
case Variant::OBJECT: {
+ if (unlikely(p_recursion_count > MAX_RECURSION)) {
+ ERR_PRINT("Max recursion reached");
+ p_store_string_func(p_store_string_ud, "null");
+ return OK;
+ }
+ p_recursion_count++;
+
Object *obj = p_variant.get_validated_object();
if (!obj) {
@@ -1934,21 +1929,20 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str
}
p_store_string_func(p_store_string_ud, "\"" + E.name + "\":");
- write(obj->get(E.name), p_store_string_func, p_store_string_ud, p_encode_res_func, p_encode_res_ud);
+ write(obj->get(E.name), p_store_string_func, p_store_string_ud, p_encode_res_func, p_encode_res_ud, p_recursion_count);
}
}
p_store_string_func(p_store_string_ud, ")\n");
-
} break;
case Variant::DICTIONARY: {
Dictionary dict = p_variant;
- if (recursion_count > MAX_RECURSION) {
+ if (unlikely(p_recursion_count > MAX_RECURSION)) {
ERR_PRINT("Max recursion reached");
p_store_string_func(p_store_string_ud, "{}");
} else {
- recursion_count++;
+ p_recursion_count++;
List<Variant> keys;
dict.get_key_list(&keys);
@@ -1961,9 +1955,9 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str
p_store_string_func(p_store_string_ud, "{\n");
for (List<Variant>::Element *E = keys.front(); E; E = E->next()) {
- write(E->get(), p_store_string_func, p_store_string_ud, p_encode_res_func, p_encode_res_ud, recursion_count);
+ write(E->get(), p_store_string_func, p_store_string_ud, p_encode_res_func, p_encode_res_ud, p_recursion_count);
p_store_string_func(p_store_string_ud, ": ");
- write(dict[E->get()], p_store_string_func, p_store_string_ud, p_encode_res_func, p_encode_res_ud, recursion_count);
+ write(dict[E->get()], p_store_string_func, p_store_string_ud, p_encode_res_func, p_encode_res_ud, p_recursion_count);
if (E->next()) {
p_store_string_func(p_store_string_ud, ",\n");
} else {
@@ -1973,7 +1967,6 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str
p_store_string_func(p_store_string_ud, "}");
}
-
} break;
case Variant::ARRAY: {
@@ -2009,11 +2002,11 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str
p_store_string_func(p_store_string_ud, "](");
}
- if (recursion_count > MAX_RECURSION) {
+ if (unlikely(p_recursion_count > MAX_RECURSION)) {
ERR_PRINT("Max recursion reached");
p_store_string_func(p_store_string_ud, "[]");
} else {
- recursion_count++;
+ p_recursion_count++;
p_store_string_func(p_store_string_ud, "[");
int len = array.size();
@@ -2021,7 +2014,7 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str
if (i > 0) {
p_store_string_func(p_store_string_ud, ", ");
}
- write(array[i], p_store_string_func, p_store_string_ud, p_encode_res_func, p_encode_res_ud, recursion_count);
+ write(array[i], p_store_string_func, p_store_string_ud, p_encode_res_func, p_encode_res_ud, p_recursion_count);
}
p_store_string_func(p_store_string_ud, "]");
@@ -2030,7 +2023,6 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str
if (array.get_typed_builtin() != Variant::NIL) {
p_store_string_func(p_store_string_ud, ")");
}
-
} break;
case Variant::PACKED_BYTE_ARRAY: {
@@ -2048,7 +2040,6 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str
}
p_store_string_func(p_store_string_ud, ")");
-
} break;
case Variant::PACKED_INT32_ARRAY: {
p_store_string_func(p_store_string_ud, "PackedInt32Array(");
@@ -2065,7 +2056,6 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str
}
p_store_string_func(p_store_string_ud, ")");
-
} break;
case Variant::PACKED_INT64_ARRAY: {
p_store_string_func(p_store_string_ud, "PackedInt64Array(");
@@ -2082,7 +2072,6 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str
}
p_store_string_func(p_store_string_ud, ")");
-
} break;
case Variant::PACKED_FLOAT32_ARRAY: {
p_store_string_func(p_store_string_ud, "PackedFloat32Array(");
@@ -2098,7 +2087,6 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str
}
p_store_string_func(p_store_string_ud, ")");
-
} break;
case Variant::PACKED_FLOAT64_ARRAY: {
p_store_string_func(p_store_string_ud, "PackedFloat64Array(");
@@ -2114,7 +2102,6 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str
}
p_store_string_func(p_store_string_ud, ")");
-
} break;
case Variant::PACKED_STRING_ARRAY: {
p_store_string_func(p_store_string_ud, "PackedStringArray(");
@@ -2130,7 +2117,6 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str
}
p_store_string_func(p_store_string_ud, ")");
-
} break;
case Variant::PACKED_VECTOR2_ARRAY: {
p_store_string_func(p_store_string_ud, "PackedVector2Array(");
@@ -2146,7 +2132,6 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str
}
p_store_string_func(p_store_string_ud, ")");
-
} break;
case Variant::PACKED_VECTOR3_ARRAY: {
p_store_string_func(p_store_string_ud, "PackedVector3Array(");
@@ -2162,7 +2147,6 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str
}
p_store_string_func(p_store_string_ud, ")");
-
} break;
case Variant::PACKED_COLOR_ARRAY: {
p_store_string_func(p_store_string_ud, "PackedColorArray(");
@@ -2178,8 +2162,8 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str
}
p_store_string_func(p_store_string_ud, ")");
-
} break;
+
default: {
ERR_PRINT("Unknown variant type");
return ERR_BUG;
diff --git a/core/variant/variant_parser.h b/core/variant/variant_parser.h
index 88be99e551..8505fff739 100644
--- a/core/variant/variant_parser.h
+++ b/core/variant/variant_parser.h
@@ -160,7 +160,7 @@ public:
typedef Error (*StoreStringFunc)(void *ud, const String &p_string);
typedef String (*EncodeResourceFunc)(void *ud, const Ref<Resource> &p_resource);
- static Error write(const Variant &p_variant, StoreStringFunc p_store_string_func, void *p_store_string_ud, EncodeResourceFunc p_encode_res_func, void *p_encode_res_ud, int recursion_count = 0);
+ static Error write(const Variant &p_variant, StoreStringFunc p_store_string_func, void *p_store_string_ud, EncodeResourceFunc p_encode_res_func, void *p_encode_res_ud, int p_recursion_count = 0);
static Error write_to_string(const Variant &p_variant, String &r_string, EncodeResourceFunc p_encode_res_func = nullptr, void *p_encode_res_ud = nullptr);
};
diff --git a/core/variant/variant_setget.cpp b/core/variant/variant_setget.cpp
index 30fb5d0e9f..05f7abf32c 100644
--- a/core/variant/variant_setget.cpp
+++ b/core/variant/variant_setget.cpp
@@ -318,7 +318,7 @@ Variant Variant::get_named(const StringName &p_member, bool &r_valid) const {
#ifdef DEBUG_ENABLED
#define NULL_TEST(m_key) \
- ERR_FAIL_COND(!m_key)
+ ERR_FAIL_NULL(m_key)
#else
@@ -1068,7 +1068,7 @@ struct VariantKeyedSetGetObject {
}
static uint32_t ptr_has(const void *base, const void *key) {
const Object *obj = PtrToArg<Object *>::convert(base);
- ERR_FAIL_COND_V(!obj, false);
+ ERR_FAIL_NULL_V(obj, false);
bool valid;
obj->getvar(PtrToArg<Variant>::convert(key), &valid);
return valid;
@@ -1245,7 +1245,7 @@ void Variant::get_property_list(List<PropertyInfo> *p_list) const {
}
} else if (type == OBJECT) {
Object *obj = get_validated_object();
- ERR_FAIL_COND(!obj);
+ ERR_FAIL_NULL(obj);
obj->get_property_list(p_list);
} else {
diff --git a/core/variant/variant_utility.cpp b/core/variant/variant_utility.cpp
index 4f6bcb58b3..cc48394b64 100644
--- a/core/variant/variant_utility.cpp
+++ b/core/variant/variant_utility.cpp
@@ -81,6 +81,18 @@ double VariantUtilityFunctions::atan2(double y, double x) {
return Math::atan2(y, x);
}
+double VariantUtilityFunctions::asinh(double arg) {
+ return Math::asinh(arg);
+}
+
+double VariantUtilityFunctions::acosh(double arg) {
+ return Math::acosh(arg);
+}
+
+double VariantUtilityFunctions::atanh(double arg) {
+ return Math::atanh(arg);
+}
+
double VariantUtilityFunctions::sqrt(double x) {
return Math::sqrt(x);
}
@@ -109,16 +121,27 @@ Variant VariantUtilityFunctions::floor(Variant x, Callable::CallError &r_error)
case Variant::VECTOR2: {
return VariantInternalAccessor<Vector2>::get(&x).floor();
} break;
+ case Variant::VECTOR2I: {
+ return VariantInternalAccessor<Vector2i>::get(&x);
+ } break;
case Variant::VECTOR3: {
return VariantInternalAccessor<Vector3>::get(&x).floor();
} break;
+ case Variant::VECTOR3I: {
+ return VariantInternalAccessor<Vector3i>::get(&x);
+ } break;
case Variant::VECTOR4: {
return VariantInternalAccessor<Vector4>::get(&x).floor();
} break;
+ case Variant::VECTOR4I: {
+ return VariantInternalAccessor<Vector4i>::get(&x);
+ } break;
default: {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
- return Variant();
- }
+ r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
+ r_error.argument = 0;
+ r_error.expected = Variant::NIL;
+ return R"(Argument "x" must be "int", "float", "Vector2", "Vector2i", "Vector3", "Vector3i", "Vector4", or "Vector4i".)";
+ } break;
}
}
@@ -142,16 +165,27 @@ Variant VariantUtilityFunctions::ceil(Variant x, Callable::CallError &r_error) {
case Variant::VECTOR2: {
return VariantInternalAccessor<Vector2>::get(&x).ceil();
} break;
+ case Variant::VECTOR2I: {
+ return VariantInternalAccessor<Vector2i>::get(&x);
+ } break;
case Variant::VECTOR3: {
return VariantInternalAccessor<Vector3>::get(&x).ceil();
} break;
+ case Variant::VECTOR3I: {
+ return VariantInternalAccessor<Vector3i>::get(&x);
+ } break;
case Variant::VECTOR4: {
return VariantInternalAccessor<Vector4>::get(&x).ceil();
} break;
+ case Variant::VECTOR4I: {
+ return VariantInternalAccessor<Vector4i>::get(&x);
+ } break;
default: {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
- return Variant();
- }
+ r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
+ r_error.argument = 0;
+ r_error.expected = Variant::NIL;
+ return R"(Argument "x" must be "int", "float", "Vector2", "Vector2i", "Vector3", "Vector3i", "Vector4", or "Vector4i".)";
+ } break;
}
}
@@ -175,16 +209,27 @@ Variant VariantUtilityFunctions::round(Variant x, Callable::CallError &r_error)
case Variant::VECTOR2: {
return VariantInternalAccessor<Vector2>::get(&x).round();
} break;
+ case Variant::VECTOR2I: {
+ return VariantInternalAccessor<Vector2i>::get(&x);
+ } break;
case Variant::VECTOR3: {
return VariantInternalAccessor<Vector3>::get(&x).round();
} break;
+ case Variant::VECTOR3I: {
+ return VariantInternalAccessor<Vector3i>::get(&x);
+ } break;
case Variant::VECTOR4: {
return VariantInternalAccessor<Vector4>::get(&x).round();
} break;
+ case Variant::VECTOR4I: {
+ return VariantInternalAccessor<Vector4i>::get(&x);
+ } break;
default: {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
- return Variant();
- }
+ r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
+ r_error.argument = 0;
+ r_error.expected = Variant::NIL;
+ return R"(Argument "x" must be "int", "float", "Vector2", "Vector2i", "Vector3", "Vector3i", "Vector4", or "Vector4i".)";
+ } break;
}
}
@@ -224,9 +269,11 @@ Variant VariantUtilityFunctions::abs(const Variant &x, Callable::CallError &r_er
return VariantInternalAccessor<Vector4i>::get(&x).abs();
} break;
default: {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
- return Variant();
- }
+ r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
+ r_error.argument = 0;
+ r_error.expected = Variant::NIL;
+ return R"(Argument "x" must be "int", "float", "Vector2", "Vector2i", "Vector3", "Vector3i", "Vector4", or "Vector4i".)";
+ } break;
}
}
@@ -266,9 +313,11 @@ Variant VariantUtilityFunctions::sign(const Variant &x, Callable::CallError &r_e
return VariantInternalAccessor<Vector4i>::get(&x).sign();
} break;
default: {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
- return Variant();
- }
+ r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
+ r_error.argument = 0;
+ r_error.expected = Variant::NIL;
+ return R"(Argument "x" must be "int", "float", "Vector2", "Vector2i", "Vector3", "Vector3i", "Vector4", or "Vector4i".)";
+ } break;
}
}
@@ -321,13 +370,40 @@ int VariantUtilityFunctions::step_decimals(float step) {
}
Variant VariantUtilityFunctions::snapped(const Variant &x, const Variant &step, Callable::CallError &r_error) {
- r_error.error = Callable::CallError::CALL_OK;
- if (x.get_type() != step.get_type() && !((x.get_type() == Variant::INT && step.get_type() == Variant::FLOAT) || (x.get_type() == Variant::FLOAT && step.get_type() == Variant::INT))) {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
- r_error.argument = 1;
- return Variant();
+ switch (x.get_type()) {
+ case Variant::INT:
+ case Variant::FLOAT:
+ case Variant::VECTOR2:
+ case Variant::VECTOR2I:
+ case Variant::VECTOR3:
+ case Variant::VECTOR3I:
+ case Variant::VECTOR4:
+ case Variant::VECTOR4I:
+ break;
+ default:
+ r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
+ r_error.argument = 0;
+ r_error.expected = Variant::NIL;
+ return R"(Argument "x" must be "int", "float", "Vector2", "Vector2i", "Vector3", "Vector3i", "Vector4", or "Vector4i".)";
}
+ if (x.get_type() != step.get_type()) {
+ if (x.get_type() == Variant::INT || x.get_type() == Variant::FLOAT) {
+ if (step.get_type() != Variant::INT && step.get_type() != Variant::FLOAT) {
+ r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
+ r_error.argument = 1;
+ r_error.expected = Variant::NIL;
+ return R"(Argument "step" must be "int" or "float".)";
+ }
+ } else {
+ r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
+ r_error.argument = 1;
+ r_error.expected = x.get_type();
+ return Variant();
+ }
+ }
+
+ r_error.error = Callable::CallError::CALL_OK;
switch (step.get_type()) {
case Variant::INT: {
return snappedi(x, VariantInternalAccessor<int64_t>::get(&step));
@@ -354,9 +430,8 @@ Variant VariantUtilityFunctions::snapped(const Variant &x, const Variant &step,
return VariantInternalAccessor<Vector4i>::get(&x).snapped(VariantInternalAccessor<Vector4i>::get(&step));
} break;
default: {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
- return Variant();
- }
+ return Variant(); // Already handled.
+ } break;
}
}
@@ -369,14 +444,31 @@ int64_t VariantUtilityFunctions::snappedi(double x, int64_t step) {
}
Variant VariantUtilityFunctions::lerp(const Variant &from, const Variant &to, double weight, Callable::CallError &r_error) {
- r_error.error = Callable::CallError::CALL_OK;
+ switch (from.get_type()) {
+ case Variant::INT:
+ case Variant::FLOAT:
+ case Variant::VECTOR2:
+ case Variant::VECTOR3:
+ case Variant::VECTOR4:
+ case Variant::QUATERNION:
+ case Variant::BASIS:
+ case Variant::COLOR:
+ break;
+ default:
+ r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
+ r_error.argument = 0;
+ r_error.expected = Variant::NIL;
+ return R"(Argument "from" must be "int", "float", "Vector2", "Vector3", "Vector4", "Quaternion", "Basis, or "Color".)";
+ }
+
if (from.get_type() != to.get_type()) {
r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
- r_error.expected = from.get_type();
r_error.argument = 1;
+ r_error.expected = from.get_type();
return Variant();
}
+ r_error.error = Callable::CallError::CALL_OK;
switch (from.get_type()) {
case Variant::INT: {
return lerpf(VariantInternalAccessor<int64_t>::get(&from), to, weight);
@@ -403,9 +495,8 @@ Variant VariantUtilityFunctions::lerp(const Variant &from, const Variant &to, do
return VariantInternalAccessor<Color>::get(&from).lerp(VariantInternalAccessor<Color>::get(&to), weight);
} break;
default: {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
- return Variant();
- }
+ return Variant(); // Already handled.
+ } break;
}
}
@@ -439,6 +530,10 @@ double VariantUtilityFunctions::bezier_derivative(double p_start, double p_contr
return Math::bezier_derivative(p_start, p_control_1, p_control_2, p_end, p_t);
}
+double VariantUtilityFunctions::angle_difference(double from, double to) {
+ return Math::angle_difference(from, to);
+}
+
double VariantUtilityFunctions::lerp_angle(double from, double to, double weight) {
return Math::lerp_angle(from, to, weight);
}
@@ -459,6 +554,10 @@ double VariantUtilityFunctions::move_toward(double from, double to, double delta
return Math::move_toward(from, to, delta);
}
+double VariantUtilityFunctions::rotate_toward(double from, double to, double delta) {
+ return Math::rotate_toward(from, to, delta);
+}
+
double VariantUtilityFunctions::deg_to_rad(double angle_deg) {
return Math::deg_to_rad(angle_deg);
}
@@ -480,7 +579,7 @@ Variant VariantUtilityFunctions::wrap(const Variant &p_x, const Variant &p_min,
if (x_type != Variant::INT && x_type != Variant::FLOAT) {
r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
r_error.argument = 0;
- r_error.expected = x_type;
+ r_error.expected = Variant::FLOAT;
return Variant();
}
@@ -546,8 +645,8 @@ Variant VariantUtilityFunctions::max(const Variant **p_args, int p_argcount, Cal
Variant::Type arg_type = p_args[i]->get_type();
if (arg_type != Variant::INT && arg_type != Variant::FLOAT) {
r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
- r_error.expected = Variant::FLOAT;
r_error.argument = i;
+ r_error.expected = Variant::FLOAT;
return Variant();
}
if (i == 0) {
@@ -557,8 +656,8 @@ Variant VariantUtilityFunctions::max(const Variant **p_args, int p_argcount, Cal
Variant::evaluate(Variant::OP_LESS, base, *p_args[i], ret, valid);
if (!valid) {
r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
- r_error.expected = base.get_type();
r_error.argument = i;
+ r_error.expected = base.get_type();
return Variant();
}
if (ret.booleanize()) {
@@ -590,8 +689,8 @@ Variant VariantUtilityFunctions::min(const Variant **p_args, int p_argcount, Cal
Variant::Type arg_type = p_args[i]->get_type();
if (arg_type != Variant::INT && arg_type != Variant::FLOAT) {
r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
- r_error.expected = Variant::FLOAT;
r_error.argument = i;
+ r_error.expected = Variant::FLOAT;
return Variant();
}
if (i == 0) {
@@ -601,8 +700,8 @@ Variant VariantUtilityFunctions::min(const Variant **p_args, int p_argcount, Cal
Variant::evaluate(Variant::OP_GREATER, base, *p_args[i], ret, valid);
if (!valid) {
r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
- r_error.expected = base.get_type();
r_error.argument = i;
+ r_error.expected = base.get_type();
return Variant();
}
if (ret.booleanize()) {
@@ -630,8 +729,8 @@ Variant VariantUtilityFunctions::clamp(const Variant &x, const Variant &min, con
Variant::evaluate(Variant::OP_LESS, value, min, ret, valid);
if (!valid) {
r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
- r_error.expected = value.get_type();
r_error.argument = 1;
+ r_error.expected = value.get_type();
return Variant();
}
if (ret.booleanize()) {
@@ -640,8 +739,8 @@ Variant VariantUtilityFunctions::clamp(const Variant &x, const Variant &min, con
Variant::evaluate(Variant::OP_GREATER, value, max, ret, valid);
if (!valid) {
r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
- r_error.expected = value.get_type();
r_error.argument = 2;
+ r_error.expected = value.get_type();
return Variant();
}
if (ret.booleanize()) {
@@ -740,10 +839,94 @@ int64_t VariantUtilityFunctions::_typeof(const Variant &obj) {
return obj.get_type();
}
+Variant VariantUtilityFunctions::type_convert(const Variant &p_variant, const Variant::Type p_type) {
+ switch (p_type) {
+ case Variant::Type::NIL:
+ return Variant();
+ case Variant::Type::BOOL:
+ return p_variant.operator bool();
+ case Variant::Type::INT:
+ return p_variant.operator int64_t();
+ case Variant::Type::FLOAT:
+ return p_variant.operator double();
+ case Variant::Type::STRING:
+ return p_variant.operator String();
+ case Variant::Type::VECTOR2:
+ return p_variant.operator Vector2();
+ case Variant::Type::VECTOR2I:
+ return p_variant.operator Vector2i();
+ case Variant::Type::RECT2:
+ return p_variant.operator Rect2();
+ case Variant::Type::RECT2I:
+ return p_variant.operator Rect2i();
+ case Variant::Type::VECTOR3:
+ return p_variant.operator Vector3();
+ case Variant::Type::VECTOR3I:
+ return p_variant.operator Vector3i();
+ case Variant::Type::TRANSFORM2D:
+ return p_variant.operator Transform2D();
+ case Variant::Type::VECTOR4:
+ return p_variant.operator Vector4();
+ case Variant::Type::VECTOR4I:
+ return p_variant.operator Vector4i();
+ case Variant::Type::PLANE:
+ return p_variant.operator Plane();
+ case Variant::Type::QUATERNION:
+ return p_variant.operator Quaternion();
+ case Variant::Type::AABB:
+ return p_variant.operator ::AABB();
+ case Variant::Type::BASIS:
+ return p_variant.operator Basis();
+ case Variant::Type::TRANSFORM3D:
+ return p_variant.operator Transform3D();
+ case Variant::Type::PROJECTION:
+ return p_variant.operator Projection();
+ case Variant::Type::COLOR:
+ return p_variant.operator Color();
+ case Variant::Type::STRING_NAME:
+ return p_variant.operator StringName();
+ case Variant::Type::NODE_PATH:
+ return p_variant.operator NodePath();
+ case Variant::Type::RID:
+ return p_variant.operator ::RID();
+ case Variant::Type::OBJECT:
+ return p_variant.operator Object *();
+ case Variant::Type::CALLABLE:
+ return p_variant.operator Callable();
+ case Variant::Type::SIGNAL:
+ return p_variant.operator Signal();
+ case Variant::Type::DICTIONARY:
+ return p_variant.operator Dictionary();
+ case Variant::Type::ARRAY:
+ return p_variant.operator Array();
+ case Variant::Type::PACKED_BYTE_ARRAY:
+ return p_variant.operator PackedByteArray();
+ case Variant::Type::PACKED_INT32_ARRAY:
+ return p_variant.operator PackedInt32Array();
+ case Variant::Type::PACKED_INT64_ARRAY:
+ return p_variant.operator PackedInt64Array();
+ case Variant::Type::PACKED_FLOAT32_ARRAY:
+ return p_variant.operator PackedFloat32Array();
+ case Variant::Type::PACKED_FLOAT64_ARRAY:
+ return p_variant.operator PackedFloat64Array();
+ case Variant::Type::PACKED_STRING_ARRAY:
+ return p_variant.operator PackedStringArray();
+ case Variant::Type::PACKED_VECTOR2_ARRAY:
+ return p_variant.operator PackedVector2Array();
+ case Variant::Type::PACKED_VECTOR3_ARRAY:
+ return p_variant.operator PackedVector3Array();
+ case Variant::Type::PACKED_COLOR_ARRAY:
+ return p_variant.operator PackedColorArray();
+ case Variant::Type::VARIANT_MAX:
+ ERR_PRINT("Invalid type argument to type_convert(), use the TYPE_* constants. Returning the unconverted Variant.");
+ }
+ return p_variant;
+}
+
String VariantUtilityFunctions::str(const Variant **p_args, int p_arg_count, Callable::CallError &r_error) {
if (p_arg_count < 1) {
r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;
- r_error.argument = 1;
+ r_error.expected = 1;
return String();
}
String s;
@@ -770,6 +953,11 @@ String VariantUtilityFunctions::error_string(Error error) {
return String(error_names[error]);
}
+String VariantUtilityFunctions::type_string(Variant::Type p_type) {
+ ERR_FAIL_INDEX_V_MSG((int)p_type, (int)Variant::VARIANT_MAX, "<invalid type>", "Invalid type argument to type_string(), use the TYPE_* constants.");
+ return Variant::get_type_name(p_type);
+}
+
void VariantUtilityFunctions::print(const Variant **p_args, int p_arg_count, Callable::CallError &r_error) {
String s;
for (int i = 0; i < p_arg_count; i++) {
@@ -887,7 +1075,7 @@ void VariantUtilityFunctions::printraw(const Variant **p_args, int p_arg_count,
void VariantUtilityFunctions::push_error(const Variant **p_args, int p_arg_count, Callable::CallError &r_error) {
if (p_arg_count < 1) {
r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;
- r_error.argument = 1;
+ r_error.expected = 1;
}
String s;
for (int i = 0; i < p_arg_count; i++) {
@@ -907,7 +1095,7 @@ void VariantUtilityFunctions::push_error(const Variant **p_args, int p_arg_count
void VariantUtilityFunctions::push_warning(const Variant **p_args, int p_arg_count, Callable::CallError &r_error) {
if (p_arg_count < 1) {
r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;
- r_error.argument = 1;
+ r_error.expected = 1;
}
String s;
for (int i = 0; i < p_arg_count; i++) {
@@ -1502,6 +1690,10 @@ void Variant::_register_variant_utility_functions() {
FUNCBINDR(atan2, sarray("y", "x"), Variant::UTILITY_FUNC_TYPE_MATH);
+ FUNCBINDR(asinh, sarray("x"), Variant::UTILITY_FUNC_TYPE_MATH);
+ FUNCBINDR(acosh, sarray("x"), Variant::UTILITY_FUNC_TYPE_MATH);
+ FUNCBINDR(atanh, sarray("x"), Variant::UTILITY_FUNC_TYPE_MATH);
+
FUNCBINDR(sqrt, sarray("x"), Variant::UTILITY_FUNC_TYPE_MATH);
FUNCBINDR(fmod, sarray("x", "y"), Variant::UTILITY_FUNC_TYPE_MATH);
FUNCBINDR(fposmod, sarray("x", "y"), Variant::UTILITY_FUNC_TYPE_MATH);
@@ -1553,12 +1745,14 @@ void Variant::_register_variant_utility_functions() {
FUNCBINDR(cubic_interpolate_angle_in_time, sarray("from", "to", "pre", "post", "weight", "to_t", "pre_t", "post_t"), Variant::UTILITY_FUNC_TYPE_MATH);
FUNCBINDR(bezier_interpolate, sarray("start", "control_1", "control_2", "end", "t"), Variant::UTILITY_FUNC_TYPE_MATH);
FUNCBINDR(bezier_derivative, sarray("start", "control_1", "control_2", "end", "t"), Variant::UTILITY_FUNC_TYPE_MATH);
+ FUNCBINDR(angle_difference, sarray("from", "to"), Variant::UTILITY_FUNC_TYPE_MATH);
FUNCBINDR(lerp_angle, sarray("from", "to", "weight"), Variant::UTILITY_FUNC_TYPE_MATH);
FUNCBINDR(inverse_lerp, sarray("from", "to", "weight"), Variant::UTILITY_FUNC_TYPE_MATH);
FUNCBINDR(remap, sarray("value", "istart", "istop", "ostart", "ostop"), Variant::UTILITY_FUNC_TYPE_MATH);
FUNCBINDR(smoothstep, sarray("from", "to", "x"), Variant::UTILITY_FUNC_TYPE_MATH);
FUNCBINDR(move_toward, sarray("from", "to", "delta"), Variant::UTILITY_FUNC_TYPE_MATH);
+ FUNCBINDR(rotate_toward, sarray("from", "to", "delta"), Variant::UTILITY_FUNC_TYPE_MATH);
FUNCBINDR(deg_to_rad, sarray("deg"), Variant::UTILITY_FUNC_TYPE_MATH);
FUNCBINDR(rad_to_deg, sarray("rad"), Variant::UTILITY_FUNC_TYPE_MATH);
@@ -1599,8 +1793,10 @@ void Variant::_register_variant_utility_functions() {
FUNCBINDVR(weakref, sarray("obj"), Variant::UTILITY_FUNC_TYPE_GENERAL);
FUNCBINDR(_typeof, sarray("variable"), Variant::UTILITY_FUNC_TYPE_GENERAL);
+ FUNCBINDR(type_convert, sarray("variant", "type"), Variant::UTILITY_FUNC_TYPE_GENERAL);
FUNCBINDVARARGS(str, sarray(), Variant::UTILITY_FUNC_TYPE_GENERAL);
FUNCBINDR(error_string, sarray("error"), Variant::UTILITY_FUNC_TYPE_GENERAL);
+ FUNCBINDR(type_string, sarray("type"), Variant::UTILITY_FUNC_TYPE_GENERAL);
FUNCBINDVARARGV(print, sarray(), Variant::UTILITY_FUNC_TYPE_GENERAL);
FUNCBINDVARARGV(print_rich, sarray(), Variant::UTILITY_FUNC_TYPE_GENERAL);
FUNCBINDVARARGV(printerr, sarray(), Variant::UTILITY_FUNC_TYPE_GENERAL);
@@ -1648,14 +1844,12 @@ void Variant::call_utility_function(const StringName &p_name, Variant *r_ret, co
if (unlikely(!bfi->is_vararg && p_argcount < bfi->argcount)) {
r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;
- r_error.argument = 0;
r_error.expected = bfi->argcount;
return;
}
if (unlikely(!bfi->is_vararg && p_argcount > bfi->argcount)) {
r_error.error = Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS;
- r_error.argument = 0;
r_error.expected = bfi->argcount;
return;
}
@@ -1772,7 +1966,7 @@ bool Variant::is_utility_function_vararg(const StringName &p_name) {
uint32_t Variant::get_utility_function_hash(const StringName &p_name) {
const VariantUtilityFunctionInfo *bfi = utility_function_table.lookup_ptr(p_name);
- ERR_FAIL_COND_V(!bfi, 0);
+ ERR_FAIL_NULL_V(bfi, 0);
uint32_t hash = hash_murmur3_one_32(bfi->is_vararg);
hash = hash_murmur3_one_32(bfi->returns_value, hash);
diff --git a/core/variant/variant_utility.h b/core/variant/variant_utility.h
index 78f66987cb..a56c84a8e9 100644
--- a/core/variant/variant_utility.h
+++ b/core/variant/variant_utility.h
@@ -45,6 +45,9 @@ struct VariantUtilityFunctions {
static double acos(double arg);
static double atan(double arg);
static double atan2(double y, double x);
+ static double asinh(double arg);
+ static double acosh(double arg);
+ static double atanh(double arg);
static double sqrt(double x);
static double fmod(double b, double r);
static double fposmod(double b, double r);
@@ -87,11 +90,13 @@ struct VariantUtilityFunctions {
double to_t, double pre_t, double post_t);
static double bezier_interpolate(double p_start, double p_control_1, double p_control_2, double p_end, double p_t);
static double bezier_derivative(double p_start, double p_control_1, double p_control_2, double p_end, double p_t);
+ static double angle_difference(double from, double to);
static double lerp_angle(double from, double to, double weight);
static double inverse_lerp(double from, double to, double weight);
static double remap(double value, double istart, double istop, double ostart, double ostop);
static double smoothstep(double from, double to, double val);
static double move_toward(double from, double to, double delta);
+ static double rotate_toward(double from, double to, double delta);
static double deg_to_rad(double angle_deg);
static double rad_to_deg(double angle_rad);
static double linear_to_db(double linear);
@@ -125,6 +130,7 @@ struct VariantUtilityFunctions {
static Variant type_convert(const Variant &p_variant, const Variant::Type p_type);
static String str(const Variant **p_args, int p_arg_count, Callable::CallError &r_error);
static String error_string(Error error);
+ static String type_string(Variant::Type p_type);
static void print(const Variant **p_args, int p_arg_count, Callable::CallError &r_error);
static void print_rich(const Variant **p_args, int p_arg_count, Callable::CallError &r_error);
#undef print_verbose