summaryrefslogtreecommitdiff
path: root/core
diff options
context:
space:
mode:
Diffstat (limited to 'core')
-rw-r--r--core/SCsub20
-rw-r--r--core/bind/core_bind.cpp209
-rw-r--r--core/bind/core_bind.h57
-rw-r--r--core/callable_method_pointer.h4
-rw-r--r--core/class_db.cpp166
-rw-r--r--core/class_db.h8
-rw-r--r--core/color.cpp130
-rw-r--r--core/color.h2
-rw-r--r--core/compressed_translation.cpp11
-rw-r--r--core/compressed_translation.h3
-rw-r--r--core/container_type_validate.h2
-rw-r--r--core/core_builders.py6
-rw-r--r--core/debugger/engine_debugger.cpp2
-rw-r--r--core/debugger/remote_debugger.cpp22
-rw-r--r--core/debugger/remote_debugger_peer.cpp2
-rw-r--r--core/engine.cpp4
-rw-r--r--core/error_macros.h20
-rw-r--r--core/global_constants.cpp32
-rw-r--r--core/global_constants.h1
-rw-r--r--core/hash_map.h4
-rw-r--r--core/image.cpp20
-rw-r--r--core/image.h4
-rw-r--r--core/input/SCsub2
-rw-r--r--core/input/gamecontrollerdb.txt123
-rw-r--r--core/input/input.h2
-rw-r--r--core/input/input_event.h76
-rw-r--r--core/io/http_client.cpp2
-rw-r--r--core/io/json.cpp4
-rw-r--r--core/io/logger.cpp2
-rw-r--r--core/io/packet_peer.h8
-rw-r--r--core/io/packet_peer_udp.cpp65
-rw-r--r--core/io/packet_peer_udp.h15
-rw-r--r--core/io/resource_loader.cpp5
-rw-r--r--core/io/stream_peer.h10
-rw-r--r--core/io/stream_peer_tcp.h10
-rw-r--r--core/io/translation_loader_po.cpp128
-rw-r--r--core/io/udp_server.cpp100
-rw-r--r--core/io/udp_server.h29
-rw-r--r--core/list.h2
-rw-r--r--core/local_vector.h2
-rw-r--r--core/math/aabb.h4
-rw-r--r--core/math/camera_matrix.cpp5
-rw-r--r--core/math/camera_matrix.h2
-rw-r--r--core/math/expression.cpp4
-rw-r--r--core/math/expression.h2
-rw-r--r--core/math/geometry_3d.cpp2
-rw-r--r--core/math/math_funcs.h12
-rw-r--r--core/math/plane.cpp4
-rw-r--r--core/math/plane.h1
-rw-r--r--core/oa_hash_map.h2
-rw-r--r--core/object.cpp112
-rw-r--r--core/object.h286
-rw-r--r--core/os/main_loop.cpp6
-rw-r--r--core/os/main_loop.h6
-rw-r--r--core/os/os.cpp40
-rw-r--r--core/os/os.h9
-rw-r--r--core/packed_data_container.h4
-rw-r--r--core/project_settings.cpp91
-rw-r--r--core/project_settings.h29
-rw-r--r--core/register_core_types.cpp5
-rw-r--r--core/resource.h8
-rw-r--r--core/rid_owner.h2
-rw-r--r--core/script_language.cpp56
-rw-r--r--core/script_language.h12
-rw-r--r--core/string_buffer.h2
-rw-r--r--core/string_name.h4
-rw-r--r--core/translation.cpp217
-rw-r--r--core/translation.h32
-rw-r--r--core/translation_po.cpp312
-rw-r--r--core/translation_po.h92
-rw-r--r--core/ustring.cpp201
-rw-r--r--core/ustring.h31
-rw-r--r--core/variant.cpp10
-rw-r--r--core/variant.h1
-rw-r--r--core/variant_call.cpp54
-rw-r--r--core/variant_internal.h118
-rw-r--r--core/variant_op.cpp52
-rw-r--r--core/vector.h4
78 files changed, 2293 insertions, 855 deletions
diff --git a/core/SCsub b/core/SCsub
index 80a5f6b623..40ee78d3ea 100644
--- a/core/SCsub
+++ b/core/SCsub
@@ -149,28 +149,38 @@ env.Depends(
env.CommandNoCache(
"#core/io/certs_compressed.gen.h",
"#thirdparty/certs/ca-certificates.crt",
- run_in_subprocess(core_builders.make_certs_header),
+ env.Run(core_builders.make_certs_header, "Building ca-certificates header."),
)
# Make binders
env.CommandNoCache(
["method_bind.gen.inc", "method_bind_ext.gen.inc", "method_bind_free_func.gen.inc"],
"make_binders.py",
- run_in_subprocess(make_binders.run),
+ env.Run(make_binders.run, "Generating method binders."),
)
# Authors
env.Depends("#core/authors.gen.h", "../AUTHORS.md")
-env.CommandNoCache("#core/authors.gen.h", "../AUTHORS.md", run_in_subprocess(core_builders.make_authors_header))
+env.CommandNoCache(
+ "#core/authors.gen.h",
+ "../AUTHORS.md",
+ env.Run(core_builders.make_authors_header, "Generating authors header."),
+)
# Donors
env.Depends("#core/donors.gen.h", "../DONORS.md")
-env.CommandNoCache("#core/donors.gen.h", "../DONORS.md", run_in_subprocess(core_builders.make_donors_header))
+env.CommandNoCache(
+ "#core/donors.gen.h",
+ "../DONORS.md",
+ env.Run(core_builders.make_donors_header, "Generating donors header."),
+)
# License
env.Depends("#core/license.gen.h", ["../COPYRIGHT.txt", "../LICENSE.txt"])
env.CommandNoCache(
- "#core/license.gen.h", ["../COPYRIGHT.txt", "../LICENSE.txt"], run_in_subprocess(core_builders.make_license_header)
+ "#core/license.gen.h",
+ ["../COPYRIGHT.txt", "../LICENSE.txt"],
+ env.Run(core_builders.make_license_header, "Generating license header."),
)
# Chain load SCsubs
diff --git a/core/bind/core_bind.cpp b/core/bind/core_bind.cpp
index 267391c4d6..489ff762c9 100644
--- a/core/bind/core_bind.cpp
+++ b/core/bind/core_bind.cpp
@@ -31,6 +31,7 @@
#include "core_bind.h"
#include "core/crypto/crypto_core.h"
+#include "core/debugger/engine_debugger.h"
#include "core/io/file_access_compressed.h"
#include "core/io/file_access_encrypted.h"
#include "core/io/json.h"
@@ -454,7 +455,7 @@ Dictionary _OS::get_datetime_from_unix_time(int64_t unix_time_val) const {
} else {
dayno = (unix_time_val - SECS_DAY + 1) / SECS_DAY;
dayclock = unix_time_val - dayno * SECS_DAY;
- date.weekday = static_cast<OS::Weekday>((dayno - 3) % 7 + 7);
+ date.weekday = static_cast<OS::Weekday>(((dayno % 7) + 11) % 7);
do {
year--;
dayno += YEARSIZE(year);
@@ -781,6 +782,7 @@ void _OS::_bind_methods() {
// Those default values need to be specified for the docs generator,
// to avoid using values from the documentation writer's own OS instance.
+ ADD_PROPERTY_DEFAULT("tablet_driver", "");
ADD_PROPERTY_DEFAULT("exit_code", 0);
ADD_PROPERTY_DEFAULT("low_processor_usage_mode", false);
ADD_PROPERTY_DEFAULT("low_processor_usage_mode_sleep_usec", 6900);
@@ -1619,12 +1621,17 @@ Error _Directory::open(const String &p_path) {
memdelete(d);
}
d = alt;
+ dir_open = true;
return OK;
}
+bool _Directory::is_open() const {
+ return d && dir_open;
+}
+
Error _Directory::list_dir_begin(bool p_skip_navigational, bool p_skip_hidden) {
- ERR_FAIL_COND_V_MSG(!d, ERR_UNCONFIGURED, "Directory must be opened before use.");
+ ERR_FAIL_COND_V_MSG(!is_open(), ERR_UNCONFIGURED, "Directory must be opened before use.");
_list_skip_navigational = p_skip_navigational;
_list_skip_hidden = p_skip_hidden;
@@ -1633,7 +1640,7 @@ Error _Directory::list_dir_begin(bool p_skip_navigational, bool p_skip_hidden) {
}
String _Directory::get_next() {
- ERR_FAIL_COND_V_MSG(!d, "", "Directory must be opened before use.");
+ ERR_FAIL_COND_V_MSG(!is_open(), "", "Directory must be opened before use.");
String next = d->get_next();
while (next != "" && ((_list_skip_navigational && (next == "." || next == "..")) || (_list_skip_hidden && d->current_is_hidden()))) {
@@ -1643,42 +1650,49 @@ String _Directory::get_next() {
}
bool _Directory::current_is_dir() const {
- ERR_FAIL_COND_V_MSG(!d, false, "Directory must be opened before use.");
+ ERR_FAIL_COND_V_MSG(!is_open(), false, "Directory must be opened before use.");
return d->current_is_dir();
}
void _Directory::list_dir_end() {
- ERR_FAIL_COND_MSG(!d, "Directory must be opened before use.");
+ ERR_FAIL_COND_MSG(!is_open(), "Directory must be opened before use.");
d->list_dir_end();
}
int _Directory::get_drive_count() {
- ERR_FAIL_COND_V_MSG(!d, 0, "Directory must be opened before use.");
+ ERR_FAIL_COND_V_MSG(!is_open(), 0, "Directory must be opened before use.");
return d->get_drive_count();
}
String _Directory::get_drive(int p_drive) {
- ERR_FAIL_COND_V_MSG(!d, "", "Directory must be opened before use.");
+ ERR_FAIL_COND_V_MSG(!is_open(), "", "Directory must be opened before use.");
return d->get_drive(p_drive);
}
int _Directory::get_current_drive() {
- ERR_FAIL_COND_V_MSG(!d, 0, "Directory must be opened before use.");
+ ERR_FAIL_COND_V_MSG(!is_open(), 0, "Directory must be opened before use.");
return d->get_current_drive();
}
Error _Directory::change_dir(String p_dir) {
- ERR_FAIL_COND_V_MSG(!d, ERR_UNCONFIGURED, "Directory must be opened before use.");
- return d->change_dir(p_dir);
+ ERR_FAIL_COND_V_MSG(!d, ERR_UNCONFIGURED, "Directory is not configured properly.");
+ Error err = d->change_dir(p_dir);
+
+ if (err != OK) {
+ return err;
+ }
+ dir_open = true;
+
+ return OK;
}
String _Directory::get_current_dir() {
- ERR_FAIL_COND_V_MSG(!d, "", "Directory must be opened before use.");
+ ERR_FAIL_COND_V_MSG(!is_open(), "", "Directory must be opened before use.");
return d->get_current_dir();
}
Error _Directory::make_dir(String p_dir) {
- ERR_FAIL_COND_V_MSG(!d, ERR_UNCONFIGURED, "Directory must be opened before use.");
+ ERR_FAIL_COND_V_MSG(!d, ERR_UNCONFIGURED, "Directory is not configured properly.");
if (!p_dir.is_rel_path()) {
DirAccess *d = DirAccess::create_for_path(p_dir);
Error err = d->make_dir(p_dir);
@@ -1689,7 +1703,7 @@ Error _Directory::make_dir(String p_dir) {
}
Error _Directory::make_dir_recursive(String p_dir) {
- ERR_FAIL_COND_V_MSG(!d, ERR_UNCONFIGURED, "Directory must be opened before use.");
+ ERR_FAIL_COND_V_MSG(!d, ERR_UNCONFIGURED, "Directory is not configured properly.");
if (!p_dir.is_rel_path()) {
DirAccess *d = DirAccess::create_for_path(p_dir);
Error err = d->make_dir_recursive(p_dir);
@@ -1700,8 +1714,7 @@ Error _Directory::make_dir_recursive(String p_dir) {
}
bool _Directory::file_exists(String p_file) {
- ERR_FAIL_COND_V_MSG(!d, false, "Directory must be opened before use.");
-
+ ERR_FAIL_COND_V_MSG(!d, false, "Directory is not configured properly.");
if (!p_file.is_rel_path()) {
return FileAccess::exists(p_file);
}
@@ -1710,30 +1723,29 @@ bool _Directory::file_exists(String p_file) {
}
bool _Directory::dir_exists(String p_dir) {
- ERR_FAIL_COND_V_MSG(!d, false, "Directory must be opened before use.");
+ ERR_FAIL_COND_V_MSG(!d, false, "Directory is not configured properly.");
if (!p_dir.is_rel_path()) {
DirAccess *d = DirAccess::create_for_path(p_dir);
bool exists = d->dir_exists(p_dir);
memdelete(d);
return exists;
-
- } else {
- return d->dir_exists(p_dir);
}
+
+ return d->dir_exists(p_dir);
}
int _Directory::get_space_left() {
- ERR_FAIL_COND_V_MSG(!d, 0, "Directory must be opened before use.");
+ ERR_FAIL_COND_V_MSG(!is_open(), 0, "Directory must be opened before use.");
return d->get_space_left() / 1024 * 1024; //return value in megabytes, given binding is int
}
Error _Directory::copy(String p_from, String p_to) {
- ERR_FAIL_COND_V_MSG(!d, ERR_UNCONFIGURED, "Directory must be opened before use.");
+ ERR_FAIL_COND_V_MSG(!is_open(), ERR_UNCONFIGURED, "Directory must be opened before use.");
return d->copy(p_from, p_to);
}
Error _Directory::rename(String p_from, String p_to) {
- ERR_FAIL_COND_V_MSG(!d, ERR_UNCONFIGURED, "Directory must be opened before use.");
+ ERR_FAIL_COND_V_MSG(!is_open(), ERR_UNCONFIGURED, "Directory must be opened before use.");
if (!p_from.is_rel_path()) {
DirAccess *d = DirAccess::create_for_path(p_from);
Error err = d->rename(p_from, p_to);
@@ -1745,7 +1757,7 @@ Error _Directory::rename(String p_from, String p_to) {
}
Error _Directory::remove(String p_name) {
- ERR_FAIL_COND_V_MSG(!d, ERR_UNCONFIGURED, "Directory must be opened before use.");
+ ERR_FAIL_COND_V_MSG(!is_open(), ERR_UNCONFIGURED, "Directory must be opened before use.");
if (!p_name.is_rel_path()) {
DirAccess *d = DirAccess::create_for_path(p_name);
Error err = d->remove(p_name);
@@ -2456,3 +2468,154 @@ Ref<JSONParseResult> _JSON::parse(const String &p_json) {
}
_JSON *_JSON::singleton = nullptr;
+
+////// _EngineDebugger //////
+
+void _EngineDebugger::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("is_active"), &_EngineDebugger::is_active);
+
+ ClassDB::bind_method(D_METHOD("register_profiler", "name", "toggle", "add", "tick"), &_EngineDebugger::register_profiler);
+ ClassDB::bind_method(D_METHOD("unregister_profiler", "name"), &_EngineDebugger::unregister_profiler);
+ ClassDB::bind_method(D_METHOD("is_profiling", "name"), &_EngineDebugger::is_profiling);
+ ClassDB::bind_method(D_METHOD("has_profiler", "name"), &_EngineDebugger::has_profiler);
+
+ ClassDB::bind_method(D_METHOD("profiler_add_frame_data", "name", "data"), &_EngineDebugger::profiler_add_frame_data);
+ ClassDB::bind_method(D_METHOD("profiler_enable", "name", "enable", "arguments"), &_EngineDebugger::profiler_enable, DEFVAL(Array()));
+
+ ClassDB::bind_method(D_METHOD("register_message_capture", "name", "callable"), &_EngineDebugger::register_message_capture);
+ ClassDB::bind_method(D_METHOD("unregister_message_capture", "name"), &_EngineDebugger::unregister_message_capture);
+ ClassDB::bind_method(D_METHOD("has_capture", "name"), &_EngineDebugger::has_capture);
+
+ ClassDB::bind_method(D_METHOD("send_message", "message", "data"), &_EngineDebugger::send_message);
+}
+
+bool _EngineDebugger::is_active() {
+ return EngineDebugger::is_active();
+}
+
+void _EngineDebugger::register_profiler(const StringName &p_name, const Callable &p_toggle, const Callable &p_add, const Callable &p_tick) {
+ ERR_FAIL_COND_MSG(profilers.has(p_name) || has_profiler(p_name), "Profiler already registered: " + p_name);
+ profilers.insert(p_name, ProfilerCallable(p_toggle, p_add, p_tick));
+ ProfilerCallable &p = profilers[p_name];
+ EngineDebugger::Profiler profiler(
+ &p,
+ &_EngineDebugger::call_toggle,
+ &_EngineDebugger::call_add,
+ &_EngineDebugger::call_tick);
+ EngineDebugger::register_profiler(p_name, profiler);
+}
+
+void _EngineDebugger::unregister_profiler(const StringName &p_name) {
+ ERR_FAIL_COND_MSG(!profilers.has(p_name), "Profiler not registered: " + p_name);
+ EngineDebugger::unregister_profiler(p_name);
+ profilers.erase(p_name);
+}
+
+bool _EngineDebugger::_EngineDebugger::is_profiling(const StringName &p_name) {
+ return EngineDebugger::is_profiling(p_name);
+}
+
+bool _EngineDebugger::has_profiler(const StringName &p_name) {
+ return EngineDebugger::has_profiler(p_name);
+}
+
+void _EngineDebugger::profiler_add_frame_data(const StringName &p_name, const Array &p_data) {
+ EngineDebugger::profiler_add_frame_data(p_name, p_data);
+}
+
+void _EngineDebugger::profiler_enable(const StringName &p_name, bool p_enabled, const Array &p_opts) {
+ if (EngineDebugger::get_singleton()) {
+ EngineDebugger::get_singleton()->profiler_enable(p_name, p_enabled, p_opts);
+ }
+}
+
+void _EngineDebugger::register_message_capture(const StringName &p_name, const Callable &p_callable) {
+ ERR_FAIL_COND_MSG(captures.has(p_name) || has_capture(p_name), "Capture already registered: " + p_name);
+ captures.insert(p_name, p_callable);
+ Callable &c = captures[p_name];
+ EngineDebugger::Capture capture(&c, &_EngineDebugger::call_capture);
+ EngineDebugger::register_message_capture(p_name, capture);
+}
+
+void _EngineDebugger::unregister_message_capture(const StringName &p_name) {
+ ERR_FAIL_COND_MSG(!captures.has(p_name), "Capture not registered: " + p_name);
+ EngineDebugger::unregister_message_capture(p_name);
+ captures.erase(p_name);
+}
+
+bool _EngineDebugger::has_capture(const StringName &p_name) {
+ return EngineDebugger::has_capture(p_name);
+}
+
+void _EngineDebugger::send_message(const String &p_msg, const Array &p_data) {
+ ERR_FAIL_COND_MSG(!EngineDebugger::is_active(), "Can't send message. No active debugger");
+ EngineDebugger::get_singleton()->send_message(p_msg, p_data);
+}
+
+void _EngineDebugger::call_toggle(void *p_user, bool p_enable, const Array &p_opts) {
+ Callable &toggle = ((ProfilerCallable *)p_user)->callable_toggle;
+ if (toggle.is_null()) {
+ return;
+ }
+ Variant enable = p_enable, opts = p_opts;
+ const Variant *args[2] = { &enable, &opts };
+ Variant retval;
+ Callable::CallError err;
+ toggle.call(args, 2, retval, err);
+ ERR_FAIL_COND_MSG(err.error != Callable::CallError::CALL_OK, "Error calling 'toggle' to callable: " + Variant::get_callable_error_text(toggle, args, 2, err));
+}
+
+void _EngineDebugger::call_add(void *p_user, const Array &p_data) {
+ Callable &add = ((ProfilerCallable *)p_user)->callable_add;
+ if (add.is_null()) {
+ return;
+ }
+ Variant data = p_data;
+ const Variant *args[1] = { &data };
+ Variant retval;
+ Callable::CallError err;
+ add.call(args, 1, retval, err);
+ ERR_FAIL_COND_MSG(err.error != Callable::CallError::CALL_OK, "Error calling 'add' to callable: " + Variant::get_callable_error_text(add, args, 1, err));
+}
+
+void _EngineDebugger::call_tick(void *p_user, float p_frame_time, float p_idle_time, float p_physics_time, float p_physics_frame_time) {
+ Callable &tick = ((ProfilerCallable *)p_user)->callable_tick;
+ if (tick.is_null()) {
+ return;
+ }
+ Variant frame_time = p_frame_time, idle_time = p_idle_time, physics_time = p_physics_time, physics_frame_time = p_physics_frame_time;
+ const Variant *args[4] = { &frame_time, &idle_time, &physics_time, &physics_frame_time };
+ Variant retval;
+ Callable::CallError err;
+ tick.call(args, 4, retval, err);
+ ERR_FAIL_COND_MSG(err.error != Callable::CallError::CALL_OK, "Error calling 'tick' to callable: " + Variant::get_callable_error_text(tick, args, 4, err));
+}
+
+Error _EngineDebugger::call_capture(void *p_user, const String &p_cmd, const Array &p_data, bool &r_captured) {
+ Callable &capture = *(Callable *)p_user;
+ if (capture.is_null()) {
+ return FAILED;
+ }
+ Variant cmd = p_cmd, data = p_data;
+ const Variant *args[2] = { &cmd, &data };
+ Variant retval;
+ Callable::CallError err;
+ capture.call(args, 2, retval, err);
+ ERR_FAIL_COND_V_MSG(err.error != Callable::CallError::CALL_OK, FAILED, "Error calling 'capture' to callable: " + Variant::get_callable_error_text(capture, args, 2, err));
+ ERR_FAIL_COND_V_MSG(retval.get_type() != Variant::BOOL, FAILED, "Error calling 'capture' to callable: " + String(capture) + ". Return type is not bool.");
+ r_captured = retval;
+ return OK;
+}
+
+_EngineDebugger::~_EngineDebugger() {
+ for (Map<StringName, Callable>::Element *E = captures.front(); E; E = E->next()) {
+ EngineDebugger::unregister_message_capture(E->key());
+ }
+ captures.clear();
+ for (Map<StringName, ProfilerCallable>::Element *E = profilers.front(); E; E = E->next()) {
+ EngineDebugger::unregister_profiler(E->key());
+ }
+ profilers.clear();
+}
+
+_EngineDebugger *_EngineDebugger::singleton = nullptr;
diff --git a/core/bind/core_bind.h b/core/bind/core_bind.h
index f9f5a4e7d7..a59fcda60c 100644
--- a/core/bind/core_bind.h
+++ b/core/bind/core_bind.h
@@ -457,6 +457,7 @@ VARIANT_ENUM_CAST(_File::CompressionMode);
class _Directory : public Reference {
GDCLASS(_Directory, Reference);
DirAccess *d;
+ bool dir_open = false;
protected:
static void _bind_methods();
@@ -464,6 +465,8 @@ protected:
public:
Error open(const String &p_path);
+ bool is_open() const;
+
Error list_dir_begin(bool p_skip_navigational = false, bool p_skip_hidden = false); // This starts dir listing.
String get_next();
bool current_is_dir() const;
@@ -712,4 +715,58 @@ public:
_JSON() { singleton = this; }
};
+class _EngineDebugger : public Object {
+ GDCLASS(_EngineDebugger, Object);
+
+ class ProfilerCallable {
+ friend class _EngineDebugger;
+
+ Callable callable_toggle;
+ Callable callable_add;
+ Callable callable_tick;
+
+ public:
+ ProfilerCallable() {}
+
+ ProfilerCallable(const Callable &p_toggle, const Callable &p_add, const Callable &p_tick) {
+ callable_toggle = p_toggle;
+ callable_add = p_add;
+ callable_tick = p_tick;
+ }
+ };
+
+ Map<StringName, Callable> captures;
+ Map<StringName, ProfilerCallable> profilers;
+
+protected:
+ static void _bind_methods();
+ static _EngineDebugger *singleton;
+
+public:
+ static _EngineDebugger *get_singleton() { return singleton; }
+
+ bool is_active();
+
+ void register_profiler(const StringName &p_name, const Callable &p_toggle, const Callable &p_add, const Callable &p_tick);
+ void unregister_profiler(const StringName &p_name);
+ bool is_profiling(const StringName &p_name);
+ bool has_profiler(const StringName &p_name);
+ void profiler_add_frame_data(const StringName &p_name, const Array &p_data);
+ void profiler_enable(const StringName &p_name, bool p_enabled, const Array &p_opts = Array());
+
+ void register_message_capture(const StringName &p_name, const Callable &p_callable);
+ void unregister_message_capture(const StringName &p_name);
+ bool has_capture(const StringName &p_name);
+
+ void send_message(const String &p_msg, const Array &p_data);
+
+ static void call_toggle(void *p_user, bool p_enable, const Array &p_opts);
+ static void call_add(void *p_user, const Array &p_data);
+ static void call_tick(void *p_user, float p_frame_time, float p_idle_time, float p_physics_time, float p_physics_frame_time);
+ static Error call_capture(void *p_user, const String &p_cmd, const Array &p_data, bool &r_captured);
+
+ _EngineDebugger() { singleton = this; }
+ ~_EngineDebugger();
+};
+
#endif // CORE_BIND_H
diff --git a/core/callable_method_pointer.h b/core/callable_method_pointer.h
index 22db7d1c82..1bb89e53e1 100644
--- a/core/callable_method_pointer.h
+++ b/core/callable_method_pointer.h
@@ -131,7 +131,7 @@ void call_with_variant_args_helper(T *p_instance, void (T::*p_method)(P...), con
#ifdef DEBUG_METHODS_ENABLED
(p_instance->*p_method)(VariantCasterAndValidate<P>::cast(p_args, Is, r_error)...);
#else
- (p_instance->*p_method)(VariantCaster<P>::cast(p_args[Is])...);
+ (p_instance->*p_method)(VariantCaster<P>::cast(*p_args[Is])...);
#endif
}
@@ -228,7 +228,7 @@ void call_with_variant_args_ret_helper(T *p_instance, R (T::*p_method)(P...), co
#ifdef DEBUG_METHODS_ENABLED
r_ret = (p_instance->*p_method)(VariantCasterAndValidate<P>::cast(p_args, Is, r_error)...);
#else
- (p_instance->*p_method)(VariantCaster<P>::cast(p_args[Is])...);
+ (p_instance->*p_method)(VariantCaster<P>::cast(*p_args[Is])...);
#endif
}
diff --git a/core/class_db.cpp b/core/class_db.cpp
index 05c9850c39..88f1df3457 100644
--- a/core/class_db.cpp
+++ b/core/class_db.cpp
@@ -548,6 +548,29 @@ void ClassDB::_add_class2(const StringName &p_class, const StringName &p_inherit
}
}
+#ifdef DEBUG_METHODS_ENABLED
+static MethodInfo info_from_bind(MethodBind *p_method) {
+ MethodInfo minfo;
+ minfo.name = p_method->get_name();
+ minfo.id = p_method->get_method_id();
+
+ for (int i = 0; i < p_method->get_argument_count(); i++) {
+ minfo.arguments.push_back(p_method->get_argument_info(i));
+ }
+
+ minfo.return_val = p_method->get_return_info();
+ minfo.flags = p_method->get_hint_flags();
+
+ for (int i = 0; i < p_method->get_argument_count(); i++) {
+ if (p_method->has_default_argument(i)) {
+ minfo.default_arguments.push_back(p_method->get_default_argument(i));
+ }
+ }
+
+ return minfo;
+}
+#endif
+
void ClassDB::get_method_list(StringName p_class, List<MethodInfo> *p_methods, bool p_no_inheritance, bool p_exclude_from_properties) {
OBJTYPE_RLOCK;
@@ -570,29 +593,12 @@ void ClassDB::get_method_list(StringName p_class, List<MethodInfo> *p_methods, b
}
for (List<StringName>::Element *E = type->method_order.front(); E; E = E->next()) {
- MethodBind *method = type->method_map.get(E->get());
- MethodInfo minfo;
- minfo.name = E->get();
- minfo.id = method->get_method_id();
-
- if (p_exclude_from_properties && type->methods_in_properties.has(minfo.name)) {
+ if (p_exclude_from_properties && type->methods_in_properties.has(E->get())) {
continue;
}
- for (int i = 0; i < method->get_argument_count(); i++) {
- //Variant::Type t=method->get_argument_type(i);
-
- minfo.arguments.push_back(method->get_argument_info(i));
- }
-
- minfo.return_val = method->get_return_info();
- minfo.flags = method->get_hint_flags();
-
- for (int i = 0; i < method->get_argument_count(); i++) {
- if (method->has_default_argument(i)) {
- minfo.default_arguments.push_back(method->get_default_argument(i));
- }
- }
+ MethodBind *method = type->method_map.get(E->get());
+ MethodInfo minfo = info_from_bind(method);
p_methods->push_back(minfo);
}
@@ -618,6 +624,57 @@ void ClassDB::get_method_list(StringName p_class, List<MethodInfo> *p_methods, b
}
}
+bool ClassDB::get_method_info(StringName p_class, StringName p_method, MethodInfo *r_info, 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
+ MethodBind **method = type->method_map.getptr(p_method);
+ if (method && *method) {
+ if (r_info != nullptr) {
+ MethodInfo minfo = info_from_bind(*method);
+ *r_info = minfo;
+ }
+ return true;
+ } else if (type->virtual_methods_map.has(p_method)) {
+ if (r_info) {
+ *r_info = type->virtual_methods_map[p_method];
+ }
+ return true;
+ }
+#else
+ if (type->method_map.has(p_method)) {
+ if (r_info) {
+ MethodBind *m = type->method_map[p_method];
+ MethodInfo mi;
+ mi.name = m->get_name();
+ *r_info = mi;
+ }
+ return true;
+ }
+#endif
+
+ if (p_no_inheritance) {
+ break;
+ }
+
+ type = type->inherits_ptr;
+ }
+
+ return false;
+}
+
MethodBind *ClassDB::get_method(StringName p_class, StringName p_name) {
OBJTYPE_RLOCK;
@@ -718,6 +775,25 @@ int ClassDB::get_integer_constant(const StringName &p_class, const StringName &p
return 0;
}
+bool ClassDB::has_integer_constant(const StringName &p_class, const StringName &p_name, bool p_no_inheritance) {
+ OBJTYPE_RLOCK;
+
+ ClassInfo *type = classes.getptr(p_class);
+
+ while (type) {
+ if (type->constant_map.has(p_name)) {
+ return true;
+ }
+ if (p_no_inheritance) {
+ return false;
+ }
+
+ type = type->inherits_ptr;
+ }
+
+ return false;
+}
+
StringName ClassDB::get_integer_constant_enum(const StringName &p_class, const StringName &p_name, bool p_no_inheritance) {
OBJTYPE_RLOCK;
@@ -784,6 +860,25 @@ void ClassDB::get_enum_constants(const StringName &p_class, const StringName &p_
}
}
+bool ClassDB::has_enum(const StringName &p_class, const StringName &p_name, bool p_no_inheritance) {
+ OBJTYPE_RLOCK;
+
+ ClassInfo *type = classes.getptr(p_class);
+
+ while (type) {
+ if (type->enum_map.has(p_name)) {
+ return true;
+ }
+ if (p_no_inheritance) {
+ return false;
+ }
+
+ type = type->inherits_ptr;
+ }
+
+ return false;
+}
+
void ClassDB::add_signal(StringName p_class, const MethodInfo &p_signal) {
OBJTYPE_WLOCK;
@@ -825,7 +920,7 @@ void ClassDB::get_signal_list(StringName p_class, List<MethodInfo> *p_signals, b
}
}
-bool ClassDB::has_signal(StringName p_class, StringName p_signal) {
+bool ClassDB::has_signal(StringName p_class, StringName p_signal, bool p_no_inheritance) {
OBJTYPE_RLOCK;
ClassInfo *type = classes.getptr(p_class);
ClassInfo *check = type;
@@ -833,6 +928,9 @@ bool ClassDB::has_signal(StringName p_class, StringName p_signal) {
if (check->signal_map.has(p_signal)) {
return true;
}
+ if (p_no_inheritance) {
+ return false;
+ }
check = check->inherits_ptr;
}
@@ -910,6 +1008,7 @@ void ClassDB::add_property(StringName p_class, const PropertyInfo &p_pinfo, cons
OBJTYPE_WLOCK
type->property_list.push_back(p_pinfo);
+ type->property_map[p_pinfo.name] = p_pinfo;
#ifdef DEBUG_METHODS_ENABLED
if (mb_get) {
type->methods_in_properties.insert(p_getter);
@@ -959,6 +1058,30 @@ void ClassDB::get_property_list(StringName p_class, List<PropertyInfo> *p_list,
}
}
+bool ClassDB::get_property_info(StringName p_class, StringName p_property, PropertyInfo *r_info, bool p_no_inheritance, const Object *p_validator) {
+ OBJTYPE_RLOCK;
+
+ ClassInfo *check = classes.getptr(p_class);
+ while (check) {
+ if (check->property_map.has(p_property)) {
+ PropertyInfo pinfo = check->property_map[p_property];
+ if (p_validator) {
+ p_validator->_validate_property(pinfo);
+ }
+ if (r_info) {
+ *r_info = pinfo;
+ }
+ return true;
+ }
+ if (p_no_inheritance) {
+ break;
+ }
+ check = check->inherits_ptr;
+ }
+
+ return false;
+}
+
bool ClassDB::set_property(Object *p_object, const StringName &p_property, const Variant &p_value, bool *r_valid) {
ClassInfo *type = classes.getptr(p_object->get_class_name());
ClassInfo *check = type;
@@ -1239,6 +1362,7 @@ void ClassDB::add_virtual_method(const StringName &p_class, const MethodInfo &p_
mi.flags |= METHOD_FLAG_VIRTUAL;
}
classes[p_class].virtual_methods.push_back(mi);
+ classes[p_class].virtual_methods_map[p_method.name] = mi;
#endif
}
diff --git a/core/class_db.h b/core/class_db.h
index eae2a9afd4..86ac2aa001 100644
--- a/core/class_db.h
+++ b/core/class_db.h
@@ -120,11 +120,13 @@ public:
HashMap<StringName, List<StringName>> enum_map;
HashMap<StringName, MethodInfo> signal_map;
List<PropertyInfo> property_list;
+ HashMap<StringName, PropertyInfo> property_map;
#ifdef DEBUG_METHODS_ENABLED
List<StringName> constant_order;
List<StringName> method_order;
Set<StringName> methods_in_properties;
List<MethodInfo> virtual_methods;
+ Map<StringName, MethodInfo> virtual_methods_map;
StringName category;
#endif
HashMap<StringName, PropertySetGet> property_setget;
@@ -328,7 +330,7 @@ public:
}
static void add_signal(StringName p_class, const MethodInfo &p_signal);
- static bool has_signal(StringName p_class, StringName p_signal);
+ static bool has_signal(StringName p_class, StringName p_signal, bool p_no_inheritance = false);
static bool get_signal(StringName p_class, StringName p_signal, MethodInfo *r_signal);
static void get_signal_list(StringName p_class, List<MethodInfo> *p_signals, bool p_no_inheritance = false);
@@ -337,6 +339,7 @@ public:
static void add_property(StringName p_class, const PropertyInfo &p_pinfo, const StringName &p_setter, const StringName &p_getter, int p_index = -1);
static void set_property_default_value(StringName p_class, const StringName &p_name, const Variant &p_default);
static void get_property_list(StringName p_class, List<PropertyInfo> *p_list, bool p_no_inheritance = false, const Object *p_validator = nullptr);
+ static bool get_property_info(StringName p_class, StringName p_property, PropertyInfo *r_info, bool p_no_inheritance = false, const Object *p_validator = nullptr);
static bool set_property(Object *p_object, const StringName &p_property, const Variant &p_value, bool *r_valid = nullptr);
static bool get_property(Object *p_object, const StringName &p_property, Variant &r_value);
static bool has_property(const StringName &p_class, const StringName &p_property, bool p_no_inheritance = false);
@@ -349,6 +352,7 @@ public:
static void set_method_flags(StringName p_class, StringName p_method, int p_flags);
static void get_method_list(StringName p_class, List<MethodInfo> *p_methods, bool p_no_inheritance = false, bool p_exclude_from_properties = false);
+ static bool get_method_info(StringName p_class, StringName p_method, MethodInfo *r_info, bool p_no_inheritance = false, bool p_exclude_from_properties = false);
static MethodBind *get_method(StringName p_class, StringName p_name);
static void add_virtual_method(const StringName &p_class, const MethodInfo &p_method, bool p_virtual = true);
@@ -357,10 +361,12 @@ public:
static void bind_integer_constant(const StringName &p_class, const StringName &p_enum, const StringName &p_name, int p_constant);
static void get_integer_constant_list(const StringName &p_class, List<String> *p_constants, bool p_no_inheritance = false);
static int get_integer_constant(const StringName &p_class, const StringName &p_name, bool *p_success = nullptr);
+ static bool has_integer_constant(const StringName &p_class, const StringName &p_name, bool p_no_inheritance = false);
static StringName get_integer_constant_enum(const StringName &p_class, const StringName &p_name, bool p_no_inheritance = false);
static void get_enum_list(const StringName &p_class, List<StringName> *p_enums, bool p_no_inheritance = false);
static void get_enum_constants(const StringName &p_class, const StringName &p_enum, List<StringName> *p_constants, bool p_no_inheritance = false);
+ static bool has_enum(const StringName &p_class, const StringName &p_name, bool p_no_inheritance = false);
static Variant class_get_default_property_value(const StringName &p_class, const StringName &p_property, bool *r_valid = nullptr);
diff --git a/core/color.cpp b/core/color.cpp
index 27a2d0af5c..c85cd9100d 100644
--- a/core/color.cpp
+++ b/core/color.cpp
@@ -261,33 +261,21 @@ Color Color::from_rgbe9995(uint32_t p_rgbe) {
return Color(rd, gd, bd, 1.0f);
}
-static float _parse_col(const String &p_str, int p_ofs) {
- int ig = 0;
-
- for (int i = 0; i < 2; i++) {
- int c = p_str[i + p_ofs];
- int v = 0;
-
- if (c >= '0' && c <= '9') {
- v = c - '0';
- } else if (c >= 'a' && c <= 'f') {
- v = c - 'a';
- v += 10;
- } else if (c >= 'A' && c <= 'F') {
- v = c - 'A';
- v += 10;
- } else {
- return -1;
- }
-
- if (i == 0) {
- ig += v * 16;
- } else {
- ig += v;
- }
+static int _parse_col4(const String &p_str, int p_ofs) {
+ char character = p_str[p_ofs];
+
+ if (character >= '0' && character <= '9') {
+ return character - '0';
+ } else if (character >= 'a' && character <= 'f') {
+ return character + (10 - 'a');
+ } else if (character >= 'A' && character <= 'F') {
+ return character + (10 - 'A');
}
+ return -1;
+}
- return ig;
+static int _parse_col8(const String &p_str, int p_ofs) {
+ return _parse_col4(p_str, p_ofs) * 16 + _parse_col4(p_str, p_ofs + 1);
}
Color Color::inverted() const {
@@ -302,49 +290,54 @@ Color Color::contrasted() const {
return c;
}
-Color Color::html(const String &p_color) {
- String color = p_color;
+Color Color::html(const String &p_rgba) {
+ String color = p_rgba;
if (color.length() == 0) {
return Color();
}
if (color[0] == '#') {
- color = color.substr(1, color.length() - 1);
- }
- if (color.length() == 3 || color.length() == 4) {
- String exp_color;
- for (int i = 0; i < color.length(); i++) {
- exp_color += color[i];
- exp_color += color[i];
- }
- color = exp_color;
+ color = color.substr(1);
}
+ // If enabled, use 1 hex digit per channel instead of 2.
+ // Other sizes aren't in the HTML/CSS spec but we could add them if desired.
+ bool is_shorthand = color.length() < 5;
bool alpha = false;
if (color.length() == 8) {
alpha = true;
} else if (color.length() == 6) {
alpha = false;
+ } else if (color.length() == 4) {
+ alpha = true;
+ } else if (color.length() == 3) {
+ alpha = false;
} else {
- ERR_FAIL_V_MSG(Color(), "Invalid color code: " + p_color + ".");
+ ERR_FAIL_V_MSG(Color(), "Invalid color code: " + p_rgba + ".");
}
- int a = 255;
- if (alpha) {
- a = _parse_col(color, 0);
- ERR_FAIL_COND_V_MSG(a < 0, Color(), "Invalid color code: " + p_color + ".");
+ float r, g, b, a = 1.0;
+ if (is_shorthand) {
+ r = _parse_col4(color, 0) / 15.0;
+ g = _parse_col4(color, 1) / 15.0;
+ b = _parse_col4(color, 2) / 15.0;
+ if (alpha) {
+ a = _parse_col4(color, 3) / 15.0;
+ }
+ } else {
+ r = _parse_col8(color, 0) / 255.0;
+ g = _parse_col8(color, 2) / 255.0;
+ b = _parse_col8(color, 4) / 255.0;
+ if (alpha) {
+ a = _parse_col8(color, 6) / 255.0;
+ }
}
+ ERR_FAIL_COND_V_MSG(r < 0, Color(), "Invalid color code: " + p_rgba + ".");
+ ERR_FAIL_COND_V_MSG(g < 0, Color(), "Invalid color code: " + p_rgba + ".");
+ ERR_FAIL_COND_V_MSG(b < 0, Color(), "Invalid color code: " + p_rgba + ".");
+ ERR_FAIL_COND_V_MSG(a < 0, Color(), "Invalid color code: " + p_rgba + ".");
- int from = alpha ? 2 : 0;
-
- int r = _parse_col(color, from + 0);
- ERR_FAIL_COND_V_MSG(r < 0, Color(), "Invalid color code: " + p_color + ".");
- int g = _parse_col(color, from + 2);
- ERR_FAIL_COND_V_MSG(g < 0, Color(), "Invalid color code: " + p_color + ".");
- int b = _parse_col(color, from + 4);
- ERR_FAIL_COND_V_MSG(b < 0, Color(), "Invalid color code: " + p_color + ".");
-
- return Color(r / 255.0, g / 255.0, b / 255.0, a / 255.0);
+ return Color(r, g, b, a);
}
bool Color::html_is_valid(const String &p_color) {
@@ -354,41 +347,22 @@ bool Color::html_is_valid(const String &p_color) {
return false;
}
if (color[0] == '#') {
- color = color.substr(1, color.length() - 1);
+ color = color.substr(1);
}
- bool alpha = false;
-
- if (color.length() == 8) {
- alpha = true;
- } else if (color.length() == 6) {
- alpha = false;
- } else {
+ // Check if the amount of hex digits is valid.
+ int len = color.length();
+ if (!(len == 3 || len == 4 || len == 6 || len == 8)) {
return false;
}
- if (alpha) {
- int a = _parse_col(color, 0);
- if (a < 0) {
+ // Check if each hex digit is valid.
+ for (int i = 0; i < len; i++) {
+ if (_parse_col4(color, i) == -1) {
return false;
}
}
- int from = alpha ? 2 : 0;
-
- int r = _parse_col(color, from + 0);
- if (r < 0) {
- return false;
- }
- int g = _parse_col(color, from + 2);
- if (g < 0) {
- return false;
- }
- int b = _parse_col(color, from + 4);
- if (b < 0) {
- return false;
- }
-
return true;
}
@@ -438,7 +412,7 @@ String Color::to_html(bool p_alpha) const {
txt += _to_hex(g);
txt += _to_hex(b);
if (p_alpha) {
- txt = _to_hex(a) + txt;
+ txt += _to_hex(a);
}
return txt;
}
diff --git a/core/color.h b/core/color.h
index 258965fd16..096e97e9ae 100644
--- a/core/color.h
+++ b/core/color.h
@@ -185,7 +185,7 @@ struct Color {
static Color hex(uint32_t p_hex);
static Color hex64(uint64_t p_hex);
- static Color html(const String &p_color);
+ static Color html(const String &p_rgba);
static bool html_is_valid(const String &p_color);
static Color named(const String &p_name);
String to_html(bool p_alpha = true) const;
diff --git a/core/compressed_translation.cpp b/core/compressed_translation.cpp
index a66997aa52..a92275565d 100644
--- a/core/compressed_translation.cpp
+++ b/core/compressed_translation.cpp
@@ -43,6 +43,8 @@ struct _PHashTranslationCmp {
};
void PHashTranslation::generate(const Ref<Translation> &p_from) {
+ // This method compresses a Translation instance.
+ // Right now it doesn't handle context or plurals, so Translation subclasses using plurals or context (i.e TranslationPO) shouldn't be compressed.
#ifdef TOOLS_ENABLED
List<StringName> keys;
p_from->get_message_list(&keys);
@@ -212,7 +214,9 @@ bool PHashTranslation::_get(const StringName &p_name, Variant &r_ret) const {
return true;
}
-StringName PHashTranslation::get_message(const StringName &p_src_text) const {
+StringName PHashTranslation::get_message(const StringName &p_src_text, const StringName &p_context) const {
+ // p_context passed in is ignore. The use of context is not yet supported in PHashTranslation.
+
int htsize = hash_table.size();
if (htsize == 0) {
@@ -267,6 +271,11 @@ StringName PHashTranslation::get_message(const StringName &p_src_text) const {
}
}
+StringName PHashTranslation::get_plural_message(const StringName &p_src_text, const StringName &p_plural_text, int p_n, const StringName &p_context) const {
+ // The use of plurals translation is not yet supported in PHashTranslation.
+ return get_message(p_src_text, p_context);
+}
+
void PHashTranslation::_get_property_list(List<PropertyInfo> *p_list) const {
p_list->push_back(PropertyInfo(Variant::PACKED_INT32_ARRAY, "hash_table"));
p_list->push_back(PropertyInfo(Variant::PACKED_INT32_ARRAY, "bucket_table"));
diff --git a/core/compressed_translation.h b/core/compressed_translation.h
index 3c029bdf58..c8b3cd2330 100644
--- a/core/compressed_translation.h
+++ b/core/compressed_translation.h
@@ -79,7 +79,8 @@ protected:
static void _bind_methods();
public:
- virtual StringName get_message(const StringName &p_src_text) const; //overridable for other implementations
+ virtual StringName get_message(const StringName &p_src_text, const StringName &p_context = "") const override; //overridable for other implementations
+ virtual StringName get_plural_message(const StringName &p_src_text, const StringName &p_plural_text, int p_n, const StringName &p_context = "") const override;
void generate(const Ref<Translation> &p_from);
PHashTranslation() {}
diff --git a/core/container_type_validate.h b/core/container_type_validate.h
index f2724e884d..8a361aa0ef 100644
--- a/core/container_type_validate.h
+++ b/core/container_type_validate.h
@@ -38,7 +38,7 @@ struct ContainerTypeValidate {
Variant::Type type = Variant::NIL;
StringName class_name;
Ref<Script> script;
- const char *where = "conatiner";
+ const char *where = "container";
_FORCE_INLINE_ bool can_reference(const ContainerTypeValidate &p_type) const {
if (type == p_type.type) {
diff --git a/core/core_builders.py b/core/core_builders.py
index d03874608e..004475faa7 100644
--- a/core/core_builders.py
+++ b/core/core_builders.py
@@ -117,14 +117,18 @@ def make_donors_header(target, source, env):
sections = [
"Platinum sponsors",
"Gold sponsors",
+ "Silver sponsors",
+ "Bronze sponsors",
"Mini sponsors",
"Gold donors",
"Silver donors",
"Bronze donors",
]
sections_id = [
- "DONORS_SPONSOR_PLAT",
+ "DONORS_SPONSOR_PLATINUM",
"DONORS_SPONSOR_GOLD",
+ "DONORS_SPONSOR_SILVER",
+ "DONORS_SPONSOR_BRONZE",
"DONORS_SPONSOR_MINI",
"DONORS_GOLD",
"DONORS_SILVER",
diff --git a/core/debugger/engine_debugger.cpp b/core/debugger/engine_debugger.cpp
index 5c9fb67de4..4bf31aa55f 100644
--- a/core/debugger/engine_debugger.cpp
+++ b/core/debugger/engine_debugger.cpp
@@ -169,7 +169,7 @@ void EngineDebugger::initialize(const String &p_uri, bool p_skip_breakpoints, Ve
for (int i = 0; i < p_breakpoints.size(); i++) {
String bp = p_breakpoints[i];
- int sp = bp.find_last(":");
+ int sp = bp.rfind(":");
ERR_CONTINUE_MSG(sp == -1, "Invalid breakpoint: '" + bp + "', expected file:line format.");
singleton_script_debugger->insert_breakpoint(bp.substr(sp + 1, bp.length()).to_int(), bp.substr(0, sp));
diff --git a/core/debugger/remote_debugger.cpp b/core/debugger/remote_debugger.cpp
index 62f600c5e5..9d55e1312e 100644
--- a/core/debugger/remote_debugger.cpp
+++ b/core/debugger/remote_debugger.cpp
@@ -373,6 +373,7 @@ struct RemoteDebugger::VisualProfiler {
struct RemoteDebugger::PerformanceProfiler {
Object *performance = nullptr;
int last_perf_time = 0;
+ uint64_t last_monitor_modification_time = 0;
void toggle(bool p_enable, const Array &p_opts) {}
void add(const Array &p_data) {}
@@ -386,12 +387,31 @@ struct RemoteDebugger::PerformanceProfiler {
return;
}
last_perf_time = pt;
+
+ Array custom_monitor_names = performance->call("get_custom_monitor_names");
+
+ uint64_t monitor_modification_time = performance->call("get_monitor_modification_time");
+ if (monitor_modification_time > last_monitor_modification_time) {
+ last_monitor_modification_time = monitor_modification_time;
+ EngineDebugger::get_singleton()->send_message("performance:profile_names", custom_monitor_names);
+ }
+
int max = performance->get("MONITOR_MAX");
Array arr;
- arr.resize(max);
+ arr.resize(max + custom_monitor_names.size());
for (int i = 0; i < max; i++) {
arr[i] = performance->call("get_monitor", i);
}
+
+ for (int i = 0; i < custom_monitor_names.size(); i++) {
+ Variant monitor_value = performance->call("get_custom_monitor", custom_monitor_names[i]);
+ if (!monitor_value.is_num()) {
+ ERR_PRINT("Value of custom monitor '" + String(custom_monitor_names[i]) + "' is not a number");
+ arr[i + max] = Variant();
+ }
+ arr[i + max] = monitor_value;
+ }
+
EngineDebugger::get_singleton()->send_message("performance:profile_frame", arr);
}
diff --git a/core/debugger/remote_debugger_peer.cpp b/core/debugger/remote_debugger_peer.cpp
index faa3a75fda..0ce0042f50 100644
--- a/core/debugger/remote_debugger_peer.cpp
+++ b/core/debugger/remote_debugger_peer.cpp
@@ -225,7 +225,7 @@ RemoteDebuggerPeer *RemoteDebuggerPeerTCP::create(const String &p_uri) {
uint16_t debug_port = 6007;
if (debug_host.find(":") != -1) {
- int sep_pos = debug_host.find_last(":");
+ int sep_pos = debug_host.rfind(":");
debug_port = debug_host.substr(sep_pos + 1).to_int();
debug_host = debug_host.substr(0, sep_pos);
}
diff --git a/core/engine.cpp b/core/engine.cpp
index c8bfffd020..d08cf92ecb 100644
--- a/core/engine.cpp
+++ b/core/engine.cpp
@@ -158,8 +158,10 @@ Array Engine::get_copyright_info() const {
Dictionary Engine::get_donor_info() const {
Dictionary donors;
- donors["platinum_sponsors"] = array_from_info(DONORS_SPONSOR_PLAT);
+ donors["platinum_sponsors"] = array_from_info(DONORS_SPONSOR_PLATINUM);
donors["gold_sponsors"] = array_from_info(DONORS_SPONSOR_GOLD);
+ donors["silver_sponsors"] = array_from_info(DONORS_SPONSOR_SILVER);
+ donors["bronze_sponsors"] = array_from_info(DONORS_SPONSOR_BRONZE);
donors["mini_sponsors"] = array_from_info(DONORS_SPONSOR_MINI);
donors["gold_donors"] = array_from_info(DONORS_GOLD);
donors["silver_donors"] = array_from_info(DONORS_SILVER);
diff --git a/core/error_macros.h b/core/error_macros.h
index d7366be453..6353961b04 100644
--- a/core/error_macros.h
+++ b/core/error_macros.h
@@ -476,7 +476,7 @@ void _err_print_index_error(const char *p_function, const char *p_file, int p_li
* The current function returns.
*/
#define ERR_FAIL() \
- if (1) { \
+ if (true) { \
_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Method/function failed."); \
return; \
} else \
@@ -489,7 +489,7 @@ void _err_print_index_error(const char *p_function, const char *p_file, int p_li
* Prints `m_msg`, and the current function returns.
*/
#define ERR_FAIL_MSG(m_msg) \
- if (1) { \
+ if (true) { \
_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Method/function failed.", DEBUG_STR(m_msg)); \
return; \
} else \
@@ -503,7 +503,7 @@ void _err_print_index_error(const char *p_function, const char *p_file, int p_li
* The current function returns `m_retval`.
*/
#define ERR_FAIL_V(m_retval) \
- if (1) { \
+ if (true) { \
_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Method/function failed. Returning: " _STR(m_retval)); \
return m_retval; \
} else \
@@ -516,7 +516,7 @@ void _err_print_index_error(const char *p_function, const char *p_file, int p_li
* Prints `m_msg`, and the current function returns `m_retval`.
*/
#define ERR_FAIL_V_MSG(m_retval, m_msg) \
- if (1) { \
+ if (true) { \
_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Method/function failed. Returning: " _STR(m_retval), DEBUG_STR(m_msg)); \
return m_retval; \
} else \
@@ -536,7 +536,7 @@ void _err_print_index_error(const char *p_function, const char *p_file, int p_li
* Prints `m_msg` once during the application lifetime.
*/
#define ERR_PRINT_ONCE(m_msg) \
- if (1) { \
+ if (true) { \
static bool first_print = true; \
if (first_print) { \
_err_print_error(FUNCTION_STR, __FILE__, __LINE__, m_msg); \
@@ -561,7 +561,7 @@ void _err_print_index_error(const char *p_function, const char *p_file, int p_li
* If warning about deprecated usage, use `WARN_DEPRECATED` or `WARN_DEPRECATED_MSG` instead.
*/
#define WARN_PRINT_ONCE(m_msg) \
- if (1) { \
+ if (true) { \
static bool first_print = true; \
if (first_print) { \
_err_print_error(FUNCTION_STR, __FILE__, __LINE__, m_msg, ERR_HANDLER_WARNING); \
@@ -576,7 +576,7 @@ void _err_print_index_error(const char *p_function, const char *p_file, int p_li
* Warns that the current function is deprecated.
*/
#define WARN_DEPRECATED \
- if (1) { \
+ if (true) { \
static volatile bool warning_shown = false; \
if (!warning_shown) { \
_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "This method has been deprecated and will be removed in the future.", ERR_HANDLER_WARNING); \
@@ -589,7 +589,7 @@ void _err_print_index_error(const char *p_function, const char *p_file, int p_li
* Warns that the current function is deprecated and prints `m_msg`.
*/
#define WARN_DEPRECATED_MSG(m_msg) \
- if (1) { \
+ if (true) { \
static volatile bool warning_shown = false; \
if (!warning_shown) { \
_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "This method has been deprecated and will be removed in the future.", DEBUG_STR(m_msg), ERR_HANDLER_WARNING); \
@@ -605,7 +605,7 @@ void _err_print_index_error(const char *p_function, const char *p_file, int p_li
* The application crashes.
*/
#define CRASH_NOW() \
- if (1) { \
+ if (true) { \
_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "FATAL: Method/function failed."); \
GENERATE_TRAP(); \
} else \
@@ -617,7 +617,7 @@ void _err_print_index_error(const char *p_function, const char *p_file, int p_li
* Prints `m_msg`, and then the application crashes.
*/
#define CRASH_NOW_MSG(m_msg) \
- if (1) { \
+ if (true) { \
_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "FATAL: Method/function failed.", DEBUG_STR(m_msg)); \
GENERATE_TRAP(); \
} else \
diff --git a/core/global_constants.cpp b/core/global_constants.cpp
index 6281e56395..b30685539a 100644
--- a/core/global_constants.cpp
+++ b/core/global_constants.cpp
@@ -38,6 +38,7 @@
struct _GlobalConstant {
#ifdef DEBUG_METHODS_ENABLED
StringName enum_name;
+ bool ignore_value_in_docs;
#endif
const char *name;
int value;
@@ -45,8 +46,9 @@ struct _GlobalConstant {
_GlobalConstant() {}
#ifdef DEBUG_METHODS_ENABLED
- _GlobalConstant(const StringName &p_enum_name, const char *p_name, int p_value) :
+ _GlobalConstant(const StringName &p_enum_name, const char *p_name, int p_value, bool p_ignore_value_in_docs = false) :
enum_name(p_enum_name),
+ ignore_value_in_docs(p_ignore_value_in_docs),
name(p_name),
value(p_value) {
}
@@ -71,6 +73,15 @@ static Vector<_GlobalConstant> _global_constants;
#define BIND_GLOBAL_ENUM_CONSTANT_CUSTOM(m_custom_name, m_constant) \
_global_constants.push_back(_GlobalConstant(__constant_get_enum_name(m_constant, #m_constant), m_custom_name, m_constant));
+#define BIND_GLOBAL_CONSTANT_NO_VAL(m_constant) \
+ _global_constants.push_back(_GlobalConstant(StringName(), #m_constant, m_constant, true));
+
+#define BIND_GLOBAL_ENUM_CONSTANT_NO_VAL(m_constant) \
+ _global_constants.push_back(_GlobalConstant(__constant_get_enum_name(m_constant, #m_constant), #m_constant, m_constant, true));
+
+#define BIND_GLOBAL_ENUM_CONSTANT_CUSTOM_NO_VAL(m_custom_name, m_constant) \
+ _global_constants.push_back(_GlobalConstant(__constant_get_enum_name(m_constant, #m_constant), m_custom_name, m_constant, true));
+
#else
#define BIND_GLOBAL_CONSTANT(m_constant) \
@@ -82,6 +93,15 @@ static Vector<_GlobalConstant> _global_constants;
#define BIND_GLOBAL_ENUM_CONSTANT_CUSTOM(m_custom_name, m_constant) \
_global_constants.push_back(_GlobalConstant(m_custom_name, m_constant));
+#define BIND_GLOBAL_CONSTANT_NO_VAL(m_constant) \
+ _global_constants.push_back(_GlobalConstant(#m_constant, m_constant));
+
+#define BIND_GLOBAL_ENUM_CONSTANT_NO_VAL(m_constant) \
+ _global_constants.push_back(_GlobalConstant(#m_constant, m_constant));
+
+#define BIND_GLOBAL_ENUM_CONSTANT_CUSTOM_NO_VAL(m_custom_name, m_constant) \
+ _global_constants.push_back(_GlobalConstant(m_custom_name, m_constant));
+
#endif
VARIANT_ENUM_CAST(KeyList);
@@ -368,7 +388,7 @@ void register_global_constants() {
BIND_GLOBAL_ENUM_CONSTANT(KEY_MASK_ALT);
BIND_GLOBAL_ENUM_CONSTANT(KEY_MASK_META);
BIND_GLOBAL_ENUM_CONSTANT(KEY_MASK_CTRL);
- BIND_GLOBAL_ENUM_CONSTANT(KEY_MASK_CMD);
+ BIND_GLOBAL_ENUM_CONSTANT_NO_VAL(KEY_MASK_CMD);
BIND_GLOBAL_ENUM_CONSTANT(KEY_MASK_KPAD);
BIND_GLOBAL_ENUM_CONSTANT(KEY_MASK_GROUP_SWITCH);
@@ -648,10 +668,18 @@ int GlobalConstants::get_global_constant_count() {
StringName GlobalConstants::get_global_constant_enum(int p_idx) {
return _global_constants[p_idx].enum_name;
}
+
+bool GlobalConstants::get_ignore_value_in_docs(int p_idx) {
+ return _global_constants[p_idx].ignore_value_in_docs;
+}
#else
StringName GlobalConstants::get_global_constant_enum(int p_idx) {
return StringName();
}
+
+bool GlobalConstants::get_ignore_value_in_docs(int p_idx) {
+ return false;
+}
#endif
const char *GlobalConstants::get_global_constant_name(int p_idx) {
diff --git a/core/global_constants.h b/core/global_constants.h
index a20b5ecd9a..989633a6fa 100644
--- a/core/global_constants.h
+++ b/core/global_constants.h
@@ -37,6 +37,7 @@ class GlobalConstants {
public:
static int get_global_constant_count();
static StringName get_global_constant_enum(int p_idx);
+ static bool get_ignore_value_in_docs(int p_idx);
static const char *get_global_constant_name(int p_idx);
static int get_global_constant_value(int p_idx);
};
diff --git a/core/hash_map.h b/core/hash_map.h
index 843430d082..10fc931e7a 100644
--- a/core/hash_map.h
+++ b/core/hash_map.h
@@ -280,13 +280,13 @@ public:
const TData &get(const TKey &p_key) const {
const TData *res = getptr(p_key);
- ERR_FAIL_COND_V(!res, *res);
+ CRASH_COND_MSG(!res, "Map key not found.");
return *res;
}
TData &get(const TKey &p_key) {
TData *res = getptr(p_key);
- ERR_FAIL_COND_V(!res, *res);
+ CRASH_COND_MSG(!res, "Map key not found.");
return *res;
}
diff --git a/core/image.cpp b/core/image.cpp
index 45f10625a9..e2f353698f 100644
--- a/core/image.cpp
+++ b/core/image.cpp
@@ -30,6 +30,7 @@
#include "image.h"
+#include "core/error_macros.h"
#include "core/hash_map.h"
#include "core/io/image_loader.h"
#include "core/io/resource_loader.h"
@@ -1894,8 +1895,10 @@ Vector<uint8_t> Image::get_data() const {
}
void Image::create(int p_width, int p_height, bool p_use_mipmaps, Format p_format) {
- ERR_FAIL_INDEX(p_width - 1, MAX_WIDTH);
- ERR_FAIL_INDEX(p_height - 1, MAX_HEIGHT);
+ ERR_FAIL_COND_MSG(p_width <= 0, "Image width must be greater than 0.");
+ ERR_FAIL_COND_MSG(p_height <= 0, "Image height must be greater than 0.");
+ ERR_FAIL_COND_MSG(p_width > MAX_WIDTH, "Image width cannot be greater than " + itos(MAX_WIDTH) + ".");
+ ERR_FAIL_COND_MSG(p_height > MAX_HEIGHT, "Image height cannot be greater than " + itos(MAX_HEIGHT) + ".");
ERR_FAIL_COND_MSG(p_width * p_height > MAX_PIXELS, "Too many pixels for image, maximum is " + itos(MAX_PIXELS));
int mm = 0;
@@ -1914,8 +1917,10 @@ void Image::create(int p_width, int p_height, bool p_use_mipmaps, Format p_forma
}
void Image::create(int p_width, int p_height, bool p_use_mipmaps, Format p_format, const Vector<uint8_t> &p_data) {
- ERR_FAIL_INDEX(p_width - 1, MAX_WIDTH);
- ERR_FAIL_INDEX(p_height - 1, MAX_HEIGHT);
+ ERR_FAIL_COND_MSG(p_width <= 0, "Image width must be greater than 0.");
+ ERR_FAIL_COND_MSG(p_height <= 0, "Image height must be greater than 0.");
+ ERR_FAIL_COND_MSG(p_width > MAX_WIDTH, "Image width cannot be greater than " + itos(MAX_WIDTH) + ".");
+ ERR_FAIL_COND_MSG(p_height > MAX_HEIGHT, "Image height cannot be greater than " + itos(MAX_HEIGHT) + ".");
ERR_FAIL_COND_MSG(p_width * p_height > MAX_PIXELS, "Too many pixels for image, maximum is " + itos(MAX_PIXELS));
int mm;
@@ -2629,6 +2634,7 @@ void Image::fill(const Color &c) {
ImageMemLoadFunc Image::_png_mem_loader_func = nullptr;
ImageMemLoadFunc Image::_jpg_mem_loader_func = nullptr;
ImageMemLoadFunc Image::_webp_mem_loader_func = nullptr;
+ImageMemLoadFunc Image::_tga_mem_loader_func = nullptr;
void (*Image::_image_compress_bc_func)(Image *, float, Image::UsedChannels) = nullptr;
void (*Image::_image_compress_bptc_func)(Image *, float, Image::UsedChannels) = nullptr;
@@ -3058,6 +3064,7 @@ void Image::_bind_methods() {
ClassDB::bind_method(D_METHOD("load_png_from_buffer", "buffer"), &Image::load_png_from_buffer);
ClassDB::bind_method(D_METHOD("load_jpg_from_buffer", "buffer"), &Image::load_jpg_from_buffer);
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);
ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE), "_set_data", "_get_data");
@@ -3389,6 +3396,11 @@ Error Image::load_webp_from_buffer(const Vector<uint8_t> &p_array) {
return _load_from_buffer(p_array, _webp_mem_loader_func);
}
+Error Image::load_tga_from_buffer(const Vector<uint8_t> &p_array) {
+ ERR_FAIL_NULL_V_MSG(_tga_mem_loader_func, ERR_UNAVAILABLE, "TGA module was not installed.");
+ return _load_from_buffer(p_array, _tga_mem_loader_func);
+}
+
void Image::convert_rg_to_ra_rgba8() {
ERR_FAIL_COND(format != FORMAT_RGBA8);
ERR_FAIL_COND(!data.size());
diff --git a/core/image.h b/core/image.h
index 53c203998e..d2572b072e 100644
--- a/core/image.h
+++ b/core/image.h
@@ -135,6 +135,7 @@ public:
static ImageMemLoadFunc _png_mem_loader_func;
static ImageMemLoadFunc _jpg_mem_loader_func;
static ImageMemLoadFunc _webp_mem_loader_func;
+ static ImageMemLoadFunc _tga_mem_loader_func;
static void (*_image_compress_bc_func)(Image *, float, UsedChannels p_channels);
static void (*_image_compress_bptc_func)(Image *, float p_lossy_quality, UsedChannels p_channels);
@@ -360,6 +361,7 @@ public:
Error load_png_from_buffer(const Vector<uint8_t> &p_array);
Error load_jpg_from_buffer(const Vector<uint8_t> &p_array);
Error load_webp_from_buffer(const Vector<uint8_t> &p_array);
+ Error load_tga_from_buffer(const Vector<uint8_t> &p_array);
void convert_rg_to_ra_rgba8();
void convert_ra_rgba8_to_rg();
@@ -367,7 +369,7 @@ public:
Image(const uint8_t *p_mem_png_jpg, int p_len = -1);
Image(const char **p_xpm);
- virtual Ref<Resource> duplicate(bool p_subresources = false) const;
+ virtual Ref<Resource> duplicate(bool p_subresources = false) const override;
UsedChannels detect_used_channels(CompressSource p_source = COMPRESS_SOURCE_GENERIC);
void optimize_channels();
diff --git a/core/input/SCsub b/core/input/SCsub
index c641819698..f40978911b 100644
--- a/core/input/SCsub
+++ b/core/input/SCsub
@@ -16,7 +16,7 @@ env.Depends("#core/input/default_controller_mappings.gen.cpp", controller_databa
env.CommandNoCache(
"#core/input/default_controller_mappings.gen.cpp",
controller_databases,
- run_in_subprocess(input_builders.make_default_controller_mappings),
+ env.Run(input_builders.make_default_controller_mappings, "Generating default controller mappings."),
)
env.add_source_files(env.core_sources, "*.cpp")
diff --git a/core/input/gamecontrollerdb.txt b/core/input/gamecontrollerdb.txt
index 7b5abdd61b..2a3cb23202 100644
--- a/core/input/gamecontrollerdb.txt
+++ b/core/input/gamecontrollerdb.txt
@@ -8,6 +8,7 @@
03000000c82d00001038000000000000,8BitDo F30 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:Windows,
03000000c82d00000090000000000000,8BitDo FC30 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:a3,righty:a4,start:b11,x:b4,y:b3,platform:Windows,
03000000c82d00000650000000000000,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:b6,righttrigger:b7,start:b11,x:b3,y:b4,platform:Windows,
+03000000c82d00005106000000000000,8BitDo M30 Gamepad,a:b1,b:b0,back:b10,guide:b2,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,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,
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,
03000000c82d00008010000000000000,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,
@@ -35,6 +36,7 @@
03000000c82d00000260000000000000,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,
03000000c82d00000261000000000000,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,
03000000c82d00000031000000000000,8BitDo Wireless Adapter,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,
+03000000c82d00001890000000000000,8BitDo Zero 2,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,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,
03000000a00500003232000000000000,8Bitdo Zero GamePad,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,
030000008f0e00001200000000000000,Acme GA-02,a:b0,b:b1,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:b2,y:b3,platform:Windows,
@@ -52,6 +54,7 @@
03000000d6200000e557000000000000,Batarang,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,
030000006f0e00003201000000000000,Battlefield 4 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,
+03000000d62000002a79000000000000,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:Windows,
03000000bc2000006012000000000000,Betop 2126F,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,
03000000bc2000000055000000000000,Betop BFM Gamepad,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,
03000000bc2000006312000000000000,Betop 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,
@@ -83,6 +86,8 @@
03000000120c0000f61c000000000000,Elite,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,
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 USB Gamepad 8206,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,
+030000006f0e00008001000000000000,Faceoff Wired Pro Controller for Nintendo Switch,a:b1,b:b2,x:b0,y:b3,back:b8,guide:b12,start:b9,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7,platform:Windows,
+030000006f0e00008401000000000000,Faceoff Deluxe+ Audio Wired Controller for Nintendo Switch,platform:Windows,a:b1,b:b2,x:b0,y:b3,back:b8,guide:b12,start:b9,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7,
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,
030000000d0f00008500000000000000,Fighting Commander 2016 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:Windows,
030000000d0f00008400000000000000,Fighting Commander 5,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,
@@ -98,8 +103,10 @@
03000000ac0500003d03000000000000,GameSir,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:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,
03000000ac0500004d04000000000000,GameSir,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:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,
03000000ffff00000000000000000000,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:Windows,
+03000000c01100000140000000000000,GameStop PS4 Fun 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,
030000006f0e00000102000000007801,GameStop Xbox 360 Wired Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,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,
030000009b2800003200000000000000,GC/N64 to USB v3.4,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,GC/N64 to USB v3.6,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,
030000008305000009a0000000000000,Genius,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,
030000008305000031b0000000000000,Genius Maxfire Blaze 3,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,
03000000451300000010000000000000,Genius Maxfire Grandias 12,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,
@@ -157,6 +164,7 @@
030000006d04000016c2000000000000,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:Windows,
030000006d04000018c2000000000000,Logitech F510 Gamepad,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,
030000006d04000019c2000000000000,Logitech F710 Gamepad,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,
+030000006d0400001ac2000000000000,Logitech Precision Gamepad,a:b1,b:b2,x:b0,y:b3,back:b8,start:b9,leftshoulder:b4,rightshoulder:b5,dpup:-a1,dpdown:+a1,dpleft:-a0,dpright:+a0,lefttrigger:b6,righttrigger:b7,platform:Windows,
03000000380700006652000000000000,Mad Catz C.T.R.L.R,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:a3,righty:a4,start:b9,x:b0,y:b3,platform:Windows,
03000000380700005032000000000000,Mad Catz FightPad PRO (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:Windows,
03000000380700005082000000000000,Mad Catz FightPad PRO (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,
@@ -183,8 +191,11 @@
03000000790000000018000000000000,Mayflash WiiU Pro Game Controller Adapter (DInput),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,
03000000790000002418000000000000,Mega Drive,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,rightshoulder:b2,start:b9,x:b3,y:b4,platform:Windows,
03000000380700006382000000000000,MLG GamePad 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,
+03000000c62400002a89000000000000,MOGA XP5-A Plus,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:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,
+03000000c62400002b89000000000000,MOGA XP5-A 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:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,
03000000efbe0000edfe000000000000,Monect Virtual 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:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b0,platform:Windows,
03000000250900006688000000000000,MP-8866 Super Dual Box,a:b2,b:b1,back:b9,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:b8,x:b3,y:b0,platform:Windows,
+030000006b140000010c000000000000,NACON GC-400ES,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:Windows,
030000001008000001e5000000000000,NEXT SNES Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b6,start:b9,x:b3,y:b0,platform:Windows,
03000000152000000182000000000000,NGDS,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:a3,righty:a4,start:b9,x:b3,y:b0,platform:Windows,
03000000bd12000015d0000000000000,Nintendo Retrolink USB Super SNES Classic Controller,a:b2,b:b1,back:b8,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,start:b9,x:b3,y:b0,platform:Windows,
@@ -197,6 +208,7 @@
030000006b14000001a1000000000000,Orange Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b2,y:b3,platform:Windows,
03000000362800000100000000000000,OUYA Game 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 Wired 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,
+030000006f0e00000901000000000000,PDP Versus Fighting Pad,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,
030000008f0e00000300000000000000,Piranha xtreme,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,
030000004c050000da0c000000000000,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: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,
@@ -232,8 +244,11 @@
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,
03000000321500000204000000000000,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: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,
+03000000321500000810000011010000,Razer Panthera Evo Arcade Stick for PS4,platform:Linux,a:b1,b:b2,x:b0,y:b3,back:b8,guide:b13,start:b9,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a5,lefttrigger:a3,righttrigger:a4,
03000000321500000507000000000000,Razer Raiju Mobile,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:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,
03000000321500000707000000000000,Razer Raiju Mobile,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:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,
+03000000321500000011000000000000,Razer Raion Fightpad for 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,
+03000000321500000009000000000000,Razer Serval,+lefty:+a2,-lefty:-a1,a:b0,b:b1,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,leftx:a0,rightshoulder:b5,rightstick:b9,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,
030000000d0f00001100000000000000,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,rightshoulder:b5,rightstick:b11,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,
030000000d0f00006a00000000000000,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:Windows,
030000000d0f00006b00000000000000,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:Windows,
@@ -248,6 +263,7 @@
0300000000f00000f100000000000000,RetroUSB.com Super RetroPort,a:b1,b:b5,back:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b3,x:b0,y:b4,platform:Windows,
030000006b140000010d000000000000,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:Windows,
030000006b140000020d000000000000,Revolution Pro Controller 2(1/2),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,
+030000006b140000130d000000000000,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: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,
@@ -275,16 +291,19 @@
03000000d11800000094000000000000,Stadia Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:b12,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:b11,rightx:a3,righty:a4,start:b9,x:b2,y:b3,platform:Windows,
03000000110100001914000000000000,SteelSeries,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftstick:b13,lefttrigger:b6,leftx:a0,lefty:a1,rightstick:b14,righttrigger:b7,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,
03000000381000001214000000000000,SteelSeries Free,a:b0,b:b1,back:b12,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:Windows,
+03000000110100003114000000000000,SteelSeries Stratus Duo,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:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,
03000000381000001814000000000000,SteelSeries Stratus XL,a:b0,b:b1,back:b18,dpdown:b13,dpleft:b14,dpright:b15,dpup:b12,guide:b19,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b2,y:b3,platform:Windows,
03000000790000001c18000000000000,STK-7024X,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:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,
03000000ff1100003133000000000000,SVEN X-PAD,a:b2,b:b3,back:b4,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:a4,start:b5,x:b0,y:b1,platform:Windows,
03000000d620000011a7000000000000,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:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,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,
030000004f04000007d0000000000000,T Mini Wireless,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,T.16000M,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,
03000000fa1900000706000000000000,Team 5,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,
03000000b50700001203000000000000,Techmobility X6-38V,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: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 3-in-1,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,
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,rightstick:b12,righttrigger:b7,rightx:a2,righty:a3,start:b10,x:b1,y:b3,platform:Windows,
030000004f04000004b3000000000000,Thrustmaster Firestorm Dual Power 3,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,
03000000666600000488000000000000,TigerGame PS/PS2 Game Controller Adapter,a:b2,b:b1,back:b9,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:a2,righty:a3,start:b8,x:b3,y:b0,platform:Windows,
@@ -306,6 +325,7 @@
03000000790000001a18000000000000,Venom,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,
03000000790000001b18000000000000,Venom Arcade Joystick,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,
030000006f0e00000302000000000000,Victrix Pro Fight Stick for PS4,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,
+030000006f0e00000702000000000000,Victrix Pro Fight Stick for 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:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,
0300000034120000adbe000000000000,vJoy Device,a:b0,b:b1,back:b15,dpdown:b6,dpleft:b7,dpright:b8,dpup:b5,guide:b16,leftshoulder:b9,leftstick:b13,lefttrigger:b11,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b14,righttrigger:b12,rightx:+a3,righty:+a4,start:b4,x:b2,y:b3,platform:Windows,
030000005e0400000a0b000000000000,Xbox Adaptive Controller,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,
03000000341a00000608000000000000,Xeox,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:Windows,
@@ -315,28 +335,39 @@
03000000786901006e70000000000000,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,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,
xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,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,
03000000790000004f18000000000000,ZD-T Android,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,
-030000009b2800006000000000000000,GC/N64 to USB v3.6,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,
+03000000120c0000101e000000000000,ZEROPLUS P4 Wired 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:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,
# Mac OS X
030000008f0e00000300000009010000,2In1 USB Joystick,+leftx:h0.2,+lefty:h0.4,-leftx:h0.8,-lefty:h0.1,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Mac OS X,
+03000000c82d00000090000001000000,8BitDo FC30 Pro,a:b1,b:b0,back:b10,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:a5,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X,
+03000000c82d00001038000000010000,8BitDo FC30 Pro,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: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 Gamepad,a:b1,b:b0,back:b10,guide:b2,leftshoulder:b6,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,start:b11,x:b4,y:b3,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,
+030000003512000012ab000001000000,8BitDo NES30 Gamepad,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,
03000000022000000090000001000000,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:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X,
03000000203800000900000000010000,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:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X,
03000000c82d00000190000001000000,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:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X,
03000000102800000900000000000000,8Bitdo SFC30 GamePad Joystick,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Mac OS X,
+03000000c82d00001290000001000000,8BitDo SN30 Gamepad,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Mac OS X,
+03000000c82d00000160000001000000,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: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,
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+,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+,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,
03000000c82d00000031000001000000,8BitDo Wireless Adapter,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:Mac OS X,
+03000000c82d00001890000001000000,8BitDo Zero 2,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Mac OS X,
03000000c82d00003032000000010000,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:a31,start:b11,x:b4,y:b3,platform:Mac OS X,
03000000a00500003232000008010000,8Bitdo Zero GamePad,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,
03000000a00500003232000009010000,8Bitdo Zero GamePad,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,
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,
+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,
030000008305000031b0000000000000,Cideko AK08b,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,
03000000260900008888000088020000,Cyber Gadget GameCube Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:a5,rightx:a2,righty:a3~,start:b7,x:b2,y:b3,platform:Mac OS X,
03000000a306000022f6000001030000,Cyborg V.3 Rumble 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:+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,
03000000ad1b000001f9000000000000,Gamestop BB-070 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,
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:Mac OS X,
+03000000c01100000140000000010000,GameStop PS4 Fun 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,
030000006f0e00000102000000000000,GameStop Xbox 360 Wired 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,
030000007d0400000540000001010000,Gravis Eliminator GamePad Pro,a:b1,b:b2,back:b8,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Mac OS X,
030000000d0f00002d00000000100000,Hori Fighting Commander 3 Pro,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,
@@ -378,11 +409,15 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
03000000d8140000cecf000000000000,MC Cthulhu,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,
030000005e0400002700000001010000,Microsoft SideWinder Plug & Play Game Pad,a:b0,b:b1,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,lefttrigger:b4,leftx:a0,lefty:a1,righttrigger:b5,x:b2,y:b3,platform:Mac OS X,
03000000d62000007162000001000000,Moga Pro 2 HID,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 XP5-A 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,
+03000000c62400002b89000000010000,MOGA XP5-A 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,
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,
030000001008000001e5000006010000,NEXT SNES Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b6,start:b9,x:b3,y:b0,platform:Mac OS X,
03000000d620000011a7000000020000,Nintendo Switch Core (Plus) Wired 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,
+03000000550900001472000025050000,NVIDIA Controller v01.04,a:b0,b:b1,back:b17,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b15,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:Mac OS X,
+030000006f0e00000901000002010000,PDP Versus Fighting Pad,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:Mac OS X,
030000008f0e00000300000000000000,Piranha xtreme,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:Mac OS X,
030000004c050000da0c000000010000,Playstation Classic 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: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,
@@ -397,12 +432,14 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
03000000321500000104000000010000,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:Mac OS X,
03000000321500000010000000010000,Razer RAIJU,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,
03000000321500000507000001010000,Razer Raiju Mobile,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,
+03000000321500000011000000010000,Razer Raion Fightpad for 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,
03000000321500000009000000020000,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:Mac OS X,
030000003215000000090000163a0000,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:Mac OS X,
0300000032150000030a000000000000,Razer Wildcat,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,
03000000790000001100000000000000,Retrolink Classic Controller,a:b2,b:b1,back:b8,leftshoulder:b4,leftx:a3,lefty:a4,rightshoulder:b5,start:b9,x:b3,y:b0,platform:Mac OS X,
03000000790000001100000006010000,Retrolink SNES Controller,a:b2,b:b1,back:b8,dpdown:+a4,dpleft:-a3,dpright:+a3,dpup:-a4,leftshoulder:b4,rightshoulder:b5,start:b9,x:b3,y:b0,platform:Mac OS X,
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,
03000000c6240000fefa000000000000,Rock Candy Gamepad for 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,
03000000730700000401000000010000,Sanwa PlayOnline Mobile,a:b0,b:b1,back:b2,leftx:a0,lefty:a1,start:b3,platform:Mac OS X,
03000000811700007e05000000000000,Sega Saturn,a:b2,b:b4,dpdown:b16,dpleft:b15,dpright:b14,dpup:b17,leftshoulder:b8,lefttrigger:a5,leftx:a0,lefty:a2,rightshoulder:b9,righttrigger:a4,start:b13,x:b0,y:b6,platform:Mac OS X,
@@ -418,18 +455,22 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
03000000381000002014000001000000,SteelSeries Nimbus,a:b0,b:b1,dpdown:b9,dpleft:b11,dpright:b10,dpup:b8,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1~,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3~,x:b2,y:b3,platform:Mac OS X,
03000000110100001714000000000000,SteelSeries Stratus XL,a:b0,b:b1,dpdown:b9,dpleft:b11,dpright:b10,dpup:b8,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1~,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3~,start:b12,x:b2,y:b3,platform:Mac OS X,
03000000110100001714000020010000,SteelSeries Stratus XL,a:b0,b:b1,dpdown:b9,dpleft:b11,dpright:b10,dpup:b8,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1~,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3~,start:b12,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,
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,
03000000bd12000015d0000000000000,Tomee SNES USB Controller,a:b2,b:b1,back:b8,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,start:b9,x:b3,y:b0,platform:Mac OS X,
03000000bd12000015d0000000010000,Tomee SNES USB 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:Mac OS X,
03000000100800000100000000000000,Twin USB Joystick,a:b4,b:b2,back:b16,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b12,leftstick:b20,lefttrigger:b8,leftx:a0,lefty:a2,rightshoulder:b14,rightstick:b22,righttrigger:b10,rightx:a6,righty:a4,start:b18,x:b6,y:b0,platform:Mac OS X,
030000006f0e00000302000025040000,Victrix Pro Fight Stick for PS4,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:Mac OS X,
+030000006f0e00000702000003060000,Victrix Pro Fight Stick for 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:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Mac OS X,
03000000791d00000103000009010000,Wii Classic Controller,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,
050000005769696d6f74652028303000,Wii Remote,a:b4,b:b5,back:b7,dpdown:b3,dpleft:b0,dpright:b1,dpup:b2,guide:b8,leftshoulder:b11,lefttrigger:b12,leftx:a0,lefty:a1,start:b6,x:b10,y:b9,platform:Mac OS X,
050000005769696d6f74652028313800,Wii U Pro Controller,a:b16,b:b15,back:b7,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b8,leftshoulder:b19,leftstick:b23,lefttrigger:b21,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b24,righttrigger:b22,rightx:a2,righty:a3,start:b6,x:b18,y:b17,platform:Mac OS X,
030000005e0400008e02000000000000,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,
03000000c6240000045d000000000000,Xbox 360 Wired 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,
030000005e0400000a0b000000000000,Xbox Adaptive 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,
+030000005e040000050b000003090000,Xbox Elite Wireless Controller Series 2,a:b0,b:b1,back:b31,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b53,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:Mac OS X,
030000005e040000d102000000000000,Xbox One Wired 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,
030000005e040000dd02000000000000,Xbox One Wired 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,
030000005e040000e302000000000000,Xbox One Wired 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,
@@ -439,8 +480,10 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
030000005e040000fd02000003090000,Xbox Wireless Controller,a:b0,b:b1,back:b16,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,
03000000172700004431000029010000,XiaoMi Game Controller,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:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a6,rightx:a2,righty:a5,start:b11,x:b3,y:b4,platform:Mac OS X,
03000000120c0000100e000000010000,ZEROPLUS P4 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: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,
+03000000120c0000101e000000010000,ZEROPLUS P4 Wired 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: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,
# Linux
+03000000c82d00000090000011010000,8BitDo FC30 Pro,a:b1,b:b0,back:b10,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:a5,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,
05000000c82d00001038000000010000,8Bitdo FC30 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,
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,
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,
@@ -465,8 +508,10 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
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,
03000000c82d00000260000011010000,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,
05000000c82d00000261000000010000,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,
+05000000202800000900000000010000,8BitDo SNES30 Gamepad,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,
030000005e0400008e02000020010000,8BitDo Wireless Adapter,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,
03000000c82d00000031000011010000,8BitDo Wireless Adapter,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,
+03000000c82d00001890000011010000,8BitDo Zero 2,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,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,
05000000a00500003232000001000000,8Bitdo Zero GamePad,a:b0,b:b1,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Linux,
05000000a00500003232000008010000,8Bitdo Zero GamePad,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,
@@ -475,10 +520,12 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
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 Wired 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,
03000000100000008200000011010000,Akishop Customs PS360+ v1.66,a:b1,b:b2,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux,
+030000007c1800000006000010010000,Alienware Dual Compatible Game Pad,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:a3,righty:a2,start:b9,x:b0,y:b3,platform:Linux,
05000000491900000204000021000000,Amazon Fire Game 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,
05000000050b00000045000031000000,ASUS Gamepad,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b6,leftshoulder:b4,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b10,x:b2,y:b3,platform:Linux,
05000000050b00000045000040000000,ASUS Gamepad,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b6,leftshoulder:b4,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b10,x:b2,y:b3,platform:Linux,
03000000120c00000500000010010000,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:Linux,
+03000000d62000002a79000011010000,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:Linux,
03000000666600006706000000010000,boom PSX to PC Converter,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,
03000000ffff0000ffff000000010000,Chinese-made 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,
03000000e82000006058000001010000,Cideko AK08b,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:Linux,
@@ -513,22 +560,27 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
030000000d0f00006a00000011010000,HORI CO. LTD. 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 CO. LTD. 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,
030000000d0f00002200000011010000,HORI CO. LTD. REAL ARCADE Pro.V3,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,
+030000000d0f00008500000010010000,HORI Fighting Commander,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,
+030000000d0f00008600000002010000,Hori Fighting Commander,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,
030000000d0f00005f00000011010000,Hori Fighting Commander 4 (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,
030000000d0f00005e00000011010000,Hori Fighting Commander 4 (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,
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,
030000000d0f00009200000011010000,Hori Pokken Tournament DX Pro Pad,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,
030000000d0f00001600000000010000,Hori Real Arcade Pro.EX-SE (Xbox 360),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,
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 mini4,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,
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,
030000008f0e00001330000010010000,HuiJia SNES Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b9,x:b3,y:b0,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,
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,
050000006964726f69643a636f6e0000,idroid:con,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 PCU#0 Gamepad Interface,a:b1,b:b0,back:b4,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,start:b5,x:b3,y:b2,platform:Linux,
03000000fd0500000030000000010000,InterAct GoPad I-73000 (Fighting Game Layout),a:b3,b:b4,back:b6,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:b5,start:b7,x:b0,y:b1,platform:Linux,
0500000049190000020400001b010000,Ipega PG-9069 - Bluetooth Gamepad,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b161,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,
+03000000632500007505000011010000,Ipega PG-9099 - Bluetooth Gamepad,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:Linux,
030000006e0500000320000010010000,JC-U3613M - DirectInput Mode,a:b2,b:b3,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a2,righty:a3,start:b11,x:b0,y:b1,platform:Linux,
03000000300f00001001000010010000,Jess Tech Dual Analog Rumble Pad,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,
03000000300f00000b01000010010000,Jess Tech GGE909 PC Recoil Pad,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:a3,righty:a2,start:b9,x:b3,y:b0,platform:Linux,
@@ -567,7 +619,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
03000000380700003888000010010000,MadCatz PC USB Wired Stick 8838,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:a0,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,
03000000120c00000500000000010000,Manta Dualshock 2,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:b2,y:b3,platform:Linux,
03000000790000004418000010010000,Mayflash GameCube Controller,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,
-03000000790000004318000010010000,Mayflash GameCube Controller Adapter,a:b1,b:b0,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:b2,y:b3,platform:Linux,
+03000000790000004318000010010000,Mayflash GameCube Controller Adapter,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:Linux,
03000000242f00007300000011010000,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:a2,righty:a3,start:b11,x:b0,y:b3,platform:Linux,
0300000079000000d218000011010000,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:Linux,
03000000d620000010a7000011010000,Mayflash Magic NS,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,
@@ -588,14 +640,17 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
05000000d6200000e589000001000000,Moga 2 HID,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:Linux,
05000000d6200000ad0d000001000000,Moga Pro,a:b0,b:b1,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:Linux,
05000000d62000007162000001000000,Moga Pro 2 HID,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:Linux,
+03000000c62400002b89000011010000,MOGA XP5-A 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:Linux,
+05000000c62400002a89000000010000,MOGA XP5-A Plus,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b22,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,
03000000250900006688000000010000,MP-8866 Super Dual Box,a:b2,b:b1,back:b9,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:b8,x:b3,y:b0,platform:Linux,
+030000006b140000010c000010010000,NACON GC-400ES,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,
030000000d0f00000900000010010000,Natec Genesis P44,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,
030000001008000001e5000010010000,NEXT SNES Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b6,start:b9,x:b3,y:b0,platform:Linux,
060000007e0500000820000000000000,Nintendo Combined Joy-Cons (joycond),a:b0,b:b1,back:b9,dpdown:b15,dpleft:b16,dpright:b17,dpup:b14,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,
-050000007e0500000920000001800000,Nintendo Switch Pro Controller (joycond),a:b0,b:b1,x:b3,y:b2,back:b9,guide:b11,start:b10,leftstick:b12,rightstick:b13,leftshoulder:b5,rightshoulder:b6,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b7,righttrigger:b8,platform:Linux,
-030000007e0500000920000011810000,Nintendo Switch Pro Controller Wired (joycond),a:b0,b:b1,x:b3,y:b2,back:b9,guide:b11,start:b10,leftstick:b12,rightstick:b13,leftshoulder:b5,rightshoulder:b6,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b7,righttrigger:b8,platform:Linux,
030000007e0500003703000000016800,Nintendo GameCube Controller,a:b0,b:b2,dpdown:b6,dpleft:b4,dpright:b5,dpup:b7,lefttrigger:a4,leftx:a0,lefty:a1~,rightshoulder:b9,righttrigger:a5,rightx:a2,righty:a3~,start:b8,x:b1,y:b3,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,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,
+050000007e0500000920000001800000,Nintendo Switch Pro Controller (joycond),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,
+030000007e0500000920000011810000,Nintendo Switch Pro Controller Wired (joycond),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,
050000007e0500003003000001000000,Nintendo Wii Remote 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,
05000000010000000100000003000000,Nintendo Wiimote,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,
030000000d0500000308000010010000,Nostromo n45 Dual Analog Gamepad,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,
@@ -603,19 +658,29 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
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,
03000000451300000830000010010000,NYKO CORE,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,
+19000000010000000100000001010000,odroidgo2_joypad,a:b1,b:b0,dpdown:b7,dpleft:b8,dpright:b9,dpup:b6,guide:b10,leftshoulder:b4,leftstick:b12,lefttrigger:b11,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b13,righttrigger:b14,start:b15,x:b2,y:b3,platform:Linux,
+19000000010000000200000011000000,odroidgo2_joypad_v11,a:b1,b:b0,dpdown:b9,dpleft:b10,dpright:b11,dpup:b8,guide:b12,leftshoulder:b4,leftstick:b14,lefttrigger:b13,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b15,righttrigger:b16,start:b17,x:b2,y:b3,platform:Linux,
030000005e0400000202000000010000,Old Xbox pad,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,
05000000362800000100000002010000,OUYA Game 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:a5,rightx:a3,righty:a4,x:b1,y:b2,platform:Linux,
05000000362800000100000003010000,OUYA Game 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:a5,rightx:a3,righty:a4,x:b1,y:b2,platform:Linux,
03000000830500005020000010010000,Padix Co. Ltd. Rockfire PSX/USB Bridge,a:b0,b:b1,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:a2,righty:a3,start:b11,x:b2,y:b3,platform:Linux,
+03000000790000001c18000011010000,PC Game 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:Linux,
03000000ff1100003133000010010000,PC Game 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:Linux,
+030000006f0e0000b802000001010000,PDP AFTERGLOW Wired Xbox One Controller,a:b0,b:b1,x:b2,y:b3,back:b6,guide:b8,start:b7,leftstick:b9,rightstick:b10,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a3,righty:a4,lefttrigger:a2,righttrigger:a5,platform:Linux,
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,
+030000006f0e00008001000011010000,PDP CO. LTD. Faceoff Wired Pro Controller for Nintendo Switch,a:b1,b:b2,x:b0,y:b3,back:b8,guide:b12,start:b9,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7,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,
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,
+030000006f0e00008701000011010000,PDP Rock Candy Wired Controller for Nintendo Switch,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,
+030000006f0e00000901000011010000,PDP Versus Fighting Pad,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,
030000006f0e0000a802000023020000,PDP Wired Controller for Xbox One,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,
+030000006f0e00008501000011010000,PDP Wired Fight Pad 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,
+0500000049190000030400001b010000,PG-9099,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,
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,
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,
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,
+03000000c62400001a58000001010000,PowerA Xbox One Cabled,a:b0,b:b1,y:b3,x:b2,start:b7,guide:b8,back:b6,leftstick:b9,rightstick:b10,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a3,righty:a4,lefttrigger:a2,righttrigger:a5,platform:Linux,
030000006d040000d2ca000011010000,Precision 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,
03000000ff1100004133000010010000,PS2 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,
03000000341a00003608000011010000,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,
@@ -640,18 +705,21 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
03000000c01100000140000011010000,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:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,
050000004c050000c405000000010000,PS4 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,
050000004c050000c405000000810000,PS4 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:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux,
+050000004c050000c405000001800000,PS4 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:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux,
050000004c050000cc09000000010000,PS4 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,
050000004c050000cc09000000810000,PS4 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:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux,
050000004c050000cc09000001800000,PS4 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:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux,
03000000300f00001211000011010000,QanBa Arcade JoyStick,a:b2,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b5,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b6,start:b9,x:b1,y:b3,platform:Linux,
030000009b2800003200000001010000,Raphnet Technologies GC/N64 to USB v3.4,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 Technologies GC/N64 to USB v3.6,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,
030000009b2800000300000001010000,raphnet.net 4nes4snes v1.5,a:b0,b:b4,back:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b3,x:b1,y:b5,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,
-030000008916000000fd000024010000,Razer Onza Tournament 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,
+030000008916000000fd000024010000,Razer Onza Tournament Edition,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,
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,
03000000321500000010000011010000,Razer RAIJU,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,
03000000321500000507000000010000,Razer Raiju Mobile,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:Linux,
+03000000321500000011000011010000,Razer Raion Fightpad for 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,
030000008916000000fe000024010000,Razer Sabertooth,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,
03000000c6240000045d000024010000,Razer Sabertooth,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,
03000000c6240000045d000025010000,Razer Sabertooth,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,
@@ -662,11 +730,11 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
0300000081170000990a000001010000,Retronic Adapter,a:b0,leftx:a0,lefty:a1,platform:Linux,
0300000000f000000300000000010000,RetroPad,a:b1,b:b5,back:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b3,x:b0,y:b4,platform:Linux,
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,
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,
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 V.1 Game 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,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a4,start:b9,x:b0,y:b3,platform:Linux,
-03000000a30600001005000000010000,Saitek Saitek P150,platform:Linux,a:b0,b:b1,y:b4,x:b3,leftshoulder:b7,rightshoulder:b2,dpup:-a1,dpleft:-a0,dpdown:+a1,dpright:+a0,lefttrigger:b6,righttrigger:b5,
03000000a30600000cff000010010000,Saitek P2500 Force Rumble Pad,a:b2,b:b3,back:b11,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a3,righty:a2,x:b0,y:b1,platform:Linux,
03000000a30600000c04000011010000,Saitek P2900 Wireless Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b9,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b12,x:b0,y:b3,platform:Linux,
03000000300f00001201000010010000,Saitek P380,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,
@@ -674,6 +742,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
03000000a30600000b04000000010000,Saitek P990 Dual Analog Pad,a:b1,b:b2,back:b9,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:b8,x:b0,y:b3,platform:Linux,
03000000a306000018f5000010010000,Saitek PLC Saitek P3200 Rumble Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a4,start:b9,x:b0,y:b3,platform:Linux,
03000000a306000020f6000011010000,Saitek PS2700 Rumble 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,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a4,start:b9,x:b0,y:b3,platform:Linux,
+03000000a30600001005000000010000,Saitek Saitek P150,a:b0,b:b1,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b7,lefttrigger:b6,rightshoulder:b2,righttrigger:b5,x:b3,y:b4,platform:Linux,
03000000d81d00000e00000010010000,Savior,a:b0,b:b1,back:b8,leftshoulder:b6,leftstick:b10,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b2,rightstick:b11,righttrigger:b3,start:b9,x:b4,y:b5,platform:Linux,
03000000c01600008704000011010000,Serial/Keyboard/Mouse/Joystick,a:b12,b:b10,back:b4,dpdown:b2,dpleft:b3,dpright:b1,dpup:b0,leftshoulder:b9,leftstick:b14,lefttrigger:b6,leftx:a1,lefty:a0,rightshoulder:b8,rightstick:b15,righttrigger:b7,rightx:a2,righty:a3,start:b5,x:b13,y:b11,platform:Linux,
03000000f025000021c1000010010000,ShanWan Gioteck PS3 Wired 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:Linux,
@@ -687,7 +756,9 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
03000000d11800000094000011010000,Stadia 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:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Linux,
03000000de2800000112000001000000,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:Linux,
03000000de2800000211000001000000,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:Linux,
+03000000de2800000211000011010000,Steam Controller,a:b2,b:b3,back:b10,dpdown:b18,dpleft:b19,dpright:b20,dpup:b17,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b5,platform:Linux,
03000000de2800004211000001000000,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:Linux,
+03000000de2800004211000011010000,Steam Controller,a:b2,b:b3,back:b10,dpdown:b18,dpleft:b19,dpright:b20,dpup:b17,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b5,platform:Linux,
03000000de280000fc11000001000000,Steam 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,
05000000de2800000212000001000000,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:Linux,
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:Linux,
@@ -695,9 +766,10 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
03000000de280000ff11000001000000,Steam Virtual Gamepad,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,
03000000381000003014000075010000,SteelSeries Stratus Duo,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,
03000000381000003114000075010000,SteelSeries Stratus Duo,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,
+0500000011010000311400001b010000,SteelSeries Stratus Duo,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b32,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,
05000000110100001914000009010000,SteelSeries Stratus XL,a:b0,b:b1,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,
03000000ad1b000038f0000090040000,Street Fighter IV FightStick TE,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
-030000003b07000004a1000000010000,Suncom SFX Plus for USB,a:b0,b:b2,x:b1,y:b3,back:b7,start:b8,leftshoulder:b6,rightshoulder:b9,leftx:a0,lefty:a1,lefttrigger:b4,righttrigger:b5,platform:Linux,
+030000003b07000004a1000000010000,Suncom SFX Plus for USB,a:b0,b:b2,back:b7,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b9,righttrigger:b5,start:b8,x:b1,y:b3,platform:Linux,
03000000666600000488000000010000,Super Joy Box 5 Pro,a:b2,b:b1,back:b9,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:a2,righty:a3,start:b8,x:b3,y:b0,platform:Linux,
0300000000f00000f100000000010000,Super RetroPort,a:b1,b:b5,back:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b3,x:b0,y:b4,platform:Linux,
03000000457500002211000010010000,SZMY-POWER CO. LTD. 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,
@@ -705,7 +777,8 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
030000004f04000020b3000010010000,Thrustmaster 2 in 1 DT,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,
030000004f04000023b3000000010000,Thrustmaster Dual Trigger 3-in-1,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:Linux,
-03000000b50700000399000000010000,Thrustmaster Firestorm Digital 2,a:b2,b:b4,x:b3,y:b5,back:b11,start:b1,leftstick:b10,rightstick:b0,leftshoulder:b6,rightshoulder:b8,leftx:a0,lefty:a1,lefttrigger:b7,righttrigger:b9,platform:Linux,
+030000004f0400000ed0000011010000,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:Linux,
+03000000b50700000399000000010000,Thrustmaster Firestorm Digital 2,a:b2,b:b4,back:b11,leftshoulder:b6,leftstick:b10,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b8,rightstick:b0,righttrigger:b9,start:b1,x:b3,y:b5,platform:Linux,
030000004f04000000b3000010010000,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,rightstick:b12,righttrigger:b7,rightx:a2,righty:a3,start:b10,x:b1,y:b3,platform:Linux,
030000004f04000026b3000002040000,Thrustmaster Gamepad GP XID,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,
03000000c6240000025b000002020000,Thrustmaster GPX Gamepad,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,
@@ -722,8 +795,10 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
03000000790000000600000007010000,USB gamepad,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:a3,righty:a4,start:b9,x:b3,y:b0,platform:Linux,
03000000790000001100000000010000,USB Gamepad1,a:b2,b:b1,back:b8,dpdown:a0,dpleft:a1,dpright:a2,dpup:a4,start:b9,platform:Linux,
030000006f0e00000302000011010000,Victrix Pro Fight Stick for PS4,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,
+030000006f0e00000702000011010000,Victrix Pro Fight Stick for 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:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux,
05000000ac0500003232000001000000,VR-BOX,a:b0,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:b2,y:b3,platform:Linux,
03000000791d00000103000010010000,Wii Classic Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,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:Linux,
+050000000d0f0000f600000001000000,Wireless 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,
030000005e0400008e02000010010000,X360 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,
030000005e0400008e02000014010000,X360 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,
030000005e0400001907000000010000,X360 Wireless 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,
@@ -735,44 +810,70 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
0000000058626f782047616d65706100,Xbox Gamepad (userspace driver),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,
030000005e040000d102000002010000,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,
050000005e040000fd02000030110000,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,
+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,
030000005e040000ea02000000000000,Xbox One 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,
050000005e040000e002000003090000,Xbox One Wireless Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,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:Linux,
050000005e040000fd02000003090000,Xbox One Wireless Controller,a:b0,b:b1,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b16,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,
030000005e040000ea02000001030000,Xbox One Wireless Controller (Model 1708),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,xbox360 Wireless 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,
03000000450c00002043000010010000,XEOX Gamepad SL-6556-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,
03000000ac0500005b05000010010000,Xiaoji 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,
05000000172700004431000029010000,XiaoMi Game 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,Xin-Mo Xin-Mo 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,
xinput,XInput 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,
03000000120c0000100e000011010000,ZEROPLUS P4 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:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,
-030000005e0400008e02000000010000,xbox360 Wireless EasySMX,a:b0,b:b1,x:b2,y:b3,back:b6,guide:b8,start:b7,leftstick:b9,rightstick:b10,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a3,righty:a4,lefttrigger:a2,righttrigger:a5,platform:Linux,
-030000009b2800006000000001010000,Raphnet Technologies GC/N64 to USB v3.6,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,
+03000000120c0000101e000011010000,ZEROPLUS P4 Wired 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:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,
# Android
+05000000c82d000006500000ffff3f00,8BitDo M30 Gamepad,a:b1,b:b0,back:b4,guide:b17,leftshoulder:b9,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b10,righttrigger:a4,start:b6,x:b3,y:b2,platform:Android,
+05000000c82d000051060000ffff3f00,8BitDo M30 Gamepad,a:b1,b:b0,back:b4,guide:b17,leftshoulder:b9,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,righttrigger:a5,start:b6,x:b3,y:b2,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,
+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,
+05000000c82d000000600000ffff3f00,8BitDo SF30 Pro,a:b1,b:b0,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:b15,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b16,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android,
+05000000c82d000000610000ffff3f00,8BitDo SF30 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,
+05000000c82d000012900000ffff3f00,8BitDo SN30 Gamepad,a:b1,b:b0,back:b4,leftshoulder:b9,leftx:a0,lefty:a1,rightshoulder:b10,start:b6,x:b3,y:b2,platform:Android,
+05000000c82d000062280000ffff3f00,8BitDo SN30 Gamepad,a:b1,b:b0,back:b4,leftshoulder:b9,leftx:a0,lefty:a1,rightshoulder:b10,start:b6,x:b3,y:b2,platform:Android,
+05000000c82d000001600000ffff3f00,8BitDo SN30 Pro,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,
+05000000c82d000002600000ffff0f00,8BitDo SN30 Pro+,a:b1,b:b0,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b17,leftshoulder:b9,leftstick:b7,lefttrigger:b15,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b16,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android,
+050000002028000009000000ffff3f00,8BitDo SNES30 Gamepad,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,
+050000003512000020ab000000780f00,8BitDo SNES30 Gamepad,a:b21,b:b20,back:b30,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b26,rightshoulder:b27,start:b31,x:b24,y:b23,platform:Android,
+05000000c82d000018900000ffff0f00,8BitDo Zero 2,a:b1,b:b0,back:b4,leftshoulder:b9,leftx:a0,lefty:a1,rightshoulder:b10,start:b6,x:b3,y:b2,platform:Android,
+05000000c82d000030320000ffff0f00,8BitDo Zero 2,a:b1,b:b0,back:b4,leftshoulder:b9,leftx:a0,lefty:a1,rightshoulder:b10,start:b6,x:b3,y:b2,platform:Android,
05000000bc20000000550000ffff3f00,GameSir G3w,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:a4,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,
+0500000031366332860c44aadfff0f00,GS Gamepad,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:b15,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b16,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android,
+0500000083050000602000000ffe0000,iBuffalo SNES Controller,a:b1,b:b0,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b15,rightshoulder:b16,start:b10,x:b3,y:b2,platform:Android,
64633436313965656664373634323364,Microsoft X-Box 360 pad,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,x:b2,y:b3,platform:Android,
+7573622067616d657061642020202020,NEXT SNES Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b6,start:b9,x:b3,y:b0,platform:Android,
050000007e05000009200000ffff0f00,Nintendo Switch Pro Controller,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b3,leftstick:b4,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b6,righttrigger:b10,rightx:a2,righty:a3,start:b16,x:b17,y:b2,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,
050000005509000003720000cf7f3f00,NVIDIA Controller v01.01,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 v01.03,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 v01.04,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,
050000004c05000068020000dfff3f00,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,
+030000004c050000cc09000000006800,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,
050000004c050000cc090000fffe3f00,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,
+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,
35643031303033326130316330353564,PS4 Controller,a:b1,b:b17,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,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,
+62653861643333663663383332396665,Razer Kishi,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:a5,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android,
050000003215000005070000ffff3f00,Razer Raiju Mobile,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,
050000003215000007070000ffff3f00,Razer Raiju Mobile,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,
050000003215000000090000bf7f3f00,Razer Serval,a:b0,b:b1,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,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,
05000000de2800000611000001000000,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,
+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 USB 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,
+30306539356238653637313730656134,Wireless 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,
050000005e040000e00200000ffe3f00,Xbox One Wireless 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,
050000005e040000fd020000ffff3f00,Xbox One Wireless 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:a4,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android,
050000005e04000091020000ff073f00,Xbox Wireless 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,
34356136633366613530316338376136,Xbox Wireless 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,
-7573622067616d657061642020202020,NEXT SNES Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b6,start:b9,x:b3,y:b0,platform:Android,
+050000001727000044310000ffff3f00,XiaoMi Game 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,
# iOS
05000000ac0500000100000000006d01,*,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b6,leftshoulder:b4,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a5,rightx:a3,righty:a4,x:b2,y:b3,platform:iOS,
diff --git a/core/input/input.h b/core/input/input.h
index 91e3b83b95..775663503b 100644
--- a/core/input/input.h
+++ b/core/input/input.h
@@ -255,7 +255,7 @@ protected:
public:
void set_mouse_mode(MouseMode p_mode);
MouseMode get_mouse_mode() const;
- void get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const;
+ void get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const override;
static Input *get_singleton();
diff --git a/core/input/input_event.h b/core/input/input_event.h
index dd1cc11982..815ba5ae80 100644
--- a/core/input/input_event.h
+++ b/core/input/input_event.h
@@ -266,7 +266,7 @@ protected:
public:
void set_pressed(bool p_pressed);
- virtual bool is_pressed() const;
+ virtual bool is_pressed() const override;
void set_keycode(uint32_t p_keycode);
uint32_t get_keycode() const;
@@ -278,17 +278,17 @@ public:
uint32_t get_unicode() const;
void set_echo(bool p_enable);
- virtual bool is_echo() const;
+ virtual bool is_echo() const override;
uint32_t get_keycode_with_modifiers() const;
uint32_t get_physical_keycode_with_modifiers() const;
- virtual bool action_match(const Ref<InputEvent> &p_event, bool *p_pressed, float *p_strength, float p_deadzone) const;
- virtual bool shortcut_match(const Ref<InputEvent> &p_event) const;
+ virtual bool action_match(const Ref<InputEvent> &p_event, bool *p_pressed, float *p_strength, float p_deadzone) const override;
+ virtual bool shortcut_match(const Ref<InputEvent> &p_event) const override;
- virtual bool is_action_type() const { return true; }
+ virtual bool is_action_type() const override { return true; }
- virtual String as_text() const;
+ virtual String as_text() const override;
InputEventKey() {}
};
@@ -336,16 +336,16 @@ public:
int get_button_index() const;
void set_pressed(bool p_pressed);
- virtual bool is_pressed() const;
+ virtual bool is_pressed() const override;
void set_doubleclick(bool p_doubleclick);
bool is_doubleclick() const;
- virtual Ref<InputEvent> xformed_by(const Transform2D &p_xform, const Vector2 &p_local_ofs = Vector2()) const;
- virtual bool action_match(const Ref<InputEvent> &p_event, bool *p_pressed, float *p_strength, float p_deadzone) const;
+ virtual Ref<InputEvent> xformed_by(const Transform2D &p_xform, const Vector2 &p_local_ofs = Vector2()) const override;
+ virtual bool action_match(const Ref<InputEvent> &p_event, bool *p_pressed, float *p_strength, float p_deadzone) const override;
- virtual bool is_action_type() const { return true; }
- virtual String as_text() const;
+ virtual bool is_action_type() const override { return true; }
+ virtual String as_text() const override;
InputEventMouseButton() {}
};
@@ -374,10 +374,10 @@ public:
void set_speed(const Vector2 &p_speed);
Vector2 get_speed() const;
- virtual Ref<InputEvent> xformed_by(const Transform2D &p_xform, const Vector2 &p_local_ofs = Vector2()) const;
- virtual String as_text() const;
+ virtual Ref<InputEvent> xformed_by(const Transform2D &p_xform, const Vector2 &p_local_ofs = Vector2()) const override;
+ virtual String as_text() const override;
- virtual bool accumulate(const Ref<InputEvent> &p_event);
+ virtual bool accumulate(const Ref<InputEvent> &p_event) override;
InputEventMouseMotion() {}
};
@@ -397,12 +397,12 @@ public:
void set_axis_value(float p_value);
float get_axis_value() const;
- virtual bool is_pressed() const;
+ virtual bool is_pressed() const override;
- virtual bool action_match(const Ref<InputEvent> &p_event, bool *p_pressed, float *p_strength, float p_deadzone) const;
+ virtual bool action_match(const Ref<InputEvent> &p_event, bool *p_pressed, float *p_strength, float p_deadzone) const override;
- virtual bool is_action_type() const { return true; }
- virtual String as_text() const;
+ virtual bool is_action_type() const override { return true; }
+ virtual String as_text() const override;
InputEventJoypadMotion() {}
};
@@ -421,16 +421,16 @@ public:
int get_button_index() const;
void set_pressed(bool p_pressed);
- virtual bool is_pressed() const;
+ virtual bool is_pressed() const override;
void set_pressure(float p_pressure);
float get_pressure() const;
- virtual bool action_match(const Ref<InputEvent> &p_event, bool *p_pressed, float *p_strength, float p_deadzone) const;
- virtual bool shortcut_match(const Ref<InputEvent> &p_event) const;
+ virtual bool action_match(const Ref<InputEvent> &p_event, bool *p_pressed, float *p_strength, float p_deadzone) const override;
+ virtual bool shortcut_match(const Ref<InputEvent> &p_event) const override;
- virtual bool is_action_type() const { return true; }
- virtual String as_text() const;
+ virtual bool is_action_type() const override { return true; }
+ virtual String as_text() const override;
InputEventJoypadButton() {}
};
@@ -452,10 +452,10 @@ public:
Vector2 get_position() const;
void set_pressed(bool p_pressed);
- virtual bool is_pressed() const;
+ virtual bool is_pressed() const override;
- virtual Ref<InputEvent> xformed_by(const Transform2D &p_xform, const Vector2 &p_local_ofs = Vector2()) const;
- virtual String as_text() const;
+ virtual Ref<InputEvent> xformed_by(const Transform2D &p_xform, const Vector2 &p_local_ofs = Vector2()) const override;
+ virtual String as_text() const override;
InputEventScreenTouch() {}
};
@@ -483,8 +483,8 @@ public:
void set_speed(const Vector2 &p_speed);
Vector2 get_speed() const;
- virtual Ref<InputEvent> xformed_by(const Transform2D &p_xform, const Vector2 &p_local_ofs = Vector2()) const;
- virtual String as_text() const;
+ virtual Ref<InputEvent> xformed_by(const Transform2D &p_xform, const Vector2 &p_local_ofs = Vector2()) const override;
+ virtual String as_text() const override;
InputEventScreenDrag() {}
};
@@ -504,18 +504,18 @@ public:
StringName get_action() const;
void set_pressed(bool p_pressed);
- virtual bool is_pressed() const;
+ virtual bool is_pressed() const override;
void set_strength(float p_strength);
float get_strength() const;
virtual bool is_action(const StringName &p_action) const;
- virtual bool action_match(const Ref<InputEvent> &p_event, bool *p_pressed, float *p_strength, float p_deadzone) const;
+ virtual bool action_match(const Ref<InputEvent> &p_event, bool *p_pressed, float *p_strength, float p_deadzone) const override;
- virtual bool shortcut_match(const Ref<InputEvent> &p_event) const;
- virtual bool is_action_type() const { return true; }
- virtual String as_text() const;
+ virtual bool shortcut_match(const Ref<InputEvent> &p_event) const override;
+ virtual bool is_action_type() const override { return true; }
+ virtual String as_text() const override;
InputEventAction() {}
};
@@ -544,8 +544,8 @@ public:
void set_factor(real_t p_factor);
real_t get_factor() const;
- virtual Ref<InputEvent> xformed_by(const Transform2D &p_xform, const Vector2 &p_local_ofs = Vector2()) const;
- virtual String as_text() const;
+ virtual Ref<InputEvent> xformed_by(const Transform2D &p_xform, const Vector2 &p_local_ofs = Vector2()) const override;
+ virtual String as_text() const override;
InputEventMagnifyGesture() {}
};
@@ -561,8 +561,8 @@ public:
void set_delta(const Vector2 &p_delta);
Vector2 get_delta() const;
- virtual Ref<InputEvent> xformed_by(const Transform2D &p_xform, const Vector2 &p_local_ofs = Vector2()) const;
- virtual String as_text() const;
+ virtual Ref<InputEvent> xformed_by(const Transform2D &p_xform, const Vector2 &p_local_ofs = Vector2()) const override;
+ virtual String as_text() const override;
InputEventPanGesture() {}
};
@@ -607,7 +607,7 @@ public:
void set_controller_value(const int p_controller_value);
int get_controller_value() const;
- virtual String as_text() const;
+ virtual String as_text() const override;
InputEventMIDI() {}
};
diff --git a/core/io/http_client.cpp b/core/io/http_client.cpp
index 40debae9e5..46e45500bf 100644
--- a/core/io/http_client.cpp
+++ b/core/io/http_client.cpp
@@ -451,7 +451,7 @@ Error HTTPClient::poll() {
}
}
- // This is a HEAD request, we wont receive anything.
+ // This is a HEAD request, we won't receive anything.
if (head_request) {
body_size = 0;
body_left = 0;
diff --git a/core/io/json.cpp b/core/io/json.cpp
index 1c603865ad..8bdd6385cb 100644
--- a/core/io/json.cpp
+++ b/core/io/json.cpp
@@ -265,7 +265,7 @@ Error JSON::_get_token(const CharType *p_str, int &index, int p_len, Token &r_to
if (p_str[index] == '-' || (p_str[index] >= '0' && p_str[index] <= '9')) {
//a number
const CharType *rptr;
- double number = String::to_double(&p_str[index], &rptr);
+ double number = String::to_float(&p_str[index], &rptr);
index += (rptr - &p_str[index]);
r_token.type = TK_NUMBER;
r_token.value = number;
@@ -371,6 +371,7 @@ Error JSON::_parse_array(Array &array, const CharType *p_str, int &index, int p_
need_comma = true;
}
+ r_err_str = "Expected ']'";
return ERR_PARSE_ERROR;
}
@@ -433,6 +434,7 @@ Error JSON::_parse_object(Dictionary &object, const CharType *p_str, int &index,
}
}
+ r_err_str = "Expected '}'";
return ERR_PARSE_ERROR;
}
diff --git a/core/io/logger.cpp b/core/io/logger.cpp
index ef78b1194e..886e5695b1 100644
--- a/core/io/logger.cpp
+++ b/core/io/logger.cpp
@@ -152,7 +152,7 @@ void RotatedFileLogger::rotate_file() {
char timestamp[21];
OS::Date date = OS::get_singleton()->get_date();
OS::Time time = OS::get_singleton()->get_time();
- sprintf(timestamp, "-%04d-%02d-%02d-%02d-%02d-%02d", date.year, date.month, date.day, time.hour, time.min, time.sec);
+ sprintf(timestamp, "_%04d-%02d-%02d_%02d-%02d-%02d", date.year, date.month, date.day, time.hour, time.min, time.sec);
String backup_name = base_path.get_basename() + timestamp;
if (base_path.get_extension() != String()) {
diff --git a/core/io/packet_peer.h b/core/io/packet_peer.h
index f0ba50087f..92cdbc4151 100644
--- a/core/io/packet_peer.h
+++ b/core/io/packet_peer.h
@@ -90,11 +90,11 @@ protected:
static void _bind_methods();
public:
- virtual int get_available_packet_count() const;
- virtual Error get_packet(const uint8_t **r_buffer, int &r_buffer_size);
- virtual Error put_packet(const uint8_t *p_buffer, int p_buffer_size);
+ virtual int get_available_packet_count() const override;
+ virtual Error get_packet(const uint8_t **r_buffer, int &r_buffer_size) override;
+ virtual Error put_packet(const uint8_t *p_buffer, int p_buffer_size) override;
- virtual int get_max_packet_size() const;
+ virtual int get_max_packet_size() const override;
void set_stream_peer(const Ref<StreamPeer> &p_peer);
Ref<StreamPeer> get_stream_peer() const;
diff --git a/core/io/packet_peer_udp.cpp b/core/io/packet_peer_udp.cpp
index 862fca96fc..e633a56d54 100644
--- a/core/io/packet_peer_udp.cpp
+++ b/core/io/packet_peer_udp.cpp
@@ -31,12 +31,14 @@
#include "packet_peer_udp.h"
#include "core/io/ip.h"
+#include "core/io/udp_server.h"
void PacketPeerUDP::set_blocking_mode(bool p_enable) {
blocking = p_enable;
}
void PacketPeerUDP::set_broadcast_enabled(bool p_enabled) {
+ ERR_FAIL_COND(udp_server);
broadcast = p_enabled;
if (_sock.is_valid() && _sock->is_open()) {
_sock->set_broadcasting_enabled(p_enabled);
@@ -44,6 +46,7 @@ void PacketPeerUDP::set_broadcast_enabled(bool p_enabled) {
}
Error PacketPeerUDP::join_multicast_group(IP_Address p_multi_address, String p_if_name) {
+ ERR_FAIL_COND_V(udp_server, ERR_LOCKED);
ERR_FAIL_COND_V(!_sock.is_valid(), ERR_UNAVAILABLE);
ERR_FAIL_COND_V(!p_multi_address.is_valid(), ERR_INVALID_PARAMETER);
@@ -58,6 +61,7 @@ Error PacketPeerUDP::join_multicast_group(IP_Address p_multi_address, String p_i
}
Error PacketPeerUDP::leave_multicast_group(IP_Address p_multi_address, String p_if_name) {
+ ERR_FAIL_COND_V(udp_server, ERR_LOCKED);
ERR_FAIL_COND_V(!_sock.is_valid(), ERR_UNAVAILABLE);
ERR_FAIL_COND_V(!_sock->is_open(), ERR_UNCONFIGURED);
return _sock->leave_multicast_group(p_multi_address, p_if_name);
@@ -130,7 +134,7 @@ Error PacketPeerUDP::put_packet(const uint8_t *p_buffer, int p_buffer_size) {
}
do {
- if (connected) {
+ if (connected && !udp_server) {
err = _sock->send(p_buffer, p_buffer_size, sent);
} else {
err = _sock->sendto(p_buffer, p_buffer_size, sent, peer_addr, peer_port);
@@ -186,26 +190,25 @@ Error PacketPeerUDP::listen(int p_port, const IP_Address &p_bind_address, int p_
return OK;
}
-Error PacketPeerUDP::connect_socket(Ref<NetSocket> p_sock) {
- Error err;
- int read = 0;
- uint16_t r_port;
- IP_Address r_ip;
-
- err = p_sock->recvfrom(recv_buffer, sizeof(recv_buffer), read, r_ip, r_port, true);
- ERR_FAIL_COND_V(err != OK, err);
- err = p_sock->connect_to_host(r_ip, r_port);
- ERR_FAIL_COND_V(err != OK, err);
+Error PacketPeerUDP::connect_shared_socket(Ref<NetSocket> p_sock, IP_Address p_ip, uint16_t p_port, UDPServer *p_server) {
+ udp_server = p_server;
+ connected = true;
_sock = p_sock;
- peer_addr = r_ip;
- peer_port = r_port;
+ peer_addr = p_ip;
+ peer_port = p_port;
packet_ip = peer_addr;
packet_port = peer_port;
- connected = true;
return OK;
}
+void PacketPeerUDP::disconnect_shared_socket() {
+ udp_server = nullptr;
+ _sock = Ref<NetSocket>(NetSocket::create());
+ close();
+}
+
Error PacketPeerUDP::connect_to_host(const IP_Address &p_host, int p_port) {
+ ERR_FAIL_COND_V(udp_server, ERR_LOCKED);
ERR_FAIL_COND_V(!_sock.is_valid(), ERR_UNAVAILABLE);
ERR_FAIL_COND_V(!p_host.is_valid(), ERR_INVALID_PARAMETER);
@@ -243,7 +246,11 @@ bool PacketPeerUDP::is_connected_to_host() const {
}
void PacketPeerUDP::close() {
- if (_sock.is_valid()) {
+ if (udp_server) {
+ udp_server->remove_peer(peer_addr, peer_port);
+ udp_server = nullptr;
+ _sock = Ref<NetSocket>(NetSocket::create());
+ } else if (_sock.is_valid()) {
_sock->close();
}
rb.resize(16);
@@ -262,6 +269,9 @@ Error PacketPeerUDP::_poll() {
if (!_sock->is_open()) {
return FAILED;
}
+ if (udp_server) {
+ return OK; // Handled by UDPServer.
+ }
Error err;
int read;
@@ -284,24 +294,29 @@ Error PacketPeerUDP::_poll() {
return FAILED;
}
- if (rb.space_left() < read + 24) {
+ err = store_packet(ip, port, recv_buffer, read);
#ifdef TOOLS_ENABLED
+ if (err != OK) {
WARN_PRINT("Buffer full, dropping packets!");
-#endif
- continue;
}
-
- uint32_t port32 = port;
- rb.write(ip.get_ipv6(), 16);
- rb.write((uint8_t *)&port32, 4);
- rb.write((uint8_t *)&read, 4);
- rb.write(recv_buffer, read);
- ++queue_count;
+#endif
}
return OK;
}
+Error PacketPeerUDP::store_packet(IP_Address p_ip, uint32_t p_port, uint8_t *p_buf, int p_buf_size) {
+ if (rb.space_left() < p_buf_size + 24) {
+ return ERR_OUT_OF_MEMORY;
+ }
+ rb.write(p_ip.get_ipv6(), 16);
+ rb.write((uint8_t *)&p_port, 4);
+ rb.write((uint8_t *)&p_buf_size, 4);
+ rb.write(p_buf, p_buf_size);
+ ++queue_count;
+ return OK;
+}
+
bool PacketPeerUDP::is_listening() const {
return _sock.is_valid() && _sock->is_open();
}
diff --git a/core/io/packet_peer_udp.h b/core/io/packet_peer_udp.h
index 23fc5460a6..9a44a1ebea 100644
--- a/core/io/packet_peer_udp.h
+++ b/core/io/packet_peer_udp.h
@@ -35,6 +35,8 @@
#include "core/io/net_socket.h"
#include "core/io/packet_peer.h"
+class UDPServer;
+
class PacketPeerUDP : public PacketPeer {
GDCLASS(PacketPeerUDP, PacketPeer);
@@ -55,6 +57,7 @@ protected:
bool connected = false;
bool blocking = true;
bool broadcast = false;
+ UDPServer *udp_server = nullptr;
Ref<NetSocket> _sock;
static void _bind_methods();
@@ -72,7 +75,9 @@ public:
Error wait();
bool is_listening() const;
- Error connect_socket(Ref<NetSocket> p_sock); // Used by UDPServer
+ Error connect_shared_socket(Ref<NetSocket> p_sock, IP_Address p_ip, uint16_t p_port, UDPServer *ref); // Used by UDPServer
+ void disconnect_shared_socket(); // Used by UDPServer
+ Error store_packet(IP_Address p_ip, uint32_t p_port, uint8_t *p_buf, int p_buf_size); // Used internally and by UDPServer
Error connect_to_host(const IP_Address &p_host, int p_port);
bool is_connected_to_host() const;
@@ -80,10 +85,10 @@ public:
int get_packet_port() const;
void set_dest_address(const IP_Address &p_address, int p_port);
- Error put_packet(const uint8_t *p_buffer, int p_buffer_size);
- Error get_packet(const uint8_t **r_buffer, int &r_buffer_size);
- int get_available_packet_count() const;
- int get_max_packet_size() const;
+ Error put_packet(const uint8_t *p_buffer, int p_buffer_size) override;
+ Error get_packet(const uint8_t **r_buffer, int &r_buffer_size) override;
+ int get_available_packet_count() const override;
+ int get_max_packet_size() const override;
void set_broadcast_enabled(bool p_enabled);
Error join_multicast_group(IP_Address p_multi_address, String p_if_name);
Error leave_multicast_group(IP_Address p_multi_address, String p_if_name);
diff --git a/core/io/resource_loader.cpp b/core/io/resource_loader.cpp
index f9d2c9067c..8b3a1f6637 100644
--- a/core/io/resource_loader.cpp
+++ b/core/io/resource_loader.cpp
@@ -865,7 +865,7 @@ String ResourceLoader::_path_remap(const String &p_path, bool *r_translation_rem
bool near_match = false;
for (int i = 0; i < res_remaps.size(); i++) {
- int split = res_remaps[i].find_last(":");
+ int split = res_remaps[i].rfind(":");
if (split == -1) {
continue;
}
@@ -999,6 +999,9 @@ void ResourceLoader::load_translation_remaps() {
void ResourceLoader::clear_translation_remaps() {
translation_remaps.clear();
+ while (remapped_list.first() != nullptr) {
+ remapped_list.remove(remapped_list.first());
+ }
}
void ResourceLoader::load_path_remaps() {
diff --git a/core/io/stream_peer.h b/core/io/stream_peer.h
index ec0b989ed8..39097a57f2 100644
--- a/core/io/stream_peer.h
+++ b/core/io/stream_peer.h
@@ -102,13 +102,13 @@ protected:
static void _bind_methods();
public:
- Error put_data(const uint8_t *p_data, int p_bytes);
- Error put_partial_data(const uint8_t *p_data, int p_bytes, int &r_sent);
+ Error put_data(const uint8_t *p_data, int p_bytes) override;
+ Error put_partial_data(const uint8_t *p_data, int p_bytes, int &r_sent) override;
- Error get_data(uint8_t *p_buffer, int p_bytes);
- Error get_partial_data(uint8_t *p_buffer, int p_bytes, int &r_received);
+ Error get_data(uint8_t *p_buffer, int p_bytes) override;
+ Error get_partial_data(uint8_t *p_buffer, int p_bytes, int &r_received) override;
- virtual int get_available_bytes() const;
+ virtual int get_available_bytes() const override;
void seek(int p_pos);
int get_size() const;
diff --git a/core/io/stream_peer_tcp.h b/core/io/stream_peer_tcp.h
index ab98d494d6..45205866b4 100644
--- a/core/io/stream_peer_tcp.h
+++ b/core/io/stream_peer_tcp.h
@@ -72,7 +72,7 @@ public:
uint16_t get_connected_port() const;
void disconnect_from_host();
- int get_available_bytes() const;
+ int get_available_bytes() const override;
Status get_status();
void set_no_delay(bool p_enabled);
@@ -81,10 +81,10 @@ public:
Error poll(NetSocket::PollType p_type, int timeout = 0);
// Read/Write from StreamPeer
- Error put_data(const uint8_t *p_data, int p_bytes);
- Error put_partial_data(const uint8_t *p_data, int p_bytes, int &r_sent);
- Error get_data(uint8_t *p_buffer, int p_bytes);
- Error get_partial_data(uint8_t *p_buffer, int p_bytes, int &r_received);
+ Error put_data(const uint8_t *p_data, int p_bytes) override;
+ Error put_partial_data(const uint8_t *p_data, int p_bytes, int &r_sent) override;
+ Error get_data(uint8_t *p_buffer, int p_bytes) override;
+ Error get_partial_data(uint8_t *p_buffer, int p_bytes, int &r_received) override;
StreamPeerTCP();
~StreamPeerTCP();
diff --git a/core/io/translation_loader_po.cpp b/core/io/translation_loader_po.cpp
index 0e0a948953..d8ddb213c3 100644
--- a/core/io/translation_loader_po.cpp
+++ b/core/io/translation_loader_po.cpp
@@ -32,30 +32,38 @@
#include "core/os/file_access.h"
#include "core/translation.h"
+#include "core/translation_po.h"
RES TranslationLoaderPO::load_translation(FileAccess *f, Error *r_error) {
enum Status {
-
STATUS_NONE,
STATUS_READING_ID,
STATUS_READING_STRING,
+ STATUS_READING_CONTEXT,
+ STATUS_READING_PLURAL,
};
Status status = STATUS_NONE;
String msg_id;
String msg_str;
+ String msg_context;
+ Vector<String> msgs_plural;
String config;
if (r_error) {
*r_error = ERR_FILE_CORRUPT;
}
- Ref<Translation> translation = Ref<Translation>(memnew(Translation));
+ Ref<TranslationPO> translation = Ref<TranslationPO>(memnew(TranslationPO));
int line = 1;
+ int plural_forms = 0;
+ int plural_index = -1;
+ bool entered_context = false;
bool skip_this = false;
bool skip_next = false;
bool is_eof = false;
+ const String path = f->get_path();
while (!is_eof) {
String l = f->get_line().strip_edges();
@@ -63,40 +71,107 @@ RES TranslationLoaderPO::load_translation(FileAccess *f, Error *r_error) {
// If we reached last line and it's not a content line, break, otherwise let processing that last loop
if (is_eof && l.empty()) {
- if (status == STATUS_READING_ID) {
+ if (status == STATUS_READING_ID || status == STATUS_READING_CONTEXT || (status == STATUS_READING_PLURAL && plural_index != plural_forms - 1)) {
memdelete(f);
- ERR_FAIL_V_MSG(RES(), f->get_path() + ":" + itos(line) + " Unexpected EOF while reading 'msgid' at file: ");
+ ERR_FAIL_V_MSG(RES(), "Unexpected EOF while reading PO file at: " + path + ":" + itos(line));
} else {
break;
}
}
- if (l.begins_with("msgid")) {
+ if (l.begins_with("msgctxt")) {
+ if (status != STATUS_READING_STRING && status != STATUS_READING_PLURAL) {
+ memdelete(f);
+ ERR_FAIL_V_MSG(RES(), "Unexpected 'msgctxt', was expecting 'msgid_plural' or 'msgstr' before 'msgctxt' while parsing: " + path + ":" + itos(line));
+ }
+
+ // In PO file, "msgctxt" appears before "msgid". If we encounter a "msgctxt", we add what we have read
+ // and set "entered_context" to true to prevent adding twice.
+ if (!skip_this && msg_id != "") {
+ if (status == STATUS_READING_STRING) {
+ translation->add_message(msg_id, msg_str, msg_context);
+ } else if (status == STATUS_READING_PLURAL) {
+ if (plural_index != plural_forms - 1) {
+ memdelete(f);
+ ERR_FAIL_V_MSG(RES(), "Number of 'msgstr[]' doesn't match with number of plural forms: " + path + ":" + itos(line));
+ }
+ translation->add_plural_message(msg_id, msgs_plural, msg_context);
+ }
+ }
+ msg_context = "";
+ l = l.substr(7, l.length()).strip_edges();
+ status = STATUS_READING_CONTEXT;
+ entered_context = true;
+ }
+
+ if (l.begins_with("msgid_plural")) {
+ if (plural_forms == 0) {
+ memdelete(f);
+ ERR_FAIL_V_MSG(RES(), "PO file uses 'msgid_plural' but 'Plural-Forms' is invalid or missing in header: " + path + ":" + itos(line));
+ } else if (status != STATUS_READING_ID) {
+ memdelete(f);
+ ERR_FAIL_V_MSG(RES(), "Unexpected 'msgid_plural', was expecting 'msgid' before 'msgid_plural' while parsing: " + path + ":" + itos(line));
+ }
+ // We don't record the message in "msgid_plural" itself as tr_n(), TTRN(), RTRN() interfaces provide the plural string already.
+ // We just have to reset variables related to plurals for "msgstr[]" later on.
+ l = l.substr(12, l.length()).strip_edges();
+ plural_index = -1;
+ msgs_plural.clear();
+ msgs_plural.resize(plural_forms);
+ status = STATUS_READING_PLURAL;
+ } else if (l.begins_with("msgid")) {
if (status == STATUS_READING_ID) {
memdelete(f);
- ERR_FAIL_V_MSG(RES(), f->get_path() + ":" + itos(line) + " Unexpected 'msgid', was expecting 'msgstr' while parsing: ");
+ ERR_FAIL_V_MSG(RES(), "Unexpected 'msgid', was expecting 'msgstr' while parsing: " + path + ":" + itos(line));
}
if (msg_id != "") {
- if (!skip_this) {
- translation->add_message(msg_id, msg_str);
+ if (!skip_this && !entered_context) {
+ if (status == STATUS_READING_STRING) {
+ translation->add_message(msg_id, msg_str, msg_context);
+ } else if (status == STATUS_READING_PLURAL) {
+ if (plural_index != plural_forms - 1) {
+ memdelete(f);
+ ERR_FAIL_V_MSG(RES(), "Number of 'msgstr[]' doesn't match with number of plural forms: " + path + ":" + itos(line));
+ }
+ translation->add_plural_message(msg_id, msgs_plural, msg_context);
+ }
}
} else if (config == "") {
config = msg_str;
+ // Record plural rule.
+ int p_start = config.find("Plural-Forms");
+ if (p_start != -1) {
+ int p_end = config.find("\n", p_start);
+ translation->set_plural_rule(config.substr(p_start, p_end - p_start));
+ plural_forms = translation->get_plural_forms();
+ }
}
l = l.substr(5, l.length()).strip_edges();
status = STATUS_READING_ID;
+ // If we did not encounter msgctxt, we reset context to empty to reset it.
+ if (!entered_context) {
+ msg_context = "";
+ }
msg_id = "";
msg_str = "";
skip_this = skip_next;
skip_next = false;
+ entered_context = false;
}
- if (l.begins_with("msgstr")) {
+ if (l.begins_with("msgstr[")) {
+ if (status != STATUS_READING_PLURAL) {
+ memdelete(f);
+ ERR_FAIL_V_MSG(RES(), "Unexpected 'msgstr[]', was expecting 'msgid_plural' before 'msgstr[]' while parsing: " + path + ":" + itos(line));
+ }
+ plural_index++; // Increment to add to the next slot in vector msgs_plural.
+ l = l.substr(9, l.length()).strip_edges();
+ } else if (l.begins_with("msgstr")) {
if (status != STATUS_READING_ID) {
memdelete(f);
- ERR_FAIL_V_MSG(RES(), f->get_path() + ":" + itos(line) + " Unexpected 'msgstr', was expecting 'msgid' while parsing: ");
+ ERR_FAIL_V_MSG(RES(), "Unexpected 'msgstr', was expecting 'msgid' before 'msgstr' while parsing: " + path + ":" + itos(line));
}
l = l.substr(6, l.length()).strip_edges();
@@ -108,10 +183,13 @@ RES TranslationLoaderPO::load_translation(FileAccess *f, Error *r_error) {
skip_next = true;
}
line++;
- continue; //nothing to read or comment
+ continue; // Nothing to read or comment.
}
- ERR_FAIL_COND_V_MSG(!l.begins_with("\"") || status == STATUS_NONE, RES(), f->get_path() + ":" + itos(line) + " Invalid line '" + l + "' while parsing: ");
+ if (!l.begins_with("\"") || status == STATUS_NONE) {
+ memdelete(f);
+ ERR_FAIL_V_MSG(RES(), "Invalid line '" + l + "' while parsing: " + path + ":" + itos(line));
+ }
l = l.substr(1, l.length());
// Find final quote, ignoring escaped ones (\").
@@ -133,34 +211,49 @@ RES TranslationLoaderPO::load_translation(FileAccess *f, Error *r_error) {
escape_next = false;
}
- ERR_FAIL_COND_V_MSG(end_pos == -1, RES(), f->get_path() + ":" + itos(line) + ": Expected '\"' at end of message while parsing file.");
+ if (end_pos == -1) {
+ memdelete(f);
+ ERR_FAIL_V_MSG(RES(), "Expected '\"' at end of message while parsing: " + path + ":" + itos(line));
+ }
l = l.substr(0, end_pos);
l = l.c_unescape();
if (status == STATUS_READING_ID) {
msg_id += l;
- } else {
+ } else if (status == STATUS_READING_STRING) {
msg_str += l;
+ } else if (status == STATUS_READING_CONTEXT) {
+ msg_context += l;
+ } else if (status == STATUS_READING_PLURAL && plural_index >= 0) {
+ msgs_plural.write[plural_index] = msgs_plural[plural_index] + l;
}
line++;
}
- f->close();
memdelete(f);
+ // Add the last set of data from last iteration.
if (status == STATUS_READING_STRING) {
if (msg_id != "") {
if (!skip_this) {
- translation->add_message(msg_id, msg_str);
+ translation->add_message(msg_id, msg_str, msg_context);
}
} else if (config == "") {
config = msg_str;
}
+ } else if (status == STATUS_READING_PLURAL) {
+ if (!skip_this && msg_id != "") {
+ if (plural_index != plural_forms - 1) {
+ memdelete(f);
+ ERR_FAIL_V_MSG(RES(), "Number of 'msgstr[]' doesn't match with number of plural forms: " + path + ":" + itos(line));
+ }
+ translation->add_plural_message(msg_id, msgs_plural, msg_context);
+ }
}
- ERR_FAIL_COND_V_MSG(config == "", RES(), "No config found in file: " + f->get_path() + ".");
+ ERR_FAIL_COND_V_MSG(config == "", RES(), "No config found in file: " + path + ".");
Vector<String> configs = config.split("\n");
for (int i = 0; i < configs.size(); i++) {
@@ -197,7 +290,6 @@ RES TranslationLoaderPO::load(const String &p_path, const String &p_original_pat
void TranslationLoaderPO::get_recognized_extensions(List<String> *p_extensions) const {
p_extensions->push_back("po");
- //p_extensions->push_back("mo"); //mo in the future...
}
bool TranslationLoaderPO::handles_type(const String &p_type) const {
diff --git a/core/io/udp_server.cpp b/core/io/udp_server.cpp
index 1d329daf8b..acd15aadc6 100644
--- a/core/io/udp_server.cpp
+++ b/core/io/udp_server.cpp
@@ -32,10 +32,58 @@
void UDPServer::_bind_methods() {
ClassDB::bind_method(D_METHOD("listen", "port", "bind_address"), &UDPServer::listen, DEFVAL("*"));
+ ClassDB::bind_method(D_METHOD("poll"), &UDPServer::poll);
ClassDB::bind_method(D_METHOD("is_connection_available"), &UDPServer::is_connection_available);
ClassDB::bind_method(D_METHOD("is_listening"), &UDPServer::is_listening);
ClassDB::bind_method(D_METHOD("take_connection"), &UDPServer::take_connection);
ClassDB::bind_method(D_METHOD("stop"), &UDPServer::stop);
+ ClassDB::bind_method(D_METHOD("set_max_pending_connections", "max_pending_connections"), &UDPServer::set_max_pending_connections);
+ ClassDB::bind_method(D_METHOD("get_max_pending_connections"), &UDPServer::get_max_pending_connections);
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "max_pending_connections", PROPERTY_HINT_RANGE, "0,256,1"), "set_max_pending_connections", "get_max_pending_connections");
+}
+
+Error UDPServer::poll() {
+ ERR_FAIL_COND_V(!_sock.is_valid(), ERR_UNAVAILABLE);
+ if (!_sock->is_open()) {
+ return ERR_UNCONFIGURED;
+ }
+ Error err;
+ int read;
+ IP_Address ip;
+ uint16_t port;
+ while (true) {
+ err = _sock->recvfrom(recv_buffer, sizeof(recv_buffer), read, ip, port);
+ if (err != OK) {
+ if (err == ERR_BUSY) {
+ break;
+ }
+ return FAILED;
+ }
+ Peer p;
+ p.ip = ip;
+ p.port = port;
+ List<Peer>::Element *E = peers.find(p);
+ if (!E) {
+ E = pending.find(p);
+ }
+ if (E) {
+ E->get().peer->store_packet(ip, port, recv_buffer, read);
+ } else {
+ if (pending.size() >= max_pending_connections) {
+ // Drop connection.
+ continue;
+ }
+ // It's a new peer, add it to the pending list.
+ Peer peer;
+ peer.ip = ip;
+ peer.port = port;
+ peer.peer = memnew(PacketPeerUDP);
+ peer.peer->connect_shared_socket(_sock, ip, port, this);
+ peer.peer->store_packet(ip, port, recv_buffer, read);
+ pending.push_back(peer);
+ }
+ }
+ return OK;
}
Error UDPServer::listen(uint16_t p_port, const IP_Address &p_bind_address) {
@@ -82,8 +130,24 @@ bool UDPServer::is_connection_available() const {
return false;
}
- Error err = _sock->poll(NetSocket::POLL_TYPE_IN, 0);
- return (err == OK);
+ return pending.size() > 0;
+}
+
+void UDPServer::set_max_pending_connections(int p_max) {
+ ERR_FAIL_COND_MSG(p_max < 0, "Max pending connections value must be a positive number (0 means refuse new connections).");
+ max_pending_connections = p_max;
+ while (p_max > pending.size()) {
+ List<Peer>::Element *E = pending.back();
+ if (!E) {
+ break;
+ }
+ memdelete(E->get().peer);
+ pending.erase(E);
+ }
+}
+
+int UDPServer::get_max_pending_connections() const {
+ return max_pending_connections;
}
Ref<PacketPeerUDP> UDPServer::take_connection() {
@@ -92,11 +156,20 @@ Ref<PacketPeerUDP> UDPServer::take_connection() {
return conn;
}
- conn = Ref<PacketPeerUDP>(memnew(PacketPeerUDP));
- conn->connect_socket(_sock);
- _sock = Ref<NetSocket>(NetSocket::create());
- listen(bind_port, bind_address);
- return conn;
+ Peer peer = pending[0];
+ pending.pop_front();
+ peers.push_back(peer);
+ return peer.peer;
+}
+
+void UDPServer::remove_peer(IP_Address p_ip, int p_port) {
+ Peer peer;
+ peer.ip = p_ip;
+ peer.port = p_port;
+ List<Peer>::Element *E = peers.find(peer);
+ if (E) {
+ peers.erase(E);
+ }
}
void UDPServer::stop() {
@@ -105,6 +178,19 @@ void UDPServer::stop() {
}
bind_port = 0;
bind_address = IP_Address();
+ List<Peer>::Element *E = peers.front();
+ while (E) {
+ E->get().peer->disconnect_shared_socket();
+ E = E->next();
+ }
+ E = pending.front();
+ while (E) {
+ E->get().peer->disconnect_shared_socket();
+ memdelete(E->get().peer);
+ E = E->next();
+ }
+ peers.clear();
+ pending.clear();
}
UDPServer::UDPServer() :
diff --git a/core/io/udp_server.h b/core/io/udp_server.h
index 90bb82b62b..3175b09b19 100644
--- a/core/io/udp_server.h
+++ b/core/io/udp_server.h
@@ -38,15 +38,40 @@ class UDPServer : public Reference {
GDCLASS(UDPServer, Reference);
protected:
- static void _bind_methods();
- int bind_port;
+ enum {
+ PACKET_BUFFER_SIZE = 65536
+ };
+
+ struct Peer {
+ PacketPeerUDP *peer;
+ IP_Address ip;
+ uint16_t port = 0;
+
+ bool operator==(const Peer &p_other) const {
+ return (ip == p_other.ip && port == p_other.port);
+ }
+ };
+ uint8_t recv_buffer[PACKET_BUFFER_SIZE];
+
+ int bind_port = 0;
IP_Address bind_address;
+
+ List<Peer> peers;
+ List<Peer> pending;
+ int max_pending_connections = 16;
+
Ref<NetSocket> _sock;
+ static void _bind_methods();
+
public:
+ void remove_peer(IP_Address p_ip, int p_port);
Error listen(uint16_t p_port, const IP_Address &p_bind_address = IP_Address("*"));
+ Error poll();
bool is_listening() const;
bool is_connection_available() const;
+ void set_max_pending_connections(int p_max);
+ int get_max_pending_connections() const;
Ref<PacketPeerUDP> take_connection();
void stop();
diff --git a/core/list.h b/core/list.h
index 6052a619fb..f850db5241 100644
--- a/core/list.h
+++ b/core/list.h
@@ -348,7 +348,7 @@ public:
* erase an element in the list, by iterator pointing to it. Return true if it was found/erased.
*/
bool erase(const Element *p_I) {
- if (_data) {
+ if (_data && p_I) {
bool ret = _data->erase(p_I);
if (_data->size_cache == 0) {
diff --git a/core/local_vector.h b/core/local_vector.h
index d97f3330dc..b0dbd22b29 100644
--- a/core/local_vector.h
+++ b/core/local_vector.h
@@ -73,10 +73,10 @@ public:
void remove(U p_index) {
ERR_FAIL_UNSIGNED_INDEX(p_index, count);
+ count--;
for (U i = p_index; i < count; i++) {
data[i] = data[i + 1];
}
- count--;
if (!__has_trivial_destructor(T) && !force_trivial) {
data[count].~T();
}
diff --git a/core/math/aabb.h b/core/math/aabb.h
index 4106fbb93c..bd1f3a1a36 100644
--- a/core/math/aabb.h
+++ b/core/math/aabb.h
@@ -99,6 +99,10 @@ public:
_FORCE_INLINE_ void project_range_in_plane(const Plane &p_plane, real_t &r_min, real_t &r_max) const;
_FORCE_INLINE_ void expand_to(const Vector3 &p_vector); /** expand to contain a point if necessary */
+ _FORCE_INLINE_ AABB abs() const {
+ return AABB(Vector3(position.x + MIN(size.x, 0), position.y + MIN(size.y, 0), position.z + MIN(size.z, 0)), size.abs());
+ }
+
operator String() const;
_FORCE_INLINE_ AABB() {}
diff --git a/core/math/camera_matrix.cpp b/core/math/camera_matrix.cpp
index 22ab83f358..c154d57a13 100644
--- a/core/math/camera_matrix.cpp
+++ b/core/math/camera_matrix.cpp
@@ -278,7 +278,7 @@ Vector2 CameraMatrix::get_viewport_half_extents() const {
return Vector2(res.x, res.y);
}
-void CameraMatrix::get_far_plane_size(real_t &r_width, real_t &r_height) const {
+Vector2 CameraMatrix::get_far_plane_half_extents() const {
const real_t *matrix = (const real_t *)this->matrix;
///////--- Far Plane ---///////
Plane far_plane = Plane(matrix[3] - matrix[2],
@@ -303,8 +303,7 @@ void CameraMatrix::get_far_plane_size(real_t &r_width, real_t &r_height) const {
Vector3 res;
far_plane.intersect_3(right_plane, top_plane, &res);
- r_width = res.x;
- r_height = res.y;
+ return Vector2(res.x, res.y);
}
bool CameraMatrix::get_endpoints(const Transform &p_transform, Vector3 *p_8points) const {
diff --git a/core/math/camera_matrix.h b/core/math/camera_matrix.h
index 49fdecae02..c5cdd98377 100644
--- a/core/math/camera_matrix.h
+++ b/core/math/camera_matrix.h
@@ -74,7 +74,7 @@ struct CameraMatrix {
bool get_endpoints(const Transform &p_transform, Vector3 *p_8points) const;
Vector2 get_viewport_half_extents() const;
- void get_far_plane_size(real_t &r_width, real_t &r_height) const;
+ Vector2 get_far_plane_half_extents() const;
void invert();
CameraMatrix inverse() const;
diff --git a/core/math/expression.cpp b/core/math/expression.cpp
index 6421606ca2..735a30f6cc 100644
--- a/core/math/expression.cpp
+++ b/core/math/expression.cpp
@@ -1062,9 +1062,9 @@ Error Expression::_get_token(Token &r_token) {
r_token.type = TK_CONSTANT;
if (is_float) {
- r_token.value = num.to_double();
+ r_token.value = num.to_float();
} else {
- r_token.value = num.to_int64();
+ r_token.value = num.to_int();
}
return OK;
diff --git a/core/math/expression.h b/core/math/expression.h
index 59a9a2f4ed..f2cfe6b1a6 100644
--- a/core/math/expression.h
+++ b/core/math/expression.h
@@ -343,7 +343,7 @@ protected:
public:
Error parse(const String &p_expression, const Vector<String> &p_input_names = Vector<String>());
- Variant execute(Array p_inputs, Object *p_base = nullptr, bool p_show_error = true);
+ Variant execute(Array p_inputs = Array(), Object *p_base = nullptr, bool p_show_error = true);
bool has_execute_failed() const;
String get_error_text() const;
diff --git a/core/math/geometry_3d.cpp b/core/math/geometry_3d.cpp
index 7807ab19a7..2c19fe2085 100644
--- a/core/math/geometry_3d.cpp
+++ b/core/math/geometry_3d.cpp
@@ -648,7 +648,7 @@ Geometry3D::MeshData Geometry3D::build_convex_mesh(const Vector<Plane> &p_planes
Vector<Vector3> vertices;
- Vector3 center = p.get_any_point();
+ Vector3 center = p.center();
// make a quad clockwise
vertices.push_back(center - up * subplane_size + right * subplane_size);
vertices.push_back(center - up * subplane_size - right * subplane_size);
diff --git a/core/math/math_funcs.h b/core/math/math_funcs.h
index 7a9fd60e23..9f8d4da5b3 100644
--- a/core/math/math_funcs.h
+++ b/core/math/math_funcs.h
@@ -231,19 +231,19 @@ public:
static _ALWAYS_INLINE_ double range_lerp(double p_value, double p_istart, double p_istop, double p_ostart, double p_ostop) { return Math::lerp(p_ostart, p_ostop, Math::inverse_lerp(p_istart, p_istop, p_value)); }
static _ALWAYS_INLINE_ float range_lerp(float p_value, float p_istart, float p_istop, float p_ostart, float p_ostop) { return Math::lerp(p_ostart, p_ostop, Math::inverse_lerp(p_istart, p_istop, p_value)); }
- static _ALWAYS_INLINE_ double smoothstep(double p_from, double p_to, double p_weight) {
+ static _ALWAYS_INLINE_ double smoothstep(double p_from, double p_to, double p_s) {
if (is_equal_approx(p_from, p_to)) {
return p_from;
}
- double x = CLAMP((p_weight - p_from) / (p_to - p_from), 0.0, 1.0);
- return x * x * (3.0 - 2.0 * x);
+ double s = CLAMP((p_s - p_from) / (p_to - p_from), 0.0, 1.0);
+ return s * s * (3.0 - 2.0 * s);
}
- static _ALWAYS_INLINE_ float smoothstep(float p_from, float p_to, float p_weight) {
+ static _ALWAYS_INLINE_ float smoothstep(float p_from, float p_to, float p_s) {
if (is_equal_approx(p_from, p_to)) {
return p_from;
}
- float x = CLAMP((p_weight - p_from) / (p_to - p_from), 0.0f, 1.0f);
- return x * x * (3.0f - 2.0f * x);
+ 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 + SGN(p_to - p_from) * p_delta; }
static _ALWAYS_INLINE_ float move_toward(float p_from, float p_to, float p_delta) { return abs(p_to - p_from) <= p_delta ? p_to : p_from + SGN(p_to - p_from) * p_delta; }
diff --git a/core/math/plane.cpp b/core/math/plane.cpp
index df37ceb0e5..4200484c59 100644
--- a/core/math/plane.cpp
+++ b/core/math/plane.cpp
@@ -52,10 +52,6 @@ Plane Plane::normalized() const {
return p;
}
-Vector3 Plane::get_any_point() const {
- return get_normal() * d;
-}
-
Vector3 Plane::get_any_perpendicular_normal() const {
static const Vector3 p1 = Vector3(1, 0, 0);
static const Vector3 p2 = Vector3(0, 1, 0);
diff --git a/core/math/plane.h b/core/math/plane.h
index 9a3e5a485f..70a6111edd 100644
--- a/core/math/plane.h
+++ b/core/math/plane.h
@@ -47,7 +47,6 @@ public:
/* Plane-Point operations */
_FORCE_INLINE_ Vector3 center() const { return normal * d; }
- Vector3 get_any_point() const;
Vector3 get_any_perpendicular_normal() const;
_FORCE_INLINE_ bool is_point_over(const Vector3 &p_point) const; ///< Point is over plane
diff --git a/core/oa_hash_map.h b/core/oa_hash_map.h
index 775e17fdb5..6061366ab3 100644
--- a/core/oa_hash_map.h
+++ b/core/oa_hash_map.h
@@ -48,7 +48,7 @@
*
* Only used keys and values are constructed. For free positions there's space
* in the arrays for each, but that memory is kept uninitialized.
- *
+ *
* The assignment operator copy the pairs from one map to the other.
*/
template <class TKey, class TValue,
diff --git a/core/object.cpp b/core/object.cpp
index 8abea9ca7e..67c605c39b 100644
--- a/core/object.cpp
+++ b/core/object.cpp
@@ -675,89 +675,11 @@ Variant Object::_call_deferred_bind(const Variant **p_args, int p_argcount, Call
StringName method = *p_args[0];
- MessageQueue::get_singleton()->push_call(get_instance_id(), method, &p_args[1], p_argcount - 1);
+ MessageQueue::get_singleton()->push_call(get_instance_id(), method, &p_args[1], p_argcount - 1, true);
return Variant();
}
-#ifdef DEBUG_ENABLED
-static void _test_call_error(const StringName &p_func, const Callable::CallError &error) {
- switch (error.error) {
- case Callable::CallError::CALL_OK:
- case Callable::CallError::CALL_ERROR_INVALID_METHOD:
- break;
- case Callable::CallError::CALL_ERROR_INVALID_ARGUMENT: {
- ERR_FAIL_MSG("Error calling function: " + String(p_func) + " - Invalid type for argument " + itos(error.argument) + ", expected " + Variant::get_type_name(Variant::Type(error.expected)) + ".");
- break;
- }
- case Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS: {
- ERR_FAIL_MSG("Error calling function: " + String(p_func) + " - Too many arguments, expected " + itos(error.argument) + ".");
- break;
- }
- case Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS: {
- ERR_FAIL_MSG("Error calling function: " + String(p_func) + " - Too few arguments, expected " + itos(error.argument) + ".");
- break;
- }
- case Callable::CallError::CALL_ERROR_INSTANCE_IS_NULL:
- break;
- }
-}
-#else
-
-#define _test_call_error(m_str, m_err)
-
-#endif
-
-void Object::call_multilevel(const StringName &p_method, const Variant **p_args, int p_argcount) {
- if (p_method == CoreStringNames::get_singleton()->_free) {
-#ifdef DEBUG_ENABLED
- ERR_FAIL_COND_MSG(Object::cast_to<Reference>(this), "Can't 'free' a reference.");
-
- ERR_FAIL_COND_MSG(_lock_index.get() > 1, "Object is locked and can't be freed.");
-#endif
-
- //must be here, must be before everything,
- memdelete(this);
- return;
- }
-
- //Variant ret;
- OBJ_DEBUG_LOCK
-
- Callable::CallError error;
-
- if (script_instance) {
- script_instance->call_multilevel(p_method, p_args, p_argcount);
- //_test_call_error(p_method,error);
- }
-
- MethodBind *method = ClassDB::get_method(get_class_name(), p_method);
-
- if (method) {
- method->call(this, p_args, p_argcount, error);
- _test_call_error(p_method, error);
- }
-}
-
-void Object::call_multilevel_reversed(const StringName &p_method, const Variant **p_args, int p_argcount) {
- MethodBind *method = ClassDB::get_method(get_class_name(), p_method);
-
- Callable::CallError error;
- OBJ_DEBUG_LOCK
-
- if (method) {
- method->call(this, p_args, p_argcount, error);
- _test_call_error(p_method, error);
- }
-
- //Variant ret;
-
- if (script_instance) {
- script_instance->call_multilevel_reversed(p_method, p_args, p_argcount);
- //_test_call_error(p_method,error);
- }
-}
-
bool Object::has_method(const StringName &p_method) const {
if (p_method == CoreStringNames::get_singleton()->_free) {
return true;
@@ -820,21 +742,6 @@ Variant Object::call(const StringName &p_name, VARIANT_ARG_DECLARE) {
return ret;
}
-void Object::call_multilevel(const StringName &p_name, VARIANT_ARG_DECLARE) {
- VARIANT_ARGPTRS;
-
- int argc = 0;
- for (int i = 0; i < VARIANT_ARG_MAX; i++) {
- if (argptr[i]->get_type() == Variant::NIL) {
- break;
- }
- argc++;
- }
-
- //Callable::CallError error;
- call_multilevel(p_name, argptr, argc);
-}
-
Variant Object::call(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) {
r_error.error = Callable::CallError::CALL_OK;
@@ -1525,12 +1432,22 @@ void Object::initialize_class() {
initialized = true;
}
-StringName Object::tr(const StringName &p_message) const {
+String Object::tr(const StringName &p_message, const StringName &p_context) const {
if (!_can_translate || !TranslationServer::get_singleton()) {
return p_message;
}
+ return TranslationServer::get_singleton()->translate(p_message, p_context);
+}
- return TranslationServer::get_singleton()->translate(p_message);
+String Object::tr_n(const StringName &p_message, const StringName &p_message_plural, int p_n, const StringName &p_context) const {
+ if (!_can_translate || !TranslationServer::get_singleton()) {
+ // Return message based on English plural rule if translation is not possible.
+ if (p_n == 1) {
+ return p_message;
+ }
+ return p_message_plural;
+ }
+ return TranslationServer::get_singleton()->translate_plural(p_message, p_message_plural, p_n, p_context);
}
void Object::_clear_internal_resource_paths(const Variant &p_var) {
@@ -1671,7 +1588,8 @@ void Object::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_message_translation", "enable"), &Object::set_message_translation);
ClassDB::bind_method(D_METHOD("can_translate_messages"), &Object::can_translate_messages);
- ClassDB::bind_method(D_METHOD("tr", "message"), &Object::tr);
+ ClassDB::bind_method(D_METHOD("tr", "message", "context"), &Object::tr, DEFVAL(""));
+ ClassDB::bind_method(D_METHOD("tr_n", "message", "plural_message", "n", "context"), &Object::tr_n, DEFVAL(""));
ClassDB::bind_method(D_METHOD("is_queued_for_deletion"), &Object::is_queued_for_deletion);
diff --git a/core/object.h b/core/object.h
index 5b46a0f93a..f9a12da8f6 100644
--- a/core/object.h
+++ b/core/object.h
@@ -250,142 +250,142 @@ public: \
\
private:
-#define GDCLASS(m_class, m_inherits) \
-private: \
- void operator=(const m_class &p_rval) {} \
- mutable StringName _class_name; \
- friend class ClassDB; \
- \
-public: \
- virtual String get_class() const { \
- return String(#m_class); \
- } \
- virtual const StringName *_get_class_namev() const { \
- if (!_class_name) { \
- _class_name = get_class_static(); \
- } \
- return &_class_name; \
- } \
- static _FORCE_INLINE_ void *get_class_ptr_static() { \
- static int ptr; \
- return &ptr; \
- } \
- static _FORCE_INLINE_ String get_class_static() { \
- return String(#m_class); \
- } \
- static _FORCE_INLINE_ String get_parent_class_static() { \
- return m_inherits::get_class_static(); \
- } \
- static void get_inheritance_list_static(List<String> *p_inheritance_list) { \
- m_inherits::get_inheritance_list_static(p_inheritance_list); \
- p_inheritance_list->push_back(String(#m_class)); \
- } \
- static String get_category_static() { \
- String category = m_inherits::get_category_static(); \
- if (_get_category != m_inherits::_get_category) { \
- if (category != "") { \
- category += "/"; \
- } \
- category += _get_category(); \
- } \
- return category; \
- } \
- static String inherits_static() { \
- return String(#m_inherits); \
- } \
- virtual bool is_class(const String &p_class) const { return (p_class == (#m_class)) ? true : m_inherits::is_class(p_class); } \
- virtual bool is_class_ptr(void *p_ptr) const { return (p_ptr == get_class_ptr_static()) ? true : m_inherits::is_class_ptr(p_ptr); } \
- \
- static void get_valid_parents_static(List<String> *p_parents) { \
- if (m_class::_get_valid_parents_static != m_inherits::_get_valid_parents_static) { \
- m_class::_get_valid_parents_static(p_parents); \
- } \
- \
- m_inherits::get_valid_parents_static(p_parents); \
- } \
- \
-protected: \
- _FORCE_INLINE_ static void (*_get_bind_methods())() { \
- return &m_class::_bind_methods; \
- } \
- \
-public: \
- static void initialize_class() { \
- static bool initialized = false; \
- if (initialized) { \
- return; \
- } \
- m_inherits::initialize_class(); \
- ClassDB::_add_class<m_class>(); \
- if (m_class::_get_bind_methods() != m_inherits::_get_bind_methods()) { \
- _bind_methods(); \
- } \
- initialized = true; \
- } \
- \
-protected: \
- virtual void _initialize_classv() { \
- initialize_class(); \
- } \
- _FORCE_INLINE_ bool (Object::*_get_get() const)(const StringName &p_name, Variant &) const { \
- return (bool (Object::*)(const StringName &, Variant &) const) & m_class::_get; \
- } \
- virtual bool _getv(const StringName &p_name, Variant &r_ret) const { \
- if (m_class::_get_get() != m_inherits::_get_get()) { \
- if (_get(p_name, r_ret)) { \
- return true; \
- } \
- } \
- return m_inherits::_getv(p_name, r_ret); \
- } \
- _FORCE_INLINE_ bool (Object::*_get_set() const)(const StringName &p_name, const Variant &p_property) { \
- return (bool (Object::*)(const StringName &, const Variant &)) & m_class::_set; \
- } \
- virtual bool _setv(const StringName &p_name, const Variant &p_property) { \
- if (m_inherits::_setv(p_name, p_property)) { \
- return true; \
- } \
- if (m_class::_get_set() != m_inherits::_get_set()) { \
- return _set(p_name, p_property); \
- } \
- return false; \
- } \
- _FORCE_INLINE_ void (Object::*_get_get_property_list() const)(List<PropertyInfo> * p_list) const { \
- return (void (Object::*)(List<PropertyInfo> *) const) & m_class::_get_property_list; \
- } \
- virtual void _get_property_listv(List<PropertyInfo> *p_list, bool p_reversed) const { \
- 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)); \
- if (!_is_gpl_reversed()) { \
- ClassDB::get_property_list(#m_class, p_list, true, this); \
- } \
- if (m_class::_get_get_property_list() != m_inherits::_get_get_property_list()) { \
- _get_property_list(p_list); \
- } \
- if (_is_gpl_reversed()) { \
- ClassDB::get_property_list(#m_class, p_list, true, this); \
- } \
- if (p_reversed) { \
- m_inherits::_get_property_listv(p_list, p_reversed); \
- } \
- } \
- _FORCE_INLINE_ void (Object::*_get_notification() const)(int) { \
- return (void (Object::*)(int)) & m_class::_notification; \
- } \
- virtual void _notificationv(int p_notification, bool p_reversed) { \
- if (!p_reversed) { \
- m_inherits::_notificationv(p_notification, p_reversed); \
- } \
- if (m_class::_get_notification() != m_inherits::_get_notification()) { \
- _notification(p_notification); \
- } \
- if (p_reversed) { \
- m_inherits::_notificationv(p_notification, p_reversed); \
- } \
- } \
- \
+#define GDCLASS(m_class, m_inherits) \
+private: \
+ void operator=(const m_class &p_rval) {} \
+ mutable StringName _class_name; \
+ friend class ClassDB; \
+ \
+public: \
+ virtual String get_class() const override { \
+ return String(#m_class); \
+ } \
+ virtual const StringName *_get_class_namev() const override { \
+ if (!_class_name) { \
+ _class_name = get_class_static(); \
+ } \
+ return &_class_name; \
+ } \
+ static _FORCE_INLINE_ void *get_class_ptr_static() { \
+ static int ptr; \
+ return &ptr; \
+ } \
+ static _FORCE_INLINE_ String get_class_static() { \
+ return String(#m_class); \
+ } \
+ static _FORCE_INLINE_ String get_parent_class_static() { \
+ return m_inherits::get_class_static(); \
+ } \
+ static void get_inheritance_list_static(List<String> *p_inheritance_list) { \
+ m_inherits::get_inheritance_list_static(p_inheritance_list); \
+ p_inheritance_list->push_back(String(#m_class)); \
+ } \
+ static String get_category_static() { \
+ String category = m_inherits::get_category_static(); \
+ if (_get_category != m_inherits::_get_category) { \
+ if (category != "") { \
+ category += "/"; \
+ } \
+ category += _get_category(); \
+ } \
+ return category; \
+ } \
+ static String inherits_static() { \
+ return String(#m_inherits); \
+ } \
+ virtual bool is_class(const String &p_class) const override { return (p_class == (#m_class)) ? true : m_inherits::is_class(p_class); } \
+ virtual bool is_class_ptr(void *p_ptr) const override { return (p_ptr == get_class_ptr_static()) ? true : m_inherits::is_class_ptr(p_ptr); } \
+ \
+ static void get_valid_parents_static(List<String> *p_parents) { \
+ if (m_class::_get_valid_parents_static != m_inherits::_get_valid_parents_static) { \
+ m_class::_get_valid_parents_static(p_parents); \
+ } \
+ \
+ m_inherits::get_valid_parents_static(p_parents); \
+ } \
+ \
+protected: \
+ _FORCE_INLINE_ static void (*_get_bind_methods())() { \
+ return &m_class::_bind_methods; \
+ } \
+ \
+public: \
+ static void initialize_class() { \
+ static bool initialized = false; \
+ if (initialized) { \
+ return; \
+ } \
+ m_inherits::initialize_class(); \
+ ClassDB::_add_class<m_class>(); \
+ if (m_class::_get_bind_methods() != m_inherits::_get_bind_methods()) { \
+ _bind_methods(); \
+ } \
+ initialized = true; \
+ } \
+ \
+protected: \
+ virtual void _initialize_classv() override { \
+ initialize_class(); \
+ } \
+ _FORCE_INLINE_ bool (Object::*_get_get() const)(const StringName &p_name, Variant &) const { \
+ return (bool (Object::*)(const StringName &, Variant &) const) & m_class::_get; \
+ } \
+ virtual bool _getv(const StringName &p_name, Variant &r_ret) const override { \
+ if (m_class::_get_get() != m_inherits::_get_get()) { \
+ if (_get(p_name, r_ret)) { \
+ return true; \
+ } \
+ } \
+ return m_inherits::_getv(p_name, r_ret); \
+ } \
+ _FORCE_INLINE_ bool (Object::*_get_set() const)(const StringName &p_name, const Variant &p_property) { \
+ return (bool (Object::*)(const StringName &, const Variant &)) & m_class::_set; \
+ } \
+ virtual bool _setv(const StringName &p_name, const Variant &p_property) override { \
+ if (m_inherits::_setv(p_name, p_property)) { \
+ return true; \
+ } \
+ if (m_class::_get_set() != m_inherits::_get_set()) { \
+ return _set(p_name, p_property); \
+ } \
+ return false; \
+ } \
+ _FORCE_INLINE_ void (Object::*_get_get_property_list() const)(List<PropertyInfo> * p_list) const { \
+ return (void (Object::*)(List<PropertyInfo> *) const) & m_class::_get_property_list; \
+ } \
+ virtual void _get_property_listv(List<PropertyInfo> *p_list, bool p_reversed) const override { \
+ 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)); \
+ if (!_is_gpl_reversed()) { \
+ ClassDB::get_property_list(#m_class, p_list, true, this); \
+ } \
+ if (m_class::_get_get_property_list() != m_inherits::_get_get_property_list()) { \
+ _get_property_list(p_list); \
+ } \
+ if (_is_gpl_reversed()) { \
+ ClassDB::get_property_list(#m_class, p_list, true, this); \
+ } \
+ if (p_reversed) { \
+ m_inherits::_get_property_listv(p_list, p_reversed); \
+ } \
+ } \
+ _FORCE_INLINE_ void (Object::*_get_notification() const)(int) { \
+ return (void (Object::*)(int)) & m_class::_notification; \
+ } \
+ virtual void _notificationv(int p_notification, bool p_reversed) override { \
+ if (!p_reversed) { \
+ m_inherits::_notificationv(p_notification, p_reversed); \
+ } \
+ if (m_class::_get_notification() != m_inherits::_get_notification()) { \
+ _notification(p_notification); \
+ } \
+ if (p_reversed) { \
+ m_inherits::_notificationv(p_notification, p_reversed); \
+ } \
+ } \
+ \
private:
#define OBJ_CATEGORY(m_category) \
@@ -394,10 +394,10 @@ protected: \
\
private:
-#define OBJ_SAVE_TYPE(m_class) \
-public: \
- virtual String get_save_class() const { return #m_class; } \
- \
+#define OBJ_SAVE_TYPE(m_class) \
+public: \
+ virtual String get_save_class() const override { return #m_class; } \
+ \
private:
class ScriptInstance;
@@ -655,10 +655,7 @@ public:
void get_method_list(List<MethodInfo> *p_list) const;
Variant callv(const StringName &p_method, const Array &p_args);
virtual Variant call(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error);
- virtual void call_multilevel(const StringName &p_method, const Variant **p_args, int p_argcount);
- virtual void call_multilevel_reversed(const StringName &p_method, const Variant **p_args, int p_argcount);
Variant call(const StringName &p_name, VARIANT_ARG_LIST); // C++ helper
- void call_multilevel(const StringName &p_name, VARIANT_ARG_LIST); // C++ helper
void notification(int p_notification, bool p_reversed = false);
String to_string();
@@ -722,7 +719,8 @@ public:
virtual void get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const;
- StringName tr(const StringName &p_message) const; // translate message (internationalization)
+ String tr(const StringName &p_message, const StringName &p_context = "") const; // translate message (internationalization)
+ String tr_n(const StringName &p_message, const StringName &p_message_plural, int p_n, const StringName &p_context = "") const;
bool _is_queued_for_deletion = false; // set to true by SceneTree::queue_delete()
bool is_queued_for_deletion() const;
diff --git a/core/os/main_loop.cpp b/core/os/main_loop.cpp
index dc68c2a9f9..434f6fa300 100644
--- a/core/os/main_loop.cpp
+++ b/core/os/main_loop.cpp
@@ -48,8 +48,10 @@ void MainLoop::_bind_methods() {
BIND_CONSTANT(NOTIFICATION_WM_ABOUT);
BIND_CONSTANT(NOTIFICATION_CRASH);
BIND_CONSTANT(NOTIFICATION_OS_IME_UPDATE);
- BIND_CONSTANT(NOTIFICATION_APP_RESUMED);
- BIND_CONSTANT(NOTIFICATION_APP_PAUSED);
+ BIND_CONSTANT(NOTIFICATION_APPLICATION_RESUMED);
+ BIND_CONSTANT(NOTIFICATION_APPLICATION_PAUSED);
+ BIND_CONSTANT(NOTIFICATION_APPLICATION_FOCUS_IN);
+ BIND_CONSTANT(NOTIFICATION_APPLICATION_FOCUS_OUT);
ADD_SIGNAL(MethodInfo("on_request_permissions_result", PropertyInfo(Variant::STRING, "permission"), PropertyInfo(Variant::BOOL, "granted")));
};
diff --git a/core/os/main_loop.h b/core/os/main_loop.h
index 90790a45a1..2c34cf193c 100644
--- a/core/os/main_loop.h
+++ b/core/os/main_loop.h
@@ -52,8 +52,10 @@ public:
NOTIFICATION_WM_ABOUT = 2011,
NOTIFICATION_CRASH = 2012,
NOTIFICATION_OS_IME_UPDATE = 2013,
- NOTIFICATION_APP_RESUMED = 2014,
- NOTIFICATION_APP_PAUSED = 2015,
+ NOTIFICATION_APPLICATION_RESUMED = 2014,
+ NOTIFICATION_APPLICATION_PAUSED = 2015,
+ NOTIFICATION_APPLICATION_FOCUS_IN = 2016,
+ NOTIFICATION_APPLICATION_FOCUS_OUT = 2017,
};
virtual void init();
diff --git a/core/os/os.cpp b/core/os/os.cpp
index c842be333c..dc8bd5ee69 100644
--- a/core/os/os.cpp
+++ b/core/os/os.cpp
@@ -41,6 +41,7 @@
#include <stdarg.h>
OS *OS::singleton = nullptr;
+uint64_t OS::target_ticks = 0;
OS *OS::get_singleton() {
return singleton;
@@ -162,6 +163,10 @@ bool OS::is_stdout_verbose() const {
return _verbose_stdout;
}
+bool OS::is_stdout_debug_enabled() const {
+ return _debug_stdout;
+}
+
void OS::dump_memory_to_file(const char *p_file) {
//Memory::dump_static_mem_to_file(p_file);
}
@@ -468,6 +473,41 @@ void OS::close_midi_inputs() {
}
}
+void OS::add_frame_delay(bool p_can_draw) {
+ const uint32_t frame_delay = Engine::get_singleton()->get_frame_delay();
+ if (frame_delay) {
+ // Add fixed frame delay to decrease CPU/GPU usage. This doesn't take
+ // the actual frame time into account.
+ // Due to the high fluctuation of the actual sleep duration, it's not recommended
+ // to use this as a FPS limiter.
+ delay_usec(frame_delay * 1000);
+ }
+
+ // Add a dynamic frame delay to decrease CPU/GPU usage. This takes the
+ // previous frame time into account for a smoother result.
+ uint64_t dynamic_delay = 0;
+ if (is_in_low_processor_usage_mode() || !p_can_draw) {
+ dynamic_delay = get_low_processor_usage_mode_sleep_usec();
+ }
+ const int target_fps = Engine::get_singleton()->get_target_fps();
+ if (target_fps > 0 && !Engine::get_singleton()->is_editor_hint()) {
+ // Override the low processor usage mode sleep delay if the target FPS is lower.
+ dynamic_delay = MAX(dynamic_delay, (uint64_t)(1000000 / target_fps));
+ }
+
+ if (dynamic_delay > 0) {
+ target_ticks += dynamic_delay;
+ uint64_t current_ticks = get_ticks_usec();
+
+ if (current_ticks < target_ticks) {
+ delay_usec(target_ticks - current_ticks);
+ }
+
+ current_ticks = get_ticks_usec();
+ target_ticks = MIN(MAX(target_ticks, current_ticks - dynamic_delay), current_ticks + dynamic_delay);
+ }
+}
+
OS::OS() {
void *volatile stack_bottom;
diff --git a/core/os/os.h b/core/os/os.h
index 04e10518dc..8e5c0c26e0 100644
--- a/core/os/os.h
+++ b/core/os/os.h
@@ -43,12 +43,14 @@
class OS {
static OS *singleton;
+ static uint64_t target_ticks;
String _execpath;
List<String> _cmdline;
bool _keep_screen_on = true; // set default value to true, because this had been true before godot 2.0.
bool low_processor_usage_mode = false;
int low_processor_usage_mode_sleep_usec = 10000;
bool _verbose_stdout = false;
+ bool _debug_stdout = false;
String _local_clipboard;
uint64_t _msec_splash;
bool _no_window = false;
@@ -84,11 +86,13 @@ public:
protected:
friend class Main;
+ // Needed by tests to setup command-line args.
+ friend int test_main(int argc, char *argv[]);
HasServerFeatureCallback has_server_feature_callback = nullptr;
RenderThreadMode _render_thread_mode = RENDER_THREAD_SAFE;
- // functions used by main to initialize/deinitialize the OS
+ // Functions used by Main to initialize/deinitialize the OS.
void add_logger(Logger *p_logger);
virtual void initialize() = 0;
@@ -212,6 +216,8 @@ public:
virtual double get_unix_time() const;
virtual void delay_usec(uint32_t p_usec) const = 0;
+ virtual void add_frame_delay(bool p_can_draw);
+
virtual uint64_t get_ticks_usec() const = 0;
uint32_t get_ticks_msec() const;
uint64_t get_splash_tick_msec() const;
@@ -219,6 +225,7 @@ public:
virtual bool is_userfs_persistent() const { return true; }
bool is_stdout_verbose() const;
+ bool is_stdout_debug_enabled() const;
virtual void disable_crash_handler() {}
virtual bool is_disable_crash_handler() const { return false; }
diff --git a/core/packed_data_container.h b/core/packed_data_container.h
index b41e9aaefc..28ec9cc87c 100644
--- a/core/packed_data_container.h
+++ b/core/packed_data_container.h
@@ -72,7 +72,7 @@ protected:
static void _bind_methods();
public:
- virtual Variant getvar(const Variant &p_key, bool *r_valid = nullptr) const;
+ virtual Variant getvar(const Variant &p_key, bool *r_valid = nullptr) const override;
Error pack(const Variant &p_data);
int size() const;
@@ -97,7 +97,7 @@ public:
bool _is_dictionary() const;
int size() const;
- virtual Variant getvar(const Variant &p_key, bool *r_valid = nullptr) const;
+ virtual Variant getvar(const Variant &p_key, bool *r_valid = nullptr) const override;
PackedDataContainerRef() {}
};
diff --git a/core/project_settings.cpp b/core/project_settings.cpp
index 9b1c146a06..efe13e740d 100644
--- a/core/project_settings.cpp
+++ b/core/project_settings.cpp
@@ -93,7 +93,7 @@ String ProjectSettings::localize_path(const String &p_path) const {
} else {
memdelete(dir);
- int sep = path.find_last("/");
+ int sep = path.rfind("/");
if (sep == -1) {
return "res://" + path;
}
@@ -122,6 +122,22 @@ void ProjectSettings::set_restart_if_changed(const String &p_name, bool p_restar
props[p_name].restart_if_changed = p_restart;
}
+void ProjectSettings::set_ignore_value_in_docs(const String &p_name, bool p_ignore) {
+ ERR_FAIL_COND_MSG(!props.has(p_name), "Request for nonexistent project setting: " + p_name + ".");
+#ifdef DEBUG_METHODS_ENABLED
+ props[p_name].ignore_value_in_docs = p_ignore;
+#endif
+}
+
+bool ProjectSettings::get_ignore_value_in_docs(const String &p_name) const {
+ ERR_FAIL_COND_V_MSG(!props.has(p_name), false, "Request for nonexistent project setting: " + p_name + ".");
+#ifdef DEBUG_METHODS_ENABLED
+ return props[p_name].ignore_value_in_docs;
+#else
+ return false;
+#endif
+}
+
String ProjectSettings::globalize_path(const String &p_path) const {
if (p_path.begins_with("res://")) {
if (resource_path != "") {
@@ -144,6 +160,12 @@ bool ProjectSettings::_set(const StringName &p_name, const Variant &p_value) {
if (p_value.get_type() == Variant::NIL) {
props.erase(p_name);
+ if (p_name.operator String().begins_with("autoload/")) {
+ String node_name = p_name.operator String().split("/")[1];
+ if (autoloads.has(node_name)) {
+ remove_autoload(node_name);
+ }
+ }
} else {
if (p_name == CoreStringNames::get_singleton()->_custom_features) {
Vector<String> custom_feature_array = String(p_value).split(",");
@@ -181,6 +203,19 @@ bool ProjectSettings::_set(const StringName &p_name, const Variant &p_value) {
} else {
props[p_name] = VariantContainer(p_value, last_order++);
}
+ if (p_name.operator String().begins_with("autoload/")) {
+ String node_name = p_name.operator String().split("/")[1];
+ AutoloadInfo autoload;
+ autoload.name = node_name;
+ String path = p_value;
+ if (path.begins_with("*")) {
+ autoload.is_singleton = true;
+ autoload.path = path.substr(1);
+ } else {
+ autoload.path = path;
+ }
+ add_autoload(autoload);
+ }
}
return true;
@@ -469,6 +504,14 @@ Error ProjectSettings::setup(const String &p_path, const String &p_main_pack, bo
_load_settings_text(custom_settings);
}
}
+ // Using GLOBAL_GET on every block for compressing can be slow, so assigning here.
+ Compression::zstd_long_distance_matching = GLOBAL_GET("compression/formats/zstd/long_distance_matching");
+ Compression::zstd_level = GLOBAL_GET("compression/formats/zstd/compression_level");
+ Compression::zstd_window_log_size = GLOBAL_GET("compression/formats/zstd/window_log_size");
+
+ Compression::zlib_level = GLOBAL_GET("compression/formats/zlib/compression_level");
+
+ Compression::gzip_level = GLOBAL_GET("compression/formats/gzip/compression_level");
return err;
}
@@ -616,6 +659,12 @@ void ProjectSettings::set_builtin_order(const String &p_name) {
}
}
+bool ProjectSettings::is_builtin_setting(const String &p_name) const {
+ // Return true because a false negative is worse than a false positive.
+ ERR_FAIL_COND_V_MSG(!props.has(p_name), true, "Request for nonexistent project setting: " + p_name + ".");
+ return props[p_name].order < NO_BUILTIN_ORDER_BASE;
+}
+
void ProjectSettings::clear(const String &p_name) {
ERR_FAIL_COND_MSG(!props.has(p_name), "Request for nonexistent project setting: " + p_name + ".");
props.erase(p_name);
@@ -849,7 +898,7 @@ Error ProjectSettings::save_custom(const String &p_path, const CustomMap &p_cust
}
}
-Variant _GLOBAL_DEF(const String &p_var, const Variant &p_default, bool p_restart_if_changed) {
+Variant _GLOBAL_DEF(const String &p_var, const Variant &p_default, bool p_restart_if_changed, bool p_ignore_value_in_docs) {
Variant ret;
if (!ProjectSettings::get_singleton()->has_setting(p_var)) {
ProjectSettings::get_singleton()->set(p_var, p_default);
@@ -859,6 +908,7 @@ Variant _GLOBAL_DEF(const String &p_var, const Variant &p_default, bool p_restar
ProjectSettings::get_singleton()->set_initial_value(p_var, p_default);
ProjectSettings::get_singleton()->set_builtin_order(p_var);
ProjectSettings::get_singleton()->set_restart_if_changed(p_var, p_restart_if_changed);
+ ProjectSettings::get_singleton()->set_ignore_value_in_docs(p_var, p_ignore_value_in_docs);
return ret;
}
@@ -945,6 +995,29 @@ bool ProjectSettings::has_custom_feature(const String &p_feature) const {
return custom_features.has(p_feature);
}
+Map<StringName, ProjectSettings::AutoloadInfo> ProjectSettings::get_autoload_list() const {
+ return autoloads;
+}
+
+void ProjectSettings::add_autoload(const AutoloadInfo &p_autoload) {
+ ERR_FAIL_COND_MSG(p_autoload.name == StringName(), "Trying to add autoload with no name.");
+ autoloads[p_autoload.name] = p_autoload;
+}
+
+void ProjectSettings::remove_autoload(const StringName &p_autoload) {
+ ERR_FAIL_COND_MSG(!autoloads.has(p_autoload), "Trying to remove non-existent autoload.");
+ autoloads.erase(p_autoload);
+}
+
+bool ProjectSettings::has_autoload(const StringName &p_autoload) const {
+ return autoloads.has(p_autoload);
+}
+
+ProjectSettings::AutoloadInfo ProjectSettings::get_autoload(const StringName &p_name) const {
+ ERR_FAIL_COND_V_MSG(!autoloads.has(p_name), AutoloadInfo(), "Trying to get non-existent autoload.");
+ return autoloads[p_name];
+}
+
void ProjectSettings::_bind_methods() {
ClassDB::bind_method(D_METHOD("has_setting", "name"), &ProjectSettings::has_setting);
ClassDB::bind_method(D_METHOD("set_setting", "name", "value"), &ProjectSettings::set_setting);
@@ -965,6 +1038,9 @@ void ProjectSettings::_bind_methods() {
}
ProjectSettings::ProjectSettings() {
+ // Initialization of engine variables should be done in the setup() method,
+ // so that the values can be overridden from project.godot or project.binary.
+
singleton = this;
Array events;
@@ -1164,18 +1240,17 @@ ProjectSettings::ProjectSettings() {
GLOBAL_DEF("debug/settings/profiler/max_functions", 16384);
custom_prop_info["debug/settings/profiler/max_functions"] = PropertyInfo(Variant::INT, "debug/settings/profiler/max_functions", PROPERTY_HINT_RANGE, "128,65535,1");
- //assigning here, because using GLOBAL_GET on every block for compressing can be slow
- Compression::zstd_long_distance_matching = GLOBAL_DEF("compression/formats/zstd/long_distance_matching", false);
+ GLOBAL_DEF("compression/formats/zstd/long_distance_matching", Compression::zstd_long_distance_matching);
custom_prop_info["compression/formats/zstd/long_distance_matching"] = PropertyInfo(Variant::BOOL, "compression/formats/zstd/long_distance_matching");
- Compression::zstd_level = GLOBAL_DEF("compression/formats/zstd/compression_level", 3);
+ GLOBAL_DEF("compression/formats/zstd/compression_level", Compression::zstd_level);
custom_prop_info["compression/formats/zstd/compression_level"] = PropertyInfo(Variant::INT, "compression/formats/zstd/compression_level", PROPERTY_HINT_RANGE, "1,22,1");
- Compression::zstd_window_log_size = GLOBAL_DEF("compression/formats/zstd/window_log_size", 27);
+ GLOBAL_DEF("compression/formats/zstd/window_log_size", Compression::zstd_window_log_size);
custom_prop_info["compression/formats/zstd/window_log_size"] = PropertyInfo(Variant::INT, "compression/formats/zstd/window_log_size", PROPERTY_HINT_RANGE, "10,30,1");
- Compression::zlib_level = GLOBAL_DEF("compression/formats/zlib/compression_level", Z_DEFAULT_COMPRESSION);
+ GLOBAL_DEF("compression/formats/zlib/compression_level", Compression::zlib_level);
custom_prop_info["compression/formats/zlib/compression_level"] = PropertyInfo(Variant::INT, "compression/formats/zlib/compression_level", PROPERTY_HINT_RANGE, "-1,9,1");
- Compression::gzip_level = GLOBAL_DEF("compression/formats/gzip/compression_level", Z_DEFAULT_COMPRESSION);
+ GLOBAL_DEF("compression/formats/gzip/compression_level", Compression::gzip_level);
custom_prop_info["compression/formats/gzip/compression_level"] = PropertyInfo(Variant::INT, "compression/formats/gzip/compression_level", PROPERTY_HINT_RANGE, "-1,9,1");
}
diff --git a/core/project_settings.h b/core/project_settings.h
index d579d44039..29b2406dd2 100644
--- a/core/project_settings.h
+++ b/core/project_settings.h
@@ -47,6 +47,12 @@ public:
NO_BUILTIN_ORDER_BASE = 1 << 16
};
+ struct AutoloadInfo {
+ StringName name;
+ String path;
+ bool is_singleton = false;
+ };
+
protected:
struct VariantContainer {
int order = 0;
@@ -56,6 +62,9 @@ protected:
bool hide_from_editor = false;
bool overridden = false;
bool restart_if_changed = false;
+#ifdef DEBUG_METHODS_ENABLED
+ bool ignore_value_in_docs = false;
+#endif
VariantContainer() {}
@@ -67,8 +76,8 @@ protected:
};
bool registering_order = true;
- int last_order = 0;
- int last_builtin_order = NO_BUILTIN_ORDER_BASE;
+ int last_order = NO_BUILTIN_ORDER_BASE;
+ int last_builtin_order = 0;
Map<StringName, VariantContainer> props;
String resource_path;
Map<StringName, PropertyInfo> custom_prop_info;
@@ -79,6 +88,8 @@ protected:
Set<String> custom_features;
Map<StringName, StringName> feature_overrides;
+ Map<StringName, AutoloadInfo> autoloads;
+
bool _set(const StringName &p_name, const Variant &p_value);
bool _get(const StringName &p_name, Variant &r_ret) const;
void _get_property_list(List<PropertyInfo> *p_list) const;
@@ -117,6 +128,9 @@ public:
void set_initial_value(const String &p_name, const Variant &p_value);
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;
+
bool property_can_revert(const String &p_name);
Variant property_get_revert(const String &p_name);
@@ -128,6 +142,7 @@ public:
int get_order(const String &p_name) const;
void set_order(const String &p_name, int p_order);
void set_builtin_order(const String &p_name);
+ bool is_builtin_setting(const String &p_name) const;
Error setup(const String &p_path, const String &p_main_pack, bool p_upwards = false);
@@ -148,14 +163,22 @@ public:
bool has_custom_feature(const String &p_feature) const;
+ Map<StringName, AutoloadInfo> get_autoload_list() const;
+ void add_autoload(const AutoloadInfo &p_autoload);
+ void remove_autoload(const StringName &p_autoload);
+ bool has_autoload(const StringName &p_autoload) const;
+ AutoloadInfo get_autoload(const StringName &p_name) const;
+
ProjectSettings();
~ProjectSettings();
};
//not a macro any longer
-Variant _GLOBAL_DEF(const String &p_var, const Variant &p_default, bool p_restart_if_changed = false);
+Variant _GLOBAL_DEF(const String &p_var, const Variant &p_default, bool p_restart_if_changed = false, bool p_ignore_value_in_docs = false);
#define GLOBAL_DEF(m_var, m_value) _GLOBAL_DEF(m_var, m_value)
#define GLOBAL_DEF_RST(m_var, m_value) _GLOBAL_DEF(m_var, m_value, true)
+#define GLOBAL_DEF_NOVAL(m_var, m_value) _GLOBAL_DEF(m_var, m_value, false, true)
+#define GLOBAL_DEF_RST_NOVAL(m_var, m_value) _GLOBAL_DEF(m_var, m_value, true, true)
#define GLOBAL_GET(m_var) ProjectSettings::get_singleton()->get(m_var)
#endif // PROJECT_SETTINGS_H
diff --git a/core/register_core_types.cpp b/core/register_core_types.cpp
index 5dac42cacb..4f094dd6c6 100644
--- a/core/register_core_types.cpp
+++ b/core/register_core_types.cpp
@@ -86,6 +86,7 @@ static _Engine *_engine = nullptr;
static _ClassDB *_classdb = nullptr;
static _Marshalls *_marshalls = nullptr;
static _JSON *_json = nullptr;
+static _EngineDebugger *_engine_debugger = nullptr;
static IP *ip = nullptr;
@@ -227,6 +228,7 @@ void register_core_types() {
_classdb = memnew(_ClassDB);
_marshalls = memnew(_Marshalls);
_json = memnew(_JSON);
+ _engine_debugger = memnew(_EngineDebugger);
}
void register_core_settings() {
@@ -256,6 +258,7 @@ void register_core_singletons() {
ClassDB::register_class<InputMap>();
ClassDB::register_class<_JSON>();
ClassDB::register_class<Expression>();
+ ClassDB::register_class<_EngineDebugger>();
Engine::get_singleton()->add_singleton(Engine::Singleton("ProjectSettings", ProjectSettings::get_singleton()));
Engine::get_singleton()->add_singleton(Engine::Singleton("IP", IP::get_singleton()));
@@ -271,6 +274,7 @@ void register_core_singletons() {
Engine::get_singleton()->add_singleton(Engine::Singleton("Input", Input::get_singleton()));
Engine::get_singleton()->add_singleton(Engine::Singleton("InputMap", InputMap::get_singleton()));
Engine::get_singleton()->add_singleton(Engine::Singleton("JSON", _JSON::get_singleton()));
+ Engine::get_singleton()->add_singleton(Engine::Singleton("EngineDebugger", _EngineDebugger::get_singleton()));
}
void unregister_core_types() {
@@ -281,6 +285,7 @@ void unregister_core_types() {
memdelete(_classdb);
memdelete(_marshalls);
memdelete(_json);
+ memdelete(_engine_debugger);
memdelete(_geometry_2d);
memdelete(_geometry_3d);
diff --git a/core/resource.h b/core/resource.h
index ad2f3ce913..7699d48735 100644
--- a/core/resource.h
+++ b/core/resource.h
@@ -40,15 +40,19 @@
#define RES_BASE_EXTENSION(m_ext) \
public: \
static void register_custom_data_to_otdb() { ClassDB::add_resource_base_extension(m_ext, get_class_static()); } \
- virtual String get_base_extension() const { return m_ext; } \
+ virtual String get_base_extension() const override { return m_ext; } \
\
private:
class Resource : public Reference {
GDCLASS(Resource, Reference);
OBJ_CATEGORY("Resources");
- RES_BASE_EXTENSION("res");
+public:
+ static void register_custom_data_to_otdb() { ClassDB::add_resource_base_extension("res", get_class_static()); }
+ virtual String get_base_extension() const { return "res"; }
+
+private:
Set<ObjectID> owners;
friend class ResBase;
diff --git a/core/rid_owner.h b/core/rid_owner.h
index 2489475c68..30f1e41733 100644
--- a/core/rid_owner.h
+++ b/core/rid_owner.h
@@ -236,7 +236,7 @@ public:
}
_FORCE_INLINE_ T *get_ptr_by_index(uint32_t p_index) {
- ERR_FAIL_INDEX_V(p_index, alloc_count, nullptr);
+ ERR_FAIL_UNSIGNED_INDEX_V(p_index, alloc_count, nullptr);
if (THREAD_SAFE) {
spin_lock.lock();
}
diff --git a/core/script_language.cpp b/core/script_language.cpp
index 38a970f3c6..b63aeb952c 100644
--- a/core/script_language.cpp
+++ b/core/script_language.cpp
@@ -308,16 +308,6 @@ Variant ScriptInstance::call(const StringName &p_method, VARIANT_ARG_DECLARE) {
return call(p_method, argptr, argc, error);
}
-void ScriptInstance::call_multilevel(const StringName &p_method, const Variant **p_args, int p_argcount) {
- Callable::CallError ce;
- call(p_method, p_args, p_argcount, ce); // script may not support multilevel calls
-}
-
-void ScriptInstance::call_multilevel_reversed(const StringName &p_method, const Variant **p_args, int p_argcount) {
- Callable::CallError ce;
- call(p_method, p_args, p_argcount, ce); // script may not support multilevel calls
-}
-
void ScriptInstance::property_set_fallback(const StringName &, const Variant &, bool *r_valid) {
if (r_valid) {
*r_valid = false;
@@ -331,19 +321,6 @@ Variant ScriptInstance::property_get_fallback(const StringName &, bool *r_valid)
return Variant();
}
-void ScriptInstance::call_multilevel(const StringName &p_method, VARIANT_ARG_DECLARE) {
- VARIANT_ARGPTRS;
- int argc = 0;
- for (int i = 0; i < VARIANT_ARG_MAX; i++) {
- if (argptr[i]->get_type() == Variant::NIL) {
- break;
- }
- argc++;
- }
-
- call_multilevel(p_method, argptr, argc);
-}
-
ScriptInstance::~ScriptInstance() {
}
@@ -352,6 +329,39 @@ ScriptCodeCompletionCache::ScriptCodeCompletionCache() {
singleton = this;
}
+void ScriptLanguage::get_core_type_words(List<String> *p_core_type_words) const {
+ p_core_type_words->push_back("String");
+ p_core_type_words->push_back("Vector2");
+ p_core_type_words->push_back("Vector2i");
+ p_core_type_words->push_back("Rect2");
+ p_core_type_words->push_back("Rect2i");
+ p_core_type_words->push_back("Vector3");
+ p_core_type_words->push_back("Vector3i");
+ p_core_type_words->push_back("Transform2D");
+ p_core_type_words->push_back("Plane");
+ p_core_type_words->push_back("Quat");
+ p_core_type_words->push_back("AABB");
+ p_core_type_words->push_back("Basis");
+ p_core_type_words->push_back("Transform");
+ p_core_type_words->push_back("Color");
+ p_core_type_words->push_back("StringName");
+ p_core_type_words->push_back("NodePath");
+ p_core_type_words->push_back("RID");
+ p_core_type_words->push_back("Callable");
+ p_core_type_words->push_back("Signal");
+ p_core_type_words->push_back("Dictionary");
+ p_core_type_words->push_back("Array");
+ p_core_type_words->push_back("PackedByteArray");
+ p_core_type_words->push_back("PackedInt32Array");
+ p_core_type_words->push_back("PackedInt64Array");
+ p_core_type_words->push_back("PackedFloat32Array");
+ p_core_type_words->push_back("PackedFloat64Array");
+ p_core_type_words->push_back("PackedStringArray");
+ p_core_type_words->push_back("PackedVector2Array");
+ p_core_type_words->push_back("PackedVector3Array");
+ p_core_type_words->push_back("PackedColorArray");
+}
+
void ScriptLanguage::frame() {
}
diff --git a/core/script_language.h b/core/script_language.h
index b6c2a47245..aa7014ed3e 100644
--- a/core/script_language.h
+++ b/core/script_language.h
@@ -116,7 +116,7 @@ class Script : public Resource {
OBJ_SAVE_TYPE(Script);
protected:
- virtual bool editor_can_reload_from_file() { return false; } // this is handled by editor better
+ virtual bool editor_can_reload_from_file() override { return false; } // this is handled by editor better
void _notification(int p_what);
static void _bind_methods();
@@ -199,9 +199,6 @@ public:
virtual bool has_method(const StringName &p_method) const = 0;
virtual Variant call(const StringName &p_method, VARIANT_ARG_LIST);
virtual Variant call(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) = 0;
- virtual void call_multilevel(const StringName &p_method, VARIANT_ARG_LIST);
- virtual void call_multilevel(const StringName &p_method, const Variant **p_args, int p_argcount);
- virtual void call_multilevel_reversed(const StringName &p_method, const Variant **p_args, int p_argcount);
virtual void notification(int p_notification) = 0;
virtual String to_string(bool *r_valid) {
if (r_valid) {
@@ -256,6 +253,7 @@ struct ScriptCodeCompletionOption {
Kind kind = KIND_PLAIN_TEXT;
String display;
String insert_text;
+ Color font_color;
RES icon;
ScriptCodeCompletionOption() {}
@@ -293,12 +291,14 @@ public:
/* EDITOR FUNCTIONS */
struct Warning {
- int line;
+ int start_line = -1, end_line = -1;
+ int leftmost_column = -1, rightmost_column = -1;
int code;
String string_code;
String message;
};
+ void get_core_type_words(List<String> *p_core_type_words) const;
virtual void get_reserved_words(List<String> *p_words) const = 0;
virtual void get_comment_delimiters(List<String> *p_delimiters) const = 0;
virtual void get_string_delimiters(List<String> *p_delimiters) const = 0;
@@ -425,8 +425,6 @@ public:
r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
return Variant();
}
- //virtual void call_multilevel(const StringName& p_method,VARIANT_ARG_LIST) { return Variant(); }
- //virtual void call_multilevel(const StringName& p_method,const Variant** p_args,int p_argcount,Callable::CallError &r_error) { return Variant(); }
virtual void notification(int p_notification) {}
virtual Ref<Script> get_script() const { return script; }
diff --git a/core/string_buffer.h b/core/string_buffer.h
index 956a6333d9..f9cf31075a 100644
--- a/core/string_buffer.h
+++ b/core/string_buffer.h
@@ -150,7 +150,7 @@ String StringBuffer<SHORT_BUFFER_SIZE>::as_string() {
template <int SHORT_BUFFER_SIZE>
double StringBuffer<SHORT_BUFFER_SIZE>::as_double() {
current_buffer_ptr()[string_length] = '\0';
- return String::to_double(current_buffer_ptr());
+ return String::to_float(current_buffer_ptr());
}
template <int SHORT_BUFFER_SIZE>
diff --git a/core/string_name.h b/core/string_name.h
index df6b458581..886ddd0ee7 100644
--- a/core/string_name.h
+++ b/core/string_name.h
@@ -35,6 +35,8 @@
#include "core/safe_refcount.h"
#include "core/ustring.h"
+class Main;
+
struct StaticCString {
const char *ptr;
static StaticCString create(const char *p_ptr);
@@ -73,7 +75,7 @@ class StringName {
void unref();
friend void register_core_types();
friend void unregister_core_types();
-
+ friend class Main;
static Mutex mutex;
static void setup();
static void cleanup();
diff --git a/core/translation.cpp b/core/translation.cpp
index 4f835bd7b4..8c8ca06740 100644
--- a/core/translation.cpp
+++ b/core/translation.cpp
@@ -794,17 +794,12 @@ static const char *locale_renames[][2] = {
///////////////////////////////////////////////
-Vector<String> Translation::_get_messages() const {
- Vector<String> msgs;
- msgs.resize(translation_map.size() * 2);
- int idx = 0;
+Dictionary Translation::_get_messages() const {
+ Dictionary d;
for (const Map<StringName, StringName>::Element *E = translation_map.front(); E; E = E->next()) {
- msgs.set(idx + 0, E->key());
- msgs.set(idx + 1, E->get());
- idx += 2;
+ d[E->key()] = E->value();
}
-
- return msgs;
+ return d;
}
Vector<String> Translation::_get_message_list() const {
@@ -819,14 +814,11 @@ Vector<String> Translation::_get_message_list() const {
return msgs;
}
-void Translation::_set_messages(const Vector<String> &p_messages) {
- int msg_count = p_messages.size();
- ERR_FAIL_COND(msg_count % 2);
-
- const String *r = p_messages.ptr();
-
- for (int i = 0; i < msg_count; i += 2) {
- add_message(r[i + 0], r[i + 1]);
+void Translation::_set_messages(const Dictionary &p_messages) {
+ List<Variant> keys;
+ p_messages.get_key_list(&keys);
+ for (auto E = keys.front(); E; E = E->next()) {
+ translation_map[E->get()] = p_messages[E->get()];
}
}
@@ -848,11 +840,21 @@ void Translation::set_locale(const String &p_locale) {
}
}
-void Translation::add_message(const StringName &p_src_text, const StringName &p_xlated_text) {
+void Translation::add_message(const StringName &p_src_text, const StringName &p_xlated_text, const StringName &p_context) {
translation_map[p_src_text] = p_xlated_text;
}
-StringName Translation::get_message(const StringName &p_src_text) const {
+void Translation::add_plural_message(const StringName &p_src_text, const Vector<String> &p_plural_xlated_texts, const StringName &p_context) {
+ WARN_PRINT("Translation class doesn't handle plural messages. Calling add_plural_message() on a Translation instance is probably a mistake. \nUse a derived Translation class that handles plurals, such as TranslationPO class");
+ ERR_FAIL_COND_MSG(p_plural_xlated_texts.empty(), "Parameter vector p_plural_xlated_texts passed in is empty.");
+ translation_map[p_src_text] = p_plural_xlated_texts[0];
+}
+
+StringName Translation::get_message(const StringName &p_src_text, const StringName &p_context) const {
+ if (p_context != StringName()) {
+ WARN_PRINT("Translation class doesn't handle context. Using context in get_message() on a Translation instance is probably a mistake. \nUse a derived Translation class that handles context, such as TranslationPO class");
+ }
+
const Map<StringName, StringName>::Element *E = translation_map.find(p_src_text);
if (!E) {
return StringName();
@@ -861,7 +863,16 @@ StringName Translation::get_message(const StringName &p_src_text) const {
return E->get();
}
-void Translation::erase_message(const StringName &p_src_text) {
+StringName Translation::get_plural_message(const StringName &p_src_text, const StringName &p_plural_text, int p_n, const StringName &p_context) const {
+ WARN_PRINT("Translation class doesn't handle plural messages. Calling get_plural_message() on a Translation instance is probably a mistake. \nUse a derived Translation class that handles plurals, such as TranslationPO class");
+ return get_message(p_src_text);
+}
+
+void Translation::erase_message(const StringName &p_src_text, const StringName &p_context) {
+ if (p_context != StringName()) {
+ WARN_PRINT("Translation class doesn't handle context. Using context in erase_message() on a Translation instance is probably a mistake. \nUse a derived Translation class that handles context, such as TranslationPO class");
+ }
+
translation_map.erase(p_src_text);
}
@@ -878,15 +889,17 @@ int Translation::get_message_count() const {
void Translation::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_locale", "locale"), &Translation::set_locale);
ClassDB::bind_method(D_METHOD("get_locale"), &Translation::get_locale);
- ClassDB::bind_method(D_METHOD("add_message", "src_message", "xlated_message"), &Translation::add_message);
- ClassDB::bind_method(D_METHOD("get_message", "src_message"), &Translation::get_message);
- ClassDB::bind_method(D_METHOD("erase_message", "src_message"), &Translation::erase_message);
+ ClassDB::bind_method(D_METHOD("add_message", "src_message", "xlated_message", "context"), &Translation::add_message, DEFVAL(""));
+ ClassDB::bind_method(D_METHOD("add_plural_message", "src_message", "xlated_messages", "context"), &Translation::add_plural_message, DEFVAL(""));
+ ClassDB::bind_method(D_METHOD("get_message", "src_message", "context"), &Translation::get_message, DEFVAL(""));
+ ClassDB::bind_method(D_METHOD("get_plural_message", "src_message", "src_plural_message", "n", "context"), &Translation::get_plural_message, DEFVAL(""));
+ ClassDB::bind_method(D_METHOD("erase_message", "src_message", "context"), &Translation::erase_message, DEFVAL(""));
ClassDB::bind_method(D_METHOD("get_message_list"), &Translation::_get_message_list);
ClassDB::bind_method(D_METHOD("get_message_count"), &Translation::get_message_count);
ClassDB::bind_method(D_METHOD("_set_messages"), &Translation::_set_messages);
ClassDB::bind_method(D_METHOD("_get_messages"), &Translation::_get_messages);
- ADD_PROPERTY(PropertyInfo(Variant::PACKED_STRING_ARRAY, "messages", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_messages", "_get_messages");
+ ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "messages", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_messages", "_get_messages");
ADD_PROPERTY(PropertyInfo(Variant::STRING, "locale"), "set_locale", "get_locale");
}
@@ -1020,11 +1033,35 @@ void TranslationServer::remove_translation(const Ref<Translation> &p_translation
translations.erase(p_translation);
}
+Ref<Translation> TranslationServer::get_translation_object(const String &p_locale) {
+ Ref<Translation> res;
+ String lang = get_language_code(p_locale);
+ bool near_match_found = false;
+
+ for (const Set<Ref<Translation>>::Element *E = translations.front(); E; E = E->next()) {
+ const Ref<Translation> &t = E->get();
+ ERR_FAIL_COND_V(t.is_null(), nullptr);
+ String l = t->get_locale();
+
+ // Exact match.
+ if (l == p_locale) {
+ return t;
+ }
+
+ // If near match found, keep that match, but keep looking to try to look for perfect match.
+ if (get_language_code(l) == lang && !near_match_found) {
+ res = t;
+ near_match_found = true;
+ }
+ }
+ return res;
+}
+
void TranslationServer::clear() {
translations.clear();
}
-StringName TranslationServer::translate(const StringName &p_message) const {
+StringName TranslationServer::translate(const StringName &p_message, const StringName &p_context) const {
// Match given message against the translation catalog for the project locale.
if (!enabled) {
@@ -1033,6 +1070,46 @@ StringName TranslationServer::translate(const StringName &p_message) const {
ERR_FAIL_COND_V_MSG(locale.length() < 2, p_message, "Could not translate message as configured locale '" + locale + "' is invalid.");
+ StringName res = _get_message_from_translations(p_message, p_context, locale, false);
+
+ if (!res && fallback.length() >= 2) {
+ res = _get_message_from_translations(p_message, p_context, fallback, false);
+ }
+
+ if (!res) {
+ return p_message;
+ }
+
+ return res;
+}
+
+StringName TranslationServer::translate_plural(const StringName &p_message, const StringName &p_message_plural, int p_n, const StringName &p_context) const {
+ if (!enabled) {
+ if (p_n == 1) {
+ return p_message;
+ }
+ return p_message_plural;
+ }
+
+ ERR_FAIL_COND_V_MSG(locale.length() < 2, p_message, "Could not translate message as configured locale '" + locale + "' is invalid.");
+
+ StringName res = _get_message_from_translations(p_message, p_context, locale, true, p_message_plural, p_n);
+
+ if (!res && fallback.length() >= 2) {
+ res = _get_message_from_translations(p_message, p_context, fallback, true, p_message_plural, p_n);
+ }
+
+ if (!res) {
+ if (p_n == 1) {
+ return p_message;
+ }
+ return p_message_plural;
+ }
+
+ return res;
+}
+
+StringName TranslationServer::_get_message_from_translations(const StringName &p_message, const StringName &p_context, const String &p_locale, bool plural, const String &p_message_plural, int p_n) const {
// Locale can be of the form 'll_CC', i.e. language code and regional code,
// e.g. 'en_US', 'en_GB', etc. It might also be simply 'll', e.g. 'en'.
// To find the relevant translation, we look for those with locale starting
@@ -1044,7 +1121,7 @@ StringName TranslationServer::translate(const StringName &p_message) const {
// logic, so be sure to propagate changes there when changing things here.
StringName res;
- String lang = get_language_code(locale);
+ String lang = get_language_code(p_locale);
bool near_match = false;
for (const Set<Ref<Translation>>::Element *E = translations.front(); E; E = E->next()) {
@@ -1052,7 +1129,7 @@ StringName TranslationServer::translate(const StringName &p_message) const {
ERR_FAIL_COND_V(t.is_null(), p_message);
String l = t->get_locale();
- bool exact_match = (l == locale);
+ bool exact_match = (l == p_locale);
if (!exact_match) {
if (near_match) {
continue; // Only near-match once, but keep looking for exact matches.
@@ -1062,7 +1139,13 @@ StringName TranslationServer::translate(const StringName &p_message) const {
}
}
- StringName r = t->get_message(p_message);
+ StringName r;
+ if (!plural) {
+ r = t->get_message(p_message, p_context);
+ } else {
+ r = t->get_plural_message(p_message, p_message_plural, p_n, p_context);
+ }
+
if (!r) {
continue;
}
@@ -1075,44 +1158,6 @@ StringName TranslationServer::translate(const StringName &p_message) const {
}
}
- if (!res && fallback.length() >= 2) {
- // Try again with the fallback locale.
- String fallback_lang = get_language_code(fallback);
- near_match = false;
-
- for (const Set<Ref<Translation>>::Element *E = translations.front(); E; E = E->next()) {
- const Ref<Translation> &t = E->get();
- ERR_FAIL_COND_V(t.is_null(), p_message);
- String l = t->get_locale();
-
- bool exact_match = (l == fallback);
- if (!exact_match) {
- if (near_match) {
- continue; // Only near-match once, but keep looking for exact matches.
- }
- if (get_language_code(l) != fallback_lang) {
- continue; // Language code does not match.
- }
- }
-
- StringName r = t->get_message(p_message);
- if (!r) {
- continue;
- }
- res = r;
-
- if (exact_match) {
- break;
- } else {
- near_match = true;
- }
- }
- }
-
- if (!res) {
- return p_message;
- }
-
return res;
}
@@ -1169,9 +1214,9 @@ void TranslationServer::set_tool_translation(const Ref<Translation> &p_translati
tool_translation = p_translation;
}
-StringName TranslationServer::tool_translate(const StringName &p_message) const {
+StringName TranslationServer::tool_translate(const StringName &p_message, const StringName &p_context) const {
if (tool_translation.is_valid()) {
- StringName r = tool_translation->get_message(p_message);
+ StringName r = tool_translation->get_message(p_message, p_context);
if (r) {
return r;
}
@@ -1179,13 +1224,27 @@ StringName TranslationServer::tool_translate(const StringName &p_message) const
return p_message;
}
+StringName TranslationServer::tool_translate_plural(const StringName &p_message, const StringName &p_message_plural, int p_n, const StringName &p_context) const {
+ if (tool_translation.is_valid()) {
+ StringName r = tool_translation->get_plural_message(p_message, p_message_plural, p_n, p_context);
+ if (r) {
+ return r;
+ }
+ }
+
+ if (p_n == 1) {
+ return p_message;
+ }
+ return p_message_plural;
+}
+
void TranslationServer::set_doc_translation(const Ref<Translation> &p_translation) {
doc_translation = p_translation;
}
-StringName TranslationServer::doc_translate(const StringName &p_message) const {
+StringName TranslationServer::doc_translate(const StringName &p_message, const StringName &p_context) const {
if (doc_translation.is_valid()) {
- StringName r = doc_translation->get_message(p_message);
+ StringName r = doc_translation->get_message(p_message, p_context);
if (r) {
return r;
}
@@ -1193,16 +1252,32 @@ StringName TranslationServer::doc_translate(const StringName &p_message) const {
return p_message;
}
+StringName TranslationServer::doc_translate_plural(const StringName &p_message, const StringName &p_message_plural, int p_n, const StringName &p_context) const {
+ if (doc_translation.is_valid()) {
+ StringName r = doc_translation->get_plural_message(p_message, p_message_plural, p_n, p_context);
+ if (r) {
+ return r;
+ }
+ }
+
+ if (p_n == 1) {
+ return p_message;
+ }
+ return p_message_plural;
+}
+
void TranslationServer::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_locale", "locale"), &TranslationServer::set_locale);
ClassDB::bind_method(D_METHOD("get_locale"), &TranslationServer::get_locale);
ClassDB::bind_method(D_METHOD("get_locale_name", "locale"), &TranslationServer::get_locale_name);
- ClassDB::bind_method(D_METHOD("translate", "message"), &TranslationServer::translate);
+ ClassDB::bind_method(D_METHOD("translate", "message", "context"), &TranslationServer::translate, DEFVAL(""));
+ ClassDB::bind_method(D_METHOD("translate_plural", "message", "plural_message", "n", "context"), &TranslationServer::translate_plural, DEFVAL(""));
ClassDB::bind_method(D_METHOD("add_translation", "translation"), &TranslationServer::add_translation);
ClassDB::bind_method(D_METHOD("remove_translation", "translation"), &TranslationServer::remove_translation);
+ ClassDB::bind_method(D_METHOD("get_translation_object", "locale"), &TranslationServer::get_translation_object);
ClassDB::bind_method(D_METHOD("clear"), &TranslationServer::clear);
diff --git a/core/translation.h b/core/translation.h
index 4f50a1a4bc..cba25a434f 100644
--- a/core/translation.h
+++ b/core/translation.h
@@ -41,10 +41,9 @@ class Translation : public Resource {
String locale = "en";
Map<StringName, StringName> translation_map;
- Vector<String> _get_message_list() const;
-
- Vector<String> _get_messages() const;
- void _set_messages(const Vector<String> &p_messages);
+ virtual Vector<String> _get_message_list() const;
+ virtual Dictionary _get_messages() const;
+ virtual void _set_messages(const Dictionary &p_messages);
protected:
static void _bind_methods();
@@ -53,12 +52,13 @@ public:
void set_locale(const String &p_locale);
_FORCE_INLINE_ String get_locale() const { return locale; }
- void add_message(const StringName &p_src_text, const StringName &p_xlated_text);
- virtual StringName get_message(const StringName &p_src_text) const; //overridable for other implementations
- void erase_message(const StringName &p_src_text);
-
- void get_message_list(List<StringName> *r_messages) const;
- int get_message_count() const;
+ virtual void add_message(const StringName &p_src_text, const StringName &p_xlated_text, const StringName &p_context = "");
+ virtual void add_plural_message(const StringName &p_src_text, const Vector<String> &p_plural_xlated_texts, const StringName &p_context = "");
+ virtual StringName get_message(const StringName &p_src_text, const StringName &p_context = "") const; //overridable for other implementations
+ virtual StringName get_plural_message(const StringName &p_src_text, const StringName &p_plural_text, int p_n, const StringName &p_context = "") const;
+ virtual void erase_message(const StringName &p_src_text, const StringName &p_context = "");
+ virtual void get_message_list(List<StringName> *r_messages) const;
+ virtual int get_message_count() const;
Translation() {}
};
@@ -80,6 +80,8 @@ class TranslationServer : public Object {
static TranslationServer *singleton;
bool _load_translations(const String &p_from);
+ StringName _get_message_from_translations(const StringName &p_message, const StringName &p_context, const String &p_locale, bool plural, const String &p_message_plural = "", int p_n = 0) const;
+
static void _bind_methods();
public:
@@ -90,6 +92,7 @@ public:
void set_locale(const String &p_locale);
String get_locale() const;
+ Ref<Translation> get_translation_object(const String &p_locale);
String get_locale_name(const String &p_locale) const;
@@ -98,7 +101,8 @@ public:
void add_translation(const Ref<Translation> &p_translation);
void remove_translation(const Ref<Translation> &p_translation);
- StringName translate(const StringName &p_message) const;
+ StringName translate(const StringName &p_message, const StringName &p_context = "") const;
+ StringName translate_plural(const StringName &p_message, const StringName &p_message_plural, int p_n, const StringName &p_context = "") const;
static Vector<String> get_all_locales();
static Vector<String> get_all_locale_names();
@@ -107,9 +111,11 @@ public:
static String get_language_code(const String &p_locale);
void set_tool_translation(const Ref<Translation> &p_translation);
- StringName tool_translate(const StringName &p_message) const;
+ StringName tool_translate(const StringName &p_message, const StringName &p_context = "") const;
+ StringName tool_translate_plural(const StringName &p_message, const StringName &p_message_plural, int p_n, const StringName &p_context = "") const;
void set_doc_translation(const Ref<Translation> &p_translation);
- StringName doc_translate(const StringName &p_message) const;
+ StringName doc_translate(const StringName &p_message, const StringName &p_context = "") const;
+ StringName doc_translate_plural(const StringName &p_message, const StringName &p_message_plural, int p_n, const StringName &p_context = "") const;
void setup();
diff --git a/core/translation_po.cpp b/core/translation_po.cpp
new file mode 100644
index 0000000000..203f29026b
--- /dev/null
+++ b/core/translation_po.cpp
@@ -0,0 +1,312 @@
+/*************************************************************************/
+/* translation_po.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* 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 "translation_po.h"
+
+#include "core/os/file_access.h"
+
+#ifdef DEBUG_TRANSLATION_PO
+void TranslationPO::print_translation_map() {
+ Error err;
+ FileAccess *file = FileAccess::open("translation_map_print_test.txt", FileAccess::WRITE, &err);
+ if (err != OK) {
+ ERR_PRINT("Failed to open translation_map_print_test.txt");
+ return;
+ }
+
+ file->store_line("NPlural : " + String::num_int64(this->get_plural_forms()));
+ file->store_line("Plural rule : " + this->get_plural_rule());
+ file->store_line("");
+
+ List<StringName> context_l;
+ translation_map.get_key_list(&context_l);
+ for (auto E = context_l.front(); E; E = E->next()) {
+ StringName ctx = E->get();
+ file->store_line(" ===== Context: " + String::utf8(String(ctx).utf8()) + " ===== ");
+ const HashMap<StringName, Vector<StringName>> &inner_map = translation_map[ctx];
+
+ List<StringName> id_l;
+ inner_map.get_key_list(&id_l);
+ for (auto E2 = id_l.front(); E2; E2 = E2->next()) {
+ StringName id = E2->get();
+ file->store_line("msgid: " + String::utf8(String(id).utf8()));
+ for (int i = 0; i < inner_map[id].size(); i++) {
+ file->store_line("msgstr[" + String::num_int64(i) + "]: " + String::utf8(String(inner_map[id][i]).utf8()));
+ }
+ file->store_line("");
+ }
+ }
+ file->close();
+}
+#endif
+
+Dictionary TranslationPO::_get_messages() const {
+ // Return translation_map as a Dictionary.
+
+ Dictionary d;
+
+ List<StringName> context_l;
+ translation_map.get_key_list(&context_l);
+ for (auto E = context_l.front(); E; E = E->next()) {
+ StringName ctx = E->get();
+ const HashMap<StringName, Vector<StringName>> &id_str_map = translation_map[ctx];
+
+ Dictionary d2;
+ List<StringName> id_l;
+ id_str_map.get_key_list(&id_l);
+ // Save list of id and strs associated with a context in a temporary dictionary.
+ for (auto E2 = id_l.front(); E2; E2 = E2->next()) {
+ StringName id = E2->get();
+ d2[id] = id_str_map[id];
+ }
+
+ d[ctx] = d2;
+ }
+
+ return d;
+}
+
+void TranslationPO::_set_messages(const Dictionary &p_messages) {
+ // Construct translation_map from a Dictionary.
+
+ List<Variant> context_l;
+ p_messages.get_key_list(&context_l);
+ for (auto E = context_l.front(); E; E = E->next()) {
+ StringName ctx = E->get();
+ const Dictionary &id_str_map = p_messages[ctx];
+
+ HashMap<StringName, Vector<StringName>> temp_map;
+ List<Variant> id_l;
+ id_str_map.get_key_list(&id_l);
+ for (auto E2 = id_l.front(); E2; E2 = E2->next()) {
+ StringName id = E2->get();
+ temp_map[id] = id_str_map[id];
+ }
+
+ translation_map[ctx] = temp_map;
+ }
+}
+
+Vector<String> TranslationPO::_get_message_list() const {
+ // Return all keys in translation_map.
+
+ List<StringName> msgs;
+ get_message_list(&msgs);
+
+ Vector<String> v;
+ for (auto E = msgs.front(); E; E = E->next()) {
+ v.push_back(E->get());
+ }
+
+ return v;
+}
+
+int TranslationPO::_get_plural_index(int p_n) const {
+ // Get a number between [0;number of plural forms).
+
+ input_val.clear();
+ input_val.push_back(p_n);
+
+ Variant result;
+ for (int i = 0; i < equi_tests.size(); i++) {
+ Error err = expr->parse(equi_tests[i], input_name);
+ ERR_FAIL_COND_V_MSG(err != OK, 0, "Cannot parse expression. Error: " + expr->get_error_text());
+
+ result = expr->execute(input_val);
+ ERR_FAIL_COND_V_MSG(expr->has_execute_failed(), 0, "Cannot evaluate expression.");
+
+ // Last expression. Variant result will either map to a bool or an integer, in both cases returning it will give the correct plural index.
+ if (i + 1 == equi_tests.size()) {
+ return result;
+ }
+
+ if (bool(result)) {
+ return i;
+ }
+ }
+
+ ERR_FAIL_V_MSG(0, "Unexpected. Function should have returned. Please report this bug.");
+}
+
+void TranslationPO::_cache_plural_tests(const String &p_plural_rule) {
+ // Some examples of p_plural_rule passed in can have the form:
+ // "n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 && n%100<=10 ? 3 : n%100>=11 && n%100<=99 ? 4 : 5" (Arabic)
+ // "n >= 2" (French) // When evaluating the last, esp careful with this one.
+ // "n != 1" (English)
+ int first_ques_mark = p_plural_rule.find("?");
+ if (first_ques_mark == -1) {
+ equi_tests.push_back(p_plural_rule.strip_edges());
+ return;
+ }
+
+ String equi_test = p_plural_rule.substr(0, first_ques_mark).strip_edges();
+ equi_tests.push_back(equi_test);
+
+ String after_colon = p_plural_rule.substr(p_plural_rule.find(":") + 1, p_plural_rule.length());
+ _cache_plural_tests(after_colon);
+}
+
+void TranslationPO::set_plural_rule(const String &p_plural_rule) {
+ // Set plural_forms and plural_rule.
+ // p_plural_rule passed in has the form "Plural-Forms: nplurals=2; plural=(n >= 2);".
+
+ int first_semi_col = p_plural_rule.find(";");
+ plural_forms = p_plural_rule.substr(p_plural_rule.find("=") + 1, first_semi_col - (p_plural_rule.find("=") + 1)).to_int();
+
+ int expression_start = p_plural_rule.find("=", first_semi_col) + 1;
+ int second_semi_col = p_plural_rule.rfind(";");
+ plural_rule = p_plural_rule.substr(expression_start, second_semi_col - expression_start);
+
+ // Setup the cache to make evaluating plural rule faster later on.
+ plural_rule = plural_rule.replacen("(", "");
+ plural_rule = plural_rule.replacen(")", "");
+ _cache_plural_tests(plural_rule);
+ expr.instance();
+ input_name.push_back("n");
+}
+
+void TranslationPO::add_message(const StringName &p_src_text, const StringName &p_xlated_text, const StringName &p_context) {
+ HashMap<StringName, Vector<StringName>> &map_id_str = translation_map[p_context];
+
+ if (map_id_str.has(p_src_text)) {
+ WARN_PRINT("Double translations for \"" + String(p_src_text) + "\" under the same context \"" + String(p_context) + "\" for locale \"" + get_locale() + "\".\nThere should only be one unique translation for a given string under the same context.");
+ map_id_str[p_src_text].set(0, p_xlated_text);
+ } else {
+ map_id_str[p_src_text].push_back(p_xlated_text);
+ }
+}
+
+void TranslationPO::add_plural_message(const StringName &p_src_text, const Vector<String> &p_plural_xlated_texts, const StringName &p_context) {
+ ERR_FAIL_COND_MSG(p_plural_xlated_texts.size() != plural_forms, "Trying to add plural texts that don't match the required number of plural forms for locale \"" + get_locale() + "\"");
+
+ HashMap<StringName, Vector<StringName>> &map_id_str = translation_map[p_context];
+
+ if (map_id_str.has(p_src_text)) {
+ WARN_PRINT("Double translations for \"" + p_src_text + "\" under the same context \"" + p_context + "\" for locale " + get_locale() + ".\nThere should only be one unique translation for a given string under the same context.");
+ map_id_str[p_src_text].clear();
+ }
+
+ for (int i = 0; i < p_plural_xlated_texts.size(); i++) {
+ map_id_str[p_src_text].push_back(p_plural_xlated_texts[i]);
+ }
+}
+
+int TranslationPO::get_plural_forms() const {
+ return plural_forms;
+}
+
+String TranslationPO::get_plural_rule() const {
+ return plural_rule;
+}
+
+StringName TranslationPO::get_message(const StringName &p_src_text, const StringName &p_context) const {
+ if (!translation_map.has(p_context) || !translation_map[p_context].has(p_src_text)) {
+ return StringName();
+ }
+ ERR_FAIL_COND_V_MSG(translation_map[p_context][p_src_text].empty(), StringName(), "Source text \"" + String(p_src_text) + "\" is registered but doesn't have a translation. Please report this bug.");
+
+ return translation_map[p_context][p_src_text][0];
+}
+
+StringName TranslationPO::get_plural_message(const StringName &p_src_text, const StringName &p_plural_text, int p_n, const StringName &p_context) const {
+ ERR_FAIL_COND_V_MSG(p_n < 0, StringName(), "N passed into translation to get a plural message should not be negative. For negative numbers, use singular translation please. Search \"gettext PO Plural Forms\" online for the documentation on translating negative numbers.");
+
+ // If the query is the same as last time, return the cached result.
+ if (p_n == last_plural_n && p_context == last_plural_context && p_src_text == last_plural_key) {
+ return translation_map[p_context][p_src_text][last_plural_mapped_index];
+ }
+
+ if (!translation_map.has(p_context) || !translation_map[p_context].has(p_src_text)) {
+ return StringName();
+ }
+ ERR_FAIL_COND_V_MSG(translation_map[p_context][p_src_text].empty(), StringName(), "Source text \"" + String(p_src_text) + "\" is registered but doesn't have a translation. Please report this bug.");
+
+ if (translation_map[p_context][p_src_text].size() == 1) {
+ WARN_PRINT("Source string \"" + String(p_src_text) + "\" doesn't have plural translations. Use singular translation API for such as tr(), TTR() to translate \"" + String(p_src_text) + "\"");
+ return translation_map[p_context][p_src_text][0];
+ }
+
+ int plural_index = _get_plural_index(p_n);
+ ERR_FAIL_COND_V_MSG(plural_index < 0 || translation_map[p_context][p_src_text].size() < plural_index + 1, StringName(), "Plural index returned or number of plural translations is not valid. Please report this bug.");
+
+ // Cache result so that if the next entry is the same, we can return directly.
+ // _get_plural_index(p_n) can get very costly, especially when evaluating long plural-rule (Arabic)
+ last_plural_key = p_src_text;
+ last_plural_context = p_context;
+ last_plural_n = p_n;
+ last_plural_mapped_index = plural_index;
+
+ return translation_map[p_context][p_src_text][plural_index];
+}
+
+void TranslationPO::erase_message(const StringName &p_src_text, const StringName &p_context) {
+ if (!translation_map.has(p_context)) {
+ return;
+ }
+
+ translation_map[p_context].erase(p_src_text);
+}
+
+void TranslationPO::get_message_list(List<StringName> *r_messages) const {
+ // PHashTranslation uses this function to get the list of msgid.
+ // Return all the keys of translation_map under "" context.
+
+ List<StringName> context_l;
+ translation_map.get_key_list(&context_l);
+
+ for (auto E = context_l.front(); E; E = E->next()) {
+ if (String(E->get()) != "") {
+ continue;
+ }
+
+ List<StringName> msgid_l;
+ translation_map[E->get()].get_key_list(&msgid_l);
+
+ for (auto E2 = msgid_l.front(); E2; E2 = E2->next()) {
+ r_messages->push_back(E2->get());
+ }
+ }
+}
+
+int TranslationPO::get_message_count() const {
+ List<StringName> context_l;
+ translation_map.get_key_list(&context_l);
+
+ int count = 0;
+ for (auto E = context_l.front(); E; E = E->next()) {
+ count += translation_map[E->get()].size();
+ }
+ return count;
+}
+
+void TranslationPO::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("get_plural_forms"), &TranslationPO::get_plural_forms);
+ ClassDB::bind_method(D_METHOD("get_plural_rule"), &TranslationPO::get_plural_rule);
+}
diff --git a/core/translation_po.h b/core/translation_po.h
new file mode 100644
index 0000000000..730635f63d
--- /dev/null
+++ b/core/translation_po.h
@@ -0,0 +1,92 @@
+/*************************************************************************/
+/* translation_po.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* 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 TRANSLATION_PO_H
+#define TRANSLATION_PO_H
+
+//#define DEBUG_TRANSLATION_PO
+
+#include "core/math/expression.h"
+#include "core/translation.h"
+
+class TranslationPO : public Translation {
+ GDCLASS(TranslationPO, Translation);
+
+ // TLDR: Maps context to a list of source strings and translated strings. In PO terms, maps msgctxt to a list of msgid and msgstr.
+ // The first key corresponds to context, and the second key (of the contained HashMap) corresponds to source string.
+ // The value Vector<StringName> in the second map stores the translated strings. Index 0, 1, 2 matches msgstr[0], msgstr[1], msgstr[2]... in the case of plurals.
+ // Otherwise index 0 mathes to msgstr in a singular translation.
+ // Strings without context have "" as first key.
+ HashMap<StringName, HashMap<StringName, Vector<StringName>>> translation_map;
+
+ int plural_forms = 0; // 0 means no "Plural-Forms" is given in the PO header file. The min for all languages is 1.
+ String plural_rule;
+
+ // Cache temporary variables related to _get_plural_index() to make it faster
+ Vector<String> equi_tests;
+ Vector<String> input_name;
+ mutable Ref<Expression> expr;
+ mutable Array input_val;
+ mutable StringName last_plural_key;
+ mutable StringName last_plural_context;
+ mutable int last_plural_n = -1; // Set it to an impossible value at the beginning.
+ mutable int last_plural_mapped_index = 0;
+
+ void _cache_plural_tests(const String &p_plural_rule);
+ int _get_plural_index(int p_n) const;
+
+ Vector<String> _get_message_list() const override;
+ Dictionary _get_messages() const override;
+ void _set_messages(const Dictionary &p_messages) override;
+
+protected:
+ static void _bind_methods();
+
+public:
+ void get_message_list(List<StringName> *r_messages) const override;
+ int get_message_count() const override;
+ void add_message(const StringName &p_src_text, const StringName &p_xlated_text, const StringName &p_context = "") override;
+ void add_plural_message(const StringName &p_src_text, const Vector<String> &p_plural_xlated_texts, const StringName &p_context = "") override;
+ StringName get_message(const StringName &p_src_text, const StringName &p_context = "") const override;
+ StringName get_plural_message(const StringName &p_src_text, const StringName &p_plural_text, int p_n, const StringName &p_context = "") const override;
+ void erase_message(const StringName &p_src_text, const StringName &p_context = "") override;
+
+ void set_plural_rule(const String &p_plural_rule);
+ int get_plural_forms() const;
+ String get_plural_rule() const;
+
+#ifdef DEBUG_TRANSLATION_PO
+ void print_translation_map();
+#endif
+
+ TranslationPO() {}
+};
+
+#endif // TRANSLATION_PO_H
diff --git a/core/ustring.cpp b/core/ustring.cpp
index 444338d5ae..9d2d938eaf 100644
--- a/core/ustring.cpp
+++ b/core/ustring.cpp
@@ -851,7 +851,7 @@ Vector<float> String::split_floats(const String &p_splitter, bool p_allow_empty)
end = len;
}
if (p_allow_empty || (end > from)) {
- ret.push_back(String::to_double(&c_str()[from]));
+ ret.push_back(String::to_float(&c_str()[from]));
}
if (end == len) {
@@ -880,7 +880,7 @@ Vector<float> String::split_floats_mk(const Vector<String> &p_splitters, bool p_
}
if (p_allow_empty || (end > from)) {
- ret.push_back(String::to_double(&c_str()[from]));
+ ret.push_back(String::to_float(&c_str()[from]));
}
if (end == len) {
@@ -1025,6 +1025,9 @@ String String::chr(CharType p_char) {
}
String String::num(double p_num, int p_decimals) {
+ if (Math::is_nan(p_num)) {
+ return "nan";
+ }
#ifndef NO_USE_STDLIB
if (p_decimals > 16) {
@@ -1313,6 +1316,9 @@ String String::num_real(double p_num) {
}
String String::num_scientific(double p_num) {
+ if (Math::is_nan(p_num)) {
+ return "nan";
+ }
#ifndef NO_USE_STDLIB
char buf[256];
@@ -1618,49 +1624,7 @@ String::String(const StrRange &p_range) {
copy_from(p_range.c_str, p_range.len);
}
-int String::hex_to_int(bool p_with_prefix) const {
- if (p_with_prefix && length() < 3) {
- return 0;
- }
-
- const CharType *s = ptr();
-
- int sign = s[0] == '-' ? -1 : 1;
-
- if (sign < 0) {
- s++;
- }
-
- if (p_with_prefix) {
- if (s[0] != '0' || s[1] != 'x') {
- return 0;
- }
- s += 2;
- }
-
- int hex = 0;
-
- while (*s) {
- CharType c = LOWERCASE(*s);
- int n;
- if (c >= '0' && c <= '9') {
- n = c - '0';
- } else if (c >= 'a' && c <= 'f') {
- n = (c - 'a') + 10;
- } else {
- return 0;
- }
-
- ERR_FAIL_COND_V_MSG(hex > INT32_MAX / 16, sign == 1 ? INT32_MAX : INT32_MIN, "Cannot represent " + *this + " as integer, provided value is " + (sign == 1 ? "too big." : "too small."));
- hex *= 16;
- hex += n;
- s++;
- }
-
- return hex * sign;
-}
-
-int64_t String::hex_to_int64(bool p_with_prefix) const {
+int64_t String::hex_to_int(bool p_with_prefix) const {
if (p_with_prefix && length() < 3) {
return 0;
}
@@ -1692,8 +1656,9 @@ int64_t String::hex_to_int64(bool p_with_prefix) const {
} else {
return 0;
}
-
- ERR_FAIL_COND_V_MSG(hex > INT64_MAX / 16, sign == 1 ? INT64_MAX : INT64_MIN, "Cannot represent " + *this + " as 64-bit integer, provided value is " + (sign == 1 ? "too big." : "too small."));
+ // Check for overflow/underflow, with special case to ensure INT64_MIN does not result in error
+ bool overflow = ((hex > INT64_MAX / 16) && (sign == 1 || (sign == -1 && hex != (INT64_MAX >> 4) + 1))) || (sign == -1 && hex == (INT64_MAX >> 4) + 1 && c > '0');
+ ERR_FAIL_COND_V_MSG(overflow, sign == 1 ? INT64_MAX : INT64_MIN, "Cannot represent " + *this + " as 64-bit integer, provided value is " + (sign == 1 ? "too big." : "too small."));
hex *= 16;
hex += n;
s++;
@@ -1702,7 +1667,7 @@ int64_t String::hex_to_int64(bool p_with_prefix) const {
return hex * sign;
}
-int64_t String::bin_to_int64(bool p_with_prefix) const {
+int64_t String::bin_to_int(bool p_with_prefix) const {
if (p_with_prefix && length() < 3) {
return 0;
}
@@ -1732,8 +1697,9 @@ int64_t String::bin_to_int64(bool p_with_prefix) const {
} else {
return 0;
}
-
- ERR_FAIL_COND_V_MSG(binary > INT64_MAX / 2, sign == 1 ? INT64_MAX : INT64_MIN, "Cannot represent " + *this + " as 64-bit integer, provided value is " + (sign == 1 ? "too big." : "too small."));
+ // Check for overflow/underflow, with special case to ensure INT64_MIN does not result in error
+ bool overflow = ((binary > INT64_MAX / 2) && (sign == 1 || (sign == -1 && binary != (INT64_MAX >> 1) + 1))) || (sign == -1 && binary == (INT64_MAX >> 1) + 1 && c > '0');
+ ERR_FAIL_COND_V_MSG(overflow, sign == 1 ? INT64_MAX : INT64_MIN, "Cannot represent " + *this + " as 64-bit integer, provided value is " + (sign == 1 ? "too big." : "too small."));
binary *= 2;
binary += n;
s++;
@@ -1742,32 +1708,7 @@ int64_t String::bin_to_int64(bool p_with_prefix) const {
return binary * sign;
}
-int String::to_int() const {
- if (length() == 0) {
- return 0;
- }
-
- int to = (find(".") >= 0) ? find(".") : length();
-
- int integer = 0;
- int sign = 1;
-
- for (int i = 0; i < to; i++) {
- CharType c = operator[](i);
- if (c >= '0' && c <= '9') {
- ERR_FAIL_COND_V_MSG(integer > INT32_MAX / 10, sign == 1 ? INT32_MAX : INT32_MIN, "Cannot represent " + *this + " as integer, provided value is " + (sign == 1 ? "too big." : "too small."));
- integer *= 10;
- integer += c - '0';
-
- } else if (integer == 0 && c == '-') {
- sign = -sign;
- }
- }
-
- return integer * sign;
-}
-
-int64_t String::to_int64() const {
+int64_t String::to_int() const {
if (length() == 0) {
return 0;
}
@@ -1780,7 +1721,8 @@ int64_t String::to_int64() const {
for (int i = 0; i < to; i++) {
CharType c = operator[](i);
if (c >= '0' && c <= '9') {
- ERR_FAIL_COND_V_MSG(integer > INT64_MAX / 10, sign == 1 ? INT64_MAX : INT64_MIN, "Cannot represent " + *this + " as 64-bit integer, provided value is " + (sign == 1 ? "too big." : "too small."));
+ bool overflow = (integer > INT64_MAX / 10) || (integer == INT64_MAX / 10 && ((sign == 1 && c > '7') || (sign == -1 && c > '8')));
+ ERR_FAIL_COND_V_MSG(overflow, sign == 1 ? INT64_MAX : INT64_MIN, "Cannot represent " + *this + " as 64-bit integer, provided value is " + (sign == 1 ? "too big." : "too small."));
integer *= 10;
integer += c - '0';
@@ -1792,7 +1734,7 @@ int64_t String::to_int64() const {
return integer * sign;
}
-int String::to_int(const char *p_str, int p_len) {
+int64_t String::to_int(const char *p_str, int p_len) {
int to = 0;
if (p_len >= 0) {
to = p_len;
@@ -1802,13 +1744,14 @@ int String::to_int(const char *p_str, int p_len) {
}
}
- int integer = 0;
- int sign = 1;
+ int64_t integer = 0;
+ int64_t sign = 1;
for (int i = 0; i < to; i++) {
char c = p_str[i];
if (c >= '0' && c <= '9') {
- ERR_FAIL_COND_V_MSG(integer > INT32_MAX / 10, sign == 1 ? INT32_MAX : INT32_MIN, "Cannot represent " + String(p_str).substr(0, to) + " as integer, provided value is " + (sign == 1 ? "too big." : "too small."));
+ bool overflow = (integer > INT64_MAX / 10) || (integer == INT64_MAX / 10 && ((sign == 1 && c > '7') || (sign == -1 && c > '8')));
+ ERR_FAIL_COND_V_MSG(overflow, sign == 1 ? INT64_MAX : INT64_MIN, "Cannot represent " + String(p_str).substr(0, to) + " as integer, provided value is " + (sign == 1 ? "too big." : "too small."));
integer *= 10;
integer += c - '0';
@@ -2063,7 +2006,7 @@ done:
#define READING_EXP 3
#define READING_DONE 4
-double String::to_double(const char *p_str) {
+double String::to_float(const char *p_str) {
#ifndef NO_USE_STDLIB
return built_in_strtod<char>(p_str);
//return atof(p_str); DOES NOT WORK ON ANDROID(??)
@@ -2072,11 +2015,7 @@ double String::to_double(const char *p_str) {
#endif
}
-float String::to_float() const {
- return to_double();
-}
-
-double String::to_double(const CharType *p_str, const CharType **r_end) {
+double String::to_float(const CharType *p_str, const CharType **r_end) {
return built_in_strtod<CharType>(p_str, (CharType **)r_end);
}
@@ -2144,7 +2083,7 @@ int64_t String::to_int(const CharType *p_str, int p_len, bool p_clamp) {
return sign * integer;
}
-double String::to_double() const {
+double String::to_float() const {
if (empty()) {
return 0;
}
@@ -2343,18 +2282,6 @@ String String::substr(int p_from, int p_chars) const {
return s;
}
-int String::find_last(const String &p_str) const {
- int pos = -1;
- int findfrom = 0;
- int findres = -1;
- while ((findres = find(p_str, findfrom)) != -1) {
- pos = findres;
- findfrom = pos + 1;
- }
-
- return pos;
-}
-
int String::find(const String &p_str, int p_from) const {
if (p_from < 0) {
return -1;
@@ -2645,7 +2572,7 @@ int String::rfindn(const String &p_str, int p_from) const {
}
bool String::ends_with(const String &p_string) const {
- int pos = find_last(p_string);
+ int pos = rfind(p_string);
if (pos == -1) {
return false;
}
@@ -3838,7 +3765,7 @@ bool String::is_valid_ip_address() const {
continue;
}
if (n.is_valid_hex_number(false)) {
- int nint = n.hex_to_int(false);
+ int64_t nint = n.hex_to_int(false);
if (nint < 0 || nint > 0xffff) {
return false;
}
@@ -3894,7 +3821,7 @@ String String::get_base_dir() const {
}
}
- int sep = MAX(rs.find_last("/"), rs.find_last("\\"));
+ int sep = MAX(rs.rfind("/"), rs.rfind("\\"));
if (sep == -1) {
return base;
}
@@ -3903,7 +3830,7 @@ String String::get_base_dir() const {
}
String String::get_file() const {
- int sep = MAX(find_last("/"), find_last("\\"));
+ int sep = MAX(rfind("/"), rfind("\\"));
if (sep == -1) {
return *this;
}
@@ -3912,8 +3839,8 @@ String String::get_file() const {
}
String String::get_extension() const {
- int pos = find_last(".");
- if (pos < 0 || pos < MAX(find_last("/"), find_last("\\"))) {
+ int pos = rfind(".");
+ if (pos < 0 || pos < MAX(rfind("/"), rfind("\\"))) {
return "";
}
@@ -4001,8 +3928,8 @@ String String::property_name_encode() const {
}
String String::get_basename() const {
- int pos = find_last(".");
- if (pos < 0 || pos < MAX(find_last("/"), find_last("\\"))) {
+ int pos = rfind(".");
+ if (pos < 0 || pos < MAX(rfind("/"), rfind("\\"))) {
return *this;
}
@@ -4342,31 +4269,58 @@ String String::unquote() const {
}
#ifdef TOOLS_ENABLED
-String TTR(const String &p_text) {
+String TTR(const String &p_text, const String &p_context) {
if (TranslationServer::get_singleton()) {
- return TranslationServer::get_singleton()->tool_translate(p_text);
+ return TranslationServer::get_singleton()->tool_translate(p_text, p_context);
}
return p_text;
}
-String DTR(const String &p_text) {
+String TTRN(const String &p_text, const String &p_text_plural, int p_n, const String &p_context) {
+ if (TranslationServer::get_singleton()) {
+ return TranslationServer::get_singleton()->tool_translate_plural(p_text, p_text_plural, p_n, p_context);
+ }
+
+ // Return message based on English plural rule if translation is not possible.
+ if (p_n == 1) {
+ return p_text;
+ }
+ return p_text_plural;
+}
+
+String DTR(const String &p_text, const String &p_context) {
// Comes straight from the XML, so remove indentation and any trailing whitespace.
const String text = p_text.dedent().strip_edges();
if (TranslationServer::get_singleton()) {
- return TranslationServer::get_singleton()->doc_translate(text);
+ return TranslationServer::get_singleton()->doc_translate(text, p_context);
}
return text;
}
+
+String DTRN(const String &p_text, const String &p_text_plural, int p_n, const String &p_context) {
+ const String text = p_text.dedent().strip_edges();
+ const String text_plural = p_text_plural.dedent().strip_edges();
+
+ if (TranslationServer::get_singleton()) {
+ return TranslationServer::get_singleton()->doc_translate_plural(text, text_plural, p_n, p_context);
+ }
+
+ // Return message based on English plural rule if translation is not possible.
+ if (p_n == 1) {
+ return text;
+ }
+ return text_plural;
+}
#endif
-String RTR(const String &p_text) {
+String RTR(const String &p_text, const String &p_context) {
if (TranslationServer::get_singleton()) {
- String rtr = TranslationServer::get_singleton()->tool_translate(p_text);
+ String rtr = TranslationServer::get_singleton()->tool_translate(p_text, p_context);
if (rtr == String() || rtr == p_text) {
- return TranslationServer::get_singleton()->translate(p_text);
+ return TranslationServer::get_singleton()->translate(p_text, p_context);
} else {
return rtr;
}
@@ -4374,3 +4328,20 @@ String RTR(const String &p_text) {
return p_text;
}
+
+String RTRN(const String &p_text, const String &p_text_plural, int p_n, const String &p_context) {
+ if (TranslationServer::get_singleton()) {
+ String rtr = TranslationServer::get_singleton()->tool_translate_plural(p_text, p_text_plural, p_n, p_context);
+ if (rtr == String() || rtr == p_text || rtr == p_text_plural) {
+ return TranslationServer::get_singleton()->translate_plural(p_text, p_text_plural, p_n, p_context);
+ } else {
+ return rtr;
+ }
+ }
+
+ // Return message based on English plural rule if translation is not possible.
+ if (p_n == 1) {
+ return p_text;
+ }
+ return p_text_plural;
+}
diff --git a/core/ustring.h b/core/ustring.h
index 5b13a1c704..7a1c1a5232 100644
--- a/core/ustring.h
+++ b/core/ustring.h
@@ -202,7 +202,6 @@ public:
int find(const String &p_str, int p_from = 0) const; ///< return <0 if failed
int find(const char *p_str, int p_from = 0) const; ///< return <0 if failed
int find_char(const CharType &p_char, int p_from = 0) const; ///< return <0 if failed
- int find_last(const String &p_str) const; ///< return <0 if failed
int findn(const String &p_str, int p_from = 0) const; ///< return <0 if failed, case insensitive
int rfind(const String &p_str, int p_from = -1) const; ///< return <0 if failed
int rfindn(const String &p_str, int p_from = -1) const; ///< return <0 if failed, case insensitive
@@ -243,17 +242,14 @@ public:
static String md5(const uint8_t *p_md5);
static String hex_encode_buffer(const uint8_t *p_buffer, int p_len);
bool is_numeric() const;
- double to_double() const;
- float to_float() const;
- int hex_to_int(bool p_with_prefix = true) const;
- int to_int() const;
-
- int64_t hex_to_int64(bool p_with_prefix = true) const;
- int64_t bin_to_int64(bool p_with_prefix = true) const;
- int64_t to_int64() const;
- static int to_int(const char *p_str, int p_len = -1);
- static double to_double(const char *p_str);
- static double to_double(const CharType *p_str, const CharType **r_end = nullptr);
+ double to_float() const;
+
+ int64_t hex_to_int(bool p_with_prefix = true) const;
+ int64_t bin_to_int(bool p_with_prefix = true) const;
+ int64_t to_int() const;
+ static int64_t to_int(const char *p_str, int p_len = -1);
+ static double to_float(const char *p_str);
+ static double to_float(const CharType *p_str, const CharType **r_end = nullptr);
static int64_t to_int(const CharType *p_str, int p_len = -1, bool p_clamp = false);
String capitalize() const;
String camelcase_to_underscore(bool lowercase = true) const;
@@ -414,8 +410,10 @@ _FORCE_INLINE_ bool is_str_less(const L *l_ptr, const R *r_ptr) {
// and doc translate for the class reference (DTR).
#ifdef TOOLS_ENABLED
// Gets parsed.
-String TTR(const String &);
-String DTR(const String &);
+String TTR(const String &p_text, const String &p_context = "");
+String TTRN(const String &p_text, const String &p_text_plural, int p_n, const String &p_context = "");
+String DTR(const String &p_text, const String &p_context = "");
+String DTRN(const String &p_text, const String &p_text_plural, int p_n, const String &p_context = "");
// Use for C strings.
#define TTRC(m_value) (m_value)
// Use to avoid parsing (for use later with C strings).
@@ -423,13 +421,16 @@ String DTR(const String &);
#else
#define TTR(m_value) (String())
+#define TTRN(m_value) (String())
#define DTR(m_value) (String())
+#define DTRN(m_value) (String())
#define TTRC(m_value) (m_value)
#define TTRGET(m_value) (m_value)
#endif
// Runtime translate for the public node API.
-String RTR(const String &);
+String RTR(const String &p_text, const String &p_context = "");
+String RTRN(const String &p_text, const String &p_text_plural, int p_n, const String &p_context = "");
bool is_symbol(CharType c);
bool select_word(const String &p_s, int p_col, int &r_beg, int &r_end);
diff --git a/core/variant.cpp b/core/variant.cpp
index 21aaa0fe9e..c19ce79e64 100644
--- a/core/variant.cpp
+++ b/core/variant.cpp
@@ -1409,7 +1409,7 @@ Variant::operator int64_t() const {
case FLOAT:
return _data._float;
case STRING:
- return operator String().to_int64();
+ return operator String().to_int();
default: {
return 0;
}
@@ -1454,7 +1454,7 @@ Variant::operator signed long() const {
case INT:
return _data._int;
case FLOAT:
- return _data._real;
+ return _data._float;
case STRING:
return operator String().to_int();
default: {
@@ -1474,7 +1474,7 @@ Variant::operator unsigned long() const {
case INT:
return _data._int;
case FLOAT:
- return _data._real;
+ return _data._float;
case STRING:
return operator String().to_int();
default: {
@@ -1573,7 +1573,7 @@ Variant::operator float() const {
case FLOAT:
return _data._float;
case STRING:
- return operator String().to_double();
+ return operator String().to_float();
default: {
return 0;
}
@@ -1591,7 +1591,7 @@ Variant::operator double() const {
case FLOAT:
return _data._float;
case STRING:
- return operator String().to_double();
+ return operator String().to_float();
default: {
return 0;
}
diff --git a/core/variant.h b/core/variant.h
index 50b7a21eda..27a709b473 100644
--- a/core/variant.h
+++ b/core/variant.h
@@ -120,6 +120,7 @@ public:
private:
friend struct _VariantCall;
+ friend class VariantInternal;
// Variant takes 20 bytes when real_t is float, and 36 if double
// it only allocates extra memory for aabb/matrix.
diff --git a/core/variant_call.cpp b/core/variant_call.cpp
index a8beac1e44..91af127d32 100644
--- a/core/variant_call.cpp
+++ b/core/variant_call.cpp
@@ -244,7 +244,6 @@ struct _VariantCall {
VCALL_LOCALMEM3R(String, countn);
VCALL_LOCALMEM2R(String, substr);
VCALL_LOCALMEM2R(String, find);
- VCALL_LOCALMEM1R(String, find_last);
VCALL_LOCALMEM2R(String, findn);
VCALL_LOCALMEM2R(String, rfind);
VCALL_LOCALMEM2R(String, rfindn);
@@ -456,7 +455,6 @@ struct _VariantCall {
VCALL_LOCALMEM0R(Plane, normalized);
VCALL_LOCALMEM0R(Plane, center);
- VCALL_LOCALMEM0R(Plane, get_any_point);
VCALL_LOCALMEM1R(Plane, is_equal_approx);
VCALL_LOCALMEM1R(Plane, is_point_over);
VCALL_LOCALMEM1R(Plane, distance_to);
@@ -702,6 +700,8 @@ struct _VariantCall {
VCALL_PARRMEM1(PackedByteArray, uint8_t, remove);
VCALL_PARRMEM1(PackedByteArray, uint8_t, append);
VCALL_PARRMEM1(PackedByteArray, uint8_t, append_array);
+ VCALL_PARRMEM1R(PackedByteArray, uint8_t, has);
+ VCALL_PARRMEM0(PackedByteArray, uint8_t, sort);
VCALL_PARRMEM0(PackedByteArray, uint8_t, invert);
VCALL_PARRMEM2R(PackedByteArray, uint8_t, subarray);
@@ -715,6 +715,8 @@ struct _VariantCall {
VCALL_PARRMEM1(PackedInt32Array, int32_t, remove);
VCALL_PARRMEM1(PackedInt32Array, int32_t, append);
VCALL_PARRMEM1(PackedInt32Array, int32_t, append_array);
+ VCALL_PARRMEM1R(PackedInt32Array, int32_t, has);
+ VCALL_PARRMEM0(PackedInt32Array, int32_t, sort);
VCALL_PARRMEM0(PackedInt32Array, int32_t, invert);
VCALL_PARRMEM0R(PackedInt64Array, int64_t, size);
@@ -727,6 +729,8 @@ struct _VariantCall {
VCALL_PARRMEM1(PackedInt64Array, int64_t, remove);
VCALL_PARRMEM1(PackedInt64Array, int64_t, append);
VCALL_PARRMEM1(PackedInt64Array, int64_t, append_array);
+ VCALL_PARRMEM1R(PackedInt64Array, int64_t, has);
+ VCALL_PARRMEM0(PackedInt64Array, int64_t, sort);
VCALL_PARRMEM0(PackedInt64Array, int64_t, invert);
VCALL_PARRMEM0R(PackedFloat32Array, float, size);
@@ -739,6 +743,8 @@ struct _VariantCall {
VCALL_PARRMEM1(PackedFloat32Array, float, remove);
VCALL_PARRMEM1(PackedFloat32Array, float, append);
VCALL_PARRMEM1(PackedFloat32Array, float, append_array);
+ VCALL_PARRMEM1R(PackedFloat32Array, float, has);
+ VCALL_PARRMEM0(PackedFloat32Array, float, sort);
VCALL_PARRMEM0(PackedFloat32Array, float, invert);
VCALL_PARRMEM0R(PackedFloat64Array, double, size);
@@ -751,6 +757,8 @@ struct _VariantCall {
VCALL_PARRMEM1(PackedFloat64Array, double, remove);
VCALL_PARRMEM1(PackedFloat64Array, double, append);
VCALL_PARRMEM1(PackedFloat64Array, double, append_array);
+ VCALL_PARRMEM1R(PackedFloat64Array, double, has);
+ VCALL_PARRMEM0(PackedFloat64Array, double, sort);
VCALL_PARRMEM0(PackedFloat64Array, double, invert);
VCALL_PARRMEM0R(PackedStringArray, String, size);
@@ -763,6 +771,8 @@ struct _VariantCall {
VCALL_PARRMEM1(PackedStringArray, String, remove);
VCALL_PARRMEM1(PackedStringArray, String, append);
VCALL_PARRMEM1(PackedStringArray, String, append_array);
+ VCALL_PARRMEM1R(PackedStringArray, String, has);
+ VCALL_PARRMEM0(PackedStringArray, String, sort);
VCALL_PARRMEM0(PackedStringArray, String, invert);
VCALL_PARRMEM0R(PackedVector2Array, Vector2, size);
@@ -775,6 +785,8 @@ struct _VariantCall {
VCALL_PARRMEM1(PackedVector2Array, Vector2, remove);
VCALL_PARRMEM1(PackedVector2Array, Vector2, append);
VCALL_PARRMEM1(PackedVector2Array, Vector2, append_array);
+ VCALL_PARRMEM1R(PackedVector2Array, Vector2, has);
+ VCALL_PARRMEM0(PackedVector2Array, Vector2, sort);
VCALL_PARRMEM0(PackedVector2Array, Vector2, invert);
VCALL_PARRMEM0R(PackedVector3Array, Vector3, size);
@@ -787,6 +799,8 @@ struct _VariantCall {
VCALL_PARRMEM1(PackedVector3Array, Vector3, remove);
VCALL_PARRMEM1(PackedVector3Array, Vector3, append);
VCALL_PARRMEM1(PackedVector3Array, Vector3, append_array);
+ VCALL_PARRMEM1R(PackedVector3Array, Vector3, has);
+ VCALL_PARRMEM0(PackedVector3Array, Vector3, sort);
VCALL_PARRMEM0(PackedVector3Array, Vector3, invert);
VCALL_PARRMEM0R(PackedColorArray, Color, size);
@@ -799,6 +813,8 @@ struct _VariantCall {
VCALL_PARRMEM1(PackedColorArray, Color, remove);
VCALL_PARRMEM1(PackedColorArray, Color, append);
VCALL_PARRMEM1(PackedColorArray, Color, append_array);
+ VCALL_PARRMEM1R(PackedColorArray, Color, has);
+ VCALL_PARRMEM0(PackedColorArray, Color, sort);
VCALL_PARRMEM0(PackedColorArray, Color, invert);
#define VCALL_PTR0(m_type, m_method) \
@@ -826,6 +842,7 @@ struct _VariantCall {
#define VCALL_PTR5R(m_type, m_method) \
static void _call_##m_type##_##m_method(Variant &r_ret, Variant &p_self, const Variant **p_args) { r_ret = reinterpret_cast<m_type *>(p_self._data._ptr)->m_method(*p_args[0], *p_args[1], *p_args[2], *p_args[3], *p_args[4]); }
+ VCALL_PTR0R(AABB, abs);
VCALL_PTR0R(AABB, get_area);
VCALL_PTR0R(AABB, has_no_area);
VCALL_PTR0R(AABB, has_no_surface);
@@ -1388,7 +1405,7 @@ Variant Variant::construct(const Variant::Type p_type, const Variant **p_args, i
return (int64_t(*p_args[0]));
}
case FLOAT: {
- return real_t(*p_args[0]);
+ return double(*p_args[0]);
}
case STRING: {
return String(*p_args[0]);
@@ -1780,7 +1797,6 @@ void register_variant_methods() {
ADDFUNC3R(STRING, INT, String, count, STRING, "what", INT, "from", INT, "to", varray(0, 0));
ADDFUNC3R(STRING, INT, String, countn, STRING, "what", INT, "from", INT, "to", varray(0, 0));
- ADDFUNC1R(STRING, INT, String, find_last, STRING, "what", varray());
ADDFUNC2R(STRING, INT, String, findn, STRING, "what", INT, "from", varray(0));
ADDFUNC2R(STRING, INT, String, rfind, STRING, "what", INT, "from", varray(-1));
ADDFUNC2R(STRING, INT, String, rfindn, STRING, "what", INT, "from", varray(-1));
@@ -1964,7 +1980,6 @@ void register_variant_methods() {
ADDFUNC0R(PLANE, PLANE, Plane, normalized, varray());
ADDFUNC0R(PLANE, VECTOR3, Plane, center, varray());
- ADDFUNC0R(PLANE, VECTOR3, Plane, get_any_point, varray());
ADDFUNC1R(PLANE, BOOL, Plane, is_equal_approx, PLANE, "plane", varray());
ADDFUNC1R(PLANE, BOOL, Plane, is_point_over, VECTOR3, "point", varray());
ADDFUNC1R(PLANE, FLOAT, Plane, distance_to, VECTOR3, "point", varray());
@@ -2087,6 +2102,8 @@ void register_variant_methods() {
ADDFUNC1(PACKED_BYTE_ARRAY, NIL, PackedByteArray, remove, INT, "idx", varray());
ADDFUNC2R(PACKED_BYTE_ARRAY, INT, PackedByteArray, insert, INT, "idx", INT, "byte", varray());
ADDFUNC1(PACKED_BYTE_ARRAY, NIL, PackedByteArray, resize, INT, "idx", varray());
+ ADDFUNC1R(PACKED_BYTE_ARRAY, BOOL, PackedByteArray, has, INT, "value", varray());
+ ADDFUNC0(PACKED_BYTE_ARRAY, NIL, PackedByteArray, sort, varray());
ADDFUNC0(PACKED_BYTE_ARRAY, NIL, PackedByteArray, invert, varray());
ADDFUNC2R(PACKED_BYTE_ARRAY, PACKED_BYTE_ARRAY, PackedByteArray, subarray, INT, "from", INT, "to", varray());
@@ -2105,6 +2122,8 @@ void register_variant_methods() {
ADDFUNC1(PACKED_INT32_ARRAY, NIL, PackedInt32Array, remove, INT, "idx", varray());
ADDFUNC2R(PACKED_INT32_ARRAY, INT, PackedInt32Array, insert, INT, "idx", INT, "integer", varray());
ADDFUNC1(PACKED_INT32_ARRAY, NIL, PackedInt32Array, resize, INT, "idx", varray());
+ ADDFUNC1R(PACKED_INT32_ARRAY, BOOL, PackedInt32Array, has, INT, "value", varray());
+ ADDFUNC0(PACKED_INT32_ARRAY, NIL, PackedInt32Array, sort, varray());
ADDFUNC0(PACKED_INT32_ARRAY, NIL, PackedInt32Array, invert, varray());
ADDFUNC0R(PACKED_INT64_ARRAY, INT, PackedInt64Array, size, varray());
@@ -2116,6 +2135,8 @@ void register_variant_methods() {
ADDFUNC1(PACKED_INT64_ARRAY, NIL, PackedInt64Array, remove, INT, "idx", varray());
ADDFUNC2R(PACKED_INT64_ARRAY, INT, PackedInt64Array, insert, INT, "idx", INT, "integer", varray());
ADDFUNC1(PACKED_INT64_ARRAY, NIL, PackedInt64Array, resize, INT, "idx", varray());
+ ADDFUNC1R(PACKED_INT64_ARRAY, BOOL, PackedInt64Array, has, INT, "value", varray());
+ ADDFUNC0(PACKED_INT64_ARRAY, NIL, PackedInt64Array, sort, varray());
ADDFUNC0(PACKED_INT64_ARRAY, NIL, PackedInt64Array, invert, varray());
ADDFUNC0R(PACKED_FLOAT32_ARRAY, INT, PackedFloat32Array, size, varray());
@@ -2127,6 +2148,8 @@ void register_variant_methods() {
ADDFUNC1(PACKED_FLOAT32_ARRAY, NIL, PackedFloat32Array, remove, INT, "idx", varray());
ADDFUNC2R(PACKED_FLOAT32_ARRAY, INT, PackedFloat32Array, insert, INT, "idx", FLOAT, "value", varray());
ADDFUNC1(PACKED_FLOAT32_ARRAY, NIL, PackedFloat32Array, resize, INT, "idx", varray());
+ ADDFUNC1R(PACKED_FLOAT32_ARRAY, BOOL, PackedFloat32Array, has, FLOAT, "value", varray());
+ ADDFUNC0(PACKED_FLOAT32_ARRAY, NIL, PackedFloat32Array, sort, varray());
ADDFUNC0(PACKED_FLOAT32_ARRAY, NIL, PackedFloat32Array, invert, varray());
ADDFUNC0R(PACKED_FLOAT64_ARRAY, INT, PackedFloat64Array, size, varray());
@@ -2138,6 +2161,8 @@ void register_variant_methods() {
ADDFUNC1(PACKED_FLOAT64_ARRAY, NIL, PackedFloat64Array, remove, INT, "idx", varray());
ADDFUNC2R(PACKED_FLOAT64_ARRAY, INT, PackedFloat64Array, insert, INT, "idx", FLOAT, "value", varray());
ADDFUNC1(PACKED_FLOAT64_ARRAY, NIL, PackedFloat64Array, resize, INT, "idx", varray());
+ ADDFUNC1R(PACKED_FLOAT64_ARRAY, BOOL, PackedFloat64Array, has, FLOAT, "value", varray());
+ ADDFUNC0(PACKED_FLOAT64_ARRAY, NIL, PackedFloat64Array, sort, varray());
ADDFUNC0(PACKED_FLOAT64_ARRAY, NIL, PackedFloat64Array, invert, varray());
ADDFUNC0R(PACKED_STRING_ARRAY, INT, PackedStringArray, size, varray());
@@ -2149,6 +2174,8 @@ void register_variant_methods() {
ADDFUNC1(PACKED_STRING_ARRAY, NIL, PackedStringArray, remove, INT, "idx", varray());
ADDFUNC2R(PACKED_STRING_ARRAY, INT, PackedStringArray, insert, INT, "idx", STRING, "string", varray());
ADDFUNC1(PACKED_STRING_ARRAY, NIL, PackedStringArray, resize, INT, "idx", varray());
+ ADDFUNC1R(PACKED_STRING_ARRAY, BOOL, PackedStringArray, has, STRING, "value", varray());
+ ADDFUNC0(PACKED_STRING_ARRAY, NIL, PackedStringArray, sort, varray());
ADDFUNC0(PACKED_STRING_ARRAY, NIL, PackedStringArray, invert, varray());
ADDFUNC0R(PACKED_VECTOR2_ARRAY, INT, PackedVector2Array, size, varray());
@@ -2160,6 +2187,8 @@ void register_variant_methods() {
ADDFUNC1(PACKED_VECTOR2_ARRAY, NIL, PackedVector2Array, remove, INT, "idx", varray());
ADDFUNC2R(PACKED_VECTOR2_ARRAY, INT, PackedVector2Array, insert, INT, "idx", VECTOR2, "vector2", varray());
ADDFUNC1(PACKED_VECTOR2_ARRAY, NIL, PackedVector2Array, resize, INT, "idx", varray());
+ ADDFUNC1R(PACKED_VECTOR2_ARRAY, BOOL, PackedVector2Array, has, VECTOR2, "value", varray());
+ ADDFUNC0(PACKED_VECTOR2_ARRAY, NIL, PackedVector2Array, sort, varray());
ADDFUNC0(PACKED_VECTOR2_ARRAY, NIL, PackedVector2Array, invert, varray());
ADDFUNC0R(PACKED_VECTOR3_ARRAY, INT, PackedVector3Array, size, varray());
@@ -2171,6 +2200,8 @@ void register_variant_methods() {
ADDFUNC1(PACKED_VECTOR3_ARRAY, NIL, PackedVector3Array, remove, INT, "idx", varray());
ADDFUNC2R(PACKED_VECTOR3_ARRAY, INT, PackedVector3Array, insert, INT, "idx", VECTOR3, "vector3", varray());
ADDFUNC1(PACKED_VECTOR3_ARRAY, NIL, PackedVector3Array, resize, INT, "idx", varray());
+ ADDFUNC1R(PACKED_VECTOR3_ARRAY, BOOL, PackedVector3Array, has, VECTOR3, "value", varray());
+ ADDFUNC0(PACKED_VECTOR3_ARRAY, NIL, PackedVector3Array, sort, varray());
ADDFUNC0(PACKED_VECTOR3_ARRAY, NIL, PackedVector3Array, invert, varray());
ADDFUNC0R(PACKED_COLOR_ARRAY, INT, PackedColorArray, size, varray());
@@ -2182,10 +2213,13 @@ void register_variant_methods() {
ADDFUNC1(PACKED_COLOR_ARRAY, NIL, PackedColorArray, remove, INT, "idx", varray());
ADDFUNC2R(PACKED_COLOR_ARRAY, INT, PackedColorArray, insert, INT, "idx", COLOR, "color", varray());
ADDFUNC1(PACKED_COLOR_ARRAY, NIL, PackedColorArray, resize, INT, "idx", varray());
+ ADDFUNC1R(PACKED_COLOR_ARRAY, BOOL, PackedColorArray, has, COLOR, "value", varray());
+ ADDFUNC0(PACKED_COLOR_ARRAY, NIL, PackedColorArray, sort, varray());
ADDFUNC0(PACKED_COLOR_ARRAY, NIL, PackedColorArray, invert, varray());
//pointerbased
+ ADDFUNC0R(AABB, AABB, AABB, abs, varray());
ADDFUNC0R(AABB, FLOAT, AABB, get_area, varray());
ADDFUNC0R(AABB, BOOL, AABB, has_no_area, varray());
ADDFUNC0R(AABB, BOOL, AABB, has_no_surface, varray());
@@ -2316,9 +2350,9 @@ void register_variant_methods() {
_VariantCall::add_variant_constant(Variant::VECTOR3, "FORWARD", Vector3(0, 0, -1));
_VariantCall::add_variant_constant(Variant::VECTOR3, "BACK", Vector3(0, 0, 1));
- _VariantCall::add_constant(Variant::VECTOR3I, "AXIS_X", Vector3::AXIS_X);
- _VariantCall::add_constant(Variant::VECTOR3I, "AXIS_Y", Vector3::AXIS_Y);
- _VariantCall::add_constant(Variant::VECTOR3I, "AXIS_Z", Vector3::AXIS_Z);
+ _VariantCall::add_constant(Variant::VECTOR3I, "AXIS_X", Vector3i::AXIS_X);
+ _VariantCall::add_constant(Variant::VECTOR3I, "AXIS_Y", Vector3i::AXIS_Y);
+ _VariantCall::add_constant(Variant::VECTOR3I, "AXIS_Z", Vector3i::AXIS_Z);
_VariantCall::add_variant_constant(Variant::VECTOR3I, "ZERO", Vector3i(0, 0, 0));
_VariantCall::add_variant_constant(Variant::VECTOR3I, "ONE", Vector3i(1, 1, 1));
@@ -2332,8 +2366,8 @@ void register_variant_methods() {
_VariantCall::add_constant(Variant::VECTOR2, "AXIS_X", Vector2::AXIS_X);
_VariantCall::add_constant(Variant::VECTOR2, "AXIS_Y", Vector2::AXIS_Y);
- _VariantCall::add_constant(Variant::VECTOR2I, "AXIS_X", Vector2::AXIS_X);
- _VariantCall::add_constant(Variant::VECTOR2I, "AXIS_Y", Vector2::AXIS_Y);
+ _VariantCall::add_constant(Variant::VECTOR2I, "AXIS_X", Vector2i::AXIS_X);
+ _VariantCall::add_constant(Variant::VECTOR2I, "AXIS_Y", Vector2i::AXIS_Y);
_VariantCall::add_variant_constant(Variant::VECTOR2, "ZERO", Vector2(0, 0));
_VariantCall::add_variant_constant(Variant::VECTOR2, "ONE", Vector2(1, 1));
diff --git a/core/variant_internal.h b/core/variant_internal.h
new file mode 100644
index 0000000000..0e0a1e398f
--- /dev/null
+++ b/core/variant_internal.h
@@ -0,0 +1,118 @@
+/*************************************************************************/
+/* variant_internal.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* 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 VARIANT_INTERNAL_H
+#define VARIANT_INTERNAL_H
+
+#include "variant.h"
+
+// For use when you want to access the internal pointer of a Variant directly.
+// Use with caution. You need to be sure that the type is correct.
+class VariantInternal {
+public:
+ // Set type.
+ _FORCE_INLINE_ static void initialize(Variant *v, Variant::Type p_type) { v->type = p_type; }
+
+ // Atomic types.
+ _FORCE_INLINE_ static bool *get_bool(Variant *v) { return &v->_data._bool; }
+ _FORCE_INLINE_ static const bool *get_bool(const Variant *v) { return &v->_data._bool; }
+ _FORCE_INLINE_ static int64_t *get_int(Variant *v) { return &v->_data._int; }
+ _FORCE_INLINE_ static const int64_t *get_int(const Variant *v) { return &v->_data._int; }
+ _FORCE_INLINE_ static double *get_float(Variant *v) { return &v->_data._float; }
+ _FORCE_INLINE_ static const double *get_float(const Variant *v) { return &v->_data._float; }
+ _FORCE_INLINE_ static String *get_string(Variant *v) { return reinterpret_cast<String *>(v->_data._mem); }
+ _FORCE_INLINE_ static const String *get_string(const Variant *v) { return reinterpret_cast<const String *>(v->_data._mem); }
+
+ // Math types.
+ _FORCE_INLINE_ static Vector2 *get_vector2(Variant *v) { return reinterpret_cast<Vector2 *>(v->_data._mem); }
+ _FORCE_INLINE_ static const Vector2 *get_vector2(const Variant *v) { return reinterpret_cast<const Vector2 *>(v->_data._mem); }
+ _FORCE_INLINE_ static Vector2i *get_vector2i(Variant *v) { return reinterpret_cast<Vector2i *>(v->_data._mem); }
+ _FORCE_INLINE_ static const Vector2i *get_vector2i(const Variant *v) { return reinterpret_cast<const Vector2i *>(v->_data._mem); }
+ _FORCE_INLINE_ static Rect2 *get_rect2(Variant *v) { return reinterpret_cast<Rect2 *>(v->_data._mem); }
+ _FORCE_INLINE_ static const Rect2 *get_rect2(const Variant *v) { return reinterpret_cast<const Rect2 *>(v->_data._mem); }
+ _FORCE_INLINE_ static Rect2i *get_rect2i(Variant *v) { return reinterpret_cast<Rect2i *>(v->_data._mem); }
+ _FORCE_INLINE_ static const Rect2i *get_rect2i(const Variant *v) { return reinterpret_cast<const Rect2i *>(v->_data._mem); }
+ _FORCE_INLINE_ static Vector3 *get_vector3(Variant *v) { return reinterpret_cast<Vector3 *>(v->_data._mem); }
+ _FORCE_INLINE_ static const Vector3 *get_vector3(const Variant *v) { return reinterpret_cast<const Vector3 *>(v->_data._mem); }
+ _FORCE_INLINE_ static Vector3i *get_vector3i(Variant *v) { return reinterpret_cast<Vector3i *>(v->_data._mem); }
+ _FORCE_INLINE_ static const Vector3i *get_vector3i(const Variant *v) { return reinterpret_cast<const Vector3i *>(v->_data._mem); }
+ _FORCE_INLINE_ static Transform2D *get_transform2d(Variant *v) { return v->_data._transform2d; }
+ _FORCE_INLINE_ static const Transform2D *get_transform2d(const Variant *v) { return v->_data._transform2d; }
+ _FORCE_INLINE_ static Plane *get_plane(Variant *v) { return reinterpret_cast<Plane *>(v->_data._mem); }
+ _FORCE_INLINE_ static const Plane *get_plane(const Variant *v) { return reinterpret_cast<const Plane *>(v->_data._mem); }
+ _FORCE_INLINE_ static Quat *get_quat(Variant *v) { return reinterpret_cast<Quat *>(v->_data._mem); }
+ _FORCE_INLINE_ static const Quat *get_quat(const Variant *v) { return reinterpret_cast<const Quat *>(v->_data._mem); }
+ _FORCE_INLINE_ static ::AABB *get_aabb(Variant *v) { return v->_data._aabb; }
+ _FORCE_INLINE_ static const ::AABB *get_aabb(const Variant *v) { return v->_data._aabb; }
+ _FORCE_INLINE_ static Basis *get_basis(Variant *v) { return v->_data._basis; }
+ _FORCE_INLINE_ static const Basis *get_basis(const Variant *v) { return v->_data._basis; }
+ _FORCE_INLINE_ static Transform *get_transform(Variant *v) { return v->_data._transform; }
+ _FORCE_INLINE_ static const Transform *get_transform(const Variant *v) { return v->_data._transform; }
+
+ // Misc types.
+ _FORCE_INLINE_ static Color *get_color(Variant *v) { return reinterpret_cast<Color *>(v->_data._mem); }
+ _FORCE_INLINE_ static const Color *get_color(const Variant *v) { return reinterpret_cast<const Color *>(v->_data._mem); }
+ _FORCE_INLINE_ static StringName *get_string_name(Variant *v) { return reinterpret_cast<StringName *>(v->_data._mem); }
+ _FORCE_INLINE_ static const StringName *get_string_name(const Variant *v) { return reinterpret_cast<const StringName *>(v->_data._mem); }
+ _FORCE_INLINE_ static NodePath *get_node_path(Variant *v) { return reinterpret_cast<NodePath *>(v->_data._mem); }
+ _FORCE_INLINE_ static const NodePath *get_node_path(const Variant *v) { return reinterpret_cast<const NodePath *>(v->_data._mem); }
+ _FORCE_INLINE_ static RID *get_rid(Variant *v) { return reinterpret_cast<RID *>(v->_data._mem); }
+ _FORCE_INLINE_ static const RID *get_rid(const Variant *v) { return reinterpret_cast<const RID *>(v->_data._mem); }
+ _FORCE_INLINE_ static Callable *get_callable(Variant *v) { return reinterpret_cast<Callable *>(v->_data._mem); }
+ _FORCE_INLINE_ static const Callable *get_callable(const Variant *v) { return reinterpret_cast<const Callable *>(v->_data._mem); }
+ _FORCE_INLINE_ static Signal *get_signal(Variant *v) { return reinterpret_cast<Signal *>(v->_data._mem); }
+ _FORCE_INLINE_ static const Signal *get_signal(const Variant *v) { return reinterpret_cast<const Signal *>(v->_data._mem); }
+ _FORCE_INLINE_ static Dictionary *get_dictionary(Variant *v) { return reinterpret_cast<Dictionary *>(v->_data._mem); }
+ _FORCE_INLINE_ static const Dictionary *get_dictionary(const Variant *v) { return reinterpret_cast<const Dictionary *>(v->_data._mem); }
+ _FORCE_INLINE_ static Array *get_array(Variant *v) { return reinterpret_cast<Array *>(v->_data._mem); }
+ _FORCE_INLINE_ static const Array *get_array(const Variant *v) { return reinterpret_cast<const Array *>(v->_data._mem); }
+
+ // Typed arrays.
+ _FORCE_INLINE_ static PackedByteArray *get_byte_array(Variant *v) { return &static_cast<Variant::PackedArrayRef<uint8_t> *>(v->_data.packed_array)->array; }
+ _FORCE_INLINE_ static const PackedByteArray *get_byte_array(const Variant *v) { return &static_cast<const Variant::PackedArrayRef<uint8_t> *>(v->_data.packed_array)->array; }
+ _FORCE_INLINE_ static PackedInt32Array *get_int32_array(Variant *v) { return &static_cast<Variant::PackedArrayRef<int32_t> *>(v->_data.packed_array)->array; }
+ _FORCE_INLINE_ static const PackedInt32Array *get_int32_array(const Variant *v) { return &static_cast<const Variant::PackedArrayRef<int32_t> *>(v->_data.packed_array)->array; }
+ _FORCE_INLINE_ static PackedInt64Array *get_int64_array(Variant *v) { return &static_cast<Variant::PackedArrayRef<int64_t> *>(v->_data.packed_array)->array; }
+ _FORCE_INLINE_ static const PackedInt64Array *get_int64_array(const Variant *v) { return &static_cast<const Variant::PackedArrayRef<int64_t> *>(v->_data.packed_array)->array; }
+ _FORCE_INLINE_ static PackedFloat32Array *get_float32_array(Variant *v) { return &static_cast<Variant::PackedArrayRef<float> *>(v->_data.packed_array)->array; }
+ _FORCE_INLINE_ static const PackedFloat32Array *get_float32_array(const Variant *v) { return &static_cast<const Variant::PackedArrayRef<float> *>(v->_data.packed_array)->array; }
+ _FORCE_INLINE_ static PackedFloat64Array *get_float64_array(Variant *v) { return &static_cast<Variant::PackedArrayRef<double> *>(v->_data.packed_array)->array; }
+ _FORCE_INLINE_ static const PackedFloat64Array *get_float64_array(const Variant *v) { return &static_cast<const Variant::PackedArrayRef<double> *>(v->_data.packed_array)->array; }
+ _FORCE_INLINE_ static PackedStringArray *get_string_array(Variant *v) { return &static_cast<Variant::PackedArrayRef<String> *>(v->_data.packed_array)->array; }
+ _FORCE_INLINE_ static const PackedStringArray *get_string_array(const Variant *v) { return &static_cast<const Variant::PackedArrayRef<String> *>(v->_data.packed_array)->array; }
+ _FORCE_INLINE_ static PackedVector2Array *get_vector2_array(Variant *v) { return &static_cast<Variant::PackedArrayRef<Vector2> *>(v->_data.packed_array)->array; }
+ _FORCE_INLINE_ static const PackedVector2Array *get_vector2_array(const Variant *v) { return &static_cast<const Variant::PackedArrayRef<Vector2> *>(v->_data.packed_array)->array; }
+ _FORCE_INLINE_ static PackedVector3Array *get_vector3_array(Variant *v) { return &static_cast<Variant::PackedArrayRef<Vector3> *>(v->_data.packed_array)->array; }
+ _FORCE_INLINE_ static const PackedVector3Array *get_vector3_array(const Variant *v) { return &static_cast<const Variant::PackedArrayRef<Vector3> *>(v->_data.packed_array)->array; }
+ _FORCE_INLINE_ static PackedColorArray *get_color_array(Variant *v) { return &static_cast<Variant::PackedArrayRef<Color> *>(v->_data.packed_array)->array; }
+ _FORCE_INLINE_ static const PackedColorArray *get_color_array(const Variant *v) { return &static_cast<const Variant::PackedArrayRef<Color> *>(v->_data.packed_array)->array; }
+};
+
+#endif // VARIANT_INTERNAL_H
diff --git a/core/variant_op.cpp b/core/variant_op.cpp
index 0c9a4a992a..0cb2fe29a1 100644
--- a/core/variant_op.cpp
+++ b/core/variant_op.cpp
@@ -118,32 +118,32 @@
/* clang-format on */
-#define CASES(PREFIX) static const void *switch_table_##PREFIX[25][Variant::VARIANT_MAX] = { \
- TYPES(PREFIX, OP_EQUAL), \
- TYPES(PREFIX, OP_NOT_EQUAL), \
- TYPES(PREFIX, OP_LESS), \
- TYPES(PREFIX, OP_LESS_EQUAL), \
- TYPES(PREFIX, OP_GREATER), \
- TYPES(PREFIX, OP_GREATER_EQUAL), \
- TYPES(PREFIX, OP_ADD), \
- TYPES(PREFIX, OP_SUBTRACT), \
- TYPES(PREFIX, OP_MULTIPLY), \
- TYPES(PREFIX, OP_DIVIDE), \
- TYPES(PREFIX, OP_NEGATE), \
- TYPES(PREFIX, OP_POSITIVE), \
- TYPES(PREFIX, OP_MODULE), \
- TYPES(PREFIX, OP_STRING_CONCAT), \
- TYPES(PREFIX, OP_SHIFT_LEFT), \
- TYPES(PREFIX, OP_SHIFT_RIGHT), \
- TYPES(PREFIX, OP_BIT_AND), \
- TYPES(PREFIX, OP_BIT_OR), \
- TYPES(PREFIX, OP_BIT_XOR), \
- TYPES(PREFIX, OP_BIT_NEGATE), \
- TYPES(PREFIX, OP_AND), \
- TYPES(PREFIX, OP_OR), \
- TYPES(PREFIX, OP_XOR), \
- TYPES(PREFIX, OP_NOT), \
- TYPES(PREFIX, OP_IN), \
+#define CASES(PREFIX) static const void *switch_table_##PREFIX[Variant::OP_MAX][Variant::VARIANT_MAX] = { \
+ TYPES(PREFIX, OP_EQUAL), \
+ TYPES(PREFIX, OP_NOT_EQUAL), \
+ TYPES(PREFIX, OP_LESS), \
+ TYPES(PREFIX, OP_LESS_EQUAL), \
+ TYPES(PREFIX, OP_GREATER), \
+ TYPES(PREFIX, OP_GREATER_EQUAL), \
+ TYPES(PREFIX, OP_ADD), \
+ TYPES(PREFIX, OP_SUBTRACT), \
+ TYPES(PREFIX, OP_MULTIPLY), \
+ TYPES(PREFIX, OP_DIVIDE), \
+ TYPES(PREFIX, OP_NEGATE), \
+ TYPES(PREFIX, OP_POSITIVE), \
+ TYPES(PREFIX, OP_MODULE), \
+ TYPES(PREFIX, OP_STRING_CONCAT), \
+ TYPES(PREFIX, OP_SHIFT_LEFT), \
+ TYPES(PREFIX, OP_SHIFT_RIGHT), \
+ TYPES(PREFIX, OP_BIT_AND), \
+ TYPES(PREFIX, OP_BIT_OR), \
+ TYPES(PREFIX, OP_BIT_XOR), \
+ TYPES(PREFIX, OP_BIT_NEGATE), \
+ TYPES(PREFIX, OP_AND), \
+ TYPES(PREFIX, OP_OR), \
+ TYPES(PREFIX, OP_XOR), \
+ TYPES(PREFIX, OP_NOT), \
+ TYPES(PREFIX, OP_IN), \
}
#define SWITCH(PREFIX, op, val) goto *switch_table_##PREFIX[op][val];
diff --git a/core/vector.h b/core/vector.h
index 4c152fb084..5fb630c21c 100644
--- a/core/vector.h
+++ b/core/vector.h
@@ -92,6 +92,10 @@ public:
void append_array(Vector<T> p_other);
+ bool has(const T &p_val) {
+ return find(p_val, 0) != -1;
+ }
+
template <class C>
void sort_custom() {
int len = _cowdata.size();